Skip to content

Commit b7ed985

Browse files
runway-github[bot]montelaidevowencrastongauthierpetetin
authored
release(runway): cherry-pick fix: styling of hover components cp-13.10.0 (#37953)
- fix: styling of hover components cp-13.10.0 (#37792) <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This PR fixes the styles in `MultichainHoveredAddressRowsList` and `MultichainAggregatedAddressListRow` Reference to the figma: https://www.figma.com/design/8u9mhomOTy9wsqvrT1aH1G?node-id=1545-5318&m=dev#1510995440 <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/37792?quickstart=1) ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: fix styling in `MultichainHoveredAddressRowsList` and `MultichainAggregatedAddressListRow` components ## **Related issues** Related to: https://consensyssoftware.atlassian.net/browse/MUL-1226?atlOrigin=eyJpIjoiNWRmZTYxYzEyOTI1NDQ1YWE2NTA1OGU0OTA3MGM5NzMiLCJwIjoiaiJ9 ## **Manual testing steps** 1. Go to the wallet home 2. Hover over the network icons 3. Hover over a row and see that it is a pointer. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <img width="992" height="660" alt="Screenshot 2025-11-13 at 21 31 21" src="https://github.com/user-attachments/assets/09c34e13-5eb5-4a26-84ff-224b055ed10e" /> <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Polishes multichain hover list styling and behavior: row-click copy with success state, smaller network avatars with limit, popover/header tweaks, and aligned tests. > > - **UI/UX**: > - **Aggregated Row (`multichain-aggregated-address-row`)**: Row is now clickable for copy; adds hover/success color states, pointer cursor, tighter gaps, fixed height, and right-aligned truncated address with icon; replaces `ButtonIcon` with `Icon`. > - **Network Avatars (`MultichainAccountNetworkGroup`)**: Uses `AvatarTokenSize.Xs`, enforces avatar `limit` via `.slice(0, limit)`, and sets row alignment via `Box` props. > - **Hover Popover (`MultichainHoveredAddressRowsList`)**: Reduces `hoverCloseDelay` to 50ms, adds popover `offset`, updates header typography and balance color. > - **Tests**: > - Update selectors/expectations for new class/test IDs, row-click copy behavior, avatar queries, and hover trigger usage across related specs. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 0f5e648. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Owen Craston <[email protected]> [35d0d22](35d0d22) Co-authored-by: Monte Lai <[email protected]> Co-authored-by: Owen Craston <[email protected]> Co-authored-by: Gauthier Petetin <[email protected]>
1 parent c5b9677 commit b7ed985

File tree

6 files changed

+92
-79
lines changed

6 files changed

+92
-79
lines changed

ui/components/multichain-accounts/multichain-account-network-group/multichain-account-network-group.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import React, { useMemo } from 'react';
22
import { useSelector } from 'react-redux';
33
import { AccountGroupId } from '@metamask/account-api';
4-
import { Box } from '@metamask/design-system-react';
5-
import { AvatarGroup } from '../../multichain/avatar-group';
6-
import { AvatarType } from '../../multichain/avatar-group/avatar-group.types';
4+
import {
5+
Box,
6+
BoxAlignItems,
7+
BoxFlexDirection,
8+
} from '@metamask/design-system-react';
79
import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../shared/constants/network';
810
import { convertCaipToHexChainId } from '../../../../shared/modules/network.utils';
911
import { getInternalAccountListSpreadByScopesByGroupId } from '../../../selectors/multichain-accounts/account-tree';
12+
import { AvatarGroup } from '../../multichain/avatar-group';
13+
import { AvatarTokenSize } from '../../component-library';
14+
import { AvatarType } from '../../multichain/avatar-group/avatar-group.types';
1015

1116
export type MultichainAccountNetworkGroupProps = {
1217
/**
@@ -141,18 +146,22 @@ export const MultichainAccountNetworkGroup: React.FC<
141146
],
142147
};
143148
})
144-
.filter((network) => network.avatarValue); // Only include networks with valid avatar images
145-
}, [filteredChainIds, excludeTestNetworks]);
149+
.filter((network) => network.avatarValue)
150+
.slice(0, limit);
151+
}, [filteredChainIds, excludeTestNetworks, limit]);
146152

147153
return (
148154
<Box
149155
style={{
150156
flexShrink: 1,
151157
width: 'fit-content',
152158
}}
159+
flexDirection={BoxFlexDirection.Row}
160+
alignItems={BoxAlignItems.Center}
153161
>
154162
<AvatarGroup
155163
limit={limit}
164+
size={AvatarTokenSize.Xs}
156165
members={networkData}
157166
avatarType={AvatarType.NETWORK}
158167
className={className}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
.multichain-address-row {
1+
.multichain-aggregated-address-row {
22
transition: background-color 0.3s ease-in-out;
3+
cursor: pointer;
4+
margin-left: -16px;
5+
margin-right: -16px;
6+
padding-left: 16px;
7+
padding-right: 16px;
8+
height: 32px;
39
}
410

ui/components/multichain-accounts/multichain-address-rows-hovered-list/multichain-aggregated-list-row.test.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const TEST_IDS = {
4848
} as const;
4949

5050
const CSS_CLASSES = {
51-
MULTICHAIN_ADDRESS_ROW: 'multichain-address-row',
51+
MULTICHAIN_ADDRESS_ROW: 'multichain-aggregated-address-row',
5252
CUSTOM_CLASS: 'custom-class',
5353
} as const;
5454

@@ -229,13 +229,10 @@ describe('MultichainAggregatedAddressListRow', () => {
229229
const row = screen.getByTestId(TEST_IDS.MULTICHAIN_ADDRESS_ROW);
230230
expect(row).toBeInTheDocument();
231231

232-
// Verify avatar group is rendered
233-
const avatarGroup = screen.getByTestId(TEST_IDS.AVATAR_GROUP);
234-
expect(avatarGroup).toBeInTheDocument();
235-
236232
// Verify network avatars are displayed
237233
// Note: Only networks with valid images will be rendered
238-
const networkAvatars = screen.getAllByAltText(ALT_TEXTS.NETWORK_LOGO);
234+
const networkAvatars = screen.queryAllByAltText(ALT_TEXTS.NETWORK_LOGO);
235+
239236
expect(networkAvatars.length).toBeGreaterThanOrEqual(1);
240237
expect(networkAvatars.length).toBeLessThanOrEqual(chainIds.length);
241238

@@ -274,7 +271,7 @@ describe('MultichainAggregatedAddressListRow', () => {
274271
});
275272

276273
describe('Copy Functionality', () => {
277-
it('executes copy callback when copy button is clicked', () => {
274+
it('executes copy callback when row is clicked', () => {
278275
const mockCallback = jest.fn();
279276
const props = createTestProps({
280277
copyActionParams: {
@@ -289,13 +286,13 @@ describe('MultichainAggregatedAddressListRow', () => {
289286
</Provider>,
290287
);
291288

292-
const copyButton = screen.getByRole('button');
293-
fireEvent.click(copyButton);
289+
const row = screen.getByTestId(TEST_IDS.MULTICHAIN_ADDRESS_ROW);
290+
fireEvent.click(row);
294291

295292
expect(mockCallback).toHaveBeenCalled();
296293
});
297294

298-
it('shows copy message after clicking copy button', () => {
295+
it('shows copy message after clicking row', () => {
299296
const props = createTestProps();
300297

301298
render(
@@ -304,15 +301,15 @@ describe('MultichainAggregatedAddressListRow', () => {
304301
</Provider>,
305302
);
306303

307-
const copyButton = screen.getByRole('button');
304+
const row = screen.getByTestId(TEST_IDS.MULTICHAIN_ADDRESS_ROW);
308305

309306
// Initially should show the truncated address
310307
expect(
311308
screen.getByText(TEST_STRINGS.TRUNCATED_ADDRESS),
312309
).toBeInTheDocument();
313310

314-
// Click copy button
315-
fireEvent.click(copyButton);
311+
// Click row
312+
fireEvent.click(row);
316313

317314
// Should show the copy message
318315
expect(screen.getByText(TEST_STRINGS.COPY_MESSAGE)).toBeInTheDocument();
@@ -328,8 +325,8 @@ describe('MultichainAggregatedAddressListRow', () => {
328325
</Provider>,
329326
);
330327

331-
const copyButton = screen.getByRole('button');
332-
fireEvent.click(copyButton);
328+
const row = screen.getByTestId(TEST_IDS.MULTICHAIN_ADDRESS_ROW);
329+
fireEvent.click(row);
333330

334331
expect(props.copyActionParams.callback).toHaveBeenCalled();
335332

@@ -354,8 +351,7 @@ describe('MultichainAggregatedAddressListRow', () => {
354351

355352
expect(row).toHaveClass(CSS_CLASSES.MULTICHAIN_ADDRESS_ROW);
356353

357-
const copyButton = screen.getByRole('button');
358-
fireEvent.click(copyButton);
354+
fireEvent.click(row);
359355

360356
expect(props.copyActionParams.callback).toHaveBeenCalled();
361357
});
@@ -369,16 +365,16 @@ describe('MultichainAggregatedAddressListRow', () => {
369365
</Provider>,
370366
);
371367

372-
const copyButton = screen.getByRole('button');
373-
fireEvent.click(copyButton);
368+
const row = screen.getByTestId(TEST_IDS.MULTICHAIN_ADDRESS_ROW);
369+
fireEvent.click(row);
374370

375371
expect(props.copyActionParams.callback).toHaveBeenCalled();
376372

377373
act(() => {
378374
jest.advanceTimersByTime(1000);
379375
});
380376

381-
fireEvent.click(copyButton);
377+
fireEvent.click(row);
382378
expect(props.copyActionParams.callback).toHaveBeenCalled();
383379
});
384380
});

ui/components/multichain-accounts/multichain-address-rows-hovered-list/multichain-aggregated-list-row.tsx

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@ import {
55
BoxBackgroundColor,
66
BoxFlexDirection,
77
BoxJustifyContent,
8-
ButtonIconSize,
98
FontWeight,
9+
Icon,
10+
IconName,
11+
IconColor,
12+
IconSize,
1013
Text,
1114
TextColor,
1215
TextVariant,
16+
TextAlign,
1317
} from '@metamask/design-system-react';
1418
import { useSelector } from 'react-redux';
1519
import { shortenAddress } from '../../../helpers/utils/util';
1620

1721
import { useI18nContext } from '../../../hooks/useI18nContext';
1822
import { CopyParams } from '../multichain-address-row/multichain-address-row';
1923
import { getNetworksByScopes } from '../../../../shared/modules/selectors/networks';
20-
import { ButtonIcon, IconName } from '../../component-library';
21-
import { IconColor } from '../../../helpers/constants/design-system';
2224
import { MultichainAccountNetworkGroup } from '../multichain-account-network-group';
2325
// eslint-disable-next-line import/no-restricted-paths
2426
import { normalizeSafeAddress } from '../../../../app/scripts/lib/multichain/address';
@@ -55,7 +57,6 @@ export const MultichainAggregatedAddressListRow = ({
5557
const [copyIcon, setCopyIcon] = useState(IconName.Copy); // Default copy icon state
5658
const [addressCopied, setAddressCopied] = useState(false);
5759
const [isHovered, setIsHovered] = useState(false);
58-
5960
// Track timeout ID for managing `setTimeout`
6061
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
6162

@@ -100,12 +101,12 @@ export const MultichainAggregatedAddressListRow = ({
100101
// Helper function to get icon color based on state
101102
const getIconColor = () => {
102103
if (addressCopied) {
103-
return IconColor.successDefault;
104+
return IconColor.SuccessDefault;
104105
}
105106
if (isHovered) {
106-
return IconColor.primaryDefault;
107+
return IconColor.PrimaryDefault;
107108
}
108-
return IconColor.iconAlternative;
109+
return IconColor.IconAlternative;
109110
};
110111

111112
const getBackgroundColor = useMemo(() => {
@@ -144,25 +145,21 @@ export const MultichainAggregatedAddressListRow = ({
144145

145146
return (
146147
<Box
147-
className={`multichain-address-row ${className}`}
148-
style={{
149-
marginLeft: -16,
150-
marginRight: -16,
151-
paddingLeft: 16,
152-
paddingRight: 16,
153-
}}
148+
className={`multichain-aggregated-address-row ${className}`}
154149
flexDirection={BoxFlexDirection.Row}
155150
alignItems={BoxAlignItems.Center}
156151
justifyContent={BoxJustifyContent.Between}
157-
gap={4}
152+
gap={3}
153+
paddingTop={1}
154+
paddingBottom={1}
158155
data-testid="multichain-address-row"
159156
backgroundColor={getBackgroundColor}
160157
onClick={handleCopyClick}
161158
onMouseEnter={() => setIsHovered(true)}
162159
onMouseLeave={() => setIsHovered(false)}
163160
>
164161
<Box
165-
gap={4}
162+
gap={3}
166163
flexDirection={BoxFlexDirection.Row}
167164
alignItems={BoxAlignItems.Center}
168165
>
@@ -174,20 +171,25 @@ export const MultichainAggregatedAddressListRow = ({
174171
<Box
175172
gap={1}
176173
flexDirection={BoxFlexDirection.Row}
174+
justifyContent={BoxJustifyContent.End}
177175
alignItems={BoxAlignItems.Center}
178176
>
179177
<Text
180178
variant={TextVariant.BodyXs}
181179
fontWeight={FontWeight.Medium}
180+
textAlign={TextAlign.Right}
182181
color={getTextColor()}
182+
style={{
183+
width: '100px',
184+
}}
183185
>
184186
{displayText}
185187
</Text>
186-
<ButtonIcon
187-
iconName={copyIcon}
188-
size={ButtonIconSize.Sm}
188+
<Icon
189+
name={copyIcon}
190+
size={IconSize.Xs}
189191
color={getIconColor()}
190-
ariaLabel={t('copyAddressShort')}
192+
aria-label={t('copyAddressShort')}
191193
/>
192194
</Box>
193195
</Box>

0 commit comments

Comments
 (0)