chore(trading): remove unnecessary forwarding of grid refs (#4486)

This commit is contained in:
Matthew Russell 2023-08-04 11:08:28 +01:00 committed by GitHub
parent 015e0188ec
commit 1041864ad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1082 additions and 1181 deletions

View File

@ -4,12 +4,9 @@ import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useSidebar, ViewType } from '../../components/sidebar'; import { useSidebar, ViewType } from '../../components/sidebar';
export const DepositsContainer = () => { export const DepositsContainer = () => {
const gridRef = useRef<AgGridReact | null>(null);
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
const { data, error } = useDataProvider({ const { data, error } = useDataProvider({
dataProvider: depositsProvider, dataProvider: depositsProvider,
@ -24,7 +21,6 @@ export const DepositsContainer = () => {
<div className="h-full"> <div className="h-full">
<DepositsTable <DepositsTable
rowData={data} rowData={data}
ref={gridRef}
overlayNoRowsTemplate={error ? error.message : t('No deposits')} overlayNoRowsTemplate={error ? error.message : t('No deposits')}
/> />
{!isReadOnly && ( {!isReadOnly && (

View File

@ -118,7 +118,6 @@ export const AccountManager = ({
onMarketClick, onMarketClick,
gridProps, gridProps,
}: AccountManagerProps) => { }: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const [breakdownAssetId, setBreakdownAssetId] = useState<string>(); const [breakdownAssetId, setBreakdownAssetId] = useState<string>();
const { data, error } = useDataProvider({ const { data, error } = useDataProvider({
dataProvider: aggregatedAccountsDataProvider, dataProvider: aggregatedAccountsDataProvider,
@ -138,7 +137,6 @@ export const AccountManager = ({
return ( return (
<div className="relative h-full"> <div className="relative h-full">
<AccountTable <AccountTable
ref={gridRef}
rowData={data} rowData={data}
onClickAsset={onClickAsset} onClickAsset={onClickAsset}
onClickDeposit={onClickDeposit} onClickDeposit={onClickDeposit}

View File

@ -1,4 +1,4 @@
import { forwardRef, useMemo, useCallback } from 'react'; import { useMemo, useCallback } from 'react';
import { import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
addDecimalsFormatNumberQuantum, addDecimalsFormatNumberQuantum,
@ -21,7 +21,7 @@ import type {
RowHeightParams, RowHeightParams,
ColDef, ColDef,
} from 'ag-grid-community'; } from 'ag-grid-community';
import type { AgGridReact, AgGridReactProps } from 'ag-grid-react'; import type { AgGridReactProps } from 'ag-grid-react';
import type { AccountFields } from './accounts-data-provider'; import type { AccountFields } from './accounts-data-provider';
import type { Asset } from '@vegaprotocol/types'; import type { Asset } from '@vegaprotocol/types';
import { CenteredGridCellWrapper } from '@vegaprotocol/datagrid'; import { CenteredGridCellWrapper } from '@vegaprotocol/datagrid';
@ -75,256 +75,250 @@ export interface AccountTableProps extends AgGridReactProps {
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
} }
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>( export const AccountTable = ({
( onClickAsset,
{ onClickWithdraw,
onClickAsset, onClickDeposit,
onClickWithdraw, onClickBreakdown,
onClickDeposit, onClickTransfer,
onClickBreakdown, rowData,
onClickTransfer, isReadOnly,
rowData, pinnedAsset,
isReadOnly, ...props
pinnedAsset, }: AccountTableProps) => {
...props const pinnedRow = useMemo(() => {
if (!pinnedAsset) {
return;
}
const currentPinnedAssetRow = rowData?.find(
(row) => row.asset.id === pinnedAsset?.id
);
if (!currentPinnedAssetRow) {
return {
asset: pinnedAsset,
available: '0',
used: '0',
total: '0',
balance: '0',
};
}
return currentPinnedAssetRow;
}, [pinnedAsset, rowData]);
const { getRowHeight } = props;
const getPinnedAssetRowHeight = useCallback(
(params: RowHeightParams) => {
if (
params.node.rowPinned &&
params.data.asset.id === pinnedAsset?.id &&
new BigNumber(params.data.total).isLessThanOrEqualTo(0)
) {
return 32;
}
return getRowHeight ? getRowHeight(params) : undefined;
}, },
ref [pinnedAsset?.id, getRowHeight]
) => { );
const pinnedRow = useMemo(() => {
if (!pinnedAsset) {
return;
}
const currentPinnedAssetRow = rowData?.find(
(row) => row.asset.id === pinnedAsset?.id
);
if (!currentPinnedAssetRow) {
return {
asset: pinnedAsset,
available: '0',
used: '0',
total: '0',
balance: '0',
};
}
return currentPinnedAssetRow;
}, [pinnedAsset, rowData]);
const { getRowHeight } = props; const showDepositButton = pinnedRow?.balance === '0';
const getPinnedAssetRowHeight = useCallback( const colDefs = useMemo(() => {
(params: RowHeightParams) => { const defs: ColDef[] = [
if ( {
params.node.rowPinned && headerName: t('Asset'),
params.data.asset.id === pinnedAsset?.id && field: 'asset.symbol',
new BigNumber(params.data.total).isLessThanOrEqualTo(0) headerTooltip: t(
) { 'Asset is the collateral that is deposited into the Vega protocol.'
return 32; ),
} cellClass: 'underline',
return getRowHeight ? getRowHeight(params) : undefined; onCellClicked: ({ data }) => {
if (data) {
onClickAsset(data.asset.id);
}
},
}, },
[pinnedAsset?.id, getRowHeight] {
); headerName: t('Used'),
type: 'rightAligned',
const showDepositButton = pinnedRow?.balance === '0'; field: 'used',
headerTooltip: t(
const colDefs = useMemo(() => { 'Currently allocated to a market as margin or bond. Check the breakdown for details.'
const defs: ColDef[] = [ ),
{ tooltipValueGetter: ({ value, data }) => {
headerName: t('Asset'), if (!value || !data) return null;
field: 'asset.symbol', return addDecimalsFormatNumber(value, data.asset.decimals);
headerTooltip: t(
'Asset is the collateral that is deposited into the Vega protocol.'
),
cellClass: 'underline',
onCellClicked: ({ data }) => {
if (data) {
onClickAsset(data.asset.id);
}
},
}, },
{ onCellClicked: ({ data }) => {
headerName: t('Used'), if (!data || !onClickBreakdown) return;
type: 'rightAligned', onClickBreakdown(data.asset.id);
field: 'used', },
headerTooltip: t( cellRenderer: ({
'Currently allocated to a market as margin or bond. Check the breakdown for details.' data,
), value,
tooltipValueGetter: ({ value, data }) => { }: VegaICellRendererParams<AccountFields, 'used'>) => {
if (!value || !data) return null; if (!value || !data) return '-';
return addDecimalsFormatNumber(value, data.asset.decimals); const percentageUsed = percentageValue(value, data.total);
}, const valueFormatted = addDecimalsFormatNumberQuantum(
onCellClicked: ({ data }) => {
if (!data || !onClickBreakdown) return;
onClickBreakdown(data.asset.id);
},
cellRenderer: ({
data,
value, value,
}: VegaICellRendererParams<AccountFields, 'used'>) => { data.asset.decimals,
if (!value || !data) return '-'; data.asset.quantum
const percentageUsed = percentageValue(value, data.total); );
const valueFormatted = addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
return data.breakdown ? ( return data.breakdown ? (
<> <>
<span className="underline">{valueFormatted}</span> <span className="underline">{valueFormatted}</span>
<span <span
className={classNames( className={classNames(
colorClass(percentageUsed), colorClass(percentageUsed),
'ml-1 inline-block w-14' 'ml-1 inline-block w-14'
)} )}
>
{percentageUsed.toFixed(2)}%
</span>
</>
) : (
<>
<span className="underline">{valueFormatted}</span>
<span className="ml-2 inline-block w-14 text-muted">
{t('0.00%')}
</span>
</>
);
},
},
{
headerName: t('Available'),
field: 'available',
type: 'rightAligned',
headerTooltip: t(
'Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
cellClass: ({ data }) => {
const percentageUsed = percentageValue(data?.used, data?.total);
return colorClass(percentageUsed, true);
},
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<AccountFields, 'available'>) => {
if (!value || !data) return '-';
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
},
},
{
headerName: t('Total'),
type: 'rightAligned',
field: 'total',
headerTooltip: t(
'The total amount of each asset on this key. Includes used and available collateral.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
valueFormatter: ({
data,
value,
}: VegaValueFormatterParams<AccountFields, 'total'>) => {
if (!data || !value) return '-';
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
},
},
{
colId: 'accounts-actions',
field: 'asset.id',
...COL_DEFS.actions,
minWidth: showDepositButton ? 130 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 130 : COL_DEFS.actions.maxWidth,
cellRenderer: ({
value: assetId,
node,
}: VegaICellRendererParams<AccountFields, 'asset.id'>) => {
if (!assetId) return null;
if (node.rowPinned && node.data?.total === '0') {
return (
<CenteredGridCellWrapper className="h-[30px] justify-end py-1">
<Button
size="xs"
variant="primary"
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(assetId);
}}
> >
{percentageUsed.toFixed(2)}% <VegaIcon name={VegaIconNames.DEPOSIT} /> {t('Deposit')}
</span> </Button>
</> </CenteredGridCellWrapper>
) : (
<>
<span className="underline">{valueFormatted}</span>
<span className="ml-2 inline-block w-14 text-muted">
{t('0.00%')}
</span>
</>
); );
}, }
}, return isReadOnly ? null : (
{ <AccountsActionsDropdown
headerName: t('Available'), assetId={assetId}
field: 'available', assetContractAddress={
type: 'rightAligned', node.data?.asset.source?.__typename === 'ERC20'
headerTooltip: t( ? node.data.asset.source.contractAddress
'Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.' : undefined
), }
tooltipValueGetter: ({ value, data }) => { onClickDeposit={() => {
if (!value || !data) return null; onClickDeposit && onClickDeposit(assetId);
return addDecimalsFormatNumber(value, data.asset.decimals); }}
}, onClickWithdraw={() => {
cellClass: ({ data }) => { onClickWithdraw && onClickWithdraw(assetId);
const percentageUsed = percentageValue(data?.used, data?.total); }}
return colorClass(percentageUsed, true); onClickBreakdown={() => {
}, onClickBreakdown && onClickBreakdown(assetId);
valueFormatter: ({ }}
value, onClickTransfer={() => {
data, onClickTransfer && onClickTransfer(assetId);
}: VegaValueFormatterParams<AccountFields, 'available'>) => { }}
if (!value || !data) return '-'; />
return addDecimalsFormatNumberQuantum( );
value,
data.asset.decimals,
data.asset.quantum
);
},
}, },
},
];
return defs;
}, [
onClickAsset,
onClickBreakdown,
onClickDeposit,
onClickWithdraw,
onClickTransfer,
isReadOnly,
showDepositButton,
]);
{ const data = rowData?.filter((data) => data.asset.id !== pinnedAsset?.id);
headerName: t('Total'),
type: 'rightAligned',
field: 'total',
headerTooltip: t(
'The total amount of each asset on this key. Includes used and available collateral.'
),
tooltipValueGetter: ({ value, data }) => {
if (!value || !data) return null;
return addDecimalsFormatNumber(value, data.asset.decimals);
},
valueFormatter: ({
data,
value,
}: VegaValueFormatterParams<AccountFields, 'total'>) => {
if (!data || !value) return '-';
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
data.asset.quantum
);
},
},
{
colId: 'accounts-actions',
field: 'asset.id',
...COL_DEFS.actions,
minWidth: showDepositButton ? 130 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 130 : COL_DEFS.actions.maxWidth,
cellRenderer: ({
value: assetId,
node,
}: VegaICellRendererParams<AccountFields, 'asset.id'>) => {
if (!assetId) return null;
if (node.rowPinned && node.data?.total === '0') {
return (
<CenteredGridCellWrapper className="h-[30px] justify-end py-1">
<Button
size="xs"
variant="primary"
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(assetId);
}}
>
<VegaIcon name={VegaIconNames.DEPOSIT} /> {t('Deposit')}
</Button>
</CenteredGridCellWrapper>
);
}
return isReadOnly ? null : (
<AccountsActionsDropdown
assetId={assetId}
assetContractAddress={
node.data?.asset.source?.__typename === 'ERC20'
? node.data.asset.source.contractAddress
: undefined
}
onClickDeposit={() => {
onClickDeposit && onClickDeposit(assetId);
}}
onClickWithdraw={() => {
onClickWithdraw && onClickWithdraw(assetId);
}}
onClickBreakdown={() => {
onClickBreakdown && onClickBreakdown(assetId);
}}
onClickTransfer={() => {
onClickTransfer && onClickTransfer(assetId);
}}
/>
);
},
},
];
return defs;
}, [
onClickAsset,
onClickBreakdown,
onClickDeposit,
onClickWithdraw,
onClickTransfer,
isReadOnly,
showDepositButton,
]);
const data = rowData?.filter((data) => data.asset.id !== pinnedAsset?.id); return (
<AgGrid
return ( {...props}
<AgGrid style={{ width: '100%', height: '100%' }}
{...props} getRowId={({ data }: { data: AccountFields }) => data.asset.id}
style={{ width: '100%', height: '100%' }} tooltipShowDelay={500}
getRowId={({ data }: { data: AccountFields }) => data.asset.id} rowData={data}
ref={ref} defaultColDef={{
tooltipShowDelay={500} resizable: true,
rowData={data} tooltipComponent: TooltipCellComponent,
defaultColDef={{ sortable: true,
resizable: true, comparator: accountValuesComparator,
tooltipComponent: TooltipCellComponent, }}
sortable: true, columnDefs={colDefs}
comparator: accountValuesComparator, getRowHeight={getPinnedAssetRowHeight}
}} pinnedTopRowData={pinnedRow ? [pinnedRow] : undefined}
columnDefs={colDefs} />
getRowHeight={getPinnedAssetRowHeight} );
pinnedTopRowData={pinnedRow ? [pinnedRow] : undefined} };
/>
);
}
);

View File

@ -21,5 +21,4 @@ export * from './lib/type-helpers';
export * from './lib/cells/grid-progress-bar'; export * from './lib/cells/grid-progress-bar';
export * from './lib/ag-grid-update';
export * from './lib/use-datagrid-events'; export * from './lib/use-datagrid-events';

View File

@ -1,20 +0,0 @@
import type { MutableRefObject, RefObject } from 'react';
import type { AgGridReact } from 'ag-grid-react';
type AnyArray = Array<any> | null; // eslint-disable-line @typescript-eslint/no-explicit-any
export const isXOrWasEmpty = (prev?: AnyArray, curr?: AnyArray) =>
Boolean(Number(!!prev?.length) ^ Number(!!curr?.length));
export const updateGridData = (
dataRef: MutableRefObject<AnyArray>,
data: AnyArray,
gridRef: RefObject<AgGridReact>
) => {
const rerender = isXOrWasEmpty(dataRef.current, data);
dataRef.current = data;
if (gridRef.current?.api?.getModel().getType() === 'infinite') {
gridRef.current?.api?.refreshInfiniteCache();
}
return !rerender;
};

View File

@ -1,4 +1,4 @@
import { forwardRef, useMemo } from 'react'; import { useMemo } from 'react';
import { import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
getDateTimeFormat, getDateTimeFormat,
@ -6,7 +6,6 @@ import {
isNumeric, isNumeric,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import type { import type {
VegaICellRendererParams, VegaICellRendererParams,
@ -17,10 +16,9 @@ import type { DepositFieldsFragment } from './__generated__/Deposit';
import { EtherscanLink } from '@vegaprotocol/environment'; import { EtherscanLink } from '@vegaprotocol/environment';
import { DepositStatusMapping } from '@vegaprotocol/types'; import { DepositStatusMapping } from '@vegaprotocol/types';
export const DepositsTable = forwardRef< export const DepositsTable = (
AgGridReact, props: TypedDataAgGrid<DepositFieldsFragment>
TypedDataAgGrid<DepositFieldsFragment> ) => {
>((props, ref) => {
const columnDefs = useMemo<ColDef[]>( const columnDefs = useMemo<ColDef[]>(
() => [ () => [
{ headerName: 'Asset', field: 'asset.symbol' }, { headerName: 'Asset', field: 'asset.symbol' },
@ -79,11 +77,10 @@ export const DepositsTable = forwardRef<
); );
return ( return (
<AgGrid <AgGrid
ref={ref}
defaultColDef={{ flex: 1 }} defaultColDef={{ flex: 1 }}
columnDefs={columnDefs} columnDefs={columnDefs}
style={{ width: '100%', height: '100%' }} style={{ width: '100%', height: '100%' }}
{...props} {...props}
/> />
); );
}); };

View File

@ -1,8 +1,7 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type * as Schema from '@vegaprotocol/types'; import type * as Schema from '@vegaprotocol/types';
import type { FilterChangedEvent } from 'ag-grid-community'; import type { FilterChangedEvent } from 'ag-grid-community';
import type { AgGridReact } from 'ag-grid-react'; import { useCallback, useState, useMemo } from 'react';
import { useCallback, useRef, useState, useMemo } from 'react';
import { subDays, formatRFC3339 } from 'date-fns'; import { subDays, formatRFC3339 } from 'date-fns';
import { ledgerEntriesProvider } from './ledger-entries-data-provider'; import { ledgerEntriesProvider } from './ledger-entries-data-provider';
import type { LedgerEntriesQueryVariables } from './__generated__/LedgerEntries'; import type { LedgerEntriesQueryVariables } from './__generated__/LedgerEntries';
@ -32,7 +31,6 @@ export const LedgerManager = ({
partyId: string; partyId: string;
gridProps: ReturnType<typeof useDataGridEvents>; gridProps: ReturnType<typeof useDataGridEvents>;
}) => { }) => {
const gridRef = useRef<AgGridReact | null>(null);
const [filter, setFilter] = useState<Filter>(defaultFilter); const [filter, setFilter] = useState<Filter>(defaultFilter);
const variables = useMemo<LedgerEntriesQueryVariables>( const variables = useMemo<LedgerEntriesQueryVariables>(
@ -64,7 +62,6 @@ export const LedgerManager = ({
return ( return (
<div className="h-full relative"> <div className="h-full relative">
<LedgerTable <LedgerTable
ref={gridRef}
rowData={data} rowData={data}
overlayNoRowsTemplate={error ? error.message : t('No entries')} overlayNoRowsTemplate={error ? error.message : t('No entries')}
{...gridProps} {...gridProps}

View File

@ -14,7 +14,6 @@ import {
DateRangeFilter, DateRangeFilter,
SetFilter, SetFilter,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import type { AgGridReact } from 'ag-grid-react';
import type * as Types from '@vegaprotocol/types'; import type * as Types from '@vegaprotocol/types';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { import {
@ -23,7 +22,7 @@ import {
TransferTypeMapping, TransferTypeMapping,
} from '@vegaprotocol/types'; } from '@vegaprotocol/types';
import type { LedgerEntry } from './ledger-entries-data-provider'; import type { LedgerEntry } from './ledger-entries-data-provider';
import { forwardRef, useMemo } from 'react'; import { useMemo } from 'react';
import { formatRFC3339, subDays } from 'date-fns'; import { formatRFC3339, subDays } from 'date-fns';
export const TransferTooltipCellComponent = ({ export const TransferTooltipCellComponent = ({
@ -45,162 +44,159 @@ const dateRangeFilterParams = {
}; };
type LedgerEntryProps = TypedDataAgGrid<LedgerEntry>; type LedgerEntryProps = TypedDataAgGrid<LedgerEntry>;
export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>( export const LedgerTable = (props: LedgerEntryProps) => {
(props, ref) => { const columnDefs = useMemo<ColDef[]>(
const columnDefs = useMemo<ColDef[]>( () => [
() => [ {
{ headerName: t('Sender'),
headerName: t('Sender'), field: 'fromAccountPartyId',
field: 'fromAccountPartyId', cellRenderer: ({
cellRenderer: ({ value,
value, }: VegaValueFormatterParams<LedgerEntry, 'fromAccountPartyId'>) =>
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountPartyId'>) => truncateByChars(value || ''),
truncateByChars(value || ''), },
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
}, },
{ field: 'fromAccountType',
headerName: t('Account type'), cellRenderer: ({
filter: SetFilter, value,
filterParams: { }: VegaValueFormatterParams<LedgerEntry, 'fromAccountType'>) =>
set: AccountTypeMapping, value ? AccountTypeMapping[value] : '-',
}, },
field: 'fromAccountType', {
cellRenderer: ({ headerName: t('Market'),
value, field: 'marketSender.tradableInstrument.instrument.code',
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountType'>) => cellRenderer: ({
value ? AccountTypeMapping[value] : '-', value,
}: VegaValueFormatterParams<
LedgerEntry,
'marketSender.tradableInstrument.instrument.code'
>) => value || '-',
},
{
headerName: t('Receiver'),
field: 'toAccountPartyId',
cellRenderer: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountPartyId'>) =>
truncateByChars(value || ''),
},
{
headerName: t('Account type'),
filter: SetFilter,
filterParams: {
set: AccountTypeMapping,
}, },
{ field: 'toAccountType',
headerName: t('Market'), cellRenderer: ({
field: 'marketSender.tradableInstrument.instrument.code', value,
cellRenderer: ({ }: VegaValueFormatterParams<LedgerEntry, 'toAccountType'>) =>
value, value ? AccountTypeMapping[value] : '-',
}: VegaValueFormatterParams< },
LedgerEntry, {
'marketSender.tradableInstrument.instrument.code' headerName: t('Market'),
>) => value || '-', field: 'marketReceiver.tradableInstrument.instrument.code',
cellRenderer: ({
value,
}: VegaValueFormatterParams<
LedgerEntry,
'marketReceiver.tradableInstrument.instrument.code'
>) => value || '-',
},
{
headerName: t('Transfer type'),
field: 'transferType',
tooltipField: 'transferType',
filter: SetFilter,
filterParams: {
set: TransferTypeMapping,
}, },
{ valueFormatter: ({
headerName: t('Receiver'), value,
field: 'toAccountPartyId', }: VegaValueFormatterParams<LedgerEntry, 'transferType'>) =>
cellRenderer: ({ value ? TransferTypeMapping[value] : '',
value, },
}: VegaValueFormatterParams<LedgerEntry, 'toAccountPartyId'>) => {
truncateByChars(value || ''), headerName: t('Quantity'),
field: 'quantity',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'quantity'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
}, },
{ },
headerName: t('Account type'), {
filter: SetFilter, headerName: t('Asset'),
filterParams: { field: 'assetId',
set: AccountTypeMapping, valueFormatter: ({
}, value,
field: 'toAccountType', data,
cellRenderer: ({ }: VegaValueFormatterParams<LedgerEntry, 'asset'>) =>
value, data?.asset?.symbol || '',
}: VegaValueFormatterParams<LedgerEntry, 'toAccountType'>) => },
value ? AccountTypeMapping[value] : '-', {
headerName: t('Sender account balance'),
field: 'fromAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
}, },
{ },
headerName: t('Market'), {
field: 'marketReceiver.tradableInstrument.instrument.code', headerName: t('Receiver account balance'),
cellRenderer: ({ field: 'toAccountBalance',
value, valueFormatter: ({
}: VegaValueFormatterParams< value,
LedgerEntry, data,
'marketReceiver.tradableInstrument.instrument.code' }: VegaValueFormatterParams<LedgerEntry, 'toAccountBalance'>) => {
>) => value || '-', const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
}, },
{ },
headerName: t('Transfer type'), {
field: 'transferType', headerName: t('Vega time'),
tooltipField: 'transferType', field: 'vegaTime',
filter: SetFilter, valueFormatter: ({
filterParams: { value,
set: TransferTypeMapping, }: VegaValueFormatterParams<LedgerEntry, 'vegaTime'>) =>
}, value ? getDateTimeFormat().format(fromNanoSeconds(value)) : '-',
valueFormatter: ({ filterParams: dateRangeFilterParams,
value, filter: DateRangeFilter,
}: VegaValueFormatterParams<LedgerEntry, 'transferType'>) => flex: 1,
value ? TransferTypeMapping[value] : '', },
],
[]
);
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
sortable: true,
tooltipComponent: TransferTooltipCellComponent,
filterParams: {
...dateRangeFilterParams,
buttons: ['reset'],
}, },
{ }}
headerName: t('Quantity'), columnDefs={columnDefs}
field: 'quantity', {...props}
valueFormatter: ({ />
value, );
data, };
}: VegaValueFormatterParams<LedgerEntry, 'quantity'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Asset'),
field: 'assetId',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'asset'>) =>
data?.asset?.symbol || '',
},
{
headerName: t('Sender account balance'),
field: 'fromAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'fromAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Receiver account balance'),
field: 'toAccountBalance',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<LedgerEntry, 'toAccountBalance'>) => {
const assetDecimalPlaces = data?.asset?.decimals || 0;
return value
? addDecimalsFormatNumber(value, assetDecimalPlaces)
: '';
},
},
{
headerName: t('Vega time'),
field: 'vegaTime',
valueFormatter: ({
value,
}: VegaValueFormatterParams<LedgerEntry, 'vegaTime'>) =>
value ? getDateTimeFormat().format(fromNanoSeconds(value)) : '-',
filterParams: dateRangeFilterParams,
filter: DateRangeFilter,
flex: 1,
},
],
[]
);
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
ref={ref}
tooltipShowDelay={500}
defaultColDef={{
resizable: true,
sortable: true,
tooltipComponent: TransferTooltipCellComponent,
filterParams: {
...dateRangeFilterParams,
buttons: ['reset'],
},
}}
columnDefs={columnDefs}
{...props}
/>
);
}
);

View File

@ -1,11 +1,9 @@
import React, { forwardRef } from 'react';
import type { TypedDataAgGrid } from '@vegaprotocol/datagrid'; import type { TypedDataAgGrid } from '@vegaprotocol/datagrid';
import { import {
AgGridLazy as AgGrid, AgGridLazy as AgGrid,
PriceFlashCell, PriceFlashCell,
MarketNameCell, MarketNameCell,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import type { AgGridReact } from 'ag-grid-react';
import type { MarketMaybeWithData } from '../../markets-provider'; import type { MarketMaybeWithData } from '../../markets-provider';
import { OracleStatus } from './oracle-status'; import { OracleStatus } from './oracle-status';
import { useColumnDefs } from './use-column-defs'; import { useColumnDefs } from './use-column-defs';
@ -43,14 +41,15 @@ const defaultColDef = {
filterParams: { buttons: ['reset'] }, filterParams: { buttons: ['reset'] },
minWidth: 100, minWidth: 100,
}; };
type Props = TypedDataAgGrid<MarketMaybeWithData> & {
export const MarketListTable = forwardRef< onMarketClick: (marketId: string, metaKey?: boolean) => void;
AgGridReact, SuccessorMarketRenderer?: React.FC<{ value: string }>;
TypedDataAgGrid<MarketMaybeWithData> & { };
onMarketClick: (marketId: string, metaKey?: boolean) => void; export const MarketListTable = ({
SuccessorMarketRenderer?: React.FC<{ value: string }>; onMarketClick,
} SuccessorMarketRenderer,
>(({ onMarketClick, SuccessorMarketRenderer, ...props }, ref) => { ...props
}: Props) => {
const columnDefs = useColumnDefs({ onMarketClick }); const columnDefs = useColumnDefs({ onMarketClick });
const components = { const components = {
PriceFlashCell, PriceFlashCell,
@ -61,7 +60,6 @@ export const MarketListTable = forwardRef<
<AgGrid <AgGrid
style={{ width: '100%', height: '100%' }} style={{ width: '100%', height: '100%' }}
getRowId={getRowId} getRowId={getRowId}
ref={ref}
defaultColDef={defaultColDef} defaultColDef={defaultColDef}
columnDefs={columnDefs} columnDefs={columnDefs}
suppressCellFocus suppressCellFocus
@ -69,6 +67,6 @@ export const MarketListTable = forwardRef<
{...props} {...props}
/> />
); );
}); };
export default MarketListTable; export default MarketListTable;

View File

@ -1,6 +1,5 @@
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import React, { useEffect, useRef } from 'react'; import React, { useEffect } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import type { CellClickedEvent } from 'ag-grid-community'; import type { CellClickedEvent } from 'ag-grid-community';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { MarketListTable } from './market-list-table'; import { MarketListTable } from './market-list-table';
@ -18,8 +17,6 @@ export const MarketsContainer = ({
onSelect, onSelect,
SuccessorMarketRenderer, SuccessorMarketRenderer,
}: MarketsContainerProps) => { }: MarketsContainerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data, error, reload } = useDataProvider({ const { data, error, reload } = useDataProvider({
dataProvider, dataProvider,
variables: undefined, variables: undefined,
@ -37,7 +34,6 @@ export const MarketsContainer = ({
return ( return (
<div className="h-full relative"> <div className="h-full relative">
<MarketListTable <MarketListTable
ref={gridRef}
rowData={data} rowData={data}
onCellClicked={(cellEvent: CellClickedEvent) => { onCellClicked={(cellEvent: CellClickedEvent) => {
const { data, column, event } = cellEvent; const { data, column, event } = cellEvent;

View File

@ -2,17 +2,13 @@ import { render, screen, act } from '@testing-library/react';
import { StopOrdersManager } from './stop-orders-manager'; import { StopOrdersManager } from './stop-orders-manager';
import * as useDataProviderHook from '@vegaprotocol/data-provider'; import * as useDataProviderHook from '@vegaprotocol/data-provider';
import type { StopOrder } from '../order-data-provider/stop-orders-data-provider'; import type { StopOrder } from '../order-data-provider/stop-orders-data-provider';
import * as stopOrdersTableMock from '../stop-orders-table/stop-orders-table';
import { forwardRef } from 'react';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet';
import { MockedProvider } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing';
// @ts-ignore StopOrdersTable is read only but we need to override with the forwardRef to jest.mock('../stop-orders-table/stop-orders-table', () => ({
// avoid warnings about padding refs StopOrdersTable: () => <div>StopOrdersTable</div>,
stopOrdersTableMock.StopOrdersTable = forwardRef(() => ( }));
<div>StopOrdersTable</div>
));
const generateJsx = () => { const generateJsx = () => {
const pubKey = '0x123'; const pubKey = '0x123';

View File

@ -8,7 +8,7 @@ import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { ForwardedRef } from 'react'; import type { ForwardedRef } from 'react';
import { memo, forwardRef, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { import {
AgGridLazy as AgGrid, AgGridLazy as AgGrid,
SetFilter, SetFilter,
@ -36,265 +36,252 @@ export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
export const StopOrdersTable = memo< export const StopOrdersTable = memo<
StopOrdersTableProps & { ref?: ForwardedRef<AgGridReact> } StopOrdersTableProps & { ref?: ForwardedRef<AgGridReact> }
>( >(({ onCancel, onMarketClick, ...props }: StopOrdersTableProps) => {
forwardRef<AgGridReact, StopOrdersTableProps>( const showAllActions = !props.isReadOnly;
({ onCancel, onMarketClick, ...props }, ref) => { const columnDefs: ColDef[] = useMemo(
const showAllActions = !props.isReadOnly; () => [
const columnDefs: ColDef[] = useMemo( {
() => [ headerName: t('Market'),
{ field: 'market.tradableInstrument.instrument.code',
headerName: t('Market'), cellRenderer: 'MarketNameCell',
field: 'market.tradableInstrument.instrument.code', cellRendererParams: { idPath: 'market.id', onMarketClick },
cellRenderer: 'MarketNameCell', minWidth: 150,
cellRendererParams: { idPath: 'market.id', onMarketClick }, },
minWidth: 150, {
}, headerName: t('Trigger'),
{ field: 'trigger',
headerName: t('Trigger'), cellClass: 'font-mono text-right',
field: 'trigger', type: 'rightAligned',
cellClass: 'font-mono text-right', sortable: false,
type: 'rightAligned', valueFormatter: ({
sortable: false, data,
valueFormatter: ({ value,
data, }: VegaValueFormatterParams<StopOrder, 'trigger'>): string => {
value, if (data && value?.__typename === 'StopOrderPrice') {
}: VegaValueFormatterParams<StopOrder, 'trigger'>): string => { return `${t('Mark')} ${
if (data && value?.__typename === 'StopOrderPrice') { data?.triggerDirection ===
return `${t('Mark')} ${ Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
data?.triggerDirection === ? '<'
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW : '>'
? '<' } ${addDecimalsFormatNumber(
: '>' value.price,
} ${addDecimalsFormatNumber( data.market.decimalPlaces
value.price, )}`;
data.market.decimalPlaces }
)}`; if (data && value?.__typename === 'StopOrderTrailingPercentOffset') {
} return `${t('Mark')} ${
if ( data?.triggerDirection ===
data && Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
value?.__typename === 'StopOrderTrailingPercentOffset' ? '+'
) { : '-'
return `${t('Mark')} ${ }${(Number(value.trailingPercentOffset) * 100).toFixed(1)}%`;
data?.triggerDirection === }
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW return '-';
? '+' },
: '-' minWidth: 100,
}${(Number(value.trailingPercentOffset) * 100).toFixed(1)}%`; },
} {
return '-'; field: 'expiresAt',
}, valueFormatter: ({
minWidth: 100, value,
}, data,
{ }: VegaValueFormatterParams<StopOrder, 'expiresAt'>) => {
field: 'expiresAt', if (
valueFormatter: ({ data &&
value, value &&
data, data?.expiryStrategy !==
}: VegaValueFormatterParams<StopOrder, 'expiresAt'>) => { Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_UNSPECIFIED
if ( ) {
data && const expiresAt = getDateTimeFormat().format(new Date(value));
value && const expiryStrategy =
data?.expiryStrategy !== data.expiryStrategy ===
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_UNSPECIFIED Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT
) { ? t('Submit')
const expiresAt = getDateTimeFormat().format(new Date(value)); : t('Cancels');
const expiryStrategy = return `${expiryStrategy} ${expiresAt}`;
data.expiryStrategy === }
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT return '';
? t('Submit') },
: t('Cancels'); minWidth: 150,
return `${expiryStrategy} ${expiresAt}`; },
} {
return ''; headerName: t('Size'),
}, field: 'submission.size',
minWidth: 150, cellClass: 'font-mono text-right',
}, type: 'rightAligned',
{ cellClassRules: {
headerName: t('Size'), [positiveClassNames]: ({ data }: { data: StopOrder }) =>
field: 'submission.size', data?.submission.size === Schema.Side.SIDE_BUY,
cellClass: 'font-mono text-right', [negativeClassNames]: ({ data }: { data: StopOrder }) =>
type: 'rightAligned', data?.submission.size === Schema.Side.SIDE_SELL,
cellClassRules: { },
[positiveClassNames]: ({ data }: { data: StopOrder }) => valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) => {
data?.submission.size === Schema.Side.SIDE_BUY, return data?.submission.size && data.market
[negativeClassNames]: ({ data }: { data: StopOrder }) => ? toBigNum(
data?.submission.size === Schema.Side.SIDE_SELL, data.submission.size,
}, data.market.positionDecimalPlaces ?? 0
valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) => { )
return data?.submission.size && data.market .multipliedBy(
? toBigNum( data.submission.side === Schema.Side.SIDE_SELL ? -1 : 1
data.submission.size,
data.market.positionDecimalPlaces ?? 0
)
.multipliedBy(
data.submission.side === Schema.Side.SIDE_SELL ? -1 : 1
)
.toNumber()
: undefined;
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<StopOrder, 'size'>) => {
if (!data) {
return '';
}
if (!data?.market || !isNumeric(data.submission.size)) {
return '-';
}
const prefix = data
? data.submission.side === Schema.Side.SIDE_BUY
? '+'
: '-'
: '';
return (
prefix +
addDecimalsFormatNumber(
data.submission.size,
data.market.positionDecimalPlaces
) )
); .toNumber()
}, : undefined;
minWidth: 80, },
}, valueFormatter: ({
{ data,
field: 'submission.type', }: VegaValueFormatterParams<StopOrder, 'size'>) => {
filter: SetFilter, if (!data) {
filterParams: { return '';
set: Schema.OrderTypeMapping, }
}, if (!data?.market || !isNumeric(data.submission.size)) {
cellRenderer: ({ return '-';
value, }
}: VegaICellRendererParams<StopOrder, 'submission.type'>) => const prefix = data
value ? Schema.OrderTypeMapping[value] : '', ? data.submission.side === Schema.Side.SIDE_BUY
minWidth: 80, ? '+'
}, : '-'
{ : '';
field: 'status', return (
filter: SetFilter, prefix +
filterParams: { addDecimalsFormatNumber(
set: Schema.StopOrderStatusMapping, data.submission.size,
}, data.market.positionDecimalPlaces
valueFormatter: ({ )
value, );
}: VegaValueFormatterParams<StopOrder, 'status'>) => { },
return value ? Schema.StopOrderStatusMapping[value] : ''; minWidth: 80,
}, },
cellRenderer: ({ {
valueFormatted, field: 'submission.type',
data, filter: SetFilter,
}: { filterParams: {
valueFormatted: string; set: Schema.OrderTypeMapping,
data: StopOrder; },
}) => ( cellRenderer: ({
<span data-testid={`order-status-${data?.id}`}> value,
{valueFormatted} }: VegaICellRendererParams<StopOrder, 'submission.type'>) =>
</span> value ? Schema.OrderTypeMapping[value] : '',
), minWidth: 80,
minWidth: 100, },
}, {
{ field: 'status',
field: 'submission.price', filter: SetFilter,
type: 'rightAligned', filterParams: {
cellClass: 'font-mono text-right', set: Schema.StopOrderStatusMapping,
valueFormatter: ({ },
value, valueFormatter: ({
data, value,
}: VegaValueFormatterParams<StopOrder, 'submission.price'>) => { }: VegaValueFormatterParams<StopOrder, 'status'>) => {
if (!data) { return value ? Schema.StopOrderStatusMapping[value] : '';
return ''; },
} cellRenderer: ({
if ( valueFormatted,
!data?.market || data,
data.submission.type === Schema.OrderType.TYPE_MARKET || }: {
!isNumeric(value) valueFormatted: string;
) { data: StopOrder;
return '-'; }) => (
} <span data-testid={`order-status-${data?.id}`}>{valueFormatted}</span>
return addDecimalsFormatNumber(value, data.market.decimalPlaces); ),
}, minWidth: 100,
minWidth: 100, },
}, {
{ field: 'submission.price',
field: 'submission.timeInForce', type: 'rightAligned',
filter: SetFilter, cellClass: 'font-mono text-right',
filterParams: { valueFormatter: ({
set: Schema.OrderTimeInForceMapping, value,
}, data,
valueFormatter: ({ }: VegaValueFormatterParams<StopOrder, 'submission.price'>) => {
value, if (!data) {
}: VegaValueFormatterParams< return '';
StopOrder, }
'submission.timeInForce' if (
>) => { !data?.market ||
return value ? Schema.OrderTimeInForceCode[value] : ''; data.submission.type === Schema.OrderType.TYPE_MARKET ||
}, !isNumeric(value)
minWidth: 150, ) {
}, return '-';
{ }
field: 'updatedAt', return addDecimalsFormatNumber(value, data.market.decimalPlaces);
filter: DateRangeFilter, },
valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) => minWidth: 100,
data?.updatedAt || data?.createdAt, },
cellRenderer: ({ {
data, field: 'submission.timeInForce',
}: VegaICellRendererParams<StopOrder, 'createdAt'>) => { filter: SetFilter,
if (!data) { filterParams: {
return undefined; set: Schema.OrderTimeInForceMapping,
} },
const value = data.updatedAt || data.createdAt; valueFormatter: ({
return ( value,
<span data-value={value}> }: VegaValueFormatterParams<StopOrder, 'submission.timeInForce'>) => {
{value ? getDateTimeFormat().format(new Date(value)) : '-'} return value ? Schema.OrderTimeInForceCode[value] : '';
</span> },
); minWidth: 150,
}, },
minWidth: 150, {
}, field: 'updatedAt',
{ filter: DateRangeFilter,
colId: 'actions', valueGetter: ({ data }: VegaValueGetterParams<StopOrder>) =>
...COL_DEFS.actions, data?.updatedAt || data?.createdAt,
minWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth, cellRenderer: ({
maxWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth, data,
cellRenderer: ({ data }: { data?: StopOrder }) => { }: VegaICellRendererParams<StopOrder, 'createdAt'>) => {
if (!data) return null; if (!data) {
return undefined;
}
const value = data.updatedAt || data.createdAt;
return (
<span data-value={value}>
{value ? getDateTimeFormat().format(new Date(value)) : '-'}
</span>
);
},
minWidth: 150,
},
{
colId: 'actions',
...COL_DEFS.actions,
minWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth,
maxWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth,
cellRenderer: ({ data }: { data?: StopOrder }) => {
if (!data) return null;
return ( return (
<div className="flex gap-2 items-center justify-end"> <div className="flex gap-2 items-center justify-end">
{data.status === Schema.StopOrderStatus.STATUS_PENDING && {data.status === Schema.StopOrderStatus.STATUS_PENDING &&
!props.isReadOnly && ( !props.isReadOnly && (
<ButtonLink <ButtonLink
data-testid="cancel" data-testid="cancel"
onClick={() => onCancel(data)} onClick={() => onCancel(data)}
> >
{t('Cancel')} {t('Cancel')}
</ButtonLink> </ButtonLink>
)} )}
</div> </div>
); );
}, },
}, },
], ],
[onCancel, onMarketClick, props.isReadOnly, showAllActions] [onCancel, onMarketClick, props.isReadOnly, showAllActions]
); );
return ( return (
<AgGrid <AgGrid
ref={ref} defaultColDef={{
defaultColDef={{ resizable: true,
resizable: true, sortable: true,
sortable: true, filterParams: { buttons: ['reset'] },
filterParams: { buttons: ['reset'] }, }}
}} columnDefs={columnDefs}
columnDefs={columnDefs} style={{
style={{ width: '100%',
width: '100%', height: '100%',
height: '100%', }}
}} getRowId={({ data }) => data.id}
getRowId={({ data }) => data.id} components={{ MarketNameCell }}
components={{ MarketNameCell }} {...props}
{...props} />
/> );
); });
}
)
);

View File

@ -1,5 +1,5 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { forwardRef, useMemo } from 'react'; import { useMemo } from 'react';
import type { CSSProperties, ReactNode } from 'react'; import type { CSSProperties, ReactNode } from 'react';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import type { import type {
@ -33,7 +33,6 @@ import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { AgGridReact } from 'ag-grid-react';
import type { Position } from './positions-data-providers'; import type { Position } from './positions-data-providers';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types'; import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types';
@ -87,383 +86,367 @@ AmountCell.displayName = 'AmountCell';
export const getRowId = ({ data }: { data: Position }) => export const getRowId = ({ data }: { data: Position }) =>
`${data.partyId}-${data.marketId}`; `${data.partyId}-${data.marketId}`;
export const PositionsTable = forwardRef<AgGridReact, Props>( export const PositionsTable = ({
( onClose,
{ onMarketClick,
onClose, multipleKeys,
onMarketClick, isReadOnly,
multipleKeys, pubKeys,
isReadOnly, pubKey,
pubKeys, ...props
pubKey, }: Props) => {
...props const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
}, return (
ref <AgGrid
) => { style={{ width: '100%', height: '100%' }}
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); overlayNoRowsTemplate={t('No positions')}
return ( getRowId={getRowId}
<AgGrid tooltipShowDelay={500}
style={{ width: '100%', height: '100%' }} defaultColDef={{
overlayNoRowsTemplate={t('No positions')} resizable: true,
getRowId={getRowId} sortable: true,
ref={ref} filter: true,
tooltipShowDelay={500} filterParams: { buttons: ['reset'] },
defaultColDef={{ tooltipComponent: TooltipCellComponent,
resizable: true, }}
sortable: true, components={{
filter: true, AmountCell,
filterParams: { buttons: ['reset'] }, PriceFlashCell,
tooltipComponent: TooltipCellComponent, ProgressBarCell,
}} MarketNameCell,
components={{ }}
AmountCell, {...props}
PriceFlashCell, columnDefs={useMemo<ColDef[]>(() => {
ProgressBarCell, const columnDefs: (ColDef | null)[] = [
MarketNameCell, multipleKeys
}} ? {
{...props} headerName: t('Vega key'),
columnDefs={useMemo<ColDef[]>(() => { field: 'partyId',
const columnDefs: (ColDef | null)[] = [ valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
multipleKeys (data?.partyId &&
? { pubKeys &&
headerName: t('Vega key'), pubKeys.find((key) => key.publicKey === data.partyId)
field: 'partyId', ?.name) ||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => data?.partyId,
(data?.partyId && minWidth: 190,
pubKeys && }
pubKeys.find((key) => key.publicKey === data.partyId) : null,
?.name) || {
data?.partyId, headerName: t('Market'),
minWidth: 190, field: 'marketName',
} cellRenderer: 'MarketNameCell',
: null, cellRendererParams: { idPath: 'marketId', onMarketClick },
{ minWidth: 190,
headerName: t('Market'), },
field: 'marketName', {
cellRenderer: 'MarketNameCell', headerName: t('Notional'),
cellRendererParams: { idPath: 'marketId', onMarketClick }, headerTooltip: t('Mark price x open volume.'),
minWidth: 190, field: 'notional',
type: 'rightAligned',
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data?.notional
? undefined
: toBigNum(data.notional, data.marketDecimalPlaces).toNumber();
}, },
{ valueFormatter: ({
headerName: t('Notional'), data,
headerTooltip: t('Mark price x open volume.'), }: VegaValueFormatterParams<Position, 'notional'>) => {
field: 'notional', return !data || !data.notional
type: 'rightAligned', ? '-'
cellClass: 'font-mono text-right', : addDecimalsFormatNumber(
filter: 'agNumberColumnFilter', data.notional,
valueGetter: ({ data }: VegaValueGetterParams<Position>) => { data.marketDecimalPlaces
return !data?.notional );
? undefined
: toBigNum(
data.notional,
data.marketDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'notional'>) => {
return !data || !data.notional
? '-'
: addDecimalsFormatNumber(
data.notional,
data.marketDecimalPlaces
);
},
minWidth: 80,
}, },
{ minWidth: 80,
headerName: t('Open volume'), },
field: 'openVolume', {
type: 'rightAligned', headerName: t('Open volume'),
cellClass: 'font-mono text-right', field: 'openVolume',
cellClassRules: signedNumberCssClassRules, type: 'rightAligned',
filter: 'agNumberColumnFilter', cellClass: 'font-mono text-right',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => { cellClassRules: signedNumberCssClassRules,
return data?.openVolume === undefined filter: 'agNumberColumnFilter',
? undefined valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
: toBigNum( return data?.openVolume === undefined
data?.openVolume, ? undefined
: toBigNum(
data?.openVolume,
data.positionDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
return data?.openVolume === undefined
? ''
: volumePrefix(
addDecimalsFormatNumber(
data.openVolume,
data.positionDecimalPlaces data.positionDecimalPlaces
).toNumber(); )
}, );
valueFormatter: ({ },
data, cellRenderer: OpenVolumeCell,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => { minWidth: 100,
return data?.openVolume === undefined },
? '' {
: volumePrefix( headerName: t('Mark price'),
addDecimalsFormatNumber( field: 'markPrice',
data.openVolume, type: 'rightAligned',
data.positionDecimalPlaces cellRenderer: PriceFlashCell,
) filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data ||
!data.markPrice ||
data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
? undefined
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'markPrice'>) => {
if (!data) {
return '';
}
if (
!data.markPrice ||
data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return '-';
}
return addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
},
minWidth: 100,
},
{
headerName: t('Liquidation price'),
colId: 'liquidationPrice',
type: 'rightAligned',
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) return null;
return (
<LiquidationPrice
marketId={data.marketId}
openVolume={data.openVolume}
collateralAvailable={data.totalBalance}
decimalPlaces={data.decimals}
formatDecimals={data.marketDecimalPlaces}
/>
);
},
},
{
headerName: t('Settlement asset'),
field: 'assetSymbol',
colId: 'asset',
minWidth: 100,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) return null;
return (
<ButtonLink
title={t('View settlement asset details')}
onClick={(e) => {
openAssetDetailsDialog(
data.assetId,
e.target as HTMLElement
); );
}, }}
cellRenderer: OpenVolumeCell, >
minWidth: 100, {data?.assetSymbol}
</ButtonLink>
);
}, },
{ },
headerName: t('Mark price'), {
field: 'markPrice', headerName: t('Entry price'),
type: 'rightAligned', field: 'averageEntryPrice',
cellRenderer: PriceFlashCell, type: 'rightAligned',
filter: 'agNumberColumnFilter', cellRenderer: PriceFlashCell,
valueGetter: ({ data }: VegaValueGetterParams<Position>) => { filter: 'agNumberColumnFilter',
return !data || valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
!data.markPrice || return data?.markPrice === undefined || !data
data.marketTradingMode === ? undefined
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION : toBigNum(
? undefined data.averageEntryPrice,
: toBigNum( data.marketDecimalPlaces
data.markPrice, ).toNumber();
data.marketDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'markPrice'>) => {
if (!data) {
return '';
}
if (
!data.markPrice ||
data.marketTradingMode ===
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return '-';
}
return addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
},
minWidth: 100,
}, },
{ valueFormatter: ({
headerName: t('Liquidation price'), data,
colId: 'liquidationPrice', }: VegaValueFormatterParams<
type: 'rightAligned', Position,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => { 'averageEntryPrice'
if (!data) return null; >): string => {
return ( if (!data) {
<LiquidationPrice return '';
marketId={data.marketId} }
openVolume={data.openVolume} return addDecimalsFormatNumber(
collateralAvailable={data.totalBalance} data.averageEntryPrice,
decimalPlaces={data.decimals} data.marketDecimalPlaces
formatDecimals={data.marketDecimalPlaces} );
/>
);
},
}, },
{ minWidth: 100,
headerName: t('Settlement asset'), },
field: 'assetSymbol', multipleKeys
colId: 'asset', ? null
minWidth: 100, : {
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => { headerName: t('Leverage'),
if (!data) return null; field: 'currentLeverage',
return ( type: 'rightAligned',
<ButtonLink filter: 'agNumberColumnFilter',
title={t('View settlement asset details')} cellRenderer: PriceFlashCell,
onClick={(e) => { valueFormatter: ({
openAssetDetailsDialog( value,
data.assetId, }: VegaValueFormatterParams<Position, 'currentLeverage'>) =>
e.target as HTMLElement value === undefined ? '' : formatNumber(value.toString(), 1),
); minWidth: 100,
}}
>
{data?.assetSymbol}
</ButtonLink>
);
}, },
}, multipleKeys
{ ? null
headerName: t('Entry price'), : {
field: 'averageEntryPrice', headerName: t('Margin allocated'),
type: 'rightAligned', field: 'marginAccountBalance',
cellRenderer: PriceFlashCell, type: 'rightAligned',
filter: 'agNumberColumnFilter', filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => { cellRenderer: PriceFlashCell,
return data?.markPrice === undefined || !data valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
? undefined return !data
: toBigNum( ? undefined
data.averageEntryPrice, : toBigNum(
data.marketDecimalPlaces data.marginAccountBalance,
).toNumber(); data.decimals
}, ).toNumber();
valueFormatter: ({
data,
}: VegaValueFormatterParams<
Position,
'averageEntryPrice'
>): string => {
if (!data) {
return '';
}
return addDecimalsFormatNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
},
minWidth: 100,
},
multipleKeys
? null
: {
headerName: t('Leverage'),
field: 'currentLeverage',
type: 'rightAligned',
filter: 'agNumberColumnFilter',
cellRenderer: PriceFlashCell,
valueFormatter: ({
value,
}: VegaValueFormatterParams<Position, 'currentLeverage'>) =>
value === undefined
? ''
: formatNumber(value.toString(), 1),
minWidth: 100,
}, },
multipleKeys valueFormatter: ({
? null data,
: { }: VegaValueFormatterParams<
headerName: t('Margin allocated'), Position,
field: 'marginAccountBalance', 'marginAccountBalance'
type: 'rightAligned', >): string => {
filter: 'agNumberColumnFilter', if (!data) {
cellRenderer: PriceFlashCell, return '';
valueGetter: ({ data }: VegaValueGetterParams<Position>) => { }
return !data return addDecimalsFormatNumber(
? undefined data.marginAccountBalance,
: toBigNum( data.decimals
data.marginAccountBalance, );
data.decimals
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<
Position,
'marginAccountBalance'
>): string => {
if (!data) {
return '';
}
return addDecimalsFormatNumber(
data.marginAccountBalance,
data.decimals
);
},
minWidth: 100,
}, },
{ minWidth: 100,
headerName: t('Realised PNL'),
field: 'realisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.realisedPNL, data.decimals).toNumber();
}, },
valueFormatter: ({ {
data, headerName: t('Realised PNL'),
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => { field: 'realisedPNL',
return !data type: 'rightAligned',
? '' cellClassRules: signedNumberCssClassRules,
: addDecimalsFormatNumber(data.realisedPNL, data.decimals); cellClass: 'font-mono text-right',
}, filter: 'agNumberColumnFilter',
headerTooltip: t( valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.' return !data
), ? undefined
cellRenderer: PNLCell, : toBigNum(data.realisedPNL, data.decimals).toNumber();
minWidth: 100,
}, },
{ valueFormatter: ({
headerName: t('Unrealised PNL'), data,
field: 'unrealisedPNL', }: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
type: 'rightAligned', return !data
cellClassRules: signedNumberCssClassRules, ? ''
cellClass: 'font-mono text-right', : addDecimalsFormatNumber(data.realisedPNL, data.decimals);
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.unrealisedPNL, data.decimals).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumber(data.unrealisedPNL, data.decimals),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
cellRenderer: PNLCell,
minWidth: 100,
}, },
{ headerTooltip: t(
headerName: t('Updated'), 'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
field: 'updatedAt', ),
type: 'rightAligned', cellRenderer: PNLCell,
filter: DateRangeFilter, minWidth: 100,
valueFormatter: ({ },
value, {
}: VegaValueFormatterParams<Position, 'updatedAt'>) => { headerName: t('Unrealised PNL'),
if (!value) { field: 'unrealisedPNL',
return ''; type: 'rightAligned',
} cellClassRules: signedNumberCssClassRules,
return getDateTimeFormat().format(new Date(value)); cellClass: 'font-mono text-right',
}, filter: 'agNumberColumnFilter',
minWidth: 150, valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.unrealisedPNL, data.decimals).toNumber();
}, },
onClose && !isReadOnly valueFormatter: ({
? { data,
...COL_DEFS.actions, }: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
cellRenderer: ({ !data
data, ? ''
}: VegaICellRendererParams<Position>) => { : addDecimalsFormatNumber(data.unrealisedPNL, data.decimals),
return ( headerTooltip: t(
<div className="flex gap-2 items-center justify-end"> 'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
{data?.openVolume && ),
data?.openVolume !== '0' && cellRenderer: PNLCell,
data.partyId === pubKey ? ( minWidth: 100,
<ButtonLink },
data-testid="close-position" {
onClick={() => data && onClose(data)} headerName: t('Updated'),
> field: 'updatedAt',
{t('Close')} type: 'rightAligned',
</ButtonLink> filter: DateRangeFilter,
) : null} valueFormatter: ({
{data?.assetId && ( value,
<PositionActionsDropdown assetId={data?.assetId} /> }: VegaValueFormatterParams<Position, 'updatedAt'>) => {
)} if (!value) {
</div> return '';
); }
}, return getDateTimeFormat().format(new Date(value));
minWidth: 90, },
maxWidth: 90, minWidth: 150,
} },
: null, onClose && !isReadOnly
]; ? {
return columnDefs.filter<ColDef>( ...COL_DEFS.actions,
(colDef: ColDef | null): colDef is ColDef => colDef !== null cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
); return (
}, [ <div className="flex gap-2 items-center justify-end">
isReadOnly, {data?.openVolume &&
multipleKeys, data?.openVolume !== '0' &&
onClose, data.partyId === pubKey ? (
onMarketClick, <ButtonLink
openAssetDetailsDialog, data-testid="close-position"
pubKey, onClick={() => data && onClose(data)}
pubKeys, >
])} {t('Close')}
/> </ButtonLink>
); ) : null}
} {data?.assetId && (
); <PositionActionsDropdown assetId={data?.assetId} />
)}
</div>
);
},
minWidth: 90,
maxWidth: 90,
}
: null,
];
return columnDefs.filter<ColDef>(
(colDef: ColDef | null): colDef is ColDef => colDef !== null
);
}, [
isReadOnly,
multipleKeys,
onClose,
onMarketClick,
openAssetDetailsDialog,
pubKey,
pubKeys,
])}
/>
);
};
export default PositionsTable; export default PositionsTable;

View File

@ -1,9 +1,7 @@
import React from 'react'; import React from 'react';
import { useRef } from 'react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid'; import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import * as Types from '@vegaprotocol/types'; import * as Types from '@vegaprotocol/types';
import type { AgGridReact } from 'ag-grid-react';
import { useColumnDefs } from './use-column-defs'; import { useColumnDefs } from './use-column-defs';
import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals'; import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals';
import { useProposalsListQuery } from '../../lib/proposals-data-provider/__generated__/Proposals'; import { useProposalsListQuery } from '../../lib/proposals-data-provider/__generated__/Proposals';
@ -25,7 +23,6 @@ interface ProposalListProps {
export const ProposalsList = ({ export const ProposalsList = ({
SuccessorMarketRenderer, SuccessorMarketRenderer,
}: ProposalListProps) => { }: ProposalListProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data } = useProposalsListQuery({ const { data } = useProposalsListQuery({
variables: { variables: {
proposalType: Types.ProposalType.TYPE_NEW_MARKET, proposalType: Types.ProposalType.TYPE_NEW_MARKET,
@ -40,7 +37,6 @@ export const ProposalsList = ({
return ( return (
<div className="relative h-full"> <div className="relative h-full">
<AgGrid <AgGrid
ref={gridRef}
className="w-full h-full" className="w-full h-full"
columnDefs={columnDefs} columnDefs={columnDefs}
rowData={filteredData} rowData={filteredData}

View File

@ -1,6 +1,4 @@
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import type { AgGridReact } from 'ag-grid-react';
import { useRef } from 'react';
import { tradesWithMarketProvider } from './trades-data-provider'; import { tradesWithMarketProvider } from './trades-data-provider';
import { TradesTable } from './trades-table'; import { TradesTable } from './trades-table';
import { useCreateOrderStore } from '@vegaprotocol/orders'; import { useCreateOrderStore } from '@vegaprotocol/orders';
@ -11,7 +9,6 @@ interface TradesContainerProps {
} }
export const TradesContainer = ({ marketId }: TradesContainerProps) => { export const TradesContainer = ({ marketId }: TradesContainerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const useOrderStoreRef = useCreateOrderStore(); const useOrderStoreRef = useCreateOrderStore();
const updateOrder = useOrderStoreRef((store) => store.update); const updateOrder = useOrderStoreRef((store) => store.update);
@ -22,7 +19,6 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
return ( return (
<TradesTable <TradesTable
ref={gridRef}
rowData={data} rowData={data}
onClick={(price?: string) => { onClick={(price?: string) => {
if (price) { if (price) {

View File

@ -1,7 +1,5 @@
import type { AgGridReact } from 'ag-grid-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { forwardRef } from 'react';
import type { import type {
VegaICellRendererParams, VegaICellRendererParams,
VegaValueFormatterParams, VegaValueFormatterParams,
@ -48,90 +46,87 @@ interface Props extends AgGridReactProps {
onClick?: (price?: string) => void; onClick?: (price?: string) => void;
} }
export const TradesTable = forwardRef<AgGridReact, Props>( export const TradesTable = ({ onClick, ...props }: Props) => {
({ onClick, ...props }, ref) => { const columnDefs = useMemo<ColDef[]>(
const columnDefs = useMemo<ColDef[]>( () => [
() => [ {
{ headerName: t('Price'),
headerName: t('Price'), field: 'price',
field: 'price', type: 'rightAligned',
type: 'rightAligned', width: 130,
width: 130, cellClass: changeCellClass,
cellClass: changeCellClass, valueFormatter: ({
valueFormatter: ({ value,
value, data,
data, }: VegaValueFormatterParams<Trade, 'price'>) => {
}: VegaValueFormatterParams<Trade, 'price'>) => { if (!value || !data?.market) {
if (!value || !data?.market) { return '';
return ''; }
} return addDecimalsFormatNumber(value, data.market.decimalPlaces);
return addDecimalsFormatNumber(value, data.market.decimalPlaces);
},
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<Trade, 'price'>) => {
if (!data?.market || !value) {
return '';
}
return (
<button
onClick={() =>
onClick &&
onClick(addDecimal(value, data.market?.decimalPlaces || 0))
}
className="hover:dark:bg-vega-cdark-800 hover:bg-vega-clight-800"
>
{addDecimalsFormatNumber(value, data.market.decimalPlaces)}
</button>
);
},
}, },
{ cellRenderer: ({
headerName: t('Size'), value,
field: 'size', data,
width: 125, }: VegaICellRendererParams<Trade, 'price'>) => {
type: 'rightAligned', if (!data?.market || !value) {
valueFormatter: ({ return '';
value, }
data, return (
}: VegaValueFormatterParams<Trade, 'size'>) => { <button
if (!value || !data?.market) { onClick={() =>
return ''; onClick &&
} onClick(addDecimal(value, data.market?.decimalPlaces || 0))
return addDecimalsFormatNumber( }
value, className="hover:dark:bg-vega-cdark-800 hover:bg-vega-clight-800"
data.market.positionDecimalPlaces >
); {addDecimalsFormatNumber(value, data.market.decimalPlaces)}
}, </button>
cellRenderer: NumericCell, );
}, },
{ },
headerName: t('Created at'), {
field: 'createdAt', headerName: t('Size'),
type: 'rightAligned', field: 'size',
width: 170, width: 125,
cellClass: 'text-right', type: 'rightAligned',
valueFormatter: ({ valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Trade, 'size'>) => {
if (!value || !data?.market) {
return '';
}
return addDecimalsFormatNumber(
value, value,
}: VegaValueFormatterParams<Trade, 'createdAt'>) => { data.market.positionDecimalPlaces
return value && getDateTimeFormat().format(new Date(value)); );
},
}, },
], cellRenderer: NumericCell,
[onClick] },
); {
return ( headerName: t('Created at'),
<AgGrid field: 'createdAt',
style={{ width: '100%', height: '100%' }} type: 'rightAligned',
getRowId={({ data }) => data.id} width: 170,
ref={ref} cellClass: 'text-right',
defaultColDef={{ valueFormatter: ({
flex: 1, value,
}} }: VegaValueFormatterParams<Trade, 'createdAt'>) => {
columnDefs={columnDefs} return value && getDateTimeFormat().format(new Date(value));
{...props} },
/> },
); ],
} [onClick]
); );
return (
<AgGrid
style={{ width: '100%', height: '100%' }}
getRowId={({ data }) => data.id}
defaultColDef={{
flex: 1,
}}
columnDefs={columnDefs}
{...props}
/>
);
};

View File

@ -1,5 +1,4 @@
import { useEffect, useRef, useState, useMemo } from 'react'; import { useEffect, useRef, useState, useMemo } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community'; import type { ColDef } from 'ag-grid-community';
import { import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
@ -40,7 +39,6 @@ export const WithdrawalsTable = ({
ready?: TimestampedWithdrawals; ready?: TimestampedWithdrawals;
delayed?: TimestampedWithdrawals; delayed?: TimestampedWithdrawals;
}) => { }) => {
const gridRef = useRef<AgGridReact | null>(null);
const createWithdrawApproval = useEthWithdrawApprovalsStore( const createWithdrawApproval = useEthWithdrawApprovalsStore(
(store) => store.create (store) => store.create
); );
@ -146,7 +144,6 @@ export const WithdrawalsTable = ({
CompleteCell, CompleteCell,
}} }}
suppressCellFocus suppressCellFocus
ref={gridRef}
{...props} {...props}
/> />
); );