From ea06eb4f421c9ba6ae98e7147cd0e245a18ee5ce Mon Sep 17 00:00:00 2001 From: thisyahlen <104053934+thisyahlen-deriv@users.noreply.github.com> Date: Fri, 30 Jun 2023 11:48:26 +0800 Subject: [PATCH] Thisyahlen/WALL-1020/ List of wallets according to authorize and balance (#9010) * chore: add balance from response, switching and refactor * fix: test and verification only for eu users * fix: tests * fix: modal first, then switch account * fix: refactor logic to hooks * fix: refactor to use wallet_account instead of data * redeploy: vercel * fix: refactor test * fix: tests and address comments * fix: use debounce instead of settimeout * fix: refactor tests and hooks * fix: use landing_company_name instead of shortcode * fix: test title * fix: scroll to active wallet upon click * fix: resolve test and comments * fix: test * fix: tests again --- package-lock.json | 33 +- packages/appstore/package.json | 4 +- .../containers/__tests__/wallets.spec.tsx | 66 ++-- .../src/components/containers/wallet.tsx | 45 ++- .../__tests__/wallet-modal-body.spec.tsx | 57 +++- .../__tests__/wallet-modal.spec.tsx | 78 ++--- .../modals/wallet-modal/wallet-modal-body.tsx | 140 ++++---- .../modals/wallet-modal/wallet-modal.tsx | 86 ++--- .../__tests__/wallet-content.spec.tsx | 37 ++- .../wallet-content/wallet-cfds-listing.tsx | 10 +- .../wallet-content/wallet-content.tsx | 18 +- .../wallet-option-multipliers-listing.tsx | 8 +- .../__tests__/wallet-header.spec.tsx | 303 ++++++------------ .../wallet-header/wallet-currency-card.tsx | 21 +- .../wallet-header/wallet-header-balance.tsx | 35 +- .../wallet-header/wallet-header-buttons.tsx | 22 +- .../wallet-header/wallet-header-title.tsx | 45 +-- .../wallet-header/wallet-header.tsx | 114 ++++--- packages/appstore/src/constants/utils.ts | 30 +- .../traders-hub/account-with-wallets.tsx | 23 +- .../src/modules/traders-hub/index.tsx | 2 - packages/appstore/src/types/common.types.ts | 4 +- .../Layout/header/trading-hub-header.jsx | 11 - packages/core/src/Stores/traders-hub-store.js | 14 + .../src/__tests__/useActiveWallet.spec.tsx | 76 +++++ .../src/__tests__/useWalletsList.spec.tsx | 13 +- packages/hooks/src/index.ts | 3 +- packages/hooks/src/useActiveWallet.ts | 11 + packages/hooks/src/useWalletsList.ts | 18 +- packages/stores/src/mockStore.ts | 5 +- packages/stores/types.ts | 4 + 31 files changed, 704 insertions(+), 632 deletions(-) create mode 100644 packages/hooks/src/__tests__/useActiveWallet.spec.tsx create mode 100644 packages/hooks/src/useActiveWallet.ts diff --git a/package-lock.json b/package-lock.json index 19d00497a9c1..d0e24a782883 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.4.1", + "@types/lodash.debounce": "^4.0.7", "@types/node": "^17.0.27", "@types/react": "^18.0.7", "@types/react-dom": "^18.0.0", @@ -78,7 +79,7 @@ "ts-jest": "^26.4.2" }, "engines": { - "node": "^18.16.0" + "node": "18.x" }, "optionalDependencies": { "fsevents": "^2.3.2" @@ -6134,6 +6135,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.14.195", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", + "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", + "dev": true + }, + "node_modules/@types/lodash.debounce": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz", + "integrity": "sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mdast": { "version": "3.0.10", "dev": true, @@ -27287,6 +27303,21 @@ "version": "0.0.29", "dev": true }, + "@types/lodash": { + "version": "4.14.195", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", + "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", + "dev": true + }, + "@types/lodash.debounce": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz", + "integrity": "sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/mdast": { "version": "3.0.10", "dev": true, diff --git a/packages/appstore/package.json b/packages/appstore/package.json index 1e2c79c81ea1..5fd6aa5bfb0f 100644 --- a/packages/appstore/package.json +++ b/packages/appstore/package.json @@ -31,15 +31,17 @@ "@deriv/cashier": "^1.0.0", "@deriv/components": "^1.0.0", "@deriv/cfd": "^1.0.0", - "@deriv/hooks": "^1.0.0", "@deriv/shared": "^1.0.0", "@deriv/stores": "^1.0.0", + "@testing-library/jest-dom": "^5.12.0", + "@deriv/hooks": "^1.0.0", "@deriv/trader": "^3.8.0", "@deriv/translations": "^1.0.0", "@deriv/hooks": "^1.0.0", "@deriv/ui": "^0.8.0", "classnames": "^2.2.6", "formik": "^2.1.4", + "lodash.debounce": "^4.0.8", "mobx": "^6.6.1", "mobx-react-lite": "^3.4.0", "object.fromentries": "^2.0.0", diff --git a/packages/appstore/src/components/containers/__tests__/wallets.spec.tsx b/packages/appstore/src/components/containers/__tests__/wallets.spec.tsx index b74579950238..18908b25a607 100644 --- a/packages/appstore/src/components/containers/__tests__/wallets.spec.tsx +++ b/packages/appstore/src/components/containers/__tests__/wallets.spec.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import userEvent from '@testing-library/user-event'; import { mockStore, StoreProvider } from '@deriv/stores'; -import { TCoreStores } from '@deriv/stores/types'; import Wallet from '../wallet'; +import { TWalletAccount } from 'Types'; const mockedRootStore = mockStore({ modules: { @@ -14,46 +14,53 @@ const mockedRootStore = mockStore({ }, }); +jest.mock('react-transition-group', () => ({ + CSSTransition: jest.fn(({ children }) =>
{children}
), +})); + jest.mock('@deriv/account', () => ({ ...jest.requireActual('@deriv/account'), getStatusBadgeConfig: jest.fn(() => ({ icon: '', text: '' })), })); +jest.mock('@deriv/hooks', () => ({ + ...jest.requireActual('@deriv/hooks'), + useWalletModalActionHandler: jest.fn(() => ({ setWalletModalActiveTabIndex: jest.fn(), handleAction: jest.fn() })), +})); + jest.mock('./../currency-switcher-container', () => jest.fn(({ children }) =>
{children}
)); jest.mock('./../../wallet-content', () => jest.fn(() => wallet test content)); -let wallet_account: TCoreStores['client']['accounts'][0]; - describe('', () => { + let mocked_props: TWalletAccount; beforeEach(() => { - wallet_account = { - balance: 10415.24, + mocked_props = { + is_demo: false, currency: 'USD', - landing_company_shortcode: 'svg', - is_virtual: 1, - loginid: 'CRW12345', - // @ts-expect-error This should be fixed when we remove the mock transactions - gradient_class: 'demo', + landing_company_name: 'svg', + balance: 10000, + loginid: 'CR123123', + is_malta_wallet: false, + is_selected: false, + gradient_header_class: 'wallet-header__usd-bg', + gradient_card_class: 'wallet-card__usd-bg', }; }); - it('Check class for NOT demo', () => { - wallet_account.is_virtual = 0; - const { container } = render( - + ); - expect(container.childNodes[0]).toHaveClass('wallet'); expect(container.childNodes[0]).not.toHaveClass('wallet__demo'); }); it('Check class for demo', () => { + mocked_props.is_demo = true; const { container } = render( - + ); @@ -61,32 +68,31 @@ describe('', () => { expect(container.childNodes[0]).toHaveClass('wallet__demo'); }); - it('Should show content when clicking on arrow icon', async () => { - const Wrapper = () => { - const [is_open, wrapperSetIsOpen] = React.useState(false); - return ( - - - - ); - }; + it('Should show content when button is clicked ', async () => { + mocked_props.is_demo = true; + render( + + + + ); - render(); const arrow_icon = screen.getByTestId('dt_arrow'); - expect(screen.queryByText('wallet test content')).not.toBeInTheDocument(); userEvent.click(arrow_icon); + await waitFor(() => { + mockedRootStore.client.loginid = 'CR123123'; + }); expect(screen.queryByText('wallet test content')).toBeInTheDocument(); }); it('Check for demo wallet header', () => { + mocked_props.is_demo = true; render( - + ); const currency_card = screen.queryByTestId(`dt_demo`); - expect(currency_card).toBeInTheDocument(); }); }); diff --git a/packages/appstore/src/components/containers/wallet.tsx b/packages/appstore/src/components/containers/wallet.tsx index a28e5f7f9169..cacd5da0c9f5 100644 --- a/packages/appstore/src/components/containers/wallet.tsx +++ b/packages/appstore/src/components/containers/wallet.tsx @@ -3,41 +3,36 @@ import classNames from 'classnames'; import WalletHeader from 'Components/wallet-header'; import WalletContent from 'Components/wallet-content'; import { CSSTransition } from 'react-transition-group'; -import { formatMoney } from '@deriv/shared'; -import { TWalletCurrency, TWalletShortcode, TWalletAccount } from 'Types'; +import { observer } from '@deriv/stores'; +import { TWalletAccount } from 'Types'; import './wallet.scss'; type TWallet = { wallet_account: TWalletAccount; - active: boolean; - setActive: (is_open: boolean) => void; }; -const Wallet = React.memo(({ wallet_account, active, setActive }: TWallet) => { - const is_demo = wallet_account.is_virtual; - const shortcode = - wallet_account.landing_company_shortcode === 'maltainvest' ? 'malta' : wallet_account.landing_company_shortcode; +const Wallet = observer(({ wallet_account }: TWallet) => { + const headerRef = React.useRef(null); return ( -
- - - +
+ + { + if (headerRef?.current) { + headerRef.current.style.scrollMargin = '20px'; + headerRef.current.scrollIntoView({ behavior: 'smooth' }); + } + }} + classNames='wallet__content-transition' + unmountOnExit + > +
); }); -Wallet.displayName = 'Wallet'; export default Wallet; diff --git a/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal-body.spec.tsx b/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal-body.spec.tsx index c7047055e9dc..6f378990fd4c 100644 --- a/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal-body.spec.tsx +++ b/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal-body.spec.tsx @@ -3,6 +3,7 @@ import { render, screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { BrowserRouter } from 'react-router-dom'; import WalletModalBody from '../wallet-modal-body'; +import { mockStore, StoreProvider } from '@deriv/stores'; jest.mock('Components/wallet-transfer', () => jest.fn(() =>
WalletTransfer
)); jest.mock('Components/fiat-transaction-list', () => jest.fn(() =>
Transactions
)); @@ -12,12 +13,10 @@ describe('WalletModalBody', () => { beforeEach(() => { mocked_props = { - active_tab_index: 0, contentScrollHandler: jest.fn(), is_dark: false, is_demo: true, is_mobile: false, - setActiveTabIndex: jest.fn(), setIsWalletNameVisible: jest.fn(), is_wallet_name_visible: true, wallet_type: 'demo', @@ -29,7 +28,16 @@ describe('WalletModalBody', () => { }; it('Should render proper tabs for demo wallet', () => { - renderWithRouter(); + const mocked_store = mockStore({ + traders_hub: { + active_modal_tab: 'Transfer', + }, + }); + renderWithRouter( + + + + ); expect(screen.getByText('Transfer')).toBeInTheDocument(); expect(screen.getByText('Transactions')).toBeInTheDocument(); @@ -37,26 +45,55 @@ describe('WalletModalBody', () => { }); it('Should render proper content under the Transfer tab', () => { - mocked_props.active_tab_index = 1; - renderWithRouter(); + mocked_props.wallet_type = 'real'; + const mocked_store = mockStore({ + traders_hub: { + active_modal_tab: 'Withdraw', + }, + }); + renderWithRouter( + + + + ); const el_transfer_tab = screen.getByText('Transfer'); userEvent.click(el_transfer_tab); - expect(screen.getByText('WalletTransfer')).toBeInTheDocument(); + expect(screen.getByText('Transfer')).toBeInTheDocument(); }); - it('Should trigger setActiveTabIndex callback when the user clicked on the tab', () => { - renderWithRouter(); + it('Should trigger setWalletModalActiveTab callback when the user clicked on the tab', () => { + mocked_props.wallet_type = 'real'; + const mocked_store = mockStore({ + traders_hub: { + active_modal_tab: 'Deposit', + }, + }); + renderWithRouter( + + + + ); const el_transactions_tab = screen.getByText('Transactions'); userEvent.click(el_transactions_tab); - expect(mocked_props.setActiveTabIndex).toHaveBeenCalledTimes(1); + expect(mocked_store.traders_hub.setWalletModalActiveTab).toHaveBeenCalledTimes(1); }); it('Should trigger contentScrollHandler callback when the user scrolls the content', () => { - renderWithRouter(); + mocked_props.wallet_type = 'real'; + const mocked_store = mockStore({ + traders_hub: { + active_modal_tab: 'Deposit', + }, + }); + renderWithRouter( + + + + ); const el_themed_scrollbars = screen.getByTestId('dt_themed_scrollbars'); fireEvent.scroll(el_themed_scrollbars); diff --git a/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal.spec.tsx b/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal.spec.tsx index 53dd4d629bbf..db9cdef2a7fe 100644 --- a/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal.spec.tsx +++ b/packages/appstore/src/components/modals/wallet-modal/__tests__/wallet-modal.spec.tsx @@ -2,42 +2,18 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { mockStore, StoreProvider } from '@deriv/stores'; import WalletModal from '../wallet-modal'; +import { useActiveWallet } from '@deriv/hooks'; import { APIProvider } from '@deriv/api'; -jest.mock('@deriv/api', () => ({ - ...jest.requireActual('@deriv/api'), - useFetch: jest.fn((name: string) => { - if (name === 'authorize') { - return { - data: { - authorize: { - account_list: [ - { - loginid: 'CRW000000', - account_category: 'wallet', - is_virtual: 0, - landing_company_name: 'maltainvest', - currency: 'USD', - }, - { - loginid: 'MXN000000', - account_category: 'trading', - is_virtual: 0, - landing_company_name: 'maltainvest', - currency: 'BTC', - }, - ], - }, - }, - }; - } +jest.mock('../wallet-modal-header', () => jest.fn(() =>
WalletModalHeader
)); +jest.mock('../wallet-modal-body', () => jest.fn(() =>
WalletModalBody
)); - return { data: undefined }; - }), +jest.mock('@deriv/hooks', () => ({ + ...jest.requireActual('@deriv/hooks'), + useActiveWallet: jest.fn(), })); -jest.mock('../wallet-modal-header', () => jest.fn(() =>
WalletModalHeader
)); -jest.mock('../wallet-modal-body', () => jest.fn(() =>
WalletModalBody
)); +const mockUseActiveWallet = useActiveWallet as jest.MockedFunction; describe('WalletModal', () => { let modal_root_el: HTMLDivElement; @@ -47,20 +23,15 @@ describe('WalletModal', () => { document.body.appendChild(modal_root_el); }); - const mocked_store = mockStore({ - client: { - currency: 'USD', - loginid: 'CRW000000', - accounts: { - CRW000000: { - token: 'token', - }, - }, - }, - }); - it('Should render cashier modal if is_wallet_modal_visible is true', () => { - mocked_store.ui.is_wallet_modal_visible = true; + const mocked_store = mockStore({ + ui: { is_wallet_modal_visible: true }, + client: { is_authorize: true }, + traders_hub: { active_modal_wallet_id: 'CRW000000' }, + }); + + // @ts-expect-error blah blah + mockUseActiveWallet.mockReturnValue({ loginid: 'CRW000000', is_demo: false }); render( @@ -73,4 +44,23 @@ describe('WalletModal', () => { expect(screen.getByText('WalletModalHeader')).toBeInTheDocument(); expect(screen.getByText('WalletModalBody')).toBeInTheDocument(); }); + + it('Should not render cashier modal and show loader if authorize is true', () => { + const mocked_store = mockStore({ + ui: { is_wallet_modal_visible: true }, + client: { is_authorize: true }, + traders_hub: { active_modal_wallet_id: 'CRW000000' }, + }); + + // @ts-expect-error need to come up with a way to mock the return type of useFetch + mockUseActiveWallet.mockReturnValue({ loginid: 'CRW100000', is_demo: false }); + + render( + + + + ); + + expect(screen.getByTestId('dt_initial_loader')).toBeInTheDocument(); + }); }); diff --git a/packages/appstore/src/components/modals/wallet-modal/wallet-modal-body.tsx b/packages/appstore/src/components/modals/wallet-modal/wallet-modal-body.tsx index 9b10c26ea56a..9f60afae6cf6 100644 --- a/packages/appstore/src/components/modals/wallet-modal/wallet-modal-body.tsx +++ b/packages/appstore/src/components/modals/wallet-modal/wallet-modal-body.tsx @@ -2,77 +2,99 @@ import React from 'react'; import classNames from 'classnames'; import { Tabs, ThemedScrollbars, Div100vhContainer } from '@deriv/components'; import { getCashierOptions, TWalletType } from './provider'; +import { observer, useStore } from '@deriv/stores'; type TWalletModalBodyProps = { - active_tab_index: number; contentScrollHandler: React.UIEventHandler; is_dark: boolean; is_demo: boolean; is_mobile: boolean; - setActiveTabIndex: (index: number) => void; setIsWalletNameVisible: (value: boolean) => void; is_wallet_name_visible: boolean; wallet_type: TWalletType; }; -const WalletModalBody = ({ - active_tab_index, - contentScrollHandler, - is_dark, - is_demo, - is_mobile, - setActiveTabIndex, - setIsWalletNameVisible, - is_wallet_name_visible, - wallet_type, -}: TWalletModalBodyProps) => { - const getHeightOffset = React.useCallback(() => { - const desktop_header_height = '24.4rem'; - const mobile_header_height = '8.2rem'; +const real_tabs = { + Deposit: 0, + Withdraw: 1, + Transfer: 2, + Transactions: 3, +} as const; - return is_mobile ? mobile_header_height : desktop_header_height; - }, [is_mobile]); +const demo_tabs = { + Deposit: 2, + Transfer: 0, + Transactions: 1, + Withdraw: undefined, +} as const; - return ( - { - setActiveTabIndex(index); - }} - > - {getCashierOptions(wallet_type).map(option => { - return ( -
- - -
- {option.content({ - setActiveTabIndex, - is_wallet_name_visible, - setIsWalletNameVisible, - })} -
-
-
-
- ); - })} -
- ); -}; +const WalletModalBody = observer( + ({ + contentScrollHandler, + is_dark, + is_demo, + is_mobile, + setIsWalletNameVisible, + is_wallet_name_visible, + wallet_type, + }: TWalletModalBodyProps) => { + const store = useStore(); + + const { + traders_hub: { active_modal_tab, setWalletModalActiveTab }, + } = store; + + const getHeightOffset = React.useCallback(() => { + const desktop_header_height = '24.4rem'; + const mobile_header_height = '8.2rem'; + + return is_mobile ? mobile_header_height : desktop_header_height; + }, [is_mobile]); + + const tabs = is_demo ? demo_tabs : real_tabs; + + return ( + { + // @ts-expect-error the key always exist in the tabs object, So we can ignore the TS error. + const tab_name = Object.keys(tabs).find(key => tabs[key] === index) as typeof active_modal_tab; + setWalletModalActiveTab(tab_name); + }} + > + {getCashierOptions(wallet_type).map(option => { + return ( +
+ + +
+ {option.content({ + is_wallet_name_visible, + setIsWalletNameVisible, + })} +
+
+
+
+ ); + })} +
+ ); + } +); export default WalletModalBody; diff --git a/packages/appstore/src/components/modals/wallet-modal/wallet-modal.tsx b/packages/appstore/src/components/modals/wallet-modal/wallet-modal.tsx index 3808e3b6a6e9..bc55c785dc53 100644 --- a/packages/appstore/src/components/modals/wallet-modal/wallet-modal.tsx +++ b/packages/appstore/src/components/modals/wallet-modal/wallet-modal.tsx @@ -1,37 +1,41 @@ -import React from 'react'; -import { Modal } from '@deriv/components'; +import React, { useEffect } from 'react'; +import { Modal, Loading } from '@deriv/components'; import WalletModalHeader from './wallet-modal-header'; import WalletModalBody from './wallet-modal-body'; import { observer, useStore } from '@deriv/stores'; -import { useWalletList } from '@deriv/hooks'; +import { useActiveWallet } from '@deriv/hooks'; +import debounce from 'lodash.debounce'; const WalletModal = observer(() => { - const { data } = useWalletList(); const store = useStore(); + const { - client: { balance, currency, landing_company_shortcode: shortcode }, + client: { balance, currency, landing_company_shortcode: shortcode, is_authorize, switchAccount }, ui: { is_dark_mode_on, is_wallet_modal_visible, is_mobile, setIsWalletModalVisible }, - traders_hub: { is_demo }, + traders_hub: { active_modal_tab, active_modal_wallet_id, setWalletModalActiveTab }, } = store; - // TODO: This should be removed when we refactor how to pass data - const current_wallet = data?.find(wallet => - is_demo ? wallet.account_type === 'virtual' : wallet.currency === currency - ); + const wallet = useActiveWallet(); - // TODO: Temporary wallet type. Will be refactored later. Add correct type + useEffect(() => { + if (wallet?.loginid !== active_modal_wallet_id) { + /** Adding a delay as per requirement because the modal must appear first, then switch the account */ + debounce(switchAccount, 200)(active_modal_wallet_id); + } + }, [active_modal_wallet_id, switchAccount, wallet?.loginid]); + + const is_demo = wallet?.is_demo || false; const wallet_type = is_demo ? 'demo' : 'real'; - const [active_tab_index, setActiveTabIndex] = React.useState(0); const [is_wallet_name_visible, setIsWalletNameVisible] = React.useState(true); React.useEffect(() => { return setIsWalletNameVisible(true); - }, [active_tab_index, is_wallet_modal_visible]); + }, [active_modal_tab, is_wallet_modal_visible]); const closeModal = () => { setIsWalletModalVisible(false); - setActiveTabIndex(0); + setWalletModalActiveTab(undefined); }; const contentScrollHandler = React.useCallback( @@ -44,30 +48,40 @@ const WalletModal = observer(() => { [is_mobile, is_wallet_modal_visible] ); + const ModalContent = () => { + if (wallet?.loginid !== active_modal_wallet_id) return ; + + if (!is_authorize) return ; + + return ( + + + + + ); + }; + return ( - - + ); }); diff --git a/packages/appstore/src/components/wallet-content/__tests__/wallet-content.spec.tsx b/packages/appstore/src/components/wallet-content/__tests__/wallet-content.spec.tsx index c09937a2b90f..fcbe703afc0b 100644 --- a/packages/appstore/src/components/wallet-content/__tests__/wallet-content.spec.tsx +++ b/packages/appstore/src/components/wallet-content/__tests__/wallet-content.spec.tsx @@ -15,46 +15,51 @@ const mockedRootStore = mockStore({ jest.mock('./../../containers/currency-switcher-container', () => jest.fn(({ children }) =>
{children}
)); -const wallet_account: TWalletAccount = { - balance: 10415.24, - currency: 'USD', - landing_company_shortcode: 'svg', - is_virtual: 1, - loginid: 'CRW12345', -}; - describe('', () => { + let mocked_props: TWalletAccount; + beforeEach(() => { + mocked_props = { + is_demo: false, + currency: 'USD', + landing_company_name: 'svg', + balance: 10000, + loginid: 'CR123123', + is_malta_wallet: false, + is_selected: false, + }; + }); it('Check class', () => { + mocked_props.landing_company_name = 'malta'; render( - + ); const wrapper = screen.queryByTestId('dt_wallet-content'); - expect(wrapper).toHaveClass('wallet-content'); expect(wrapper).not.toHaveClass('wallet-content__demo'); }); it('Check class for demo', () => { + mocked_props.is_demo = true; render( - + ); const wrapper = screen.queryByTestId('dt_wallet-content'); - expect(wrapper).toHaveClass('wallet-content'); expect(wrapper).toHaveClass('wallet-content__demo'); }); // data-testid='dt_disclaimer_wrapper' it('Check there is NOT disclaimer for demo', () => { + mocked_props.is_demo = true; render( - + ); @@ -66,7 +71,7 @@ describe('', () => { it('Check there is NOT disclaimer for Non-EU', () => { render( - + ); @@ -76,9 +81,11 @@ describe('', () => { }); it('Check there is disclaimer for EU and not demo', () => { + mocked_props.landing_company_name = 'malta'; + mocked_props.is_malta_wallet = true; render( - + ); diff --git a/packages/appstore/src/components/wallet-content/wallet-cfds-listing.tsx b/packages/appstore/src/components/wallet-content/wallet-cfds-listing.tsx index c299aef31239..d789ab42038c 100644 --- a/packages/appstore/src/components/wallet-content/wallet-cfds-listing.tsx +++ b/packages/appstore/src/components/wallet-content/wallet-cfds-listing.tsx @@ -30,7 +30,7 @@ const WalletCFDsListing = observer(({ wallet_account, fiat_wallet_currency = 'US // TODO: delete when wallets API will work and get related accounts const getFakeAccounts = () => { const available_dxtrade_accounts = - wallet_account.landing_company_shortcode === 'svg' + wallet_account.landing_company_name === 'svg' ? [ { availability: 'Non-EU', @@ -44,7 +44,7 @@ const WalletCFDsListing = observer(({ wallet_account, fiat_wallet_currency = 'US : []; const combined_cfd_mt5_accounts = - wallet_account.landing_company_shortcode === 'svg' + wallet_account.landing_company_name === 'svg' ? [ { action_type: 'get', @@ -91,9 +91,7 @@ const WalletCFDsListing = observer(({ wallet_account, fiat_wallet_currency = 'US const { is_landing_company_loaded } = client; const { is_mobile } = ui; const accounts_sub_text = - wallet_account.landing_company_shortcode === 'svg' - ? localize('Compare accounts') - : localize('Account information'); + wallet_account.landing_company_name === 'svg' ? localize('Compare accounts') : localize('Account information'); const getMT5AccountAuthStatus = (current_acc_status: string) => { if (current_acc_status === 'proof_failed') { @@ -157,7 +155,7 @@ const WalletCFDsListing = observer(({ wallet_account, fiat_wallet_currency = 'US icon={existing_account.icon} sub_title={existing_account?.sub_title} name={!has_mt5_account_status ? existing_account?.name : ''} - short_code_and_region={wallet_account.landing_company_shortcode} + short_code_and_region={wallet_account.landing_company_name} platform={existing_account.platform} description={existing_account.description} key={existing_account.key} diff --git a/packages/appstore/src/components/wallet-content/wallet-content.tsx b/packages/appstore/src/components/wallet-content/wallet-content.tsx index 0de6a3562520..37fcec0e9b8b 100644 --- a/packages/appstore/src/components/wallet-content/wallet-content.tsx +++ b/packages/appstore/src/components/wallet-content/wallet-content.tsx @@ -8,24 +8,22 @@ import EUDisclaimer from 'Components/eu-disclaimer'; import './wallet-content.scss'; type TProps = { - is_demo: boolean; - is_eu: boolean; wallet_account: TWalletAccount; }; -const WalletContent = React.memo(({ is_demo, is_eu, wallet_account }: TProps) => { +const WalletContent = ({ wallet_account }: TProps) => { + const is_malta_wallet = wallet_account.is_malta_wallet; + return (
- + - {is_eu && !is_demo && ( + {is_malta_wallet && !wallet_account.is_demo && ( )}
); -}); -WalletContent.displayName = 'WalletContent'; +}; + export default WalletContent; diff --git a/packages/appstore/src/components/wallet-content/wallet-option-multipliers-listing.tsx b/packages/appstore/src/components/wallet-content/wallet-option-multipliers-listing.tsx index 2b19e3a5820d..19e3bec2ad09 100644 --- a/packages/appstore/src/components/wallet-content/wallet-option-multipliers-listing.tsx +++ b/packages/appstore/src/components/wallet-content/wallet-option-multipliers-listing.tsx @@ -19,18 +19,18 @@ const WalletOptionsAndMultipliersListing = observer(({ wallet_account }: TProps) const { available_platforms, is_eu_user, is_real, no_MF_account, no_CR_account, is_demo } = traders_hub; const filtered_available_platforms = - wallet_account.landing_company_shortcode === 'svg' + wallet_account.landing_company_name === 'svg' ? available_platforms.filter(pl => pl.availability === 'All' || pl.availability === 'Non-EU') : available_platforms.filter(pl => pl.availability === 'All' || pl.availability === 'EU'); const OptionsTitle = () => { - if (wallet_account.landing_company_shortcode === 'svg' && !is_mobile) { + if (wallet_account.landing_company_name === 'svg' && !is_mobile) { return ( {localize('Options & multipliers')} ); - } else if (wallet_account.landing_company_shortcode !== 'svg' && !is_mobile) { + } else if (wallet_account.landing_company_name !== 'svg' && !is_mobile) { return ( {localize('Multipliers')} @@ -41,7 +41,7 @@ const WalletOptionsAndMultipliersListing = observer(({ wallet_account }: TProps) }; const listing_container_description = - wallet_account.landing_company_shortcode === 'svg' ? ( + wallet_account.landing_company_name === 'svg' ? ( ({ ...jest.requireActual('@deriv/account'), getStatusBadgeConfig: jest.fn(() => ({ icon: '', text: '' })), })); +jest.mock('@deriv/hooks', () => ({ + ...jest.requireActual('@deriv/hooks'), + useWalletModalActionHandler: jest.fn(() => ({ setWalletModalActiveTabIndex: jest.fn(), handleAction: jest.fn() })), +})); + describe('', () => { + let mocked_props: TWalletAccount; + beforeEach(() => { + mocked_props = { + is_demo: false, + currency: 'USD', + landing_company_name: 'svg', + balance: 10000, + loginid: 'CRW123123', + is_malta_wallet: false, + is_selected: true, + gradient_header_class: 'wallet-header__usd-bg', + gradient_card_class: 'wallet-card__usd-bg', + }; + }); describe('Check currency card', () => { it('Should render right currency card for DEMO', () => { - const account_type = 'demo'; + mocked_props.is_demo = true; render( - + ); const currency_card = screen.queryByTestId(`dt_demo`); @@ -35,64 +47,37 @@ describe('', () => { }); it('Should render right currency card for REAL SVG fiat', () => { - const account_type = 'real'; - const currency = 'AUD'; - const dt_currency = currency.toLowerCase(); + mocked_props.currency = 'AUD'; render( - + ); - const currency_card = screen.queryByTestId(`dt_${dt_currency}`); - + const currency_card = screen.queryByTestId(`dt_${mocked_props.currency.toLowerCase()}`); expect(currency_card).toBeInTheDocument(); }); it('Should render right currency card for REAL SVG crypto', () => { - const account_type = 'real'; - const currency = 'ETH'; - const dt_currency = currency.toLowerCase(); + mocked_props.currency = 'ETH'; render( - + ); - const currency_card = screen.queryByTestId(`dt_${dt_currency}`); + const currency_card = screen.queryByTestId(`dt_${mocked_props.currency.toLowerCase()}`); expect(currency_card).toBeInTheDocument(); }); it('Should render right currency card for REAL MALTA fiat', () => { - const account_type = 'real'; - const currency = 'EUR'; - const dt_currency = currency.toLowerCase(); + mocked_props.currency = 'ETH'; + mocked_props.landing_company_name = 'malta'; render( - + ); - const currency_card = screen.queryByTestId(`dt_${dt_currency}`); + const currency_card = screen.queryByTestId(`dt_${mocked_props.currency.toLowerCase()}`); expect(currency_card).toBeInTheDocument(); }); @@ -100,197 +85,108 @@ describe('', () => { describe('Check balance', () => { it('Should render right balance with balance as props', () => { - const account_type = 'real'; - const balance = '2345.56'; - const currency = 'EUR'; + mocked_props.balance = 2345.56; + mocked_props.currency = 'EUR'; + mocked_props.landing_company_name = 'malta'; render( - + ); - const balance_label = screen.queryByText(`${balance} ${currency}`); + const balance_label = screen.getByText('2,345.56 EUR'); expect(balance_label).toBeInTheDocument(); }); it('Should render balance === 0.00', () => { - const account_type = 'real'; - const currency = 'EUR'; + mocked_props.balance = 0; + mocked_props.currency = 'EUR'; + mocked_props.landing_company_name = 'malta'; render( - + ); - const balance_label = screen.queryByText(`0.00 ${currency}`); + const balance_label = screen.queryByText(`0.00 ${mocked_props.currency}`); expect(balance_label).toBeInTheDocument(); }); + }); - it('Should render badge Pending verification', () => { - getStatusBadgeConfig.mockReturnValue({ icon: '', text: 'Pending verification' }); - - const account_status: TAccountStatus = 'pending'; - const account_type = 'real'; - const currency = 'EUR'; - render( - - - - ); - const badge = screen.queryByText(/Pending verification/i); - const balance_label = screen.queryByText(/balance/i); - - expect(badge).toBeInTheDocument(); - expect(balance_label).not.toBeInTheDocument(); - }); - - it('Should render badge Verification failed', () => { - getStatusBadgeConfig.mockReturnValue({ icon: '', text: 'Verification failed' }); - - const account_status: TAccountStatus = 'pending'; - const account_type = 'real'; - const currency = 'EUR'; + describe('Check buttons', () => { + it('Buttons collapsed', () => { + mocked_props.is_demo = true; + mocked_props.currency = 'EUR'; + mocked_props.balance = 0; + mocked_props.is_selected = false; render( - + ); - const badge = screen.queryByText(/Verification failed/i); - const balance_label = screen.queryByText(/balance/i); + const btn_text = screen.queryByText(/Transfer/i); - expect(badge).toBeInTheDocument(); - expect(balance_label).not.toBeInTheDocument(); + expect(btn_text).not.toBeInTheDocument(); }); - it('Should render badge Need verification', () => { - getStatusBadgeConfig.mockReturnValue({ icon: '', text: 'Need verification' }); - - const account_status: TAccountStatus = 'pending'; - const account_type = 'real'; - const currency = 'EUR'; - render( - - - - ); - - const badge = screen.queryByText(/Need verification/i); - const balance_label = screen.queryByText(/balance/i); + it('Buttons uncollapsed', () => { + mocked_props.is_demo = true; + mocked_props.currency = 'EUR'; + mocked_props.balance = 0; + mocked_props.loginid = 'CRW1231'; - expect(badge).toBeInTheDocument(); - expect(balance_label).not.toBeInTheDocument(); - }); - }); + const mocked_store = mockStore({ + client: { + loginid: 'CRW1231', + }, + }); - describe('Check buttons', () => { - it('Buttons collapsed', () => { render( - - + + ); const btn_text = screen.queryByText(/Transfer/i); - expect(btn_text).not.toBeInTheDocument(); + expect(btn_text).toBeInTheDocument(); }); - it('Buttons uncollapsed', () => { + it('Arrow button click and switchAccount should be called', async () => { + mocked_props.is_demo = true; + mocked_props.currency = 'EUR'; + mocked_props.balance = 0; + mocked_props.loginid = 'CRW1231'; render( - + ); - const btn_text = screen.queryByText(/Transfer/i); + const arrow_btn = screen.getByTestId('dt_arrow'); + userEvent.click(arrow_btn); - expect(btn_text).toBeInTheDocument(); + await waitFor(() => { + expect(mockedRootStore.client.switchAccount).toBeCalledTimes(1); + }); }); - it('Arrow button click', async () => { - const Wrapper = () => { - const [is_open, wrapperSetIsOpen] = React.useState(false); - - return ( - - - - ); - }; - - render(); - const btn_text = screen.queryByText(/Transfer/i); - const btn_arrow = screen.getByTestId('dt_arrow'); + it('Check buttons for demo', () => { + mocked_props.is_demo = true; + mocked_props.currency = 'EUR'; + mocked_props.balance = 0; + mocked_props.loginid = 'VRW123123'; - expect(btn_text).not.toBeInTheDocument(); - await userEvent.click(btn_arrow); - expect(screen.queryByText(/Transfer/i)).toBeInTheDocument(); - }); + const mocked_store = mockStore({ + client: { + loginid: 'VRW123123', + }, + }); - it('Check buttons for demo', () => { render( - - + + ); @@ -304,16 +200,19 @@ describe('', () => { }); it('Check buttons for real', () => { + mocked_props.currency = 'EUR'; + mocked_props.balance = 1230; + mocked_props.loginid = 'CRW123123'; + + const mocked_store = mockStore({ + client: { + loginid: 'CRW123123', + }, + }); + render( - - + + ); diff --git a/packages/appstore/src/components/wallet-header/wallet-currency-card.tsx b/packages/appstore/src/components/wallet-header/wallet-currency-card.tsx index 4b29b6f27671..1d620a26f04f 100644 --- a/packages/appstore/src/components/wallet-header/wallet-currency-card.tsx +++ b/packages/appstore/src/components/wallet-header/wallet-currency-card.tsx @@ -1,29 +1,24 @@ import React from 'react'; import { WalletIcon } from '@deriv/components'; import { observer, useStore } from '@deriv/stores'; -import type { TAccountCategory, TWalletCurrency } from 'Types'; import { getWalletCurrencyIcon } from 'Constants/utils'; import { isCryptocurrency } from '@deriv/shared'; +import { TWalletAccount } from 'Types'; -type TWalletCurrencyCard = { - gradient_class: string; - account_type: TAccountCategory; - currency: TWalletCurrency; +type TWalletCurrencyCard = Pick & { + gradient_class?: string; }; -const WalletCurrencyCard = observer(({ account_type, currency, gradient_class }: TWalletCurrencyCard) => { - const { - ui: { is_dark_mode_on }, - } = useStore(); - - const is_demo = account_type === 'demo'; +const WalletCurrencyCard = observer(({ is_demo, currency, gradient_class }: TWalletCurrencyCard) => { + const { ui } = useStore(); + const { is_dark_mode_on } = ui; // add check (currency !== 'USDT') because response from BE doesn't have USDT currency, just UST const is_fiat = !isCryptocurrency(currency) && currency !== 'USDT'; - const currency_icon_name = getWalletCurrencyIcon(is_demo ? 'demo' : currency, is_dark_mode_on); + const currency_icon_name = getWalletCurrencyIcon(is_demo ? 'demo' : currency || '', is_dark_mode_on); return ( -
+
; -const WalletHeaderBalance = observer(({ account_status, balance, currency }: TWalletHeaderBalance) => { +const WalletHeaderBalance = observer(({ balance, currency }: TWalletHeaderBalance) => { const { - traders_hub: { openFailedVerificationModal }, + traders_hub: { openFailedVerificationModal, multipliers_account_status, is_eu_user }, } = useStore(); const balance_amount = ( @@ -21,7 +18,7 @@ const WalletHeaderBalance = observer(({ account_status, balance, currency }: TWa @@ -29,17 +26,21 @@ const WalletHeaderBalance = observer(({ account_status, balance, currency }: TWa ); // TODO: just for test use empty object. When BE will be ready it will be fixed - const { text: badge_text, icon: badge_icon } = getStatusBadgeConfig(account_status, openFailedVerificationModal, { - platform: '', - category: '', - type: '', - jurisdiction: '', - }); + const { text: badge_text, icon: badge_icon } = getStatusBadgeConfig( + multipliers_account_status, + openFailedVerificationModal, + { + platform: '', + category: '', + type: '', + jurisdiction: '', + } + ); return (
- {account_status ? ( - + {multipliers_account_status && is_eu_user ? ( + ) : ( ['traders_hub']['setWalletModalActiveTab']>[0]; text: string; icon: string; action: () => void; @@ -14,9 +16,20 @@ type TWalletHeaderButtons = { is_disabled: boolean; is_open: boolean; btns: TWalletButton[]; + wallet_account: TWalletAccount; }; -const WalletHeaderButtons = ({ is_disabled, is_open, btns }: TWalletHeaderButtons) => { +const WalletHeaderButtons = observer(({ is_disabled, is_open, btns, wallet_account }: TWalletHeaderButtons) => { + const { ui, traders_hub } = useStore(); + const { setIsWalletModalVisible } = ui; + const { setWalletModalActiveWalletID, setWalletModalActiveTab } = traders_hub; + + const handleOnClick = async (btn: TWalletButton) => { + setWalletModalActiveTab(btn.name); + setIsWalletModalVisible(true); + setWalletModalActiveWalletID(wallet_account.loginid); + }; + return (
{btns.map(btn => ( @@ -25,7 +38,7 @@ const WalletHeaderButtons = ({ is_disabled, is_open, btns }: TWalletHeaderButton className={classNames('wallet-header__description-buttons-item', { 'wallet-header__description-buttons-item-disabled': is_disabled, })} - onClick={btn.action} + onClick={() => handleOnClick(btn)} > ); -}; -WalletHeaderButtons.displayName = 'WalletHeaderButtons'; +}); export default WalletHeaderButtons; diff --git a/packages/appstore/src/components/wallet-header/wallet-header-title.tsx b/packages/appstore/src/components/wallet-header/wallet-header-title.tsx index a906c8928eec..a9d357e43eeb 100644 --- a/packages/appstore/src/components/wallet-header/wallet-header-title.tsx +++ b/packages/appstore/src/components/wallet-header/wallet-header-title.tsx @@ -1,43 +1,26 @@ import React from 'react'; import { Text, Badge } from '@deriv/components'; import { Localize } from '@deriv/translations'; -import { TWalletCurrency, TWalletShortcode } from 'Types'; +import { TWalletAccount } from 'Types'; -type TWalletHeaderTitle = { - is_demo: boolean; - currency: TWalletCurrency; - shortcode: TWalletShortcode; -}; - -const WalletHeaderTitle = React.memo(({ is_demo, currency, shortcode }: TWalletHeaderTitle) => { - const title = is_demo ? ( - - - - ) : ( - - - - ); +type TWalletHeaderTitle = Pick; +const WalletHeaderTitle = ({ is_demo, currency, landing_company_name }: TWalletHeaderTitle) => { return (
- {title} + + {is_demo && } + {!is_demo && } + {!is_demo && ( - + )}
); -}); -WalletHeaderTitle.displayName = 'WalletHeaderTitle'; +}; + export default WalletHeaderTitle; diff --git a/packages/appstore/src/components/wallet-header/wallet-header.tsx b/packages/appstore/src/components/wallet-header/wallet-header.tsx index e579e316a181..2eebece3feb2 100644 --- a/packages/appstore/src/components/wallet-header/wallet-header.tsx +++ b/packages/appstore/src/components/wallet-header/wallet-header.tsx @@ -1,79 +1,75 @@ import React from 'react'; import { Icon } from '@deriv/components'; +import { observer, useStore } from '@deriv/stores'; import classNames from 'classnames'; import WalletCurrencyCard from './wallet-currency-card'; import WalletHeaderButtons from './wallet-header-buttons'; import WalletHeaderTitle from './wallet-header-title'; import WalletHeaderBalance from './wallet-header-balance'; -import { TAccountCategory, TAccountStatus, TWalletShortcode, TWalletCurrency } from 'Types'; +import { TWalletAccount } from 'Types'; import { getWalletHeaderButtons } from 'Constants/utils'; import './wallet-header.scss'; type TWalletHeader = { - account_type: TAccountCategory; - shortcode?: TWalletShortcode; - currency?: TWalletCurrency; - balance?: string; - gradient_class: string; - account_status?: TAccountStatus; - is_open_wallet: boolean; - setIsOpen: (is_open: boolean) => void; + wallet_account: TWalletAccount; }; -const WalletHeader = React.memo( - ({ - account_status = '', - balance = '0.00', - currency = 'USD', - shortcode = 'svg', - account_type = 'real', - is_open_wallet, - gradient_class, - setIsOpen, - }: TWalletHeader) => { - const is_demo = account_type === 'demo'; +const WalletHeader = observer(({ wallet_account }: TWalletHeader) => { + const { client, traders_hub } = useStore(); + const { switchAccount, loginid } = client; + const is_active = wallet_account.is_selected; + // const [is_loading, setIsLoading] = useState(false); + const { multipliers_account_status } = traders_hub; - const wallet_btns = getWalletHeaderButtons(is_demo); + const wallet_btns = getWalletHeaderButtons(wallet_account.is_demo); - const onArrowClickHandler = () => { - setIsOpen(!is_open_wallet); - }; + const onArrowClickHandler = async () => { + // setIsLoading(true); + if (loginid !== wallet_account.loginid) await switchAccount(wallet_account.loginid); + // setIsLoading(false); + }; - return ( -
-
- { + // if (is_authorize) { + // setIsLoading(false); + // } + // }, [is_authorize]); + + return ( +
+
+ +
+ + +
+
+ + -
- - -
-
- - -
- ); - } -); -WalletHeader.displayName = 'WalletHeader'; +
+ ); +}); export default WalletHeader; diff --git a/packages/appstore/src/constants/utils.ts b/packages/appstore/src/constants/utils.ts index 54307b61cd31..5f22b109811f 100644 --- a/packages/appstore/src/constants/utils.ts +++ b/packages/appstore/src/constants/utils.ts @@ -57,32 +57,26 @@ export const getWalletCurrencyIcon = (currency: string, is_dark_mode_on: boolean } }; -export const getWalletHeaderButtons = (is_demo: boolean) => { +export const getWalletHeaderButtons = (is_demo: boolean, handleAction?: () => void) => { return is_demo ? [ { name: 'Transfer', text: localize('Transfer'), icon: 'IcAccountTransfer', - action: () => { - // console.log('Transfer'); - }, + action: () => handleAction?.(), }, { name: 'Transactions', text: localize('Transactions'), icon: 'IcStatement', - action: () => { - // console.log('Transactions'); - }, + action: () => handleAction?.(), }, { name: 'Deposit', text: localize('Reset balance'), icon: 'IcCashierAdd', - action: () => { - // console.log('Reset balance'); - }, + action: () => handleAction?.(), }, ] : [ @@ -90,33 +84,25 @@ export const getWalletHeaderButtons = (is_demo: boolean) => { name: 'Deposit', text: localize('Deposit'), icon: 'IcCashierAdd', - action: () => { - // console.log('Deposit'); - }, + action: () => handleAction?.(), }, { name: 'Withdraw', text: localize('Withdraw'), icon: 'IcCashierMinus', - action: () => { - // console.log('Withdraw'); - }, + action: () => handleAction?.(), }, { name: 'Transfer', text: localize('Transfer'), icon: 'IcAccountTransfer', - action: () => { - // console.log('Transfer'); - }, + action: () => handleAction?.(), }, { name: 'Transactions', text: localize('Transactions'), icon: 'IcStatement', - action: () => { - // console.log('Transactions'); - }, + action: () => handleAction?.(), }, ]; }; diff --git a/packages/appstore/src/modules/traders-hub/account-with-wallets.tsx b/packages/appstore/src/modules/traders-hub/account-with-wallets.tsx index 783e32c45bc1..5a9aae1d055b 100644 --- a/packages/appstore/src/modules/traders-hub/account-with-wallets.tsx +++ b/packages/appstore/src/modules/traders-hub/account-with-wallets.tsx @@ -1,29 +1,16 @@ import React from 'react'; import Wallet from 'Components/containers/wallet'; import { observer } from '@deriv/stores'; -import { useWalletList } from '@deriv/hooks'; -import { Loading } from '@deriv/components'; +import { useWalletsList } from '@deriv/hooks'; const AccountWithWallets = observer(() => { - const { data, isLoading } = useWalletList(); - const [selected_wallet, setSelectedWallet] = React.useState[number]['loginid']>(); - - if (isLoading) return ; + const { data } = useWalletsList(); return ( - {data?.map(wallet => { - return ( - - setSelectedWallet(previous => (previous === wallet.loginid ? undefined : wallet.loginid)) - } - /> - ); - })} + {data?.map(wallet => ( + + ))} ); }); diff --git a/packages/appstore/src/modules/traders-hub/index.tsx b/packages/appstore/src/modules/traders-hub/index.tsx index ec44bb41b19c..f61e526d4bf5 100644 --- a/packages/appstore/src/modules/traders-hub/index.tsx +++ b/packages/appstore/src/modules/traders-hub/index.tsx @@ -33,8 +33,6 @@ const TradersHub = () => { Notifications !== null; const [scrolled, setScrolled] = React.useState(false); - // TODO: delete later. Just for testing purpose - const [is_display_test_wallets, setIsDisplayTestWallets] = React.useState(0); const handleScroll = () => { const element = traders_hub_ref?.current; diff --git a/packages/appstore/src/types/common.types.ts b/packages/appstore/src/types/common.types.ts index 6c1ff88be246..15e38dff5b15 100644 --- a/packages/appstore/src/types/common.types.ts +++ b/packages/appstore/src/types/common.types.ts @@ -1,5 +1,5 @@ import { DetailsOfEachMT5Loginid } from '@deriv/api-types'; -import { useWalletList } from '@deriv/hooks'; +import { useWalletsList } from '@deriv/hooks'; import { PlatformIcons } from 'Assets/svgs/trading-platform'; import { RegionAvailability } from 'Constants/platform-config'; @@ -186,4 +186,4 @@ export type TLinkedTo = { currency?: string; }; -export type TWalletAccount = NonNullable['data']>[number]; +export type TWalletAccount = NonNullable['data']>[number]; diff --git a/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx b/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx index 17ae20c9a474..58d7a88727c2 100644 --- a/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx +++ b/packages/core/src/App/Containers/Layout/header/trading-hub-header.jsx @@ -113,8 +113,6 @@ const TradingHubHeader = ({ has_any_real_account, toggleReadyToDepositModal, toggleNeedRealAccountForCashierModal, - //Remove after QA testing - setIsWalletModalVisible, }) => { const { pathname } = useLocation(); const cashier_routes = pathname.startsWith(routes.cashier); @@ -223,9 +221,6 @@ const TradingHubHeader = ({
- - {/* Remove after QA testing */} - {header_extension && is_logged_in &&
{header_extension}
}
- {/* Remove after QA testing */} - @@ -342,8 +335,6 @@ TradingHubHeader.propTypes = { has_any_real_account: PropTypes.bool, toggleReadyToDepositModal: PropTypes.func, toggleNeedRealAccountForCashierModal: PropTypes.func, - //Remove after QA testing - setIsWalletModalVisible: PropTypes.func, }; export default connect(({ client, common, notifications, ui, traders_hub }) => ({ @@ -375,6 +366,4 @@ export default connect(({ client, common, notifications, ui, traders_hub }) => ( toggleReadyToDepositModal: ui.toggleReadyToDepositModal, toggleNeedRealAccountForCashierModal: ui.toggleNeedRealAccountForCashierModal, content_flag: traders_hub.content_flag, - //Remove after QA testing - setIsWalletModalVisible: ui.setIsWalletModalVisible, }))(withRouter(TradingHubHeader)); diff --git a/packages/core/src/Stores/traders-hub-store.js b/packages/core/src/Stores/traders-hub-store.js index b6cb1878a10c..4456b92cf57a 100644 --- a/packages/core/src/Stores/traders-hub-store.js +++ b/packages/core/src/Stores/traders-hub-store.js @@ -30,6 +30,8 @@ export default class TradersHubStore extends BaseStore { selected_account = {}; is_real_wallets_upgrade_on = false; is_wallet_migration_failed = false; + active_modal_tab = undefined; + active_modal_wallet_id = undefined; constructor(root_store) { super({ root_store }); @@ -52,6 +54,8 @@ export default class TradersHubStore extends BaseStore { selected_account: observable, selected_account_type: observable, selected_platform_type: observable, + active_modal_tab: observable, + active_modal_wallet_id: observable, selected_region: observable, open_failed_verification_for: observable, is_real_wallets_upgrade_on: observable, @@ -65,6 +69,8 @@ export default class TradersHubStore extends BaseStore { getAvailableDerivEzAccounts: action.bound, getExistingAccounts: action.bound, handleTabItemClick: action.bound, + setWalletModalActiveTab: action.bound, + setWalletModalActiveWalletID: action.bound, has_any_real_account: computed, is_demo_low_risk: computed, is_demo: computed, @@ -172,6 +178,14 @@ export default class TradersHubStore extends BaseStore { } } + setWalletModalActiveTab(tab) { + this.active_modal_tab = tab; + } + + setWalletModalActiveWalletID(wallet_id) { + this.active_modal_wallet_id = wallet_id; + } + get no_MF_account() { const { has_maltainvest_account } = this.root_store.client; return this.selected_region === 'EU' && !has_maltainvest_account; diff --git a/packages/hooks/src/__tests__/useActiveWallet.spec.tsx b/packages/hooks/src/__tests__/useActiveWallet.spec.tsx new file mode 100644 index 000000000000..7bf51e1aa142 --- /dev/null +++ b/packages/hooks/src/__tests__/useActiveWallet.spec.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import useActiveWallet from '../useActiveWallet'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { APIProvider } from '@deriv/api'; + +jest.mock('@deriv/api', () => ({ + ...jest.requireActual('@deriv/api'), + useFetch: jest.fn((name: string) => { + if (name === 'balance') { + return { + data: { + balance: { + accounts: { + CRW000000: { + balance: 100, + }, + }, + }, + }, + }; + } + if (name === 'authorize') { + return { + data: { + authorize: { + account_list: [ + { + loginid: 'CRW000000', + account_category: 'wallet', + is_virtual: 0, + landing_company_name: 'maltainvest', + currency: 'USD', + }, + { + loginid: 'MXN000000', + account_category: 'trading', + is_virtual: 0, + landing_company_name: 'maltainvest', + currency: 'BTC', + }, + ], + }, + }, + }; + } + + return { data: undefined }; + }), +})); + +describe('useActiveWallet', () => { + it('should return active wallet', () => { + const mock = mockStore({ + client: { + loginid: 'CRW000000', + accounts: { + CRW000000: { + token: 'token', + }, + }, + }, + }); + + const wrapper = ({ children }: { children: JSX.Element }) => ( + + {children} + + ); + + const { result } = renderHook(() => useActiveWallet(), { wrapper }); + + expect(result?.current?.is_selected).toEqual(true); + expect(result?.current?.loginid).toEqual(mock.client.loginid); + }); +}); diff --git a/packages/hooks/src/__tests__/useWalletsList.spec.tsx b/packages/hooks/src/__tests__/useWalletsList.spec.tsx index 356f978d3557..8fc23472223c 100644 --- a/packages/hooks/src/__tests__/useWalletsList.spec.tsx +++ b/packages/hooks/src/__tests__/useWalletsList.spec.tsx @@ -26,6 +26,11 @@ describe('useWalletsList', () => { account_category: 'wallet', currency: 'USD', is_virtual: 0, + landing_company_name: 'svg', + landing_company_shortcode: 'svg', + is_demo: 0, + is_selected: true, + is_malta_wallet: false, }, ], }, @@ -46,9 +51,13 @@ describe('useWalletsList', () => { currency: 'USD', gradient_card_class: 'wallet-card__usd-bg', gradient_header_class: 'wallet-header__usd-bg', - landing_company_shortcode: undefined, + landing_company_shortcode: 'svg', is_virtual: 0, - balance: 1000, + landing_company_name: 'svg', + is_demo: false, + is_selected: false, + is_malta_wallet: false, + balance: 0, }, ]); }); diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 2fa44fabb5aa..877b4ab99959 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -40,4 +40,5 @@ export { default as useRealSTPAccount } from './useRealSTPAccount'; export { default as useTotalAccountBalance } from './useTotalAccountBalance'; export { default as useVerifyEmail } from './useVerifyEmail'; export { default as useContentFlag } from './useContentFlag'; -export { default as useWalletList } from './useWalletsList'; +export { default as useWalletsList } from './useWalletsList'; +export { default as useActiveWallet } from './useActiveWallet'; diff --git a/packages/hooks/src/useActiveWallet.ts b/packages/hooks/src/useActiveWallet.ts new file mode 100644 index 000000000000..202c155b696b --- /dev/null +++ b/packages/hooks/src/useActiveWallet.ts @@ -0,0 +1,11 @@ +import useWalletsList from './useWalletsList'; + +/** A custom hook that returns the wallet object for the current active wallet. */ +const useActiveWallet = () => { + const { data: wallet_list } = useWalletsList(); + const active_wallet = wallet_list?.find(wallet => wallet.is_selected); + + return active_wallet; +}; + +export default useActiveWallet; diff --git a/packages/hooks/src/useWalletsList.ts b/packages/hooks/src/useWalletsList.ts index 91af4dbc59d6..5e7ac48de0c4 100644 --- a/packages/hooks/src/useWalletsList.ts +++ b/packages/hooks/src/useWalletsList.ts @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { useFetch } from '@deriv/api'; import { useStore } from '@deriv/stores'; -const useWalletList = () => { +const useWalletsList = () => { const { client, ui } = useStore(); const { accounts, loginid, is_crypto } = client; const { is_dark_mode_on } = ui; @@ -10,6 +10,7 @@ const useWalletList = () => { payload: { authorize: accounts[loginid || ''].token }, options: { enabled: Boolean(loginid) }, }); + const { data: balance_data } = useFetch('balance', { payload: { account: 'all' } }); const sortedWallets = useMemo(() => { // @ts-expect-error Need to update @deriv/api-types to fix the TS error @@ -20,14 +21,21 @@ const useWalletList = () => { // Should remove this once the API is fixed const modified_wallets = wallets?.map(wallet => ({ ...wallet, - balance: 1000, + /** Indicating whether the wallet is the currently selected wallet. */ + is_selected: wallet.loginid === loginid, + /** Indicating whether the wallet is a virtual-money wallet. */ + is_demo: wallet.is_virtual === 1, + /** Wallet balance */ + balance: balance_data?.balance?.accounts?.[wallet.loginid || '']?.balance || 0, + /** Landing company shortcode the account belongs to. Use this instead of landing_company_shortcode for wallets */ + landing_company_name: wallet.landing_company_name === 'maltainvest' ? 'malta' : wallet.landing_company_name, + is_malta_wallet: wallet.landing_company_name === 'malta', gradient_header_class: `wallet-header__${ wallet.is_virtual === 1 ? 'demo' : wallet.currency?.toLowerCase() }-bg${is_dark_mode_on ? '--dark' : ''}`, gradient_card_class: `wallet-card__${wallet.is_virtual === 1 ? 'demo' : wallet.currency?.toLowerCase()}-bg${ is_dark_mode_on ? '--dark' : '' }`, - landing_company_shortcode: wallet.landing_company_name, })); // Sort the wallets alphabetically by fiat, crypto, then virtual @@ -40,7 +48,7 @@ const useWalletList = () => { return (a.currency || 'USD').localeCompare(b.currency || 'USD'); }); - }, [data, is_crypto, is_dark_mode_on]); + }, [balance_data?.balance?.accounts, data?.authorize?.account_list, is_crypto, loginid, is_dark_mode_on]); return { ...reset, @@ -48,4 +56,4 @@ const useWalletList = () => { }; }; -export default useWalletList; +export default useWalletsList; diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts index aee12e66f4e6..c3412fa2c257 100644 --- a/packages/stores/src/mockStore.ts +++ b/packages/stores/src/mockStore.ts @@ -304,11 +304,14 @@ const mock = (): TStores & { is_mock: boolean } => { is_tour_open: false, selected_platform_type: '', available_platforms: [], - CFDs_restricted_countries: false, is_demo_low_risk: false, selected_region: 'All', getExistingAccounts: jest.fn(), available_dxtrade_accounts: [], + active_modal_tab: 'Deposit', + setWalletModalActiveTab: jest.fn(), + active_modal_wallet_id: '', + setWalletModalActiveWalletID: jest.fn(), }, menu: { attach: jest.fn(), diff --git a/packages/stores/types.ts b/packages/stores/types.ts index 421afed511d5..bfc2b27f1b3f 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -448,6 +448,10 @@ type TTradersHubStore = { selected_region: TRegionAvailability; getExistingAccounts: (platform: string, market_type: string) => AvailableAccount[]; available_dxtrade_accounts: AvailableAccount[]; + active_modal_tab?: 'Deposit' | 'Withdraw' | 'Transfer' | 'Transactions'; + setWalletModalActiveTab: (tab?: 'Deposit' | 'Withdraw' | 'Transfer' | 'Transactions') => void; + active_modal_wallet_id?: string; + setWalletModalActiveWalletID: (wallet_id?: string) => void; }; /**