Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7901430
feat(MUSD-741, MUSD-743): polish Money Home header
Kureev May 4, 2026
b9ad446
feat(MUSD-744): render global tab bar on Money Home
Kureev May 4, 2026
0a44b30
feat(MUSD-744): route wallet Cash entry to Money tab so tab bar stays…
Kureev May 4, 2026
ec9e3b5
fix(MUSD-744): drop always-on Add money footer to avoid stacking with…
Kureev May 4, 2026
b9374e0
fix(MUSD-744): unpin Add money button so it flows inside scroll content
Kureev May 4, 2026
e152695
fix(MUSD-744): unpin Add money button and give it breathing room
Kureev May 4, 2026
6370d24
fix(MUSD-744): fix divider rhythm around Add money footer
Kureev May 4, 2026
bdc4c9d
fix(MUSD-744): replace trailing Divider with scroll content paddingBo…
Kureev May 4, 2026
2b3eb4f
fix(MUSD-744): swap Money tab icon to coin to match Figma
Kureev May 4, 2026
c7e4d9d
fix(MUSD-744): rewire tab nav inside MainNavigator to avoid DS files
Kureev May 4, 2026
be2bcf0
fix(MUSD-744): update Money tab test for new Tab.Screen registration
Kureev May 5, 2026
e270466
chore(MUSD-741): remove orphan MoneyFooter.styles.ts
Kureev May 5, 2026
8f04072
feat(MUSD-744): set explicit initialRouteName on MoneyScreenStack
Kureev May 6, 2026
8e1bd23
fix(MUSD-741): align Money title with options menu via HeaderStandard…
Kureev May 6, 2026
f61c9a7
fix(MUSD-741): left-align Money title via HeaderBaseVariant.Display
Kureev May 6, 2026
46b5efe
fix(MUSD-741): use HeaderBase directly so Money title left-aligns
Kureev May 6, 2026
9f9bd30
fix(MUSD-741): add 16px horizontal padding to Money header
Kureev May 6, 2026
01598d3
Merge remote-tracking branch 'origin/main' into kureev/MUSD-741-743-m…
Kureev May 6, 2026
488d5e6
revert(MUSD-744): drop floating Add money footer in favour of static …
Kureev May 6, 2026
a742e0d
fix(MUSD-744): inline Add money footer as last ScrollView child
Kureev May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -866,9 +866,9 @@ const HomeTabs = () => {
{/* Activity Tab (replaced by Money when feature flag is on) */}
{isMoneyHomeScreenEnabled ? (
<Tab.Screen
name={Routes.MONEY.HOME}
name={Routes.MONEY.ROOT}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add initialRouteName={Routes.MONEY.HOME] to the MoneyScreenStack. This way we explicitly navigate to the home screen first. Right now this behaviour is implicit due to the Money Home route being first in the list.

options={options.money}
component={WalletTabModalFlow}
component={MoneyScreenStack}
/>
) : (
<Tab.Screen
Expand Down Expand Up @@ -1222,11 +1222,6 @@ const MainNavigator = () => {
/>
{isMoneyHomeScreenEnabled && (
<>
<Stack.Screen
name={Routes.MONEY.ROOT}
component={MoneyScreenStack}
options={{ headerShown: false, ...slideFromRightAnimation }}
/>
<Stack.Screen
name={Routes.MONEY.MODALS.ROOT}
component={MoneyModalStack}
Expand Down
53 changes: 29 additions & 24 deletions app/components/Nav/Main/MainNavigator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1433,45 +1433,50 @@ describe('MainNavigator', () => {
});

describe('Money home screen conditional rendering', () => {
it('includes Money route when feature flag is enabled', () => {
mockSelectMoneyHomeScreenEnabledFlag.mockReturnValue(true);

const container = renderWithProvider(<MainNavigator />, {
const getHomeTabsScreenNames = (): string[] => {
const { root: mainRoot } = renderWithProvider(<MainNavigator />, {
state: initialRootState,
});

const screenProps = container.root.children
const homeScreen = mainRoot.findAll(
(node: ReactTestInstance) =>
node.type?.toString?.() === 'Screen' && node.props?.name === 'Home',
)[0];
const HomeTabs = homeScreen?.props?.component as React.ComponentType<
Record<string, unknown>
>;
const { root: homeRoot } = renderWithProvider(
<HomeTabs route={{ params: {} }} />,
{ state: initialRootState },
);
const tabNavigatorNode = homeRoot.findAll(
(node: ReactTestInstance) =>
node.type?.toString?.() === 'TabNavigator',
)[0];
return (tabNavigatorNode?.children ?? [])
.filter(
(child): child is ReactTestInstance =>
typeof child === 'object' &&
'type' in child &&
'props' in child &&
child.type?.toString() === 'Screen',
typeof child.props?.name === 'string',
)
.map((child) => child.props.name);
.map((child) => child.props.name as string);
};

it('includes Money route when feature flag is enabled', () => {
mockSelectMoneyHomeScreenEnabledFlag.mockReturnValue(true);

expect(screenProps).toContain(Routes.MONEY.ROOT);
const tabScreenNames = getHomeTabsScreenNames();

expect(tabScreenNames).toContain(Routes.MONEY.ROOT);
mockSelectMoneyHomeScreenEnabledFlag.mockReturnValue(false);
});

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

const container = renderWithProvider(<MainNavigator />, {
state: initialRootState,
});

const screenProps = container.root.children
.filter(
(child): child is ReactTestInstance =>
typeof child === 'object' &&
'type' in child &&
'props' in child &&
child.type?.toString() === 'Screen',
)
.map((child) => child.props.name);
const tabScreenNames = getHomeTabsScreenNames();

expect(screenProps).not.toContain(Routes.MONEY.ROOT);
expect(tabScreenNames).not.toContain(Routes.MONEY.ROOT);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const styleSheet = (params: { theme: Theme }) =>
backgroundColor: params.theme.colors.background.default,
},
scrollContent: {
paddingBottom: 0,
paddingBottom: 40,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,6 @@ describe('MoneyHomeView', () => {
expect(getByTestId(MoneyFooterTestIds.CONTAINER)).toBeOnTheScreen();
});

it('pressing the back button calls navigation.goBack', () => {
const { getByTestId } = renderWithProvider(<MoneyHomeView />);

fireEvent.press(getByTestId(MoneyHeaderTestIds.BACK_BUTTON));

expect(mockGoBack).toHaveBeenCalledTimes(1);
});

it('navigates to the Money activity screen when View all is pressed', () => {
const { getByTestId } = renderWithProvider(<MoneyHomeView />);

Expand Down
22 changes: 9 additions & 13 deletions app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,6 @@ const MoneyHomeView = () => {
return moneyFormatFiat(new BigNumber(earnings), currentCurrency);
}, [totalFiatRaw, apyPercent, currentCurrency, formattedZero]);

const handleBackPress = useCallback(() => {
navigation.goBack();
}, [navigation]);

const handleMenuPress = useCallback(() => {
navigation.navigate(Routes.MONEY.MODALS.ROOT, {
screen: Routes.MONEY.MODALS.MORE_SHEET,
Expand Down Expand Up @@ -218,10 +214,7 @@ const MoneyHomeView = () => {
twClassName="flex-1 bg-default"
testID={MoneyHomeViewTestIds.CONTAINER}
>
<MoneyHeader
onBackPress={handleBackPress}
onMenuPress={handleMenuPress}
/>
<MoneyHeader onMenuPress={handleMenuPress} />
<ScrollView
testID={MoneyHomeViewTestIds.SCROLL_VIEW}
contentContainerStyle={styles.scrollContent}
Expand Down Expand Up @@ -309,13 +302,16 @@ const MoneyHomeView = () => {
</>
)}
{!isMilestone && (
<MoneyWhatYouGet
apy={apyPercent}
onLearnMorePress={handleLearnMorePress}
/>
<>
<MoneyWhatYouGet
apy={apyPercent}
onLearnMorePress={handleLearnMorePress}
/>
<Divider />
</>
)}
<MoneyFooter onAddMoneyPress={handleAddPress} />
</ScrollView>
<MoneyFooter onAddMoneyPress={handleAddPress} />
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
IconColor,
IconName,
Skeleton,
Tag,
TagSeverity,
Text,
TextColor,
TextVariant,
Expand Down Expand Up @@ -87,19 +89,19 @@ const MoneyBalanceSummary = ({
/>
) : (
isPositiveNumber(apy) && (
<Box
twClassName="self-start rounded-md bg-success-muted px-2 py-0.5"
<Tag
severity={TagSeverity.Success}
testID={MoneyBalanceSummaryTestIds.APY_TAG}
>
<Text
variant={TextVariant.BodySm}
variant={TextVariant.BodyMd}
fontWeight={FontWeight.Medium}
color={TextColor.SuccessDefault}
testID={MoneyBalanceSummaryTestIds.APY}
>
{strings('money.apy_label', { percentage: apy })}
</Text>
</Box>
</Tag>
Comment thread
Kureev marked this conversation as resolved.
)
)}
{onApyInfoPress && isPositiveNumber(apy) && !isLoading && (
Expand Down

This file was deleted.

37 changes: 13 additions & 24 deletions app/components/UI/Money/components/MoneyFooter/MoneyFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useMemo } from 'react';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import React from 'react';
import {
Box,
Button,
Expand All @@ -8,35 +7,25 @@ import {
} from '@metamask/design-system-react-native';
import { strings } from '../../../../../../locales/i18n';
import { MoneyFooterTestIds } from './MoneyFooter.testIds';
import createStyles from './MoneyFooter.styles';

interface MoneyFooterProps {
onAddMoneyPress?: () => void;
}

const MoneyFooter = ({
onAddMoneyPress = () => undefined,
}: MoneyFooterProps) => {
const { bottom } = useSafeAreaInsets();
const styles = useMemo(() => createStyles(bottom), [bottom]);

return (
<Box
twClassName="px-4 pt-4 bg-default"
style={styles.container}
testID={MoneyFooterTestIds.CONTAINER}
}: MoneyFooterProps) => (
<Box twClassName="px-4 py-3" testID={MoneyFooterTestIds.CONTAINER}>
<Button
variant={ButtonVariant.Primary}
size={ButtonSize.Lg}
isFullWidth
onPress={onAddMoneyPress}
testID={MoneyFooterTestIds.ADD_MONEY_BUTTON}
>
<Button
variant={ButtonVariant.Primary}
size={ButtonSize.Lg}
Comment thread
cursor[bot] marked this conversation as resolved.
isFullWidth
onPress={onAddMoneyPress}
testID={MoneyFooterTestIds.ADD_MONEY_BUTTON}
>
{strings('money.footer.add_money')}
</Button>
</Box>
);
};
{strings('money.footer.add_money')}
</Button>
</Box>
);

export default MoneyFooter;
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,17 @@ import { render, fireEvent } from '@testing-library/react-native';
import MoneyHeader from './MoneyHeader';
import { MoneyHeaderTestIds } from './MoneyHeader.testIds';

const noop = jest.fn();

describe('MoneyHeader', () => {
it('renders the back and menu buttons', () => {
const { getByTestId } = render(
<MoneyHeader onBackPress={noop} onMenuPress={noop} />,
);
it('renders the menu button', () => {
const { getByTestId } = render(<MoneyHeader onMenuPress={jest.fn()} />);

expect(getByTestId(MoneyHeaderTestIds.BACK_BUTTON)).toBeOnTheScreen();
expect(getByTestId(MoneyHeaderTestIds.MENU_BUTTON)).toBeOnTheScreen();
});

it('calls onBackPress when the back button is pressed', () => {
const mockOnBackPress = jest.fn();
const { getByTestId } = render(
<MoneyHeader onBackPress={mockOnBackPress} onMenuPress={noop} />,
);

fireEvent.press(getByTestId(MoneyHeaderTestIds.BACK_BUTTON));

expect(mockOnBackPress).toHaveBeenCalledTimes(1);
});

it('calls onMenuPress when the menu button is pressed', () => {
const mockOnMenuPress = jest.fn();
const { getByTestId } = render(
<MoneyHeader onBackPress={noop} onMenuPress={mockOnMenuPress} />,
<MoneyHeader onMenuPress={mockOnMenuPress} />,
);

fireEvent.press(getByTestId(MoneyHeaderTestIds.MENU_BUTTON));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export const MoneyHeaderTestIds = {
CONTAINER: 'money-header-container',
BACK_BUTTON: 'money-header-back-button',
MENU_BUTTON: 'money-header-menu-button',
} as const;
11 changes: 1 addition & 10 deletions app/components/UI/Money/components/MoneyHeader/MoneyHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,15 @@ import { HeaderStandard, IconName } from '@metamask/design-system-react-native';
import { MoneyHeaderTestIds } from './MoneyHeader.testIds';

interface MoneyHeaderProps {
/**
* Handler for the back/navigation button
*/
onBackPress: () => void;
/**
* Handler for the options menu button
*/
onMenuPress: () => void;
}

const MoneyHeader = ({ onBackPress, onMenuPress }: MoneyHeaderProps) => (
const MoneyHeader = ({ onMenuPress }: MoneyHeaderProps) => (
<HeaderStandard
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The options menu button is not aligned with the header text.

image

testID={MoneyHeaderTestIds.CONTAINER}
onBack={onBackPress}
backButtonProps={{
accessibilityLabel: 'Back',
testID: MoneyHeaderTestIds.BACK_BUTTON,
}}
endButtonIconProps={[
{
iconName: IconName.MoreVertical,
Expand Down
Loading