diff --git a/package-lock.json b/package-lock.json index fd5856d0aec2..875c67c1eb15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@deriv-com/quill-tokens": "^2.0.4", "@deriv-com/quill-ui": "1.13.17", "@deriv-com/translations": "1.3.3", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv-com/utils": "^0.0.25", "@deriv/api-types": "1.0.172", "@deriv/deriv-api": "^1.0.15", diff --git a/packages/account/package.json b/packages/account/package.json index 0922a13d0cec..815eefb81afa 100644 --- a/packages/account/package.json +++ b/packages/account/package.json @@ -33,7 +33,7 @@ "@deriv-com/analytics": "1.5.9", "@deriv-com/translations": "1.3.3", "@deriv-com/utils": "^0.0.25", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv/api": "^1.0.0", "@deriv-com/quill-ui": "1.13.17", "@deriv/components": "^1.0.0", diff --git a/packages/cashier-v2/package.json b/packages/cashier-v2/package.json index d17d2073e893..595058a572fd 100644 --- a/packages/cashier-v2/package.json +++ b/packages/cashier-v2/package.json @@ -14,7 +14,7 @@ "start": "rimraf dist && npm run test && npm run serve" }, "dependencies": { - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv-com/utils": "^0.0.25", "@deriv/api-v2": "^1.0.0", "@deriv/integration": "^1.0.0", diff --git a/packages/cashier-v2/src/flows/FiatOnRamp/FiatOnRamp.tsx b/packages/cashier-v2/src/flows/FiatOnRamp/FiatOnRamp.tsx index 5890b04c0e79..86559dee4e0a 100644 --- a/packages/cashier-v2/src/flows/FiatOnRamp/FiatOnRamp.tsx +++ b/packages/cashier-v2/src/flows/FiatOnRamp/FiatOnRamp.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { useHistory } from 'react-router-dom'; import { useActiveAccount } from '@deriv/api-v2'; import { Dropdown, Loader, useDevice } from '@deriv-com/ui'; -import ChevronIcon from '../../assets/images/chevron-icon.svg'; import { PageContainer } from '../../components'; import { FiatOnRampModule } from '../../lib'; import { TRouteTypes } from '../../types'; @@ -30,7 +29,6 @@ const FiatOnRamp: React.FC = ({ path, routes }) => { {isMobile && ( } isFullWidth list={routeList} listHeight='sm' diff --git a/packages/cashier-v2/src/lib/PaymentAgent/components/PaymentAgentMethodsDropdown/PaymentAgentMethodsDropdown.tsx b/packages/cashier-v2/src/lib/PaymentAgent/components/PaymentAgentMethodsDropdown/PaymentAgentMethodsDropdown.tsx index 619dad943bd9..ce2e60ee354c 100644 --- a/packages/cashier-v2/src/lib/PaymentAgent/components/PaymentAgentMethodsDropdown/PaymentAgentMethodsDropdown.tsx +++ b/packages/cashier-v2/src/lib/PaymentAgent/components/PaymentAgentMethodsDropdown/PaymentAgentMethodsDropdown.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Dropdown } from '@deriv-com/ui'; -import ChevronIcon from '../../../../assets/images/chevron-icon.svg'; import { usePaymentAgentContext } from '../../provider'; import styles from './PaymentAgentMethodsDropdown.module.scss'; @@ -15,7 +14,6 @@ const PaymentAgentMethodsDropdown = () => { return (
} isFullWidth list={supportedPaymentMethodsList} listHeight='sm' diff --git a/packages/cfd/package.json b/packages/cfd/package.json index b083d4b95d05..197c9ea01d5a 100644 --- a/packages/cfd/package.json +++ b/packages/cfd/package.json @@ -85,7 +85,7 @@ }, "dependencies": { "@deriv-com/analytics": "1.5.9", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv-com/translations": "1.3.3", "@deriv-com/utils": "^0.0.25", "@deriv/account": "^1.0.0", diff --git a/packages/components/package.json b/packages/components/package.json index 2d5e5588c949..43f35aa51cd8 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -73,7 +73,7 @@ }, "dependencies": { "@contentpass/zxcvbn": "^4.4.3", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv/shared": "^1.0.0", "@deriv/stores": "^1.0.0", "@deriv/translations": "^1.0.0", diff --git a/packages/core/package.json b/packages/core/package.json index 18c1cbdf6a45..01e8df01b076 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -100,7 +100,7 @@ "@deriv-com/quill-tokens": "^2.0.4", "@deriv-com/quill-ui": "1.13.17", "@deriv-com/translations": "1.3.3", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv-com/utils": "^0.0.25", "@deriv/account": "^1.0.0", "@deriv/api": "^1.0.0", diff --git a/packages/reports/package.json b/packages/reports/package.json index 4e87fa81863e..3e8126fe7c3e 100644 --- a/packages/reports/package.json +++ b/packages/reports/package.json @@ -78,7 +78,7 @@ }, "dependencies": { "@deriv-com/analytics": "1.5.9", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.15", "@deriv/api-types": "1.0.172", diff --git a/packages/trader/package.json b/packages/trader/package.json index 1636fa600e22..23e1f1918e57 100644 --- a/packages/trader/package.json +++ b/packages/trader/package.json @@ -92,7 +92,7 @@ "@deriv-com/quill-tokens": "^2.0.4", "@deriv-com/quill-ui": "1.13.17", "@deriv-com/utils": "^0.0.25", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv/api-types": "1.0.172", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.15", diff --git a/packages/wallets/component-tests/crypto-payment-redirection.spec.tsx b/packages/wallets/component-tests/crypto-payment-redirection.spec.tsx index 72b328bb5bbf..db524a834d03 100644 --- a/packages/wallets/component-tests/crypto-payment-redirection.spec.tsx +++ b/packages/wallets/component-tests/crypto-payment-redirection.spec.tsx @@ -31,7 +31,7 @@ test.describe('Wallets - Crypto withdrawal', () => { await page.goto(`${baseURL}/`); await page.click('.wallets-textfield__field--listcard'); - await page.click('#downshift-0-item-1'); + await page.click('#wallets-listcard-dropdown__item-1'); }); test('render withdrawal form with all elements', async ({ baseURL, page }) => { diff --git a/packages/wallets/package.json b/packages/wallets/package.json index 00ff0bd924f6..fec753b18d8c 100644 --- a/packages/wallets/package.json +++ b/packages/wallets/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@deriv-com/analytics": "1.5.9", - "@deriv-com/ui": "^1.28.3", + "@deriv-com/ui": "1.29.3", "@deriv-com/utils": "^0.0.25", "@deriv/api-v2": "^1.0.0", "@deriv/integration": "^1.0.0", diff --git a/packages/wallets/src/components/Base/WalletDropdown/WalletDropdown.scss b/packages/wallets/src/components/Base/WalletDropdown/WalletDropdown.scss deleted file mode 100644 index 6733c84c91a4..000000000000 --- a/packages/wallets/src/components/Base/WalletDropdown/WalletDropdown.scss +++ /dev/null @@ -1,173 +0,0 @@ -.wallets-dropdown { - width: 100%; - position: relative; - cursor: pointer; - - &--disabled { - pointer-events: none; - - & label { - color: var(--system-light-5-active-background, #999); - } - } - - &__button { - all: unset; - right: 1.6rem; - transform: rotate(0); - transform-origin: 50%; - transition: transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1); - - &--active { - transform: rotate(180deg); - } - } - - &__content { - width: 100%; - background: var(--system-light-8-primary-background, #fff); - display: flex; - align-items: center; - - &--listcard { - border: unset; - background: transparent; - } - - .wallets-textfield__field { - cursor: pointer; - } - } - - &__field { - position: absolute; - inset: 0; - min-width: 0; /* this is required to reset input's default width */ - padding-left: 2rem; - display: flex; - flex-grow: 1; - font-family: inherit; - outline: 0; - font-size: 1.4rem; - background-color: transparent; - color: var(--system-light-2-general-text, #333); - transition: border-color 0.2s; - cursor: unset; - user-select: none; - &::selection { - background-color: transparent; - } - - &::placeholder { - color: transparent; - } - } - - &__field:placeholder-shown ~ &__label { - font-size: 1.4rem; - cursor: text; - top: 30%; - padding: 0; - } - - &__field:placeholder-shown ~ &__label--with-icon { - left: 4.4rem; - } - - label, - &__field:focus ~ &__label { - position: absolute; - display: block; - transition: 0.2s; - font-size: 1rem; - color: var(--system-light-3-less-prominent-text, #999); - background: var(--system-light-8-primary-background, #fff); - padding-inline: 0.4rem; - left: 1.6rem; - } - - &__field:focus ~ &__label { - color: var(--brand-blue, #85acb0); - } - - &__items { - position: absolute; - top: 4.8rem; - width: 100%; - display: flex; - flex-direction: column; - align-items: flex-start; - z-index: 2; - border-radius: 0.4rem; - background: var(--system-light-8-primary-background, #fff); - box-shadow: 0 3.2rem 6.4rem 0 rgba(14, 14, 14, 0.14); - overflow-y: auto; - - &--listcard { - width: 26.9rem; - padding: 0 0.8rem; - top: 3.2rem; - - & > .wallets-dropdown__item:last-child { - margin-bottom: 0.4rem; - } - } - - & > :first-child { - border-radius: 0.4rem 0.4rem 0 0; - } - - & > :last-child { - border-radius: 0 0 0.4rem 0.4rem; - } - - &--sm { - max-height: 22rem; - } - - &--md { - max-height: 42rem; - } - - &--lg { - max-height: 66rem; - } - } - - &__icon { - position: absolute; - left: 1.6rem; - width: 1.6rem; - height: 1.6rem; - } - - &__item { - padding: 10px 16px; - width: 100%; - z-index: 2; - - &:hover:not(&--active) { - cursor: pointer; - background: var(--system-light-6-hover-background, #e6e9e9); - } - - &--active { - background: var(--system-light-5-active-background, #d6dadb); - } - - &--listcard { - height: 6.4rem; - display: flex; - align-items: center; - justify-content: flex-start; - border-radius: 0.4rem; - } - } - - &__list-header { - padding: 1rem; - margin-bottom: 0.4rem; - border-bottom: 0.1rem solid #f2f3f4; - width: 100%; - } -} diff --git a/packages/wallets/src/components/Base/WalletDropdown/WalletDropdown.tsx b/packages/wallets/src/components/Base/WalletDropdown/WalletDropdown.tsx deleted file mode 100644 index 62e4bcdb29d7..000000000000 --- a/packages/wallets/src/components/Base/WalletDropdown/WalletDropdown.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import classNames from 'classnames'; -import { useCombobox } from 'downshift'; -import { LabelPairedChevronDownLgFillIcon } from '@deriv/quill-icons'; -import { TGenericSizes } from '../../../types'; -import reactNodeToString from '../../../utils/react-node-to-string'; -import { WalletText } from '../WalletText'; -import WalletTextField, { WalletTextFieldProps } from '../WalletTextField/WalletTextField'; -import './WalletDropdown.scss'; - -type TProps = { - disabled?: boolean; - errorMessage?: WalletTextFieldProps['errorMessage']; - icon?: React.ReactNode; - inputWidth?: string; - isRequired?: boolean; - label?: WalletTextFieldProps['label']; - list: { - listItem?: React.ReactNode; - text?: React.ReactNode; - value?: string; - }[]; - listHeader?: React.ReactNode; - listHeight?: Extract; - name: WalletTextFieldProps['name']; - onChange?: (inputValue: string) => void; - onSelect: (value: string) => void; - showListHeader?: boolean; - showMessageContainer?: boolean; - typeVariant?: 'listcard' | 'normal'; - value?: WalletTextFieldProps['value']; - variant?: 'comboBox' | 'prompt'; -}; - -const WalletDropdown: React.FC = ({ - disabled, - errorMessage, - icon = false, - inputWidth, - isRequired = false, - label, - list, - listHeader, - listHeight = 'md', - name, - onChange, - onSelect, - showListHeader = false, - showMessageContainer = true, - typeVariant = 'normal', - value, - variant = 'prompt', -}) => { - const [items, setItems] = useState(list); - const [hasSelected, setHasSelected] = useState(false); - const [shouldFilterList, setShouldFilterList] = useState(false); - const clearFilter = useCallback(() => { - setShouldFilterList(false); - setItems(list); - }, [list]); - const { closeMenu, getInputProps, getItemProps, getMenuProps, getToggleButtonProps, isOpen, openMenu } = - useCombobox({ - defaultSelectedItem: items.find(item => item.value === value) ?? null, - items, - itemToString(item) { - return item ? reactNodeToString(item.text) : ''; - }, - onInputValueChange({ inputValue }) { - onChange?.(inputValue ?? ''); - if (shouldFilterList) { - setItems( - list.filter(item => - reactNodeToString(item.text) - .toLowerCase() - .includes(inputValue?.toLowerCase() ?? '') - ) - ); - } - }, - onIsOpenChange({ isOpen }) { - if (!isOpen) { - clearFilter(); - } - }, - onSelectedItemChange({ selectedItem }) { - onSelect(selectedItem?.value ?? ''); - closeMenu(); - }, - }); - - const handleInputClick = useCallback(() => { - variant === 'comboBox' && setShouldFilterList(true); - - if (isOpen) { - closeMenu(); - } else { - openMenu(); - } - }, [closeMenu, isOpen, openMenu, variant]); - - useEffect(() => { - setItems(list); - }, [list]); - - return ( -
-
- setHasSelected(true)} - onKeyUp={() => setShouldFilterList(true)} - placeholder={reactNodeToString(label)} - readOnly={variant !== 'comboBox'} - renderLeftIcon={icon ? () => icon : undefined} - renderRightIcon={() => ( - - )} - showMessageContainer={showMessageContainer} - type='text' - typeVariant={typeVariant} - value={value} - {...getInputProps()} - /> -
-
    - {isOpen && showListHeader &&
    {listHeader}
    } - {isOpen && - items.map((item, index) => ( -
  • clearFilter()} - {...getItemProps({ index, item })} - > - {item?.listItem ? ( - item?.listItem - ) : ( - - {item.text} - - )} -
  • - ))} -
-
- ); -}; - -export default WalletDropdown; diff --git a/packages/wallets/src/components/Base/WalletDropdown/index.ts b/packages/wallets/src/components/Base/WalletDropdown/index.ts deleted file mode 100644 index 722c0d14170e..000000000000 --- a/packages/wallets/src/components/Base/WalletDropdown/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as WalletDropdown } from './WalletDropdown'; diff --git a/packages/wallets/src/components/Base/index.ts b/packages/wallets/src/components/Base/index.ts index 894b8605e0c5..b5004d83fdc0 100644 --- a/packages/wallets/src/components/Base/index.ts +++ b/packages/wallets/src/components/Base/index.ts @@ -11,7 +11,6 @@ export * from './WalletButton'; export * from './WalletButtonGroup'; export * from './WalletCheckbox'; export * from './WalletClipboard'; -export * from './WalletDropdown'; export * from './WalletLink'; export * from './WalletPasswordFieldLazy'; export * from './WalletText'; diff --git a/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.scss b/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.scss index 0a4ea9e20ad8..bd6e2d79b54a 100644 --- a/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.scss +++ b/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.scss @@ -1,11 +1,78 @@ -.wallets-list-card-dropdown { - &__item { - display: flex; - gap: 1.6rem; +.wallets-listcard-dropdown { + position: relative; + cursor: pointer; + + .wallets-textfield__field { + cursor: pointer; + } + + &__list { + &-item { + display: flex; + gap: 1.6rem; + } &-content { display: flex; flex-direction: column; } } + + &__button { + all: unset; + right: 1.6rem; + transform: rotate(0); + transform-origin: 50%; + transition: transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1); + + &--active { + transform: rotate(180deg); + } + } + + &__items { + position: absolute; + display: flex; + flex-direction: column; + align-items: flex-start; + z-index: 2; + border-radius: 0.4rem; + background: var(--system-light-8-primary-background, #fff); + box-shadow: 0 3.2rem 6.4rem 0 var(--shadow-menu, rgba(0, 0, 0, 0.16)); + overflow-y: auto; + width: 26.9rem; + padding: 0 0.8rem; + top: 3.2rem; + + &-header { + padding: 1rem; + margin-bottom: 0.4rem; + border-bottom: 0.1rem solid var(--general-section-1, #f2f3f4); + width: 100%; + } + + & > :last-child { + margin-bottom: 0.4rem; + } + } + + &__item { + padding: 1rem 1.6rem; + width: 100%; + z-index: 2; + height: 6.4rem; + display: flex; + align-items: center; + justify-content: flex-start; + border-radius: 0.4rem; + + &:hover:not(&--active) { + cursor: pointer; + background: var(--system-light-6-hover-background, #e6e9e9); + } + + &--active { + background: var(--system-light-5-active-background, #d6dadb); + } + } } diff --git a/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.tsx b/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.tsx index 10fd92c2796a..9b2740cef9b1 100644 --- a/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.tsx +++ b/packages/wallets/src/components/WalletListCardDropdown/WalletListCardDropdown.tsx @@ -1,24 +1,36 @@ -import React, { ComponentProps, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import classNames from 'classnames'; import { Trans, useTranslation } from 'react-i18next'; +import { useEventListener, useOnClickOutside } from 'usehooks-ts'; import { useActiveWalletAccount, useWalletAccountsList } from '@deriv/api-v2'; import { displayMoney } from '@deriv/api-v2/src/utils'; +import { LabelPairedChevronDownLgFillIcon } from '@deriv/quill-icons'; import useWalletAccountSwitcher from '../../hooks/useWalletAccountSwitcher'; import { THooks, TSubscribedBalance } from '../../types'; -import { WalletDropdown, WalletText } from '../Base'; +import reactNodeToString from '../../utils/react-node-to-string'; +import { WalletText, WalletTextField } from '../Base'; import { WalletCurrencyIcon } from '../WalletCurrencyIcon'; import './WalletListCardDropdown.scss'; -type WalletList = ComponentProps['list'] | undefined; +type WalletList = { + currency: THooks.WalletAccountsList['currency']; + currencyConfig: THooks.WalletAccountsList['currency_config']; + loginid: THooks.WalletAccountsList['loginid']; + text: React.ReactNode; +}[]; const WalletListCardDropdown: React.FC = ({ balance }) => { const { data: wallets } = useWalletAccountsList(); const { data: activeWallet } = useActiveWalletAccount(); const switchWalletAccount = useWalletAccountSwitcher(); const { t } = useTranslation(); - const { data: balanceData } = balance; + const dropdownRef = useRef(null); - const [inputWidth, setInputWidth] = useState('auto'); + const { data: balanceData } = balance; const loginId = activeWallet?.loginid; + const [inputWidth, setInputWidth] = useState('auto'); + const [isOpen, setIsOpen] = useState(false); + const [selectedText, setSelectedText] = useState(''); const generateTitleText = useCallback( (wallet: THooks.WalletAccountsList) => { @@ -27,66 +39,118 @@ const WalletListCardDropdown: React.FC = ({ balance }) => { [t] ); - useEffect(() => { - const selectedWallet = wallets?.find(wallet => wallet.loginid === loginId); - if (selectedWallet) { - const selectedTextWidth = generateTitleText(selectedWallet).length; - setInputWidth(`${selectedTextWidth * 10 - 20}px`); + const walletList: WalletList = useMemo(() => { + return ( + wallets + ?.filter(wallet => !wallet.is_virtual) + .map(wallet => ({ + currency: wallet.currency, + currencyConfig: wallet.currency_config, + loginid: wallet.loginid, + text: generateTitleText(wallet), + })) ?? [] + ); + }, [generateTitleText, wallets]); + + useOnClickOutside(dropdownRef, () => { + setIsOpen(false); + }); + + useEventListener('keydown', (event: KeyboardEvent) => { + if (event.key === 'Escape') { + setIsOpen(false); } - }, [generateTitleText, wallets, loginId]); + }); - const walletList: WalletList = useMemo(() => { - return wallets - ?.filter(wallet => !wallet.is_virtual) - ?.map(wallet => ({ - listItem: ( -
- -
- - - - - - -
-
- ), - text: generateTitleText(wallet), - value: wallet.loginid, - })); - }, [balanceData?.accounts, generateTitleText, wallets]); + const handleInputClick = useCallback(() => { + setIsOpen(prevIsOpen => !prevIsOpen); + }, []); + + const handleItemClick = (value: string, text: string) => { + switchWalletAccount(value); + setSelectedText(text); + setIsOpen(false); + }; + + useEffect(() => { + if (loginId && wallets) { + const selectedWallet = wallets.find(wallet => wallet.loginid === loginId); + if (selectedWallet) { + const titleText = generateTitleText(selectedWallet); + setSelectedText(titleText); + setInputWidth(`${titleText.length * 10 - 20}px`); + } + } + }, [generateTitleText, loginId, wallets]); return ( -
- {wallets && ( - - - - } - name='wallets-list-card-dropdown' - onSelect={selectedItem => { - switchWalletAccount(selectedItem); - }} - showListHeader - showMessageContainer={false} - typeVariant='listcard' - value={loginId} - /> + + {walletList.length > 0 && ( +
+ ( + + )} + showMessageContainer={false} + type='text' + typeVariant='listcard' + value={selectedText} + /> + {isOpen && ( +
    +
    + + + +
    + {walletList.map((wallet, index) => ( +
  • handleItemClick(wallet.loginid, reactNodeToString(wallet.text))} + > +
    + +
    + + + + + + +
    +
    +
  • + ))} +
+ )} +
)} -
+ ); }; diff --git a/packages/wallets/src/components/WalletListCardDropdown/__tests__/WalletListCardDropdown.spec.tsx b/packages/wallets/src/components/WalletListCardDropdown/__tests__/WalletListCardDropdown.spec.tsx index 0f6decf5eeeb..bb5c5be9f757 100644 --- a/packages/wallets/src/components/WalletListCardDropdown/__tests__/WalletListCardDropdown.spec.tsx +++ b/packages/wallets/src/components/WalletListCardDropdown/__tests__/WalletListCardDropdown.spec.tsx @@ -1,36 +1,21 @@ import React from 'react'; -import { useWalletAccountsList } from '@deriv/api-v2'; +import { useActiveWalletAccount, useWalletAccountsList } from '@deriv/api-v2'; import { fireEvent, render, screen } from '@testing-library/react'; import { TSubscribedBalance } from '../../../types'; import WalletListCardDropdown from '../WalletListCardDropdown'; +import '@testing-library/jest-dom'; -const mockSwitchAccount = jest.fn(); jest.mock('@deriv/api-v2', () => ({ - useActiveWalletAccount: jest.fn(() => ({ - data: { - loginid: '1234567', - }, - })), - useAuthorize: jest.fn(() => ({ - switchAccount: mockSwitchAccount, - })), - useBalanceSubscription: jest.fn(() => ({ - data: {}, - })), - useWalletAccountsList: jest.fn(() => ({ - data: [ - { - currency: 'USD', - display_balance: '1000.00', - loginid: '1234567', - }, - { - currency: 'BTC', - display_balance: '1.0000000', - loginid: '7654321', - }, - ], - })), + ...jest.requireActual('@deriv/api-v2'), + useActiveWalletAccount: jest.fn(), + useWalletAccountsList: jest.fn(), +})); + +const mockSwitchAccount = jest.fn(); + +jest.mock('../../../hooks/useWalletAccountSwitcher', () => ({ + __esModule: true, + default: jest.fn(() => mockSwitchAccount), })); const mockBalanceData: TSubscribedBalance['balance'] = { @@ -66,30 +51,104 @@ const mockBalanceData: TSubscribedBalance['balance'] = { describe('WalletListCardDropdown', () => { beforeEach(() => { jest.clearAllMocks(); + (useActiveWalletAccount as jest.Mock).mockReturnValue({ + data: { + loginid: '1234567', + }, + }); + (useWalletAccountsList as jest.Mock).mockReturnValue({ + data: [ + { + currency: 'USD', + currency_config: { fractional_digits: 2 }, + display_balance: '1000.00', + is_virtual: false, + loginid: '1234567', + }, + { + currency: 'BTC', + currency_config: { fractional_digits: 8 }, + display_balance: '1.0000000', + is_virtual: false, + loginid: '7654321', + }, + ], + }); }); - it('should render with the correct data', async () => { + it('renders with correct default data', () => { render(); + + expect(screen.getByDisplayValue('USD Wallet')).toBeInTheDocument(); }); - it('should switch to selected account on click of the list item', async () => { + it('switches to selected account on click of the list item', () => { render(); - expect(screen.getByDisplayValue('USD Wallet')).toBeInTheDocument(); + const input = screen.getByDisplayValue('USD Wallet'); + fireEvent.click(input); - fireEvent.click(screen.getByDisplayValue('USD Wallet')); expect(screen.getByText('USD Wallet')).toBeInTheDocument(); expect(screen.getByText('BTC Wallet')).toBeInTheDocument(); - fireEvent.click(screen.getByText('BTC Wallet')); + + const btcWallet = screen.getByText('BTC Wallet'); + fireEvent.click(btcWallet); expect(mockSwitchAccount).toHaveBeenCalledWith('7654321'); + expect(screen.getByDisplayValue('BTC Wallet')).toBeInTheDocument(); }); - it('should render dropdown without crashing when unable to fetch wallets', async () => { - (useWalletAccountsList as jest.Mock).mockReturnValueOnce({ data: [] }); + it('displays correct wallet details with balance in items list', () => { + render(); + + const input = screen.getByDisplayValue('USD Wallet'); + fireEvent.click(input); + expect(screen.getByText('BTC Wallet')).toBeInTheDocument(); + expect(screen.getByText('1.00000000 BTC')).toBeInTheDocument(); + expect(screen.getByText('USD Wallet')).toBeInTheDocument(); + expect(screen.getByText('1,000.00 USD')).toBeInTheDocument(); + }); + + it('handles case where no active wallet is set', () => { + (useActiveWalletAccount as jest.Mock).mockReturnValue({ + data: null, + }); + + render(); + + expect(screen.queryByDisplayValue('USD Wallet')).not.toBeInTheDocument(); + }); + + it('handles case where wallets data is empty', () => { + (useWalletAccountsList as jest.Mock).mockReturnValue({ data: [] }); + + render(); + + expect(screen.queryByDisplayValue('USD Wallet')).not.toBeInTheDocument(); + }); + + it('closes the dropdown when clicking outside', () => { render(); - expect(screen.getByDisplayValue('')).toBeInTheDocument(); + const input = screen.getByDisplayValue('USD Wallet'); + fireEvent.click(input); + + expect(screen.getByText('BTC Wallet')).toBeInTheDocument(); + + fireEvent.mouseDown(document); + expect(screen.queryByText('BTC Wallet')).not.toBeInTheDocument(); + }); + + it('closes the dropdown when pressing the escape key', () => { + render(); + + const input = screen.getByDisplayValue('USD Wallet'); + fireEvent.click(input); + + expect(screen.getByText('BTC Wallet')).toBeInTheDocument(); + + fireEvent.keyDown(document, { key: 'Escape' }); + expect(screen.queryByText('BTC Wallet')).not.toBeInTheDocument(); }); }); diff --git a/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.scss b/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.scss index 756d49a26e21..5244780939dc 100644 --- a/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.scss +++ b/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.scss @@ -35,6 +35,11 @@ } } } + + &__dropdown { + padding-bottom: 2rem; + } + &__inline { display: flex; align-items: flex-start; @@ -59,12 +64,5 @@ @include mobile { width: 100%; } - - & .wallets-dropdown, - & .wallets-textfield { - @include desktop { - max-width: 84rem; - } - } } } diff --git a/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.tsx b/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.tsx index 83020e2ec2fe..416e92f9e404 100644 --- a/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.tsx +++ b/packages/wallets/src/features/accounts/screens/AddressSection/AddressSection.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { useSettings, useStatesList } from '@deriv/api-v2'; +import { Dropdown } from '@deriv-com/ui'; import { FlowTextField, useFlow } from '../../../../components'; -import { InlineMessage, WalletDropdown, WalletText } from '../../../../components/Base'; +import { InlineMessage, WalletText } from '../../../../components/Base'; import { addressFirstLineValidator, addressSecondLineValidator, @@ -49,14 +50,18 @@ const AddressSection: React.FC = () => { name='townCityLine' validationSchema={cityValidator} /> - setFormValues('stateProvinceDropdownLine', selectedItem)} - value={getSettings?.address_state ?? ''} - /> +
+ setFormValues('stateProvinceDropdownLine', selectedItem)} + value={getSettings?.address_state ?? ''} + /> +
{
Identity verification
- { - setFormValues('documentType', textToValueMapper[inputValue]); - }} - onSelect={selectedItem => { - setFormValues('documentType', selectedItem); - }} - value={formValues?.documentType} - variant='comboBox' - /> +
+ { + setFormValues('documentType', textToValueMapper[inputValue]); + }} + onSelect={selectedItem => { + setFormValues('documentType', selectedItem); + }} + value={formValues?.documentType} + variant='comboBox' + /> +
{
- ({ - text: residence.text as ReactNode, - value: residence.value ?? '', - }))} - listHeight='sm' - name='wallets-personal-details__dropdown-citizenship' - onSelect={selectedItem => setFormValues('citizenship', selectedItem)} - value={getSettings?.citizen ?? formValues?.citizenship} - variant='comboBox' - /> - ({ - text: residence.text as ReactNode, - value: residence.value ?? '', - }))} - listHeight='sm' - name='wallets-personal-details__dropdown-pob' - onSelect={selectedItem => setFormValues('placeOfBirth', selectedItem)} - value={getSettings?.place_of_birth ?? ''} - variant='comboBox' - /> - ({ - text: residence.text as ReactNode, - value: residence.value ?? '', - }))} - listHeight='sm' - name='wallets-personal-details__dropdown-tax-residence' - onChange={inputValue => { - residenceList.forEach(residence => { - if (residence.text?.toLowerCase() === inputValue.toLowerCase()) { - setFormValues('taxResidence', residence.value); - } - }); - }} - onSelect={selectedItem => { - setFormValues('taxResidence', selectedItem); - }} - value={getSettings?.tax_residence ?? formValues?.taxResidence} - variant='comboBox' - /> +
+ ({ + text: residence.text, + value: residence.value ?? '', + }))} + listHeight='sm' + name='wallets-personal-details__dropdown-citizenship' + onSelect={selectedItem => setFormValues('citizenship', selectedItem)} + value={formValues?.citizenship ?? getSettings?.citizen} + variant='comboBox' + /> +
+
+ ({ + text: residence.text, + value: residence.value ?? '', + }))} + listHeight='sm' + name='wallets-personal-details__dropdown-pob' + onSelect={selectedItem => setFormValues('placeOfBirth', selectedItem)} + value={getSettings?.place_of_birth ?? ''} + variant='comboBox' + /> +
+
+ ({ + text: residence.text, + value: residence.value ?? '', + }))} + listHeight='sm' + name='wallets-personal-details__dropdown-tax-residence' + onSearch={inputValue => { + residenceList.forEach(residence => { + if (residence.text?.toLowerCase() === inputValue.toLowerCase()) { + setFormValues('taxResidence', residence.value); + } + }); + }} + onSelect={selectedItem => { + setFormValues('taxResidence', selectedItem); + }} + value={formValues?.taxResidence ?? getSettings?.tax_residence} + variant='comboBox' + /> +
{ label='Tax identification number*' name='taxIdentificationNumber' /> - setFormValues('accountOpeningReason', selectedItem)} - value={getSettings?.account_opening_reason ?? formValues?.accountOpeningReason} - variant='comboBox' - /> +
+ setFormValues('accountOpeningReason', selectedItem)} + value={formValues?.accountOpeningReason ?? getSettings?.account_opening_reason} + variant='comboBox' + /> +
)} diff --git a/packages/wallets/src/features/cashier/modules/Transactions/Transactions.scss b/packages/wallets/src/features/cashier/modules/Transactions/Transactions.scss index bd45024a9c4f..c5f749d2b8a0 100644 --- a/packages/wallets/src/features/cashier/modules/Transactions/Transactions.scss +++ b/packages/wallets/src/features/cashier/modules/Transactions/Transactions.scss @@ -8,26 +8,22 @@ // ignore CashierContent top padding margin-top: -4.8rem; + @include mobile { margin-top: -2.4rem; } - // adjust filter dropdown width - .wallets-dropdown { + &__dropdown { max-width: 19.5rem; + @include mobile { max-width: unset; } - } - - // don't need details/error message - .wallets-textfield__message-container { - display: none; - } - // don't show text selection - .wallets-textfield__field::selection { - background: none; + /* stylelint-disable-next-line selector-class-pattern */ + .deriv-dropdown__items--xs { + max-height: unset; + } } &__header { @@ -42,6 +38,10 @@ z-index: 2; background-color: var(--system-light-8-primary-background, #fff); + @include desktop { + height: 8rem; + } + @include mobile { top: -2.4rem; padding: 1.6rem 0; diff --git a/packages/wallets/src/features/cashier/modules/Transactions/Transactions.tsx b/packages/wallets/src/features/cashier/modules/Transactions/Transactions.tsx index 1b6b3bfa4e0b..0dd115074d65 100644 --- a/packages/wallets/src/features/cashier/modules/Transactions/Transactions.tsx +++ b/packages/wallets/src/features/cashier/modules/Transactions/Transactions.tsx @@ -3,7 +3,8 @@ import classNames from 'classnames'; import { useHistory } from 'react-router-dom'; import { useActiveWalletAccount, useCurrencyConfig } from '@deriv/api-v2'; import { LegacyFilter1pxIcon } from '@deriv/quill-icons'; -import { ToggleSwitch, WalletDropdown, WalletText } from '../../../../components'; +import { Dropdown } from '@deriv-com/ui'; +import { ToggleSwitch, WalletText } from '../../../../components'; import useDevice from '../../../../hooks/useDevice'; import { TransactionsCompleted, TransactionsCompletedDemoResetBalance, TransactionsPending } from './components'; import './Transactions.scss'; @@ -86,14 +87,23 @@ const Transactions = () => { setIsPendingActive(!isPendingActive)} value={isPendingActive} /> )} - } - label='Filter' - list={filterOptionsList} - name='wallets-transactions__dropdown' - onSelect={value => setFilterValue(value)} - value={filterValue} - /> +
+ } + isFullWidth + label='Filter' + list={filterOptionsList} + name='wallets-transactions__dropdown' + onSelect={value => { + if (typeof value === 'string') { + setFilterValue(value); + } + }} + value={filterValue} + variant='comboBox' + /> +
{isPendingActive && ( diff --git a/packages/wallets/src/features/cashier/modules/Transactions/__tests__/Transactions.spec.tsx b/packages/wallets/src/features/cashier/modules/Transactions/__tests__/Transactions.spec.tsx index e79466843d22..c2afec9d1508 100644 --- a/packages/wallets/src/features/cashier/modules/Transactions/__tests__/Transactions.spec.tsx +++ b/packages/wallets/src/features/cashier/modules/Transactions/__tests__/Transactions.spec.tsx @@ -87,7 +87,7 @@ describe('Transactions', () => { render(); expect(screen.getByText('Transactions Completed')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('dt_wallets_textfield_icon_right')); + fireEvent.click(screen.getByTestId('dt_wallets_transactions_dropdown')); fireEvent.click(screen.getByText('Reset balance')); expect(screen.getByRole('combobox')).toHaveValue('Reset balance'); expect(screen.getByText('Transactions Completed Demo Reset Balance')).toBeInTheDocument(); @@ -100,19 +100,19 @@ describe('Transactions', () => { (useCurrencyConfig as jest.Mock).mockReturnValue({ isLoading: false }); render(); - fireEvent.click(screen.getByTestId('dt_wallets_textfield_icon_right')); + fireEvent.click(screen.getByTestId('dt_wallets_transactions_dropdown')); fireEvent.click(screen.getByText('Deposit')); expect(screen.getByRole('combobox')).toHaveValue('Deposit'); - fireEvent.click(screen.getByTestId('dt_wallets_textfield_icon_right')); + fireEvent.click(screen.getByTestId('dt_wallets_transactions_dropdown')); fireEvent.click(screen.getByText('Withdrawal')); expect(screen.getByRole('combobox')).toHaveValue('Withdrawal'); - fireEvent.click(screen.getByTestId('dt_wallets_textfield_icon_right')); + fireEvent.click(screen.getByTestId('dt_wallets_transactions_dropdown')); fireEvent.click(screen.getByText('Transfer')); expect(screen.getByRole('combobox')).toHaveValue('Transfer'); - fireEvent.click(screen.getByTestId('dt_wallets_textfield_icon_right')); + fireEvent.click(screen.getByTestId('dt_wallets_transactions_dropdown')); fireEvent.click(screen.getByText('All')); expect(screen.getByRole('combobox')).toHaveValue('All'); @@ -133,7 +133,7 @@ describe('Transactions', () => { render(); expect(screen.getByText('Transactions Pending')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('dt_wallets_textfield_icon_right')); + fireEvent.click(screen.getByTestId('dt_wallets_transactions_dropdown')); fireEvent.click(screen.getByText('All')); expect(screen.getByRole('combobox')).toHaveValue('All'); @@ -154,7 +154,7 @@ describe('Transactions', () => { render(); expect(screen.getByText('Transactions Completed')).toBeInTheDocument(); - fireEvent.click(screen.getByTestId('dt_wallets_textfield_icon_right')); + fireEvent.click(screen.getByTestId('dt_wallets_transactions_dropdown')); fireEvent.click(screen.getByText('All')); expect(screen.getByRole('combobox')).toHaveValue('All'); diff --git a/packages/wallets/src/features/cashier/modules/Transfer/components/TransferFormDropdown/TransferFormDropdown.tsx b/packages/wallets/src/features/cashier/modules/Transfer/components/TransferFormDropdown/TransferFormDropdown.tsx index 7d6eef004342..86b9ceff810b 100644 --- a/packages/wallets/src/features/cashier/modules/Transfer/components/TransferFormDropdown/TransferFormDropdown.tsx +++ b/packages/wallets/src/features/cashier/modules/Transfer/components/TransferFormDropdown/TransferFormDropdown.tsx @@ -109,6 +109,7 @@ const TransferFormDropdown: React.FC = ({ fieldName, mobileAccountsListR return (