Skip to content

Commit

Permalink
bahar/extract_copy_to_clipboard_to_hook (deriv-com#6753)
Browse files Browse the repository at this point in the history
* extract_copy_to_clipboard_to_hook

* add_error_handling
  • Loading branch information
Bahar authored and adrienne-deriv committed Nov 17, 2022
1 parent 79b3507 commit c2afb5a
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -306,28 +306,17 @@ describe('<ApiToken/>', () => {

fireEvent.click(copy_btns_1[0]);
expect(screen.queryByText(warning_msg)).not.toBeInTheDocument();
expect(await screen.findByTestId('dt_token_copied_icon')).toBeInTheDocument();

act(() => jest.advanceTimersByTime(2100));
expect(screen.queryByTestId('dt_token_copied_icon')).not.toBeInTheDocument();

fireEvent.click(copy_btns_1[1]);
expect(await screen.findByText(warning_msg)).toBeInTheDocument();

expect(document.execCommand).toHaveBeenCalledTimes(1);

const ok_btn = screen.getByRole('button', { name: /ok/i });
expect(ok_btn).toBeInTheDocument();

fireEvent.click(ok_btn);
expect(await screen.findByTestId('dt_token_copied_icon')).toBeInTheDocument();
const copy_btns_2 = await screen.findAllByTestId('dt_copy_token_icon');
expect(copy_btns_2.length).toBe(1);

act(() => jest.advanceTimersByTime(2100));
expect(screen.queryByTestId('dt_token_copied_icon')).not.toBeInTheDocument();

expect(document.execCommand).toHaveBeenCalledTimes(2);

jest.clearAllMocks();
});
Expand Down
15 changes: 3 additions & 12 deletions packages/account/src/Components/api-token/api-token-clipboard.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIsMounted } from '@deriv/shared';
import { Button, Icon, Modal, Text, Popover } from '@deriv/components';
import { Button, Icon, Modal, Text, Popover, useCopyToClipboard } from '@deriv/components';
import { localize } from '@deriv/translations';

