Skip to content

Commit d33e9b2

Browse files
authored
feat: MUSD-741, MUSD-743, MUSD-744 — Money Home header polish + tab bar (#29645)
## **Description** Bundle of polish items on the Money Home screen. - **MUSD-741** migrates the "X% APY" pill in the Money Home header from a hand-rolled `Box + Text` to the MMDS `Tag` component (severity = success), and bumps the inner text from `BodySm` (14px) to `BodyMd` (16px) so yield reads as a primary value prop. - **MUSD-743** removes the back chevron from the Money Home header. With Money now a top-level tab destination, the back button was misleading; the kebab on the right naturally aligns with the title via `HeaderStandard`'s built-in layout. - **MUSD-744** renders the global wallet action bar at the bottom of Money Home so the screen functions as a proper tab destination instead of a dead-end. Concretely: - The Money `Tab.Screen` is registered as `Routes.MONEY.ROOT` (instead of `Routes.MONEY.HOME`) and wired to `MoneyScreenStack`. This matches the existing `TabBar` handler which navigates to `Routes.MONEY.ROOT, { screen: Routes.MONEY.HOME }` — so the tab mounts with its inner stack and the global tab bar stays visible. - The outer `Stack.Screen` for `Routes.MONEY.ROOT` (which was pushing above `HomeTabs` and hiding the tab bar) is removed from `MainNavigator`. - All changes are inside `app/components/Nav/Main/MainNavigator.js` — no design-system-owned files are touched. - `MoneyFooter` ("Add money" CTA) was unpinned from its sticky-bottom position and now flows inside the `ScrollView` content as a normal section (`px-4 py-3`), with a single Divider above it (matching the section rhythm) and `paddingBottom: 40` on the scroll content. MUSD-747 will reintroduce sticky behaviour with peek-and-hide based on the onboarding stepper's viewport state. No analytics changes, no copy changes, no behavioural changes outside the navigation rewiring described above. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: - https://consensyssoftware.atlassian.net/browse/MUSD-741 - https://consensyssoftware.atlassian.net/browse/MUSD-743 - https://consensyssoftware.atlassian.net/browse/MUSD-744 ## **Manual testing steps** ```gherkin Feature: Money Home header polish + tab bar visibility Scenario: user opens Money Home via the bottom tab Given the Money Account feature flag is enabled When the user taps the Money tab in the bottom navigation Then Money Home renders with the global tab bar visible at the bottom And the "Money" tab is shown as active And no back chevron is rendered in the Money Home header And the options (kebab) icon sits in the top-right And the "X% APY" pill is rendered as an MMDS Tag at 16px text Scenario: Add money button placement Given Money Home is open When the user scrolls to the bottom of the page Then the "Add money" button renders inside the scroll content as the last section And it is preceded by the standard section divider And it has visible breathing room before the global tab bar ``` ## **Screenshots/Recordings** ### **Before** <!-- to be added --> ### **After** <!-- to be added --> ## **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 - [ ] 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 - [ ] I've tested with a power user scenario - [ ] I've instrumented key operations with Sentry traces for production performance metrics ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] 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** > Moderate risk due to navigation rewiring (Money tab now routes to `Routes.MONEY.ROOT`/`MoneyScreenStack` and a stack-level screen is removed), which could affect tab routing and back-stack behavior. UI/layout changes are otherwise straightforward and covered by updated tests. > > **Overview** > Makes Money a first-class bottom-tab destination by switching the conditional tab registration from `Routes.MONEY.HOME` to `Routes.MONEY.ROOT` and mounting `MoneyScreenStack` directly, while removing the extra `Routes.MONEY.ROOT` stack screen that previously sat above `HomeTabs` (keeping the global tab bar visible). > > Polishes Money Home UI: replaces the APY pill with the design-system `Tag`, updates the header to a title+menu-only `HeaderBase` (removing the back button), and simplifies the “Add money” footer from an animated/sticky overlay to a normal scroll section with fixed bottom padding; associated peek/hide stepper logic and its unit tests are deleted and component tests are updated accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a742e0d. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent d28ce2e commit d33e9b2

16 files changed

Lines changed: 95 additions & 643 deletions

app/components/Nav/Main/MainNavigator.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -867,9 +867,9 @@ const HomeTabs = () => {
867867
{/* Activity Tab (replaced by Money when feature flag is on) */}
868868
{isMoneyHomeScreenEnabled ? (
869869
<Tab.Screen
870-
name={Routes.MONEY.HOME}
870+
name={Routes.MONEY.ROOT}
871871
options={options.money}
872-
component={WalletTabModalFlow}
872+
component={MoneyScreenStack}
873873
/>
874874
) : (
875875
<Tab.Screen
@@ -1223,11 +1223,6 @@ const MainNavigator = () => {
12231223
/>
12241224
{isMoneyHomeScreenEnabled && (
12251225
<>
1226-
<Stack.Screen
1227-
name={Routes.MONEY.ROOT}
1228-
component={MoneyScreenStack}
1229-
options={{ headerShown: false, ...slideFromRightAnimation }}
1230-
/>
12311226
<Stack.Screen
12321227
name={Routes.MONEY.MODALS.ROOT}
12331228
component={MoneyModalStack}

app/components/Nav/Main/MainNavigator.test.tsx

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,45 +1446,50 @@ describe('MainNavigator', () => {
14461446
});
14471447

14481448
describe('Money home screen conditional rendering', () => {
1449-
it('includes Money route when feature flag is enabled', () => {
1450-
mockSelectMoneyHomeScreenEnabledFlag.mockReturnValue(true);
1451-
1452-
const container = renderWithProvider(<MainNavigator />, {
1449+
const getHomeTabsScreenNames = (): string[] => {
1450+
const { root: mainRoot } = renderWithProvider(<MainNavigator />, {
14531451
state: initialRootState,
14541452
});
1455-
1456-
const screenProps = container.root.children
1453+
const homeScreen = mainRoot.findAll(
1454+
(node: ReactTestInstance) =>
1455+
node.type?.toString?.() === 'Screen' && node.props?.name === 'Home',
1456+
)[0];
1457+
const HomeTabs = homeScreen?.props?.component as React.ComponentType<
1458+
Record<string, unknown>
1459+
>;
1460+
const { root: homeRoot } = renderWithProvider(
1461+
<HomeTabs route={{ params: {} }} />,
1462+
{ state: initialRootState },
1463+
);
1464+
const tabNavigatorNode = homeRoot.findAll(
1465+
(node: ReactTestInstance) =>
1466+
node.type?.toString?.() === 'TabNavigator',
1467+
)[0];
1468+
return (tabNavigatorNode?.children ?? [])
14571469
.filter(
14581470
(child): child is ReactTestInstance =>
14591471
typeof child === 'object' &&
1460-
'type' in child &&
14611472
'props' in child &&
1462-
child.type?.toString() === 'Screen',
1473+
typeof child.props?.name === 'string',
14631474
)
1464-
.map((child) => child.props.name);
1475+
.map((child) => child.props.name as string);
1476+
};
1477+
1478+
it('includes Money route when feature flag is enabled', () => {
1479+
mockSelectMoneyHomeScreenEnabledFlag.mockReturnValue(true);
14651480

1466-
expect(screenProps).toContain(Routes.MONEY.ROOT);
1481+
const tabScreenNames = getHomeTabsScreenNames();
1482+
1483+
expect(tabScreenNames).toContain(Routes.MONEY.ROOT);
14671484
mockSelectMoneyHomeScreenEnabledFlag.mockReturnValue(false);
14681485
});
14691486

14701487
it('excludes Money route when feature flag is disabled', () => {
14711488
mockSelectMoneyHomeScreenEnabledFlag.mockReturnValue(false);
14721489

1473-
const container = renderWithProvider(<MainNavigator />, {
1474-
state: initialRootState,
1475-
});
1476-
1477-
const screenProps = container.root.children
1478-
.filter(
1479-
(child): child is ReactTestInstance =>
1480-
typeof child === 'object' &&
1481-
'type' in child &&
1482-
'props' in child &&
1483-
child.type?.toString() === 'Screen',
1484-
)
1485-
.map((child) => child.props.name);
1490+
const tabScreenNames = getHomeTabsScreenNames();
14861491

1487-
expect(screenProps).not.toContain(Routes.MONEY.ROOT);
1492+
expect(tabScreenNames).not.toContain(Routes.MONEY.ROOT);
14881493
});
14891494
});
14901495
});

app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.styles.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ const styleSheet = (params: { theme: Theme }) =>
77
flex: 1,
88
backgroundColor: params.theme.colors.background.default,
99
},
10-
footerOverlay: {
11-
position: 'absolute',
12-
left: 0,
13-
right: 0,
14-
bottom: 0,
10+
scrollContent: {
11+
paddingBottom: 40,
1512
},
1613
});
1714

0 commit comments

Comments
 (0)