Skip to content

Commit bff1503

Browse files
chore: refactor header for tokens (#11855)
<!-- 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 updates the Header for the Token details view, based on this [design update ](https://www.figma.com/design/bkJ9Ot0HGXiakMqZtQfs8G/Design-Quality?node-id=2362-458)by @amandaye0h . Specifically - Left and right button icons are now using the `ButtonIcon` component with the large size - Overflow icon is updated to vertical version - Alignment of header is now top aligned <!-- 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? --> ## **Related issues** Fixes: #11854 ## **Manual testing steps** 1. Go to Wallet 2. Click on a token 3. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** ![Token 2 - Dark](https://github.com/user-attachments/assets/2696b7a8-e726-4048-828c-cd912e63f4c2) ![Token 2 - Light](https://github.com/user-attachments/assets/1d953931-1d32-4daf-bf65-e68148ae4454) <!-- [screenshots/recordings] --> ### **After** https://github.com/user-attachments/assets/d1e0c824-f403-4f34-95c0-5d27c0dd6b3b <!-- [screenshots/recordings] --> ## **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 - [ ] 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. ## **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 42263b2 commit bff1503

File tree

29 files changed

+251
-135
lines changed

29 files changed

+251
-135
lines changed

app/component-library/components/BottomSheets/BottomSheetHeader/__snapshots__/BottomSheetHeader.test.tsx.snap

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
exports[`BottomSheetHeader should render snapshot correctly 1`] = `
44
<View
55
style={
6-
{
7-
"backgroundColor": "#ffffff",
8-
"flexDirection": "row",
9-
"padding": 16,
10-
}
6+
[
7+
{
8+
"backgroundColor": "#ffffff",
9+
"flexDirection": "row",
10+
"padding": 16,
11+
},
12+
false,
13+
]
1114
}
1215
testID="header"
1316
>

app/component-library/components/HeaderBase/HeaderBase.test.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Third party dependencies.
22
import React from 'react';
33
import { render } from '@testing-library/react-native';
4+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
45

56
// External dependencies.
67
import Text, { TextVariant } from '../Texts/Text';
@@ -15,7 +16,16 @@ import {
1516
HEADERBASE_TITLE_TEST_ID,
1617
} from './HeaderBase.constants';
1718

19+
jest.mock('react-native-safe-area-context', () => ({
20+
useSafeAreaInsets: jest.fn(),
21+
}));
1822
describe('HeaderBase', () => {
23+
const mockInsets = { top: 20, bottom: 0, left: 0, right: 0 };
24+
25+
beforeEach(() => {
26+
(useSafeAreaInsets as jest.Mock).mockReturnValue(mockInsets);
27+
});
28+
1929
it('should render snapshot correctly', () => {
2030
const wrapper = render(<HeaderBase>Sample HeaderBase Title</HeaderBase>);
2131
expect(wrapper).toMatchSnapshot();
@@ -55,4 +65,28 @@ describe('HeaderBase', () => {
5565

5666
expect(getByRole('text').props.style.fontFamily).toBe(fontFamily);
5767
});
68+
69+
it('applies marginTop when includesTopInset is true', () => {
70+
const { getByTestId } = render(
71+
<HeaderBase includesTopInset>Header Content</HeaderBase>,
72+
);
73+
74+
const headerBase = getByTestId(HEADERBASE_TEST_ID);
75+
// Verify the marginTop is applied
76+
expect(headerBase.props.style).toEqual(
77+
expect.arrayContaining([{ marginTop: mockInsets.top }]),
78+
);
79+
});
80+
81+
it('does not apply marginTop when includesTopInset is false', () => {
82+
const { getByTestId } = render(
83+
<HeaderBase includesTopInset={false}>Header Content</HeaderBase>,
84+
);
85+
86+
const headerBase = getByTestId(HEADERBASE_TEST_ID);
87+
// Verify the marginTop is not applied
88+
expect(headerBase.props.style).toEqual(
89+
expect.not.arrayContaining([{ marginTop: mockInsets.top }]),
90+
);
91+
});
5892
});

app/component-library/components/HeaderBase/HeaderBase.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Third party dependencies.
44
import React from 'react';
55
import { View } from 'react-native';
6+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
67

78
// External dependencies.
89
import { useComponentSize, useStyles } from '../../hooks';
@@ -22,19 +23,25 @@ const HeaderBase: React.FC<HeaderBaseProps> = ({
2223
children,
2324
startAccessory,
2425
endAccessory,
26+
includesTopInset = false,
2527
}) => {
2628
const { size: startAccessorySize, onLayout: startAccessoryOnLayout } =
2729
useComponentSize();
2830
const { size: endAccessorySize, onLayout: endAccessoryOnLayout } =
2931
useComponentSize();
32+
const insets = useSafeAreaInsets();
33+
3034
const { styles } = useStyles(styleSheet, {
3135
style,
3236
startAccessorySize,
3337
endAccessorySize,
3438
});
3539

3640
return (
37-
<View style={styles.base} testID={HEADERBASE_TEST_ID}>
41+
<View
42+
style={[styles.base, includesTopInset && { marginTop: insets.top }]}
43+
testID={HEADERBASE_TEST_ID}
44+
>
3845
<View style={styles.accessoryWrapper}>
3946
<View onLayout={startAccessoryOnLayout}>{startAccessory}</View>
4047
</View>

app/component-library/components/HeaderBase/HeaderBase.types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ export interface HeaderBaseProps extends ViewProps {
1717
* Optional prop to include content to be displayed after the title.
1818
*/
1919
endAccessory?: React.ReactNode;
20+
/**
21+
* Optional prop to include the top inset to make sure the header is visible
22+
* below device's knob
23+
* @default: false
24+
*/
25+
includesTopInset?: boolean;
2026
}
2127

2228
/**

app/component-library/components/HeaderBase/__snapshots__/HeaderBase.test.tsx.snap

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
exports[`HeaderBase should render snapshot correctly 1`] = `
44
<View
55
style={
6-
{
7-
"backgroundColor": "#ffffff",
8-
"flexDirection": "row",
9-
}
6+
[
7+
{
8+
"backgroundColor": "#ffffff",
9+
"flexDirection": "row",
10+
},
11+
false,
12+
]
1013
}
1114
testID="header"
1215
>

app/components/Base/RemoteImage/index.test.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import { act, render } from '@testing-library/react-native';
66

77
jest.mock('react-redux', () => ({
88
...jest.requireActual('react-redux'),
9-
useSelector: jest
10-
.fn()
11-
.mockImplementation(() => 'https://dweb.link/ipfs/'),
9+
useSelector: jest.fn().mockImplementation(() => 'https://dweb.link/ipfs/'),
1210
}));
1311

1412
jest.mock('../../../components/hooks/useIpfsGateway', () => jest.fn());

app/components/UI/Navbar/index.js

Lines changed: 37 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import Icon, {
5353
IconColor,
5454
} from '../../../component-library/components/Icons/Icon';
5555
import { AddContactViewSelectorsIDs } from '../../../../e2e/selectors/Settings/Contacts/AddContactView.selectors';
56+
import HeaderBase from '../../../component-library/components/HeaderBase';
5657
import AddressCopy from '../AddressCopy';
5758
import PickerAccount from '../../../component-library/components/Pickers/PickerAccount';
5859
import { createAccountSelectorNavDetails } from '../../../components/Views/AccountSelector';
@@ -143,6 +144,12 @@ const styles = StyleSheet.create({
143144
top: 2,
144145
right: 10,
145146
},
147+
headerLeftButton: {
148+
marginHorizontal: 16,
149+
},
150+
headerRightButton: {
151+
marginHorizontal: 16,
152+
},
146153
addressCopyWrapper: {
147154
marginHorizontal: 4,
148155
},
@@ -1316,62 +1323,40 @@ export function getNetworkNavbarOptions(
13161323
contentOffset = 0,
13171324
networkName = '',
13181325
) {
1319-
const innerStyles = StyleSheet.create({
1320-
headerStyle: {
1321-
backgroundColor: themeColors.background.default,
1322-
shadowColor: importedColors.transparent,
1323-
elevation: 0,
1324-
},
1325-
headerShadow: {
1326-
elevation: 2,
1327-
shadowColor: themeColors.background.primary,
1328-
shadowOpacity: contentOffset < 20 ? contentOffset / 100 : 0.2,
1329-
shadowOffset: { height: 4, width: 0 },
1330-
shadowRadius: 8,
1331-
},
1332-
headerIcon: {
1333-
color: themeColors.primary.default,
1334-
},
1335-
});
13361326
return {
1337-
headerTitle: () => (
1338-
<NavbarTitle
1339-
disableNetwork={disableNetwork}
1340-
title={title}
1341-
translate={translate}
1342-
networkName={networkName}
1343-
/>
1344-
),
1345-
headerLeft: () => (
1346-
// eslint-disable-next-line react/jsx-no-bind
1347-
<TouchableOpacity
1348-
onPress={() => navigation.pop()}
1349-
style={styles.backButton}
1350-
testID={CommonSelectorsIDs.BACK_ARROW_BUTTON}
1327+
header: () => (
1328+
<HeaderBase
1329+
includesTopInset
1330+
startAccessory={
1331+
<ButtonIcon
1332+
style={styles.headerLeftButton}
1333+
onPress={() => navigation.pop()}
1334+
testID={CommonSelectorsIDs.BACK_ARROW_BUTTON}
1335+
size={ButtonIconSizes.Lg}
1336+
iconName={IconName.ArrowLeft}
1337+
iconColor={IconColor.Default}
1338+
/>
1339+
}
1340+
endAccessory={
1341+
onRightPress && (
1342+
<ButtonIcon
1343+
style={styles.headerRightButton}
1344+
onPress={onRightPress}
1345+
size={ButtonIconSizes.Lg}
1346+
iconName={IconName.MoreVertical}
1347+
iconColor={IconColor.Default}
1348+
/>
1349+
)
1350+
}
13511351
>
1352-
<IonicIcon
1353-
name={'ios-close'}
1354-
size={38}
1355-
style={innerStyles.headerIcon}
1352+
<NavbarTitle
1353+
disableNetwork={disableNetwork}
1354+
title={title}
1355+
translate={translate}
1356+
networkName={networkName}
13561357
/>
1357-
</TouchableOpacity>
1358+
</HeaderBase>
13581359
),
1359-
headerRight: onRightPress
1360-
? () => (
1361-
<TouchableOpacity style={styles.backButton} onPress={onRightPress}>
1362-
<MaterialCommunityIcon
1363-
name={'dots-horizontal'}
1364-
size={28}
1365-
style={innerStyles.headerIcon}
1366-
/>
1367-
</TouchableOpacity>
1368-
// eslint-disable-next-line no-mixed-spaces-and-tabs
1369-
)
1370-
: () => <View />,
1371-
headerStyle: [
1372-
innerStyles.headerStyle,
1373-
contentOffset && innerStyles.headerShadow,
1374-
],
13751360
};
13761361
}
13771362

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* eslint-disable react/prop-types */
2+
import React from 'react';
3+
import { fireEvent } from '@testing-library/react-native';
4+
import { createStackNavigator } from '@react-navigation/stack';
5+
import renderWithProvider from '../../../util/test/renderWithProvider';
6+
import { getNetworkNavbarOptions } from '.';
7+
8+
describe('getNetworkNavbarOptions', () => {
9+
const Stack = createStackNavigator();
10+
11+
const mockNavigation = {
12+
pop: jest.fn(),
13+
};
14+
15+
const TestNavigator = ({ options }) => (
16+
<Stack.Navigator>
17+
<Stack.Screen name="TestScreen" component={() => options.header()} />
18+
</Stack.Navigator>
19+
);
20+
21+
beforeEach(() => {
22+
jest.clearAllMocks();
23+
});
24+
25+
it('renders correctly with default options', () => {
26+
const options = getNetworkNavbarOptions(
27+
'Test Title',
28+
false,
29+
mockNavigation,
30+
);
31+
32+
const { getByText, getByRole } = renderWithProvider(
33+
<TestNavigator options={options} />,
34+
{
35+
state: {},
36+
},
37+
);
38+
39+
expect(getByText('Test Title')).toBeTruthy();
40+
});
41+
});

app/components/UI/NetworkModal/__snapshots__/index.test.tsx.snap

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,14 @@ exports[`NetworkDetails renders correctly 1`] = `
145145
>
146146
<View
147147
style={
148-
{
149-
"backgroundColor": "#ffffff",
150-
"flexDirection": "row",
151-
"padding": 16,
152-
}
148+
[
149+
{
150+
"backgroundColor": "#ffffff",
151+
"flexDirection": "row",
152+
"padding": 16,
153+
},
154+
false,
155+
]
153156
}
154157
testID="header"
155158
>

app/components/UI/NetworkVerificationInfo/__snapshots__/NetworkVerificationInfo.test.tsx.snap

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ exports[`NetworkVerificationInfo renders correctly 1`] = `
66
>
77
<View
88
style={
9-
{
10-
"backgroundColor": "#ffffff",
11-
"flexDirection": "row",
12-
"padding": 16,
13-
}
9+
[
10+
{
11+
"backgroundColor": "#ffffff",
12+
"flexDirection": "row",
13+
"padding": 16,
14+
},
15+
false,
16+
]
1417
}
1518
testID="header"
1619
>

app/components/UI/Ramp/Views/NetworkSwitcher/__snapshots__/NetworkSwitcher.test.tsx.snap

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -953,11 +953,14 @@ exports[`NetworkSwitcher View renders and dismisses network modal when pressing
953953
>
954954
<View
955955
style={
956-
{
957-
"backgroundColor": "#ffffff",
958-
"flexDirection": "row",
959-
"padding": 16,
960-
}
956+
[
957+
{
958+
"backgroundColor": "#ffffff",
959+
"flexDirection": "row",
960+
"padding": 16,
961+
},
962+
false,
963+
]
961964
}
962965
testID="header"
963966
>

0 commit comments

Comments
 (0)