Skip to content

Commit

Permalink
Aum/wall 228/crypto cashier changes for third party processor coinspa…
Browse files Browse the repository at this point in the history
…id (binary-com#8343)

* feat: Tooltip for crypto transactions processed by third-party payement processors

* feat: Added popup note for mobile view for third-party payment tooltip

* feat: added alert banner component in cashier package

* feat: added IcAlertWarningDark icon

* chore: working on test cases 1

* chore: reseting testcase to upstream/master

* chore: added test case for tooltip in desktop mode

* feat: AlertBanner for cryptoc with minimum deposit using third-party processor (CoinsPaid)

* feat: changed styles for crypto-deposit.tsx

* chore: added data-testid for tooltip in mobile view

* chore: added test cases for crypto-deposit.tsx

* chore: added test cases for crypto-deposit.tsx

* fix: fixed the test cases for crypto-deposit.tsx

* chore: made changes based on comments in PR

* chore: fixing changes suggested by @heorhi-deriv

* chore: updated alert-banner.tsx

* refactor: changed the way localization is handled by alert-banner.tsx

* chore: minor fixes

* chore: removed unused classnames

* chore: add test cases and made changes suggested by @nijil-deriv

* chore: made changes for the comments in PR

* Update packages/cashier/src/components/alert-banner/__tests__/alert-banner.spec.tsx

Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com>

---------

Co-authored-by: Farzin Mirzaie <72082844+farzin-deriv@users.noreply.github.com>
  • Loading branch information
2 people authored and nijil-deriv committed May 24, 2023
1 parent 23ac3e7 commit 250bae7
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import AlertBanner from '../alert-banner';

describe('<AlertBanner />', () => {
it('should render the icon and message props passed for the banner', () => {
const props: React.ComponentProps<typeof AlertBanner> = {
icon: 'IcAlertWarningDark',
message: 'Alert banner test message.',
};
render(<AlertBanner {...props} />);
const dt_alert_banner_icon = screen.getByTestId('dt_alert_banner_icon');
expect(dt_alert_banner_icon).toBeInTheDocument();
expect(screen.getByText(props.message)).toBeInTheDocument();
});
});
13 changes: 13 additions & 0 deletions packages/cashier/src/components/alert-banner/alert-banner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.alert-banner {
display: grid;
grid-template-columns: auto auto;
width: auto;
padding: 0.8rem;
margin: 2.4rem 0;
border-radius: $BORDER_RADIUS;
background-color: $alpha-color-yellow-1;

svg {
margin: 0.3rem 0.3rem 0 0;
}
}
23 changes: 23 additions & 0 deletions packages/cashier/src/components/alert-banner/alert-banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import classNames from 'classnames';
import { Icon, Text } from '@deriv/components';
import './alert-banner.scss';

type TAlertBanner = {
className?: string;
icon: string;
message: string;
};

const AlertBanner = ({ className, icon, message }: TAlertBanner) => {
return (
<div className={classNames('alert-banner', className)}>
<Icon size={16} icon={icon} data_testid='dt_alert_banner_icon' />
<Text as='p' align='center' size='xs'>
{message}
</Text>
</div>
);
};

export default AlertBanner;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { isMobile } from '@deriv/shared';
import CryptoTransactionsRenderer from '../crypto-transactions-renderer';
import CashierProviders from '../../../cashier-providers';
Expand Down Expand Up @@ -135,4 +136,68 @@ describe('<CryptoTransactionsRenderer />', () => {
mockRootStore.modules.cashier.transaction_history.showCryptoTransactionsCancelModal
).toHaveBeenCalledTimes(1);
});

it('should display popover when hovering on tooltip for third-party transactions (CoinsPaid)', () => {
(isMobile as jest.Mock).mockReturnValue(false);
const tooltip_props = {
row: {
address_hash: 'tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt',
address_url: 'https://explorer.coinspaid.com/CP:Abcd1234',
amount: 0.005,
id: '3',
is_valid_to_cancel: 1,
status_code: 'LOCKED',
status_message:
"We're reviewing your withdrawal request. You may still cancel this transaction if you wish. Once we start processing, you won't be able to cancel.",
submit_date: 1640603927,
transaction_type: 'withdrawal',
transaction_url: 'CP:Abcd1234',
},
};

render(<CryptoTransactionsRenderer {...tooltip_props} />, {
wrapper: ({ children }) => <CashierProviders store={mockRootStore}>{children}</CashierProviders>,
});

const crypto_transactions_history_table_tooltip = screen.getByTestId(
'dt_crypto_transactions_history_table_tooltip'
);

expect(crypto_transactions_history_table_tooltip).toBeInTheDocument();

userEvent.hover(crypto_transactions_history_table_tooltip);
expect(screen.getByText('The details of this transaction is available on CoinsPaid.')).toBeInTheDocument();
userEvent.unhover(crypto_transactions_history_table_tooltip);
});

