diff --git a/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.stories.tsx b/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.stories.tsx index 338b4daeb53..76b0d342553 100644 --- a/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.stories.tsx +++ b/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.stories.tsx @@ -6,7 +6,7 @@ import { configureStore } from '@reduxjs/toolkit'; import { Box } from '@metamask/design-system-react-native'; import AccountCell from '.'; import initialBackgroundState from '../../../../util/test/initial-background-state.json'; -import { AvatarAccountType } from '../../../components/Avatars/Avatar/variants/AvatarAccount'; +import { AvatarAccountType } from '../avatarAccountVariant'; interface StoryArgs { accountGroup: AccountGroupObject; diff --git a/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.test.tsx b/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.test.tsx index ef114ceff86..0f641986cb1 100644 --- a/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.test.tsx +++ b/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.test.tsx @@ -9,12 +9,14 @@ import { createMockState, createMockWallet, } from '../test-utils'; -import { AvatarAccountType } from '../../../components/Avatars/Avatar'; -import { Maskicon } from '@metamask/design-system-react-native'; -import JazzIcon from 'react-native-jazzicon'; -import { Image as RNImage } from 'react-native'; +import { + Blockies, + Jazzicon, + Maskicon, +} from '@metamask/design-system-react-native'; import { AccountCellIds } from './AccountCell.testIds'; import { backgroundState } from '../../../../util/test/initial-root-state'; +import { AvatarAccountType } from '../avatarAccountVariant'; // Configurable mock balance for selector const mockBalance: { value: number; currency: string } = { @@ -159,18 +161,18 @@ describe('AccountCell', () => { expect(UNSAFE_getByType(Maskicon)).toBeTruthy(); }); - it('renders JazzIcon AvatarAccount when avatarAccountType is JazzIcon', () => { + it('renders Jazzicon AvatarAccount when avatarAccountType is JazzIcon', () => { const { UNSAFE_getByType } = renderAccountCell({ avatarAccountType: AvatarAccountType.JazzIcon, }); - expect(UNSAFE_getByType(JazzIcon)).toBeTruthy(); + expect(UNSAFE_getByType(Jazzicon)).toBeTruthy(); }); it('renders Blockies AvatarAccount when avatarAccountType is Blockies', () => { const { UNSAFE_getByType } = renderAccountCell({ avatarAccountType: AvatarAccountType.Blockies, }); - expect(UNSAFE_getByType(RNImage)).toBeTruthy(); + expect(UNSAFE_getByType(Blockies)).toBeTruthy(); }); it('calls onSelectAccount when account name is pressed', () => { diff --git a/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.tsx b/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.tsx index 9560ea5811b..96bc0c19114 100644 --- a/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.tsx +++ b/app/component-library/components-temp/MultichainAccounts/AccountCell/AccountCell.tsx @@ -1,30 +1,36 @@ import { AccountGroupObject } from '@metamask/account-tree-controller'; import React, { useCallback, useMemo } from 'react'; -import { type ImageSourcePropType, TouchableOpacity, View } from 'react-native'; +import { TouchableOpacity, View } from 'react-native'; import { useSelector } from 'react-redux'; import { useNavigation } from '@react-navigation/native'; +import { + AvatarAccount, + AvatarAccountSize, + AvatarNetwork, + AvatarNetworkSize, + FontWeight, + Icon, + IconColor, + IconName, + IconSize, + SensitiveText, + SensitiveTextLength, + Text, + TextColor, + TextVariant, +} from '@metamask/design-system-react-native'; import { useStyles } from '../../../hooks'; import styleSheet from './AccountCell.styles'; -import Text, { TextColor, TextVariant } from '../../../components/Texts/Text'; -import SensitiveText, { - SensitiveTextLength, -} from '../../../components/Texts/SensitiveText'; import { Box } from '../../../../components/UI/Box/Box'; import { AlignItems, FlexDirection, JustifyContent, } from '../../../../components/UI/Box/box.types'; -import Icon, { IconName, IconSize } from '../../../components/Icons/Icon'; import { AccountCellIds } from './AccountCell.testIds'; import { selectBalanceByAccountGroup } from '../../../../selectors/assets/balances'; import { formatWithThreshold } from '../../../../util/assets'; import I18n from '../../../../../locales/i18n'; -import AvatarAccount, { - AvatarAccountType, -} from '../../../components/Avatars/Avatar/variants/AvatarAccount'; -import Avatar, { AvatarVariant } from '../../../components/Avatars/Avatar'; -import { AvatarSize } from '../../../components/Avatars/Avatar/Avatar.types'; import { selectIconSeedAddressByAccountGroupId, selectInternalAccountByAccountGroupAndScope, @@ -34,10 +40,14 @@ import { createAccountGroupDetailsNavigationDetails } from '../../../../componen import { getNetworkImageSource } from '../../../../util/networks'; import { formatChainIdToCaip } from '@metamask/bridge-controller'; import { renderShortAddress } from '../../../../util/address'; +import { + type AccountAvatarVariant, + getAvatarAccountVariant, +} from '../avatarAccountVariant'; interface AccountCellProps { accountGroup: AccountGroupObject; - avatarAccountType: AvatarAccountType; + avatarAccountType: AccountAvatarVariant; hideMenu?: boolean; startAccessory?: React.ReactNode; endContainer?: React.ReactNode; @@ -49,7 +59,7 @@ type BalanceEndContainerProps = Pick< AccountCellProps, 'accountGroup' | 'hideMenu' | 'onSelectAccount' > & { - networkImageSource?: ImageSourcePropType; + networkImageSource?: React.ComponentProps['src']; }; const BalanceEndContainer = ({ @@ -89,8 +99,9 @@ const BalanceEndContainer = ({ {networkImageSource && ( - )} @@ -118,7 +128,7 @@ const BalanceEndContainer = ({ )} @@ -136,6 +146,7 @@ const AccountCell = ({ onSelectAccount, }: AccountCellProps) => { const { styles } = useStyles(styleSheet, {}); + const avatarAccountVariant = getAvatarAccountVariant(avatarAccountType); const selectEvmAddress = useMemo( () => selectIconSeedAddressByAccountGroupId(accountGroup.id), @@ -170,16 +181,17 @@ const AccountCell = ({ {startAccessory} diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.test.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.test.tsx index dd99cb5b65a..e5ca4737379 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.test.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.test.tsx @@ -8,9 +8,9 @@ import { createMockState, createMockWallet, } from '../../test-utils'; -import { AvatarAccountType } from '../../../../components/Avatars/Avatar'; +import { AvatarAccountType } from '../../avatarAccountVariant'; import { RootState } from '../../../../../reducers'; -import { CHECKBOX_ICON_TESTID } from '../../../../components/Checkbox/Checkbox.constants'; +import { ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID } from './AccountListCell.testIds'; const mockNavigate = jest.fn(); @@ -169,7 +169,7 @@ describe('AccountListCell', () => { expect( getByTestId(`account-list-cell-checkbox-${mockAccountGroup.id}`), ).toBeTruthy(); - expect(getByTestId(CHECKBOX_ICON_TESTID)).toBeTruthy(); + expect(getByTestId(ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID)).toBeTruthy(); }); it('renders unchecked checkbox when isSelected is false', () => { @@ -188,7 +188,9 @@ describe('AccountListCell', () => { expect( getByTestId(`account-list-cell-checkbox-${mockAccountGroup.id}`), ).toBeTruthy(); - expect(queryByTestId(CHECKBOX_ICON_TESTID)).toBeFalsy(); + expect( + queryByTestId(ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID), + ).toBeFalsy(); }); it('calls onSelectAccount when checkbox is pressed', () => { @@ -246,7 +248,7 @@ describe('AccountListCell', () => { expect( getByTestId(`account-list-cell-checkbox-${mockAccountGroup.id}`), ).toBeTruthy(); - expect(getByTestId(CHECKBOX_ICON_TESTID)).toBeTruthy(); + expect(getByTestId(ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID)).toBeTruthy(); expect(getByText('Test Account')).toBeTruthy(); }); }); diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.testIds.ts b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.testIds.ts index 9f12c726403..8ada8d85ce7 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.testIds.ts +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.testIds.ts @@ -1,3 +1,6 @@ export const ACCOUNT_LIST_CELL_TEST_IDS = { ACCOUNT_LIST_CELL: 'account-list-cell-checkbox-', } as const; + +export const ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID = + 'account-list-cell-checkbox-icon'; diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.tsx index 13bff7a533b..951310e0644 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.tsx @@ -1,12 +1,15 @@ import React, { memo, useCallback } from 'react'; import { View } from 'react-native'; +import { Checkbox } from '@metamask/design-system-react-native'; import { useStyles } from '../../../../hooks'; import AccountCell from '../../AccountCell'; import createStyles from '../MultichainAccountSelectorList.styles'; import { AccountListCellProps } from './AccountListCell.types'; -import Checkbox from '../../../../components/Checkbox'; -import { ACCOUNT_LIST_CELL_TEST_IDS } from './AccountListCell.testIds'; +import { + ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID, + ACCOUNT_LIST_CELL_TEST_IDS, +} from './AccountListCell.testIds'; const AccountListCell = memo( ({ @@ -32,10 +35,17 @@ const AccountListCell = memo( - + + ) : undefined } diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.types.ts b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.types.ts index 7d07a74d1a3..3c215f2cfbc 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.types.ts +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListCell/AccountListCell.types.ts @@ -1,9 +1,9 @@ import { AccountGroupObject } from '@metamask/account-tree-controller'; -import { AvatarAccountType } from '../../../../components/Avatars/Avatar/variants/AvatarAccount'; +import type { AccountAvatarVariant } from '../../avatarAccountVariant'; export interface AccountListCellProps { accountGroup: AccountGroupObject; - avatarAccountType: AvatarAccountType; + avatarAccountType: AccountAvatarVariant; isSelected: boolean; onSelectAccount: (accountGroup: AccountGroupObject) => void; showCheckbox?: boolean; diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListHeader/AccountListHeader.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListHeader/AccountListHeader.tsx index 4c66899ba39..ce54fd76963 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListHeader/AccountListHeader.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/AccountListHeader/AccountListHeader.tsx @@ -1,11 +1,13 @@ import React, { memo } from 'react'; import { View } from 'react-native'; - -import { useStyles } from '../../../../hooks'; -import Text, { +import { + FontWeight, + Text, TextColor, TextVariant, -} from '../../../../components/Texts/Text'; +} from '@metamask/design-system-react-native'; + +import { useStyles } from '../../../../hooks'; import createStyles from '../MultichainAccountSelectorList.styles'; import { AccountListHeaderProps } from './AccountListHeader.types'; @@ -16,8 +18,9 @@ const AccountListHeader = memo( return ( {title} diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.test.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.test.tsx index c286affe85a..bc852071d15 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.test.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.test.tsx @@ -3,6 +3,7 @@ import { fireEvent } from '@testing-library/react-native'; import ExternalAccountCell from './ExternalAccountCell'; import renderWithProvider from '../../../../../util/test/renderWithProvider'; import { strings } from '../../../../../../locales/i18n'; +import { EXTERNAL_ACCOUNT_CELL_TEST_IDS } from './ExternalAccountCell.testIds'; // Mock the settings selector jest.mock('../../../../../selectors/settings', () => ({ @@ -75,7 +76,9 @@ describe('ExternalAccountCell', () => { ); // Network avatar should not be rendered - expect(queryByTestId('network-avatar-image')).toBeFalsy(); + expect( + queryByTestId(EXTERNAL_ACCOUNT_CELL_TEST_IDS.NETWORK_AVATAR), + ).toBeFalsy(); }); it('renders with network avatar when chainId is provided', () => { @@ -88,7 +91,9 @@ describe('ExternalAccountCell', () => { ); // Network avatar should be rendered - expect(getByTestId('network-avatar-image')).toBeTruthy(); + expect( + getByTestId(EXTERNAL_ACCOUNT_CELL_TEST_IDS.NETWORK_AVATAR), + ).toBeTruthy(); }); it('renders account avatar', () => { @@ -205,7 +210,9 @@ describe('ExternalAccountCell', () => { />, ); - expect(getByTestId('network-avatar-image')).toBeTruthy(); + expect( + getByTestId(EXTERNAL_ACCOUNT_CELL_TEST_IDS.NETWORK_AVATAR), + ).toBeTruthy(); }); it('renders correctly with Polygon chain ID', () => { @@ -217,7 +224,9 @@ describe('ExternalAccountCell', () => { />, ); - expect(getByTestId('network-avatar-image')).toBeTruthy(); + expect( + getByTestId(EXTERNAL_ACCOUNT_CELL_TEST_IDS.NETWORK_AVATAR), + ).toBeTruthy(); }); it('renders correctly with Optimism chain ID', () => { @@ -229,7 +238,9 @@ describe('ExternalAccountCell', () => { />, ); - expect(getByTestId('network-avatar-image')).toBeTruthy(); + expect( + getByTestId(EXTERNAL_ACCOUNT_CELL_TEST_IDS.NETWORK_AVATAR), + ).toBeTruthy(); }); }); diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.testIds.ts b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.testIds.ts index 1124f88a96d..d53375a3765 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.testIds.ts +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.testIds.ts @@ -1,3 +1,4 @@ export const EXTERNAL_ACCOUNT_CELL_TEST_IDS = { CONTAINER: 'external-account-cell-touchable', + NETWORK_AVATAR: 'network-avatar-image', } as const; diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.tsx index 7dd6514b412..c20108640eb 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/ExternalAccountCell/ExternalAccountCell.tsx @@ -1,21 +1,25 @@ import React from 'react'; import { TouchableOpacity, View } from 'react-native'; import { useSelector } from 'react-redux'; - -import { useStyles } from '../../../../hooks'; -import Text, { +import { + AvatarAccount, + AvatarAccountSize, + AvatarNetwork, + AvatarNetworkSize, + FontWeight, + Text, TextColor, TextVariant, -} from '../../../../components/Texts/Text'; -import AvatarAccount from '../../../../components/Avatars/Avatar/variants/AvatarAccount'; -import Avatar, { AvatarVariant } from '../../../../components/Avatars/Avatar'; -import { AvatarSize } from '../../../../components/Avatars/Avatar/Avatar.types'; +} from '@metamask/design-system-react-native'; + +import { useStyles } from '../../../../hooks'; import { formatAddress } from '../../../../../util/address'; import { getNetworkImageSource } from '../../../../../util/networks'; import { selectAvatarAccountType } from '../../../../../selectors/settings'; import { strings } from '../../../../../../locales/i18n'; import createStyles from '../MultichainAccountSelectorList.styles'; import { EXTERNAL_ACCOUNT_CELL_TEST_IDS } from './ExternalAccountCell.testIds'; +import { getAvatarAccountVariant } from '../../avatarAccountVariant'; /** * ExternalAccountCell Component @@ -39,6 +43,7 @@ const ExternalAccountCell: React.FC = ({ }) => { const { styles } = useStyles(createStyles, { isSelected }); const avatarAccountType = useSelector(selectAvatarAccountType); + const avatarAccountVariant = getAvatarAccountVariant(avatarAccountType); const formattedAddress = formatAddress(address, 'short'); // Get network image if chainId is provided @@ -60,21 +65,24 @@ const ExternalAccountCell: React.FC = ({ testID={EXTERNAL_ACCOUNT_CELL_TEST_IDS.CONTAINER} > {strings('bridge.external_account')} {formattedAddress} @@ -82,10 +90,10 @@ const ExternalAccountCell: React.FC = ({ {networkImageSource && ( - )} diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.test.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.test.tsx index 36681d12b28..d26934b151b 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.test.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.test.tsx @@ -48,6 +48,7 @@ import { createMockInternalAccountsWithAddresses, } from '../test-utils'; import { AccountCellIds } from '../AccountCell/AccountCell.testIds'; +import { ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID } from './AccountListCell/AccountListCell.testIds'; jest.mock('../../../../core/Engine', () => ({ context: { @@ -1286,7 +1287,9 @@ describe('MultichainAccountSelectorList', () => { expect(account2Checkboxes.length).toEqual(1); // Only container (unselected account, no icon rendered) // Check that there are no checked checkbox icons (since none are selected) - expect(queryByTestId('checkbox-icon-component')).toBeFalsy(); + expect( + queryByTestId(ACCOUNT_LIST_CELL_CHECKBOX_ICON_TEST_ID), + ).toBeFalsy(); }); it('calls onSelectAccount when checkbox is pressed', () => { diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.tsx index b24f7ac6574..ce373128587 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAccountSelectorList/MultichainAccountSelectorList.tsx @@ -10,10 +10,14 @@ import { ScrollView } from 'react-native-gesture-handler'; import { FlashList, ListRenderItem, FlashListRef } from '@shopify/flash-list'; import { useSelector } from 'react-redux'; import { AccountGroupObject } from '@metamask/account-tree-controller'; +import { + Text, + TextColor, + TextFieldSearch, + TextVariant, +} from '@metamask/design-system-react-native'; import { useStyles } from '../../../hooks'; -import Text, { TextColor, TextVariant } from '../../../components/Texts/Text'; -import TextFieldSearch from '../../../components/Form/TextFieldSearch'; import { selectAccountGroupsByWallet } from '../../../../selectors/multichainAccounts/accountTreeController'; import { selectInternalAccountsById } from '../../../../selectors/accountsController'; import AccountListHeader from './AccountListHeader'; @@ -408,14 +412,16 @@ const MultichainAccountSelectorList = ({ onChangeText={setSearchText} onPressClearButton={() => setSearchText('')} placeholder={strings('accounts.search_your_accounts')} - testID={MULTICHAIN_ACCOUNT_SELECTOR_SEARCH_INPUT_TESTID} + inputProps={{ + testID: MULTICHAIN_ACCOUNT_SELECTOR_SEARCH_INPUT_TESTID, + }} autoFocus={false} isError={shouldShowInvalidAddressError} /> {shouldShowInvalidAddressError ? ( @@ -430,8 +436,8 @@ const MultichainAccountSelectorList = ({ testID={MULTICHAIN_ACCOUNT_SELECTOR_EMPTY_STATE_TESTID} > {emptyStateText} diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.test.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.test.tsx index d403642fc29..760f3f32ea6 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.test.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, fireEvent } from '@testing-library/react-native'; +import { act, render, fireEvent } from '@testing-library/react-native'; import MultichainAddressRow from './MultichainAddressRow'; import { SAMPLE_MULTICHAIN_ADDRESS_ROW_PROPS, @@ -24,6 +24,10 @@ jest.mock('../../../../util/networks', () => ({ })); describe('MultichainAddressRow', () => { + afterEach(() => { + jest.useRealTimers(); + }); + it('renders MultichainAddressRow correctly', () => { const { getByTestId } = render( , @@ -142,8 +146,9 @@ describe('MultichainAddressRow', () => { const copyButton = getByTestId(MULTICHAIN_ADDRESS_ROW_COPY_BUTTON_TEST_ID); - // Simulate pressing the copy button - fireEvent.press(copyButton); + await act(async () => { + fireEvent.press(copyButton); + }); // Callback should be called expect(mockCallback).toHaveBeenCalled(); @@ -176,12 +181,14 @@ describe('MultichainAddressRow', () => { }); it('shows toast when copy button is pressed and toastRef is provided', async () => { + jest.useFakeTimers(); const mockCallback = jest.fn(); const mockShowToast = jest.fn(); + const mockCloseToast = jest.fn(); const mockToastRef = { current: { showToast: mockShowToast, - closeToast: jest.fn(), + closeToast: mockCloseToast, }, }; const copyParams = { @@ -199,19 +206,31 @@ describe('MultichainAddressRow', () => { const copyButton = getByTestId(MULTICHAIN_ADDRESS_ROW_COPY_BUTTON_TEST_ID); - // Simulate pressing the copy button - fireEvent.press(copyButton); - - // Wait for async operations - await new Promise((resolve) => setTimeout(resolve, 0)); + await act(async () => { + fireEvent.press(copyButton); + }); // Toast should be called with Plain variant (no icon) expect(mockShowToast).toHaveBeenCalled(); expect(mockShowToast).toHaveBeenCalledWith( expect.objectContaining({ variant: expect.stringContaining('Plain'), + labelOptions: [{ label: 'Address copied' }], + closeButtonOptions: expect.objectContaining({ + variant: 'Icon', + iconName: IconName.Close, + }), }), ); + + const toastOptions = mockShowToast.mock.calls[0][0]; + toastOptions.closeButtonOptions.onPress(); + expect(mockCloseToast).toHaveBeenCalled(); + + act(() => { + // Flush the fake timer that resets the copy feedback icon from check back to copy. + jest.advanceTimersByTime(400); + }); }); it('renders truncated address correctly when copyParams is missing', () => { diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.tsx index d525a391f7b..e6c083c4a2a 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAddressRow/MultichainAddressRow.tsx @@ -18,14 +18,16 @@ import { IconName, IconColor, FontWeight, + AvatarNetwork, + AvatarNetworkSize, } from '@metamask/design-system-react-native'; -import Avatar, { - AvatarSize, - AvatarVariant, -} from '../../../components/Avatars/Avatar'; import { formatAddress } from '../../../../util/address'; import { getNetworkImageSource } from '../../../../util/networks'; -import { Icon, MultichainAddressRowProps } from './MultichainAddressRow.types'; +import type { + CopyToastOptions, + Icon, + MultichainAddressRowProps, +} from './MultichainAddressRow.types'; import { MULTICHAIN_ADDRESS_ROW_NETWORK_ICON_TEST_ID, MULTICHAIN_ADDRESS_ROW_NETWORK_NAME_TEST_ID, @@ -33,8 +35,6 @@ import { MULTICHAIN_ADDRESS_ROW_TEST_ID, MULTICHAIN_ADDRESS_ROW_COPY_BUTTON_TEST_ID, } from './MultichainAddressRow.constants'; -import { ToastVariants, ButtonIconVariant } from '../../../components/Toast'; -import { IconName as ToastIconName } from '../../../components/Icons/Icon'; const MultichainAddressRow = ({ chainId, @@ -69,18 +69,21 @@ const MultichainAddressRow = ({ setIconState('copy'); }, 400); - // Show legacy row-managed toast only when both ref and message are provided. if (copyParams.toastRef?.current && copyParams.toastMessage) { - copyParams.toastRef.current.showToast({ - variant: ToastVariants.Plain, - labelOptions: [{ label: copyParams.toastMessage }], + const toastOptions: CopyToastOptions = { + variant: 'Plain', + labelOptions: [{ label: copyParams.toastMessage ?? '' }], hasNoTimeout: false, closeButtonOptions: { - variant: ButtonIconVariant.Icon, - iconName: ToastIconName.Close, + variant: 'Icon', + iconName: IconName.Close, onPress: () => copyParams.toastRef?.current?.closeToast(), }, - }); + }; + const showToast = copyParams.toastRef.current.showToast as ( + toastOptions: CopyToastOptions, + ) => void; + showToast(toastOptions); } }, [copyParams]); @@ -118,11 +121,10 @@ const MultichainAddressRow = ({ testID={testID} {...viewProps} > - void; + }; +} + +export interface CopyToastRef { + // Keep legacy ToastRef structurally assignable without importing deprecated Toast types. + showToast(toastOptions: never): void; + closeToast(): void; +} + /** * Parameters for the copy operation. */ @@ -30,7 +46,7 @@ export interface CopyParams { /** * Optional toast ref for legacy callers that need the row to show a toast. */ - toastRef?: React.RefObject; + toastRef?: React.RefObject; /** * Toast message used when toastRef is provided. */ diff --git a/app/component-library/components-temp/MultichainAccounts/MultichainAddressRowsList/MultichainAddressRowsList.tsx b/app/component-library/components-temp/MultichainAccounts/MultichainAddressRowsList/MultichainAddressRowsList.tsx index 97b1bd1b9ec..8ae6868f90d 100644 --- a/app/component-library/components-temp/MultichainAccounts/MultichainAddressRowsList/MultichainAddressRowsList.tsx +++ b/app/component-library/components-temp/MultichainAccounts/MultichainAddressRowsList/MultichainAddressRowsList.tsx @@ -3,11 +3,15 @@ import { View, FlatList, StyleProp, ViewStyle } from 'react-native'; import { useSelector } from 'react-redux'; import { InternalAccount } from '@metamask/keyring-internal-api'; import { CaipChainId, toCaipChainId } from '@metamask/utils'; +import { + Text, + TextColor, + TextFieldSearch, + TextVariant, +} from '@metamask/design-system-react-native'; import { useStyles } from '../../../hooks'; import styleSheet from './MultichainAddressRowsList.styles'; -import Text, { TextVariant, TextColor } from '../../../components/Texts/Text'; -import TextFieldSearch from '../../../components/Form/TextFieldSearch'; import { strings } from '../../../../../locales/i18n'; import MultichainAddressRow, { SAMPLE_ICONS } from '../MultichainAddressRow'; import { selectEvmNetworkConfigurationsByChainId } from '../../../../selectors/networkController'; @@ -122,8 +126,8 @@ const MultichainAddressRowsList: React.FC = ({ return ( {strings(messageKey)} @@ -142,7 +146,9 @@ const MultichainAddressRowsList: React.FC = ({ value={searchPattern} onChangeText={handleSearchChange} onPressClearButton={() => handleSearchChange('')} - testID={MULTICHAIN_ADDRESS_ROWS_LIST_SEARCH_TEST_ID} + inputProps={{ + testID: MULTICHAIN_ADDRESS_ROWS_LIST_SEARCH_TEST_ID, + }} /> diff --git a/app/component-library/components-temp/MultichainAccounts/avatarAccountVariant.test.ts b/app/component-library/components-temp/MultichainAccounts/avatarAccountVariant.test.ts new file mode 100644 index 00000000000..cf703954dc4 --- /dev/null +++ b/app/component-library/components-temp/MultichainAccounts/avatarAccountVariant.test.ts @@ -0,0 +1,38 @@ +import { AvatarAccountVariant } from '@metamask/design-system-react-native'; +import { + type AccountAvatarVariant, + AvatarAccountType, + getAvatarAccountVariant, +} from './avatarAccountVariant'; + +const avatarVariantCases: [AccountAvatarVariant, AvatarAccountVariant][] = [ + ['JazzIcon', AvatarAccountVariant.Jazzicon], + ['Blockies', AvatarAccountVariant.Blockies], + ['Maskicon', AvatarAccountVariant.Maskicon], + [AvatarAccountVariant.Jazzicon, AvatarAccountVariant.Jazzicon], + [AvatarAccountVariant.Blockies, AvatarAccountVariant.Blockies], + [AvatarAccountVariant.Maskicon, AvatarAccountVariant.Maskicon], +]; + +describe('avatarAccountVariant', () => { + it.each(avatarVariantCases)( + 'maps %s to the matching DS avatar variant', + (input, expected) => { + expect(getAvatarAccountVariant(input)).toBe(expected); + }, + ); + + it('defaults to Maskicon for unknown avatar variants', () => { + expect(getAvatarAccountVariant('Unknown' as AccountAvatarVariant)).toBe( + AvatarAccountVariant.Maskicon, + ); + }); + + it('exposes legacy avatar type names with DS variant values', () => { + expect(AvatarAccountType).toEqual({ + JazzIcon: AvatarAccountVariant.Jazzicon, + Blockies: AvatarAccountVariant.Blockies, + Maskicon: AvatarAccountVariant.Maskicon, + }); + }); +}); diff --git a/app/component-library/components-temp/MultichainAccounts/avatarAccountVariant.ts b/app/component-library/components-temp/MultichainAccounts/avatarAccountVariant.ts new file mode 100644 index 00000000000..f8d21803d0d --- /dev/null +++ b/app/component-library/components-temp/MultichainAccounts/avatarAccountVariant.ts @@ -0,0 +1,33 @@ +import { AvatarAccountVariant } from '@metamask/design-system-react-native'; + +export type LegacyAvatarAccountType = 'JazzIcon' | 'Blockies' | 'Maskicon'; + +export type AccountAvatarVariant = + | AvatarAccountVariant + | LegacyAvatarAccountType; + +export const AvatarAccountType = { + JazzIcon: AvatarAccountVariant.Jazzicon, + Blockies: AvatarAccountVariant.Blockies, + Maskicon: AvatarAccountVariant.Maskicon, +} as const; + +export type AvatarAccountType = + (typeof AvatarAccountType)[keyof typeof AvatarAccountType]; + +export const getAvatarAccountVariant = ( + avatarAccountType: AccountAvatarVariant, +): AvatarAccountVariant => { + switch (avatarAccountType) { + case 'JazzIcon': + case AvatarAccountVariant.Jazzicon: + return AvatarAccountVariant.Jazzicon; + case 'Blockies': + case AvatarAccountVariant.Blockies: + return AvatarAccountVariant.Blockies; + case 'Maskicon': + case AvatarAccountVariant.Maskicon: + default: + return AvatarAccountVariant.Maskicon; + } +};