chore: format limits (547) (#1742)
This commit is contained in:
parent
5d04efe52d
commit
50611a4ba6
@ -77,18 +77,24 @@ describe('withdraw', { tags: '@smoke' }, () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
selectAsset(asset1Name);
|
selectAsset(asset1Name);
|
||||||
cy.getByTestId('balance-available')
|
cy.getByTestId('BALANCE_AVAILABLE_label').should(
|
||||||
.should('contain.text', 'Balance available')
|
'contain.text',
|
||||||
.find('td')
|
'Balance available'
|
||||||
.should('have.text', '1,000.00000');
|
);
|
||||||
cy.getByTestId('withdrawal-threshold')
|
cy.getByTestId('BALANCE_AVAILABLE_value').should(
|
||||||
.should('contain.text', 'Delayed withdrawal threshold')
|
'have.text',
|
||||||
.find('td')
|
'1,000.00000'
|
||||||
.should('contain.text', '1m+');
|
);
|
||||||
cy.getByTestId('delay-time')
|
cy.getByTestId('WITHDRAWAL_THRESHOLD_label').should(
|
||||||
.should('contain.text', 'Delay time')
|
'contain.text',
|
||||||
.find('td')
|
'Delayed withdrawal threshold'
|
||||||
.should('have.text', 'None');
|
);
|
||||||
|
cy.getByTestId('WITHDRAWAL_THRESHOLD_value').should(
|
||||||
|
'contain.text',
|
||||||
|
'100.00000'
|
||||||
|
);
|
||||||
|
cy.getByTestId('DELAY_TIME_label').should('contain.text', 'Delay time');
|
||||||
|
cy.getByTestId('DELAY_TIME_value').should('have.text', 'None');
|
||||||
cy.get(amountField).clear().type('10');
|
cy.get(amountField).clear().type('10');
|
||||||
cy.getByTestId(submitWithdrawBtn).click();
|
cy.getByTestId(submitWithdrawBtn).click();
|
||||||
cy.getByTestId('dialog-title').should(
|
cy.getByTestId('dialog-title').should(
|
||||||
|
@ -227,20 +227,12 @@ describe('Deposit form', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Check deposit limit is displayed
|
// Check deposit limit is displayed
|
||||||
expect(
|
expect(screen.getByTestId('BALANCE_AVAILABLE_value')).toHaveTextContent(
|
||||||
screen.getByText('Balance available', { selector: 'th' })
|
'50'
|
||||||
.nextElementSibling
|
);
|
||||||
).toHaveTextContent(balance.toString());
|
expect(screen.getByTestId('MAX_LIMIT_value')).toHaveTextContent('20');
|
||||||
expect(
|
expect(screen.getByTestId('DEPOSITED_value')).toHaveTextContent('10');
|
||||||
screen.getByText('Maximum total deposit amount', { selector: 'th' })
|
expect(screen.getByTestId('REMAINING_value')).toHaveTextContent('10');
|
||||||
.nextElementSibling
|
|
||||||
).toHaveTextContent(max.toString());
|
|
||||||
expect(
|
|
||||||
screen.getByText('Deposited', { selector: 'th' }).nextElementSibling
|
|
||||||
).toHaveTextContent(deposited.toString());
|
|
||||||
expect(
|
|
||||||
screen.getByText('Remaining', { selector: 'th' }).nextElementSibling
|
|
||||||
).toHaveTextContent(max.minus(deposited).toString());
|
|
||||||
|
|
||||||
fireEvent.change(screen.getByLabelText('Amount'), {
|
fireEvent.change(screen.getByLabelText('Amount'), {
|
||||||
target: { value: '8' },
|
target: { value: '8' },
|
||||||
|
@ -220,7 +220,12 @@ export const DepositForm = ({
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
{selectedAsset && max && deposited && (
|
{selectedAsset && max && deposited && (
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<DepositLimits max={max} deposited={deposited} balance={balance} />
|
<DepositLimits
|
||||||
|
max={max}
|
||||||
|
deposited={deposited}
|
||||||
|
balance={balance}
|
||||||
|
asset={selectedAsset}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<FormGroup label={t('Amount')} labelFor="amount">
|
<FormGroup label={t('Amount')} labelFor="amount">
|
||||||
|
@ -1,60 +1,65 @@
|
|||||||
import { formatNumber, t } from '@vegaprotocol/react-helpers';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
|
import { compactNumber, t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||||
import type BigNumber from 'bignumber.js';
|
import type BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
|
// Note: all of the values here are with correct asset's decimals
|
||||||
|
// See `libs/deposits/src/lib/use-deposit-balances.ts`
|
||||||
|
|
||||||
interface DepositLimitsProps {
|
interface DepositLimitsProps {
|
||||||
max: BigNumber;
|
max: BigNumber;
|
||||||
deposited: BigNumber;
|
deposited: BigNumber;
|
||||||
|
asset: Asset;
|
||||||
balance?: BigNumber;
|
balance?: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DepositLimits = ({
|
export const DepositLimits = ({
|
||||||
max,
|
max,
|
||||||
deposited,
|
deposited,
|
||||||
|
asset,
|
||||||
balance,
|
balance,
|
||||||
}: DepositLimitsProps) => {
|
}: DepositLimitsProps) => {
|
||||||
let maxLimit = '';
|
const limits = [
|
||||||
if (max.isEqualTo(Infinity)) {
|
{
|
||||||
maxLimit = t('No limit');
|
key: 'BALANCE_AVAILABLE',
|
||||||
} else if (max.isGreaterThan(1_000_000)) {
|
label: t('Balance available'),
|
||||||
maxLimit = t('1m+');
|
rawValue: balance,
|
||||||
} else {
|
value: balance ? compactNumber(balance, asset.decimals) : '-',
|
||||||
maxLimit = max.toString();
|
},
|
||||||
}
|
{
|
||||||
|
key: 'MAX_LIMIT',
|
||||||
let remaining = '';
|
label: t('Maximum total deposit amount'),
|
||||||
if (deposited.isEqualTo(0)) {
|
rawValue: max,
|
||||||
remaining = maxLimit;
|
value: compactNumber(max, asset.decimals),
|
||||||
} else {
|
},
|
||||||
const amountRemaining = max.minus(deposited);
|
{
|
||||||
remaining = amountRemaining.isGreaterThan(1_000_000)
|
key: 'DEPOSITED',
|
||||||
? t('1m+')
|
label: t('Deposited'),
|
||||||
: amountRemaining.toString();
|
rawValue: deposited,
|
||||||
}
|
value: compactNumber(deposited, asset.decimals),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'REMAINING',
|
||||||
|
label: t('Remaining'),
|
||||||
|
rawValue: max.minus(deposited),
|
||||||
|
value: compactNumber(max.minus(deposited), asset.decimals),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="w-full text-sm">
|
<KeyValueTable>
|
||||||
<tbody>
|
{limits.map(({ key, label, rawValue, value }) => (
|
||||||
<tr>
|
<KeyValueTableRow>
|
||||||
<th className="text-left font-normal">{t('Balance available')}</th>
|
<div data-testid={`${key}_label`}>{label}</div>
|
||||||
<td className="text-right">
|
<div
|
||||||
{balance ? formatNumber(balance) : '-'}
|
data-testid={`${key}_value`}
|
||||||
</td>
|
className="truncate"
|
||||||
</tr>
|
title={rawValue?.toString()}
|
||||||
<tr>
|
>
|
||||||
<th className="text-left font-normal">
|
{value}
|
||||||
{t('Maximum total deposit amount')}
|
</div>
|
||||||
</th>
|
</KeyValueTableRow>
|
||||||
<td className="text-right">{maxLimit}</td>
|
))}
|
||||||
</tr>
|
</KeyValueTable>
|
||||||
<tr>
|
|
||||||
<th className="text-left font-normal">{t('Deposited')}</th>
|
|
||||||
<td className="text-right">{formatNumber(deposited)}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th className="text-left font-normal">{t('Remaining')}</th>
|
|
||||||
<td className="text-right">{remaining}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
formatNumberPercentage,
|
formatNumberPercentage,
|
||||||
toNumberParts,
|
toNumberParts,
|
||||||
isNumeric,
|
isNumeric,
|
||||||
|
compactNumber,
|
||||||
} from './number';
|
} from './number';
|
||||||
|
|
||||||
describe('formatNumber and formatNumberPercentage', () => {
|
describe('formatNumber and formatNumberPercentage', () => {
|
||||||
@ -89,3 +90,93 @@ describe('isNumeric', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('compactNumber', () => {
|
||||||
|
const short: [BigNumber, string | JSX.Element, number | 'infer'][] = [
|
||||||
|
[new BigNumber(Infinity), '∞', 'infer'],
|
||||||
|
[new BigNumber(-Infinity), '-∞', 'infer'],
|
||||||
|
[new BigNumber(0), '0', 'infer'],
|
||||||
|
[new BigNumber(1), '1', 'infer'],
|
||||||
|
[new BigNumber(100), '100', 'infer'],
|
||||||
|
[new BigNumber(100.456601), '100.456601', 'infer'],
|
||||||
|
[new BigNumber(1_000), '1,000', 'infer'],
|
||||||
|
[new BigNumber(999_999), '999,999', 'infer'],
|
||||||
|
[new BigNumber(1_000_000), '1M', 'infer'],
|
||||||
|
[new BigNumber(100_000_000), '100M', 'infer'],
|
||||||
|
[new BigNumber(1_000_000_000), '1B', 'infer'],
|
||||||
|
[new BigNumber(1_000_000_000_000), '1T', 'infer'],
|
||||||
|
[new BigNumber(3.23e12), '3.23T', 2],
|
||||||
|
[new BigNumber(3.23e12), '3.23000T', 5],
|
||||||
|
[
|
||||||
|
new BigNumber(3.23e24),
|
||||||
|
<span>
|
||||||
|
3.23000{' '}
|
||||||
|
<span>
|
||||||
|
× 10<sup>24</sup>
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
5,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
new BigNumber(1.579208923731619e59),
|
||||||
|
<span>
|
||||||
|
1.57921{' '}
|
||||||
|
<span>
|
||||||
|
× 10
|
||||||
|
<sup>59</sup>
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
5,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
it.each(short)(
|
||||||
|
'compacts %d to %p (decimal places: %p)',
|
||||||
|
(input, output, decimals) => {
|
||||||
|
expect(compactNumber(input, decimals)).toEqual(output);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const long: [BigNumber, string | JSX.Element, number | 'infer'][] = [
|
||||||
|
[new BigNumber(Infinity), '∞', 'infer'],
|
||||||
|
[new BigNumber(-Infinity), '-∞', 'infer'],
|
||||||
|
[new BigNumber(0), '0', 'infer'],
|
||||||
|
[new BigNumber(1), '1', 'infer'],
|
||||||
|
[new BigNumber(100), '100', 'infer'],
|
||||||
|
[new BigNumber(100.456601), '100.456601', 'infer'],
|
||||||
|
[new BigNumber(1_000), '1,000', 'infer'],
|
||||||
|
[new BigNumber(999_999), '999,999', 'infer'],
|
||||||
|
[new BigNumber(1_000_000), '1 million', 'infer'],
|
||||||
|
[new BigNumber(100_000_000), '100 million', 'infer'],
|
||||||
|
[new BigNumber(1_000_000_000), '1 billion', 'infer'],
|
||||||
|
[new BigNumber(1_000_000_000_000), '1 trillion', 'infer'],
|
||||||
|
[new BigNumber(3.23e12), '3.23 trillion', 2],
|
||||||
|
[new BigNumber(3.23e12), '3.23000 trillion', 5],
|
||||||
|
[
|
||||||
|
new BigNumber(3.23e24),
|
||||||
|
<span>
|
||||||
|
3.23000{' '}
|
||||||
|
<span>
|
||||||
|
× 10<sup>24</sup>
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
5,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
new BigNumber(1.579208923731619e59),
|
||||||
|
<span>
|
||||||
|
1.57921{' '}
|
||||||
|
<span>
|
||||||
|
× 10
|
||||||
|
<sup>59</sup>
|
||||||
|
</span>
|
||||||
|
</span>,
|
||||||
|
5,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
it.each(long)(
|
||||||
|
'compacts %d to %p (decimal places: %p)',
|
||||||
|
(input, output, decimals) => {
|
||||||
|
expect(compactNumber(input, decimals, 'long')).toEqual(output);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
@ -100,3 +100,67 @@ export const useNumberParts = (
|
|||||||
export const isNumeric = (
|
export const isNumeric = (
|
||||||
value?: string | number | BigNumber | null
|
value?: string | number | BigNumber | null
|
||||||
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
): value is NonNullable<number | string> => /^-?\d*\.?\d+$/.test(String(value));
|
||||||
|
|
||||||
|
const INFINITY = '∞';
|
||||||
|
const DEFAULT_COMPACT_ABOVE = 1_000_000;
|
||||||
|
const DEFAULT_COMPACT_CAP = new BigNumber(1e24);
|
||||||
|
/**
|
||||||
|
* Compacts given number to human readable format.
|
||||||
|
* @param number
|
||||||
|
* @param decimals Number of decimal places
|
||||||
|
* @param compactDisplay Display mode; short -> 1e6 == 1M; ling -> 1e6 1 million
|
||||||
|
* @param compactAbove Compact number above threshold
|
||||||
|
* @param cap Use scientific notation above threshold
|
||||||
|
*/
|
||||||
|
export const compactNumber = (
|
||||||
|
number: BigNumber,
|
||||||
|
decimals: number | 'infer' = 'infer',
|
||||||
|
compactDisplay: 'short' | 'long' = 'short',
|
||||||
|
compactAbove = DEFAULT_COMPACT_ABOVE,
|
||||||
|
cap = DEFAULT_COMPACT_CAP
|
||||||
|
) => {
|
||||||
|
if (!number.isFinite()) return `${number.isNegative() ? '-' : ''}${INFINITY}`;
|
||||||
|
|
||||||
|
const decimalPlaces =
|
||||||
|
(decimals === 'infer' ? number.decimalPlaces() : decimals) || 0;
|
||||||
|
|
||||||
|
if (number.isLessThan(DEFAULT_COMPACT_ABOVE)) {
|
||||||
|
return formatNumber(number, decimalPlaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: it compacts number up to 1_000_000_000_000 (1e12) -> 1T, all above is formatted as iteration of T.
|
||||||
|
* Example: 1.579208923731619e59 -> 157,920,892,373,161,900,000,000,000,000,000,000,000,000,000,000T
|
||||||
|
*/
|
||||||
|
const compactNumFormat = new Intl.NumberFormat(getUserLocale(), {
|
||||||
|
minimumFractionDigits: Math.max(0, decimalPlaces),
|
||||||
|
maximumFractionDigits: Math.max(0, decimalPlaces),
|
||||||
|
notation: 'compact',
|
||||||
|
compactDisplay,
|
||||||
|
});
|
||||||
|
const scientificNumFormat = new Intl.NumberFormat(getUserLocale(), {
|
||||||
|
minimumFractionDigits: Math.max(0, decimalPlaces),
|
||||||
|
maximumFractionDigits: Math.max(0, decimalPlaces),
|
||||||
|
notation: 'scientific',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (number.isGreaterThan(DEFAULT_COMPACT_CAP)) {
|
||||||
|
const r = /E(\d+)$/i;
|
||||||
|
const formatted = scientificNumFormat.format(Number(number));
|
||||||
|
const eNotation = formatted.match(r);
|
||||||
|
if (eNotation && eNotation.length > 1) {
|
||||||
|
const power = eNotation[1];
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{formatted.replace(r, '')}{' '}
|
||||||
|
<span>
|
||||||
|
× 10
|
||||||
|
<sup>{power}</sup>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return compactNumFormat.format(Number(number));
|
||||||
|
};
|
@ -1,5 +1,4 @@
|
|||||||
import type { Asset } from '@vegaprotocol/assets';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
import { formatNumber } from '@vegaprotocol/react-helpers';
|
|
||||||
import {
|
import {
|
||||||
ethereumAddress,
|
ethereumAddress,
|
||||||
minSafe,
|
minSafe,
|
||||||
@ -147,7 +146,8 @@ export const WithdrawForm = ({
|
|||||||
amount={amount}
|
amount={amount}
|
||||||
threshold={threshold}
|
threshold={threshold}
|
||||||
delay={delay}
|
delay={delay}
|
||||||
balance={formatNumber(balance, selectedAsset.decimals)}
|
balance={balance}
|
||||||
|
asset={selectedAsset}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import { t } from '@vegaprotocol/react-helpers';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
|
import { compactNumber, t } from '@vegaprotocol/react-helpers';
|
||||||
|
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
|
|
||||||
interface WithdrawLimitsProps {
|
interface WithdrawLimitsProps {
|
||||||
amount: string;
|
amount: string;
|
||||||
threshold: BigNumber;
|
threshold: BigNumber;
|
||||||
balance: string;
|
balance: BigNumber;
|
||||||
delay: number | undefined;
|
delay: number | undefined;
|
||||||
|
asset: Asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WithdrawLimits = ({
|
export const WithdrawLimits = ({
|
||||||
@ -14,40 +17,47 @@ export const WithdrawLimits = ({
|
|||||||
threshold,
|
threshold,
|
||||||
balance,
|
balance,
|
||||||
delay,
|
delay,
|
||||||
|
asset,
|
||||||
}: WithdrawLimitsProps) => {
|
}: WithdrawLimitsProps) => {
|
||||||
let text = '';
|
|
||||||
|
|
||||||
if (threshold.isEqualTo(Infinity)) {
|
|
||||||
text = t('No limit');
|
|
||||||
} else if (threshold.isGreaterThan(1_000_000)) {
|
|
||||||
text = t('1m+');
|
|
||||||
} else {
|
|
||||||
text = threshold.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const delayTime =
|
const delayTime =
|
||||||
new BigNumber(amount).isGreaterThan(threshold) && delay
|
new BigNumber(amount).isGreaterThan(threshold) && delay
|
||||||
? formatDistanceToNow(Date.now() + delay * 1000)
|
? formatDistanceToNow(Date.now() + delay * 1000)
|
||||||
: t('None');
|
: t('None');
|
||||||
|
|
||||||
|
const limits = [
|
||||||
|
{
|
||||||
|
key: 'BALANCE_AVAILABLE',
|
||||||
|
label: t('Balance available'),
|
||||||
|
rawValue: balance,
|
||||||
|
value: balance ? compactNumber(balance, asset.decimals) : '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'WITHDRAWAL_THRESHOLD',
|
||||||
|
label: t('Delayed withdrawal threshold'),
|
||||||
|
rawValue: threshold,
|
||||||
|
value: compactNumber(threshold, asset.decimals),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'DELAY_TIME',
|
||||||
|
label: t('Delay time'),
|
||||||
|
value: delayTime,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="w-full text-sm">
|
<KeyValueTable>
|
||||||
<tbody>
|
{limits.map(({ key, label, rawValue, value }) => (
|
||||||
<tr data-testid="balance-available">
|
<KeyValueTableRow>
|
||||||
<th className="text-left font-normal">{t('Balance available')}</th>
|
<div data-testid={`${key}_label`}>{label}</div>
|
||||||
<td className="text-right">{balance}</td>
|
<div
|
||||||
</tr>
|
data-testid={`${key}_value`}
|
||||||
<tr data-testid="withdrawal-threshold">
|
className="truncate"
|
||||||
<th className="text-left font-normal">
|
title={rawValue?.toString()}
|
||||||
{t('Delayed withdrawal threshold')}
|
>
|
||||||
</th>
|
{value}
|
||||||
<td className="text-right">{text}</td>
|
</div>
|
||||||
</tr>
|
</KeyValueTableRow>
|
||||||
<tr data-testid="delay-time">
|
))}
|
||||||
<th className="text-left font-normal">{t('Delay time')}</th>
|
</KeyValueTable>
|
||||||
<td className="text-right">{delayTime}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user