diff --git a/libs/accounts/src/lib/accounts-table.tsx b/libs/accounts/src/lib/accounts-table.tsx index aeb25886b..f17b1bb81 100644 --- a/libs/accounts/src/lib/accounts-table.tsx +++ b/libs/accounts/src/lib/accounts-table.tsx @@ -1,7 +1,11 @@ import { forwardRef, useState } from 'react'; import type { ValueFormatterParams } from 'ag-grid-community'; import type { Asset } from '@vegaprotocol/assets'; -import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers'; +import { + addDecimalsFormatNumber, + isNumeric, + t, +} from '@vegaprotocol/react-helpers'; import type { ValueProps, VegaICellRendererParams, @@ -95,7 +99,7 @@ export const AccountTable = forwardRef( cellRenderer={({ value, }: VegaICellRendererParams) => { - return ( + return value ? ( { @@ -104,7 +108,7 @@ export const AccountTable = forwardRef( > {value} - ); + ) : null; }} maxWidth={300} /> @@ -120,6 +124,7 @@ export const AccountTable = forwardRef( }: VegaValueFormatterParams) => data && data.asset && + isNumeric(value) && addDecimalsFormatNumber(value, data.asset.decimals) } maxWidth={300} @@ -162,7 +167,7 @@ export const AccountTable = forwardRef( cellRenderer={({ data, }: VegaICellRendererParams) => { - return ( + return data ? (
- ); + ) : null; }} /> diff --git a/libs/accounts/src/lib/breakdown-table.tsx b/libs/accounts/src/lib/breakdown-table.tsx index fdf809fe0..4f9f3ee7c 100644 --- a/libs/accounts/src/lib/breakdown-table.tsx +++ b/libs/accounts/src/lib/breakdown-table.tsx @@ -1,6 +1,7 @@ import { forwardRef } from 'react'; import { addDecimalsFormatNumber, + isNumeric, PriceCell, t, } from '@vegaprotocol/react-helpers'; @@ -48,7 +49,7 @@ const BreakdownTable = forwardRef( valueFormatter={({ value, }: VegaValueFormatterParams) => - AccountTypeMapping[value] + value ? AccountTypeMapping[value] : '' } /> ( value, data, }: VegaValueFormatterParams) => { - if (data && data.asset) { + if (data && data.asset && isNumeric(value)) { return addDecimalsFormatNumber(value, data.asset.decimals); } return '-'; diff --git a/libs/deposits/src/lib/deposits-table.tsx b/libs/deposits/src/lib/deposits-table.tsx index eddcf32ce..705ad8b4e 100644 --- a/libs/deposits/src/lib/deposits-table.tsx +++ b/libs/deposits/src/lib/deposits-table.tsx @@ -4,6 +4,7 @@ import { addDecimalsFormatNumber, getDateTimeFormat, truncateByChars, + isNumeric, } from '@vegaprotocol/react-helpers'; import type { VegaICellRendererParams, @@ -36,7 +37,9 @@ export const DepositsTable = ({ deposits }: DepositsTableProps) => { value, data, }: VegaValueFormatterParams) => { - return addDecimalsFormatNumber(value, data.asset.decimals); + return isNumeric(value) && data + ? addDecimalsFormatNumber(value, data.asset.decimals) + : null; }} /> { DepositFieldsFragment, 'createdTimestamp' >) => { - return getDateTimeFormat().format(new Date(value)); + return value ? getDateTimeFormat().format(new Date(value)) : ''; }} /> { valueFormatter={({ value, }: VegaValueFormatterParams) => { - return DepositStatusMapping[value]; + return value ? DepositStatusMapping[value] : ''; }} /> ( valueFormatter={({ value, }: VegaValueFormatterParams) => { - return getDateTimeFormat().format(new Date(value)); + return value ? getDateTimeFormat().format(new Date(value)) : ''; }} /> @@ -93,7 +94,7 @@ const formatPrice = ({ value, data, }: VegaValueFormatterParams) => { - if (!data.market) { + if (!data?.market || !isNumeric(value)) { return '-'; } const asset = @@ -107,7 +108,7 @@ const formatPrice = ({ const formatSize = (partyId: string) => { return ({ value, data }: VegaValueFormatterParams) => { - if (!data.market) { + if (!data?.market || !isNumeric(value)) { return '-'; } let prefix = ''; @@ -144,7 +145,7 @@ const formatTotal = ({ value, data, }: VegaValueFormatterParams) => { - if (!data?.market) { + if (!data?.market || !isNumeric(value)) { return '-'; } const asset = @@ -189,7 +190,7 @@ const formatFee = (partyId: string) => { Trade, 'market.tradableInstrument.instrument.product' >) => { - if (!value?.settlementAsset) { + if (!value?.settlementAsset || !data) { return '-'; } const asset = value.settlementAsset; diff --git a/libs/orders/src/lib/components/order-list/order-list.spec.tsx b/libs/orders/src/lib/components/order-list/order-list.spec.tsx index f8fdacba5..78daca377 100644 --- a/libs/orders/src/lib/components/order-list/order-list.spec.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.spec.tsx @@ -1,6 +1,6 @@ import { act, render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { addDecimal, getDateTimeFormat } from '@vegaprotocol/react-helpers'; +import { getDateTimeFormat } from '@vegaprotocol/react-helpers'; import { OrderTimeInForce, OrderType } from '@vegaprotocol/types'; import { OrderRejectionReasonMapping, @@ -108,7 +108,7 @@ describe('OrderListTable', () => { OrderTypeMapping[limitOrder.type || OrderType.TYPE_LIMIT], OrderStatusMapping[limitOrder.status], '5', - addDecimal(limitOrder.price, limitOrder.market?.decimalPlaces ?? 0), + '-', `${ OrderTimeInForceMapping[limitOrder.timeInForce] }: ${getDateTimeFormat().format(new Date(limitOrder.expiresAt ?? ''))}`, diff --git a/libs/orders/src/lib/components/order-list/order-list.tsx b/libs/orders/src/lib/components/order-list/order-list.tsx index e192ad84e..9ddc1373d 100644 --- a/libs/orders/src/lib/components/order-list/order-list.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.tsx @@ -14,6 +14,7 @@ import { t, positiveClassNames, negativeClassNames, + isNumeric, } from '@vegaprotocol/react-helpers'; import type { VegaICellRendererParams, @@ -128,7 +129,7 @@ export const OrderListTable = forwardRef( value, data, }: VegaValueFormatterParams) => { - if (!data.market) { + if (!data?.market || !isNumeric(value)) { return '-'; } const prefix = data @@ -148,8 +149,8 @@ export const OrderListTable = forwardRef( value, }: VegaValueFormatterParams) => { if (!value) return '-'; - if (order.peggedOrder) return t('Pegged'); - if (order.liquidityProvision) return t('Liquidity provision'); + if (order?.peggedOrder) return t('Pegged'); + if (order?.liquidityProvision) return t('Liquidity provision'); return OrderTypeMapping[value]; }} /> @@ -161,11 +162,11 @@ export const OrderListTable = forwardRef( }: VegaValueFormatterParams) => { if (value === OrderStatus.STATUS_REJECTED) { return `${OrderStatusMapping[value]}: ${ - data.rejectionReason && + data?.rejectionReason && OrderRejectionReasonMapping[data.rejectionReason] }`; } - return OrderStatusMapping[value]; + return value ? OrderStatusMapping[value] : ''; }} /> ( data, value, }: VegaValueFormatterParams) => { - if (!data.market) { + if (!data?.market || !isNumeric(value) || !isNumeric(data.size)) { return '-'; } const dps = data.market.positionDecimalPlaces; @@ -198,7 +199,11 @@ export const OrderListTable = forwardRef( value, data, }: VegaValueFormatterParams) => { - if (!data.market || data.type === OrderType.TYPE_MARKET) { + if ( + !data?.market || + data.type === OrderType.TYPE_MARKET || + !isNumeric(value) + ) { return '-'; } return addDecimal(value, data.market.decimalPlaces); @@ -212,7 +217,7 @@ export const OrderListTable = forwardRef( }: VegaValueFormatterParams) => { if ( value === OrderTimeInForce.TIME_IN_FORCE_GTT && - data.expiresAt + data?.expiresAt ) { const expiry = getDateTimeFormat().format( new Date(data.expiresAt) @@ -220,7 +225,7 @@ export const OrderListTable = forwardRef( return `${OrderTimeInForceMapping[value]}: ${expiry}`; } - return OrderTimeInForceMapping[value]; + return value ? OrderTimeInForceMapping[value] : ''; }} /> ( field="status" cellRenderer={({ data }: VegaICellRendererParams) => { if (isOrderAmendable(data)) { - return ( + return data ? (
- ); + ) : null; } return null; diff --git a/libs/react-helpers/src/lib/format/number.spec.ts b/libs/react-helpers/src/lib/format/number.spec.ts index bce513f5d..331b3f336 100644 --- a/libs/react-helpers/src/lib/format/number.spec.ts +++ b/libs/react-helpers/src/lib/format/number.spec.ts @@ -1,5 +1,10 @@ import BigNumber from 'bignumber.js'; -import { formatNumber, formatNumberPercentage, toNumberParts } from './number'; +import { + formatNumber, + formatNumberPercentage, + toNumberParts, + isNumeric, +} from './number'; describe('formatNumber and formatNumberPercentage', () => { it.each([ @@ -46,3 +51,41 @@ describe('toNumberParts', () => { expect(toNumberParts(v, d)).toStrictEqual(o); }); }); + +describe('isNumeric', () => { + it.each([ + { i: null, o: false }, + { i: undefined, o: false }, + { i: 1, o: true }, + { i: '1', o: true }, + { i: '-1', o: true }, + { i: 0.1, o: true }, + { i: '.1', o: true }, + { i: '-.1', o: true }, + { i: 123, o: true }, + { i: -123, o: true }, + { i: '123', o: true }, + { i: '123.01', o: true }, + { i: '-123.01', o: true }, + { i: '--123.01', o: false }, + { i: '123.', o: false }, + { i: '123.1.1', o: false }, + { i: new BigNumber(123), o: true }, + { i: new BigNumber(123.123), o: true }, + { i: new BigNumber(123.123).toString(), o: true }, + { i: new BigNumber(123), o: true }, + { i: Infinity, o: false }, + { i: NaN, o: false }, + ])( + 'returns correct results', + ({ + i, + o, + }: { + i: number | string | undefined | null | BigNumber; + o: boolean; + }) => { + expect(isNumeric(i)).toStrictEqual(o); + } + ); +}); diff --git a/libs/react-helpers/src/lib/format/number.ts b/libs/react-helpers/src/lib/format/number.ts index 4a13c067e..2f309cad6 100644 --- a/libs/react-helpers/src/lib/format/number.ts +++ b/libs/react-helpers/src/lib/format/number.ts @@ -96,3 +96,7 @@ export const useNumberParts = ( ): [integers: string, decimalPlaces: string] => { return React.useMemo(() => toNumberParts(value, decimals), [decimals, value]); }; + +export const isNumeric = ( + value?: string | number | BigNumber | null +): value is NonNullable => /^-?\d*\.?\d+$/.test(String(value)); diff --git a/libs/ui-toolkit/src/components/ag-grid/index.tsx b/libs/ui-toolkit/src/components/ag-grid/index.tsx index 9db7ee4d9..d557fe440 100644 --- a/libs/ui-toolkit/src/components/ag-grid/index.tsx +++ b/libs/ui-toolkit/src/components/ag-grid/index.tsx @@ -16,8 +16,8 @@ type RowHelper = Omit< TObj, 'data' | 'value' > & { - data: TRow; - value: Get; + data?: TRow; + value?: Get; }; export type VegaValueFormatterParams = RowHelper< diff --git a/libs/withdraws/src/lib/pending-withdrawals-table.tsx b/libs/withdraws/src/lib/pending-withdrawals-table.tsx index b58163ca9..84cfb7312 100644 --- a/libs/withdraws/src/lib/pending-withdrawals-table.tsx +++ b/libs/withdraws/src/lib/pending-withdrawals-table.tsx @@ -4,6 +4,7 @@ import { t, truncateByChars, addDecimalsFormatNumber, + isNumeric, } from '@vegaprotocol/react-helpers'; import type { TypedDataAgGrid, @@ -60,7 +61,9 @@ export const PendingWithdrawalsTable = ( value, data, }: VegaValueFormatterParams) => { - return addDecimalsFormatNumber(value, data.asset.decimals); + return isNumeric(value) && data?.asset + ? addDecimalsFormatNumber(value, data.asset.decimals) + : null; }} /> ) => { - return getDateTimeFormat().format(new Date(value)); + return value ? getDateTimeFormat().format(new Date(value)) : ''; }} /> ) => { value, data, }: VegaValueFormatterParams) => { - return addDecimalsFormatNumber(value, data.asset.decimals); + return isNumeric(value) && data?.asset + ? addDecimalsFormatNumber(value, data.asset.decimals) + : ''; }} /> ) => { WithdrawalFields, 'withdrawnTimestamp' >) => { - const ts = data.withdrawnTimestamp; + const ts = data?.withdrawnTimestamp; if (!ts) return '-'; return getDateTimeFormat().format(new Date(ts)); }}