Skip to content

Commit

Permalink
george / rm65033 / ts migration of withdraw UI components (binary-com…
Browse files Browse the repository at this point in the history
…#6343)

* perf(cashier withdraw): convert withdrawal-verification-email to TS

* perf: add data-testid for checklist action

* perf(cashier withdraw): convert withdrawal-locked to TS

* perf(types): add types for TClinetStore

* perf(cashier withdraw): convert withdraw to TS

* perf(cashier withdraw): convert crypto-withdraw-receipt to TS

* perf(cashier withdraw): convert crypto-withdraw-form to TS

* perf(cashier withdraw): convert withdrawal to TS

* refactor(cashier withdraw): refactor array syntax (ts)

* test(cashier withdraw): remove eslint disables for tests
  • Loading branch information
heorhi-deriv committed Sep 6, 2022
1 parent 3e5a76d commit 773d105
Show file tree
Hide file tree
Showing 27 changed files with 335 additions and 275 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const CryptoFiatConverter = ({
required
hint={hint}
classNameHint='crypto-fiat-converter__hint'
data-testid='dt_converter_from_amount_input'
/>
)}
</Field>
Expand Down Expand Up @@ -149,6 +150,7 @@ const CryptoFiatConverter = ({
autoComplete='off'
hint={localize('Approximate value')}
classNameHint='crypto-fiat-converter__hint'
data-testid='dt_converter_to_amount_input'
/>
{is_timer_visible && (
<Timer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { TReactMouseEvent } from 'Types';
type TPercentageSelectorProps = {
amount: number;
currency: string;
from_account: string;
from_account?: string;
getCalculatedAmount: (amount: string) => void;
percentage: number;
should_percentage_reset: boolean;
to_account: string;
to_account?: string;
};

type TCalculateAmountInputEvent = { target: { id: number } };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ jest.mock('Stores/connect.js', () => ({
connect: () => Component => Component,
}));

jest.mock('Components/cashier-locked', () => () => <div>CashierLocked</div>);
jest.mock('Components/cashier-container/virtual', () => () => <div>Virtual</div>);
jest.mock('../withdrawal-locked', () => () => <div>WithdrawalLocked</div>);
jest.mock('Components/no-balance', () => () => <div>NoBalance</div>);
jest.mock('Components/error', () => () => <div>Error</div>);
jest.mock('../withdraw', () => () => <div>Withdraw</div>);
jest.mock('../crypto-withdraw-form', () => () => <div>CryptoWithdrawForm</div>);
jest.mock('../crypto-withdraw-receipt', () => () => <div>CryptoWithdrawReceipt</div>);
jest.mock('Components/crypto-transactions-history', () => () => <div>CryptoTransactionsHistory</div>);
jest.mock('../withdrawal-verification-email', () => () => <div>WithdrawalVerificationEmail</div>);
jest.mock('Components/recent-transaction', () => () => <div>RecentTransaction</div>);
jest.mock('Components/cashier-locked', () => jest.fn(() => 'CashierLocked'));
jest.mock('Components/cashier-container/virtual', () => jest.fn(() => 'Virtual'));
jest.mock('../withdrawal-locked', () => jest.fn(() => 'WithdrawalLocked'));
jest.mock('Components/no-balance', () => jest.fn(() => 'NoBalance'));
jest.mock('Components/error', () => jest.fn(() => 'Error'));
jest.mock('../withdraw', () => jest.fn(() => 'Withdraw'));
jest.mock('../crypto-withdraw-form', () => jest.fn(() => 'CryptoWithdrawForm'));
jest.mock('../crypto-withdraw-receipt', () => jest.fn(() => 'CryptoWithdrawReceipt'));
jest.mock('Components/crypto-transactions-history', () => jest.fn(() => 'CryptoTransactionsHistory'));
jest.mock('../withdrawal-verification-email', () => jest.fn(() => 'WithdrawalVerificationEmail'));
jest.mock('Components/recent-transaction', () => jest.fn(() => 'RecentTransaction'));
jest.mock('@deriv/components', () => ({
...jest.requireActual('@deriv/components'),
Loading: () => <div>Loading</div>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import CryptoWithdrawForm from '../crypto-withdraw-form.jsx';
import CryptoWithdrawForm from '../crypto-withdraw-form';

jest.mock('Stores/connect.js', () => ({
__esModule: true,
Expand All @@ -10,7 +10,7 @@ jest.mock('Stores/connect.js', () => ({
}));

describe('<CryptoWithdrawForm />', () => {
const mockProps = () => ({
const props = {
account_platform_icon: 'icon',
blockchain_address: 'tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt',
currency: 'BTC',
Expand All @@ -23,22 +23,19 @@ describe('<CryptoWithdrawForm />', () => {
requestWithdraw: jest.fn(),
setBlockchainAddress: jest.fn(),
setWithdrawPercentageSelectorResult: jest.fn(),
});
};

it('component and header should be rendered', () => {
const props = mockProps();
const { container } = render(<CryptoWithdrawForm {...props} />);
render(<CryptoWithdrawForm {...props} />);

expect(screen.getByText('Your BTC wallet address')).toBeInTheDocument();
expect(container.querySelector('.cashier__wrapper')).toBeInTheDocument();
expect(container.querySelector('.cashier__content-header')).toBeInTheDocument();
expect(screen.getByTestId('dt_crypto_withdraw_form')).toBeInTheDocument();
});

it('should show a proper error if address is not provided', async () => {
const props = mockProps();
const { container } = render(<CryptoWithdrawForm {...props} />);
render(<CryptoWithdrawForm {...props} />);

const address_field = container.querySelector('input[name=address]');
const address_field = screen.getByTestId('dt_address_input');

act(() => {
fireEvent.change(address_field, { target: { value: '1' } });
Expand All @@ -52,10 +49,9 @@ describe('<CryptoWithdrawForm />', () => {
});

it('should show a proper error if provided address has less characters than needed', async () => {
const props = mockProps();
const { container } = render(<CryptoWithdrawForm {...props} />);
render(<CryptoWithdrawForm {...props} />);

const address_field = container.querySelector('input[name=address]');
const address_field = screen.getByTestId('dt_address_input');

act(() => {
fireEvent.change(address_field, { target: { value: 'address less than 25' } });
Expand All @@ -66,10 +62,9 @@ describe('<CryptoWithdrawForm />', () => {
});

it('should show a proper error if provided address has more characters than needed', async () => {
const props = mockProps();
const { container } = render(<CryptoWithdrawForm {...props} />);
render(<CryptoWithdrawForm {...props} />);

const address_field = container.querySelector('input[name=address]');
const address_field = screen.getByTestId('dt_address_input');

act(() => {
fireEvent.change(address_field, {
Expand All @@ -82,11 +77,10 @@ describe('<CryptoWithdrawForm />', () => {
});

it("requestWithdraw func should be called if value provided from 'converter_from_amount' input and withdraw button is clicked", async () => {
const props = mockProps();
const { container } = render(<CryptoWithdrawForm {...props} />);
render(<CryptoWithdrawForm {...props} />);

const address_field = container.querySelector('input[name=address]');
const converter_from_amount_field = container.querySelector('input[name=converter_from_amount]');
const address_field = screen.getByTestId('dt_address_input');
const converter_from_amount_field = screen.getByTestId('dt_converter_from_amount_input');
const withdraw_button = screen.getByText('Withdraw');

act(() => {
Expand All @@ -103,11 +97,10 @@ describe('<CryptoWithdrawForm />', () => {
});

it("requestWithdraw func should be called if value provided from 'converter_to_amount' input and withdraw button is clicked", async () => {
const props = mockProps();
const { container } = render(<CryptoWithdrawForm {...props} />);
render(<CryptoWithdrawForm {...props} />);

const address_field = container.querySelector('input[name=address]');
const converter_to_amount_field = container.querySelector('input[name=converter_to_amount]');
const address_field = screen.getByTestId('dt_address_input');
const converter_to_amount_field = screen.getByTestId('dt_converter_to_amount_input');
const withdraw_button = screen.getByText('Withdraw');

act(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,64 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import { Field, FieldProps, Formik, FormikProps } from 'formik';
import { Button, Icon, Input, Loading, MobileWrapper, Text } from '@deriv/components';
import { CryptoConfig, getCurrencyName, isCryptocurrency, isMobile } from '@deriv/shared';
import { localize, Localize } from '@deriv/translations';
import { Field, Formik } from 'formik';
import { connect } from 'Stores/connect';
import RecentTransaction from 'Components/recent-transaction';
import CryptoFiatConverter from 'Components/crypto-fiat-converter';
import PercentageSelector from 'Components/percentage-selector';
import RecentTransaction from 'Components/recent-transaction';
import { connect } from 'Stores/connect';
import { TClientStore, TCryptoTransactionDetails, TReactChangeEvent, TRootStore } from 'Types';
import './crypto-withdraw-form.scss';

type THeaderProps = {
currency: string;
};

type TCryptoWithdrawFormProps = {
account_platform_icon: string;
balance: TClientStore['balance'];
blockchain_address: string;
crypto_currency: TClientStore['currency'];
crypto_transactions: TCryptoTransactionDetails[];
converter_to_error: string;
converter_from_error: string;
currency: TClientStore['currency'];
current_fiat_currency: TClientStore['current_fiat_currency'];
is_loading: boolean;
percentage: number;
should_percentage_reset: boolean;
verification_code: TClientStore['verification_code']['payment_withdraw'];
onChangeConverterFromAmount: (
e: React.ChangeEvent<HTMLInputElement>,
from_currency: string,
to_currency: string
) => void;
onChangeConverterToAmount: (
e: React.ChangeEvent<HTMLInputElement>,
from_currency: string,
to_currency: string
) => void;
onMountWithdraw: (verification_code: string) => void;
percentageSelectorSelectionStatus: (should_percentage_reset: boolean) => void;
recentTransactionOnMount: () => void;
requestWithdraw: (verification_code: string) => void;
resetConverter: () => void;
setBlockchainAddress: (address: string) => void;
setWithdrawPercentageSelectorResult: (amount: string) => void;
validateWithdrawFromAmount: () => void;
validateWithdrawToAmount: () => void;
};

type TFormValues = {
address: string;
};

const MIN_ADDRESS_LENGTH = 25;
const MAX_ADDRESS_LENGTH = 64;
const DEFAULT_FIAT_CURRENCY = 'USD';

const Header = ({ currency }) => {
const Header = ({ currency }: THeaderProps) => {
const currency_name = getCurrencyName(currency);
const currency_display_code = CryptoConfig.get()[currency].display_code;

Expand Down Expand Up @@ -63,7 +106,7 @@ const CryptoWithdrawForm = ({
validateWithdrawFromAmount,
validateWithdrawToAmount,
verification_code,
}) => {
}: TCryptoWithdrawFormProps) => {
React.useEffect(() => {
recentTransactionOnMount();
}, [recentTransactionOnMount]);
Expand All @@ -74,7 +117,7 @@ const CryptoWithdrawForm = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const validateAddress = address => {
const validateAddress = (address: string): string | undefined => {
if (!address) return localize('This field is required.');

if (address.length < MIN_ADDRESS_LENGTH || address.length > MAX_ADDRESS_LENGTH) {
Expand All @@ -87,7 +130,7 @@ const CryptoWithdrawForm = ({
if (is_loading) return <Loading />;

return (
<div className='cashier__wrapper'>
<div className='cashier__wrapper' data-testid='dt_crypto_withdraw_form'>
{!isMobile() && <Header currency={currency} />}
<div className={classNames({ 'crypto-withdraw-form__icon': isMobile() })}>
<Icon icon={`IcCurrency-${account_platform_icon.toLowerCase()}`} size={isMobile() ? 64 : 128} />
Expand All @@ -97,19 +140,29 @@ const CryptoWithdrawForm = ({
initialValues={{
address: '',
}}
onSubmit={() => requestWithdraw(verification_code)}
>
{({ errors, isSubmitting, touched, setFieldTouched, handleChange, values }) => (
<div className='crypto-withdraw-form'>
{({
errors,
isSubmitting,
touched,
setFieldTouched,
handleChange,
handleSubmit,
values,
}: FormikProps<TFormValues>) => (
<form className='crypto-withdraw-form' onSubmit={handleSubmit} autoComplete='off'>
<Field name='address' validate={validateAddress}>
{({ field }) => (
{({ field }: FieldProps<string, TFormValues>) => (
<Input
{...field}
onChange={e => {
onChange={(e: TReactChangeEvent) => {
handleChange(e);
setBlockchainAddress(e.target.value);
setFieldTouched('address', true, false);
}}
className='cashier__input withdraw__input'
data-testid='dt_address_input'
type='text'
label={
<Localize
Expand All @@ -128,7 +181,7 @@ const CryptoWithdrawForm = ({
<div>
<div className='crypto-withdraw-form__percentage-selector'>
<PercentageSelector
amount={+balance}
amount={Number(balance)}
currency={currency}
getCalculatedAmount={setWithdrawPercentageSelectorResult}
percentage={percentage}
Expand Down Expand Up @@ -157,13 +210,12 @@ const CryptoWithdrawForm = ({
type='submit'
primary
large
onClick={() => requestWithdraw(verification_code)}
>
<Localize i18n_default_text='Withdraw' />
</Button>
</div>
</div>
</div>
</form>
)}
</Formik>
<MobileWrapper>
Expand All @@ -173,34 +225,7 @@ const CryptoWithdrawForm = ({
);
};

CryptoWithdrawForm.propTypes = {
account_platform_icon: PropTypes.string,
balance: PropTypes.number,
blockchain_address: PropTypes.string,
converter_from_error: PropTypes.string,
converter_to_error: PropTypes.string,
crypto_currency: PropTypes.string,
crypto_transactions: PropTypes.array,
currency: PropTypes.string,
current_fiat_currency: PropTypes.string,
is_loading: PropTypes.bool,
onChangeConverterFromAmount: PropTypes.func,
onChangeConverterToAmount: PropTypes.func,
onMountWithdraw: PropTypes.func,
percentage: PropTypes.number,
percentageSelectorSelectionStatus: PropTypes.func,
recentTransactionOnMount: PropTypes.func,
requestWithdraw: PropTypes.func,
resetConverter: PropTypes.func,
setBlockchainAddress: PropTypes.func,
setWithdrawPercentageSelectorResult: PropTypes.func,
should_percentage_reset: PropTypes.bool,
validateWithdrawFromAmount: PropTypes.func,
validateWithdrawToAmount: PropTypes.func,
verification_code: PropTypes.string,
};

export default connect(({ client, modules }) => ({
export default connect(({ client, modules }: TRootStore) => ({
account_platform_icon: modules.cashier.withdraw.account_platform_icon,
balance: client.balance,
blockchain_address: modules.cashier.withdraw.blockchain_address,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import CryptoWithdrawForm from './crypto-withdraw-form';

export default CryptoWithdrawForm;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import CryptoWithdrawReceipt from '../crypto-withdraw-receipt.jsx';
import CryptoWithdrawReceipt from '../crypto-withdraw-receipt';

jest.mock('Stores/connect.js', () => ({
__esModule: true,
Expand Down
Loading

0 comments on commit 773d105

Please sign in to comment.