From fcee6ad09811259b10d909457e6aaf74ea6501d4 Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Mon, 29 Jul 2024 20:20:54 -0700 Subject: [PATCH 01/20] feat: add network picker to asset-picker --- app/_locales/en/messages.json | 3 + .../asset-picker-modal-network.tsx | 111 ++++++++++++++++++ .../asset-picker-modal.test.tsx | 23 ++++ .../asset-picker-modal/asset-picker-modal.tsx | 26 +++- .../asset-picker-modal/index.scss | 18 +++ .../__snapshots__/asset-picker.test.tsx.snap | 66 +++++++++++ .../asset-picker/asset-picker.stories.tsx | 50 ++++++++ .../asset-picker/asset-picker.test.tsx | 31 +++++ .../asset-picker/asset-picker.tsx | 26 ++++ 9 files changed, 349 insertions(+), 5 deletions(-) create mode 100644 ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 60ec9579059d..6084fd3efe3d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -870,6 +870,9 @@ "bridgeDontSend": { "message": "Bridge, don't send" }, + "bridgeSelectNetwork": { + "message": "Select network" + }, "browserNotSupported": { "message": "Your browser is not supported..." }, diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx new file mode 100644 index 000000000000..0297a39e3667 --- /dev/null +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx @@ -0,0 +1,111 @@ +import React from 'react'; + +import { useSelector } from 'react-redux'; +import { + Display, + FlexDirection, + BlockSize, +} from '../../../../helpers/constants/design-system'; +import { + ModalOverlay, + ModalContent, + ModalHeader, + Modal, + Box, +} from '../../../component-library'; +import { + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, + RPCDefinition, +} from '../../../../../shared/constants/network'; +///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) +import { useI18nContext } from '../../../../hooks/useI18nContext'; +///: END:ONLY_INCLUDE_IF +import { NetworkListItem } from '../../network-list-item'; +import { getNetworkConfigurations } from '../../../../selectors'; +import { getProviderConfig } from '../../../../ducks/metamask/metamask'; + +/** + * AssetPickerModalNetwork component displays a modal for selecting a network in the asset picker. + * + * @param props + * @param props.isOpen - Determines whether the modal is open or not. + * @param props.network - The currently selected network, not necessarily the active wallet network. + * @param props.networks - The list of selectable networks. + * @param props.onNetworkChange - The callback function to handle network change. + * @param props.onClose - The callback function to handle modal close. + * @param props.onBack - The callback function to handle going back in the modal. + * @returns A modal with a list of selectable networks. + */ +export const AssetPickerModalNetwork = ({ + isOpen, + onClose, + onBack, + network, + networks, + onNetworkChange, +}: { + isOpen: boolean; + network?: RPCDefinition; + networks?: RPCDefinition[]; + onNetworkChange: (network: RPCDefinition) => void; + onClose: () => void; + onBack: () => void; +}) => { + ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) + const t = useI18nContext(); + ///: END:ONLY_INCLUDE_IF + + const currentNetwork = useSelector(getProviderConfig); + const allNetworks: RPCDefinition[] = useSelector(getNetworkConfigurations); + + const selectedNetwork: RPCDefinition = network ?? currentNetwork; + const networksList = networks ?? allNetworks ?? []; + + return ( + + + + + {t('bridgeSelectNetwork')} + + + + {networksList.map((networkConfig) => { + const { nickname } = networkConfig; + return ( + { + onNetworkChange(networkConfig); + onBack(); + }} + iconSrc={ + networkConfig?.rpcPrefs?.imageUrl ?? + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ + networkConfig.chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP + ] + } + focus={false} + /> + ); + })} + + + + + ); +}; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx index 15cc339775c9..cddcb0c874c3 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx @@ -69,6 +69,7 @@ describe('AssetPickerModal', () => { const defaultProps = { header: 'sendSelectReceiveAsset', + onNetworkPickerClick: jest.fn(), isOpen: true, onClose: onCloseMock, asset: { @@ -291,4 +292,26 @@ describe('AssetPickerModal', () => { }), ).toBe(true); }); + + it('should render network picker when networks prop is defined', () => { + const { getByText, getAllByRole } = renderWithProvider( + , + store, + ); + + const modalTitle = getByText('selectNetworkHeader'); + expect(modalTitle).toBeInTheDocument(); + + expect(getAllByRole('img')).toHaveLength(2); + const modalContent = getByText('Select network'); + expect(modalContent).toBeInTheDocument(); + }); }); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index 0d9e01627878..05d533ba4087 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -17,6 +17,7 @@ import { AvatarTokenSize, AvatarToken, Text, + PickerNetwork, } from '../../../component-library'; import { BorderRadius, @@ -61,6 +62,7 @@ import { AssetPickerModalTabs, TabName } from './asset-picker-modal-tabs'; import { AssetPickerModalNftTab } from './asset-picker-modal-nft-tab'; import AssetList from './AssetList'; import { Search } from './asset-picker-modal-search'; +import { AssetPickerModalNetwork } from './asset-picker-modal-network'; type AssetPickerModalProps = { header: JSX.Element | string | null; @@ -74,10 +76,12 @@ type AssetPickerModalProps = { * Sending asset for UI treatments; only for dest component */ sendingAsset?: { image: string; symbol: string } | undefined; + onNetworkPickerClick?: () => void; } & Pick< React.ComponentProps, 'visibleTabs' | 'defaultActiveTabKey' ->; +> & + Pick, 'network'>; const MAX_UNOWNED_TOKENS_RENDERED = 30; @@ -88,6 +92,8 @@ export function AssetPickerModal({ asset, onAssetChange, sendingAsset, + network, + onNetworkPickerClick, ...tabProps }: AssetPickerModalProps) { const t = useI18nContext(); @@ -240,9 +246,6 @@ export function AssetPickerModal({ return filteredTokens; }, [ - memoizedUsersTokens, - topTokens, - searchQuery, nativeCurrency, nativeCurrencyImage, balanceValue, @@ -255,7 +258,6 @@ export function AssetPickerModal({ conversionRate, currentCurrency, chainId, - tokenList, ]); return ( @@ -289,6 +291,20 @@ export function AssetPickerModal({ )} + {onNetworkPickerClick && ( + + + + )} diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss b/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss index bf231003a671..b3822894fa16 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss @@ -1,5 +1,12 @@ @use "design-system"; +.multichain-asset-picker__network-modal { + overflow-y:auto; + .mm-modal-content__dialog { + overflow-y: scroll; + } +} + .asset-picker-modal { $self: &; @@ -56,6 +63,17 @@ max-height: 100%; } + .network-picker { + display: flex; + justify-content: center; + align-items: center; + padding-top: 4px; + + button: { + background: var(--color-background-alternative); + } + } + .modal-tab { &__main-view { max-height: 100%; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap index d2748ce758bf..720afe75474e 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap +++ b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap @@ -131,3 +131,69 @@ exports[`AssetPicker render if disabled 1`] = ` `; + +exports[`AssetPicker should render network picker when networks prop is defined 1`] = ` + + + +`; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx index 753ee2644f5a..1d51d819a7fc 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx @@ -5,6 +5,7 @@ import mockState from '../../../../../test/data/mock-state.json'; import { AssetType } from '../../../../../shared/constants/transaction'; import { AssetPicker } from './asset-picker'; import { useI18nContext } from '../../../../hooks/useI18nContext'; +import { TabName } from '../asset-picker-modal/asset-picker-modal-tabs'; import { CHAIN_ID_TOKEN_IMAGE_MAP } from '../../../../../shared/constants/network'; import { ERC20Asset } from '../asset-picker-modal/types'; @@ -88,4 +89,53 @@ SendDestStory.decorators = [ SendDestStory.storyName = 'With Sending Asset'; +export const NetworksStory = ({ isOpen }: { isOpen: boolean }) => { + const t = useI18nContext(); + return ( + ({})} + {...props} + asset={{ + symbol: 'ETH', + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + type: AssetType.native, + }} + networkProps={{ + network: { + chainId: '0x1', + nickname: 'Mainnet', + rpcUrl: 'https://mainnet.infura.io/v3/', + type: 'rpc', + ticker: 'ETH', + }, + networks: [ + { + chainId: '0x1', + nickname: 'Mainnet', + rpcUrl: 'https://mainnet.infura.io/v3/', + type: 'rpc', + ticker: 'ETH', + }, + { + chainId: '0x10', + nickname: 'Optimism', + rpcUrl: 'https://optimism.infura.io/v3/', + type: 'rpc', + ticker: 'ETH', + }, + ], + onNetworkChange: () => ({}), + }} + visibleTabs={[TabName.TOKENS]} + /> + ); +}; + +NetworksStory.decorators = [ + (story) => {story()}, +]; + +NetworksStory.storyName = 'With Network Picker'; + export default storybook; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx index 262016ce4e69..83f44e2d4f2c 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx @@ -284,4 +284,35 @@ describe('AssetPicker', () => { expect(container).toMatchSnapshot(); }); + + it('should render network picker when networks prop is defined', () => { + const asset = { + type: AssetType.native, + image: CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP['0x1'], + symbol: NATIVE_TICKER, + } as NativeAsset; + + const mockAssetChange = jest.fn(); + + const { asFragment } = render( + + mockAssetChange()} + isDisabled + networkProps={{ + network: { chainId: '0x1', type: 'rpc', ticker: 'ETH' }, + networks: [ + { chainId: '0x1', type: 'rpc', ticker: 'ETH' }, + { chainId: '0xa', type: 'rpc', ticker: 'ETH' }, + ], + onNetworkChange: jest.fn(), + }} + /> + , + ); + + expect(asFragment()).toMatchSnapshot(); + }); }); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index 3b1526e4af62..71e5f0111b09 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -41,6 +41,7 @@ import { NFT, } from '../asset-picker-modal/types'; import { TabName } from '../asset-picker-modal/asset-picker-modal-tabs'; +import { AssetPickerModalNetwork } from '../asset-picker-modal/asset-picker-modal-network'; const ELLIPSIFY_LENGTH = 13; // 6 (start) + 4 (end) + 3 (...) @@ -60,6 +61,10 @@ export type AssetPickerProps = { ) => void; onClick?: () => void; isDisabled?: boolean; + networkProps?: Pick< + React.ComponentProps, + 'network' | 'networks' | 'onNetworkChange' + >; } & Pick< React.ComponentProps, 'visibleTabs' | 'header' | 'sendingAsset' @@ -70,6 +75,7 @@ export function AssetPicker({ header, asset, onAssetChange, + networkProps, sendingAsset, onClick, isDisabled = false, @@ -107,8 +113,23 @@ export function AssetPicker({ return undefined; }; + const [isSelectingNetwork, setIsSelectingNetwork] = useState(false); + return ( <> + {networkProps && ( + { + setIsSelectingNetwork(false); + }} + onBack={() => { + setIsSelectingNetwork(false); + setShowAssetPickerModal(true); + }} + {...networkProps} + /> + )} {/* This is the Modal that ask to choose token to send */} { + setShowAssetPickerModal(false); + setIsSelectingNetwork(true); + }} defaultActiveTabKey={ asset?.type === AssetType.NFT ? TabName.NFTS : TabName.TOKENS } From 9c16831fd32c895066806f1ad8f546c780d57908 Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Thu, 1 Aug 2024 14:26:32 -0700 Subject: [PATCH 02/20] fix: show correct network icon when selected network !== active network --- .../__snapshots__/asset-picker.test.tsx.snap | 6 +++- .../asset-picker/asset-picker.stories.tsx | 2 +- .../asset-picker/asset-picker.test.tsx | 22 ++++++++++--- .../asset-picker/asset-picker.tsx | 31 ++++++++++++++----- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap index 720afe75474e..75e2b5505601 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap +++ b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap @@ -167,7 +167,11 @@ exports[`AssetPicker should render network picker when networks prop is defined
- ? + network logo
diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx index 1d51d819a7fc..5786fabc1852 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.stories.tsx @@ -62,7 +62,7 @@ export const SendDestStory = () => { type: AssetType.native, }} sendingAsset={{ - image: 'token image', + image: CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], symbol: 'ETH', }} /> diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx index 83f44e2d4f2c..3624b445dd67 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx @@ -113,6 +113,10 @@ describe('AssetPicker', () => { const img = getByAltText('Ethereum Mainnet logo'); expect(img).toBeInTheDocument(); expect(img).toHaveAttribute('src', './images/eth_logo.svg'); + expect(getByAltText('NATIVE logo')).toHaveAttribute( + 'src', + CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + ); }); it('native: renders overflowing symbol and image', () => { @@ -136,6 +140,10 @@ describe('AssetPicker', () => { const img = getByAltText('Ethereum Mainnet logo'); expect(img).toBeInTheDocument(); expect(img).toHaveAttribute('src', './images/eth_logo.svg'); + expect(getByAltText('NATIVE TICKER logo')).toHaveAttribute( + 'src', + CHAIN_ID_TOKEN_IMAGE_MAP['0x1'], + ); }); it('token: renders symbol and image', () => { @@ -160,6 +168,10 @@ describe('AssetPicker', () => { const img = getByAltText('symbol logo'); expect(img).toBeInTheDocument(); expect(img).toHaveAttribute('src', 'token icon url'); + expect(getByAltText('symbol logo')).toHaveAttribute( + 'src', + 'token icon url', + ); }); it('token: renders symbol and image overflowing', () => { @@ -172,11 +184,7 @@ describe('AssetPicker', () => { const mockAssetChange = jest.fn(); const { getByText, getByAltText } = render( - + { const img = getByAltText('symbol overflow logo'); expect(img).toBeInTheDocument(); expect(img).toHaveAttribute('src', 'token icon url'); + expect(getByAltText('symbol overflow logo')).toHaveAttribute( + 'src', + 'token icon url', + ); }); it('token: renders symbol and image falls back', () => { diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index 71e5f0111b09..2a4983ab2996 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -24,10 +24,7 @@ import { } from '../../../../helpers/constants/design-system'; import { AssetType } from '../../../../../shared/constants/transaction'; import { AssetPickerModal } from '../asset-picker-modal/asset-picker-modal'; -import { - getCurrentNetwork, - getTestNetworkBackgroundColor, -} from '../../../../selectors'; +import { getCurrentNetwork } from '../../../../selectors'; import Tooltip from '../../../ui/tooltip'; import { LARGE_SYMBOL_LENGTH } from '../constants'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) @@ -42,6 +39,11 @@ import { } from '../asset-picker-modal/types'; import { TabName } from '../asset-picker-modal/asset-picker-modal-tabs'; import { AssetPickerModalNetwork } from '../asset-picker-modal/asset-picker-modal-network'; +import { + CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, + GOERLI_DISPLAY_NAME, + SEPOLIA_DISPLAY_NAME, +} from '../../../../../shared/constants/network'; const ELLIPSIFY_LENGTH = 13; // 6 (start) + 4 (end) + 3 (...) @@ -101,7 +103,7 @@ export function AssetPicker({ // Badge details const currentNetwork = useSelector(getCurrentNetwork); - const testNetworkBackgroundColor = useSelector(getTestNetworkBackgroundColor); + const selectedNetwork = networkProps?.network ?? currentNetwork; const handleAssetPickerTitle = (): string | undefined => { ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) @@ -186,9 +188,22 @@ export function AssetPicker({ badge={ + selectedNetwork?.ticker?.includes(tickerSubstring), + )?.[1] + } borderColor={ primaryTokenImage ? BorderColor.borderMuted From 9831a4181597f35b232db88a1e90225bd9038024 Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Thu, 8 Aug 2024 18:03:22 -0700 Subject: [PATCH 03/20] fix: override native token details in AssetList --- .../asset-picker-modal/AssetList.tsx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx index 9061592cf37c..f6cbee5183a5 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx @@ -112,15 +112,28 @@ export default function AssetList({ {token.type === AssetType.native ? ( ) : ( Date: Thu, 8 Aug 2024 14:59:43 -0700 Subject: [PATCH 04/20] refactor: split up token sorting and filtering --- .../asset-picker-modal/asset-picker-modal.tsx | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index 05d533ba4087..173a1093b85c 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -48,7 +48,6 @@ import { import { useTokenTracker } from '../../../../hooks/useTokenTracker'; import { getTopAssets } from '../../../../ducks/swaps/swaps'; import { getRenderableTokenData } from '../../../../hooks/useTokensToSearch'; -import { useEqualityCheck } from '../../../../hooks/useEqualityCheck'; import { getSwapsBlockedTokens } from '../../../../ducks/send'; import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; import { @@ -138,13 +137,6 @@ export function AssetPickerModal({ const tokenList = useSelector(getTokenList) as TokenListMap; const topTokens = useSelector(getTopAssets, isEqual); - const usersTokens = uniqBy( - [...tokensWithBalances, ...tokens], - 'address', - ); - - const memoizedUsersTokens: TokenWithBalance[] = useEqualityCheck(usersTokens); - const getIsDisabled = useCallback( ({ address, @@ -163,28 +155,31 @@ export function AssetPickerModal({ [sendingAsset?.symbol, memoizedSwapsBlockedTokens], ); - const filteredTokenList = useMemo(() => { - const nativeToken: AssetWithDisplayData = { - address: null, - symbol: nativeCurrency, - decimals: 18, - image: nativeCurrencyImage, - balance: balanceValue, - string: undefined, - type: AssetType.native, - }; + const memoizedUsersTokens: TokenWithBalance[] = useMemo(() => { + return uniqBy( + [...tokensWithBalances, ...tokens], + 'address', + ); + }, [tokensWithBalances, tokens]); - const filteredTokens: AssetWithDisplayData[] = []; - // undefined would be the native token address - const filteredTokensAddresses = new Set(); - - function* tokenGenerator(): Generator< + const sortedTokenListGenerator = useCallback( + function* (): Generator< | AssetWithDisplayData | ((Token | TokenListToken) & { balance?: string; string?: string; }) > { + const nativeToken: AssetWithDisplayData = { + address: null, + symbol: nativeCurrency, + decimals: 18, + image: nativeCurrencyImage, + balance: balanceValue, + string: undefined, + type: AssetType.native, + }; + yield nativeToken; const blockedTokens = []; @@ -213,9 +208,24 @@ export function AssetPickerModal({ for (const token of blockedTokens) { yield token; } - } + }, + [ + nativeCurrency, + nativeCurrencyImage, + balanceValue, + memoizedUsersTokens, + topTokens, + tokenList, + getIsDisabled, + ], + ); + + const filteredTokenList = useMemo(() => { + const filteredTokens: AssetWithDisplayData[] = []; + // undefined would be the native token address + const filteredTokensAddresses = new Set(); - for (const token of tokenGenerator()) { + for (const token of sortedTokenListGenerator()) { if ( token.symbol?.toLowerCase().includes(searchQuery.toLowerCase()) && !filteredTokensAddresses.has(token.address?.toLowerCase()) @@ -246,18 +256,12 @@ export function AssetPickerModal({ return filteredTokens; }, [ - nativeCurrency, - nativeCurrencyImage, - balanceValue, - memoizedUsersTokens, - topTokens, - tokenList, - getIsDisabled, searchQuery, tokenConversionRates, conversionRate, currentCurrency, chainId, + sortedTokenListGenerator, ]); return ( From 246195d0f9f776e88adb79d46f0010d844d04e58 Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Thu, 8 Aug 2024 15:23:43 -0700 Subject: [PATCH 05/20] feat: pass in custom filteredTokensGenerator function to asset-picker --- .../asset-picker-modal/asset-picker-modal.tsx | 33 ++++++++++++++++++- .../asset-picker/asset-picker.tsx | 4 ++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index 173a1093b85c..786f25250c80 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -76,6 +76,15 @@ type AssetPickerModalProps = { */ sendingAsset?: { image: string; symbol: string } | undefined; onNetworkPickerClick?: () => void; + /** + * Generator function that returns a list of tokens filtered by a predicate and sorted + * by a custom order. + */ + customTokenListGenerator?: ( + filterPredicate: (token: ERC20Asset | NativeAsset) => boolean, + ) => Generator< + AssetWithDisplayData | AssetWithDisplayData + >; } & Pick< React.ComponentProps, 'visibleTabs' | 'defaultActiveTabKey' @@ -93,6 +102,7 @@ export function AssetPickerModal({ sendingAsset, network, onNetworkPickerClick, + customTokenListGenerator, ...tabProps }: AssetPickerModalProps) { const t = useI18nContext(); @@ -221,10 +231,31 @@ export function AssetPickerModal({ ); const filteredTokenList = useMemo(() => { - const filteredTokens: AssetWithDisplayData[] = []; + const filteredTokens: ( + | AssetWithDisplayData + | AssetWithDisplayData + )[] = []; // undefined would be the native token address const filteredTokensAddresses = new Set(); + // If filteredTokensGenerator is passed in, use it to generate the filtered tokens + if (customTokenListGenerator) { + for (const token of customTokenListGenerator( + ({ symbol, address }) => + symbol?.toLowerCase().includes(searchQuery.toLowerCase()) && + !filteredTokensAddresses.has(address?.toLowerCase()), + )) { + filteredTokensAddresses.add(token.address?.toLowerCase()); + filteredTokens.push(token); + + if (filteredTokens.length > MAX_UNOWNED_TOKENS_RENDERED) { + break; + } + } + return filteredTokens; + } + + // Otherwise use the default token list generator for (const token of sortedTokenListGenerator()) { if ( token.symbol?.toLowerCase().includes(searchQuery.toLowerCase()) && diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index 2a4983ab2996..ed73e0303eac 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -69,7 +69,7 @@ export type AssetPickerProps = { >; } & Pick< React.ComponentProps, - 'visibleTabs' | 'header' | 'sendingAsset' + 'visibleTabs' | 'header' | 'sendingAsset' | 'customTokenListGenerator' >; // A component that lets the user pick from a list of assets. @@ -82,6 +82,7 @@ export function AssetPicker({ onClick, isDisabled = false, visibleTabs, + customTokenListGenerator, }: AssetPickerProps) { ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) const t = useI18nContext(); @@ -156,6 +157,7 @@ export function AssetPicker({ defaultActiveTabKey={ asset?.type === AssetType.NFT ? TabName.NFTS : TabName.TOKENS } + customTokenListGenerator={customTokenListGenerator} /> Date: Thu, 8 Aug 2024 16:09:46 -0700 Subject: [PATCH 06/20] chore: pass filter predicate to filteredTokensGenerator --- .../asset-picker-modal/asset-picker-modal.tsx | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index 786f25250c80..9361b9653224 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -81,7 +81,7 @@ type AssetPickerModalProps = { * by a custom order. */ customTokenListGenerator?: ( - filterPredicate: (token: ERC20Asset | NativeAsset) => boolean, + filterPredicate: (symbol: string, address?: string) => boolean, ) => Generator< AssetWithDisplayData | AssetWithDisplayData >; @@ -172,8 +172,10 @@ export function AssetPickerModal({ ); }, [tokensWithBalances, tokens]); - const sortedTokenListGenerator = useCallback( - function* (): Generator< + const tokenListGenerator = useCallback( + function* ( + shouldAddToken: (symbol: string, address?: null | string) => boolean, + ): Generator< | AssetWithDisplayData | ((Token | TokenListToken) & { balance?: string; @@ -190,18 +192,22 @@ export function AssetPickerModal({ type: AssetType.native, }; - yield nativeToken; + if (shouldAddToken(nativeToken.symbol, nativeToken.address)) { + yield nativeToken; + } const blockedTokens = []; for (const token of memoizedUsersTokens) { - yield token; + if (shouldAddToken(token.symbol, token.address)) { + yield token; + } } // topTokens should already be sorted by popularity for (const address of Object.keys(topTokens)) { const token = tokenList?.[address]; - if (token) { + if (token && shouldAddToken(token.symbol, token.address)) { if (getIsDisabled(token)) { blockedTokens.push(token); continue; @@ -212,7 +218,9 @@ export function AssetPickerModal({ } for (const token of Object.values(tokenList)) { - yield token; + if (shouldAddToken(token.symbol, token.address)) { + yield token; + } } for (const token of blockedTokens) { @@ -238,47 +246,38 @@ export function AssetPickerModal({ // undefined would be the native token address const filteredTokensAddresses = new Set(); - // If filteredTokensGenerator is passed in, use it to generate the filtered tokens - if (customTokenListGenerator) { - for (const token of customTokenListGenerator( - ({ symbol, address }) => - symbol?.toLowerCase().includes(searchQuery.toLowerCase()) && - !filteredTokensAddresses.has(address?.toLowerCase()), - )) { - filteredTokensAddresses.add(token.address?.toLowerCase()); - filteredTokens.push(token); - - if (filteredTokens.length > MAX_UNOWNED_TOKENS_RENDERED) { - break; - } - } - return filteredTokens; - } + // Default filter predicate for whether a token should be included in displayed list + const shouldAddToken = (symbol: string, address?: string | null) => { + return ( + symbol?.toLowerCase().includes(searchQuery.toLowerCase()) && + !filteredTokensAddresses.has(address?.toLowerCase()) + ); + }; - // Otherwise use the default token list generator - for (const token of sortedTokenListGenerator()) { - if ( - token.symbol?.toLowerCase().includes(searchQuery.toLowerCase()) && - !filteredTokensAddresses.has(token.address?.toLowerCase()) - ) { - filteredTokensAddresses.add(token.address?.toLowerCase()); - filteredTokens.push( - getRenderableTokenData( - token.address - ? ({ - ...token, - ...tokenList[token.address.toLowerCase()], - type: AssetType.token, - } as AssetWithDisplayData) - : token, - tokenConversionRates, - conversionRate, - currentCurrency, - chainId, - tokenList, - ), - ); - } + // If filteredTokensGenerator is passed in, use it to generate the filtered tokens + // Otherwise use the default tokenGenerator + for (const token of (customTokenListGenerator ?? tokenListGenerator)( + shouldAddToken, + )) { + filteredTokensAddresses.add(token.address?.toLowerCase()); + filteredTokens.push( + customTokenListGenerator + ? token + : getRenderableTokenData( + token.address + ? ({ + ...token, + ...tokenList[token.address.toLowerCase()], + type: AssetType.token, + } as AssetWithDisplayData) + : token, + tokenConversionRates, + conversionRate, + currentCurrency, + chainId, + tokenList, + ), + ); if (filteredTokens.length > MAX_UNOWNED_TOKENS_RENDERED) { break; @@ -292,7 +291,8 @@ export function AssetPickerModal({ conversionRate, currentCurrency, chainId, - sortedTokenListGenerator, + tokenListGenerator, + customTokenListGenerator, ]); return ( From fb0a4f60f8de94cbe1c62125ab698401964736fe Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Fri, 23 Aug 2024 14:49:16 -0700 Subject: [PATCH 07/20] chore: add AssetPicker fallback text --- .../asset-picker/asset-picker.tsx | 128 ++++++++++-------- .../asset-picker/index.scss | 5 + 2 files changed, 73 insertions(+), 60 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index ed73e0303eac..349e205fbeec 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -184,72 +184,80 @@ export function AssetPicker({ }} title={handleAssetPickerTitle()} > - - - - selectedNetwork?.ticker?.includes(tickerSubstring), - )?.[1] - } - borderColor={ - primaryTokenImage - ? BorderColor.borderMuted - : BorderColor.borderDefault - } + {asset ? ( + + + + selectedNetwork?.ticker?.includes(tickerSubstring), + )?.[1] + } + borderColor={ + primaryTokenImage + ? BorderColor.borderMuted + : BorderColor.borderDefault + } + /> + } + > + - } - > - - - + + - - - {formattedSymbol} - - {isNFT && asset?.tokenId && ( - # - {String(asset.tokenId).length < ELLIPSIFY_LENGTH - ? asset.tokenId - : ellipsify(String(asset.tokenId), 6, 4)} + {formattedSymbol} - )} - - + {isNFT && asset?.tokenId && ( + + # + {String(asset.tokenId).length < ELLIPSIFY_LENGTH + ? asset.tokenId + : ellipsify(String(asset.tokenId), 6, 4)} + + )} + + + ) : ( + + {t('swapSelectToken')} + + )} ); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/index.scss b/ui/components/multichain/asset-picker-amount/asset-picker/index.scss index d30ce8c016d7..8218c7f3a1c3 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/index.scss +++ b/ui/components/multichain/asset-picker-amount/asset-picker/index.scss @@ -25,4 +25,9 @@ opacity: 1; cursor: not-allowed; } + + &__fallback{ + text-wrap: nowrap; + padding-left: 8px; + } } From 6a9e5ec3ba88afa9066cb98ec826e6be8b9d45f0 Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Wed, 4 Sep 2024 18:32:34 -0700 Subject: [PATCH 08/20] fix: lint and unit tests --- .../asset-picker-modal.test.tsx | 37 +++++++++++++++++-- .../asset-picker-modal/asset-picker-modal.tsx | 6 +-- .../asset-picker-modal/index.scss | 3 +- .../asset-picker/asset-picker.test.tsx | 33 +++++++++++++++-- .../asset-picker/asset-picker.tsx | 2 +- .../asset-picker/index.scss | 2 +- 6 files changed, 69 insertions(+), 14 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx index cddcb0c874c3..c2e91bbca905 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx @@ -293,15 +293,20 @@ describe('AssetPickerModal', () => { ).toBe(true); }); - it('should render network picker when networks prop is defined', () => { + it('should render network picker when onNetworkPickerClick prop is defined', () => { const { getByText, getAllByRole } = renderWithProvider( , store, @@ -311,7 +316,33 @@ describe('AssetPickerModal', () => { expect(modalTitle).toBeInTheDocument(); expect(getAllByRole('img')).toHaveLength(2); - const modalContent = getByText('Select network'); + const modalContent = getByText('Network name'); expect(modalContent).toBeInTheDocument(); }); + + it('should not render network picker when onNetworkPickerClick prop is not defined', () => { + const { getByText, getAllByRole } = renderWithProvider( + , + store, + ); + + const modalTitle = getByText('selectNetworkHeader'); + expect(modalTitle).toBeInTheDocument(); + + expect(getAllByRole('img')).toHaveLength(1); + }); }); diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index 9361b9653224..c29f73081e73 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -330,11 +330,7 @@ export function AssetPickerModal({ diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss b/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss index b3822894fa16..be1e27dc1416 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/index.scss @@ -1,7 +1,8 @@ @use "design-system"; .multichain-asset-picker__network-modal { - overflow-y:auto; + overflow-y: auto; + .mm-modal-content__dialog { overflow-y: scroll; } diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx index 3624b445dd67..cce42bc9593a 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.test.tsx @@ -314,10 +314,37 @@ describe('AssetPicker', () => { onAssetChange={() => mockAssetChange()} isDisabled networkProps={{ - network: { chainId: '0x1', type: 'rpc', ticker: 'ETH' }, + network: { + chainId: '0x1', + ticker: 'ETH', + rpcUrl: 'https://rpcurl', + rpcPrefs: { + blockExplorerUrl: 'https://explorerurl', + imageUrl: './images/eth_logo.svg', + }, + nickname: 'network', + }, networks: [ - { chainId: '0x1', type: 'rpc', ticker: 'ETH' }, - { chainId: '0xa', type: 'rpc', ticker: 'ETH' }, + { + chainId: '0x1', + ticker: 'ETH', + rpcUrl: 'https://rpcurl', + rpcPrefs: { + blockExplorerUrl: 'https://explorerurl', + imageUrl: 'https://image.com', + }, + nickname: 'Network name 3', + }, + { + chainId: '0xa', + ticker: 'ETH', + rpcUrl: 'https://rpcurl', + rpcPrefs: { + blockExplorerUrl: 'https://explorerurl', + imageUrl: 'https://image.com', + }, + nickname: 'Network name 4', + }, ], onNetworkChange: jest.fn(), }} diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index 349e205fbeec..694731a2acdd 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -196,7 +196,7 @@ export function AssetPicker({ selectedNetwork?.rpcPrefs?.imageUrl ?? (selectedNetwork?.chainId && CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ - selectedNetwork.chainId + selectedNetwork.chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP ]) } backgroundColor={ diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/index.scss b/ui/components/multichain/asset-picker-amount/asset-picker/index.scss index 8218c7f3a1c3..6cfaf877efbd 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/index.scss +++ b/ui/components/multichain/asset-picker-amount/asset-picker/index.scss @@ -26,7 +26,7 @@ cursor: not-allowed; } - &__fallback{ + &__fallback { text-wrap: nowrap; padding-left: 8px; } From a4a6914ad5ae0ae585ff092d7eb63ee42779915f Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Wed, 25 Sep 2024 14:44:07 -0700 Subject: [PATCH 09/20] fix: show network picker button only if networkProps are present --- .../asset-picker/asset-picker.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx index 694731a2acdd..de78c2330d1e 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker/asset-picker.tsx @@ -150,10 +150,14 @@ export function AssetPicker({ }} sendingAsset={sendingAsset} network={networkProps?.network ? networkProps.network : undefined} - onNetworkPickerClick={() => { - setShowAssetPickerModal(false); - setIsSelectingNetwork(true); - }} + onNetworkPickerClick={ + networkProps + ? () => { + setShowAssetPickerModal(false); + setIsSelectingNetwork(true); + } + : undefined + } defaultActiveTabKey={ asset?.type === AssetType.NFT ? TabName.NFTS : TabName.TOKENS } From f5e0adc446a6729d27997e041dd8fbb8410349d1 Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Wed, 25 Sep 2024 14:46:34 -0700 Subject: [PATCH 10/20] fix: use NetworkConfiguration instead of ProviderConfig/RPCEndpoint --- .../asset-picker-modal-network.tsx | 28 ++++----- .../asset-picker-modal.test.tsx | 39 +++++++----- .../asset-picker-modal/asset-picker-modal.tsx | 10 ++- .../__snapshots__/asset-picker.test.tsx.snap | 8 ++- .../asset-picker/asset-picker.stories.tsx | 63 +++++++++++++------ .../asset-picker/asset-picker.test.tsx | 58 ++++++++++------- .../asset-picker/asset-picker.tsx | 25 +++++--- 7 files changed, 149 insertions(+), 82 deletions(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx index 0297a39e3667..5d325aad1918 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal-network.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; +import { NetworkConfiguration } from '@metamask/network-controller'; import { Display, FlexDirection, @@ -13,15 +14,12 @@ import { Modal, Box, } from '../../../component-library'; -import { - CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, - RPCDefinition, -} from '../../../../../shared/constants/network'; +import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../shared/constants/network'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import { useI18nContext } from '../../../../hooks/useI18nContext'; ///: END:ONLY_INCLUDE_IF import { NetworkListItem } from '../../network-list-item'; -import { getNetworkConfigurations } from '../../../../selectors'; +import { getNetworkConfigurationsByChainId } from '../../../../selectors'; import { getProviderConfig } from '../../../../ducks/metamask/metamask'; /** @@ -45,9 +43,9 @@ export const AssetPickerModalNetwork = ({ onNetworkChange, }: { isOpen: boolean; - network?: RPCDefinition; - networks?: RPCDefinition[]; - onNetworkChange: (network: RPCDefinition) => void; + network?: NetworkConfiguration; + networks?: NetworkConfiguration[]; + onNetworkChange: (network: NetworkConfiguration) => void; onClose: () => void; onBack: () => void; }) => { @@ -56,10 +54,13 @@ export const AssetPickerModalNetwork = ({ ///: END:ONLY_INCLUDE_IF const currentNetwork = useSelector(getProviderConfig); - const allNetworks: RPCDefinition[] = useSelector(getNetworkConfigurations); + const allNetworks = useSelector(getNetworkConfigurationsByChainId); + + const selectedNetwork = + network ?? (currentNetwork?.chainId && allNetworks[currentNetwork.chainId]); - const selectedNetwork: RPCDefinition = network ?? currentNetwork; - const networksList = networks ?? allNetworks ?? []; + const networksList: NetworkConfiguration[] = + networks ?? Object.values(allNetworks) ?? []; return ( {networksList.map((networkConfig) => { - const { nickname } = networkConfig; + const { name } = networkConfig; return ( { onNetworkChange(networkConfig); onBack(); }} iconSrc={ - networkConfig?.rpcPrefs?.imageUrl ?? CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ networkConfig.chainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP ] diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx index c2e91bbca905..566783abed11 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx @@ -4,6 +4,7 @@ import configureStore from 'redux-mock-store'; import { useSelector } from 'react-redux'; import thunk from 'redux-thunk'; import sinon from 'sinon'; +import { RpcEndpointType } from '@metamask/network-controller'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import { useNftsCollections } from '../../../../hooks/useNftsCollections'; import { useTokenTracker } from '../../../../hooks/useTokenTracker'; @@ -299,14 +300,19 @@ describe('AssetPickerModal', () => { {...defaultProps} header="selectNetworkHeader" network={{ - ticker: 'ETH', + nativeCurrency: 'ETH', chainId: '0x1', - rpcUrl: 'https://rpcurl', - rpcPrefs: { - blockExplorerUrl: 'https://explorerurl', - imageUrl: 'https://image.com', - }, - nickname: 'Network name', + defaultBlockExplorerUrlIndex: 0, + blockExplorerUrls: ['https://explorerurl'], + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'test1', + url: 'https://rpcurl', + type: RpcEndpointType.Custom, + }, + ], + name: 'Network name', }} />, store, @@ -327,14 +333,19 @@ describe('AssetPickerModal', () => { onNetworkPickerClick={undefined} header="selectNetworkHeader" network={{ - ticker: 'ETH', + nativeCurrency: 'ETH', chainId: '0x1', - rpcUrl: 'https://rpcurl', - rpcPrefs: { - blockExplorerUrl: 'https://explorerurl', - imageUrl: 'https://image.com', - }, - nickname: 'Network name', + defaultBlockExplorerUrlIndex: 0, + blockExplorerUrls: ['https://explorerurl'], + defaultRpcEndpointIndex: 0, + rpcEndpoints: [ + { + networkClientId: 'test1', + url: 'https://rpcurl', + type: RpcEndpointType.Custom, + }, + ], + name: 'Network name', }} />, store, diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index c29f73081e73..d7c88efa2bf6 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -50,6 +50,7 @@ import { getTopAssets } from '../../../../ducks/swaps/swaps'; import { getRenderableTokenData } from '../../../../hooks/useTokensToSearch'; import { getSwapsBlockedTokens } from '../../../../ducks/send'; import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; +import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../shared/constants/network'; import { ERC20Asset, NativeAsset, @@ -329,8 +330,13 @@ export function AssetPickerModal({ {onNetworkPickerClick && ( diff --git a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap index 75e2b5505601..42c7b204d708 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap +++ b/ui/components/multichain/asset-picker-amount/asset-picker/__snapshots__/asset-picker.test.tsx.snap @@ -135,13 +135,13 @@ exports[`AssetPicker render if disabled 1`] = ` exports[`AssetPicker should render network picker when networks prop is defined 1`] = ` + +
+

+ Select network +

+
+
+ +
+ +
+
+
+ +
+ +
+
+ +`; + +exports[`AssetPickerModalNetwork should not show selected network when network prop is not passed in 1`] = ` + +
+
+
+