const WarningNoteBullet = ({ message }) => (
Expand Down Expand Up @@ -29,7 +29,7 @@ const WarningDialogMessage = () => (
);

const ApiTokenClipboard = ({ scopes, text_copy, info_message, success_message, popover_alignment = 'bottom' }) => {
const [is_copied, setIsCopied] = React.useState(false);
const [is_copied, copyToClipboard, setIsCopied] = useCopyToClipboard();
const [is_modal_open, setIsModalOpen] = React.useState(false);
const [is_popover_open, setIsPopoverOpen] = React.useState(false);
const isMounted = useIsMounted();
Expand All @@ -45,27 +45,18 @@ const ApiTokenClipboard = ({ scopes, text_copy, info_message, success_message, p
if (!is_copied) setIsPopoverOpen(false);
};

const copyToClipboard = text => {
const textField = document.createElement('textarea');
textField.innerText = text;
document.body.appendChild(textField);
textField.select();
document.execCommand('copy');
textField.remove();
};

/* two timeouts help to prevent popup window blinking.
without early hiding the popup we will see shortly the description message like during hovering.
this bug appears when popup is handled outside like here
*/
const onClick = () => {
setIsModalOpen(false);
copyToClipboard(text_copy);
setIsCopied(true);
setIsPopoverOpen(true);
timeout_clipboard = setTimeout(() => {
if (isMounted()) {
setIsPopoverOpen(false);
setIsCopied(false);
}
}, 1900);
timeout_clipboard_2 = setTimeout(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ describe('<OnRampProviderPopup />', () => {
api_error: '',
deposit_address: 'tb1qhux20f7h42ya9nqdntl6r9p7p264a2ct8t3n6p',
is_deposit_address_loading: false,
is_deposit_address_popover_open: false,
is_requesting_widget_html: false,
selected_provider: {
name: 'Changelly',
Expand All @@ -29,7 +28,6 @@ describe('<OnRampProviderPopup />', () => {
should_show_widget: false,
widget_error: '',
widget_html: 'Widget HTML',
onClickCopyDepositAddress: jest.fn(),
onClickDisclaimerContinue: jest.fn(),
onClickGoToDepositPage: jest.fn(),
setIsOnRampModalOpen: jest.fn(),
Expand Down Expand Up @@ -112,28 +110,13 @@ describe('<OnRampProviderPopup />', () => {
expect(screen.getByRole('button', { name: 'Continue' })).toBeInTheDocument();
});

it('should trigger onClick callback when the user clicks on copy icon', () => {
render(<OnRampProviderPopup {...props} />);

const copy_icon = screen.getByTestId('dti_deposit_address_icon');
fireEvent.click(copy_icon);

expect(props.onClickCopyDepositAddress).toHaveBeenCalledTimes(1);
});

it('should trigger onFocus method when the user clicks on deposit address field', () => {
render(<OnRampProviderPopup {...props} />);

const deposit_address_input = screen.getByRole('textbox');
expect(fireEvent.focus(deposit_address_input)).toBeTruthy();
});

it('should show "Copied!" message', () => {
render(<OnRampProviderPopup {...props} is_deposit_address_popover_open />);

expect(screen.getByText('Copied!')).toBeInTheDocument();
});

it('should trigger onClick calbacks when the user clicks on "Cancel" and "Continue" buttons', () => {
const { rerender } = render(<OnRampProviderPopup {...props} />);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import classNames from 'classnames';
import React from 'react';
import { Button, HintBox, Icon, Loading, Popover, Text } from '@deriv/components';
import { Button, HintBox, Icon, Loading, Popover, Text, useCopyToClipboard } from '@deriv/components';
import { getKebabCase, website_name, isMobile } from '@deriv/shared';
import { localize, Localize } from '@deriv/translations';
import { connect } from 'Stores/connect';
Expand All @@ -11,13 +11,10 @@ type TOnRampProviderPopupProps = {
deposit_address: string;
is_dark_mode_on: TUiStore['is_dark_mode_on'];
is_deposit_address_loading: boolean;
is_deposit_address_popover_open: boolean;
is_requesting_widget_html: boolean;
onClickCopyDepositAddress: () => void;
onClickDisclaimerContinue: () => void;
onClickGoToDepositPage: () => void;
selected_provider: TProviderDetails;
setDepositAddressRef: (ref: HTMLDivElement | null) => void;
setIsOnRampModalOpen: (boolean: boolean) => void;
should_show_dialog: boolean;
should_show_widget: boolean;
Expand All @@ -30,20 +27,31 @@ const OnRampProviderPopup = ({
deposit_address,
is_dark_mode_on,
is_deposit_address_loading,
is_deposit_address_popover_open,
is_requesting_widget_html,
onClickCopyDepositAddress,
onClickDisclaimerContinue,
onClickGoToDepositPage,
selected_provider,
setDepositAddressRef,
setIsOnRampModalOpen,
should_show_dialog,
should_show_widget,
widget_error,
widget_html,
}: TOnRampProviderPopupProps) => {
const el_onramp_widget_container_ref = React.useRef(null);
const [is_copied, copyToClipboard, setIsCopied] = useCopyToClipboard();
let timeout_clipboard: ReturnType<typeof setTimeout>;

const onClickCopyDepositAddress = () => {
copyToClipboard(deposit_address);

timeout_clipboard = setTimeout(() => {
setIsCopied(false);
}, 500);
};

React.useEffect(() => {
return () => clearTimeout(timeout_clipboard);
}, []);

// JS executed after "on-ramp__widget-container" has been added to the DOM.
// Used for providers that require JS to be executed for inclusion of their widget.
Expand Down Expand Up @@ -111,17 +119,11 @@ const OnRampProviderPopup = ({
<Localize i18n_default_text="Please copy the crypto address you see below. You'll need it to deposit your cryptocurrency." />
</Text>
<div className='on-ramp__popup-deposit-address'>
<Popover
zIndex={9998}
alignment='right'
message={localize('Copied!')}
is_open={is_deposit_address_popover_open}
>
<Popover zIndex={9998} alignment='right' message={localize('Copied!')} is_open={is_copied}>
<input
className={classNames('on-ramp__popup-deposit-address-text', {
'on-ramp__popup-deposit-address-text--dark': is_dark_mode_on,
})}
ref={setDepositAddressRef}
defaultValue={deposit_address}
disabled
onFocus={e => e.preventDefault()}
Expand Down Expand Up @@ -197,13 +199,10 @@ export default connect(({ modules, ui }: TRootStore) => ({
deposit_address: modules.cashier.onramp.deposit_address,
is_dark_mode_on: ui.is_dark_mode_on,
is_deposit_address_loading: modules.cashier.onramp.is_deposit_address_loading,
is_deposit_address_popover_open: modules.cashier.onramp.is_deposit_address_popover_open,
is_requesting_widget_html: modules.cashier.onramp.is_requesting_widget_html,
onClickCopyDepositAddress: modules.cashier.onramp.onClickCopyDepositAddress,
onClickDisclaimerContinue: modules.cashier.onramp.onClickDisclaimerContinue,
onClickGoToDepositPage: modules.cashier.onramp.onClickGoToDepositPage,
selected_provider: modules.cashier.onramp.selected_provider,
setDepositAddressRef: modules.cashier.onramp.setDepositAddressRef,
setIsOnRampModalOpen: modules.cashier.onramp.setIsOnRampModalOpen,
should_show_dialog: modules.cashier.onramp.should_show_dialog,
should_show_widget: modules.cashier.onramp.should_show_widget,
Expand Down
42 changes: 0 additions & 42 deletions packages/cashier/src/stores/__tests__/on-ramp-store.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,35 +164,6 @@ describe('OnRampStore', () => {
// expect(spyDisposeGetWidgetHtmlReaction).toBeCalledTimes(1);
// });

// it('should show and hide deposit address popover when deposit address is copied', async () => {
// jest.useFakeTimers();
// jest.spyOn(document, 'createRange').mockImplementation(() => ({
// selectNodeContents: jest.fn(),
// }));
// jest.spyOn(window, 'window', 'get').mockImplementation(() => ({
// getSelection: () => ({
// addRange: jest.fn(),
// removeAllRanges: jest.fn(),
// }),
// }));
// Object.assign(navigator, {
// clipboard: {
// writeText: jest.fn(() => Promise.resolve()),
// },
// });
// const spySetIsDepositAddressPopoverOpen = jest.spyOn(onramp_store, 'setIsDepositAddressPopoverOpen');
// onramp_store.onClickCopyDepositAddress();

// expect(await spySetIsDepositAddressPopoverOpen).toHaveBeenCalledWith(true);
// jest.runAllTimers();
// expect(setTimeout).toHaveBeenCalledTimes(1);
// expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 500);
// expect(await spySetIsDepositAddressPopoverOpen).toHaveBeenCalledWith(false);

// jest.restoreAllMocks();
// jest.useRealTimers();
// });

it('should show widget when onClickDisclaimerContinue method was called', () => {
onramp_store.onClickDisclaimerContinue();

Expand Down Expand Up @@ -277,7 +248,6 @@ describe('OnRampStore', () => {

expect(onramp_store.api_error).toBeNull();
expect(onramp_store.deposit_address).toBeNull();
expect(onramp_store.deposit_address_ref).toBeNull();
expect(onramp_store.is_deposit_address_loading).toBeTruthy();
expect(onramp_store.selected_provider).toBeNull();
expect(onramp_store.should_show_widget).toBeFalsy();
Expand All @@ -303,24 +273,12 @@ describe('OnRampStore', () => {
expect(onramp_store.deposit_address).toBe('deposit address');
});

it('should set deposit address ref', () => {
onramp_store.setDepositAddressRef('deposit address ref');

expect(onramp_store.deposit_address_ref).toBe('deposit address ref');
});

it('should change value of the variable is_deposit_address_loading', () => {
onramp_store.setIsDepositAddressLoading(true);

expect(onramp_store.is_deposit_address_loading).toBeTruthy();
});

it('should change value of the variable is_deposit_address_popover_open', () => {
onramp_store.setIsDepositAddressPopoverOpen(true);

expect(onramp_store.is_deposit_address_popover_open).toBeTruthy();
});

it('should change value of the variable is_onramp_modal_open', () => {
onramp_store.setIsOnRampModalOpen(true);

Expand Down
30 changes: 0 additions & 30 deletions packages/cashier/src/stores/on-ramp-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default class OnRampStore extends BaseStore {
api_error = null;
deposit_address = null;
is_deposit_address_loading = true;
is_deposit_address_popover_open = false;
is_onramp_modal_open = false;
is_requesting_widget_html = false;
onramp_providers = [];
Expand All @@ -17,16 +16,13 @@ export default class OnRampStore extends BaseStore {
widget_error = null;
widget_html = null;

deposit_address_ref = null;

constructor({ WS, root_store }) {
super({ root_store });

makeObservable(this, {
api_error: observable,
deposit_address: observable,
is_deposit_address_loading: observable,
is_deposit_address_popover_open: observable,
is_onramp_modal_open: observable,
is_requesting_widget_html: observable,
onramp_providers: observable.shallow,
Expand All @@ -40,17 +36,14 @@ export default class OnRampStore extends BaseStore {
should_show_dialog: computed,
onMountOnramp: action.bound,
onUnmountOnramp: action.bound,
onClickCopyDepositAddress: action.bound,
onClickDisclaimerContinue: action.bound,
onClickGoToDepositPage: action.bound,
pollApiForDepositAddress: action.bound,
resetPopup: action.bound,
setApiError: action.bound,
setCopyIconRef: action.bound,
setDepositAddress: action.bound,
setDepositAddressRef: action.bound,
setIsDepositAddressLoading: action.bound,
setIsDepositAddressPopoverOpen: action.bound,
setIsOnRampModalOpen: action.bound,
setIsRequestingWidgetHtml: action.bound,
setSelectedProvider: action.bound,
Expand Down Expand Up @@ -182,20 +175,6 @@ export default class OnRampStore extends BaseStore {
}
}

onClickCopyDepositAddress() {
const range = document.createRange();
range.selectNodeContents(this.deposit_address_ref);

const selections = window.getSelection();
selections.removeAllRanges();
selections.addRange(range);

navigator.clipboard.writeText(this.deposit_address).then(() => {
this.setIsDepositAddressPopoverOpen(true);
setTimeout(() => this.setIsDepositAddressPopoverOpen(false), 500);
});
}

onClickDisclaimerContinue() {
this.setShouldShowWidget(true);
}
Expand Down Expand Up @@ -248,7 +227,6 @@ export default class OnRampStore extends BaseStore {
resetPopup() {
this.setApiError(null);
this.setDepositAddress(null);
this.setDepositAddressRef(null);
this.setIsDepositAddressLoading(true);
this.setSelectedProvider(null);
this.setShouldShowWidget(false);
Expand All @@ -268,18 +246,10 @@ export default class OnRampStore extends BaseStore {
this.deposit_address = deposit_address;
}

setDepositAddressRef(ref) {
this.deposit_address_ref = ref;
}

setIsDepositAddressLoading(is_loading) {
this.is_deposit_address_loading = is_loading;
}

setIsDepositAddressPopoverOpen(is_open) {
this.is_deposit_address_popover_open = is_open;
}

setIsOnRampModalOpen(is_open) {
this.is_onramp_modal_open = is_open;
}
Expand Down
10 changes: 1 addition & 9 deletions packages/cashier/src/utils/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,6 @@ class PromiseClass {
}
}

const copyToClipboard = text => {
const textField = document.createElement('textarea');
textField.innerText = text;
document.body.appendChild(textField);
textField.select();
document.execCommand('copy');
textField.remove();
};
// TODO: [duplicate_code] - Move this to shared package
// eu countries to support
const eu_countries = [
Expand Down Expand Up @@ -104,4 +96,4 @@ const getAccountText = account => {
return account_text;
};

export { copyToClipboard, createElement, getAccountText, getStaticHash, isEuCountry, PromiseClass, template };
export { createElement, getAccountText, getStaticHash, isEuCountry, PromiseClass, template };
Loading

0 comments on commit c2afb5a

Please sign in to comment.