chore(deposits): impove UX on deposit dialog (#3401)
This commit is contained in:
parent
0d9bd67465
commit
ecd362615e
@ -87,7 +87,10 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
|
|||||||
.clear()
|
.clear()
|
||||||
.type('850')
|
.type('850')
|
||||||
.next(`[data-testid="${formFieldError}"]`)
|
.next(`[data-testid="${formFieldError}"]`)
|
||||||
.should('have.text', 'Insufficient amount in Ethereum wallet');
|
.should(
|
||||||
|
'have.text',
|
||||||
|
"You can't deposit more than you have in your Ethereum wallet, 800 tEURO"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,11 +51,12 @@ export const ApproveNotification = ({
|
|||||||
intent={intent}
|
intent={intent}
|
||||||
testId="approve-default"
|
testId="approve-default"
|
||||||
message={t(
|
message={t(
|
||||||
`Before you can make a deposit of your chosen asset, ${selectedAsset?.symbol}, you need to approve its use in your Ethereum wallet`
|
'Before you can make a deposit of your chosen asset, %s, you need to approve its use in your Ethereum wallet',
|
||||||
|
selectedAsset?.symbol
|
||||||
)}
|
)}
|
||||||
buttonProps={{
|
buttonProps={{
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
text: `Approve ${selectedAsset?.symbol}`,
|
text: t('Approve %s', selectedAsset?.symbol),
|
||||||
action: onApprove,
|
action: onApprove,
|
||||||
dataTestId: 'approve-submit',
|
dataTestId: 'approve-submit',
|
||||||
}}
|
}}
|
||||||
@ -68,13 +69,12 @@ export const ApproveNotification = ({
|
|||||||
intent={intent}
|
intent={intent}
|
||||||
testId="reapprove-default"
|
testId="reapprove-default"
|
||||||
message={t(
|
message={t(
|
||||||
`Approve again to deposit more than ${formatNumber(
|
'Approve again to deposit more than %s',
|
||||||
balances.allowance.toString()
|
formatNumber(balances.allowance.toString())
|
||||||
)}`
|
|
||||||
)}
|
)}
|
||||||
buttonProps={{
|
buttonProps={{
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
text: `Approve ${selectedAsset?.symbol}`,
|
text: t('Approve %s', selectedAsset?.symbol),
|
||||||
action: onApprove,
|
action: onApprove,
|
||||||
dataTestId: 'reapprove-submit',
|
dataTestId: 'reapprove-submit',
|
||||||
}}
|
}}
|
||||||
@ -157,7 +157,8 @@ const ApprovalTxFeedback = ({
|
|||||||
intent={Intent.Warning}
|
intent={Intent.Warning}
|
||||||
testId="approve-requested"
|
testId="approve-requested"
|
||||||
message={t(
|
message={t(
|
||||||
`Go to your Ethereum wallet and approve the transaction to enable the use of ${selectedAsset?.symbol}`
|
'Go to your Ethereum wallet and approve the transaction to enable the use of %s',
|
||||||
|
selectedAsset?.symbol
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -174,7 +175,8 @@ const ApprovalTxFeedback = ({
|
|||||||
<>
|
<>
|
||||||
<p>
|
<p>
|
||||||
{t(
|
{t(
|
||||||
`Your ${selectedAsset?.symbol} approval is being confirmed by the Ethereum network. When this is complete, you can continue your deposit`
|
'Your %s approval is being confirmed by the Ethereum network. When this is complete, you can continue your deposit',
|
||||||
|
selectedAsset?.symbol
|
||||||
)}{' '}
|
)}{' '}
|
||||||
</p>
|
</p>
|
||||||
{txLink && <p>{txLink}</p>}
|
{txLink && <p>{txLink}</p>}
|
||||||
@ -194,13 +196,10 @@ const ApprovalTxFeedback = ({
|
|||||||
message={
|
message={
|
||||||
<>
|
<>
|
||||||
<p>
|
<p>
|
||||||
{t(
|
{t('You approved deposits of up to %s %s.', [
|
||||||
`You can now make deposits in ${
|
selectedAsset?.symbol,
|
||||||
selectedAsset?.symbol
|
formatNumber(allowance?.toString() || 0),
|
||||||
}, up to a maximum of ${formatNumber(
|
])}
|
||||||
allowance?.toString() || 0
|
|
||||||
)}`
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
{txLink && <p>{txLink}</p>}
|
{txLink && <p>{txLink}</p>}
|
||||||
</>
|
</>
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { waitFor, fireEvent, render, screen } from '@testing-library/react';
|
import {
|
||||||
|
waitFor,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
act,
|
||||||
|
} from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import type { DepositFormProps } from './deposit-form';
|
import type { DepositFormProps } from './deposit-form';
|
||||||
import { DepositForm } from './deposit-form';
|
import { DepositForm } from './deposit-form';
|
||||||
@ -141,12 +148,14 @@ describe('Deposit form', () => {
|
|||||||
fireEvent.submit(screen.getByTestId('deposit-form'));
|
fireEvent.submit(screen.getByTestId('deposit-form'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText('Insufficient amount in Ethereum wallet')
|
await screen.findByText(
|
||||||
|
"You can't deposit more than you have in your Ethereum wallet, 5"
|
||||||
|
)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails when submitted amount is more than the maximum limit', async () => {
|
it('fails when submitted amount is more than the maximum limit', async () => {
|
||||||
render(<DepositForm {...props} />);
|
render(<DepositForm {...props} selectedAsset={asset} />);
|
||||||
|
|
||||||
const amountMoreThanLimit = '21';
|
const amountMoreThanLimit = '21';
|
||||||
fireEvent.change(screen.getByLabelText('Amount'), {
|
fireEvent.change(screen.getByLabelText('Amount'), {
|
||||||
@ -155,7 +164,9 @@ describe('Deposit form', () => {
|
|||||||
fireEvent.submit(screen.getByTestId('deposit-form'));
|
fireEvent.submit(screen.getByTestId('deposit-form'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText('Amount is above lifetime deposit limit')
|
await screen.findByText(
|
||||||
|
"You can't deposit more than your remaining deposit allowance, 10 asset-symbol"
|
||||||
|
)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -179,7 +190,9 @@ describe('Deposit form', () => {
|
|||||||
fireEvent.submit(screen.getByTestId('deposit-form'));
|
fireEvent.submit(screen.getByTestId('deposit-form'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await screen.findByText('Amount is above approved amount')
|
await screen.findByText(
|
||||||
|
"You can't deposit more than your approved deposit amount, 30"
|
||||||
|
)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -283,8 +296,6 @@ describe('Deposit form', () => {
|
|||||||
expect(screen.getByTestId('BALANCE_AVAILABLE_value')).toHaveTextContent(
|
expect(screen.getByTestId('BALANCE_AVAILABLE_value')).toHaveTextContent(
|
||||||
'50'
|
'50'
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('MAX_LIMIT_value')).toHaveTextContent('20');
|
|
||||||
expect(screen.getByTestId('DEPOSITED_value')).toHaveTextContent('10');
|
|
||||||
expect(screen.getByTestId('REMAINING_value')).toHaveTextContent('10');
|
expect(screen.getByTestId('REMAINING_value')).toHaveTextContent('10');
|
||||||
|
|
||||||
fireEvent.change(screen.getByLabelText('Amount'), {
|
fireEvent.change(screen.getByLabelText('Amount'), {
|
||||||
@ -365,4 +376,32 @@ describe('Deposit form', () => {
|
|||||||
/this app only works on/i
|
/this app only works on/i
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Remaining deposit allowance tooltip should be rendered', async () => {
|
||||||
|
render(<DepositForm {...props} selectedAsset={asset} />);
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.hover(screen.getByText('Remaining deposit allowance'));
|
||||||
|
});
|
||||||
|
await waitFor(async () => {
|
||||||
|
await expect(
|
||||||
|
screen.getByRole('tooltip', {
|
||||||
|
name: /VEGA has a lifetime deposit limit of 20 asset-symbol per address/,
|
||||||
|
})
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ethereum deposit cap tooltip should be rendered', async () => {
|
||||||
|
render(<DepositForm {...props} selectedAsset={asset} />);
|
||||||
|
await act(async () => {
|
||||||
|
await userEvent.hover(screen.getByText('Ethereum deposit cap'));
|
||||||
|
});
|
||||||
|
await waitFor(async () => {
|
||||||
|
await expect(
|
||||||
|
screen.getByRole('tooltip', {
|
||||||
|
name: /The deposit cap is set when you approve an asset for use with this app/,
|
||||||
|
})
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
maxSafe,
|
maxSafe,
|
||||||
addDecimal,
|
addDecimal,
|
||||||
isAssetTypeERC20,
|
isAssetTypeERC20,
|
||||||
|
formatNumber,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
@ -133,11 +134,8 @@ export const DepositForm = ({
|
|||||||
return _pubKeys ? _pubKeys.map((pk) => pk.publicKey) : [];
|
return _pubKeys ? _pubKeys.map((pk) => pk.publicKey) : [];
|
||||||
}, [_pubKeys]);
|
}, [_pubKeys]);
|
||||||
|
|
||||||
const approved = balances
|
const approved =
|
||||||
? balances.allowance.isGreaterThan(0)
|
balances && balances.allowance.isGreaterThan(0) ? true : false;
|
||||||
? true
|
|
||||||
: false
|
|
||||||
: false;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
@ -194,6 +192,44 @@ export const DepositForm = ({
|
|||||||
<InputError intent="danger">{errors.from.message}</InputError>
|
<InputError intent="danger">{errors.from.message}</InputError>
|
||||||
)}
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
<FormGroup label={t('To (Vega key)')} labelFor="to">
|
||||||
|
<AddressField
|
||||||
|
pubKeys={pubKeys}
|
||||||
|
onChange={() => setValue('to', '')}
|
||||||
|
select={
|
||||||
|
<Select {...register('to')} id="to" defaultValue="">
|
||||||
|
<option value="" disabled>
|
||||||
|
{t('Please select')}
|
||||||
|
</option>
|
||||||
|
{pubKeys?.length &&
|
||||||
|
pubKeys.map((pk) => (
|
||||||
|
<option key={pk} value={pk}>
|
||||||
|
{pk}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
}
|
||||||
|
input={
|
||||||
|
<Input
|
||||||
|
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||||
|
autoFocus={true} // focus input immediately after is shown
|
||||||
|
id="to"
|
||||||
|
type="text"
|
||||||
|
{...register('to', {
|
||||||
|
validate: {
|
||||||
|
required,
|
||||||
|
vegaPublicKey,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{errors.to?.message && (
|
||||||
|
<InputError intent="danger" forInput="to">
|
||||||
|
{errors.to.message}
|
||||||
|
</InputError>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
<FormGroup label={t('Asset')} labelFor="asset">
|
<FormGroup label={t('Asset')} labelFor="asset">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@ -250,45 +286,7 @@ export const DepositForm = ({
|
|||||||
selectedAsset={selectedAsset}
|
selectedAsset={selectedAsset}
|
||||||
faucetTxId={faucetTxId}
|
faucetTxId={faucetTxId}
|
||||||
/>
|
/>
|
||||||
<FormGroup label={t('To (Vega key)')} labelFor="to">
|
{approved && selectedAsset && balances && (
|
||||||
<AddressField
|
|
||||||
pubKeys={pubKeys}
|
|
||||||
onChange={() => setValue('to', '')}
|
|
||||||
select={
|
|
||||||
<Select {...register('to')} id="to" defaultValue="">
|
|
||||||
<option value="" disabled={true}>
|
|
||||||
{t('Please select')}
|
|
||||||
</option>
|
|
||||||
{pubKeys?.length &&
|
|
||||||
pubKeys.map((pk) => (
|
|
||||||
<option key={pk} value={pk}>
|
|
||||||
{pk}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
}
|
|
||||||
input={
|
|
||||||
<Input
|
|
||||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
||||||
autoFocus={true} // focus input immediately after is shown
|
|
||||||
id="to"
|
|
||||||
type="text"
|
|
||||||
{...register('to', {
|
|
||||||
validate: {
|
|
||||||
required,
|
|
||||||
vegaPublicKey,
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{errors.to?.message && (
|
|
||||||
<InputError intent="danger" forInput="to">
|
|
||||||
{errors.to.message}
|
|
||||||
</InputError>
|
|
||||||
)}
|
|
||||||
</FormGroup>
|
|
||||||
{selectedAsset && balances && (
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<DepositLimits {...balances} asset={selectedAsset} />
|
<DepositLimits {...balances} asset={selectedAsset} />
|
||||||
</div>
|
</div>
|
||||||
@ -305,8 +303,15 @@ export const DepositForm = ({
|
|||||||
minSafe: (value) => minSafe(new BigNumber(min))(value),
|
minSafe: (value) => minSafe(new BigNumber(min))(value),
|
||||||
approved: (v) => {
|
approved: (v) => {
|
||||||
const value = new BigNumber(v);
|
const value = new BigNumber(v);
|
||||||
if (value.isGreaterThan(balances?.allowance || 0)) {
|
const allowance = new BigNumber(balances?.allowance || 0);
|
||||||
return t('Amount is above approved amount');
|
if (value.isGreaterThan(allowance)) {
|
||||||
|
return t(
|
||||||
|
"You can't deposit more than your approved deposit amount, %s %s",
|
||||||
|
[
|
||||||
|
formatNumber(allowance.toString()),
|
||||||
|
selectedAsset?.symbol || ' ',
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -322,14 +327,24 @@ export const DepositForm = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value.isGreaterThan(lifetimeLimit)) {
|
if (value.isGreaterThan(lifetimeLimit)) {
|
||||||
return t('Amount is above lifetime deposit limit');
|
return t(
|
||||||
|
"You can't deposit more than your remaining deposit allowance, %s %s",
|
||||||
|
[
|
||||||
|
formatNumber(lifetimeLimit.toString()),
|
||||||
|
selectedAsset?.symbol || ' ',
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
balance: (v) => {
|
balance: (v) => {
|
||||||
const value = new BigNumber(v);
|
const value = new BigNumber(v);
|
||||||
if (value.isGreaterThan(balances?.balance || 0)) {
|
const balance = new BigNumber(balances?.balance || 0);
|
||||||
return t('Insufficient amount in Ethereum wallet');
|
if (value.isGreaterThan(balance)) {
|
||||||
|
return t(
|
||||||
|
"You can't deposit more than you have in your Ethereum wallet, %s %s",
|
||||||
|
[formatNumber(balance), selectedAsset?.symbol || ' ']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@ -405,7 +420,7 @@ const FormButton = ({ approved, selectedAsset }: FormButtonProps) => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
data-testid="deposit-submit"
|
data-testid="deposit-submit"
|
||||||
variant={isActive ? 'primary' : 'default'}
|
variant={isActive ? 'primary' : 'default'}
|
||||||
fill={true}
|
fill
|
||||||
disabled={invalidChain}
|
disabled={invalidChain}
|
||||||
>
|
>
|
||||||
{t('Deposit')}
|
{t('Deposit')}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import type { Asset } from '@vegaprotocol/assets';
|
import type { Asset } from '@vegaprotocol/assets';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
||||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
import {
|
||||||
|
KeyValueTable,
|
||||||
|
KeyValueTableRow,
|
||||||
|
Tooltip,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import type BigNumber from 'bignumber.js';
|
import type BigNumber from 'bignumber.js';
|
||||||
|
import { formatNumber } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
// Note: all of the values here are with correct asset's decimals
|
// Note: all of the values here are with correct asset's decimals
|
||||||
// See `libs/deposits/src/lib/use-deposit-balances.ts`
|
// See `libs/deposits/src/lib/use-deposit-balances.ts`
|
||||||
@ -33,21 +38,35 @@ export const DepositLimits = ({
|
|||||||
'-'
|
'-'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: 'MAX_LIMIT',
|
|
||||||
label: t('Lifetime deposit allowance'),
|
|
||||||
rawValue: max,
|
|
||||||
value: <CompactNumber number={max} decimals={asset.decimals} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'DEPOSITED',
|
|
||||||
label: t('Deposited'),
|
|
||||||
rawValue: deposited,
|
|
||||||
value: <CompactNumber number={deposited} decimals={asset.decimals} />,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: 'REMAINING',
|
key: 'REMAINING',
|
||||||
label: t('Remaining'),
|
label: (
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{t(
|
||||||
|
'VEGA has a lifetime deposit limit of %s %s per address. This can be changed through governance',
|
||||||
|
[formatNumber(max.toString()), asset.symbol]
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t(
|
||||||
|
'To date, %s %s has been deposited from this Ethereum address, so you can deposit up to %s %s more.',
|
||||||
|
[
|
||||||
|
formatNumber(deposited.toString()),
|
||||||
|
asset.symbol,
|
||||||
|
formatNumber(max.minus(deposited).toString()),
|
||||||
|
asset.symbol,
|
||||||
|
]
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button type="button">{t('Remaining deposit allowance')}</button>
|
||||||
|
</Tooltip>
|
||||||
|
),
|
||||||
rawValue: max.minus(deposited),
|
rawValue: max.minus(deposited),
|
||||||
value: (
|
value: (
|
||||||
<CompactNumber
|
<CompactNumber
|
||||||
@ -58,7 +77,20 @@ export const DepositLimits = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ALLOWANCE',
|
key: 'ALLOWANCE',
|
||||||
label: t('Approved'),
|
label: (
|
||||||
|
<Tooltip
|
||||||
|
description={
|
||||||
|
<p>
|
||||||
|
{t(
|
||||||
|
'The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve %s again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.',
|
||||||
|
asset.symbol
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button type="button">{t('Ethereum deposit cap')}</button>
|
||||||
|
</Tooltip>
|
||||||
|
),
|
||||||
rawValue: allowance,
|
rawValue: allowance,
|
||||||
value: allowance ? (
|
value: allowance ? (
|
||||||
<CompactNumber number={allowance} decimals={asset.decimals} />
|
<CompactNumber number={allowance} decimals={asset.decimals} />
|
||||||
|
@ -1 +1,4 @@
|
|||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
|
|
||||||
|
global.ResizeObserver = ResizeObserver;
|
||||||
|
Loading…
Reference in New Issue
Block a user