Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbb6a42675 | ||
|
|
7cafc6e6cc | ||
|
|
cd56545042 | ||
|
|
b6e21f7265 | ||
|
|
b1979c4948 | ||
|
|
5e674b1bec | ||
|
|
a9ca215276 | ||
|
|
61a9eb2f5c | ||
|
|
bb47747501 |
@@ -1,5 +1,5 @@
|
||||
import { useAssetDataProvider } from '@vegaprotocol/assets';
|
||||
import { addDecimalsFixedFormatNumber } from '@vegaprotocol/utils';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { AssetLink } from '../links';
|
||||
|
||||
export type AssetBalanceProps = {
|
||||
@@ -23,7 +23,7 @@ const AssetBalance = ({
|
||||
|
||||
const label =
|
||||
!loading && asset && asset.decimals
|
||||
? addDecimalsFixedFormatNumber(price, asset.decimals)
|
||||
? addDecimalsFormatNumber(price, asset.decimals)
|
||||
: price;
|
||||
|
||||
return (
|
||||
|
||||
@@ -120,6 +120,6 @@ describe('Order TX Summary component', () => {
|
||||
// After fetch renders formatted price and asset quotename
|
||||
expect(await res.findByText('3.33')).toBeInTheDocument();
|
||||
expect(await res.findByText('TEST')).toBeInTheDocument();
|
||||
expect(await res.getByText('0.10')).toBeInTheDocument();
|
||||
expect(await res.findByText('0.1')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('Price in Market component', () => {
|
||||
|
||||
it('Renders the formatted price when market data is fetched, using market decimals by default', async () => {
|
||||
const res = render(renderComponent('100', '123', [fullMock]));
|
||||
expect(await res.findByText('1.00')).toBeInTheDocument();
|
||||
expect(await res.findByText('1')).toBeInTheDocument();
|
||||
expect(await res.findByText('dai')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -69,6 +69,6 @@ describe('Size in Market component', () => {
|
||||
|
||||
it('Renders the formatted size when market data is fetched', async () => {
|
||||
const res = render(renderComponent('100', '123', [fullMock]));
|
||||
expect(await res.findByText('1.00')).toBeInTheDocument();
|
||||
expect(await res.findByText('1')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,14 +3,14 @@ import { formatNumber } from './format-number';
|
||||
|
||||
describe('formatNumber and formatNumberPercentage', () => {
|
||||
it.each([
|
||||
{ v: new BigNumber(123), d: 3, o: '123.00' },
|
||||
{ v: new BigNumber(123), d: 3, o: '123.000' },
|
||||
{ v: new BigNumber(123.123), d: 3, o: '123.123' },
|
||||
{ v: new BigNumber(123.123), d: 6, o: '123.123' },
|
||||
{ v: new BigNumber(123.123), d: 6, o: '123.123000' },
|
||||
{ v: new BigNumber(123.123), d: 0, o: '123' },
|
||||
{ v: new BigNumber(123), d: undefined, o: '123.00' }, // it default to 2 decimal places
|
||||
{ v: new BigNumber(30000), d: undefined, o: '30,000.00' },
|
||||
{ v: new BigNumber(3.000001), d: undefined, o: '3.000001' },
|
||||
])(`formats given number with decimals correctly`, ({ v, d, o }) => {
|
||||
])(`formatNumber($v, $d) -> $o`, ({ v, d, o }) => {
|
||||
expect(formatNumber(v, d)).toStrictEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getProposalDialogIcon,
|
||||
getProposalDialogIntent,
|
||||
getProposalDialogTitle,
|
||||
useGetProposalDialogTitle,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import type { ProposalEventFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import type { DialogProps } from '@vegaprotocol/proposals';
|
||||
@@ -15,6 +15,7 @@ export const ProposalFormTransactionDialog = ({
|
||||
finalizedProposal,
|
||||
TransactionDialog,
|
||||
}: ProposalFormTransactionDialogProps) => {
|
||||
const title = useGetProposalDialogTitle(finalizedProposal?.state);
|
||||
// Render a custom complete UI if the proposal was rejected otherwise
|
||||
// pass undefined so that the default vega transaction dialog UI gets used
|
||||
const completeContent = finalizedProposal?.rejectionReason ? (
|
||||
@@ -24,7 +25,7 @@ export const ProposalFormTransactionDialog = ({
|
||||
return (
|
||||
<div data-testid="proposal-transaction-dialog">
|
||||
<TransactionDialog
|
||||
title={getProposalDialogTitle(finalizedProposal?.state)}
|
||||
title={title}
|
||||
intent={getProposalDialogIntent(finalizedProposal?.state)}
|
||||
icon={getProposalDialogIcon(finalizedProposal?.state)}
|
||||
content={{
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -41,6 +41,7 @@ export interface NewAssetProposalFormFields {
|
||||
const DOCS_LINK = '/new-asset-proposal';
|
||||
|
||||
export const ProposeNewAsset = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -39,6 +39,7 @@ export interface NewMarketProposalFormFields {
|
||||
const DOCS_LINK = '/new-market-proposal';
|
||||
|
||||
export const ProposeNewMarket = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
RoundedWrapper,
|
||||
TextArea,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -31,6 +31,7 @@ export interface RawProposalFormFields {
|
||||
}
|
||||
|
||||
export const ProposeRaw = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -39,6 +39,7 @@ export interface UpdateAssetProposalFormFields {
|
||||
const DOCS_LINK = '/update-asset-proposal';
|
||||
|
||||
export const ProposeUpdateAsset = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
|
||||
+3
-2
@@ -8,7 +8,7 @@ import {
|
||||
useProposalSubmit,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
|
||||
import { validateJson } from '@vegaprotocol/utils';
|
||||
import { useValidateJson } from '@vegaprotocol/utils';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@@ -53,6 +53,7 @@ export interface UpdateMarketProposalFormFields {
|
||||
const DOCS_LINK = '/update-market-proposal';
|
||||
|
||||
export const ProposeUpdateMarket = () => {
|
||||
const validateJson = useValidateJson();
|
||||
const {
|
||||
params,
|
||||
loading: networkParamsLoading,
|
||||
@@ -260,7 +261,7 @@ export const ProposeUpdateMarket = () => {
|
||||
</FormGroup>
|
||||
|
||||
{selectedMarket && (
|
||||
<div className="mt-[-20px] mb-6">
|
||||
<div className="mb-6 mt-[-20px]">
|
||||
<KeyValueTable data-testid="update-market-details">
|
||||
<KeyValueTableRow>
|
||||
{t('MarketName')}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getNumberFormat } from '@vegaprotocol/utils';
|
||||
import { addDays } from 'date-fns';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import omit from 'lodash/omit';
|
||||
import { useReferralProgramQuery } from './__generated__/CurrentReferralProgram';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
|
||||
const STAKING_TIERS_MAPPING: Record<number, string> = {
|
||||
1: 'Tradestarter',
|
||||
@@ -85,9 +85,7 @@ export const useReferralProgram = () => {
|
||||
discountFactor: Number(t.referralDiscountFactor),
|
||||
discount: Number(t.referralDiscountFactor) * 100 + '%',
|
||||
minimumVolume: Number(t.minimumRunningNotionalTakerVolume),
|
||||
volume: getNumberFormat(0).format(
|
||||
Number(t.minimumRunningNotionalTakerVolume)
|
||||
),
|
||||
volume: addDecimalsFormatNumber(t.minimumRunningNotionalTakerVolume, 0),
|
||||
epochs: Number(t.minimumEpochs),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
addDecimalsFormatNumber,
|
||||
getDateFormat,
|
||||
getDateTimeFormat,
|
||||
getNumberFormat,
|
||||
getUserLocale,
|
||||
removePaginationWrapper,
|
||||
} from '@vegaprotocol/utils';
|
||||
@@ -256,7 +255,7 @@ export const Statistics = ({
|
||||
})}
|
||||
description={<QUSDTooltip />}
|
||||
>
|
||||
{getNumberFormat(0).format(Number(totalCommissionValue))}
|
||||
{addDecimalsFormatNumber(totalCommissionValue.toString(), 0)}
|
||||
</StatTile>
|
||||
);
|
||||
|
||||
@@ -418,8 +417,8 @@ export const Statistics = ({
|
||||
)
|
||||
.map((r) => ({
|
||||
...r,
|
||||
volume: getNumberFormat(0).format(r.volume),
|
||||
commission: getNumberFormat(0).format(r.commission),
|
||||
volume: addDecimalsFormatNumber(r.volume, 0),
|
||||
commission: addDecimalsFormatNumber(r.commission, 0),
|
||||
}))
|
||||
.reverse()}
|
||||
/>
|
||||
|
||||
@@ -139,7 +139,7 @@ describe('RewardPot', () => {
|
||||
renderComponent(props);
|
||||
|
||||
expect(screen.getByTestId('total-rewards')).toHaveTextContent(
|
||||
`7.00 ${rewardAsset.symbol}`
|
||||
`7.0000 ${rewardAsset.symbol}`
|
||||
);
|
||||
|
||||
expect(screen.getByText(/Locked/).nextElementSibling).toHaveTextContent(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import { act, render, screen, within } from '@testing-library/react';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import type { AccountFields } from './accounts-data-provider';
|
||||
import { getAccountData } from './accounts-data-provider';
|
||||
@@ -123,11 +123,13 @@ describe('AccountsTable', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
const cells = await screen.findAllByRole('gridcell');
|
||||
const expectedValues = ['tBTC', '1,256.00', '1,256.00', '2,512.00', ''];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
await assertCells({
|
||||
usedAmount: '1,256',
|
||||
usedPct: '0.00%',
|
||||
available: '1,256',
|
||||
total: '2,512',
|
||||
});
|
||||
|
||||
const rows = container.querySelector('.ag-center-cols-container');
|
||||
expect(rows?.childElementCount).toBe(1);
|
||||
});
|
||||
@@ -160,12 +162,13 @@ describe('AccountsTable', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
const cells = await screen.findAllByRole('gridcell');
|
||||
const expectedValues = ['tBTC', '1,256.00', '1,256.00', '2,512.00', ''];
|
||||
expect(cells.length).toBe(expectedValues.length);
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
await assertCells({
|
||||
usedAmount: '1,256',
|
||||
usedPct: '0.00%',
|
||||
available: '1,256',
|
||||
total: '2,512',
|
||||
});
|
||||
|
||||
const rows = container.querySelector('.ag-center-cols-container');
|
||||
expect(rows?.childElementCount).toBe(1);
|
||||
});
|
||||
@@ -248,4 +251,36 @@ describe('AccountsTable', () => {
|
||||
];
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
const assertCells = async ({
|
||||
usedAmount,
|
||||
usedPct,
|
||||
available,
|
||||
total,
|
||||
}: {
|
||||
usedAmount: string;
|
||||
usedPct: string;
|
||||
available: string;
|
||||
total: string;
|
||||
}) => {
|
||||
const cells = await screen.findAllByRole('gridcell');
|
||||
|
||||
const usedCell = within(
|
||||
cells.find(
|
||||
(cell) => cell.getAttribute('col-id') === 'used'
|
||||
) as HTMLElement
|
||||
);
|
||||
expect(usedCell.getByTestId('used-amount')).toHaveTextContent(usedAmount);
|
||||
expect(usedCell.getByTestId('used-pct')).toHaveTextContent(usedPct);
|
||||
|
||||
const availableCell = cells.find(
|
||||
(cell) => cell.getAttribute('col-id') === 'available'
|
||||
);
|
||||
expect(availableCell).toHaveTextContent(available);
|
||||
|
||||
const totalCell = cells.find(
|
||||
(cell) => cell.getAttribute('col-id') === 'total'
|
||||
);
|
||||
expect(totalCell).toHaveTextContent(total);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -178,20 +178,28 @@ export const AccountTable = ({
|
||||
|
||||
return data.breakdown ? (
|
||||
<>
|
||||
<span className="underline">{valueFormatted}</span>
|
||||
<span className="underline" data-testid="used-amount">
|
||||
{valueFormatted}
|
||||
</span>
|
||||
<span
|
||||
className={classNames(
|
||||
colorClass(percentageUsed),
|
||||
'ml-1 inline-block w-14'
|
||||
)}
|
||||
data-testid="used-pct"
|
||||
>
|
||||
{percentageUsed.toFixed(2)}%
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="underline">{valueFormatted}</span>
|
||||
<span className="inline-block ml-2 w-14 text-muted">
|
||||
<span className="underline" data-testid="used-amount">
|
||||
{valueFormatted}
|
||||
</span>
|
||||
<span
|
||||
className="inline-block ml-2 w-14 text-muted"
|
||||
data-testid="used-pct"
|
||||
>
|
||||
{(0).toFixed(2)}%
|
||||
</span>
|
||||
</>
|
||||
|
||||
@@ -63,9 +63,9 @@ describe('BreakdownTable', () => {
|
||||
const expectedValues = [
|
||||
'BTCUSD.MF21',
|
||||
'Margin',
|
||||
'1,256.00 (50%)',
|
||||
'1,256.00',
|
||||
'1,256.00',
|
||||
'1,256 (50%)',
|
||||
'1,256',
|
||||
'1,256',
|
||||
];
|
||||
cells.slice(0, -1).forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('MarginHealthChart', () => {
|
||||
it('should render correct values', async () => {
|
||||
render(<MarginHealthChart marketId="marketId" assetId="assetId" />);
|
||||
const chart = screen.getByTestId('margin-health-chart');
|
||||
expect(chart).toHaveTextContent('3.00 above maintenance level');
|
||||
expect(chart).toHaveTextContent('3 above maintenance level');
|
||||
const red = screen.getByTestId('margin-health-chart-red');
|
||||
const orange = screen.getByTestId('margin-health-chart-orange');
|
||||
const yellow = screen.getByTestId('margin-health-chart-yellow');
|
||||
@@ -121,7 +121,7 @@ describe('MarginHealthChartTooltip', () => {
|
||||
expect(value).toHaveTextContent(expectedLabels[i]);
|
||||
});
|
||||
const values = await screen.findAllByTestId('margin-health-tooltip-value');
|
||||
const expectedValues = ['4.00', '5.00', '6.00', '8.00', '10.00'];
|
||||
const expectedValues = ['4', '5', '6', '8', '10'];
|
||||
values.forEach((value, i) => {
|
||||
expect(value).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
@@ -137,7 +137,7 @@ describe('MarginHealthChartTooltip', () => {
|
||||
);
|
||||
|
||||
let values = await screen.findAllByTestId('margin-health-tooltip-value');
|
||||
expect(values[2]).toHaveTextContent('7.00');
|
||||
expect(values[2]).toHaveTextContent('7');
|
||||
|
||||
rerender(
|
||||
<MarginHealthChartTooltip
|
||||
@@ -149,6 +149,6 @@ describe('MarginHealthChartTooltip', () => {
|
||||
|
||||
values = await screen.findAllByTestId('margin-health-tooltip-value');
|
||||
expect(values.length).toBe(5);
|
||||
expect(values[3]).toHaveTextContent('9.00');
|
||||
expect(values[3]).toHaveTextContent('9');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import {
|
||||
maxSafe,
|
||||
required,
|
||||
vegaPublicKey,
|
||||
useMaxSafe,
|
||||
useRequired,
|
||||
useVegaPublicKey,
|
||||
addDecimal,
|
||||
formatNumber,
|
||||
addDecimalsFormatNumber,
|
||||
@@ -67,6 +67,9 @@ export const TransferForm = ({
|
||||
minQuantumMultiple,
|
||||
}: TransferFormProps) => {
|
||||
const t = useT();
|
||||
const maxSafe = useMaxSafe();
|
||||
const required = useRequired();
|
||||
const vegaPublicKey = useVegaPublicKey();
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
@@ -415,7 +418,7 @@ export const TransferForm = ({
|
||||
{accountBalance && (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-0 right-0 ml-auto text-xs underline"
|
||||
className="absolute right-0 top-0 ml-auto text-xs underline"
|
||||
onClick={() =>
|
||||
setValue('amount', parseFloat(accountBalance).toString(), {
|
||||
shouldValidate: true,
|
||||
@@ -491,7 +494,7 @@ export const TransferFee = ({
|
||||
const totalValue = new BigNumber(transferAmount).plus(fee).toString();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col mb-4 text-xs gap-2">
|
||||
<div className="mb-4 flex flex-col gap-2 text-xs">
|
||||
<div className="flex flex-wrap items-center justify-between gap-1">
|
||||
<Tooltip
|
||||
description={t(
|
||||
@@ -560,7 +563,7 @@ export const AddressField = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onChange}
|
||||
className="absolute top-0 right-0 ml-auto text-xs underline"
|
||||
className="absolute right-0 top-0 ml-auto text-xs underline"
|
||||
>
|
||||
{isInput ? t('Select from wallet') : t('Enter manually')}
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
import { addDecimalsFixedFormatNumber } from '@vegaprotocol/utils';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { NumericCell } from './numeric-cell';
|
||||
import { theme } from '@vegaprotocol/tailwindcss-config';
|
||||
|
||||
@@ -57,7 +57,7 @@ export const CumulativeVol = memo(
|
||||
(
|
||||
<NumericCell
|
||||
value={Number(indicativeVolume)}
|
||||
valueFormatted={addDecimalsFixedFormatNumber(
|
||||
valueFormatted={addDecimalsFormatNumber(
|
||||
indicativeVolume,
|
||||
positionDecimalPlaces ?? 0
|
||||
)}
|
||||
@@ -69,7 +69,7 @@ export const CumulativeVol = memo(
|
||||
{ask ? (
|
||||
<NumericCell
|
||||
value={ask}
|
||||
valueFormatted={addDecimalsFixedFormatNumber(
|
||||
valueFormatted={addDecimalsFormatNumber(
|
||||
ask,
|
||||
positionDecimalPlaces ?? 0
|
||||
)}
|
||||
@@ -79,7 +79,7 @@ export const CumulativeVol = memo(
|
||||
{bid ? (
|
||||
<NumericCell
|
||||
value={ask}
|
||||
valueFormatted={addDecimalsFixedFormatNumber(
|
||||
valueFormatted={addDecimalsFormatNumber(
|
||||
bid,
|
||||
positionDecimalPlaces ?? 0
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { getDecimalSeparator, isNumeric } from '@vegaprotocol/utils';
|
||||
import { getNumberParts, isNumeric } from '@vegaprotocol/utils';
|
||||
|
||||
interface NumericCellProps {
|
||||
value: number | bigint | null | undefined;
|
||||
@@ -23,7 +23,7 @@ export const NumericCell = forwardRef<HTMLSpanElement, NumericCellProps>(
|
||||
);
|
||||
}
|
||||
|
||||
const decimalSeparator = getDecimalSeparator();
|
||||
const decimalSeparator = getNumberParts().decimalSeparator;
|
||||
const valueSplit: string[] = decimalSeparator
|
||||
? valueFormatted.split(decimalSeparator).map((v) => `${v}`)
|
||||
: [`${value}`];
|
||||
|
||||
@@ -10,12 +10,12 @@ describe('PriceChangeCell', () => {
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText('-48.51%')).toBeInTheDocument();
|
||||
expect(screen.getByText('-22.10')).toBeInTheDocument();
|
||||
expect(screen.getByText('-22.100')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders correctly and calculates the price change without decimals', () => {
|
||||
render(<PriceChangeCell candles={['45556', '678678', '23456']} />);
|
||||
expect(screen.getByText('-48.51%')).toBeInTheDocument();
|
||||
expect(screen.getByText('-22,100.00')).toBeInTheDocument();
|
||||
expect(screen.getByText('-22,100.000')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Controller, type Control } from 'react-hook-form';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
||||
import { toDecimal, useValidateAmount } from '@vegaprotocol/utils';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
@@ -28,6 +28,7 @@ export const DealTicketSizeIceberg = ({
|
||||
peakSize,
|
||||
}: DealTicketSizeIcebergProps) => {
|
||||
const t = useT();
|
||||
const validateAmount = useValidateAmount();
|
||||
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
||||
|
||||
const renderPeakSizeError = () => {
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('StopOrder', () => {
|
||||
await userEvent.type(screen.getByTestId(sizeInput), '10');
|
||||
await userEvent.type(screen.getByTestId(priceInput), '10');
|
||||
expect(screen.getByTestId('deal-ticket-fee-notional')).toHaveTextContent(
|
||||
'Notional100.00 BTC'
|
||||
'Notional100 BTC'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -147,13 +147,13 @@ describe('StopOrder', () => {
|
||||
await userEvent.type(screen.getByTestId(sizeInput), '10');
|
||||
// price trigger is selected but it's empty, calculate base on size and marketPrice prop
|
||||
expect(screen.getByTestId('deal-ticket-fee-notional')).toHaveTextContent(
|
||||
'Notional20.00 BTC'
|
||||
'Notional20 BTC'
|
||||
);
|
||||
|
||||
await userEvent.type(screen.getByTestId(triggerPriceInput), '3');
|
||||
// calculate base on size and price trigger
|
||||
expect(screen.getByTestId('deal-ticket-fee-notional')).toHaveTextContent(
|
||||
'Notional30.00 BTC'
|
||||
'Notional30 BTC'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
formatValue,
|
||||
removeDecimal,
|
||||
toDecimal,
|
||||
validateAmount,
|
||||
useValidateAmount,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { type Control, type UseFormWatch } from 'react-hook-form';
|
||||
import { useForm, Controller, useController } from 'react-hook-form';
|
||||
@@ -109,6 +109,7 @@ const Trigger = ({
|
||||
decimalPlaces: number;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const validateAmount = useValidateAmount();
|
||||
const triggerType = watch(oco ? 'ocoTriggerType' : 'triggerType');
|
||||
const triggerDirection = watch('triggerDirection');
|
||||
const isPriceTrigger = triggerType === 'price';
|
||||
@@ -341,6 +342,7 @@ const Size = ({
|
||||
assetUnit?: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const validateAmount = useValidateAmount();
|
||||
return (
|
||||
<Controller
|
||||
name={oco ? 'ocoSize' : 'size'}
|
||||
@@ -401,6 +403,7 @@ const Price = ({
|
||||
oco?: boolean;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const validateAmount = useValidateAmount();
|
||||
if (watch(oco ? 'ocoType' : 'type') === Schema.OrderType.TYPE_MARKET) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import { useOpenVolume } from '@vegaprotocol/positions';
|
||||
import {
|
||||
toBigNum,
|
||||
removeDecimal,
|
||||
validateAmount,
|
||||
useValidateAmount,
|
||||
toDecimal,
|
||||
formatForInput,
|
||||
formatValue,
|
||||
@@ -140,6 +140,7 @@ export const DealTicket = ({
|
||||
onDeposit,
|
||||
}: DealTicketProps) => {
|
||||
const t = useT();
|
||||
const validateAmount = useValidateAmount();
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const setType = useDealTicketFormValues((state) => state.setType);
|
||||
const storedFormValues = useDealTicketFormValues(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { Asset, AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { AssetOption } from '@vegaprotocol/assets';
|
||||
import {
|
||||
ethereumAddress,
|
||||
required,
|
||||
vegaPublicKey,
|
||||
minSafe,
|
||||
maxSafe,
|
||||
useEthereumAddress,
|
||||
useRequired,
|
||||
useVegaPublicKey,
|
||||
useMinSafe,
|
||||
useMaxSafe,
|
||||
addDecimal,
|
||||
isAssetTypeERC20,
|
||||
formatNumber,
|
||||
@@ -85,6 +85,11 @@ export const DepositForm = ({
|
||||
isFaucetable,
|
||||
}: DepositFormProps) => {
|
||||
const t = useT();
|
||||
const ethereumAddress = useEthereumAddress();
|
||||
const required = useRequired();
|
||||
const vegaPublicKey = useVegaPublicKey();
|
||||
const minSafe = useMinSafe();
|
||||
const maxSafe = useMaxSafe();
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const openDialog = useWeb3ConnectStore((store) => store.open);
|
||||
const { isActive, account } = useWeb3React();
|
||||
@@ -459,7 +464,7 @@ const UseButton = (props: UseButtonProps) => {
|
||||
<button
|
||||
{...props}
|
||||
type="button"
|
||||
className="absolute top-0 right-0 ml-auto text-sm underline"
|
||||
className="absolute right-0 top-0 ml-auto text-sm underline"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -519,7 +524,7 @@ export const AddressField = ({
|
||||
setIsInput((curr) => !curr);
|
||||
onChange();
|
||||
}}
|
||||
className="absolute top-0 right-0 ml-auto text-sm underline"
|
||||
className="absolute right-0 top-0 ml-auto text-sm underline"
|
||||
data-testid="enter-pubkey-manually"
|
||||
>
|
||||
{isInput ? t('Select from wallet') : t('Enter manually')}
|
||||
|
||||
@@ -84,11 +84,11 @@ describe('FillsTable', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market?.tradableInstrument.instrument.code || '',
|
||||
'+3.00',
|
||||
'1.00 BTC',
|
||||
'+3',
|
||||
'1 BTC',
|
||||
'3.00 BTC',
|
||||
'Maker',
|
||||
'2.00 BTC',
|
||||
'2 BTC',
|
||||
'0.27 BTC',
|
||||
getDateTimeFormat().format(new Date(buyerFill.createdAt)),
|
||||
'', // action column
|
||||
@@ -121,8 +121,8 @@ describe('FillsTable', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market?.tradableInstrument.instrument.code || '',
|
||||
'-3.00',
|
||||
'1.00 BTC',
|
||||
'-3',
|
||||
'1 BTC',
|
||||
'3.00 BTC',
|
||||
'Taker',
|
||||
'0.03 BTC',
|
||||
@@ -158,8 +158,8 @@ describe('FillsTable', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market?.tradableInstrument.instrument.code || '',
|
||||
'-3.00',
|
||||
'1.00 BTC',
|
||||
'-3',
|
||||
'1 BTC',
|
||||
'3.00 BTC',
|
||||
'-',
|
||||
'0.03 BTC',
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('FundingPaymentsTable', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
fundingPayment.market?.tradableInstrument.instrument.code || '',
|
||||
'1.00 BTC',
|
||||
'1 BTC',
|
||||
getDateTimeFormat().format(new Date(fundingPayment.timestamp)),
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
@@ -77,7 +77,7 @@ describe('FundingPaymentsTable', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
fundingPayment.market?.tradableInstrument.instrument.code || '',
|
||||
'-1.00 BTC',
|
||||
'-1 BTC',
|
||||
getDateTimeFormat().format(new Date(fundingPayment.timestamp)),
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import en_governance from './locales/en/governance.json';
|
||||
import en_trading from './locales/en/trading.json';
|
||||
import en_markets from './locales/en/markets.json';
|
||||
import en_web3 from './locales/en/web3.json';
|
||||
import en_proposals from './locales/en/proposals.json';
|
||||
import en_positions from './locales/en/positions.json';
|
||||
import en_trades from './locales/en/trading.json';
|
||||
import en_ui_toolkit from './locales/en/ui-toolkit.json';
|
||||
@@ -33,6 +34,7 @@ export const locales = {
|
||||
markets: en_markets,
|
||||
web3: en_web3,
|
||||
positions: en_positions,
|
||||
proposals: en_proposals,
|
||||
trades: en_trades,
|
||||
'ui-toolkit': en_ui_toolkit,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"[This is {{network}} transaction only]": "[This is {{network}} transaction only]",
|
||||
"{{proposalChange}} proposal {{proposalState}}": "{{proposalChange}} proposal {{proposalState}}",
|
||||
"<0>{{count}}</0> blocks": "<0>{{count}}</0> blocks",
|
||||
"Awaiting network confirmation": "Awaiting network confirmation",
|
||||
"blocks": "blocks",
|
||||
"Changes have been proposed for this asset.": "Changes have been proposed for this asset.",
|
||||
"Changes have been proposed for this market.": "Changes have been proposed for this market.",
|
||||
"Closing date": "Closing date",
|
||||
"Confirm transaction in wallet": "Confirm transaction in wallet",
|
||||
"Enactment date: {{date}}": "Enactment date: {{date}}",
|
||||
"Enactment date": "Enactment date",
|
||||
"estimated time to protocol upgrade": "estimated time to protocol upgrade",
|
||||
"estimating...": "estimating...",
|
||||
"Market": "Market",
|
||||
"Network upgrade in {{countdown}}": "Network upgrade in {{countdown}}",
|
||||
"No proposed markets": "No proposed markets",
|
||||
"Parent market": "Parent market",
|
||||
"Please open your wallet application and confirm or reject the transaction": "Please open your wallet application and confirm or reject the transaction",
|
||||
"Please wait for your transaction to be confirmed": "Please wait for your transaction to be confirmed",
|
||||
"Proposal declined": "Proposal declined",
|
||||
"Proposal enacted": "Proposal enacted",
|
||||
"Proposal failed": "Proposal failed",
|
||||
"Proposal passed": "Proposal passed",
|
||||
"Proposal rejected": "Proposal rejected",
|
||||
"Proposal submitted": "Proposal submitted",
|
||||
"Proposal waiting for node vote": "Proposal waiting for node vote",
|
||||
"Rejection reason: {{reason}}": "Rejection reason: {{reason}}",
|
||||
"Settlement asset": "Settlement asset",
|
||||
"State": "State",
|
||||
"Submission failed": "Submission failed",
|
||||
"The network is being upgraded to {{vegaReleaseTag}}": "The network is being upgraded to {{vegaReleaseTag}}",
|
||||
"The network will upgrade to {{vegaReleaseTag}} in {{countdown}}": "The network will upgrade to {{vegaReleaseTag}} in {{countdown}}",
|
||||
"Trading activity will be interrupted, manage your risk appropriately.": "Trading activity will be interrupted, manage your risk appropriately.",
|
||||
"Trading and other network activity has stopped until the upgrade is complete.": "Trading and other network activity has stopped until the upgrade is complete.",
|
||||
"Transaction complete": "Transaction complete",
|
||||
"Transaction failed": "Transaction failed",
|
||||
"Unknown proposal {{proposalState}}": "Unknown proposal {{proposalState}}",
|
||||
"Update <0>{{key}}</0> to {{value}}": "Update <0>{{key}}</0> to {{value}}",
|
||||
"View details": "View details",
|
||||
"View in block explorer": "View in block explorer",
|
||||
"View proposal details": "View proposal details",
|
||||
"View proposal": "View proposal",
|
||||
"Voting": "Voting",
|
||||
"Your transaction has been confirmed": "Your transaction has been confirmed"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"Expired on {{date}}": "Expired on {{date}}",
|
||||
"Not time-based": "Not time-based",
|
||||
"Expired": "Expired",
|
||||
"Mark": "Mark",
|
||||
"Required": "Required",
|
||||
"Invalid Ethereum address": "Invalid Ethereum address",
|
||||
"Invalid Vega key": "Invalid Vega key",
|
||||
"Value is below minimum": "Value is below minimum",
|
||||
"Value is above maximum": "Value is above maximum",
|
||||
"Must be valid JSON": "Must be valid JSON",
|
||||
"{{field}} must be a multiple of {{step}} for this market": "{{field}} must be a multiple of {{step}} for this market",
|
||||
"{{field}} must be whole numbers for this market": "{{field}} must be whole numbers for this market",
|
||||
"{{field}} accepts up to {{decimals}} decimal places": "{{field}} accepts up to {{decimals}} decimal places"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DepthChart } from 'pennant';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { addDecimal, getNumberFormat } from '@vegaprotocol/utils';
|
||||
import { addDecimal, addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { marketDepthProvider } from './market-depth-provider';
|
||||
@@ -216,13 +216,13 @@ export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
|
||||
|
||||
const volumeFormat = useCallback(
|
||||
(volume: number) =>
|
||||
getNumberFormat(market?.positionDecimalPlaces || 0).format(volume),
|
||||
addDecimalsFormatNumber(volume, market?.positionDecimalPlaces || 0),
|
||||
[market?.positionDecimalPlaces]
|
||||
);
|
||||
|
||||
const priceFormat = useCallback(
|
||||
(price: number) =>
|
||||
getNumberFormat(market?.decimalPlaces || 0).format(price),
|
||||
addDecimalsFormatNumber(price, market?.decimalPlaces || 0),
|
||||
[market?.decimalPlaces]
|
||||
);
|
||||
|
||||
|
||||
@@ -102,12 +102,7 @@ export const OrderbookControls = ({
|
||||
};
|
||||
|
||||
export const formatResolution = (r: number, decimalPlaces: number) => {
|
||||
let num = addDecimalsFormatNumber(r, decimalPlaces);
|
||||
|
||||
// Remove trailing zeroes
|
||||
num = num.replace(/\.?0+$/, '');
|
||||
|
||||
return num;
|
||||
return addDecimalsFormatNumber(r, decimalPlaces);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { addDecimal, addDecimalsFixedFormatNumber } from '@vegaprotocol/utils';
|
||||
import { addDecimal, addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { NumericCell } from '@vegaprotocol/datagrid';
|
||||
import { VolumeType } from './orderbook-data';
|
||||
import classNames from 'classnames';
|
||||
@@ -55,7 +55,7 @@ export const OrderbookRow = memo(
|
||||
<NumericCell
|
||||
testId={`price-${price}`}
|
||||
value={BigInt(price)}
|
||||
valueFormatted={addDecimalsFixedFormatNumber(
|
||||
valueFormatted={addDecimalsFormatNumber(
|
||||
price,
|
||||
decimalPlaces,
|
||||
priceFormatDecimalPlaces
|
||||
@@ -76,7 +76,7 @@ export const OrderbookRow = memo(
|
||||
<NumericCell
|
||||
testId={`${txtId}-vol-${price}`}
|
||||
value={volume}
|
||||
valueFormatted={addDecimalsFixedFormatNumber(
|
||||
valueFormatted={addDecimalsFormatNumber(
|
||||
volume,
|
||||
positionDecimalPlaces ?? 0
|
||||
)}
|
||||
@@ -94,7 +94,7 @@ export const OrderbookRow = memo(
|
||||
<NumericCell
|
||||
testId={`cumulative-vol-${price}`}
|
||||
value={cumulativeVolume}
|
||||
valueFormatted={addDecimalsFixedFormatNumber(
|
||||
valueFormatted={addDecimalsFormatNumber(
|
||||
cumulativeVolume,
|
||||
positionDecimalPlaces
|
||||
)}
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('Orderbook', () => {
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId(`last-traded-${params.lastTradedPrice}`)
|
||||
).toHaveTextContent('122.90');
|
||||
).toHaveTextContent('122.9');
|
||||
});
|
||||
|
||||
it('should format correctly the numbers on resolution change', async () => {
|
||||
|
||||
@@ -172,8 +172,9 @@ export const Orderbook = ({
|
||||
|
||||
// we'll want to only display a relevant number of dps based on the
|
||||
// current resolution selection
|
||||
const priceFormatDecimalPlaces = Math.ceil(
|
||||
decimalPlaces - Math.log10(resolution)
|
||||
const priceFormatDecimalPlaces = Math.max(
|
||||
0,
|
||||
Math.ceil(decimalPlaces - Math.log10(resolution))
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
getDateTimeFormat,
|
||||
addDecimal,
|
||||
addDecimalsFormatNumber,
|
||||
validateAmount,
|
||||
useValidateAmount,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { Size } from '@vegaprotocol/datagrid';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
@@ -39,6 +39,7 @@ export const OrderEditDialog = ({
|
||||
onSubmit,
|
||||
}: OrderEditDialogProps) => {
|
||||
const t = useT();
|
||||
const validateAmount = useValidateAmount();
|
||||
const headerClassName = 'text-xs font-bold text-black dark:text-white';
|
||||
const {
|
||||
register,
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('OrderListTable', () => {
|
||||
const expectedValues: string[] = [
|
||||
marketOrder.market?.tradableInstrument.instrument.code || '',
|
||||
'0.05',
|
||||
'0.10',
|
||||
'0.1',
|
||||
Schema.OrderTypeMapping[marketOrder.type as Schema.OrderType] || '',
|
||||
Schema.OrderStatusMapping[marketOrder.status],
|
||||
'-',
|
||||
@@ -102,7 +102,7 @@ describe('OrderListTable', () => {
|
||||
const expectedValues: string[] = [
|
||||
limitOrder.market?.tradableInstrument.instrument.code || '',
|
||||
'0.05',
|
||||
'0.10',
|
||||
'0.1',
|
||||
Schema.OrderTypeMapping[limitOrder.type || Schema.OrderType.TYPE_LIMIT],
|
||||
Schema.OrderStatusMapping[limitOrder.status],
|
||||
'-',
|
||||
@@ -135,8 +135,8 @@ describe('OrderListTable', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues: string[] = [
|
||||
icebergOrder.market?.tradableInstrument.instrument.code || '',
|
||||
'0.00',
|
||||
'+1.00',
|
||||
'0',
|
||||
'+1',
|
||||
Schema.OrderTypeMapping[
|
||||
icebergOrder.type || Schema.OrderType.TYPE_LIMIT
|
||||
] + ' (Iceberg)',
|
||||
@@ -277,7 +277,7 @@ describe('OrderListTable', () => {
|
||||
|
||||
const amendCell = getAmendCell();
|
||||
const typeCell = screen.getAllByRole('gridcell')[3];
|
||||
expect(typeCell).toHaveTextContent('Mid - 10.0 Peg limit');
|
||||
expect(typeCell).toHaveTextContent('Mid - 10 Peg limit');
|
||||
expect(amendCell.queryByTestId('edit')).toBeInTheDocument();
|
||||
expect(amendCell.queryByTestId('cancel')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -162,17 +162,15 @@ describe('OrderViewDialog', () => {
|
||||
expect(screen.getByTestId('order-type-label')).toHaveTextContent('Type');
|
||||
expect(screen.getByTestId('order-type-value')).toHaveTextContent('Limit');
|
||||
expect(screen.getByTestId('order-price-label')).toHaveTextContent('Price');
|
||||
expect(screen.getByTestId('order-price-value')).toHaveTextContent('150.00');
|
||||
expect(screen.getByTestId('order-price-value')).toHaveTextContent('150');
|
||||
expect(screen.getByTestId('order-size-label')).toHaveTextContent('Size');
|
||||
expect(screen.getByTestId('order-size-value')).toHaveTextContent('+10.00');
|
||||
expect(screen.getByTestId('order-size-value')).toHaveTextContent('+10');
|
||||
expect(screen.getByTestId('order-remaining-label')).toHaveTextContent(
|
||||
'Remaining'
|
||||
);
|
||||
expect(screen.getByTestId('order-remaining-value')).toHaveTextContent(
|
||||
'+5.00'
|
||||
);
|
||||
expect(screen.getByTestId('order-remaining-value')).toHaveTextContent('+5');
|
||||
expect(
|
||||
screen.getByTestId('order-iceberg-order-reserved-remaining-value')
|
||||
).toHaveTextContent('5.00');
|
||||
).toHaveTextContent('5');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,8 +140,8 @@ describe('StopOrdersTable', () => {
|
||||
const cells = grid.querySelectorAll('.ag-body [col-id="trigger"]');
|
||||
|
||||
const expectedValues: string[] = [
|
||||
'Mark < 8.0',
|
||||
'Mark > 9.0',
|
||||
'Mark < 8',
|
||||
'Mark > 9',
|
||||
'Mark +10.0%',
|
||||
'Mark -20.0%',
|
||||
];
|
||||
@@ -173,7 +173,7 @@ describe('StopOrdersTable', () => {
|
||||
const grid = screen.getByRole('treegrid');
|
||||
const cells = grid.querySelectorAll('.ag-body [col-id="submission.size"]');
|
||||
|
||||
const expectedValues: string[] = ['+1.00', '-1.10'];
|
||||
const expectedValues: string[] = ['+1', '-1.1'];
|
||||
expectedValues.forEach((expectedValue, i) =>
|
||||
expect(cells[i]).toHaveTextContent(expectedValue)
|
||||
);
|
||||
@@ -219,7 +219,7 @@ describe('StopOrdersTable', () => {
|
||||
const grid = screen.getByRole('treegrid');
|
||||
const cells = grid.querySelectorAll('.ag-body [col-id="submission.price"]');
|
||||
|
||||
const expectedValues: string[] = ['12.0', '-'];
|
||||
const expectedValues: string[] = ['12', '-'];
|
||||
expectedValues.forEach((expectedValue, i) =>
|
||||
expect(cells[i]).toHaveTextContent(expectedValue)
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
getDateTimeFormat,
|
||||
isNumeric,
|
||||
toBigNum,
|
||||
formatTrigger,
|
||||
useFormatTrigger,
|
||||
} from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
@@ -52,6 +52,7 @@ export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
|
||||
export const StopOrdersTable = memo(
|
||||
({ onCancel, onMarketClick, onView, ...props }: StopOrdersTableProps) => {
|
||||
const t = useT();
|
||||
const formatTrigger = useFormatTrigger();
|
||||
const showAllActions = !props.isReadOnly;
|
||||
const columnDefs: ColDef[] = useMemo(
|
||||
() => [
|
||||
@@ -282,7 +283,15 @@ export const StopOrdersTable = memo(
|
||||
},
|
||||
},
|
||||
],
|
||||
[onCancel, onMarketClick, onView, props.isReadOnly, showAllActions, t]
|
||||
[
|
||||
onCancel,
|
||||
onMarketClick,
|
||||
onView,
|
||||
props.isReadOnly,
|
||||
showAllActions,
|
||||
t,
|
||||
formatTrigger,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('Positions', () => {
|
||||
'+100'
|
||||
);
|
||||
expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent(
|
||||
'1,230.0'
|
||||
'1,230'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('Positions', () => {
|
||||
'-100'
|
||||
);
|
||||
expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent(
|
||||
'1,230.0'
|
||||
'1,230'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -334,7 +334,7 @@ describe('Positions', () => {
|
||||
const tooltip = within(await screen.findByRole('tooltip'));
|
||||
expect(tooltip.getByText('Realised PNL: 1.23')).toBeInTheDocument();
|
||||
expect(
|
||||
tooltip.getByText('Lifetime loss socialisation deductions: 5.00')
|
||||
tooltip.getByText('Lifetime loss socialisation deductions: 5')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
tooltip.getByText(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ExternalLink, Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import { useUpdateProposal } from '../lib';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
type AssetProposalNotificationProps = {
|
||||
assetId?: string;
|
||||
@@ -10,6 +10,7 @@ type AssetProposalNotificationProps = {
|
||||
export const AssetProposalNotification = ({
|
||||
assetId,
|
||||
}: AssetProposalNotificationProps) => {
|
||||
const t = useT();
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const { data: proposal } = useUpdateProposal({
|
||||
id: assetId,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ExternalLink, Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||
import { useUpdateProposal } from '../lib';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
type MarketProposalNotificationProps = {
|
||||
marketId?: string;
|
||||
@@ -10,6 +10,7 @@ type MarketProposalNotificationProps = {
|
||||
export const MarketProposalNotification = ({
|
||||
marketId,
|
||||
}: MarketProposalNotificationProps) => {
|
||||
const t = useT();
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const { data: proposal } = useUpdateProposal({
|
||||
id: marketId,
|
||||
@@ -29,7 +30,7 @@ export const MarketProposalNotification = ({
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="border-l border-default pl-1 pr-1 pb-1 min-w-min whitespace-nowrap">
|
||||
<div className="border-default min-w-min whitespace-nowrap border-l pb-1 pl-1 pr-1">
|
||||
<Notification
|
||||
intent={Intent.Warning}
|
||||
message={message}
|
||||
|
||||
@@ -5,10 +5,11 @@ import {
|
||||
Link,
|
||||
ActionsDropdown,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
export const ProposalActionsDropdown = ({ id }: { id: string }) => {
|
||||
const t = useT();
|
||||
const linkCreator = useLinks(DApp.Governance);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { FC } from 'react';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals';
|
||||
import { useProposalsListQuery } from '../../lib/proposals-data-provider/__generated__/Proposals';
|
||||
import { useColumnDefs } from './use-column-defs';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) =>
|
||||
data.filter((proposal) =>
|
||||
@@ -30,6 +30,7 @@ interface ProposalListProps {
|
||||
}
|
||||
|
||||
export const ProposalsList = ({ cellRenderers }: ProposalListProps) => {
|
||||
const t = useT();
|
||||
const { data } = useProposalsListQuery({
|
||||
variables: {
|
||||
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import compact from 'lodash/compact';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
@@ -19,8 +18,11 @@ import {
|
||||
} from '@vegaprotocol/types';
|
||||
import type { ProposalListFieldsFragment } from '../../lib/proposals-data-provider/__generated__/Proposals';
|
||||
import { ProposalActionsDropdown } from '../proposal-actions-dropdown';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const useColumnDefs = () => {
|
||||
const t = useT();
|
||||
|
||||
const columnDefs: ColDef[] = useMemo(() => {
|
||||
return compact([
|
||||
{
|
||||
@@ -124,7 +126,7 @@ export const useColumnDefs = () => {
|
||||
},
|
||||
},
|
||||
]);
|
||||
}, []);
|
||||
}, [t]);
|
||||
|
||||
return columnDefs;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useNextProtocolUpgradeProposal, useTimeToUpgrade } from '../lib';
|
||||
import { convertToCountdownString } from '@vegaprotocol/utils';
|
||||
import classNames from 'classnames';
|
||||
@@ -9,6 +8,8 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useProtocolUpgradeProposalLink } from '@vegaprotocol/environment';
|
||||
import { useContext } from 'react';
|
||||
import { useT } from '../use-t';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
export enum ProtocolUpgradeCountdownMode {
|
||||
IN_BLOCKS,
|
||||
@@ -21,6 +22,7 @@ type ProtocolUpgradeCountdownProps = {
|
||||
export const ProtocolUpgradeCountdown = ({
|
||||
mode = ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING,
|
||||
}: ProtocolUpgradeCountdownProps) => {
|
||||
const t = useT();
|
||||
const { theme } = useContext(NavigationContext);
|
||||
const { data, lastBlockHeight } = useNextProtocolUpgradeProposal();
|
||||
|
||||
@@ -45,12 +47,13 @@ export const ProtocolUpgradeCountdown = ({
|
||||
switch (mode) {
|
||||
case ProtocolUpgradeCountdownMode.IN_BLOCKS:
|
||||
countdown = (
|
||||
<>
|
||||
<span className={emphasis}>
|
||||
{Number(data.upgradeBlockHeight) - Number(lastBlockHeight)}
|
||||
</span>{' '}
|
||||
{t('blocks')}
|
||||
</>
|
||||
<Trans
|
||||
defaults="<0>{{count}}</0> blocks"
|
||||
components={[<span className={emphasis}>count</span>]}
|
||||
values={{
|
||||
count: Number(data.upgradeBlockHeight) - Number(lastBlockHeight),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING:
|
||||
@@ -61,7 +64,7 @@ export const ProtocolUpgradeCountdown = ({
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className={classNames('italic lowercase text-vega-orange-600', {
|
||||
className={classNames('text-vega-orange-600 lowercase italic', {
|
||||
'!text-black': theme === 'yellow',
|
||||
})}
|
||||
>
|
||||
@@ -80,20 +83,19 @@ export const ProtocolUpgradeCountdown = ({
|
||||
<div
|
||||
data-testid="protocol-upgrade-counter"
|
||||
className={classNames(
|
||||
'flex flex-nowrap gap-1 items-center text-xs py-1 px-2 lg:px-4 h-8',
|
||||
'border rounded',
|
||||
'flex h-8 flex-nowrap items-center gap-1 px-2 py-1 text-xs lg:px-4',
|
||||
'rounded border',
|
||||
'border-vega-orange-500 dark:border-vega-orange-500',
|
||||
'bg-vega-orange-300 dark:bg-vega-orange-700',
|
||||
'text-default',
|
||||
{
|
||||
'!bg-transparent !border-black': theme === 'yellow',
|
||||
'!border-black !bg-transparent': theme === 'yellow',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.EXCLAIMATION_MARK} size={12} />{' '}
|
||||
<span className="flex gap-1 flex-nowrap whitespace-nowrap">
|
||||
<span>{t('Network upgrade in')} </span>
|
||||
{countdown}
|
||||
<span className="flex flex-nowrap gap-1 whitespace-nowrap">
|
||||
<span>{t('Network upgrade in {{countdown}}', { countdown })} </span>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useProtocolUpgradeProposalLink } from '@vegaprotocol/environment';
|
||||
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
} from '../lib';
|
||||
import { useLocalStorageSnapshot } from '@vegaprotocol/react-helpers';
|
||||
import { useBlockRising } from '../lib/protocol-upgrade-proposals/use-block-rising';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
/**
|
||||
* A flag determining whether to get the upgrade proposal data from local
|
||||
@@ -22,6 +22,7 @@ import { useBlockRising } from '../lib/protocol-upgrade-proposals/use-block-risi
|
||||
const ALLOW_STORED_PROPOSAL_DATA = true;
|
||||
|
||||
export const ProtocolUpgradeInProgressNotification = () => {
|
||||
const t = useT();
|
||||
const { data, error } = useNextProtocolUpgradeProposal(undefined, true);
|
||||
const [nextUpgrade] = useLocalStorageSnapshot(
|
||||
NEXT_PROTOCOL_UPGRADE_PROPOSAL_SNAPSHOT
|
||||
@@ -71,7 +72,9 @@ export const ProtocolUpgradeInProgressNotification = () => {
|
||||
return (
|
||||
<NotificationBanner intent={Intent.Danger} className={SHORT}>
|
||||
<div className="uppercase">
|
||||
{t('The network is being upgraded to %s', vegaReleaseTag)}
|
||||
{t('The network is being upgraded to {{vegaReleaseTag}}', {
|
||||
vegaReleaseTag,
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
{t(
|
||||
|
||||
@@ -4,11 +4,12 @@ import {
|
||||
NotificationBanner,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useNextProtocolUpgradeProposal, useTimeToUpgrade } from '../lib';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useProtocolUpgradeProposalLink } from '@vegaprotocol/environment';
|
||||
import { ProtocolUpgradeCountdownMode } from './protocol-upgrade-countdown';
|
||||
import { convertToCountdownString } from '@vegaprotocol/utils';
|
||||
import { useState } from 'react';
|
||||
import { useT } from '../use-t';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
type ProtocolUpgradeProposalNotificationProps = {
|
||||
mode?: ProtocolUpgradeCountdownMode;
|
||||
@@ -16,6 +17,7 @@ type ProtocolUpgradeProposalNotificationProps = {
|
||||
export const ProtocolUpgradeProposalNotification = ({
|
||||
mode = ProtocolUpgradeCountdownMode.IN_BLOCKS,
|
||||
}: ProtocolUpgradeProposalNotificationProps) => {
|
||||
const t = useT();
|
||||
const [visible, setVisible] = useState(true);
|
||||
const { data, lastBlockHeight } = useNextProtocolUpgradeProposal();
|
||||
const detailsLink = useProtocolUpgradeProposalLink();
|
||||
@@ -40,10 +42,13 @@ export const ProtocolUpgradeProposalNotification = ({
|
||||
switch (mode) {
|
||||
case ProtocolUpgradeCountdownMode.IN_BLOCKS:
|
||||
countdown = (
|
||||
<>
|
||||
<span className="text-vega-orange-500">{blocksLeft}</span>{' '}
|
||||
{t('blocks')}
|
||||
</>
|
||||
<Trans
|
||||
defaults="<0>{{count}}</0> blocks"
|
||||
components={[<span className="text-vega-orange-500">count</span>]}
|
||||
values={{
|
||||
count: blocksLeft,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING:
|
||||
@@ -72,10 +77,13 @@ export const ProtocolUpgradeProposalNotification = ({
|
||||
}}
|
||||
>
|
||||
<div className="uppercase ">
|
||||
{t('The network will upgrade to %s in ', [data.vegaReleaseTag])}
|
||||
{countdown}
|
||||
{t('The network will upgrade to {{vegaReleaseTag}} in {{countdown}}', {
|
||||
vegaReleaseTag: data.vegaReleaseTag,
|
||||
countdown,
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<Trans />
|
||||
{t(
|
||||
'Trading activity will be interrupted, manage your risk appropriately.'
|
||||
)}{' '}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { WalletClientError } from '@vegaprotocol/wallet-client';
|
||||
import type { VegaTxState } from '../../lib/proposals-hooks/use-vega-transaction';
|
||||
import { VegaTxStatus } from '../../lib/proposals-hooks/use-vega-transaction';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export type VegaTransactionContentMap = {
|
||||
[C in VegaTxStatus]?: JSX.Element;
|
||||
@@ -28,8 +28,9 @@ export const VegaTransactionDialog = ({
|
||||
icon,
|
||||
content,
|
||||
}: VegaTransactionDialogProps) => {
|
||||
const t = useT();
|
||||
const computedIntent = intent ? intent : getIntent(transaction);
|
||||
const computedTitle = title ? title : getTitle(transaction);
|
||||
const computedTitle = title ? title : getTitle(transaction, t);
|
||||
const computedIcon = icon ? icon : getIcon(transaction);
|
||||
|
||||
return (
|
||||
@@ -86,6 +87,7 @@ interface VegaDialogProps {
|
||||
* Default dialog content
|
||||
*/
|
||||
export const VegaDialog = ({ transaction }: VegaDialogProps) => {
|
||||
const t = useT();
|
||||
const { links, network } = useVegaWallet();
|
||||
|
||||
let content = null;
|
||||
@@ -99,7 +101,7 @@ export const VegaDialog = ({ transaction }: VegaDialogProps) => {
|
||||
</p>
|
||||
{network !== 'MAINNET' && (
|
||||
<p data-testid="testnet-transaction-info">
|
||||
{t('[This is %s transaction only]').replace('%s', network)}
|
||||
{t('[This is {{network}} transaction only]', { network })}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
@@ -176,7 +178,7 @@ const getIntent = (transaction: VegaTxState) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getTitle = (transaction: VegaTxState) => {
|
||||
const getTitle = (transaction: VegaTxState, t: ReturnType<typeof useT>) => {
|
||||
switch (transaction.status) {
|
||||
case VegaTxStatus.Requested:
|
||||
return t('Confirm transaction in wallet');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
ProposalChangeMapping,
|
||||
ProposalRejectionReasonMapping,
|
||||
@@ -16,6 +15,8 @@ import {
|
||||
useOnProposalSubscription,
|
||||
type OnProposalFragmentFragment,
|
||||
} from './__generated__/Proposal';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const PROPOSAL_STATES_TO_TOAST = [
|
||||
ProposalState.STATE_DECLINED,
|
||||
@@ -27,6 +28,7 @@ const CLOSE_AFTER = 0;
|
||||
type Proposal = OnProposalFragmentFragment;
|
||||
|
||||
const ProposalDetails = ({ proposal }: { proposal: Proposal }) => {
|
||||
const t = useT();
|
||||
const change = proposal.terms.change;
|
||||
switch (change.__typename) {
|
||||
case 'UpdateNetworkParameter':
|
||||
@@ -43,8 +45,10 @@ const ProposalDetails = ({ proposal }: { proposal: Proposal }) => {
|
||||
{proposal.state === ProposalState.STATE_REJECTED &&
|
||||
proposal.rejectionReason ? (
|
||||
<p data-testid="proposal-toast-rejection-reason">
|
||||
{t('Rejection reason:')}{' '}
|
||||
{ProposalRejectionReasonMapping[proposal.rejectionReason]}
|
||||
{t('Rejection reason: {{reason}}', {
|
||||
reason:
|
||||
ProposalRejectionReasonMapping[proposal.rejectionReason],
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
</>
|
||||
@@ -61,24 +65,30 @@ const UpdateNetworkParameterDetails = ({
|
||||
if (change.__typename !== 'UpdateNetworkParameter') return null;
|
||||
return (
|
||||
<p data-testid="proposal-toast-network-param" className="italic">
|
||||
'{t('Update ')}
|
||||
<span className="break-all">{change.networkParameter.key}</span>
|
||||
{t(' to ')}
|
||||
<span>{change.networkParameter.value}</span>'
|
||||
<Trans
|
||||
defaults="Update <0>{{key}}</0> to {{value}}"
|
||||
values={change.networkParameter}
|
||||
components={[<span className="break-all">key</span>]}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export const ProposalToastContent = ({ proposal }: { proposal: Proposal }) => {
|
||||
const t = useT();
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const change = proposal.terms.change;
|
||||
|
||||
// Generates toast's title,
|
||||
// e.g. Update market proposal enacted, New transfer proposal open, ...
|
||||
const title = t('%s proposal %s', [
|
||||
change.__typename ? ProposalChangeMapping[change.__typename] : 'Unknown',
|
||||
ProposalStateMapping[proposal.state].toLowerCase(),
|
||||
]);
|
||||
const title = change.__typename
|
||||
? t('{{proposalChange}} proposal {{proposalState}}', {
|
||||
proposalChange: ProposalChangeMapping[change.__typename],
|
||||
proposalState: ProposalStateMapping[proposal.state].toLowerCase(),
|
||||
})
|
||||
: t('Unknown proposal {{proposalState}}', {
|
||||
proposalState: ProposalStateMapping[proposal.state].toLowerCase(),
|
||||
});
|
||||
|
||||
const enactment = Date.parse(proposal.terms.enactmentDatetime);
|
||||
|
||||
@@ -88,7 +98,9 @@ export const ProposalToastContent = ({ proposal }: { proposal: Proposal }) => {
|
||||
<ProposalDetails proposal={proposal} />
|
||||
{!isNaN(enactment) && (
|
||||
<p>
|
||||
{t('Enactment date:')} {getDateTimeFormat().format(enactment)}
|
||||
{t('Enactment date: {{date}}', {
|
||||
date: getDateTimeFormat().format(enactment),
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
import '@testing-library/jest-dom';
|
||||
import { locales } from '@vegaprotocol/i18n';
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
// Set up i18n instance so that components have the correct default
|
||||
// en translations
|
||||
i18n.use(initReactI18next).init({
|
||||
// we init with resources
|
||||
resources: locales,
|
||||
fallbackLng: 'en',
|
||||
ns: ['proposals'],
|
||||
defaultNS: 'proposals',
|
||||
});
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const ns = 'proposals';
|
||||
export const useT = () => useTranslation(ns).t;
|
||||
@@ -1,11 +1,12 @@
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Icon, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
export const getProposalDialogTitle = (
|
||||
export const useGetProposalDialogTitle = (
|
||||
status?: ProposalState
|
||||
): string | undefined => {
|
||||
const t = useT();
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ import type { BigNumber } from 'bignumber.js';
|
||||
import { toNumberParts } from '@vegaprotocol/utils';
|
||||
|
||||
export const useNumberParts = (
|
||||
value: BigNumber | null | undefined,
|
||||
decimals: number
|
||||
value: BigNumber | null | undefined
|
||||
): [integers: string, decimalPlaces: string, separator: string | undefined] => {
|
||||
return useMemo(() => toNumberParts(value, decimals), [decimals, value]);
|
||||
return useMemo(() => toNumberParts(value), [value]);
|
||||
};
|
||||
|
||||
@@ -41,8 +41,8 @@ describe('TradesTable', () => {
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
'1,111,222.00',
|
||||
'20.00',
|
||||
'1,111,222',
|
||||
'20',
|
||||
getTimeFormat().format(new Date(trade.createdAt)),
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
export const useTranslation = () => ({
|
||||
t: (label: string, replacements?: Record<string, string>) => {
|
||||
let translatedLabel = label;
|
||||
if (typeof replacements === 'object' && replacements !== null) {
|
||||
Object.keys(replacements).forEach((key) => {
|
||||
translatedLabel = translatedLabel.replace(
|
||||
`{{${key}}}`,
|
||||
replacements[key]
|
||||
);
|
||||
});
|
||||
}
|
||||
return translatedLabel;
|
||||
},
|
||||
});
|
||||
@@ -3,7 +3,6 @@ import BigNumber from 'bignumber.js';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
addDecimalsFormatNumberQuantum,
|
||||
formatNumber,
|
||||
formatNumberPercentage,
|
||||
getUnlimitedThreshold,
|
||||
isNumeric,
|
||||
@@ -12,94 +11,140 @@ import {
|
||||
quantumDecimalPlaces,
|
||||
toDecimal,
|
||||
toNumberParts,
|
||||
formatNumberRounded,
|
||||
} from './number';
|
||||
|
||||
describe('number utils', () => {
|
||||
it.each([
|
||||
{ v: new BigNumber(123000), d: 5, o: '1.23' },
|
||||
{ v: new BigNumber(123000), d: 3, o: '123.00' },
|
||||
{ v: new BigNumber(123000), d: 1, o: '12,300.0' },
|
||||
{ v: new BigNumber(123001), d: 2, o: '1,230.01' },
|
||||
{ v: new BigNumber(123001000), d: 2, o: '1,230,010.00' },
|
||||
])(
|
||||
'formats with addDecimalsFormatNumber given number correctly',
|
||||
({ v, d, o }) => {
|
||||
expect(addDecimalsFormatNumber(v.toString(), d)).toStrictEqual(o);
|
||||
}
|
||||
);
|
||||
{ v: new BigNumber(123000), d: 5, f: undefined, o: '1.23' },
|
||||
{ v: new BigNumber(123000), d: 5, f: 3, o: '1.230' },
|
||||
{ v: new BigNumber(123000), d: 3, f: undefined, o: '123' },
|
||||
{ v: new BigNumber(123000), d: 1, f: undefined, o: '12,300' },
|
||||
{ v: new BigNumber(123001), d: 2, f: undefined, o: '1,230.01' },
|
||||
{ v: new BigNumber(123001000), d: 2, f: undefined, o: '1,230,010' },
|
||||
|
||||
it.each([
|
||||
{ v: new BigNumber(123000), d: 5, o: '1.23', q: 0.1 },
|
||||
{ v: new BigNumber(123000), d: 3, o: '123.00', q: 0.1 },
|
||||
{ v: new BigNumber(123000), d: 1, o: '12,300.00', q: 0.1 },
|
||||
{ v: new BigNumber(123001000), d: 2, o: '1,230,010.00', q: 0.1 },
|
||||
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 100 },
|
||||
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 0.1 },
|
||||
{ v: new BigNumber(123001), d: 2, o: '1,230.01', q: 1 },
|
||||
// these would lose precision normally and get rounded to 0.9041951688292778
|
||||
{
|
||||
v: BigNumber('123456789123456789'),
|
||||
d: 10,
|
||||
o: '12,345,678.91234568',
|
||||
q: '0.00003846',
|
||||
v: new BigNumber('904195168829277777'),
|
||||
d: 18,
|
||||
f: undefined,
|
||||
o: '0.904195168829277777',
|
||||
},
|
||||
{
|
||||
v: BigNumber('123456789123456789'),
|
||||
d: 10,
|
||||
o: '12,345,678.91234568',
|
||||
q: '1',
|
||||
v: new BigNumber('1234567904195168829277777'),
|
||||
d: 18,
|
||||
f: undefined,
|
||||
o: '1,234,567.904195168829277777',
|
||||
},
|
||||
// USDT / USDC
|
||||
{ v: new BigNumber(12345678), d: 6, o: '12.35', q: 1000000 },
|
||||
])(
|
||||
'formats with addDecimalsFormatNumberQuantum given number correctly',
|
||||
({ v, d, o, q }) => {
|
||||
expect(addDecimalsFormatNumberQuantum(v.toString(), d, q)).toStrictEqual(
|
||||
o
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
{ v: new BigNumber(123), d: 3, o: '123.00' },
|
||||
{ v: new BigNumber(123.123), d: 3, o: '123.123' },
|
||||
{ v: new BigNumber(123.6666), d: 3, o: '123.667' },
|
||||
{ v: new BigNumber(123.123), d: 6, o: '123.123' },
|
||||
{ v: new BigNumber(123.123), d: 0, o: '123' },
|
||||
{ v: new BigNumber(123), d: undefined, o: '123' },
|
||||
{ v: new BigNumber(30000), d: undefined, o: '30,000' },
|
||||
{ v: new BigNumber(3.000001), d: undefined, o: '3' },
|
||||
])('formats with formatNumber given number correctly', ({ v, d, o }) => {
|
||||
expect(formatNumber(v, d)).toStrictEqual(o);
|
||||
{
|
||||
v: new BigNumber('1234567904195168829277777'),
|
||||
d: 18,
|
||||
f: 2,
|
||||
o: '1,234,567.90',
|
||||
},
|
||||
{
|
||||
v: new BigNumber('1234567906195168829277777'),
|
||||
d: 18,
|
||||
f: 2,
|
||||
o: '1,234,567.91', // should round here
|
||||
},
|
||||
])('addDecimalsFormatNumber formats $v as $o', ({ v, d, f, o }) => {
|
||||
expect(addDecimalsFormatNumber(v.toString(), d, f)).toStrictEqual(o);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ v: new BigNumber(123), d: 3, o: '123.00%' },
|
||||
// USDT / USDC
|
||||
{
|
||||
v: new BigNumber('12111111'),
|
||||
d: 6,
|
||||
q: '1000000',
|
||||
o: '12.11',
|
||||
},
|
||||
{
|
||||
v: new BigNumber('12456111111'),
|
||||
d: 6,
|
||||
q: '1000000',
|
||||
o: '12,456.11',
|
||||
},
|
||||
{
|
||||
v: new BigNumber('12345678'),
|
||||
d: 6,
|
||||
q: '1000000',
|
||||
o: '12.35', // quantum should round
|
||||
},
|
||||
|
||||
// WETH
|
||||
{
|
||||
v: new BigNumber('1'),
|
||||
d: 18,
|
||||
q: '500000000000000',
|
||||
o: '0.000000', // actually 0.000000000000000001 but we are formatting with quantum so that last 1 weth is not relevant
|
||||
},
|
||||
{
|
||||
v: new BigNumber('493000000000000'),
|
||||
d: 18,
|
||||
q: '500000000000000',
|
||||
o: '0.000493', // 1 USD of WETH ~0.000493
|
||||
},
|
||||
{
|
||||
v: new BigNumber('1000000493000000000000'),
|
||||
d: 18,
|
||||
q: '500000000000000',
|
||||
o: '1,000.000493',
|
||||
},
|
||||
{ v: new BigNumber(123001), d: 2, q: 1, o: '1,230.0100' },
|
||||
{ v: new BigNumber(123001), d: 2, q: 100, o: '1,230.01' },
|
||||
{
|
||||
v: BigNumber('123456789123456789'),
|
||||
d: 10,
|
||||
q: '1',
|
||||
o: '12,345,678.912345678900',
|
||||
},
|
||||
|
||||
// FRACTIONAL QUANTUM
|
||||
{ v: new BigNumber(123000), d: 5, q: 0.1, o: '1.23000000' },
|
||||
{ v: new BigNumber(123000), d: 3, q: 0.1, o: '123.000000' },
|
||||
{ v: new BigNumber(123000), d: 1, q: 0.1, o: '12,300.0000' },
|
||||
{ v: new BigNumber(123001000), d: 2, q: 0.1, o: '1,230,010.00000' },
|
||||
{ v: new BigNumber(123001), d: 2, q: 0.1, o: '1,230.01000' },
|
||||
{
|
||||
v: BigNumber('123456789123456789'),
|
||||
d: 10,
|
||||
q: '0.00003846',
|
||||
o: '12,345,678.91234567890000000',
|
||||
},
|
||||
])('addDecimalsFormatNumberQuantum($v, $d, $q) = $o', ({ v, d, q, o }) => {
|
||||
expect(addDecimalsFormatNumberQuantum(v.toString(), d, q)).toStrictEqual(o);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ v: new BigNumber(123), d: 3, o: '123.000%' },
|
||||
{ v: new BigNumber(123.123), d: 3, o: '123.123%' },
|
||||
{ v: new BigNumber(123.123), d: 6, o: '123.123%' },
|
||||
{ v: new BigNumber(123.123), d: 6, o: '123.123000%' },
|
||||
{ v: new BigNumber(123.123), d: 0, o: '123%' },
|
||||
{ v: new BigNumber(123), d: undefined, o: '123%' }, // it default to 2 decimal places
|
||||
{ v: new BigNumber(30000), d: undefined, o: '30,000%' },
|
||||
{ v: new BigNumber(3.000001), d: undefined, o: '3.000001%' },
|
||||
])('formats given number correctly', ({ v, d, o }) => {
|
||||
])('formatNumberRounded($v, $d) -> $o', ({ v, d, o }) => {
|
||||
expect(formatNumberPercentage(v, d)).toStrictEqual(o);
|
||||
});
|
||||
|
||||
describe('toNumberParts', () => {
|
||||
it.each([
|
||||
{ v: null, d: 3, o: ['0', '000', '.'] },
|
||||
{ v: undefined, d: 3, o: ['0', '000', '.'] },
|
||||
{ v: new BigNumber(123), d: 3, o: ['123', '00', '.'] },
|
||||
{ v: new BigNumber(123.123), d: 3, o: ['123', '123', '.'] },
|
||||
{ v: new BigNumber(123.123), d: 6, o: ['123', '123', '.'] },
|
||||
{ v: new BigNumber(123.123), d: 0, o: ['123', '', '.'] },
|
||||
{ v: new BigNumber(123), d: undefined, o: ['123', '00', '.'] },
|
||||
{ v: null, o: ['0', '', '.'] },
|
||||
{ v: undefined, o: ['0', '', '.'] },
|
||||
{ v: new BigNumber(123), o: ['123', '', '.'] },
|
||||
{ v: new BigNumber(123.123), o: ['123', '123', '.'] },
|
||||
{ v: new BigNumber(123.123), o: ['123', '123', '.'] },
|
||||
{ v: new BigNumber(123.123), o: ['123', '123', '.'] },
|
||||
{ v: new BigNumber(123), o: ['123', '', '.'] },
|
||||
{
|
||||
v: new BigNumber(30000),
|
||||
d: undefined,
|
||||
o: ['30,000', '00', '.'],
|
||||
o: ['30,000', '', '.'],
|
||||
},
|
||||
])('returns correct tuple given the different arguments', ({ v, d, o }) => {
|
||||
expect(toNumberParts(v, d)).toStrictEqual(o);
|
||||
])('$v -> $o', ({ v, o }) => {
|
||||
expect(toNumberParts(v)).toStrictEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -246,3 +291,23 @@ describe('getUnlimitedThreshold', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('formatNumberRounded', () => {
|
||||
it.each([
|
||||
{ n: new BigNumber(123), o: '123' },
|
||||
{ n: new BigNumber(1234), o: '1,234' },
|
||||
{ n: new BigNumber(404000), o: '404,000' },
|
||||
{ n: new BigNumber(500000), o: '500,000' },
|
||||
{ n: new BigNumber(1000000), o: '1m' },
|
||||
{ n: new BigNumber(1500000), o: '1.5m' },
|
||||
{ n: new BigNumber(1500001), o: '1.5m' },
|
||||
{ n: new BigNumber(1510001), o: '1.5m' },
|
||||
{ n: new BigNumber(1000000000), o: '1b' },
|
||||
{ n: new BigNumber(1500000000), o: '1.5b' },
|
||||
{ n: new BigNumber(1000000000000), o: '1t' },
|
||||
{ n: new BigNumber(1500000000000), o: '1.5t' },
|
||||
{ n: new BigNumber(99510000000000), o: '99.5t' },
|
||||
])('$n -> $o', ({ n, o }) => {
|
||||
expect(formatNumberRounded(n)).toEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,75 @@
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import isNil from 'lodash/isNil';
|
||||
import memoize from 'lodash/memoize';
|
||||
|
||||
import { getUserLocale } from '../get-user-locale';
|
||||
|
||||
const DEFAULT_DECIMAL_SEPARATOR = '.';
|
||||
const DEFAULT_GROUP_SEPARATOR = ',';
|
||||
|
||||
// get formatting characters for users locale
|
||||
export const getNumberParts = memoize(() => {
|
||||
// 1000.1 will get us a group character (, for thousand groups, . for decimals in en-GB)
|
||||
const parts = new Intl.NumberFormat(getUserLocale()).formatToParts(1000.1);
|
||||
|
||||
const decimalSeparator = parts.find((part) => part.type === 'decimal');
|
||||
const groupSeparator = parts.find((part) => part.type === 'group');
|
||||
|
||||
if (!decimalSeparator) {
|
||||
console.warn('Could not get locales decimalSeparator');
|
||||
}
|
||||
|
||||
if (!groupSeparator) {
|
||||
console.warn('Could not get locales groupSeparator');
|
||||
}
|
||||
|
||||
return {
|
||||
decimalSeparator: decimalSeparator
|
||||
? decimalSeparator.value
|
||||
: DEFAULT_DECIMAL_SEPARATOR,
|
||||
groupSeparator: groupSeparator
|
||||
? groupSeparator.value
|
||||
: DEFAULT_GROUP_SEPARATOR,
|
||||
};
|
||||
});
|
||||
|
||||
const parts = getNumberParts();
|
||||
|
||||
// Format for bignumber formatting
|
||||
const FORMAT = {
|
||||
prefix: '',
|
||||
decimalSeparator: parts.decimalSeparator,
|
||||
groupSeparator: parts.groupSeparator,
|
||||
groupSize: 3,
|
||||
secondaryGroupSize: 0,
|
||||
fractionGroupSeparator: ' ',
|
||||
fractionGroupSize: 0,
|
||||
suffix: '',
|
||||
};
|
||||
|
||||
BigNumber.config({ FORMAT });
|
||||
|
||||
export const isNumeric = (
|
||||
value?: string | number | BigNumber | bigint | null
|
||||
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
||||
|
||||
export const toNumberParts = (
|
||||
value: BigNumber | null | undefined
|
||||
): [integers: string, decimalPlaces: string, separator: string] => {
|
||||
if (!value) {
|
||||
return ['0', '', '.'];
|
||||
}
|
||||
|
||||
const separator = getNumberParts().decimalSeparator;
|
||||
|
||||
const dps = value.dp() || 0;
|
||||
|
||||
const [integers, decimalsPlaces] = formatNumber(value, dps)
|
||||
.toString()
|
||||
.split(separator);
|
||||
|
||||
return [integers, decimalsPlaces || '', separator];
|
||||
};
|
||||
|
||||
/**
|
||||
* A raw unformatted value greater than this is considered and displayed
|
||||
* as UNLIMITED.
|
||||
@@ -16,9 +82,6 @@ export const UNLIMITED_THRESHOLD = new BigNumber(2).pow(256).times(0.8);
|
||||
export const getUnlimitedThreshold = (decimalPlaces: number) =>
|
||||
UNLIMITED_THRESHOLD.dividedBy(Math.pow(10, decimalPlaces));
|
||||
|
||||
const MIN_FRACTION_DIGITS = 2;
|
||||
const MAX_FRACTION_DIGITS = 20;
|
||||
|
||||
export function toDecimal(numberOfDecimals: number) {
|
||||
return new BigNumber(1)
|
||||
.dividedBy(new BigNumber(10).exponentiatedBy(numberOfDecimals))
|
||||
@@ -53,35 +116,6 @@ export function removeDecimal(
|
||||
return new BigNumber(value || 0).times(times).toFixed(0);
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
|
||||
export const getNumberFormat = memoize((digits: number) => {
|
||||
if (isNil(digits) || digits < 0) {
|
||||
return new Intl.NumberFormat(getUserLocale());
|
||||
}
|
||||
return new Intl.NumberFormat(getUserLocale(), {
|
||||
minimumFractionDigits: Math.min(Math.max(0, digits), MIN_FRACTION_DIGITS),
|
||||
maximumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
|
||||
});
|
||||
});
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
|
||||
export const getFixedNumberFormat = memoize((digits: number) => {
|
||||
if (isNil(digits) || digits < 0) {
|
||||
return new Intl.NumberFormat(getUserLocale());
|
||||
}
|
||||
return new Intl.NumberFormat(getUserLocale(), {
|
||||
minimumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
|
||||
maximumFractionDigits: Math.min(Math.max(0, digits), MAX_FRACTION_DIGITS),
|
||||
});
|
||||
});
|
||||
|
||||
export const getDecimalSeparator = memoize(
|
||||
() =>
|
||||
getNumberFormat(1)
|
||||
.formatToParts(1.1)
|
||||
.find((part) => part.type === 'decimal')?.value
|
||||
);
|
||||
|
||||
/** formatNumber will format the number with fixed decimals
|
||||
* @param rawValue - should be a number that is not outside the safe range fail as in https://mikemcl.github.io/bignumber.js/#toN
|
||||
* @param formatDecimals - number of decimals to use
|
||||
@@ -90,18 +124,7 @@ export const formatNumber = (
|
||||
rawValue: string | number | BigNumber,
|
||||
formatDecimals = 0
|
||||
) => {
|
||||
return getNumberFormat(formatDecimals).format(Number(rawValue));
|
||||
};
|
||||
|
||||
/** formatNumberFixed will format the number with fixed decimals
|
||||
* @param rawValue - should be a number that is not outside the safe range fail as in https://mikemcl.github.io/bignumber.js/#toN
|
||||
* @param formatDecimals - number of decimals to use
|
||||
*/
|
||||
export const formatNumberFixed = (
|
||||
rawValue: string | number | BigNumber,
|
||||
formatDecimals = 0
|
||||
) => {
|
||||
return getFixedNumberFormat(formatDecimals).format(Number(rawValue));
|
||||
return new BigNumber(rawValue).toFormat(formatDecimals);
|
||||
};
|
||||
|
||||
export const quantumDecimalPlaces = (
|
||||
@@ -117,7 +140,11 @@ export const quantumDecimalPlaces = (
|
||||
? decimalPlaces
|
||||
: Math.max(
|
||||
0,
|
||||
Math.log10(100 / Number(addDecimal(rawQuantum, decimalPlaces)))
|
||||
Math.log10(
|
||||
new BigNumber(100)
|
||||
.dividedBy(toBigNum(rawQuantum, decimalPlaces))
|
||||
.toNumber()
|
||||
)
|
||||
);
|
||||
|
||||
return Math.ceil(formatDecimals);
|
||||
@@ -128,61 +155,44 @@ export const addDecimalsFormatNumberQuantum = (
|
||||
decimalPlaces: number,
|
||||
quantum: number | string
|
||||
) => {
|
||||
const val = toBigNum(rawValue, decimalPlaces);
|
||||
let formatDps = val.dp() ?? decimalPlaces;
|
||||
|
||||
if (isNaN(Number(quantum))) {
|
||||
return addDecimalsFormatNumber(rawValue, decimalPlaces);
|
||||
return val.toFormat(formatDps);
|
||||
}
|
||||
const quantumValue = addDecimal(quantum, decimalPlaces);
|
||||
const numberDP = Math.max(0, Math.log10(100 / Number(quantumValue)));
|
||||
return addDecimalsFormatNumber(rawValue, decimalPlaces, Math.ceil(numberDP));
|
||||
|
||||
formatDps = quantumDecimalPlaces(quantum, decimalPlaces);
|
||||
|
||||
return val.toFormat(formatDps);
|
||||
};
|
||||
|
||||
export const addDecimalsFormatNumber = (
|
||||
rawValue: string | number,
|
||||
decimalPlaces: number,
|
||||
formatDecimals: number = decimalPlaces
|
||||
formatDecimals?: number
|
||||
) => {
|
||||
const x = addDecimal(rawValue, decimalPlaces);
|
||||
|
||||
return formatNumber(x, formatDecimals);
|
||||
const val = toBigNum(rawValue, decimalPlaces);
|
||||
const naturalDp = val.dp() ?? 0;
|
||||
const formatDps = Math.max(
|
||||
0,
|
||||
formatDecimals === undefined ? naturalDp : formatDecimals
|
||||
);
|
||||
return val.toFormat(formatDps || 0);
|
||||
};
|
||||
|
||||
export const addDecimalsFixedFormatNumber = (
|
||||
rawValue: string | number,
|
||||
decimalPlaces: number,
|
||||
formatDecimals: number = decimalPlaces
|
||||
export const formatNumberPercentage = (
|
||||
value: BigNumber,
|
||||
formatDecimals?: number
|
||||
) => {
|
||||
const x = addDecimal(rawValue, decimalPlaces);
|
||||
|
||||
return formatNumberFixed(x, formatDecimals);
|
||||
};
|
||||
|
||||
export const formatNumberPercentage = (value: BigNumber, decimals?: number) => {
|
||||
const decimalPlaces =
|
||||
typeof decimals === 'undefined' ? value.dp() || 0 : decimals;
|
||||
return `${formatNumber(value, decimalPlaces)}%`;
|
||||
typeof formatDecimals === 'undefined' ? value.dp() || 0 : formatDecimals;
|
||||
return `${value.toFormat(decimalPlaces)}%`;
|
||||
};
|
||||
|
||||
export const toNumberParts = (
|
||||
value: BigNumber | null | undefined,
|
||||
decimals = 18
|
||||
): [integers: string, decimalPlaces: string, separator: string] => {
|
||||
if (!value) {
|
||||
return ['0', '0'.repeat(decimals), '.'];
|
||||
}
|
||||
const separator = getDecimalSeparator() || '.';
|
||||
const [integers, decimalsPlaces] = formatNumber(value, decimals)
|
||||
.toString()
|
||||
.split(separator);
|
||||
return [integers, decimalsPlaces || '', separator];
|
||||
};
|
||||
|
||||
export const isNumeric = (
|
||||
value?: string | number | BigNumber | bigint | null
|
||||
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
||||
|
||||
/**
|
||||
* Format a number greater than 1 million with m for million, b for billion
|
||||
* and t for trillion
|
||||
* Format numbers greater than 1 million with m for million, b for billion
|
||||
* and t for trillion, rounding to the nearest half
|
||||
*/
|
||||
export const formatNumberRounded = (num: BigNumber) => {
|
||||
let value = '';
|
||||
@@ -204,7 +214,7 @@ export const formatNumberRounded = (num: BigNumber) => {
|
||||
// Million
|
||||
value = `${format('1e6')}m`;
|
||||
} else {
|
||||
value = formatNumber(num);
|
||||
value = num.toFormat();
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -3,61 +3,59 @@ import { formatRange, formatValue } from './range';
|
||||
describe('formatValue', () => {
|
||||
it.each([
|
||||
{ v: 123000, d: 5, o: '1.23' },
|
||||
{ v: 123000, d: 3, o: '123.00' },
|
||||
{ v: 123000, d: 1, o: '12,300.0' },
|
||||
{ v: 123001000, d: 2, o: '1,230,010.00' },
|
||||
{ v: 123000, d: 3, o: '123' },
|
||||
{ v: 123000, d: 1, o: '12,300' },
|
||||
{ v: 123001000, d: 2, o: '1,230,010' },
|
||||
{ v: 123001, d: 2, o: '1,230.01' },
|
||||
{
|
||||
v: '123456789123456789',
|
||||
d: 10,
|
||||
o: '12,345,678.91234568',
|
||||
o: '12,345,678.9123456789',
|
||||
},
|
||||
])('formats values correctly', ({ v, d, o }) => {
|
||||
])('formatValue($v, $d) -> $o', ({ v, d, o }) => {
|
||||
expect(formatValue(v, d)).toStrictEqual(o);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ v: 123000, d: 5, o: '1.23', q: '0.1' },
|
||||
{ v: 123000, d: 3, o: '123.00', q: '0.1' },
|
||||
{ v: 123000, d: 1, o: '12,300.00', q: '0.1' },
|
||||
{ v: 123001000, d: 2, o: '1,230,010.00', q: '0.1' },
|
||||
{ v: 123000, d: 5, o: '1.2300000', q: '1' },
|
||||
{ v: 123000, d: 3, o: '123.00000', q: '1' },
|
||||
{ v: 123000, d: 1, o: '12,300.000', q: '1' },
|
||||
{ v: 123001000, d: 2, o: '1,230,010.0000', q: '1' },
|
||||
{ v: 123001, d: 2, o: '1,230.01', q: '100' },
|
||||
{ v: 123001, d: 2, o: '1,230.01', q: '0.1' },
|
||||
{ v: 123001, d: 2, o: '1,230.0100', q: '1' },
|
||||
{
|
||||
v: '123456789123456789',
|
||||
d: 10,
|
||||
o: '12,345,678.91234568',
|
||||
o: '12,345,678.91234567890000000',
|
||||
q: '0.00003846',
|
||||
},
|
||||
])(
|
||||
'formats with formatValue with quantum given number correctly',
|
||||
({ v, d, o, q }) => {
|
||||
expect(formatValue(v.toString(), d, q)).toStrictEqual(o);
|
||||
}
|
||||
);
|
||||
])('with quantum formatValue($v, $d, $q) -> $o', ({ v, d, o, q }) => {
|
||||
expect(formatValue(v.toString(), d, q)).toStrictEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatRange', () => {
|
||||
it.each([
|
||||
{
|
||||
min: 123000,
|
||||
max: 12300011111,
|
||||
d: 5,
|
||||
o: '1.23 - 123,000.11111',
|
||||
q: '0.1',
|
||||
o: '1.2300000 - 123,000.1111100',
|
||||
q: '1',
|
||||
},
|
||||
{
|
||||
min: 123000,
|
||||
max: 12300011111,
|
||||
d: 3,
|
||||
o: '123.00 - 12,300,011.111',
|
||||
q: '0.1',
|
||||
o: '123.00000 - 12,300,011.11100',
|
||||
q: '1',
|
||||
},
|
||||
{
|
||||
min: 123000,
|
||||
max: 12300011111,
|
||||
d: 1,
|
||||
o: '12,300.00 - 1,230,001,111.10',
|
||||
q: '0.1',
|
||||
o: '12,300.000 - 1,230,001,111.100',
|
||||
q: '1',
|
||||
},
|
||||
{
|
||||
min: 123001000,
|
||||
@@ -66,10 +64,7 @@ describe('formatRange', () => {
|
||||
o: '1,230,010.00 - 123,000,111.11',
|
||||
q: '100',
|
||||
},
|
||||
])(
|
||||
'formats with formatValue with quantum given number correctly',
|
||||
({ min, max, d, o, q }) => {
|
||||
expect(formatRange(min, max, d, q)).toStrictEqual(o);
|
||||
}
|
||||
);
|
||||
])('formatRange($min, $max, $d, $q) -> $o', ({ min, max, d, o, q }) => {
|
||||
expect(formatRange(min, max, d, q)).toStrictEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,27 +1,37 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { addDecimalsFormatNumber } from './number';
|
||||
import { useCallback } from 'react';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
export const formatTrigger = (
|
||||
data: Pick<Schema.StopOrder, 'trigger' | 'triggerDirection'> | undefined,
|
||||
marketDecimalPlaces: number,
|
||||
defaultValue = '-'
|
||||
) => {
|
||||
if (data && data?.trigger?.__typename === 'StopOrderPrice') {
|
||||
return `${t('Mark')} ${
|
||||
data?.triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
? '<'
|
||||
: '>'
|
||||
} ${addDecimalsFormatNumber(data.trigger.price, marketDecimalPlaces)}`;
|
||||
}
|
||||
if (data && data?.trigger?.__typename === 'StopOrderTrailingPercentOffset') {
|
||||
return `${t('Mark')} ${
|
||||
data?.triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
? '+'
|
||||
: '-'
|
||||
}${(Number(data?.trigger.trailingPercentOffset) * 100).toFixed(1)}%`;
|
||||
}
|
||||
return defaultValue;
|
||||
export const useFormatTrigger = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(
|
||||
data: Pick<Schema.StopOrder, 'trigger' | 'triggerDirection'> | undefined,
|
||||
marketDecimalPlaces: number,
|
||||
defaultValue = '-'
|
||||
) => {
|
||||
if (data && data?.trigger?.__typename === 'StopOrderPrice') {
|
||||
return `${t('Mark')} ${
|
||||
data?.triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
? '<'
|
||||
: '>'
|
||||
} ${addDecimalsFormatNumber(data.trigger.price, marketDecimalPlaces)}`;
|
||||
}
|
||||
if (
|
||||
data &&
|
||||
data?.trigger?.__typename === 'StopOrderTrailingPercentOffset'
|
||||
) {
|
||||
return `${t('Mark')} ${
|
||||
data?.triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
? '+'
|
||||
: '-'
|
||||
}${(Number(data?.trigger.trailingPercentOffset) * 100).toFixed(1)}%`;
|
||||
}
|
||||
return defaultValue;
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { isValid, parseISO } from 'date-fns';
|
||||
import { getDateTimeFormat } from './format';
|
||||
import { useT } from './use-t';
|
||||
|
||||
export const getMarketExpiryDate = (
|
||||
tags?: ReadonlyArray<string> | null
|
||||
@@ -40,12 +40,15 @@ export const getExpiryDate = (
|
||||
close: string | null,
|
||||
state: MarketState
|
||||
): string => {
|
||||
const t = useT();
|
||||
const metadataExpiryDate = getMarketExpiryDate(tags);
|
||||
const marketTimestampCloseDate = close && new Date(close);
|
||||
let content = null;
|
||||
if (!metadataExpiryDate) {
|
||||
content = marketTimestampCloseDate
|
||||
? `Expired on ${getDateTimeFormat().format(marketTimestampCloseDate)}`
|
||||
? t('Expired on {{date}}', {
|
||||
date: getDateTimeFormat().format(marketTimestampCloseDate),
|
||||
})
|
||||
: t('Not time-based');
|
||||
} else {
|
||||
const isExpired =
|
||||
@@ -54,7 +57,9 @@ export const getExpiryDate = (
|
||||
state === MarketState.STATE_SETTLED);
|
||||
if (isExpired) {
|
||||
content = marketTimestampCloseDate
|
||||
? `Expired on ${getDateTimeFormat().format(marketTimestampCloseDate)}`
|
||||
? t('Expired on {{date}}', {
|
||||
date: getDateTimeFormat().format(marketTimestampCloseDate),
|
||||
})
|
||||
: t('Expired');
|
||||
} else {
|
||||
content = getDateTimeFormat().format(metadataExpiryDate);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const ns = 'utils';
|
||||
export const useT = () => useTranslation(ns).t;
|
||||
@@ -1,6 +1,10 @@
|
||||
import { ethereumAddress, vegaPublicKey } from './common';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useEthereumAddress, useVegaPublicKey } from './common';
|
||||
|
||||
it('ethereumAddress', () => {
|
||||
const result = renderHook(useEthereumAddress);
|
||||
const ethereumAddress = result.result.current;
|
||||
|
||||
const errorMessage = 'Invalid Ethereum address';
|
||||
|
||||
const validAddress = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853';
|
||||
@@ -17,6 +21,9 @@ it('ethereumAddress', () => {
|
||||
});
|
||||
|
||||
it('vegaPublicKey', () => {
|
||||
const result = renderHook(useVegaPublicKey);
|
||||
const vegaPublicKey = result.result.current;
|
||||
|
||||
const errorMessage = 'Invalid Vega key';
|
||||
|
||||
const validKey =
|
||||
|
||||
@@ -1,40 +1,71 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from '../use-t';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const required = (value: string) => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return t('Required');
|
||||
}
|
||||
return true;
|
||||
export const useRequired = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(value: string) => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return t('Required');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const ethereumAddress = (value: string) => {
|
||||
if (!/^0x[0-9a-fA-F]{40}$/i.test(value)) {
|
||||
return t('Invalid Ethereum address');
|
||||
}
|
||||
return true;
|
||||
export const useEthereumAddress = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(value: string) => {
|
||||
if (!/^0x[0-9a-fA-F]{40}$/i.test(value)) {
|
||||
return t('Invalid Ethereum address');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const VEGA_ID_REGEX = /^[A-Fa-f0-9]{64}$/i;
|
||||
export const vegaPublicKey = (value: string) => {
|
||||
if (!VEGA_ID_REGEX.test(value)) {
|
||||
return t('Invalid Vega key');
|
||||
}
|
||||
return true;
|
||||
export const useVegaPublicKey = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(value: string) => {
|
||||
if (!VEGA_ID_REGEX.test(value)) {
|
||||
return t('Invalid Vega key');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const minSafe = (min: BigNumber) => (value: string) => {
|
||||
if (new BigNumber(value).isLessThan(min)) {
|
||||
return t('Value is below minimum');
|
||||
}
|
||||
return true;
|
||||
export const useMinSafe = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(min: BigNumber) => (value: string) => {
|
||||
if (new BigNumber(value).isLessThan(min)) {
|
||||
return t('Value is below minimum');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const maxSafe = (max: BigNumber) => (value: string) => {
|
||||
if (new BigNumber(value).isGreaterThan(max)) {
|
||||
return t('Value is above maximum');
|
||||
}
|
||||
return true;
|
||||
export const useMaxSafe = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(max: BigNumber) => (value: string) => {
|
||||
if (new BigNumber(value).isGreaterThan(max)) {
|
||||
return t('Value is above maximum');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const suitableForSyntaxHighlighter = (str: string) => {
|
||||
@@ -46,11 +77,17 @@ export const suitableForSyntaxHighlighter = (str: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const validateJson = (value: string) => {
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return t('Must be valid JSON');
|
||||
}
|
||||
export const useValidateJson = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(value: string) => {
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return t('Must be valid JSON');
|
||||
}
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCallback } from 'react';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
export const validateAmount = (step: number | string, field: string) => {
|
||||
const [, stepDecimals = ''] = String(step).split('.');
|
||||
export const useValidateAmount = () => {
|
||||
const t = useT();
|
||||
return useCallback(
|
||||
(step: number | string, field: string) => {
|
||||
const [, stepDecimals = ''] = String(step).split('.');
|
||||
|
||||
return (value?: string) => {
|
||||
if (Number(step) > 1) {
|
||||
if (Number(value) % Number(step) > 0) {
|
||||
return t(`${field} must be a multiple of ${step} for this market`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const [, valueDecimals = ''] = (value || '').split('.');
|
||||
if (stepDecimals.length < valueDecimals.length) {
|
||||
if (stepDecimals === '') {
|
||||
return t(`${field} must be whole numbers for this market`);
|
||||
}
|
||||
return t(`${field} accepts up to ${stepDecimals.length} decimal places`);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return (value?: string) => {
|
||||
if (Number(step) > 1) {
|
||||
if (Number(value) % Number(step) > 0) {
|
||||
return t(
|
||||
'{{field}} must be a multiple of {{step}} for this market',
|
||||
{
|
||||
field,
|
||||
step,
|
||||
}
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const [, valueDecimals = ''] = (value || '').split('.');
|
||||
if (stepDecimals.length < valueDecimals.length) {
|
||||
if (stepDecimals === '') {
|
||||
return t('{{field}} must be whole numbers for this market', {
|
||||
field,
|
||||
});
|
||||
}
|
||||
return t('{{field}} accepts up to {{decimals}} decimal places', {
|
||||
field,
|
||||
decimals: stepDecimals.length,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
},
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -365,20 +365,20 @@ describe('VegaTransactionDetails', () => {
|
||||
});
|
||||
it.each([
|
||||
{ tx: withdraw, details: 'Withdraw 12.34 $A' },
|
||||
{ tx: submitOrder, details: 'Submit order - activeM1+0.10 @ 12.34 $A' },
|
||||
{ tx: submitOrder, details: 'Submit order - activeM1+0.1 @ 12.34 $A' },
|
||||
{
|
||||
tx: submitStopOrder,
|
||||
details: 'Submit stop orderM1+0.10 @ ~ $AMark > 12.34',
|
||||
details: 'Submit stop orderM1+0.1 @ ~ $AMark > 12.34',
|
||||
},
|
||||
{
|
||||
tx: editOrder,
|
||||
details: 'Edit order - activeM1+0.10 @ 12.34 $A+0.11 @ 10.00 $A',
|
||||
details: 'Edit order - activeM1+0.1 @ 12.34 $A+0.11 @ 10 $A',
|
||||
},
|
||||
{ tx: cancelOrder, details: 'Cancel orderM1+0.10 @ 12.34 $A' },
|
||||
{ tx: cancelOrder, details: 'Cancel orderM1+0.1 @ 12.34 $A' },
|
||||
{ tx: cancelAll, details: 'Cancel all orders' },
|
||||
{
|
||||
tx: cancelStopOrder,
|
||||
details: 'Cancel stop orderM1-0.10 @ 12.34 $AMark > 12.34',
|
||||
details: 'Cancel stop orderM1-0.1 @ 12.34 $AMark > 12.34',
|
||||
},
|
||||
{ tx: closePosition, details: 'Close position for M1' },
|
||||
{ tx: batch, details: 'Batch market instruction' },
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
formatNumber,
|
||||
toBigNum,
|
||||
truncateByChars,
|
||||
formatTrigger,
|
||||
useFormatTrigger,
|
||||
MAXGOINT64,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { useAssetsMapProvider } from '@vegaprotocol/assets';
|
||||
@@ -260,6 +260,7 @@ const SubmitStopOrderSetup = ({
|
||||
triggerDirection: Schema.StopOrderTriggerDirection;
|
||||
market: Market;
|
||||
}) => {
|
||||
const formatTrigger = useFormatTrigger();
|
||||
if (!market || !stopOrderSetup) return null;
|
||||
|
||||
const { price, size, side } = stopOrderSetup.orderSubmission;
|
||||
@@ -446,6 +447,7 @@ const CancelOrderDetails = ({
|
||||
|
||||
const CancelStopOrderDetails = ({ stopOrderId }: { stopOrderId: string }) => {
|
||||
const t = useT();
|
||||
const formatTrigger = useFormatTrigger();
|
||||
const { data: orderById } = useStopOrderByIdQuery({
|
||||
variables: { stopOrderId },
|
||||
});
|
||||
@@ -732,7 +734,7 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
<p>{t('Your funds have been unlocked for withdrawal.')}</p>
|
||||
{tx.txHash && (
|
||||
<ExternalLink
|
||||
className="block mb-[5px] break-all"
|
||||
className="mb-[5px] block break-all"
|
||||
href={explorerLink(EXPLORER_TX.replace(':hash', tx.txHash))}
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { AssetOption } from '@vegaprotocol/assets';
|
||||
import {
|
||||
ethereumAddress,
|
||||
minSafe,
|
||||
useEthereumAddress,
|
||||
useRequired,
|
||||
useMinSafe,
|
||||
removeDecimal,
|
||||
required,
|
||||
isAssetTypeERC20,
|
||||
formatNumber,
|
||||
} from '@vegaprotocol/utils';
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useEffect, type ButtonHTMLAttributes } from 'react';
|
||||
import type { ControllerRenderProps } from 'react-hook-form';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { useForm, Controller, useWatch } from 'react-hook-form';
|
||||
import { WithdrawLimits } from './withdraw-limits';
|
||||
@@ -112,6 +111,10 @@ export const WithdrawForm = ({
|
||||
onSelectAsset,
|
||||
submitWithdraw,
|
||||
}: WithdrawFormProps) => {
|
||||
const ethereumAddress = useEthereumAddress();
|
||||
const required = useRequired();
|
||||
const minSafe = useMinSafe();
|
||||
|
||||
const { account: address } = useWeb3React();
|
||||
const {
|
||||
register,
|
||||
@@ -150,36 +153,6 @@ export const WithdrawForm = ({
|
||||
trigger('to');
|
||||
}, [address, setValue, trigger]);
|
||||
|
||||
const renderAssetsSelector = ({
|
||||
field,
|
||||
}: {
|
||||
field: ControllerRenderProps<FormFields, 'asset'>;
|
||||
}) => {
|
||||
return (
|
||||
<TradingRichSelect
|
||||
data-testid="select-asset"
|
||||
id="asset"
|
||||
name="asset"
|
||||
required
|
||||
onValueChange={(value) => {
|
||||
onSelectAsset(value);
|
||||
field.onChange(value);
|
||||
}}
|
||||
placeholder={t('Please select an asset')}
|
||||
value={selectedAsset?.id}
|
||||
hasError={Boolean(errors.asset?.message)}
|
||||
>
|
||||
{assets.filter(isAssetTypeERC20).map((a) => (
|
||||
<AssetOption
|
||||
key={a.id}
|
||||
asset={a}
|
||||
balance={<AssetBalance asset={a} />}
|
||||
/>
|
||||
))}
|
||||
</TradingRichSelect>
|
||||
);
|
||||
};
|
||||
|
||||
const showWithdrawDelayNotification =
|
||||
Boolean(delay) &&
|
||||
Boolean(selectedAsset) &&
|
||||
@@ -189,7 +162,7 @@ export const WithdrawForm = ({
|
||||
<>
|
||||
<div className="mb-4 text-sm">
|
||||
<p>{t('There are two steps required to make a withdrawal')}</p>
|
||||
<ol className="pl-4 list-disc">
|
||||
<ol className="list-disc pl-4">
|
||||
<li>{t('Step 1 - Release funds from Vega')}</li>
|
||||
<li>{t('Step 2 - Transfer funds to your Ethereum wallet')}</li>
|
||||
</ol>
|
||||
@@ -208,7 +181,29 @@ export const WithdrawForm = ({
|
||||
required: (value) => !!selectedAsset || required(value),
|
||||
},
|
||||
}}
|
||||
render={renderAssetsSelector}
|
||||
render={({ field }) => (
|
||||
<TradingRichSelect
|
||||
data-testid="select-asset"
|
||||
id="asset"
|
||||
name="asset"
|
||||
required
|
||||
onValueChange={(value) => {
|
||||
onSelectAsset(value);
|
||||
field.onChange(value);
|
||||
}}
|
||||
placeholder={t('Please select an asset')}
|
||||
value={selectedAsset?.id}
|
||||
hasError={Boolean(errors.asset?.message)}
|
||||
>
|
||||
{assets.filter(isAssetTypeERC20).map((a) => (
|
||||
<AssetOption
|
||||
key={a.id}
|
||||
asset={a}
|
||||
balance={<AssetBalance asset={a} />}
|
||||
/>
|
||||
))}
|
||||
</TradingRichSelect>
|
||||
)}
|
||||
/>
|
||||
{errors.asset?.message && (
|
||||
<TradingInputError intent="danger">
|
||||
@@ -314,7 +309,7 @@ const UseButton = (props: UseButtonProps) => {
|
||||
<button
|
||||
{...props}
|
||||
type="button"
|
||||
className="absolute top-0 right-0 ml-auto text-sm underline"
|
||||
className="absolute right-0 top-0 ml-auto text-sm underline"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('Withdrawals', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
'asset-symbol',
|
||||
'1.00',
|
||||
'1',
|
||||
'123456…123456',
|
||||
getTimeFormat().format(new Date(withdrawal.createdTimestamp as string)),
|
||||
'-',
|
||||
@@ -67,7 +67,7 @@ describe('Withdrawals', () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
'asset-symbol',
|
||||
'1.00',
|
||||
'1',
|
||||
'123456…123456',
|
||||
getTimeFormat().format(new Date(withdrawal.createdTimestamp as string)),
|
||||
getTimeFormat().format(
|
||||
|
||||
Reference in New Issue
Block a user