it('should check whether the tooltip is clickable for third-party transactions (CoinsPaid)', () => {
(isMobile as jest.Mock).mockReturnValue(true);
const tooltip_props = {
row: {
address_hash: 'tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt',
address_url: 'https://explorer.coinspaid.com/CP:Abcd1234',
amount: 0.005,
id: '3',
is_valid_to_cancel: 1,
status_code: 'LOCKED',
status_message:
"We're reviewing your withdrawal request. You may still cancel this transaction if you wish. Once we start processing, you won't be able to cancel.",
submit_date: 1640603927,
transaction_type: 'withdrawal',
transaction_url: 'CP:Abcd1234',
},
onTooltipClick: jest.fn(),
};

render(<CryptoTransactionsRenderer {...tooltip_props} />, {
wrapper: ({ children }) => <CashierProviders store={mockRootStore}>{children}</CashierProviders>,
});

const crypto_transactions_history_table_tooltip_mobile = screen.getByTestId(
'dt_crypto_transactions_history_table_tooltip_mobile'
);

expect(crypto_transactions_history_table_tooltip_mobile).toBeInTheDocument();
fireEvent.click(crypto_transactions_history_table_tooltip_mobile);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@
justify-content: center;
}
}

&-tooltip {
margin-left: 0.4rem;
svg {
display: flex;
align-items: center;
}
}
}
&__data-list {
flex: 1;
Expand Down Expand Up @@ -186,4 +194,8 @@
position: static;
}
}

