Skip to content

Commit c980411

Browse files
feat: updated delete networks flow (#43499)
This PR is to: 1. Change the text "delete" to disable for enabled networks and do not show modal for these networks 2. For Custom, networks show the text "delete" and on Click show the confirmation flow 3. Delete modal UI will be updated in a different PR ## **Description** <!-- 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? 4. What is the improvement/solution? --> ## **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: changed delete code for networks ## **Related issues** Fixes: ## **Manual testing steps** 1. Run extension 2. Go to Manage Networks flow in global menu 3. Click kebab menu for enabled networks, check text is disable for enabled networks and on click it does not show delete confirmation modal for these networks 5. For Custom, networks show the text "delete" and on Click show the confirmation flow ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** https://github.com/user-attachments/assets/e27f8ba9-7d11-4fa6-9e71-1512388a9d4c https://github.com/user-attachments/assets/d6da5849-ba53-4dc6-a66e-9263ce0ef1d8 ## **Pre-merge author checklist** - [ ] 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). - [ ] I've completed the PR template to the best of my ability - [ ] 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/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] > **Medium Risk** > Changes when networks are removed without confirmation (featured defaults), which affects active-network edge cases; mainnet remains protected but mistaken disable could surprise users. > > **Overview** > Network list menus now distinguish **featured default** chains from **custom** ones: defaults (except Ethereum mainnet) show **Disable**, call `removeNetwork` immediately, and skip the confirmation modal; custom EVM networks still show **Delete** and open `CONFIRM_DELETE_NETWORK`. > > Logic is centralized via `isDisableableDefaultNetwork` and shared through `useNetworkItemCallbacks` / `network-list-menu`, with a `deleteMenuLabel` prop on list items and menus. A new **disable** locale string was added. > > The confirm-delete modal was rebuilt on `@metamask/design-system-react` (danger icon, `data-testid="confirm-delete-network-modal-delete-button"`). E2E helpers add `disableNetwork` and target the new confirm button; unit tests cover disable vs delete behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 5afc20d. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 8354cbe commit c980411

19 files changed

Lines changed: 623 additions & 153 deletions

File tree

app/_locales/en/messages.json

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

app/_locales/en_GB/messages.json

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

test/e2e/page-objects/pages/dialog/select-network.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ class SelectNetwork {
1010

1111
private readonly closeButton = '[data-testid="settings-header-back-button"]';
1212

13-
private readonly confirmDeleteNetworkButton = {
14-
text: 'Delete',
15-
tag: 'button',
16-
};
13+
private readonly confirmDeleteNetworkButton =
14+
'[data-testid="confirm-delete-network-modal-delete-button"]';
1715

1816
private readonly confirmDeleteNetworkModal = {
1917
testId: 'confirm-delete-network-modal',
@@ -121,7 +119,22 @@ class SelectNetwork {
121119
}
122120

123121
/**
124-
* Delete a network from the network list.
122+
* Disable a featured default network from the network list.
123+
* Default networks are removed immediately without a confirmation modal.
124+
*
125+
* @param chainId - The chain ID of the network to disable.
126+
*/
127+
async disableNetwork(chainId: string): Promise<void> {
128+
console.log(`Disable network ${chainId} from network list`);
129+
await this.openNetworkListOptions(chainId);
130+
await this.driver.clickElement(this.deleteNetworkButton);
131+
await this.driver.assertElementNotPresent(this.confirmDeleteNetworkModal, {
132+
waitAtLeastGuard: 200,
133+
});
134+
}
135+
136+
/**
137+
* Delete a custom network from the network list.
125138
*
126139
* @param chainId - The chain ID of the network to delete.
127140
*/

test/e2e/page-objects/pages/network-manager.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,8 @@ class NetworkManager {
5252

5353
private readonly networkManagerToggle = '[data-testid="sort-by-networks"]';
5454

55-
private readonly networkPopupDeleteButton = {
56-
text: 'Delete',
57-
tag: 'button',
58-
};
55+
private readonly networkPopupDeleteButton =
56+
'[data-testid="confirm-delete-network-modal-delete-button"]';
5957

6058
private readonly selectedNetworkListItem = (selector: string) =>
6159
`:is(${selector}.multichain-network-list-item--selected, ${selector} .multichain-network-list-item--selected)`;

test/e2e/tests/network/popular-network.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ describe('Popular Networks', function (this: Suite) {
9696
);
9797
});
9898

99-
it('delete the Arbitrum network', async function () {
99+
it('disable the Arbitrum network', async function () {
100100
await withFixtures(
101101
{
102102
fixtures: new FixtureBuilderV2().build(),
@@ -110,7 +110,7 @@ describe('Popular Networks', function (this: Suite) {
110110

111111
const selectNetworkDialog = new SelectNetwork(driver);
112112
await selectNetworkDialog.checkPageIsLoaded();
113-
await selectNetworkDialog.deleteNetwork('eip155:42161');
113+
await selectNetworkDialog.disableNetwork('eip155:42161');
114114
await selectNetworkDialog.clickCloseButton();
115115
await headerNavbar.clickDrawerBackButton();
116116

test/jest/console-baseline-unit.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,6 @@
256256
"React: componentWill* lifecycle deprecations": 1,
257257
"error: Error: MetaMetrics context trackEvent was": 3
258258
},
259-
"ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js": {
260-
"React: PropTypes validation failures": 1,
261-
"error: Error: Insufficient number of substitutions": 1
262-
},
263259
"ui/components/app/modals/customize-nonce/customize-nonce.test.js": {
264260
"React: PropTypes validation failures": 1
265261
},
Lines changed: 107 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,123 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`Confirm Delete Network should match snapshot 1`] = `
4-
<div>
4+
<body>
55
<div
6-
class="modal-container"
6+
id="popover-content"
7+
/>
8+
<div />
9+
<div
10+
class=""
711
data-testid="confirm-delete-network-modal"
812
>
913
<div
10-
class="modal-container__content"
14+
aria-hidden="true"
15+
class="bg-overlay-default fixed inset-0 z-[1050] motion-safe:animate-fade-in"
16+
/>
17+
<div
18+
data-focus-guard="true"
19+
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
20+
tabindex="0"
21+
/>
22+
<div
23+
data-focus-lock-disabled="false"
1124
>
1225
<div
13-
class="modal-content"
26+
class="fixed inset-0 z-[1050] flex justify-center [@media(max-height:475px)]:p-2 items-center p-4 sm:py-6 md:py-6"
1427
>
15-
<div
16-
class="modal-content__title"
17-
>
18-
Delete Goerli network?
19-
</div>
20-
<div
21-
class="modal-content__description"
28+
<section
29+
aria-modal="true"
30+
class="flex-col items-stretch justify-start bg-default flex max-h-full w-full rounded-lg motion-safe:animate-slide-up max-w-[360px] overflow-hidden p-4 shadow-lg"
31+
role="dialog"
2232
>
23-
If you delete this network, you will need to add it again to view your assets in this network
24-
</div>
33+
<header
34+
class="grid grid-cols-[1fr_auto_1fr] items-center px-0 pb-4"
35+
>
36+
<div
37+
class="col-start-2 col-end-3 w-full"
38+
>
39+
<div
40+
class="flex flex-col gap-2 items-center min-w-0"
41+
>
42+
<svg
43+
class="inline-block w-8 h-8 text-error-default"
44+
fill="currentColor"
45+
viewBox="0 0 24 24"
46+
xmlns="http://www.w3.org/2000/svg"
47+
>
48+
<path
49+
d="M1 21 12 2l11 19zm3.45-2h15.1L12 6zM12 18q.424 0 .713-.287c.289-.288.287-.43.287-.713s-.096-.52-.287-.712S12.283 16 12 16s-.52.096-.712.288S11 16.717 11 17s.096.52.288.713.429.287.712.287m-1-3h2v-5h-2z"
50+
/>
51+
</svg>
52+
<h2
53+
class="text-default text-s-heading-sm leading-s-heading-sm tracking-s-heading-sm md:text-l-heading-sm md:leading-l-heading-sm md:tracking-l-heading-sm font-bold font-default text-center"
54+
>
55+
Delete Goerli network?
56+
</h2>
57+
</div>
58+
</div>
59+
<div
60+
class="col-start-3 justify-self-end"
61+
>
62+
<button
63+
aria-label="Close"
64+
class="inline-flex items-center justify-center p-0 h-8 w-8 rounded-lg bg-transparent hover:bg-hover active:bg-pressed text-icon-default"
65+
>
66+
<svg
67+
class="inline-block w-6 h-6 text-inherit"
68+
fill="currentColor"
69+
viewBox="0 0 24 24"
70+
xmlns="http://www.w3.org/2000/svg"
71+
>
72+
<path
73+
d="M6.4 19 5 17.6l5.6-5.6L5 6.4 6.4 5l5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6z"
74+
/>
75+
</svg>
76+
</button>
77+
</div>
78+
</header>
79+
<p
80+
class="text-default text-s-body-md leading-s-body-md tracking-s-body-md md:text-l-body-md md:leading-l-body-md md:tracking-l-body-md font-regular font-default w-full"
81+
>
82+
If you delete this network, you will need to add it again to view your assets in this network
83+
</p>
84+
<footer
85+
class="px-0 pt-6"
86+
>
87+
<div
88+
class="flex flex-row gap-4"
89+
>
90+
<button
91+
class="inline-flex items-center justify-center rounded-xl px-4 font-medium overflow-hidden relative h-12 transition-all duration-100 ease-linear active:scale-[0.97] active:ease-[cubic-bezier(0.3,0.8,0.3,1)] bg-muted text-default hover:bg-muted-hover active:bg-muted-pressed focus-visible:ring-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-default flex-1"
92+
role="button"
93+
>
94+
<span
95+
class="text-inherit text-s-body-md leading-s-body-md tracking-s-body-md md:text-l-body-md md:leading-l-body-md md:tracking-l-body-md font-medium font-default"
96+
>
97+
Cancel
98+
</span>
99+
</button>
100+
<button
101+
class="inline-flex items-center justify-center rounded-xl px-4 font-medium overflow-hidden relative h-12 transition-all duration-100 ease-linear active:scale-[0.97] active:ease-[cubic-bezier(0.3,0.8,0.3,1)] bg-error-default text-error-inverse hover:bg-error-default-hover active:bg-error-default-pressed focus-visible:ring-0 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-default flex-1"
102+
data-testid="confirm-delete-network-modal-delete-button"
103+
role="button"
104+
>
105+
<span
106+
class="text-inherit text-s-body-md leading-s-body-md tracking-s-body-md md:text-l-body-md md:leading-l-body-md md:tracking-l-body-md font-medium font-default"
107+
>
108+
Delete
109+
</span>
110+
</button>
111+
</div>
112+
</footer>
113+
</section>
25114
</div>
26115
</div>
27116
<div
28-
class="modal-container__footer"
29-
>
30-
<button
31-
class="button btn-secondary modal-container__footer-button"
32-
>
33-
Cancel
34-
</button>
35-
<button
36-
class="button btn-danger-primary modal-container__footer-button"
37-
>
38-
Delete
39-
</button>
40-
</div>
117+
data-focus-guard="true"
118+
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
119+
tabindex="0"
120+
/>
41121
</div>
42-
</div>
122+
</body>
43123
`;

ui/components/app/modals/confirm-delete-network/confirm-delete-network.component.js

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
import React, { PureComponent } from 'react';
22
import PropTypes from 'prop-types';
33
import { toEvmCaipChainId } from '@metamask/multichain-network-controller';
4+
import {
5+
Box,
6+
BoxAlignItems,
7+
BoxFlexDirection,
8+
ButtonIconSize,
9+
ButtonSize,
10+
ButtonVariant,
11+
Icon,
12+
IconColor,
13+
IconName,
14+
IconSize,
15+
Modal,
16+
ModalFooter,
17+
ModalHeader,
18+
ModalContent,
19+
ModalOverlay,
20+
Text,
21+
TextAlign,
22+
TextVariant,
23+
} from '@metamask/design-system-react';
424
import { I18nContext } from '../../../../contexts/i18n';
525

6-
import Modal, { ModalContent } from '../../modal';
7-
826
export default class ConfirmDeleteNetwork extends PureComponent {
927
static propTypes = {
1028
hideModal: PropTypes.func.isRequired,
@@ -48,17 +66,67 @@ export default class ConfirmDeleteNetwork extends PureComponent {
4866

4967
return (
5068
<Modal
51-
onSubmit={this.handleDelete}
52-
onCancel={() => this.props.hideModal()}
53-
submitText={t('delete')}
54-
cancelText={t('cancel')}
55-
submitType="danger-primary"
56-
testId="confirm-delete-network-modal"
69+
isOpen
70+
onClose={() => this.props.hideModal()}
71+
isClosedOnOutsideClick={false}
72+
data-testid="confirm-delete-network-modal"
5773
>
74+
<ModalOverlay />
5875
<ModalContent
59-
title={t('deleteNetworkTitle', [networkNickname])}
60-
description={t('deleteNetworkIntro')}
61-
/>
76+
modalDialogProps={{
77+
className: 'overflow-hidden p-4 shadow-lg',
78+
}}
79+
className="items-center p-4 sm:py-6 md:py-6"
80+
>
81+
<ModalHeader
82+
className="px-0 pb-4"
83+
onClose={() => this.props.hideModal()}
84+
closeButtonProps={{
85+
ariaLabel: t('close'),
86+
size: ButtonIconSize.Md,
87+
}}
88+
>
89+
<Box
90+
flexDirection={BoxFlexDirection.Column}
91+
alignItems={BoxAlignItems.Center}
92+
gap={2}
93+
className="min-w-0"
94+
>
95+
<Icon
96+
name={IconName.Danger}
97+
size={IconSize.Xl}
98+
color={IconColor.ErrorDefault}
99+
/>
100+
<Text
101+
asChild
102+
variant={TextVariant.HeadingSm}
103+
textAlign={TextAlign.Center}
104+
>
105+
<h2>{t('deleteNetworkTitle', [networkNickname])}</h2>
106+
</Text>
107+
</Box>
108+
</ModalHeader>
109+
<Text className="w-full" variant={TextVariant.BodyMd}>
110+
{t('deleteNetworkIntro')}
111+
</Text>
112+
<ModalFooter
113+
className="px-0 pt-6"
114+
secondaryButtonProps={{
115+
children: t('cancel'),
116+
onClick: () => this.props.hideModal(),
117+
size: ButtonSize.Lg,
118+
variant: ButtonVariant.Secondary,
119+
}}
120+
primaryButtonProps={{
121+
children: t('delete'),
122+
'data-testid': 'confirm-delete-network-modal-delete-button',
123+
isDanger: true,
124+
onClick: this.handleDelete,
125+
size: ButtonSize.Lg,
126+
variant: ButtonVariant.Primary,
127+
}}
128+
/>
129+
</ModalContent>
62130
</Modal>
63131
);
64132
}

ui/components/app/modals/confirm-delete-network/confirm-delete-network.test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,21 @@ describe('Confirm Delete Network', () => {
1111
hideModal: jest.fn(),
1212
onConfirm: jest.fn(),
1313
removeNetwork: jest.fn().mockResolvedValue(),
14-
switchEvmNetwork: jest.fn(),
14+
switchToEthereumNetwork: jest.fn(),
15+
networkNickname: 'Goerli',
1516
target: '0x5',
1617
chainId: '0xe708',
1718
ethereumMainnetClientId: '0x1',
1819
};
1920

2021
it('should match snapshot', () => {
2122
const mockStore = configureMockStore()(mockState);
22-
const { container } = renderWithProvider(
23+
const { baseElement } = renderWithProvider(
2324
<ConfirmDeleteNetwork {...props} />,
2425
mockStore,
2526
);
2627

27-
expect(container).toMatchSnapshot();
28+
expect(baseElement).toMatchSnapshot();
2829
});
2930

3031
it('should mention network name in modal', () => {

0 commit comments

Comments
 (0)