chore(deposits): impove UX on deposit dialog (#3401)

This commit is contained in:
Maciek 2023-04-10 13:59:02 +02:00 committed by GitHub
parent 0d9bd67465
commit ecd362615e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 179 additions and 88 deletions

View File

@ -87,7 +87,10 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
.clear()
.type('850')
.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"
);
});
});

View File

@ -51,11 +51,12 @@ export const ApproveNotification = ({
intent={intent}
testId="approve-default"
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={{
size: 'sm',
text: `Approve ${selectedAsset?.symbol}`,
text: t('Approve %s', selectedAsset?.symbol),
action: onApprove,
dataTestId: 'approve-submit',
}}
@ -68,13 +69,12 @@ export const ApproveNotification = ({
intent={intent}
testId="reapprove-default"
message={t(
`Approve again to deposit more than ${formatNumber(
balances.allowance.toString()
)}`
'Approve again to deposit more than %s',
formatNumber(balances.allowance.toString())
)}
buttonProps={{
size: 'sm',
text: `Approve ${selectedAsset?.symbol}`,
text: t('Approve %s', selectedAsset?.symbol),
action: onApprove,
dataTestId: 'reapprove-submit',
}}
@ -157,7 +157,8 @@ const ApprovalTxFeedback = ({
intent={Intent.Warning}
testId="approve-requested"
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>
@ -174,7 +175,8 @@ const ApprovalTxFeedback = ({
<>
<p>
{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>
{txLink && <p>{txLink}</p>}
@ -194,13 +196,10 @@ const ApprovalTxFeedback = ({
message={
<>
<p>
{t(
`You can now make deposits in ${
selectedAsset?.symbol
}, up to a maximum of ${formatNumber(
allowance?.toString() || 0
)}`
)}
{t('You approved deposits of up to %s %s.', [
selectedAsset?.symbol,
formatNumber(allowance?.toString() || 0),
])}
</p>
{txLink && <p>{txLink}</p>}
</>

View File

@ -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 type { DepositFormProps } from './deposit-form';
import { DepositForm } from './deposit-form';
@ -141,12 +148,14 @@ describe('Deposit form', () => {
fireEvent.submit(screen.getByTestId('deposit-form'));
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();
});
it('fails when submitted amount is more than the maximum limit', async () => {
render(<DepositForm {...props} />);
render(<DepositForm {...props} selectedAsset={asset} />);
const amountMoreThanLimit = '21';
fireEvent.change(screen.getByLabelText('Amount'), {
@ -155,7 +164,9 @@ describe('Deposit form', () => {
fireEvent.submit(screen.getByTestId('deposit-form'));
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();
});
@ -179,7 +190,9 @@ describe('Deposit form', () => {
fireEvent.submit(screen.getByTestId('deposit-form'));
expect(
await screen.findByText('Amount is above approved amount')
await screen.findByText(
"You can't deposit more than your approved deposit amount, 30"
)
).toBeInTheDocument();
});
@ -283,8 +296,6 @@ describe('Deposit form', () => {
expect(screen.getByTestId('BALANCE_AVAILABLE_value')).toHaveTextContent(
'50'
);
expect(screen.getByTestId('MAX_LIMIT_value')).toHaveTextContent('20');
expect(screen.getByTestId('DEPOSITED_value')).toHaveTextContent('10');
expect(screen.getByTestId('REMAINING_value')).toHaveTextContent('10');
fireEvent.change(screen.getByLabelText('Amount'), {
@ -365,4 +376,32 @@ describe('Deposit form', () => {
/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();
});
});
});

View File

@ -8,6 +8,7 @@ import {
maxSafe,
addDecimal,
isAssetTypeERC20,
formatNumber,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
@ -133,11 +134,8 @@ export const DepositForm = ({
return _pubKeys ? _pubKeys.map((pk) => pk.publicKey) : [];
}, [_pubKeys]);
const approved = balances
? balances.allowance.isGreaterThan(0)
? true
: false
: false;
const approved =
balances && balances.allowance.isGreaterThan(0) ? true : false;
return (
<form
@ -194,6 +192,44 @@ export const DepositForm = ({
<InputError intent="danger">{errors.from.message}</InputError>
)}
</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">
<Controller
control={control}
@ -250,45 +286,7 @@ export const DepositForm = ({
selectedAsset={selectedAsset}
faucetTxId={faucetTxId}
/>
<FormGroup label={t('To (Vega key)')} labelFor="to">
<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 && (
{approved && selectedAsset && balances && (
<div className="mb-6">
<DepositLimits {...balances} asset={selectedAsset} />
</div>
@ -305,8 +303,15 @@ export const DepositForm = ({
minSafe: (value) => minSafe(new BigNumber(min))(value),
approved: (v) => {
const value = new BigNumber(v);
if (value.isGreaterThan(balances?.allowance || 0)) {
return t('Amount is above approved amount');
const allowance = new BigNumber(balances?.allowance || 0);
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;
},
@ -322,14 +327,24 @@ export const DepositForm = ({
}
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;
},
balance: (v) => {
const value = new BigNumber(v);
if (value.isGreaterThan(balances?.balance || 0)) {
return t('Insufficient amount in Ethereum wallet');
const balance = new BigNumber(balances?.balance || 0);
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;
},
@ -405,7 +420,7 @@ const FormButton = ({ approved, selectedAsset }: FormButtonProps) => {
type="submit"
data-testid="deposit-submit"
variant={isActive ? 'primary' : 'default'}
fill={true}
fill
disabled={invalidChain}
>
{t('Deposit')}

View File

@ -1,8 +1,13 @@
import type { Asset } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
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 { formatNumber } from '@vegaprotocol/utils';
// Note: all of the values here are with correct asset's decimals
// 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',
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),
value: (
<CompactNumber
@ -58,7 +77,20 @@ export const DepositLimits = ({
},
{
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,
value: allowance ? (
<CompactNumber number={allowance} decimals={asset.decimals} />

View File

@ -1 +1,4 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;