chore: ag grid row data and value can be undefined (#1709)

* chore: improve ag-grid typings - fix runtime bug

* chore: improve ag-grid typings - add numeric type checking

* chore: improve ag-grid typings - remove redundant cl

* chore: improve ag-grid typings - remove redundant cl

* chore: improve ag-grid typings - add some basic unit test

* chore: improve ag-grid typings - add some basic unit test

Co-authored-by: maciek <maciek@vegaprotocol.io>
This commit is contained in:
macqbat 2022-10-12 11:06:19 +02:00 committed by GitHub
parent 50df63c858
commit 00381a2b3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 103 additions and 35 deletions

View File

@ -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<AgGridReact, AccountTableProps>(
cellRenderer={({
value,
}: VegaICellRendererParams<AccountFields, 'asset.symbol'>) => {
return (
return value ? (
<ButtonLink
data-testid="deposit"
onClick={() => {
@ -104,7 +108,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
>
{value}
</ButtonLink>
);
) : null;
}}
maxWidth={300}
/>
@ -120,6 +124,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
}: VegaValueFormatterParams<AccountFields, 'deposited'>) =>
data &&
data.asset &&
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals)
}
maxWidth={300}
@ -162,7 +167,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
cellRenderer={({
data,
}: VegaICellRendererParams<AccountFields>) => {
return (
return data ? (
<div className="flex gap-2 justify-end">
<Button
size="xs"
@ -184,7 +189,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
{t('Withdraw')}
</Button>
</div>
);
) : null;
}}
/>
</AgGrid>

View File