&-body {
font-size: var(--text-size-xxs);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { DataList, Icon, Loading, MobileWrapper, Table, Text } from '@deriv/components';
import { DataList, Icon, Loading, MobileWrapper, Modal, Table, Text } from '@deriv/components';
import { isDesktop, isMobile, routes } from '@deriv/shared';
import { localize, Localize } from '@deriv/translations';
import { useStore, observer } from '@deriv/stores';
Expand Down Expand Up @@ -29,6 +29,7 @@ const CryptoTransactionsHistory = observer(() => {
const { crypto_transactions, is_loading, setIsCryptoTransactionsVisible } = transaction_history;
const { setIsDeposit } = general_store;
const { currency } = client;
const [is_modal_visible, setIsModalVisible] = React.useState(false);

React.useEffect(() => {
return () => setIsCryptoTransactionsVisible(false);
Expand Down Expand Up @@ -79,7 +80,10 @@ const CryptoTransactionsHistory = observer(() => {
// TODO: CHECK THIS TYPE ERROR
data_source={crypto_transactions}
rowRenderer={(row_props: TCryptoTransactionDetailsRow) => (
<CryptoTransactionsRenderer {...row_props} />
<CryptoTransactionsRenderer
{...row_props}
onTooltipClick={() => setIsModalVisible(true)}
/>
)}
keyMapper={(row: TCryptoTransactionDetails) => row.id}
row_gap={isMobile() ? 8 : 0}
Expand All @@ -95,6 +99,19 @@ const CryptoTransactionsHistory = observer(() => {
</div>
)}
</div>
<Modal
has_close_icon
is_open={is_modal_visible}
title='Note'
toggleModal={() => setIsModalVisible(old => !old)}
width='44rem'
height='14rem'
className='crypto-transactions-history__modal'
>
<Modal.Body className='crypto-transactions-history__modal-body'>
{localize('The details of this transaction is available on CoinsPaid.')}
</Modal.Body>
</Modal>
</React.Fragment>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import { useCashierStore } from '../../stores/useCashierStores';

type TCryptoTransactionsRendererProps = {
row: TCryptoTransactionDetails;
onTooltipClick: VoidFunction;
};

const CryptoTransactionsRenderer = observer(({ row: crypto }: TCryptoTransactionsRendererProps) => {
const CryptoTransactionsRenderer = observer(({ row: crypto, onTooltipClick }: TCryptoTransactionsRendererProps) => {
const { client } = useStore();
const { transaction_history } = useCashierStore();
const { cancelCryptoTransaction, showCryptoTransactionsCancelModal, showCryptoTransactionsStatusModal } =
Expand Down Expand Up @@ -61,6 +62,8 @@ const CryptoTransactionsRenderer = observer(({ row: crypto }: TCryptoTransaction
showCryptoTransactionsStatusModal(description, name);
};

const is_third_party_transaction = transaction_url?.includes('CP:');

if (isMobile()) {
return (
<div>
Expand Down Expand Up @@ -130,6 +133,15 @@ const CryptoTransactionsRenderer = observer(({ row: crypto }: TCryptoTransaction
<Text as='p' color='prominent' size='xxs' weight='bold'>
{localize('Transaction hash')}
</Text>
{is_third_party_transaction && (
<Icon
className='crypto-transactions-history__table-tooltip'
data_testid='dt_crypto_transactions_history_table_tooltip_mobile'
onClick={onTooltipClick}
icon='IcHelpCentre'
custom_color='var(--button-secondary-default)'
/>
)}
</Table.Cell>
<Table.Cell className='crypto-transactions-history__table-hash'>
<a
Expand Down Expand Up @@ -223,22 +235,34 @@ const CryptoTransactionsRenderer = observer(({ row: crypto }: TCryptoTransaction
</Table.Cell>
<Table.Cell className='crypto-transactions-history__table-hash'>
{transaction_url ? (
<Popover
alignment='right'
className='crypto-transactions-history__table-popover'
message={localize('View transaction on Blockchain')}
>
<a
className='crypto-transactions-history__table-link'
href={transaction_url}
rel='noopener noreferrer'
target='_blank'
<>
<Popover
alignment='right'
className='crypto-transactions-history__table-popover'
message={localize('View transaction on Blockchain')}
>
<Text as='p' size='xs' color='red'>
{status.transaction_hash}
</Text>
</a>
</Popover>
<a
className='crypto-transactions-history__table-link'
href={transaction_url}
rel='noopener noreferrer'
target='_blank'
>
<Text as='p' size='xs' color='red'>
{status.transaction_hash}
</Text>
</a>
</Popover>
{is_third_party_transaction && (
<Popover
alignment='right'
className='crypto-transactions-history__table-tooltip'
data_testid='dt_crypto_transactions_history_table_tooltip'
message={localize('The details of this transaction is available on CoinsPaid.')}
>
<Icon icon='IcHelpCentre' custom_color='var(--button-secondary-default)' />
</Popover>
)}
</>
) : (
<Text as='p' size='xs' color='red'>
{status.transaction_hash}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getCurrencyName, isMobile } from '@deriv/shared';
import CryptoDeposit from '../crypto-deposit';
import { TRootStore } from 'Types';
import CashierProviders from '../../../../cashier-providers';
import { mockStore } from '@deriv/stores';

jest.mock('@deriv/components', () => ({
...jest.requireActual('@deriv/components'),
Expand All @@ -27,6 +28,24 @@ jest.mock('Components/recent-transaction', () => {
return RecentTransactions;
});

jest.mock('@deriv/api', () => {
return {
...jest.requireActual('@deriv/api'),
useFetch: jest.fn(() => ({
data: {
currencies_config: {
tUSDT: {
minimum_deposit: 2,
minimum_withdrawal: 4.54,
},
},
},
isLoading: false,
isSuccess: true,
})),
};
});

describe('<CryptoDeposit />', () => {
let history: ReturnType<typeof createBrowserHistory>;
const renderWithRouter = (component: JSX.Element, mockRootStore: TRootStore) => {
Expand Down Expand Up @@ -437,4 +456,45 @@ describe('<CryptoDeposit />', () => {

expect(screen.getByText('RecentTransactions')).toBeInTheDocument();
});

it('should show AlertBanner for minimum deposit when third-party payment processor is used (CoinsPaid)', () => {
const minimum_deposit = 2;
const currency = 'tUSDT';
const mock = mockStore({
client: {
currency: 'tUSDT',
},
modules: {
cashier: {
onramp: {
is_deposit_address_loading: false,
api_error: '',
deposit_address: 'tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt',
pollApiForDepositAddress: jest.fn(),
},
transaction_history: {
crypto_transactions: [{}],
onMount: jest.fn(),
},
general_store: {
setIsDeposit: jest.fn(),
},
},
},
});

const wrapper = ({ children }: { children: JSX.Element }) => {
return (
<Router history={history}>
<CashierProviders store={mock}>{children}</CashierProviders>;
</Router>
);
};
render(<CryptoDeposit />, { wrapper });
expect(
screen.getByText(
`A minimum deposit value of ${minimum_deposit} ${currency} is required. Otherwise, the funds will be lost and cannot be recovered.`
)
).toBeInTheDocument();
});
});
Loading

0 comments on commit 250bae7

Please sign in to comment.