chore: single position table (1645) (#1749)
* chore: single position table (1645) chore: single position table (1645) * chore: tests fixed * chore: remove unused withSummaryRow arg * chore: use ag grid value formatter type helper * chore: update console-lite to use value formatter params helper * chore: fix e2e test by ignoring pinned row Co-authored-by: Rado <szpiechrados@gmail.com> Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
ce283aeee7
commit
8d2fe118ec
@ -7,10 +7,7 @@ import {
|
|||||||
signedNumberCssClassRules,
|
signedNumberCssClassRules,
|
||||||
t,
|
t,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import type {
|
import type { Position } from '@vegaprotocol/positions';
|
||||||
PositionsTableValueFormatterParams,
|
|
||||||
Position,
|
|
||||||
} from '@vegaprotocol/positions';
|
|
||||||
import { AmountCell } from '@vegaprotocol/positions';
|
import { AmountCell } from '@vegaprotocol/positions';
|
||||||
import type {
|
import type {
|
||||||
CellRendererSelectorResult,
|
CellRendererSelectorResult,
|
||||||
@ -20,6 +17,7 @@ import type {
|
|||||||
ColDef,
|
ColDef,
|
||||||
} from 'ag-grid-community';
|
} from 'ag-grid-community';
|
||||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
import type { VegaValueFormatterParams } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Intent, ProgressBarCell } from '@vegaprotocol/ui-toolkit';
|
import { Intent, ProgressBarCell } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
const EmptyCell = () => '';
|
const EmptyCell = () => '';
|
||||||
@ -77,9 +75,7 @@ const useColumnDefinitions = () => {
|
|||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
node,
|
node,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'openVolume'>) => {
|
||||||
value: Position['openVolume'];
|
|
||||||
}) => {
|
|
||||||
let ret;
|
let ret;
|
||||||
if (value && data) {
|
if (value && data) {
|
||||||
ret = node?.rowPinned
|
ret = node?.rowPinned
|
||||||
@ -107,9 +103,7 @@ const useColumnDefinitions = () => {
|
|||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
node,
|
node,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'markPrice'>) => {
|
||||||
value: Position['markPrice'];
|
|
||||||
}) => {
|
|
||||||
if (
|
if (
|
||||||
data &&
|
data &&
|
||||||
value &&
|
value &&
|
||||||
@ -186,9 +180,8 @@ const useColumnDefinitions = () => {
|
|||||||
valueFormatter: ({
|
valueFormatter: ({
|
||||||
value,
|
value,
|
||||||
node,
|
node,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'currentLeverage'>) =>
|
||||||
value: Position['currentLeverage'];
|
value === undefined ? '' : formatNumber(value.toString(), 1),
|
||||||
}) => (value === undefined ? '' : formatNumber(value.toString(), 1)),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
colId: 'marginallocated',
|
colId: 'marginallocated',
|
||||||
@ -229,10 +222,8 @@ const useColumnDefinitions = () => {
|
|||||||
valueFormatter: ({
|
valueFormatter: ({
|
||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'realisedPNL'>) =>
|
||||||
value: Position['realisedPNL'];
|
value === undefined || data === undefined
|
||||||
}) =>
|
|
||||||
value === undefined
|
|
||||||
? ''
|
? ''
|
||||||
: addDecimalsFormatNumber(value.toString(), data.decimals),
|
: addDecimalsFormatNumber(value.toString(), data.decimals),
|
||||||
cellRenderer: 'PriceFlashCell',
|
cellRenderer: 'PriceFlashCell',
|
||||||
@ -249,10 +240,8 @@ const useColumnDefinitions = () => {
|
|||||||
valueFormatter: ({
|
valueFormatter: ({
|
||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
|
||||||
value: Position['unrealisedPNL'];
|
value === undefined || data === undefined
|
||||||
}) =>
|
|
||||||
value === undefined
|
|
||||||
? ''
|
? ''
|
||||||
: addDecimalsFormatNumber(value.toString(), data.decimals),
|
: addDecimalsFormatNumber(value.toString(), data.decimals),
|
||||||
cellRenderer: 'PriceFlashCell',
|
cellRenderer: 'PriceFlashCell',
|
||||||
@ -266,9 +255,7 @@ const useColumnDefinitions = () => {
|
|||||||
type: 'rightAligned',
|
type: 'rightAligned',
|
||||||
valueFormatter: ({
|
valueFormatter: ({
|
||||||
value,
|
value,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'updatedAt'>) => {
|
||||||
value: Position['updatedAt'];
|
|
||||||
}) => {
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -31,36 +31,35 @@ describe('positions', { tags: '@smoke' }, () => {
|
|||||||
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('[col-id="openVolume"]').each(($openVolume) => {
|
cy.get('.ag-center-cols-container [col-id="openVolume"]').each(
|
||||||
cy.wrap($openVolume).invoke('text').should('not.be.empty');
|
($openVolume) => {
|
||||||
});
|
cy.wrap($openVolume).invoke('text').should('not.be.empty');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// includes average entry price, mark price, realised PNL & leverage
|
// includes average entry price, mark price, realised PNL & leverage
|
||||||
cy.getByTestId('flash-cell').each(($prices) => {
|
cy.getByTestId('flash-cell').each(($prices) => {
|
||||||
cy.wrap($prices).invoke('text').should('not.be.empty');
|
cy.wrap($prices).invoke('text').should('not.be.empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('[col-id="averageEntryPrice"]')
|
cy.get('[col-id="liquidationPrice"]')
|
||||||
.should('contain.text', '85,093.38') // entry price
|
.should('contain.text', '85,093.38') // entry price
|
||||||
.should('contain.text', '0.00'); // liquidation price
|
.should('contain.text', '0.00'); // liquidation price
|
||||||
|
|
||||||
cy.get('[col-id="currentLeverage"]').should('contain.text', '0.8');
|
cy.get('[col-id="currentLeverage"]').should('contain.text', '0.8');
|
||||||
|
|
||||||
cy.get('[col-id="capitalUtilisation"]') // margin allocated
|
cy.get('[col-id="marginAccountBalance"]') // margin allocated
|
||||||
.should('contain.text', '0.00%')
|
.should('contain.text', '1,000.00000');
|
||||||
.should('contain.text', '1,000.01000');
|
|
||||||
|
|
||||||
cy.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
|
cy.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
|
||||||
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty');
|
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.getByTestId('flash-cell').should('contain.text', '276,761.40348'); // Total tDAI position
|
cy.get('[col-id="notional"]').should('contain.text', '276,761.40348'); // Total tDAI position
|
||||||
cy.getByTestId('flash-cell').should('contain.text', '0.00000'); // Total Realised PNL
|
cy.get('[col-id="realisedPNL"]').should('contain.text', '0.00100'); // Total Realised PNL
|
||||||
cy.get('[col-id="unrealisedPNL"]').should('contain.text', '8.95000'); // Total Unrealised PNL
|
cy.get('[col-id="unrealisedPNL"]').should('contain.text', '8.95000'); // Total Unrealised PNL
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.getByTestId('balance').eq(1).should('have.text', '1,000.01000'); // Asset balance
|
|
||||||
|
|
||||||
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
|
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -42,6 +42,7 @@ interface PositionRejoined {
|
|||||||
export interface Position {
|
export interface Position {
|
||||||
marketName: string;
|
marketName: string;
|
||||||
averageEntryPrice: string;
|
averageEntryPrice: string;
|
||||||
|
marginAccountBalance: BigNumber;
|
||||||
capitalUtilisation: number;
|
capitalUtilisation: number;
|
||||||
currentLeverage: number;
|
currentLeverage: number;
|
||||||
decimals: number;
|
decimals: number;
|
||||||
@ -151,6 +152,7 @@ export const getMetrics = (
|
|||||||
metrics.push({
|
metrics.push({
|
||||||
marketName: market.tradableInstrument.instrument.name,
|
marketName: market.tradableInstrument.instrument.name,
|
||||||
averageEntryPrice: position.averageEntryPrice,
|
averageEntryPrice: position.averageEntryPrice,
|
||||||
|
marginAccountBalance,
|
||||||
capitalUtilisation: Math.round(capitalUtilisation.toNumber()),
|
capitalUtilisation: Math.round(capitalUtilisation.toNumber()),
|
||||||
currentLeverage: currentLeverage.toNumber(),
|
currentLeverage: currentLeverage.toNumber(),
|
||||||
marketDecimalPlaces,
|
marketDecimalPlaces,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { Position } from './positions-data-providers';
|
import type { Position } from './positions-data-providers';
|
||||||
import { Positions } from './positions';
|
import { PositionsTable, useClosePosition, usePositionsData } from '../';
|
||||||
import { useClosePosition, usePositionsAssets } from '../';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
|
|
||||||
interface PositionsManagerProps {
|
interface PositionsManagerProps {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
@ -17,18 +17,20 @@ export const PositionsManager = ({ partyId }: PositionsManagerProps) => {
|
|||||||
[submit]
|
[submit]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, error, loading, assetSymbols } = usePositionsAssets(partyId);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
const { data, error, loading, getRows } = usePositionsData(partyId, gridRef);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||||
{assetSymbols?.map((assetSymbol) => (
|
<PositionsTable
|
||||||
<Positions
|
domLayout="autoHeight"
|
||||||
partyId={partyId}
|
style={{ width: '100%' }}
|
||||||
assetSymbol={assetSymbol}
|
ref={gridRef}
|
||||||
key={assetSymbol}
|
rowModelType={data?.length ? 'infinite' : 'clientSide'}
|
||||||
onClose={onClose}
|
rowData={data?.length ? undefined : []}
|
||||||
/>
|
datasource={{ getRows }}
|
||||||
))}
|
onClose={onClose}
|
||||||
|
/>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
|
|
||||||
<Dialog>
|
<Dialog>
|
||||||
|
@ -3,28 +3,31 @@ import { act, render, screen } from '@testing-library/react';
|
|||||||
import PositionsTable from './positions-table';
|
import PositionsTable from './positions-table';
|
||||||
import type { Position } from './positions-data-providers';
|
import type { Position } from './positions-data-providers';
|
||||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
const singleRow: Position = {
|
const singleRow: Position = {
|
||||||
marketName: 'ETH/BTC (31 july 2022)',
|
marketName: 'ETH/BTC (31 july 2022)',
|
||||||
averageEntryPrice: '133', // 13.3
|
averageEntryPrice: '133',
|
||||||
capitalUtilisation: 11, // 11.00%
|
capitalUtilisation: 11,
|
||||||
currentLeverage: 1.1,
|
currentLeverage: 1.1,
|
||||||
marketDecimalPlaces: 1,
|
marketDecimalPlaces: 1,
|
||||||
positionDecimalPlaces: 0,
|
positionDecimalPlaces: 0,
|
||||||
decimals: 2,
|
decimals: 2,
|
||||||
totalBalance: '123456',
|
totalBalance: '123456',
|
||||||
assetSymbol: 'BTC',
|
assetSymbol: 'BTC',
|
||||||
liquidationPrice: '83', // 8.3
|
liquidationPrice: '83',
|
||||||
lowMarginLevel: false,
|
lowMarginLevel: false,
|
||||||
marketId: 'string',
|
marketId: 'string',
|
||||||
marketTradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
marketTradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||||
markPrice: '123', // 12.3
|
markPrice: '123',
|
||||||
notional: '12300', // 1230.0
|
notional: '12300',
|
||||||
openVolume: '100', // 100
|
openVolume: '100',
|
||||||
realisedPNL: '123', // 1.23
|
realisedPNL: '123',
|
||||||
unrealisedPNL: '456', // 4.56
|
unrealisedPNL: '456',
|
||||||
searchPrice: '0',
|
searchPrice: '0',
|
||||||
updatedAt: '2022-07-27T15:02:58.400Z',
|
updatedAt: '2022-07-27T15:02:58.400Z',
|
||||||
|
marginAccountBalance: new BigNumber(123456),
|
||||||
};
|
};
|
||||||
|
|
||||||
const singleRowData = [singleRow];
|
const singleRowData = [singleRow];
|
||||||
@ -42,14 +45,17 @@ it('Render correct columns', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const headers = screen.getAllByRole('columnheader');
|
const headers = screen.getAllByRole('columnheader');
|
||||||
expect(headers).toHaveLength(9);
|
expect(headers).toHaveLength(12);
|
||||||
expect(
|
expect(
|
||||||
headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
|
headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
|
||||||
).toEqual([
|
).toEqual([
|
||||||
'Market',
|
'Market',
|
||||||
'Size',
|
'Notional size',
|
||||||
|
'Open volume',
|
||||||
'Mark price',
|
'Mark price',
|
||||||
|
'Settlement asset',
|
||||||
'Entry price',
|
'Entry price',
|
||||||
|
'Liquidation price (est)',
|
||||||
'Leverage',
|
'Leverage',
|
||||||
'Margin allocated',
|
'Margin allocated',
|
||||||
'Realised PNL',
|
'Realised PNL',
|
||||||
@ -84,22 +90,21 @@ it('add color and sign to amount, displays positive notional value', async () =>
|
|||||||
result = render(<PositionsTable rowData={singleRowData} />);
|
result = render(<PositionsTable rowData={singleRowData} />);
|
||||||
});
|
});
|
||||||
let cells = screen.getAllByRole('gridcell');
|
let cells = screen.getAllByRole('gridcell');
|
||||||
let values = cells[1].querySelectorAll('.text-right');
|
|
||||||
expect(values[0].classList.contains('text-vega-green-dark')).toBeTruthy();
|
expect(cells[2].classList.contains('text-vega-green-dark')).toBeTruthy();
|
||||||
expect(values[0].classList.contains('text-vega-red-dark')).toBeFalsy();
|
expect(cells[2].classList.contains('text-vega-red-dark')).toBeFalsy();
|
||||||
expect(values[0].textContent).toEqual('+100');
|
expect(cells[2].textContent).toEqual('+100');
|
||||||
expect(values[1].textContent).toEqual('1,230.0');
|
expect(cells[1].textContent).toEqual('123.00');
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
result.rerender(
|
result.rerender(
|
||||||
<PositionsTable rowData={[{ ...singleRow, openVolume: '-100' }]} />
|
<PositionsTable rowData={[{ ...singleRow, openVolume: '-100' }]} />
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
cells = screen.getAllByRole('gridcell');
|
cells = screen.getAllByRole('gridcell');
|
||||||
values = cells[1].querySelectorAll('.text-right');
|
expect(cells[2].classList.contains('text-vega-green-dark')).toBeFalsy();
|
||||||
expect(values[0].classList.contains('text-vega-green-dark')).toBeFalsy();
|
expect(cells[2].classList.contains('text-vega-red-dark')).toBeTruthy();
|
||||||
expect(values[0].classList.contains('text-vega-red-dark')).toBeTruthy();
|
expect(cells[2].textContent?.startsWith('-100')).toBeTruthy();
|
||||||
expect(values[0].textContent?.startsWith('-100')).toBeTruthy();
|
expect(cells[1].textContent).toEqual('123.00');
|
||||||
expect(values[1].textContent).toEqual('1,230.0');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays mark price', async () => {
|
it('displays mark price', async () => {
|
||||||
@ -109,7 +114,7 @@ it('displays mark price', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let cells = screen.getAllByRole('gridcell');
|
let cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[2].textContent).toEqual('12.3');
|
expect(cells[3].textContent).toEqual('12.3');
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
result.rerender(
|
result.rerender(
|
||||||
@ -125,7 +130,7 @@ it('displays mark price', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
cells = screen.getAllByRole('gridcell');
|
cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[2].textContent).toEqual('-');
|
expect(cells[3].textContent).toEqual('-');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays properly entry, liquidation price and liquidation bar and it's intent", async () => {
|
it("displays properly entry, liquidation price and liquidation bar and it's intent", async () => {
|
||||||
@ -134,11 +139,10 @@ it("displays properly entry, liquidation price and liquidation bar and it's inte
|
|||||||
result = render(<PositionsTable rowData={singleRowData} />);
|
result = render(<PositionsTable rowData={singleRowData} />);
|
||||||
});
|
});
|
||||||
let cells = screen.getAllByRole('gridcell');
|
let cells = screen.getAllByRole('gridcell');
|
||||||
let cell = cells[3];
|
const entryPrice = cells[5].firstElementChild?.firstElementChild?.textContent;
|
||||||
const entryPrice = cell.firstElementChild?.firstElementChild?.textContent;
|
|
||||||
const liquidationPrice =
|
const liquidationPrice =
|
||||||
cell.firstElementChild?.lastElementChild?.textContent;
|
cells[6].firstElementChild?.lastElementChild?.textContent;
|
||||||
const progressBarTrack = cell.lastElementChild;
|
const progressBarTrack = cells[6].lastElementChild;
|
||||||
let progressBar = progressBarTrack?.firstElementChild as HTMLElement;
|
let progressBar = progressBarTrack?.firstElementChild as HTMLElement;
|
||||||
const progressBarWidth = progressBar?.style?.width;
|
const progressBarWidth = progressBar?.style?.width;
|
||||||
expect(entryPrice).toEqual('13.3');
|
expect(entryPrice).toEqual('13.3');
|
||||||
@ -151,8 +155,7 @@ it("displays properly entry, liquidation price and liquidation bar and it's inte
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
cells = screen.getAllByRole('gridcell');
|
cells = screen.getAllByRole('gridcell');
|
||||||
cell = cells[3];
|
progressBar = cells[6].lastElementChild?.firstElementChild as HTMLElement;
|
||||||
progressBar = cell.lastElementChild?.firstElementChild as HTMLElement;
|
|
||||||
expect(progressBar?.classList.contains('bg-warning')).toEqual(true);
|
expect(progressBar?.classList.contains('bg-warning')).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -161,24 +164,16 @@ it('displays leverage', async () => {
|
|||||||
render(<PositionsTable rowData={singleRowData} />);
|
render(<PositionsTable rowData={singleRowData} />);
|
||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[4].textContent).toEqual('1.1');
|
expect(cells[7].textContent).toEqual('1.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays allocated margin and margin bar', async () => {
|
it('displays allocated margin', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<PositionsTable rowData={singleRowData} />);
|
render(<PositionsTable rowData={singleRowData} />);
|
||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
const cell = cells[5];
|
const cell = cells[8];
|
||||||
const capitalUtilisation =
|
expect(cell.textContent).toEqual('123,456.00');
|
||||||
cell.firstElementChild?.firstElementChild?.textContent;
|
|
||||||
const totalBalance = cell.firstElementChild?.lastElementChild?.textContent;
|
|
||||||
const progressBarTrack = cell.lastElementChild;
|
|
||||||
const progressBar = progressBarTrack?.firstElementChild as HTMLElement;
|
|
||||||
const progressBarWidth = progressBar?.style?.width;
|
|
||||||
expect(capitalUtilisation).toEqual('11.00%');
|
|
||||||
expect(totalBalance).toEqual('1,234.56');
|
|
||||||
expect(progressBarWidth).toEqual('11%');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays realised and unrealised PNL', async () => {
|
it('displays realised and unrealised PNL', async () => {
|
||||||
@ -186,6 +181,6 @@ it('displays realised and unrealised PNL', async () => {
|
|||||||
render(<PositionsTable rowData={singleRowData} />);
|
render(<PositionsTable rowData={singleRowData} />);
|
||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[6].textContent).toEqual('1.23');
|
expect(cells[9].textContent).toEqual('1.23');
|
||||||
expect(cells[7].textContent).toEqual('4.56');
|
expect(cells[10].textContent).toEqual('4.56');
|
||||||
});
|
});
|
||||||
|
@ -2,12 +2,13 @@ import classNames from 'classnames';
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import type {
|
import type {
|
||||||
ValueFormatterParams,
|
|
||||||
ValueGetterParams,
|
|
||||||
ICellRendererParams,
|
ICellRendererParams,
|
||||||
CellRendererSelectorResult,
|
CellRendererSelectorResult,
|
||||||
} from 'ag-grid-community';
|
} from 'ag-grid-community';
|
||||||
import type { ValueProps as PriceCellProps } from '@vegaprotocol/ui-toolkit';
|
import type {
|
||||||
|
ValueProps as PriceCellProps,
|
||||||
|
VegaValueFormatterParams,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { EmptyCell, ProgressBarCell } from '@vegaprotocol/ui-toolkit';
|
import { EmptyCell, ProgressBarCell } from '@vegaprotocol/ui-toolkit';
|
||||||
import {
|
import {
|
||||||
PriceFlashCell,
|
PriceFlashCell,
|
||||||
@ -43,13 +44,6 @@ interface Props extends AgGridReactProps {
|
|||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PositionsTableValueFormatterParams = Omit<
|
|
||||||
ValueFormatterParams,
|
|
||||||
'data' | 'value'
|
|
||||||
> & {
|
|
||||||
data: Position;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface MarketNameCellProps {
|
export interface MarketNameCellProps {
|
||||||
valueFormatted?: [string, string];
|
valueFormatted?: [string, string];
|
||||||
}
|
}
|
||||||
@ -118,7 +112,7 @@ const ButtonCell = ({
|
|||||||
const progressBarValueFormatter = ({
|
const progressBarValueFormatter = ({
|
||||||
data,
|
data,
|
||||||
node,
|
node,
|
||||||
}: PositionsTableValueFormatterParams):
|
}: VegaValueFormatterParams<Position, 'liquidationPrice'>):
|
||||||
| PriceCellProps['valueFormatted']
|
| PriceCellProps['valueFormatted']
|
||||||
| undefined => {
|
| undefined => {
|
||||||
if (!data || node?.rowPinned) {
|
if (!data || node?.rowPinned) {
|
||||||
@ -151,7 +145,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
resizable: true,
|
resizable: true,
|
||||||
tooltipComponent: TooltipCellComponent,
|
tooltipComponent: TooltipCellComponent,
|
||||||
}}
|
}}
|
||||||
components={{ PriceFlashCell, ProgressBarCell }}
|
components={{ AmountCell, PriceFlashCell, ProgressBarCell }}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
@ -160,9 +154,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
cellRenderer={MarketNameCell}
|
cellRenderer={MarketNameCell}
|
||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
value,
|
value,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'marketName'>) => {
|
||||||
value: Position['marketName'];
|
|
||||||
}) => {
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -175,33 +167,38 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
headerName={t('Size')}
|
headerName={t('Notional size')}
|
||||||
field="openVolume"
|
field="notional"
|
||||||
valueGetter={({ node, data }: ValueGetterParams) => {
|
|
||||||
return node?.rowPinned ? data?.notional : data?.openVolume;
|
|
||||||
}}
|
|
||||||
type="rightAligned"
|
type="rightAligned"
|
||||||
cellRendererSelector={(
|
cellClass="font-mono text-right"
|
||||||
params: ICellRendererParams
|
|
||||||
): CellRendererSelectorResult => {
|
|
||||||
return {
|
|
||||||
component: params.node.rowPinned ? PriceFlashCell : AmountCell,
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
node,
|
}: VegaValueFormatterParams<Position, 'notional'>): string => {
|
||||||
}: PositionsTableValueFormatterParams & {
|
if (!value || !data) {
|
||||||
value: Position['openVolume'];
|
return '';
|
||||||
}): AmountCellProps['valueFormatted'] | string => {
|
}
|
||||||
|
return addDecimalsFormatNumber(value, data.decimals);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<AgGridColumn
|
||||||
|
headerName={t('Open volume')}
|
||||||
|
field="openVolume"
|
||||||
|
type="rightAligned"
|
||||||
|
cellClass="font-mono text-right"
|
||||||
|
cellClassRules={signedNumberCssClassRules}
|
||||||
|
valueFormatter={({
|
||||||
|
value,
|
||||||
|
data,
|
||||||
|
}: VegaValueFormatterParams<Position, 'openVolume'>):
|
||||||
|
| string
|
||||||
|
| undefined => {
|
||||||
if (!value || !data) {
|
if (!value || !data) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (node?.rowPinned) {
|
return volumePrefix(
|
||||||
return addDecimalsFormatNumber(value, data.decimals);
|
addDecimalsFormatNumber(value, data.positionDecimalPlaces)
|
||||||
}
|
);
|
||||||
return data;
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
@ -219,9 +216,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
node,
|
node,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'markPrice'>) => {
|
||||||
value: Position['markPrice'];
|
|
||||||
}) => {
|
|
||||||
if (!data || !value || node?.rowPinned) {
|
if (!data || !value || node?.rowPinned) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -237,16 +232,34 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<AgGridColumn headerName={t('Settlement asset')} field="assetSymbol" />
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
headerName={t('Entry price')}
|
headerName={t('Entry price')}
|
||||||
field="averageEntryPrice"
|
field="averageEntryPrice"
|
||||||
headerComponentParams={{
|
type="rightAligned"
|
||||||
template:
|
cellRendererSelector={(
|
||||||
'<div class="ag-cell-label-container" role="presentation">' +
|
params: ICellRendererParams
|
||||||
` <span>${t('Liquidation price (est)')}</span>` +
|
): CellRendererSelectorResult => {
|
||||||
' <span ref="eText" class="ag-header-cell-text"></span>' +
|
return {
|
||||||
'</div>',
|
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
|
||||||
|
};
|
||||||
}}
|
}}
|
||||||
|
valueFormatter={({
|
||||||
|
data,
|
||||||
|
value,
|
||||||
|
node,
|
||||||
|
}: VegaValueFormatterParams<Position, 'averageEntryPrice'>):
|
||||||
|
| string
|
||||||
|
| undefined => {
|
||||||
|
if (!data || node?.rowPinned || !value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return addDecimalsFormatNumber(value, data.marketDecimalPlaces);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<AgGridColumn
|
||||||
|
headerName={t('Liquidation price (est)')}
|
||||||
|
field="liquidationPrice"
|
||||||
flex={2}
|
flex={2}
|
||||||
headerTooltip={t(
|
headerTooltip={t(
|
||||||
'Liquidation prices are based on the amount of collateral you have available, the risk of your position and the liquidity on the order book. They can change rapidly based on the profit and loss of your positions and any changes to collateral from opening/closing other positions and making deposits/withdrawals.'
|
'Liquidation prices are based on the amount of collateral you have available, the risk of your position and the liquidity on the order book. They can change rapidly based on the profit and loss of your positions and any changes to collateral from opening/closing other positions and making deposits/withdrawals.'
|
||||||
@ -273,41 +286,32 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
}}
|
}}
|
||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
value,
|
value,
|
||||||
node,
|
}: VegaValueFormatterParams<Position, 'currentLeverage'>) =>
|
||||||
}: PositionsTableValueFormatterParams & {
|
|
||||||
value: Position['currentLeverage'];
|
|
||||||
}) =>
|
|
||||||
value === undefined ? undefined : formatNumber(value.toString(), 1)
|
value === undefined ? undefined : formatNumber(value.toString(), 1)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
headerName={t('Margin allocated')}
|
headerName={t('Margin allocated')}
|
||||||
field="capitalUtilisation"
|
field="marginAccountBalance"
|
||||||
type="rightAligned"
|
type="rightAligned"
|
||||||
flex={2}
|
|
||||||
cellRenderer="ProgressBarCell"
|
|
||||||
cellRendererSelector={(
|
cellRendererSelector={(
|
||||||
params: ICellRendererParams
|
params: ICellRendererParams
|
||||||
): CellRendererSelectorResult => {
|
): CellRendererSelectorResult => {
|
||||||
return {
|
return {
|
||||||
component: params.node.rowPinned ? EmptyCell : ProgressBarCell,
|
component: params.node.rowPinned ? EmptyCell : PriceFlashCell,
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
data,
|
data,
|
||||||
value,
|
value,
|
||||||
node,
|
node,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'marginAccountBalance'>):
|
||||||
value: Position['capitalUtilisation'];
|
| string
|
||||||
}): PriceCellProps['valueFormatted'] | undefined => {
|
| undefined => {
|
||||||
if (!data || node?.rowPinned) {
|
if (!data || node?.rowPinned || !value) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return formatNumber(value, data.decimals);
|
||||||
low: `${formatNumber(value, 2)}%`,
|
|
||||||
high: addDecimalsFormatNumber(data.totalBalance, data.decimals),
|
|
||||||
value: Number(value),
|
|
||||||
};
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
@ -318,10 +322,8 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'realisedPNL'>) =>
|
||||||
value: Position['realisedPNL'];
|
value === undefined || data === undefined
|
||||||
}) =>
|
|
||||||
value === undefined
|
|
||||||
? undefined
|
? undefined
|
||||||
: addDecimalsFormatNumber(value.toString(), data.decimals)
|
: addDecimalsFormatNumber(value.toString(), data.decimals)
|
||||||
}
|
}
|
||||||
@ -338,10 +340,8 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
value,
|
value,
|
||||||
data,
|
data,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
|
||||||
value: Position['unrealisedPNL'];
|
value === undefined || data === undefined
|
||||||
}) =>
|
|
||||||
value === undefined
|
|
||||||
? undefined
|
? undefined
|
||||||
: addDecimalsFormatNumber(value.toString(), data.decimals)
|
: addDecimalsFormatNumber(value.toString(), data.decimals)
|
||||||
}
|
}
|
||||||
@ -356,9 +356,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
|
|||||||
type="rightAligned"
|
type="rightAligned"
|
||||||
valueFormatter={({
|
valueFormatter={({
|
||||||
value,
|
value,
|
||||||
}: PositionsTableValueFormatterParams & {
|
}: VegaValueFormatterParams<Position, 'updatedAt'>) => {
|
||||||
value: Position['updatedAt'];
|
|
||||||
}) => {
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user