-
Notifications
You must be signed in to change notification settings - Fork 5.4k
fix: hovered address list component #37539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+2,835
−62
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
9c1bbf8
feat: add new multichian aggregated address list row
montelaidev a878b2e
fix: remove groupName prop
montelaidev a798f21
feat: add a hover component with a address row list
montelaidev 03c82c6
refactor: wire up hovered component
montelaidev 518abb9
Merge remote-tracking branch 'origin/main' into fix/mul-1226
montelaidev a36d728
fix: hover styling
montelaidev 3d70871
fix: lint
montelaidev 42726bc
fix: lint
montelaidev 2da485d
fix: test
montelaidev eb401e9
fix: styling
montelaidev 2fccaef
fix: gap between button and address
montelaidev 70c4879
refactor: array to map
montelaidev 56751b9
Merge remote-tracking branch 'origin/main' into fix/mul-1226
montelaidev c4e5e7e
refactor: make popover more reusable
montelaidev c3514cf
fix: ordering
montelaidev 7aaa8e4
fix: lint and test
montelaidev 0da5333
fix: lint
montelaidev 4e35048
fix: test
montelaidev a747b2d
fix: lint
montelaidev e8072b4
fix: lint
montelaidev 19ef96b
fix: styling
montelaidev b4a37cf
fix: stories
montelaidev 1f56716
fix: lint
montelaidev fb807b6
fix: missing locale
montelaidev 34b0287
fix: shrink hover width of reference element
montelaidev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
2 changes: 2 additions & 0 deletions
2
ui/components/multichain-accounts/multichain-account-network-group/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { MultichainAccountNetworkGroup } from './multichain-account-network-group'; | ||
| export type { MultichainAccountNetworkGroupProps } from './multichain-account-network-group'; |
162 changes: 162 additions & 0 deletions
162
...multichain-accounts/multichain-account-network-group/multichain-account-network-group.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| import React, { useMemo } from 'react'; | ||
| import { useSelector } from 'react-redux'; | ||
| import { AccountGroupId } from '@metamask/account-api'; | ||
| import { Box } from '@metamask/design-system-react'; | ||
| import { AvatarGroup } from '../../multichain/avatar-group'; | ||
| import { AvatarType } from '../../multichain/avatar-group/avatar-group.types'; | ||
| import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../shared/constants/network'; | ||
| import { convertCaipToHexChainId } from '../../../../shared/modules/network.utils'; | ||
| import { getInternalAccountListSpreadByScopesByGroupId } from '../../../selectors/multichain-accounts/account-tree'; | ||
|
|
||
| export type MultichainAccountNetworkGroupProps = { | ||
| /** | ||
| * The account group ID to fetch networks for | ||
| */ | ||
| groupId?: AccountGroupId; | ||
| /** | ||
| * Array of specific chain IDs to display | ||
| * - If provided with groupId: shows only chains that exist in both the group and this list | ||
| * - If provided without groupId: shows only these specific chains | ||
| */ | ||
| chainIds?: string[]; | ||
| /** | ||
| * Whether to exclude test networks (default: true) | ||
| */ | ||
| excludeTestNetworks?: boolean; | ||
| /** | ||
| * Maximum number of avatars to display before showing "+X" | ||
| */ | ||
| limit?: number; | ||
| /** | ||
| * Optional className for additional styling | ||
| */ | ||
| className?: string; | ||
| }; | ||
|
|
||
| /** | ||
| * A reusable component that displays a group of network avatars. | ||
| * Can fetch networks based on account group ID or accept explicit chain IDs. | ||
| * Handles conversion from CAIP chain IDs to hex format for EVM chains. | ||
| * | ||
| * @param props - The component props | ||
| * @param props.groupId - The account group ID to fetch networks for. When provided, fetches chain IDs from the account group. | ||
| * @param props.chainIds - Array of specific chain IDs to display. Behavior depends on groupId: | ||
| * - If provided with groupId: shows only chains that exist in both the group and this list (intersection) | ||
| * - If provided without groupId: shows only these specific chains | ||
| * @param props.excludeTestNetworks - Whether to exclude test networks from display. Defaults to true. | ||
| * @param props.limit - Maximum number of avatars to display before showing "+X" indicator. Defaults to 4. | ||
| * @param props.className - Optional CSS class name for additional styling | ||
| * @returns A React component displaying network avatars in a group | ||
| */ | ||
| export const MultichainAccountNetworkGroup: React.FC< | ||
| MultichainAccountNetworkGroupProps | ||
| > = ({ | ||
| groupId, | ||
| chainIds, | ||
| excludeTestNetworks = true, | ||
| limit = 4, | ||
| className, | ||
| }) => { | ||
| // Fetch chain IDs from account group if groupId is provided | ||
| const accountGroupScopes = useSelector((state) => | ||
| groupId | ||
| ? getInternalAccountListSpreadByScopesByGroupId(state, groupId) | ||
| : [], | ||
| ); | ||
|
|
||
| const filteredChainIds = useMemo(() => { | ||
| // If only filterChainIds is provided (no groupId), show those chains | ||
| if (chainIds && !groupId) { | ||
| return chainIds; | ||
| } | ||
|
|
||
| // If groupId is provided | ||
| if (groupId && accountGroupScopes.length > 0) { | ||
| // Extract unique chain IDs from account group scopes | ||
| const groupChainIds = new Set<string>(); | ||
| accountGroupScopes.forEach((item) => { | ||
| groupChainIds.add(item.scope); | ||
| }); | ||
|
|
||
| // If filterChainIds is also provided, show intersection | ||
| if (chainIds) { | ||
| const filterSet = new Set(chainIds); | ||
| return Array.from(groupChainIds).filter((chainId) => | ||
| filterSet.has(chainId), | ||
| ); | ||
| } | ||
|
|
||
| // Otherwise, show all chains from the group | ||
| return Array.from(groupChainIds); | ||
| } | ||
|
|
||
| return []; | ||
| }, [chainIds, groupId, accountGroupScopes]); | ||
|
|
||
| const networkData = useMemo(() => { | ||
| if (excludeTestNetworks) { | ||
| // TODO: Add test network filtering logic here | ||
| // For now, we'll keep all networks | ||
| } | ||
|
|
||
| // Define chain priority - these chains will appear first in this order | ||
| const chainPriority: Record<string, number> = { | ||
| // Ethereum mainnet | ||
| 'eip155:1': 1, | ||
| '0x1': 1, | ||
| // Linea mainnet | ||
| 'eip155:59144': 2, | ||
| '0xe708': 2, | ||
| // Solana mainnet | ||
| 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': 3, | ||
| // Bitcoin mainnet | ||
| 'bip122:000000000019d6689c085ae165831e93': 4, | ||
| }; | ||
|
|
||
| // Sort chainIds based on priority | ||
| const sortedChainIds = [...filteredChainIds].sort((a, b) => { | ||
| const priorityA = chainPriority[a] || 999; | ||
| const priorityB = chainPriority[b] || 999; | ||
| return priorityA - priorityB; | ||
| }); | ||
|
|
||
| return sortedChainIds | ||
| .map((chain) => { | ||
| let hexChainId = chain; | ||
| // Convert CAIP chain ID to hex format for EVM chains | ||
| if (chain.startsWith('eip155:')) { | ||
| try { | ||
| hexChainId = convertCaipToHexChainId( | ||
| chain as `${string}:${string}`, | ||
| ); | ||
| } catch { | ||
| // If conversion fails, fall back to using the original chain ID | ||
| hexChainId = chain; | ||
| } | ||
| } | ||
| return { | ||
| avatarValue: | ||
| CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ | ||
| hexChainId as keyof typeof CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP | ||
| ], | ||
| }; | ||
| }) | ||
| .filter((network) => network.avatarValue); // Only include networks with valid avatar images | ||
| }, [filteredChainIds, excludeTestNetworks]); | ||
|
|
||
| return ( | ||
| <Box | ||
| style={{ | ||
| flexShrink: 1, | ||
| width: 'fit-content', | ||
| }} | ||
| > | ||
| <AvatarGroup | ||
| limit={limit} | ||
| members={networkData} | ||
| avatarType={AvatarType.NETWORK} | ||
| className={className} | ||
| /> | ||
| </Box> | ||
| ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
ui/components/multichain-accounts/multichain-address-rows-hovered-list/index.scss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .multichain-address-row { | ||
| transition: background-color 0.3s ease-in-out; | ||
| } | ||
|
|
2 changes: 2 additions & 0 deletions
2
ui/components/multichain-accounts/multichain-address-rows-hovered-list/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { MultichainAggregatedAddressListRow } from './multichain-aggregated-list-row'; | ||
| export { MultichainHoveredAddressRowsList } from './multichain-hovered-address-rows-hovered-list'; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Priority Mismatch: Inconsistent Network Display Order
The
chainPriorityvalues inMultichainAccountNetworkGroupdon't match thePRIORITY_CHAIN_IDSMap inMultichainHoveredAddressRowsList. Bitcoin has priority 4 here but priority 1 in the parent, and Linea has priority 2 here but isn't in the parent's priority list at all. This causes inconsistent ordering where the parent component sorts items one way but the child component displays networks in a different order.