@ -1,6 +1,7 @@
import { forwardRef } from 'react';
import {
addDecimalsFormatNumber,
isNumeric,
PriceCell,
t,
} from '@vegaprotocol/react-helpers';
@ -48,7 +49,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
valueFormatter={({
value,
}: VegaValueFormatterParams<AccountFields, 'type'>) =>
AccountTypeMapping[value]
value ? AccountTypeMapping[value] : ''
}
/>
<AgGridColumn
@ -81,7 +82,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
value,
data,
}: VegaValueFormatterParams<AccountFields, 'balance'>) => {
if (data && data.asset) {
if (data && data.asset && isNumeric(value)) {
return addDecimalsFormatNumber(value, data.asset.decimals);
}
return '-';

View File

@ -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<DepositFieldsFragment, 'amount'>) => {
return addDecimalsFormatNumber(value, data.asset.decimals);
return isNumeric(value) && data
? addDecimalsFormatNumber(value, data.asset.decimals)
: null;
}}
/>
<AgGridColumn
@ -48,7 +51,7 @@ export const DepositsTable = ({ deposits }: DepositsTableProps) => {
DepositFieldsFragment,
'createdTimestamp'
>) => {
return getDateTimeFormat().format(new Date(value));
return value ? getDateTimeFormat().format(new Date(value)) : '';
}}
/>
<AgGridColumn
@ -57,7 +60,7 @@ export const DepositsTable = ({ deposits }: DepositsTableProps) => {
valueFormatter={({
value,
}: VegaValueFormatterParams<DepositFieldsFragment, 'status'>) => {
return DepositStatusMapping[value];
return value ? DepositStatusMapping[value] : '';
}}
/>
<AgGridColumn

View File

@ -7,6 +7,7 @@ import {
positiveClassNames,
negativeClassNames,
t,
isNumeric,
} from '@vegaprotocol/react-helpers';
import { Side } from '@vegaprotocol/types';
import { AgGridColumn } from 'ag-grid-react';
@ -81,7 +82,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
valueFormatter={({
value,
}: VegaValueFormatterParams<Trade, 'createdAt'>) => {
return getDateTimeFormat().format(new Date(value));
return value ? getDateTimeFormat().format(new Date(value)) : '';
}}
/>
</AgGrid>
@ -93,7 +94,7 @@ const formatPrice = ({
value,
data,
}: VegaValueFormatterParams<Trade, 'price'>) => {
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<Trade, 'size'>) => {
if (!data.market) {
if (!data?.market || !isNumeric(value)) {
return '-';
}
let prefix = '';
@ -144,7 +145,7 @@ const formatTotal = ({
value,
data,
}: VegaValueFormatterParams<Trade, 'price'>) => {
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;

View File

@ -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 ?? ''))}`,

View File

@ -14,6 +14,7 @@ import {
t,
positiveClassNames,
negativeClassNames,
isNumeric,
} from '@vegaprotocol/react-helpers';
import type {
VegaICellRendererParams,
@ -128,7 +129,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
value,
data,
}: VegaValueFormatterParams<Order, 'size'>) => {
if (!data.market) {
if (!data?.market || !isNumeric(value)) {
return '-';
}
const prefix = data
@ -148,8 +149,8 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
value,
}: VegaValueFormatterParams<Order, 'type'>) => {
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<AgGridReact, OrderListTableProps>(
}: VegaValueFormatterParams<Order, 'status'>) => {
if (value === OrderStatus.STATUS_REJECTED) {
return `${OrderStatusMapping[value]}: ${
data.rejectionReason &&
data?.rejectionReason &&
OrderRejectionReasonMapping[data.rejectionReason]
}`;
}
return OrderStatusMapping[value];
return value ? OrderStatusMapping[value] : '';
}}
/>
<AgGridColumn
@ -177,7 +178,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
data,
value,
}: VegaValueFormatterParams<Order, 'remaining'>) => {
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<AgGridReact, OrderListTableProps>(
value,
data,
}: VegaValueFormatterParams<Order, 'price'>) => {
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<AgGridReact, OrderListTableProps>(
}: VegaValueFormatterParams<Order, 'timeInForce'>) => {
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<AgGridReact, OrderListTableProps>(
return `${OrderTimeInForceMapping[value]}: ${expiry}`;
}
return OrderTimeInForceMapping[value];
return value ? OrderTimeInForceMapping[value] : '';
}}
/>
<AgGridColumn
@ -245,7 +250,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
field="status"
cellRenderer={({ data }: VegaICellRendererParams<Order>) => {
if (isOrderAmendable(data)) {
return (
return data ? (
<div className="flex gap-2">
<Button
data-testid="edit"
@ -262,7 +267,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
{t('Cancel')}
</Button>
</div>
);
) : null;
}
return null;

View File

@ -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);
}
);
});

View File

@ -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<number | string> => /^-?\d*\.?\d+$/.test(String(value));

View File

@ -16,8 +16,8 @@ type RowHelper<TObj, TRow, TField extends Field> = Omit<
TObj,
'data' | 'value'
> & {
data: TRow;
value: Get<TRow, TField>;
data?: TRow;
value?: Get<TRow, TField>;
};
export type VegaValueFormatterParams<TRow, TField extends Field> = RowHelper<

View File

@ -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<WithdrawalFields, 'amount'>) => {
return addDecimalsFormatNumber(value, data.asset.decimals);
return isNumeric(value) && data?.asset
? addDecimalsFormatNumber(value, data.asset.decimals)
: null;
}}
/>
<AgGridColumn
@ -105,7 +108,7 @@ export const PendingWithdrawalsTable = (
WithdrawalFields,
'createdTimestamp'
>) => {
return getDateTimeFormat().format(new Date(value));
return value ? getDateTimeFormat().format(new Date(value)) : '';
}}
/>
<AgGridColumn

View File

@ -4,6 +4,7 @@ import {
t,
truncateByChars,
addDecimalsFormatNumber,
isNumeric,
} from '@vegaprotocol/react-helpers';
import type {
TypedDataAgGrid,
@ -37,7 +38,9 @@ export const WithdrawalsTable = (props: TypedDataAgGrid<WithdrawalFields>) => {
value,
data,
}: VegaValueFormatterParams<WithdrawalFields, 'amount'>) => {
return addDecimalsFormatNumber(value, data.asset.decimals);
return isNumeric(value) && data?.asset
? addDecimalsFormatNumber(value, data.asset.decimals)
: '';
}}
/>
<AgGridColumn
@ -64,7 +67,7 @@ export const WithdrawalsTable = (props: TypedDataAgGrid<WithdrawalFields>) => {
WithdrawalFields,
'withdrawnTimestamp'
>) => {
const ts = data.withdrawnTimestamp;
const ts = data?.withdrawnTimestamp;
if (!ts) return '-';
return getDateTimeFormat().format(new Date(ts));
}}