fix(trading): positions tooltips update (#4504)
This commit is contained in:
parent
d20c2a08ae
commit
aac0c25d09
@ -41,16 +41,17 @@ const singleRow: Position = {
|
|||||||
|
|
||||||
const singleRowData = [singleRow];
|
const singleRowData = [singleRow];
|
||||||
|
|
||||||
it('should render successfully', async () => {
|
describe('Positions', () => {
|
||||||
|
it('should render successfully', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
const { baseElement } = render(
|
const { baseElement } = render(
|
||||||
<PositionsTable rowData={[]} isReadOnly={false} />
|
<PositionsTable rowData={[]} isReadOnly={false} />
|
||||||
);
|
);
|
||||||
expect(baseElement).toBeTruthy();
|
expect(baseElement).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('render correct columns', async () => {
|
it('render correct columns', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<PositionsTable rowData={singleRowData} isReadOnly={true} />);
|
render(<PositionsTable rowData={singleRowData} isReadOnly={true} />);
|
||||||
});
|
});
|
||||||
@ -73,16 +74,16 @@ it('render correct columns', async () => {
|
|||||||
'Unrealised PNL',
|
'Unrealised PNL',
|
||||||
'Updated',
|
'Updated',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders market name', async () => {
|
it('renders market name', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
||||||
});
|
});
|
||||||
expect(screen.getByText('ETH/BTC (31 july 2022)')).toBeTruthy();
|
expect(screen.getByText('ETH/BTC (31 july 2022)')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Does not fail if the market name does not match the split pattern', async () => {
|
it('Does not fail if the market name does not match the split pattern', async () => {
|
||||||
const breakingMarketName = 'OP/USD AUG-SEP22 - Incentive';
|
const breakingMarketName = 'OP/USD AUG-SEP22 - Incentive';
|
||||||
const row = [
|
const row = [
|
||||||
Object.assign({}, singleRow, { marketName: breakingMarketName }),
|
Object.assign({}, singleRow, { marketName: breakingMarketName }),
|
||||||
@ -92,9 +93,9 @@ it('Does not fail if the market name does not match the split pattern', async ()
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(screen.getByText(breakingMarketName)).toBeTruthy();
|
expect(screen.getByText(breakingMarketName)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('add color and sign to amount, displays positive notional value', async () => {
|
it('add color and sign to amount, displays positive notional value', async () => {
|
||||||
let result: RenderResult;
|
let result: RenderResult;
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
result = render(
|
result = render(
|
||||||
@ -120,9 +121,9 @@ it('add color and sign to amount, displays positive notional value', async () =>
|
|||||||
expect(cells[2].classList.contains('text-market-red')).toBeTruthy();
|
expect(cells[2].classList.contains('text-market-red')).toBeTruthy();
|
||||||
expect(cells[2].textContent?.startsWith('-100')).toBeTruthy();
|
expect(cells[2].textContent?.startsWith('-100')).toBeTruthy();
|
||||||
expect(cells[1].textContent).toEqual('1,230.0');
|
expect(cells[1].textContent).toEqual('1,230.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays mark price', async () => {
|
it('displays mark price', async () => {
|
||||||
let result: RenderResult;
|
let result: RenderResult;
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
result = render(
|
result = render(
|
||||||
@ -150,34 +151,34 @@ it('displays mark price', async () => {
|
|||||||
|
|
||||||
cells = screen.getAllByRole('gridcell');
|
cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[3].textContent).toEqual('-');
|
expect(cells[3].textContent).toEqual('-');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays liquidation price', async () => {
|
it('displays liquidation price', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[4].textContent).toEqual('liquidation price');
|
expect(cells[4].textContent).toEqual('liquidation price');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays leverage', async () => {
|
it('displays leverage', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[7].textContent).toEqual('1.1');
|
expect(cells[7].textContent).toEqual('1.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays allocated margin', async () => {
|
it('displays allocated margin', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
|
||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
const cell = cells[8];
|
const cell = cells[8];
|
||||||
expect(cell.textContent).toEqual('123,456.00');
|
expect(cell.textContent).toEqual('123,456.00');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays realised and unrealised PNL', async () => {
|
it('displays realised and unrealised PNL', async () => {
|
||||||
// pnl cells should be rendered with asset dps
|
// pnl cells should be rendered with asset dps
|
||||||
const expectedRealised = addDecimalsFormatNumber(
|
const expectedRealised = addDecimalsFormatNumber(
|
||||||
singleRow.realisedPNL,
|
singleRow.realisedPNL,
|
||||||
@ -195,9 +196,9 @@ it('displays realised and unrealised PNL', async () => {
|
|||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[9].textContent).toEqual(expectedRealised);
|
expect(cells[9].textContent).toEqual(expectedRealised);
|
||||||
expect(cells[10].textContent).toEqual(expectedUnrealised);
|
expect(cells[10].textContent).toEqual(expectedUnrealised);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays close button', async () => {
|
it('displays close button', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
<PositionsTable
|
<PositionsTable
|
||||||
@ -212,9 +213,9 @@ it('displays close button', async () => {
|
|||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[12].textContent).toEqual('Close');
|
expect(cells[12].textContent).toEqual('Close');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('do not display close button if openVolume is zero', async () => {
|
it('do not display close button if openVolume is zero', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
<PositionsTable
|
<PositionsTable
|
||||||
@ -228,9 +229,9 @@ it('do not display close button if openVolume is zero', async () => {
|
|||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[12].textContent).toEqual('');
|
expect(cells[12].textContent).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PNLCell', () => {
|
describe('PNLCell', () => {
|
||||||
const props = {
|
const props = {
|
||||||
data: undefined,
|
data: undefined,
|
||||||
valueFormatted: '100',
|
valueFormatted: '100',
|
||||||
@ -272,12 +273,19 @@ describe('PNLCell', () => {
|
|||||||
expect(tooltip).toBeInTheDocument();
|
expect(tooltip).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
// using within as radix renders tooltip content twice
|
// using within as radix renders tooltip content twice
|
||||||
within(tooltip).getByText('Lifetime loss socialisation deductions: 5.00')
|
within(tooltip).getByText(
|
||||||
|
'Lifetime loss socialisation deductions: 5.00'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
within(tooltip).getByText(
|
||||||
|
`You received less BTC in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`
|
||||||
|
)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('OpenVolumeCell', () => {
|
describe('OpenVolumeCell', () => {
|
||||||
const props = {
|
const props = {
|
||||||
data: undefined,
|
data: undefined,
|
||||||
valueFormatted: '100',
|
valueFormatted: '100',
|
||||||
@ -300,7 +308,7 @@ describe('OpenVolumeCell', () => {
|
|||||||
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders status with warning tooltip if not normal', async () => {
|
it('renders status with warning tooltip if orders were closed', async () => {
|
||||||
const props = {
|
const props = {
|
||||||
data: {
|
data: {
|
||||||
...singleRow,
|
...singleRow,
|
||||||
@ -321,5 +329,41 @@ describe('OpenVolumeCell', () => {
|
|||||||
`Status: ${PositionStatusMapping[props.data.status]}`
|
`Status: ${PositionStatusMapping[props.data.status]}`
|
||||||
)
|
)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
// using within as radix renders tooltip content twice
|
||||||
|
within(tooltip).getByText(
|
||||||
|
'The position was distressed, but removing open orders from the book brought the margin level back to a point where the open position could be maintained.'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders status with warning tooltip if position was closed out', async () => {
|
||||||
|
const props = {
|
||||||
|
data: {
|
||||||
|
...singleRow,
|
||||||
|
status: PositionStatus.POSITION_STATUS_CLOSED_OUT,
|
||||||
|
},
|
||||||
|
valueFormatted: '100',
|
||||||
|
};
|
||||||
|
render(<OpenVolumeCell {...(props as ICellRendererParams)} />);
|
||||||
|
const content = screen.getByText(props.valueFormatted);
|
||||||
|
expect(content).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||||
|
await userEvent.hover(content);
|
||||||
|
const tooltip = await screen.findByRole('tooltip');
|
||||||
|
expect(tooltip).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
// using within as radix renders tooltip content twice
|
||||||
|
within(tooltip).getByText(
|
||||||
|
`Status: ${PositionStatusMapping[props.data.status]}`
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
// using within as radix renders tooltip content twice
|
||||||
|
within(tooltip).getByText(
|
||||||
|
'You did not have enough BTC collateral to meet the maintenance margin requirements for your position, so it was closed by the network.'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -478,6 +478,12 @@ export const PNLCell = ({
|
|||||||
<p className="mb-2">
|
<p className="mb-2">
|
||||||
{t('Lifetime loss socialisation deductions: %s', lossesFormatted)}
|
{t('Lifetime loss socialisation deductions: %s', lossesFormatted)}
|
||||||
</p>
|
</p>
|
||||||
|
<p className="mb-2">
|
||||||
|
{t(
|
||||||
|
`You received less %s in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
|
||||||
|
[data.assetSymbol]
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
{LOSS_SOCIALIZATION_LINK && (
|
{LOSS_SOCIALIZATION_LINK && (
|
||||||
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
|
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
|
||||||
{t('Read more about loss socialisation')}
|
{t('Read more about loss socialisation')}
|
||||||
@ -499,27 +505,51 @@ export const OpenVolumeCell = ({
|
|||||||
return <>-</>;
|
return <>-</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.status === PositionStatus.POSITION_STATUS_UNSPECIFIED) {
|
|
||||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
|
||||||
return <>{valueFormatted}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const POSITION_RESOLUTION_LINK = DocsLinks?.POSITION_RESOLUTION ?? '';
|
const POSITION_RESOLUTION_LINK = DocsLinks?.POSITION_RESOLUTION ?? '';
|
||||||
|
|
||||||
|
let primaryTooltip;
|
||||||
|
switch (data.status) {
|
||||||
|
case PositionStatus.POSITION_STATUS_CLOSED_OUT:
|
||||||
|
primaryTooltip = t('Your position was closed.');
|
||||||
|
break;
|
||||||
|
case PositionStatus.POSITION_STATUS_ORDERS_CLOSED:
|
||||||
|
primaryTooltip = t('Your open orders were cancelled.');
|
||||||
|
break;
|
||||||
|
case PositionStatus.POSITION_STATUS_DISTRESSED:
|
||||||
|
primaryTooltip = t('Your position is distressed.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let secondaryTooltip;
|
||||||
|
switch (data.status) {
|
||||||
|
case PositionStatus.POSITION_STATUS_CLOSED_OUT:
|
||||||
|
secondaryTooltip = t(
|
||||||
|
`You did not have enough %s collateral to meet the maintenance margin requirements for your position, so it was closed by the network.`,
|
||||||
|
[data.assetSymbol]
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case PositionStatus.POSITION_STATUS_ORDERS_CLOSED:
|
||||||
|
secondaryTooltip = t(
|
||||||
|
'The position was distressed, but removing open orders from the book brought the margin level back to a point where the open position could be maintained.'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case PositionStatus.POSITION_STATUS_DISTRESSED:
|
||||||
|
secondaryTooltip = t(
|
||||||
|
'The position was distressed, but could not be closed out - orders were removed from the book, and the open volume will be closed out once there is sufficient volume on the book.'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
secondaryTooltip = t('Maintained by network');
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<WarningCell
|
<WarningCell
|
||||||
|
showIcon={data.status !== PositionStatus.POSITION_STATUS_UNSPECIFIED}
|
||||||
tooltipContent={
|
tooltipContent={
|
||||||
<>
|
<>
|
||||||
|
<p className="mb-2">{primaryTooltip}</p>
|
||||||
|
<p className="mb-2">{secondaryTooltip}</p>
|
||||||
<p className="mb-2">
|
<p className="mb-2">
|
||||||
{t('Your position was affected by market conditions')}
|
{t('Status: %s', PositionStatusMapping[data.status])}
|
||||||
</p>
|
|
||||||
<p className="mb-2">
|
|
||||||
{t(
|
|
||||||
'Status: %s',
|
|
||||||
PositionStatusMapping[
|
|
||||||
PositionStatus.POSITION_STATUS_ORDERS_CLOSED
|
|
||||||
]
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
{POSITION_RESOLUTION_LINK && (
|
{POSITION_RESOLUTION_LINK && (
|
||||||
<ExternalLink href={POSITION_RESOLUTION_LINK}>
|
<ExternalLink href={POSITION_RESOLUTION_LINK}>
|
||||||
@ -537,15 +567,17 @@ export const OpenVolumeCell = ({
|
|||||||
const WarningCell = ({
|
const WarningCell = ({
|
||||||
children,
|
children,
|
||||||
tooltipContent,
|
tooltipContent,
|
||||||
|
showIcon = true,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
tooltipContent: ReactNode;
|
tooltipContent: ReactNode;
|
||||||
|
showIcon?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Tooltip description={tooltipContent}>
|
<Tooltip description={tooltipContent}>
|
||||||
<div className="w-full flex items-center justify-between underline decoration-dashed underline-offest-2">
|
<div className="w-full flex items-center justify-between underline decoration-dashed underline-offest-2">
|
||||||
<span className="text-black dark:text-white mr-1">
|
<span className="text-black dark:text-white mr-1">
|
||||||
<Icon name="warning-sign" size={3} />
|
{showIcon && <Icon name="warning-sign" size={3} />}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-ellipsis overflow-hidden">{children}</span>
|
<span className="text-ellipsis overflow-hidden">{children}</span>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user