Skip to content

Commit 7f3f590

Browse files
refactor: migrate RevealSRP and EditMultichainAccountName to design system (#29697)
## **Description** Migrates the `RevealSRP` and `EditMultichainAccountName` multichain account sheets from component-library primitives to `@metamask/design-system-react-native`. ## **Changelog** CHANGELOG entry: null ## **Related issues** Refs: [MUL-1687](https://consensyssoftware.atlassian.net/browse/MUL-1687) ## **Manual testing steps** ```gherkin Feature: Multichain account sheets (design system) Scenario: Reveal SRP intro screen Given the user opens the Reveal SRP flow for a multichain account When they view the intro sheet Then the header, description, primary "Get started", and secondary "Learn more" actions render and match prior behavior When they tap the back control Then navigation returns to the previous screen Scenario: Edit multichain account group name Given the user opens Edit multichain account name for an account group When they change the name and tap Save Then the name is persisted and the screen closes When they tap the back control without saving Then navigation returns to the previous screen ``` ## **Screenshots/Recordings** https://github.com/user-attachments/assets/a4faecee-8e04-4e5a-a3b8-194933d74fa2 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example ## **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. <!-- Generated with the help of the pr-description AI skill --> [MUL-1687]: https://consensyssoftware.atlassian.net/browse/MUL-1687?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Primarily a UI refactor, but it touches the SRP reveal entry flow and navigation controls, so regressions could impact a security-sensitive user journey (layout/safe-area/back/save behaviors across iOS/Android). > > **Overview** > Migrates the `RevealSRP` and `EditMultichainAccountName` sheets from component-library primitives and custom `StyleSheet` files to `@metamask/design-system-react-native` components with Tailwind-based styling, including Android-specific status bar inset handling. > > Adds a dedicated back-button test id (`EditAccountNameIds.BACK_BUTTON`) and updates/expands tests to assert navigation via that control, cover a missing-`accountGroup` fallback header, and exercise Android rendering paths (status bar height present/absent). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e74bb6c. 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 b14edae commit 7f3f590

7 files changed

Lines changed: 212 additions & 179 deletions

File tree

app/components/Views/MultichainAccounts/sheets/EditAccountName.testIds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export const EditAccountNameIds = {
22
EDIT_ACCOUNT_NAME_CONTAINER: 'edit-account-name-container',
33
ACCOUNT_NAME_INPUT: 'edit-account-name-input',
44
SAVE_BUTTON: 'edit-account-name-save-button',
5+
BACK_BUTTON: 'edit-multichain-account-name-back-button',
56
};

app/components/Views/MultichainAccounts/sheets/EditMultichainAccountName/EditMultichainAccountName.styles.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

app/components/Views/MultichainAccounts/sheets/EditMultichainAccountName/EditMultichainAccountName.test.tsx

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { fireEvent } from '@testing-library/react-native';
3+
import { Platform, StatusBar } from 'react-native';
34
import { EditMultichainAccountName } from './EditMultichainAccountName';
45
import { strings } from '../../../../../../locales/i18n';
56
import { EditAccountNameIds } from '../EditAccountName.testIds';
@@ -37,12 +38,29 @@ jest.mock('../../../../../core/Engine', () => ({
3738
}));
3839

3940
describe('EditMultichainAccountName', () => {
41+
const originalPlatformOs = Platform.OS;
42+
const originalStatusBarCurrentHeight = StatusBar.currentHeight;
4043
const render = () => renderWithProvider(<EditMultichainAccountName />);
4144

4245
beforeEach(() => {
4346
jest.clearAllMocks();
4447
mockSetAccountGroupName.mockReset();
4548
mockUseRoute.mockReturnValue(mockRoute);
49+
Platform.OS = 'ios';
50+
Object.defineProperty(StatusBar, 'currentHeight', {
51+
configurable: true,
52+
writable: true,
53+
value: originalStatusBarCurrentHeight,
54+
});
55+
});
56+
57+
afterAll(() => {
58+
Platform.OS = originalPlatformOs;
59+
Object.defineProperty(StatusBar, 'currentHeight', {
60+
configurable: true,
61+
writable: true,
62+
value: originalStatusBarCurrentHeight,
63+
});
4664
});
4765

4866
describe('rendering', () => {
@@ -85,9 +103,9 @@ describe('EditMultichainAccountName', () => {
85103
});
86104

87105
it('navigates back when back button is pressed', () => {
88-
const { getByRole } = render();
106+
const { getByTestId } = render();
89107

90-
const backButton = getByRole('button');
108+
const backButton = getByTestId(EditAccountNameIds.BACK_BUTTON);
91109
fireEvent.press(backButton);
92110

93111
expect(mockGoBack).toHaveBeenCalledTimes(1);
@@ -235,5 +253,28 @@ describe('EditMultichainAccountName', () => {
235253
const nameInput = getByTestId(EditAccountNameIds.ACCOUNT_NAME_INPUT);
236254
expect(nameInput.props.value).toBe('');
237255
});
256+
257+
it('renders fallback header when route omits account group', () => {
258+
mockUseRoute.mockReturnValue({
259+
params: {},
260+
} as typeof mockRoute);
261+
262+
const { getByText } = render();
263+
expect(getByText('Account Group')).toBeOnTheScreen();
264+
});
265+
});
266+
267+
describe('platform-specific layout', () => {
268+
it('renders on Android with status bar inset and keyboard avoiding height behavior', () => {
269+
Platform.OS = 'android';
270+
Object.defineProperty(StatusBar, 'currentHeight', {
271+
configurable: true,
272+
writable: true,
273+
value: 24,
274+
});
275+
276+
const { getByTestId } = render();
277+
expect(getByTestId(EditAccountNameIds.BACK_BUTTON)).toBeOnTheScreen();
278+
});
238279
});
239280
});

app/components/Views/MultichainAccounts/sheets/EditMultichainAccountName/EditMultichainAccountName.tsx

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import React, { useCallback, useState } from 'react';
22
import { useSelector } from 'react-redux';
3+
import {
4+
KeyboardAvoidingView,
5+
Platform,
6+
StatusBar,
7+
TextInput,
8+
} from 'react-native';
39
import { strings } from '../../../../../../locales/i18n';
410
import Engine from '../../../../../core/Engine';
511
import {
@@ -8,31 +14,28 @@ import {
814
useNavigation,
915
useRoute,
1016
} from '@react-navigation/native';
11-
import Text, {
12-
TextColor,
13-
TextVariant,
14-
} from '../../../../../component-library/components/Texts/Text';
15-
import { Box } from '../../../../UI/Box/Box';
1617
import {
18+
Box,
19+
BoxFlexDirection,
1720
Button,
21+
ButtonIcon,
22+
ButtonIconSize,
23+
ButtonSize,
1824
ButtonVariant,
19-
ButtonBaseSize,
25+
HeaderBase,
26+
IconName,
27+
Text,
28+
TextColor,
29+
TextVariant,
30+
FontWeight,
2031
} from '@metamask/design-system-react-native';
21-
import styleSheet from './EditMultichainAccountName.styles';
22-
import { useStyles } from '../../../../hooks/useStyles';
23-
import { useTheme } from '../../../../../util/theme';
24-
import { TextInput, KeyboardAvoidingView, Platform } from 'react-native';
32+
import { useTailwind } from '@metamask/design-system-twrnc-preset';
2533
import { SafeAreaView } from 'react-native-safe-area-context';
2634
import { EditAccountNameIds } from '../EditAccountName.testIds';
2735
import { AccountGroupObject } from '@metamask/account-tree-controller';
2836
import { RootState } from '../../../../../reducers';
2937
import { selectAccountGroupById } from '../../../../../selectors/multichainAccounts/accountTreeController';
30-
import HeaderBase from '../../../../../component-library/components/HeaderBase/HeaderBase';
31-
import ButtonLink from '../../../../../component-library/components/Buttons/Button/variants/ButtonLink';
32-
import Icon, {
33-
IconName,
34-
IconSize,
35-
} from '../../../../../component-library/components/Icons/Icon';
38+
import { useTheme } from '../../../../../util/theme';
3639

3740
interface RootNavigationParamList extends ParamListBase {
3841
EditMultichainAccountName: {
@@ -46,7 +49,7 @@ type EditMultichainAccountNameRouteProp = RouteProp<
4649
>;
4750

4851
export const EditMultichainAccountName = () => {
49-
const { styles } = useStyles(styleSheet, {});
52+
const tw = useTailwind();
5053
const { colors, themeAppearance } = useTheme();
5154
const route = useRoute<EditMultichainAccountNameRouteProp>();
5255
const { accountGroup: initialAccountGroup } = route.params;
@@ -65,6 +68,18 @@ export const EditMultichainAccountName = () => {
6568
const [accountName, setAccountName] = useState(initialName);
6669
const [error, setError] = useState<string | null>(null);
6770

71+
const safeAreaStyle = tw.style(
72+
'flex-1 bg-default',
73+
Platform.OS === 'android' && StatusBar.currentHeight
74+
? { paddingTop: StatusBar.currentHeight }
75+
: undefined,
76+
);
77+
78+
const inputStyle = tw.style(
79+
'h-10 w-full rounded-lg border-2 border-default p-2.5',
80+
{ color: colors.text.default },
81+
);
82+
6883
const handleAccountNameChange = useCallback(() => {
6984
// Validate that account name is not empty
7085
if (!accountName || accountName.trim() === '') {
@@ -93,33 +108,35 @@ export const EditMultichainAccountName = () => {
93108
}, [accountName, accountGroup, navigation]);
94109

95110
return (
96-
<SafeAreaView style={styles.safeArea}>
111+
<SafeAreaView style={safeAreaStyle}>
97112
<HeaderBase
98-
style={styles.header}
113+
twClassName="m-4 flex-row items-center justify-center"
99114
startAccessory={
100-
<ButtonLink
101-
labelTextVariant={TextVariant.BodyMDMedium}
102-
label={<Icon name={IconName.ArrowLeft} size={IconSize.Md} />}
115+
<ButtonIcon
116+
testID={EditAccountNameIds.BACK_BUTTON}
117+
iconName={IconName.ArrowLeft}
118+
size={ButtonIconSize.Md}
103119
onPress={() => navigation.goBack()}
104120
/>
105121
}
106122
>
107123
{accountGroup?.metadata?.name || 'Account Group'}
108124
</HeaderBase>
109125
<KeyboardAvoidingView
110-
style={styles.keyboardAvoidingView}
126+
style={tw.style('flex-1 justify-between')}
111127
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
112128
>
113129
<Box
114-
style={styles.contentContainer}
130+
flexDirection={BoxFlexDirection.Column}
131+
twClassName="mt-4 gap-4 px-6"
115132
testID={EditAccountNameIds.EDIT_ACCOUNT_NAME_CONTAINER}
116133
>
117-
<Text variant={TextVariant.BodyMDMedium}>
134+
<Text variant={TextVariant.BodyMd} fontWeight={FontWeight.Medium}>
118135
{strings('multichain_accounts.edit_account_name.account_name')}
119136
</Text>
120137
<TextInput
121138
testID={EditAccountNameIds.ACCOUNT_NAME_INPUT}
122-
style={styles.input}
139+
style={inputStyle}
123140
value={accountName}
124141
onChangeText={(newName: string) => {
125142
setAccountName(newName);
@@ -136,13 +153,13 @@ export const EditMultichainAccountName = () => {
136153
autoFocus
137154
editable
138155
/>
139-
{error && <Text color={TextColor.Error}>{error}</Text>}
156+
{error ? <Text color={TextColor.ErrorDefault}>{error}</Text> : null}
140157
</Box>
141-
<Box style={styles.saveButtonContainer}>
158+
<Box twClassName="mt-4 w-full px-6 py-2.5">
142159
<Button
143160
isFullWidth
144161
variant={ButtonVariant.Primary}
145-
size={ButtonBaseSize.Lg}
162+
size={ButtonSize.Lg}
146163
onPress={handleAccountNameChange}
147164
testID={EditAccountNameIds.SAVE_BUTTON}
148165
>

app/components/Views/MultichainAccounts/sheets/RevealSRP/RevealSRP.styles.ts

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)