Skip to content

Commit 89911ca

Browse files
refactor(rewards): use MMDS HeaderStandard (#29706)
## **Description** This PR replaces `HeaderCompactStandard` with `HeaderStandard` from `@metamask/design-system-react-native` across Rewards screens and related sheets, and updates unit tests to match. **Reason:** Align Rewards with the MetaMask design system and reduce use of `component-library/components-temp` for standard headers. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-703 <!-- Add issue link(s) if applicable. --> ## **Manual testing steps** ```gherkin Feature: Rewards headers use design system HeaderStandard Scenario: Benefits and campaigns Given the user opens Rewards benefits (list or single benefit), campaigns list, or campaign mechanics When they use the header back control and read titles Then navigation and copy match prior behavior Scenario: Ondo and Season One campaign flows Given the user opens Ondo campaign details, portfolio, or Season One campaign details When they navigate back from the header Then behavior matches the previous implementation Scenario: Rewards settings and auxiliary sheets Given the user opens Rewards settings, referral, MUSD calculator, end-of-season claim, linked off-device accounts, opt-in modal, or environment toggle surfaces touched by this PR When they dismiss or navigate via the header Then sheets and screens behave as before ``` ## **Screenshots/Recordings** ### **Before** ### **After** ## **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 For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **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] > **Low Risk** > Low risk refactor limited to UI header component swaps and corresponding test updates; main risk is minor visual/layout or `testID` regressions affecting navigation/back-button automation. > > **Overview** > Rewards screens and sheets now use **MMDS `HeaderStandard`** instead of the legacy `HeaderCompactStandard` component, including benefits, campaigns, referral, settings, calculators, and multiple bottom sheets/modals. > > Unit tests were updated to remove `HeaderCompactStandard` mocks, align back/close button `testID`s (e.g., campaign mechanics/details), and add missing Safe Area hook stubs (notably `useSafeAreaInsets`) so `HeaderStandard` can render cleanly in Jest. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d6a8c44. 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 d63cea6 commit 89911ca

31 files changed

Lines changed: 59 additions & 491 deletions

app/components/UI/Rewards/Views/BenefitFullView.test.tsx

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,27 +69,6 @@ jest.mock('@metamask/design-system-twrnc-preset', () => ({
6969
useTailwind: () => ({ style: (...args: unknown[]) => args }),
7070
}));
7171

72-
jest.mock(
73-
'../../../../component-library/components-temp/HeaderCompactStandard',
74-
() => {
75-
const ReactActual = jest.requireActual('react');
76-
const { Pressable, Text, View } = jest.requireActual('react-native');
77-
return {
78-
__esModule: true,
79-
default: ({ title, onBack }: { title: string; onBack: () => void }) =>
80-
ReactActual.createElement(
81-
View,
82-
{ testID: 'header' },
83-
ReactActual.createElement(Text, null, title),
84-
ReactActual.createElement(Pressable, {
85-
onPress: onBack,
86-
testID: 'header-back-button',
87-
}),
88-
),
89-
};
90-
},
91-
);
92-
9372
jest.mock('../../../Views/ErrorBoundary', () => ({
9473
__esModule: true,
9574
default: ({ children }: { children: React.ReactNode }) => children,
@@ -104,6 +83,7 @@ jest.mock('react-native-safe-area-context', () => {
10483
testID,
10584
}: React.PropsWithChildren<{ testID?: string }>) =>
10685
ReactActual.createElement(View, { testID }, children),
86+
useSafeAreaInsets: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
10787
};
10888
});
10989

app/components/UI/Rewards/Views/BenefitFullView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import {
1818
Text,
1919
TextColor,
2020
TextVariant,
21+
HeaderStandard,
2122
} from '@metamask/design-system-react-native';
2223
import { SafeAreaView } from 'react-native-safe-area-context';
2324
import { BenefitFullViewRouteProp } from './BenefitFullView.types.ts';
2425
import { REWARDS_VIEW_SELECTORS } from './RewardsView.constants.ts';
2526
import { formatDateRemaining } from '../utils/formatUtils.ts';
26-
import HeaderCompactStandard from '../../../../component-library/components-temp/HeaderCompactStandard';
2727
import { strings } from '../../../../../locales/i18n';
2828
import ErrorBoundary from '../../../Views/ErrorBoundary';
2929
import Routes from '../../../../constants/navigation/Routes.ts';
@@ -80,7 +80,7 @@ const BenefitFullView = () => {
8080
style={tw.style('flex-1')}
8181
testID={REWARDS_VIEW_SELECTORS.DETAIL_BENEFIT_VIEW}
8282
>
83-
<HeaderCompactStandard
83+
<HeaderStandard
8484
title={strings('rewards.benefits.title_claim')}
8585
onBack={() => navigation.goBack()}
8686
backButtonProps={{ testID: 'header-back-button' }}

app/components/UI/Rewards/Views/BenefitsFullView.test.tsx

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -43,32 +43,6 @@ jest.mock('@metamask/design-system-twrnc-preset', () => ({
4343
useTailwind: () => ({ style: (...args: unknown[]) => args }),
4444
}));
4545

46-
jest.mock('@metamask/design-system-react-native', () => {
47-
const actual = jest.requireActual('@metamask/design-system-react-native');
48-
return { ...actual };
49-
});
50-
51-
jest.mock(
52-
'../../../../component-library/components-temp/HeaderCompactStandard',
53-
() => {
54-
const ReactActual = jest.requireActual('react');
55-
const { Pressable, Text, View } = jest.requireActual('react-native');
56-
return {
57-
__esModule: true,
58-
default: ({ title, onBack }: { title: string; onBack: () => void }) =>
59-
ReactActual.createElement(
60-
View,
61-
{ testID: 'header' },
62-
ReactActual.createElement(Text, null, title),
63-
ReactActual.createElement(Pressable, {
64-
onPress: onBack,
65-
testID: 'header-back-button',
66-
}),
67-
),
68-
};
69-
},
70-
);
71-
7246
jest.mock('../../../Views/ErrorBoundary', () => ({
7347
__esModule: true,
7448
default: ({ children }: { children: React.ReactNode }) => children,
@@ -123,6 +97,7 @@ jest.mock('react-native-safe-area-context', () => {
12397
testID,
12498
}: React.PropsWithChildren<{ testID?: string }>) =>
12599
ReactActual.createElement(View, { testID }, children),
100+
useSafeAreaInsets: () => ({ top: 0, bottom: 0, left: 0, right: 0 }),
126101
};
127102
});
128103

app/components/UI/Rewards/Views/BenefitsFullView.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { Text, TextVariant } from '@metamask/design-system-react-native';
1+
import {
2+
HeaderStandard,
3+
Text,
4+
TextVariant,
5+
} from '@metamask/design-system-react-native';
26
import React, { useState } from 'react';
37
import useTrackRewardsPageView from '../hooks/useTrackRewardsPageView';
48
import { strings } from '../../../../../locales/i18n';
@@ -12,7 +16,6 @@ import { useBenefits } from '../hooks/useBenefits.ts';
1216
import { SubscriptionBenefitDto } from '../../../../core/Engine/controllers/rewards-controller/types.ts';
1317
import BenefitCard from '../components/Benefits/BenefitCard.tsx';
1418
import { useTailwind } from '@metamask/design-system-twrnc-preset';
15-
import HeaderCompactStandard from '../../../../component-library/components-temp/HeaderCompactStandard';
1619
import { useNavigation } from '@react-navigation/native';
1720
import ErrorBoundary from '../../../Views/ErrorBoundary';
1821
import TheMiracleFooter from '../components/Benefits/TheMiracleFooter.tsx';
@@ -59,7 +62,7 @@ const BenefitsFullView = () => {
5962
style={tw.style('flex-1 bg-default')}
6063
testID={REWARDS_VIEW_SELECTORS.LIST_BENEFIT_VIEW}
6164
>
62-
<HeaderCompactStandard
65+
<HeaderStandard
6366
title={strings('rewards.benefits.title')}
6467
onBack={() => navigation.goBack()}
6568
backButtonProps={{ testID: 'header-back-button' }}

app/components/UI/Rewards/Views/CampaignMechanicsView.test.tsx

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,10 @@ jest.mock('@react-navigation/native', () => ({
1616
useRoute: () => ({ params: { campaignId: 'campaign-1' } }),
1717
}));
1818

19-
jest.mock('@metamask/design-system-react-native', () => {
20-
const actual = jest.requireActual('@metamask/design-system-react-native');
21-
return { ...actual };
22-
});
23-
2419
jest.mock('@metamask/design-system-twrnc-preset', () => ({
2520
useTailwind: () => ({ style: (...args: unknown[]) => args }),
2621
}));
2722

28-
jest.mock(
29-
'../../../../component-library/components-temp/HeaderCompactStandard',
30-
() => {
31-
const ReactActual = jest.requireActual('react');
32-
const { View, Text, Pressable } = jest.requireActual('react-native');
33-
return {
34-
__esModule: true,
35-
default: ({ title, onBack }: { title: string; onBack: () => void }) =>
36-
ReactActual.createElement(
37-
View,
38-
{ testID: 'header' },
39-
ReactActual.createElement(Text, null, title),
40-
ReactActual.createElement(Pressable, {
41-
onPress: onBack,
42-
testID: 'header-back-button',
43-
}),
44-
),
45-
};
46-
},
47-
);
48-
4923
jest.mock('../../../Views/ErrorBoundary', () => {
5024
const ReactActual = jest.requireActual('react');
5125
return {
@@ -151,7 +125,7 @@ describe('CampaignMechanicsView', () => {
151125

152126
it('navigates back when the back button is pressed', () => {
153127
const { getByTestId } = render(<CampaignMechanicsView />);
154-
fireEvent.press(getByTestId('header-back-button'));
128+
fireEvent.press(getByTestId('campaign-mechanics-back-button'));
155129
expect(mockGoBack).toHaveBeenCalledTimes(1);
156130
});
157131

app/components/UI/Rewards/Views/CampaignMechanicsView.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import React, { useMemo } from 'react';
22
import { ScrollView } from 'react-native';
33
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
4-
import { Box } from '@metamask/design-system-react-native';
4+
import { Box, HeaderStandard } from '@metamask/design-system-react-native';
55
import { useTailwind } from '@metamask/design-system-twrnc-preset';
66
import { SafeAreaView } from 'react-native-safe-area-context';
7-
import HeaderCompactStandard from '../../../../component-library/components-temp/HeaderCompactStandard';
87
import ErrorBoundary from '../../../Views/ErrorBoundary';
98
import CampaignHowItWorks from '../components/Campaigns/CampaignHowItWorks';
109
import ContentfulRichText, {
@@ -54,7 +53,7 @@ const CampaignMechanicsView: React.FC = () => {
5453
style={tw.style('flex-1 bg-default')}
5554
testID={CAMPAIGN_MECHANICS_TEST_IDS.CONTAINER}
5655
>
57-
<HeaderCompactStandard
56+
<HeaderStandard
5857
title={strings('rewards.campaign_mechanics.title')}
5958
onBack={() => navigation.goBack()}
6059
backButtonProps={{ testID: 'campaign-mechanics-back-button' }}

app/components/UI/Rewards/Views/CampaignsView.test.tsx

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ jest.mock('@react-navigation/native', () => ({
1515
useNavigation: () => ({ goBack: mockGoBack }),
1616
}));
1717

18-
jest.mock('@metamask/design-system-react-native', () => {
19-
const actual = jest.requireActual('@metamask/design-system-react-native');
20-
return { ...actual };
21-
});
22-
2318
jest.mock('@metamask/design-system-twrnc-preset', () => ({
2419
useTailwind: () => ({ style: (...args: unknown[]) => args }),
2520
}));
@@ -102,27 +97,6 @@ jest.mock('../components/RewardsErrorBanner', () => {
10297
};
10398
});
10499

105-
jest.mock(
106-
'../../../../component-library/components-temp/HeaderCompactStandard',
107-
() => {
108-
const ReactActual = jest.requireActual('react');
109-
const { View, Text, Pressable } = jest.requireActual('react-native');
110-
return {
111-
__esModule: true,
112-
default: ({ title, onBack }: { title: string; onBack: () => void }) =>
113-
ReactActual.createElement(
114-
View,
115-
{ testID: 'header' },
116-
ReactActual.createElement(Text, null, title),
117-
ReactActual.createElement(Pressable, {
118-
onPress: onBack,
119-
testID: 'header-back-button',
120-
}),
121-
),
122-
};
123-
},
124-
);
125-
126100
jest.mock('../../../Views/ErrorBoundary', () => {
127101
const ReactActual = jest.requireActual('react');
128102
return {

app/components/UI/Rewards/Views/CampaignsView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import {
99
TextVariant,
1010
BoxFlexDirection,
1111
BoxAlignItems,
12+
HeaderStandard,
1213
Skeleton,
1314
} from '@metamask/design-system-react-native';
1415
import { useTailwind } from '@metamask/design-system-twrnc-preset';
1516
import { SafeAreaView } from 'react-native-safe-area-context';
1617
import ErrorBoundary from '../../../Views/ErrorBoundary';
17-
import HeaderCompactStandard from '../../../../component-library/components-temp/HeaderCompactStandard';
1818
import { useRewardCampaigns } from '../hooks/useRewardCampaigns';
1919
import RewardsErrorBanner from '../components/RewardsErrorBanner';
2020
import { REWARDS_VIEW_SELECTORS } from './RewardsView.constants';
@@ -110,7 +110,7 @@ const CampaignsView: React.FC = () => {
110110
style={tw.style('flex-1 bg-default')}
111111
testID={REWARDS_VIEW_SELECTORS.CAMPAIGNS_VIEW}
112112
>
113-
<HeaderCompactStandard
113+
<HeaderStandard
114114
title={strings('rewards.campaigns_view.title')}
115115
onBack={() => navigation.goBack()}
116116
backButtonProps={{ testID: 'header-back-button' }}

app/components/UI/Rewards/Views/MusdCalculatorView.test.tsx

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,6 @@ jest.mock('../../../Views/ErrorBoundary', () => ({
2929
default: ({ children }: { children: React.ReactNode }) => children,
3030
}));
3131

32-
jest.mock(
33-
'../../../../component-library/components-temp/HeaderCompactStandard',
34-
() => {
35-
const ReactActual = jest.requireActual('react');
36-
const { View, Pressable } = jest.requireActual('react-native');
37-
return {
38-
__esModule: true,
39-
default: ({ onBack }: { title: string; onBack: () => void }) =>
40-
ReactActual.createElement(
41-
View,
42-
{ testID: 'header' },
43-
ReactActual.createElement(Pressable, {
44-
onPress: onBack,
45-
testID: 'header-back-button',
46-
}),
47-
),
48-
};
49-
},
50-
);
51-
5232
jest.mock('../components/Tabs/MusdCalculatorTab/MusdCalculatorTab', () => {
5333
const ReactActual = jest.requireActual('react');
5434
const { View } = jest.requireActual('react-native');

app/components/UI/Rewards/Views/MusdCalculatorView.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React from 'react';
2+
import { HeaderStandard } from '@metamask/design-system-react-native';
23
import { useNavigation } from '@react-navigation/native';
34
import { SafeAreaView } from 'react-native-safe-area-context';
45
import { useTailwind } from '@metamask/design-system-twrnc-preset';
56
import ErrorBoundary from '../../../Views/ErrorBoundary';
6-
import HeaderCompactStandard from '../../../../component-library/components-temp/HeaderCompactStandard';
77
import MusdCalculatorTab from '../components/Tabs/MusdCalculatorTab/MusdCalculatorTab';
88
import { strings } from '../../../../../locales/i18n';
99
import useTrackRewardsPageView from '../hooks/useTrackRewardsPageView';
@@ -20,9 +20,10 @@ const MusdCalculatorView: React.FC = () => {
2020
edges={{ top: 'additive' }}
2121
style={tw.style('flex-1 bg-default')}
2222
>
23-
<HeaderCompactStandard
23+
<HeaderStandard
2424
title={strings('rewards.musd.page_title')}
2525
onBack={() => navigation.goBack()}
26+
backButtonProps={{ testID: 'header-back-button' }}
2627
/>
2728
<MusdCalculatorTab />
2829
</SafeAreaView>

0 commit comments

Comments
 (0)