Skip to content

Commit 65769f1

Browse files
authored
feat: MCv1 Permissions: Site Connections Tooltip (#23189)
## **Description** A follow-up PR to add tooltips to ConnectionListItem. <!-- 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/23189?quickstart=1) ## **Related issues** Fixes: https://github.com/MetaMask/MetaMask-planning/issues/1980 ## **Manual testing steps** 1. Open Metamask 2. Connect to a dapp on at least 8 accounts 3. Go To "All Permissions" screen from the global menu item 4. Hover over an Account AvatarGroup 5. Verify that the tooltip shows a maximum of 7 accounts with blockies/jazzicons and has a "+X more" representing the accounts we could not show in the tooltip. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** No tooltip <!-- [screenshots/recordings] --> ### **After** <img width="358" alt="image" src="https://github.com/MetaMask/metamask-extension/assets/10986371/158aa934-c22d-4094-890e-f6ab81bd31b1"> <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've clearly explained what problem this PR is solving and how it is solved. - [ ] I've linked related issues - [ ] I've included manual testing steps - [ ] I've included screenshots/recordings if applicable - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. - [ ] I’ve properly set the pull request status: - [ ] In case it's not yet "ready for review", I've set it to "draft". - [ ] In case it's "ready for review", I've changed it from "draft" to "non-draft". ## **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.
1 parent dec5421 commit 65769f1

File tree

8 files changed

+338
-56
lines changed

8 files changed

+338
-56
lines changed

app/_locales/en/messages.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/components/multichain/pages/permissions-page/__snapshots__/permissions-page.test.js.snap

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ exports[`All Connections render renders correctly 1`] = `
5454
</p>
5555
<button
5656
class="mm-box mm-box--padding-4 mm-box--display-flex mm-box--gap-4 mm-box--flex-direction-row mm-box--align-items-baseline mm-box--width-full mm-box--background-color-background-default"
57+
data-testid="connection-list-item"
5758
>
5859
<div
5960
class="mm-box mm-box--display-flex mm-box--align-items-center"
@@ -64,6 +65,7 @@ exports[`All Connections render renders correctly 1`] = `
6465
>
6566
<div
6667
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-md mm-avatar-favicon mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-transparent box--border-style-solid box--border-width-1"
68+
data-testid="connection-list-item__avatar-favicon"
6769
>
6870
<img
6971
alt="avatar-favicon logo"
@@ -91,64 +93,72 @@ exports[`All Connections render renders correctly 1`] = `
9193
metamask.github.io
9294
</p>
9395
<div
94-
class="mm-box mm-box--display-flex mm-box--gap-1 mm-box--flex-direction-row"
96+
class="mm-box mm-box--display-flex mm-box--gap-1 mm-box--flex-direction-row mm-box--align-items-center"
9597
>
9698
<span
9799
class="mm-box mm-text mm-text--body-md mm-box--width-max mm-box--color-text-alternative"
98100
>
99101
Connected with
100102
</span>
101103
<div
102-
class="mm-box multichain-avatar-group mm-box--display-flex mm-box--gap-1 mm-box--align-items-center"
103-
data-testid="avatar-group"
104+
aria-describedby="tippy-tooltip-1"
105+
class=""
106+
data-original-title="This can be changed in "Settings > Alerts""
107+
data-tooltipped=""
108+
style="display: inline;"
104109
>
105110
<div
106-
class="mm-box mm-box--display-flex"
111+
class="mm-box multichain-avatar-group mm-box--display-flex mm-box--gap-1 mm-box--align-items-center"
112+
data-testid="avatar-group"
107113
>
108114
<div
109-
class="mm-box mm-box--rounded-full"
110-
style="margin-left: 0px;"
115+
class="mm-box mm-box--display-flex"
111116
>
112117
<div
113-
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-account mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-background-default box--border-style-solid box--border-width-1"
118+
class="mm-box mm-box--rounded-full"
119+
style="margin-left: 0px;"
114120
>
115121
<div
116-
class="mm-avatar-account__jazzicon"
122+
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-account mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-background-default box--border-style-solid box--border-width-1"
117123
>
118124
<div
119-
style="border-radius: 50px; overflow: hidden; padding: 0px; margin: 0px; width: 16px; height: 16px; display: inline-block; background: rgb(250, 58, 0);"
125+
class="mm-avatar-account__jazzicon"
120126
>
121-
<svg
122-
height="16"
123-
width="16"
124-
x="0"
125-
y="0"
127+
<div
128+
style="border-radius: 50px; overflow: hidden; padding: 0px; margin: 0px; width: 16px; height: 16px; display: inline-block; background: rgb(250, 58, 0);"
126129
>
127-
<rect
128-
fill="#18CDF2"
130+
<svg
129131
height="16"
130-
transform="translate(-0.52419675189697 -1.6521420347302493) rotate(328.9 8 8)"
131132
width="16"
132133
x="0"
133134
y="0"
134-
/>
135-
<rect
136-
fill="#035E56"
137-
height="16"
138-
transform="translate(-9.149230854416022 5.2962309358743) rotate(176.2 8 8)"
139-
width="16"
140-
x="0"
141-
y="0"
142-
/>
143-
<rect
144-
fill="#F26602"
145-
height="16"
146-
transform="translate(8.333921009111961 -7.102569861498541) rotate(468.9 8 8)"
147-
width="16"
148-
x="0"
149-
y="0"
150-
/>
151-
</svg>
135+
>
136+
<rect
137+
fill="#18CDF2"
138+
height="16"
139+
transform="translate(-0.52419675189697 -1.6521420347302493) rotate(328.9 8 8)"
140+
width="16"
141+
x="0"
142+
y="0"
143+
/>
144+
<rect
145+
fill="#035E56"
146+
height="16"
147+
transform="translate(-9.149230854416022 5.2962309358743) rotate(176.2 8 8)"
148+
width="16"
149+
x="0"
150+
y="0"
151+
/>
152+
<rect
153+
fill="#F26602"
154+
height="16"
155+
transform="translate(8.333921009111961 -7.102569861498541) rotate(468.9 8 8)"
156+
width="16"
157+
x="0"
158+
y="0"
159+
/>
160+
</svg>
161+
</div>
152162
</div>
153163
</div>
154164
</div>
@@ -175,13 +185,14 @@ exports[`All Connections render renders correctly 1`] = `
175185
</p>
176186
<button
177187
class="mm-box mm-box--padding-4 mm-box--display-flex mm-box--gap-4 mm-box--flex-direction-row mm-box--align-items-baseline mm-box--width-full mm-box--background-color-background-default"
188+
data-testid="connection-list-item"
178189
>
179190
<div
180191
class="mm-box mm-box--display-flex mm-box--align-items-center"
181192
style="align-self: center;"
182193
>
183194
<div
184-
class="mm-box mm-badge-wrapper snap-avatar mm-box--display-inline-block"
195+
class="mm-box mm-badge-wrapper snap-avatar connection-list-item__snap-avatar mm-box--display-inline-block"
185196
>
186197
<div
187198
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-md mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-alternative mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-border-default box--border-style-solid box--border-width-1"
@@ -225,13 +236,14 @@ exports[`All Connections render renders correctly 1`] = `
225236
</button>
226237
<button
227238
class="mm-box mm-box--padding-4 mm-box--display-flex mm-box--gap-4 mm-box--flex-direction-row mm-box--align-items-baseline mm-box--width-full mm-box--background-color-background-default"
239+
data-testid="connection-list-item"
228240
>
229241
<div
230242
class="mm-box mm-box--display-flex mm-box--align-items-center"
231243
style="align-self: center;"
232244
>
233245
<div
234-
class="mm-box mm-badge-wrapper snap-avatar mm-box--display-inline-block"
246+
class="mm-box mm-badge-wrapper snap-avatar connection-list-item__snap-avatar mm-box--display-inline-block"
235247
>
236248
<div
237249
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-md mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-alternative mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-border-default box--border-style-solid box--border-width-1"
@@ -275,13 +287,14 @@ exports[`All Connections render renders correctly 1`] = `
275287
</button>
276288
<button
277289
class="mm-box mm-box--padding-4 mm-box--display-flex mm-box--gap-4 mm-box--flex-direction-row mm-box--align-items-baseline mm-box--width-full mm-box--background-color-background-default"
290+
data-testid="connection-list-item"
278291
>
279292
<div
280293
class="mm-box mm-box--display-flex mm-box--align-items-center"
281294
style="align-self: center;"
282295
>
283296
<div
284-
class="mm-box mm-badge-wrapper snap-avatar mm-box--display-inline-block"
297+
class="mm-box mm-badge-wrapper snap-avatar connection-list-item__snap-avatar mm-box--display-inline-block"
285298
>
286299
<div
287300
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-md mm-text--body-sm mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-alternative mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-border-default box--border-style-solid box--border-width-1"

ui/components/multichain/pages/permissions-page/connection-list-item.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,15 @@ import {
2525
} from '../../../component-library';
2626
import { getURLHost } from '../../../../helpers/utils/util';
2727
import SnapAvatar from '../../../app/snaps/snap-avatar/snap-avatar';
28-
import { AvatarGroup } from '../../avatar-group';
29-
import { AvatarType } from '../../avatar-group/avatar-group.types';
28+
import { ConnectionListTooltip } from './connection-list-tooltip/connection-list-tooltip';
3029

3130
export const ConnectionListItem = ({ connection, onClick }) => {
3231
const t = useI18nContext();
3332
const isSnap = connection.subjectType === SubjectType.Snap;
34-
const AVATAR_GROUP_LIMIT = 5;
35-
const addressIconList = connection.addresses?.map((address) => ({
36-
avatarValue: address,
37-
}));
3833

3934
return (
4035
<Box
36+
data-testid="connection-list-item"
4137
as="button"
4238
display={Display.Flex}
4339
flexDirection={FlexDirection.Row}
@@ -55,6 +51,7 @@ export const ConnectionListItem = ({ connection, onClick }) => {
5551
>
5652
{isSnap ? (
5753
<SnapAvatar
54+
className="connection-list-item__snap-avatar"
5855
snapId={connection.id}
5956
badgeSize={IconSize.Xs}
6057
avatarSize={IconSize.Md}
@@ -71,7 +68,10 @@ export const ConnectionListItem = ({ connection, onClick }) => {
7168
/>
7269
}
7370
>
74-
<AvatarFavicon src={connection.iconUrl} />
71+
<AvatarFavicon
72+
data-testid="connection-list-item__avatar-favicon"
73+
src={connection.iconUrl}
74+
/>
7575
</BadgeWrapper>
7676
)}
7777
</Box>
@@ -85,7 +85,12 @@ export const ConnectionListItem = ({ connection, onClick }) => {
8585
{isSnap ? connection.packageName : getURLHost(connection.origin)}
8686
</Text>
8787
{isSnap ? null : (
88-
<Box display={Display.Flex} flexDirection={FlexDirection.Row} gap={1}>
88+
<Box
89+
display={Display.Flex}
90+
flexDirection={FlexDirection.Row}
91+
alignItems={AlignItems.center}
92+
gap={1}
93+
>
8994
<Text
9095
as="span"
9196
width={BlockSize.Max}
@@ -94,12 +99,7 @@ export const ConnectionListItem = ({ connection, onClick }) => {
9499
>
95100
{t('connectedWith')}
96101
</Text>
97-
<AvatarGroup
98-
members={addressIconList}
99-
limit={AVATAR_GROUP_LIMIT}
100-
avatarType={AvatarType.ACCOUNT}
101-
borderColor={BackgroundColor.backgroundDefault}
102-
/>
102+
<ConnectionListTooltip connection={connection} />
103103
</Box>
104104
)}
105105
</Box>

ui/components/multichain/pages/permissions-page/connection-list-item.stories.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@ export default {
2121
name: 'MetaMask Portfolio',
2222
origin: 'https://portfolio.metamask.io',
2323
subjectType: 'website',
24-
addresses: ['0xTestAddress1', '0xTestAddress2'],
24+
addresses: [
25+
'0xaaaF07C80ce267F3132cE7e6048B66E6E669365B',
26+
'0xbbbD671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1',
27+
],
28+
addressToNameMap: {
29+
'0xaaaF07C80ce267F3132cE7e6048B66E6E669365B': 'TestAddress1',
30+
'0xbbbD671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1': 'TestAddress2',
31+
},
2532
},
2633
onClick: () => console.log('clicked'),
2734
},
@@ -63,6 +70,18 @@ ChaosStory.args = {
6370
'0x777F07C80ce267F3132cE7e6048B66E6E669365B',
6471
'0x666D671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1',
6572
],
73+
addressToNameMap: {
74+
'0xaaaF07C80ce267F3132cE7e6048B66E6E669365B': 'TestAddress1',
75+
'0xbbbD671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1': 'TestAddress2',
76+
'0xcccF07C80ce267F3132cE7e6048B66E6E669365B': 'TestAddress3',
77+
'0xdddD671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1': 'TestAddress4',
78+
'0xeeeF07C80ce267F3132cE7e6048B66E6E669365B': 'TestAddress5',
79+
'0xfffD671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1': 'TestAddress6',
80+
'0x999F07C80ce267F3132cE7e6048B66E6E669365B': 'TestAddress7',
81+
'0x888D671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1': 'TestAddress8',
82+
'0x777F07C80ce267F3132cE7e6048B66E6E669365B': 'TestAddress9',
83+
'0x666D671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1': 'TestAddress10',
84+
},
6685
},
6786
onClick: () => {
6887
console.log(
@@ -85,7 +104,14 @@ SnapStory.args = {
85104
name: 'Connect'.repeat(100),
86105
packageName: '@metamask/storybooksnap',
87106
subjectType: 'snap',
88-
addresses: ['OxTestAddress1', 'OxTestAddress2'],
107+
addresses: [
108+
'0xaaaF07C80ce267F3132cE7e6048B66E6E669365B',
109+
'0xbbbD671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1',
110+
],
111+
addressToNameMap: {
112+
'0xaaaF07C80ce267F3132cE7e6048B66E6E669365B': 'TestAddress1',
113+
'0xbbbD671F1Fcc94bCF0ebC6Ec4790Da35E8d5e1E1': 'TestAddress2',
114+
},
89115
},
90116
onClick: () =>
91117
console.log(
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React from 'react';
2+
import { fireEvent } from '@testing-library/react';
3+
import configureStore from '../../../../store/store';
4+
import mockState from '../../../../../test/data/mock-state.json';
5+
import { getURLHost } from '../../../../helpers/utils/util';
6+
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
7+
import { ConnectionListItem } from './connection-list-item';
8+
9+
describe('ConnectionListItem', () => {
10+
const store = configureStore(mockState);
11+
12+
it('renders correctly for Snap connection', () => {
13+
const mockConnection = {
14+
id: 'npm:@metamask/testSnap1',
15+
origin: 'npm:@metamask/testSnap1',
16+
packageName: 'Test Snap 1',
17+
subjectType: 'snap',
18+
iconUrl: null,
19+
};
20+
const { getByText, getByTestId } = renderWithProvider(
21+
<ConnectionListItem connection={mockConnection} onClick={jest.fn()} />,
22+
store,
23+
);
24+
25+
expect(getByTestId('connection-list-item')).toBeInTheDocument();
26+
expect(getByText('Test Snap 1')).toBeInTheDocument();
27+
expect(
28+
document.querySelector('.connection-list-item__snap-avatar'),
29+
).toBeInTheDocument();
30+
});
31+
32+
it('renders correctly for non-Snap connection', () => {
33+
const mockConnection = {
34+
id: 'https://metamask.github.io',
35+
origin: 'https://metamask.github.io',
36+
subjectType: 'website',
37+
iconUrl: 'https://metamask.github.io/test-dapp/metamask-fox.svg',
38+
};
39+
40+
const { getByText, getByTestId } = renderWithProvider(
41+
<ConnectionListItem connection={mockConnection} onClick={jest.fn()} />,
42+
store,
43+
);
44+
45+
expect(getByTestId('connection-list-item')).toBeInTheDocument();
46+
expect(
47+
getByText(getURLHost('https://metamask.github.io')),
48+
).toBeInTheDocument();
49+
expect(
50+
getByTestId('connection-list-item__avatar-favicon'),
51+
).toBeInTheDocument();
52+
});
53+
54+
it('calls onClick when clicked', () => {
55+
const onClickMock = jest.fn();
56+
const mockConnection = {
57+
id: 'npm:@metamask/testSnap1',
58+
origin: 'npm:@metamask/testSnap1',
59+
packageName: 'Test Snap 1',
60+
subjectType: 'snap',
61+
iconUrl: null,
62+
};
63+
const { getByTestId } = renderWithProvider(
64+
<ConnectionListItem connection={mockConnection} onClick={onClickMock} />,
65+
store,
66+
);
67+
68+
fireEvent.click(getByTestId('connection-list-item'));
69+
expect(onClickMock).toHaveBeenCalledTimes(1);
70+
});
71+
});

0 commit comments

Comments
 (0)