Skip to content

Commit 218d54f

Browse files
committed
Update welcome splash screen
Update nav options Update Signup Update ConfirmEmail Update ConfirmPhoneNumber Update Address Update VerifyIdentity Update PersonalDetails Update KYC success and failure Update tests Update index.test.tsx.snap
1 parent 08fdc9c commit 218d54f

37 files changed

+2249
-2110
lines changed

app/components/UI/Card/Views/CardWelcome/CardWelcome.styles.ts

Lines changed: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,120 @@
1-
import { StyleSheet } from 'react-native';
1+
import { Platform, StyleSheet, Dimensions } from 'react-native';
2+
import { colors as importedColors } from '../../../../../styles/common';
23
import { Theme } from '@metamask/design-tokens';
34

4-
const createStyles = (theme: Theme, deviceWidth: number) =>
5+
// Responsive scaling utilities
6+
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
7+
8+
// Platform-specific base dimensions
9+
const BASE_WIDTH = 375;
10+
const BASE_HEIGHT_IOS = 812; // iPhone X/11/12/13/14/15 Pro base
11+
const BASE_HEIGHT_ANDROID = 736; // Common Android base
12+
13+
const MIN_SCREEN_HEIGHT_FOR_SMALL_SCREEN_STYLES = 750;
14+
15+
// Calculate platform-aware scaling factors
16+
const isIOS = Platform.OS === 'ios';
17+
const baseHeight = isIOS ? BASE_HEIGHT_IOS : BASE_HEIGHT_ANDROID;
18+
19+
const widthScale = screenWidth / BASE_WIDTH;
20+
const heightScale = screenHeight / baseHeight;
21+
22+
// Use more conservative scaling to prevent excessive padding
23+
const scale = Math.min(widthScale, heightScale);
24+
const conservativeScale = Math.min(scale, 1.2); // Cap scaling at 120%
25+
26+
// Platform-aware responsive scaling functions
27+
const scaleSize = (size: number) => Math.ceil(size * conservativeScale);
28+
const scaleFont = (size: number) => Math.ceil(size * conservativeScale);
29+
30+
// For vertical spacing, use percentage of available height instead of pure scaling
31+
const scaleVertical = (size: number) => {
32+
// Use percentage of screen height for more consistent spacing
33+
const percentage = size / baseHeight;
34+
return Math.ceil(screenHeight * percentage);
35+
};
36+
37+
const scaleHorizontal = (size: number) => Math.ceil(size * widthScale);
38+
39+
const createStyles = (theme: Theme) =>
540
StyleSheet.create({
6-
safeAreaView: {
41+
pageContainer: {
742
flex: 1,
43+
position: 'relative',
44+
maxHeight: '100%',
45+
width: '100%',
46+
backgroundColor: theme.colors.accent03.dark,
847
},
9-
container: {
48+
imageContainer: {
1049
flex: 1,
11-
backgroundColor: theme.colors.background.default,
12-
paddingHorizontal: 16,
13-
justifyContent: 'space-between',
14-
},
15-
wrapper: {
1650
alignItems: 'center',
51+
justifyContent: 'center',
52+
marginBottom: scaleVertical(16),
1753
},
18-
imageWrapper: {
54+
image: {
55+
width: '100%',
56+
height: '100%',
57+
resizeMode: 'cover',
58+
},
59+
contentContainer: {
60+
flex: 1,
61+
},
62+
headerContainer: {
1963
alignItems: 'center',
64+
paddingHorizontal: scaleHorizontal(16),
65+
paddingVertical: scaleVertical(16),
2066
},
21-
image: {
22-
width: deviceWidth * 0.9,
23-
height: deviceWidth,
67+
title: {
68+
fontFamily: Platform.OS === 'ios' ? 'MM Poly' : 'MM Poly Regular',
69+
fontWeight: '400',
70+
// make it smaller on smaller screens
71+
fontSize:
72+
screenHeight < MIN_SCREEN_HEIGHT_FOR_SMALL_SCREEN_STYLES ? 40 : 50,
73+
lineHeight:
74+
screenHeight < MIN_SCREEN_HEIGHT_FOR_SMALL_SCREEN_STYLES ? 40 : 50, // 100% of font size
75+
letterSpacing: 0,
76+
textAlign: 'center',
77+
paddingTop: scaleVertical(
78+
screenHeight < MIN_SCREEN_HEIGHT_FOR_SMALL_SCREEN_STYLES ? 8 : 12,
79+
),
80+
color: theme.colors.accent03.light,
81+
},
82+
titleDescription: {
83+
// make it smaller on smaller screens
84+
fontSize:
85+
screenHeight < MIN_SCREEN_HEIGHT_FOR_SMALL_SCREEN_STYLES ? 14 : 16,
86+
paddingTop: scaleVertical(10),
87+
paddingHorizontal: scaleHorizontal(8),
88+
textAlign: 'center',
89+
fontFamily: Platform.OS === 'ios' ? 'System' : 'Roboto', // Default system font
90+
fontWeight: '500',
91+
lineHeight: 24, // Line Height BodyMd
92+
letterSpacing: 0,
93+
color: theme.colors.accent03.light,
94+
},
95+
footerContainer: {
96+
display: 'flex',
97+
rowGap: scaleVertical(8),
98+
paddingHorizontal: scaleHorizontal(30),
99+
},
100+
getStartedButton: {
101+
borderRadius: scaleSize(12),
102+
backgroundColor: importedColors.white,
103+
},
104+
getStartedButtonText: {
105+
color: importedColors.btnBlack,
106+
fontWeight: '600',
107+
fontSize: scaleFont(16),
108+
},
109+
notNowButton: {
110+
borderRadius: scaleSize(12),
111+
backgroundColor: importedColors.transparent,
112+
borderWidth: 0,
24113
},
25-
button: {
26-
marginTop: 48,
27-
marginBottom: 32,
114+
notNowButtonText: {
115+
color: importedColors.white,
116+
fontWeight: '500',
117+
fontSize: scaleFont(16),
28118
},
29119
});
30120

app/components/UI/Card/Views/CardWelcome/CardWelcome.test.tsx

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@ import CardWelcome from './CardWelcome';
66
import { CardWelcomeSelectors } from '../../../../../../e2e/selectors/Card/CardWelcome.selectors';
77
import { strings } from '../../../../../../locales/i18n';
88
import Routes from '../../../../../constants/navigation/Routes';
9+
import { MetaMetricsEvents } from '../../../../hooks/useMetrics';
910

1011
// Mocks
1112
const mockNavigate = jest.fn();
13+
const mockTrackEvent = jest.fn();
14+
const mockBuild = jest.fn();
15+
const mockAddProperties = jest.fn(() => ({ build: mockBuild }));
16+
const mockCreateEventBuilder = jest.fn(() => ({
17+
addProperties: mockAddProperties,
18+
}));
1219

1320
jest.mock('@react-navigation/native', () => {
1421
const actual = jest.requireActual('@react-navigation/native');
@@ -20,25 +27,31 @@ jest.mock('@react-navigation/native', () => {
2027
};
2128
});
2229

30+
jest.mock('../../../../hooks/useMetrics', () => ({
31+
useMetrics: () => ({
32+
trackEvent: mockTrackEvent,
33+
createEventBuilder: mockCreateEventBuilder,
34+
}),
35+
MetaMetricsEvents: {
36+
CARD_VIEWED: 'Card Viewed',
37+
CARD_BUTTON_CLICKED: 'Card Button Clicked',
38+
},
39+
}));
40+
2341
jest.mock('../../../../../../locales/i18n', () => ({
2442
strings: (key: string) => {
2543
const map: Record<string, string> = {
2644
'card.card_onboarding.title': 'Enable MetaMask Card features',
2745
'card.card_onboarding.description':
2846
'Change your spending token and network by signing in with your Crypto Life email and password.',
29-
'card.card_onboarding.verify_account_button': 'Sign in',
30-
'card.card_onboarding.non_cardholder_title': 'Welcome to MetaMask Card',
31-
'card.card_onboarding.non_cardholder_description':
32-
'MetaMask Card is the free and easy way to spend your crypto, with rich onchain rewards.',
33-
'card.card_onboarding.non_cardholder_verify_account_button':
34-
'Get started',
35-
'card.card': 'Card',
47+
'card.card_onboarding.apply_now_button': 'Sign in',
48+
'predict.gtm_content.not_now': 'Not now',
3649
};
3750
return map[key] || key;
3851
},
3952
}));
4053

41-
jest.mock('../../../../../images/metal-card.png', () => 1);
54+
jest.mock('../../../../../images/mm-card-welcome.png', () => 1);
4255

4356
jest.mock('../../../../../util/theme', () => ({
4457
useTheme: () => ({ colors: { background: { default: '#fff' } } }),
@@ -57,9 +70,11 @@ describe('CardWelcome', () => {
5770
beforeEach(() => {
5871
jest.clearAllMocks();
5972
mockNavigate.mockClear();
73+
mockTrackEvent.mockClear();
74+
mockCreateEventBuilder.mockClear();
6075
});
6176

62-
describe('Non-cardholder flow', () => {
77+
describe('Render', () => {
6378
beforeEach(() => {
6479
store = createTestStore({ cardholderAccounts: [] });
6580
});
@@ -81,9 +96,10 @@ describe('CardWelcome', () => {
8196
expect(
8297
getByTestId(CardWelcomeSelectors.VERIFY_ACCOUNT_BUTTON),
8398
).toBeTruthy();
99+
expect(getByTestId('predict-gtm-not-now-button')).toBeTruthy();
84100
});
85101

86-
it('displays non-cardholder title when no cardholder accounts exist', () => {
102+
it('displays correct title and description', () => {
87103
const { getByTestId } = render(
88104
<Provider store={store}>
89105
<CardWelcome />
@@ -92,69 +108,62 @@ describe('CardWelcome', () => {
92108

93109
expect(
94110
getByTestId(CardWelcomeSelectors.WELCOME_TO_CARD_TITLE_TEXT),
95-
).toHaveTextContent(strings('card.card_onboarding.non_cardholder_title'));
111+
).toHaveTextContent(strings('card.card_onboarding.title'));
112+
expect(
113+
getByTestId(CardWelcomeSelectors.WELCOME_TO_CARD_DESCRIPTION_TEXT),
114+
).toHaveTextContent(strings('card.card_onboarding.description'));
96115
});
97116

98-
it('displays non-cardholder description when no cardholder accounts exist', () => {
99-
const { getByTestId } = render(
117+
it('tracks view event on mount', () => {
118+
render(
100119
<Provider store={store}>
101120
<CardWelcome />
102121
</Provider>,
103122
);
104123

105-
expect(
106-
getByTestId(CardWelcomeSelectors.WELCOME_TO_CARD_DESCRIPTION_TEXT),
107-
).toHaveTextContent(
108-
strings('card.card_onboarding.non_cardholder_description'),
124+
expect(mockCreateEventBuilder).toHaveBeenCalledWith(
125+
MetaMetricsEvents.CARD_VIEWED,
109126
);
127+
expect(mockTrackEvent).toHaveBeenCalled();
110128
});
129+
});
111130

112-
it('navigates to onboarding root when verify account button pressed', () => {
131+
describe('Interactions', () => {
132+
it('navigates to wallet home when "Not Now" is pressed', () => {
133+
store = createTestStore();
113134
const { getByTestId } = render(
114135
<Provider store={store}>
115136
<CardWelcome />
116137
</Provider>,
117138
);
118139

119-
fireEvent.press(getByTestId(CardWelcomeSelectors.VERIFY_ACCOUNT_BUTTON));
140+
fireEvent.press(getByTestId('predict-gtm-not-now-button'));
120141

121-
expect(mockNavigate).toHaveBeenCalledTimes(1);
122-
expect(mockNavigate).toHaveBeenCalledWith(Routes.CARD.ONBOARDING.ROOT);
142+
expect(mockNavigate).toHaveBeenCalledWith(Routes.WALLET.HOME);
123143
});
124144
});
125145

126-
describe('Cardholder flow', () => {
127-
beforeEach(() => {
128-
store = createTestStore({
129-
cardholderAccounts: ['0x1234567890abcdef'],
130-
});
131-
});
132-
133-
it('displays cardholder title when cardholder accounts exist', () => {
146+
describe('Navigation Flow', () => {
147+
it('navigates to onboarding root when verify account button pressed (Non-cardholder)', () => {
148+
store = createTestStore({ cardholderAccounts: [] });
134149
const { getByTestId } = render(
135150
<Provider store={store}>
136151
<CardWelcome />
137152
</Provider>,
138153
);
139154

140-
expect(
141-
getByTestId(CardWelcomeSelectors.WELCOME_TO_CARD_TITLE_TEXT),
142-
).toHaveTextContent(strings('card.card_onboarding.title'));
143-
});
155+
fireEvent.press(getByTestId(CardWelcomeSelectors.VERIFY_ACCOUNT_BUTTON));
144156

145-
it('displays cardholder description when cardholder accounts exist', () => {
146-
const { getByTestId } = render(
147-
<Provider store={store}>
148-
<CardWelcome />
149-
</Provider>,
157+
expect(mockNavigate).toHaveBeenCalledWith(Routes.CARD.ONBOARDING.ROOT);
158+
expect(mockCreateEventBuilder).toHaveBeenCalledWith(
159+
MetaMetricsEvents.CARD_BUTTON_CLICKED,
150160
);
151-
152-
expect(
153-
getByTestId(CardWelcomeSelectors.WELCOME_TO_CARD_DESCRIPTION_TEXT),
154-
).toHaveTextContent(strings('card.card_onboarding.description'));
155161
});
156162

157-
it('navigates to authentication when verify account button pressed', () => {
163+
it('navigates to authentication when verify account button pressed (Cardholder)', () => {
164+
store = createTestStore({
165+
cardholderAccounts: ['0x1234567890abcdef'],
166+
});
158167
const { getByTestId } = render(
159168
<Provider store={store}>
160169
<CardWelcome />
@@ -163,8 +172,10 @@ describe('CardWelcome', () => {
163172

164173
fireEvent.press(getByTestId(CardWelcomeSelectors.VERIFY_ACCOUNT_BUTTON));
165174

166-
expect(mockNavigate).toHaveBeenCalledTimes(1);
167175
expect(mockNavigate).toHaveBeenCalledWith(Routes.CARD.AUTHENTICATION);
176+
expect(mockCreateEventBuilder).toHaveBeenCalledWith(
177+
MetaMetricsEvents.CARD_BUTTON_CLICKED,
178+
);
168179
});
169180
});
170181
});

0 commit comments

Comments
 (0)