Skip to content

Commit 4e7c414

Browse files
authored
Merge pull request #18159 from mozilla/FXA-10237
feat(settings): Create frontend components for recovery phone setup from Settings
2 parents 9634c11 + c6f1042 commit 4e7c414

37 files changed

Lines changed: 1179 additions & 135 deletions

File tree

packages/functional-tests/pages/resetPassword.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,14 @@ export class ResetPasswordPage extends BaseLayout {
3636
});
3737
}
3838

39-
get statusBar() {
39+
get successBanner() {
4040
return this.page.getByRole('status');
4141
}
4242

43+
get errorBanner() {
44+
return this.page.getByRole('alert');
45+
}
46+
4347
get createNewPasswordHeading() {
4448
return this.page.getByRole('heading', { name: 'Create new password' });
4549
}

packages/functional-tests/tests/oauth/totp.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ test.describe('severity-1 #smoke', () => {
2929
const { secret } = await totp.fillOutTotpForms();
3030
await expect(settings.settingsHeading).toBeVisible();
3131
await expect(settings.alertBar).toHaveText(
32-
'Two-step authentication enabled'
32+
'Two-step authentication has been enabled'
3333
);
3434
await expect(settings.totp.status).toHaveText('Enabled');
3535
await settings.signOut();

packages/functional-tests/tests/react-conversion/signinTotp.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ test.describe('severity-1 #smoke', () => {
3333

3434
await expect(settings.settingsHeading).toBeVisible();
3535
await expect(settings.alertBar).toHaveText(
36-
'Two-step authentication enabled'
36+
'Two-step authentication has been enabled'
3737
);
3838
await expect(settings.totp.status).toHaveText('Enabled');
3939

@@ -78,7 +78,7 @@ test.describe('severity-1 #smoke', () => {
7878

7979
await expect(settings.settingsHeading).toBeVisible();
8080
await expect(settings.alertBar).toHaveText(
81-
'Two-step authentication enabled'
81+
'Two-step authentication has been enabled'
8282
);
8383
await expect(settings.totp.status).toHaveText('Enabled');
8484

@@ -122,7 +122,7 @@ test.describe('severity-1 #smoke', () => {
122122

123123
await expect(settings.settingsHeading).toBeVisible();
124124
await expect(settings.alertBar).toHaveText(
125-
'Two-step authentication enabled'
125+
'Two-step authentication has been enabled'
126126
);
127127
await expect(settings.totp.status).toHaveText('Enabled');
128128

packages/functional-tests/tests/resetPassword/resetPassword.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ test.describe('severity-1 #smoke', () => {
109109

110110
resetPassword.resendButton.click();
111111

112-
await expect(resetPassword.statusBar).toHaveText(
112+
await expect(resetPassword.successBanner).toHaveText(
113113
/A new code was sent to your email./
114114
);
115115
});
@@ -121,7 +121,7 @@ test.describe('severity-1 #smoke', () => {
121121

122122
await resetPassword.fillOutEmailForm('[email protected]');
123123

124-
await expect(resetPassword.statusBar).toHaveText('Unknown account');
124+
await expect(resetPassword.errorBanner).toHaveText('Unknown account');
125125
});
126126

127127
test('browse directly to page with email on query params', async ({
@@ -161,7 +161,7 @@ test.describe('severity-1 #smoke', () => {
161161

162162
await expect(settings.settingsHeading).toBeVisible();
163163
await expect(settings.alertBar).toHaveText(
164-
'Two-step authentication enabled'
164+
'Two-step authentication has been enabled'
165165
);
166166
await expect(settings.totp.status).toHaveText('Enabled');
167167

@@ -226,7 +226,7 @@ test.describe('severity-1 #smoke', () => {
226226

227227
await expect(settings.settingsHeading).toBeVisible();
228228
await expect(settings.alertBar).toHaveText(
229-
'Two-step authentication enabled'
229+
'Two-step authentication has been enabled'
230230
);
231231
await expect(settings.totp.status).toHaveText('Enabled');
232232

@@ -300,7 +300,7 @@ test.describe('severity-1 #smoke', () => {
300300

301301
await expect(settings.settingsHeading).toBeVisible();
302302
await expect(settings.alertBar).toHaveText(
303-
'Two-step authentication enabled'
303+
'Two-step authentication has been enabled'
304304
);
305305
await expect(settings.totp.status).toHaveText('Enabled');
306306

@@ -385,7 +385,7 @@ test.describe('severity-1 #smoke', () => {
385385

386386
await expect(settings.settingsHeading).toBeVisible();
387387
await expect(settings.alertBar).toHaveText(
388-
'Two-step authentication enabled'
388+
'Two-step authentication has been enabled'
389389
);
390390
await expect(settings.totp.status).toHaveText('Enabled');
391391

packages/functional-tests/tests/settings/totp.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ test.describe('severity-1 #smoke', () => {
5656

5757
await expect(settings.settingsHeading).toBeVisible();
5858
await expect(settings.alertBar).toHaveText(
59-
'Two-step authentication enabled'
59+
'Two-step authentication has been enabled'
6060
);
6161
await expect(settings.totp.status).toHaveText('Enabled');
6262

@@ -343,7 +343,9 @@ async function addTotp(
343343
const totpCredentials = await totp.fillOutTotpForms();
344344

345345
await expect(settings.settingsHeading).toBeVisible();
346-
await expect(settings.alertBar).toHaveText('Two-step authentication enabled');
346+
await expect(settings.alertBar).toHaveText(
347+
'Two-step authentication has been enabled'
348+
);
347349
await expect(settings.totp.status).toHaveText('Enabled');
348350

349351
return totpCredentials;

packages/fxa-settings/src/components/Banner/index.stories.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,21 @@ export const TypeInfo = () => (
161161
</AppLayout>
162162
);
163163

164+
export const TypeInfoFancy = () => (
165+
<AppLayout>
166+
<Banner
167+
type="info"
168+
isFancy
169+
content={{
170+
localizedHeading: sampleHeading,
171+
localizedDescription: sampleDescription,
172+
}}
173+
link={{ url: '#', localizedText: sampleCtaText }}
174+
dismissButton={{ action: () => alert('Dismiss clicked') }}
175+
/>
176+
</AppLayout>
177+
);
178+
164179
export const TypeSuccess = () => (
165180
<AppLayout>
166181
<Banner

packages/fxa-settings/src/components/Banner/index.test.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,24 @@ describe('Banner Component', () => {
2323
render(<Banner {...getDefaultProps()} />);
2424
expect(screen.getByText('Heading')).toBeInTheDocument();
2525
expect(screen.getByText('This is a description')).toBeInTheDocument();
26-
expect(
27-
screen.getByRole('img', { name: /Information/i })
28-
).toBeInTheDocument();
2926
});
3027

3128
it('renders the component with error type', () => {
3229
render(<Banner {...getDefaultProps('error')} />);
3330
expect(screen.getByText('Heading')).toBeInTheDocument();
3431
expect(screen.getByText('This is a description')).toBeInTheDocument();
35-
expect(screen.getByRole('img', { name: /Error/i })).toBeInTheDocument();
3632
});
3733

3834
it('renders the component with success type', () => {
3935
render(<Banner {...getDefaultProps('success')} />);
4036
expect(screen.getByText('Heading')).toBeInTheDocument();
4137
expect(screen.getByText('This is a description')).toBeInTheDocument();
42-
expect(screen.getByRole('img', { name: /Success/i })).toBeInTheDocument();
4338
});
4439

4540
it('renders the component with warning type', () => {
4641
render(<Banner {...getDefaultProps('warning')} />);
4742
expect(screen.getByText('Heading')).toBeInTheDocument();
4843
expect(screen.getByText('This is a description')).toBeInTheDocument();
49-
expect(
50-
screen.getByRole('img', { name: 'Attention' })
51-
).toBeInTheDocument();
5244
});
5345
});
5446

packages/fxa-settings/src/components/Banner/index.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
InformationOutlineCurrentIcon as InfoIcon,
1111
AlertOutlineCurrentIcon as WarningIcon,
1212
ErrorOutlineCurrentIcon as ErrorIcon,
13+
InformationOutlineBlueIcon,
1314
} from '../Icons';
1415
import classNames from 'classnames';
1516
import { useFtlMsgResolver } from '../../models';
@@ -23,31 +24,45 @@ export const Banner = ({
2324
animation,
2425
dismissButton,
2526
link,
27+
isFancy,
28+
bannerId,
2629
}: BannerProps) => {
2730
return (
2831
<div
32+
id={bannerId || ''}
2933
className={classNames(
3034
'my-4 flex flex-row no-wrap items-center px-4 py-3 gap-3.5 rounded-md border border-transparent text-start text-sm',
3135
type === 'error' && 'bg-red-100',
32-
type === 'info' && 'bg-blue-50',
36+
type === 'info' && !isFancy && 'bg-blue-50',
37+
type === 'info' &&
38+
isFancy &&
39+
'bg-gradient-to-tr from-blue-600/10 to-purple-500/10',
3340
type === 'success' && 'bg-green-200',
3441
type === 'warning' && 'bg-orange-50',
3542
animation?.animate && animation?.className
3643
)}
37-
role="status"
44+
role={type === 'error' ? 'alert' : 'status'}
45+
aria-live={type === 'error' ? 'assertive' : 'polite'}
3846
onAnimationEnd={animation?.handleAnimationEnd}
47+
tabIndex={-1}
3948
>
4049
{/* Icon fills use 'currentColor' (from text color) for better accessibility in HCM mode */}
41-
{type === 'error' && <ErrorIcon className="shrink-0" />}
42-
{type === 'info' && <InfoIcon className="shrink-0" />}
50+
{type === 'error' && <ErrorIcon className="shrink-0" ariaHidden />}
51+
{type === 'info' && !isFancy && (
52+
<InfoIcon className="shrink-0" ariaHidden />
53+
)}
54+
{type === 'info' && isFancy && (
55+
<InformationOutlineBlueIcon className="shrink-0" ariaHidden />
56+
)}
4357
{type === 'success' && (
4458
<CheckmarkCircleOutlineCurrentIcon
4559
className="shrink-0"
4660
mode="success"
61+
ariaHidden
4762
/>
4863
)}
4964
{type === 'warning' && (
50-
<WarningIcon className="shrink-0" mode="attention" />
65+
<WarningIcon className="shrink-0" mode="attention" ariaHidden />
5166
)}
5267

5368
<div className="flex flex-col grow ">
@@ -86,7 +101,12 @@ export const Banner = ({
86101
className={classNames(
87102
'shrink-0 self-start hover:backdrop-saturate-150 focus:backdrop-saturate-200',
88103
type === 'error' && 'hover:bg-red-200 focus:bg-red-300',
89-
type === 'info' && 'hover:bg-blue-100 focus:bg-blue-200',
104+
type === 'info' &&
105+
!isFancy &&
106+
'hover:bg-blue-100 focus:bg-blue-200',
107+
type === 'info' &&
108+
isFancy &&
109+
'hover:bg-gradient-to-tr hover:from-blue-700/10 hover:to-purple-600/10 focus:bg-gradient-to-tr focus:from-blue-800/10 focus:to-purple-700/10',
90110
type === 'success' && 'hover:bg-green-400 focus:bg-green-500',
91111
type === 'warning' && 'hover:bg-orange-100 focus:bg-orange-200'
92112
)}

packages/fxa-settings/src/components/Banner/interfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export type BannerProps = {
2020
animation?: Animation;
2121
dismissButton?: DismissButtonProps;
2222
link?: BannerLinkProps;
23+
isFancy?: boolean;
24+
bannerId?: string;
2325
};
2426

2527
export type Animation = {

packages/fxa-settings/src/components/FormPhoneNumber/index.stories.tsx

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,55 @@ import React from 'react';
66
import FormPhoneNumber from '.';
77
import { withLocalization } from 'fxa-react/lib/storybooks';
88
import { Meta } from '@storybook/react';
9+
import { action } from '@storybook/addon-actions';
910
import AppLayout from '../AppLayout';
1011

1112
export default {
1213
title: 'Components/FormPhoneNumber',
1314
component: FormPhoneNumber,
14-
decorators: [withLocalization],
15+
decorators: [
16+
(Story) => (
17+
<AppLayout>
18+
<Story />
19+
</AppLayout>
20+
),
21+
withLocalization,
22+
],
1523
} as Meta;
1624

25+
const mockSubmit = async (phoneNumber: string) => {
26+
action('submitPhoneNumber')(phoneNumber);
27+
return { hasErrors: false };
28+
};
29+
1730
export const Default = () => (
18-
<AppLayout>
19-
<FormPhoneNumber localizedCTAText="Send code" />
20-
</AppLayout>
31+
<FormPhoneNumber
32+
localizedCTAText="Send code"
33+
submitPhoneNumber={mockSubmit}
34+
/>
35+
);
36+
37+
export const WithError = () => (
38+
<FormPhoneNumber
39+
localizedCTAText="Send code"
40+
submitPhoneNumber={async () => {
41+
action('submitPhoneNumber')();
42+
return { hasErrors: true };
43+
}}
44+
/>
2145
);
2246

23-
export const WithInfo = () => (
24-
<AppLayout>
25-
<FormPhoneNumber showInfo localizedCTAText="Confirm" />
26-
</AppLayout>
47+
export const WithInfoBanner = () => (
48+
<FormPhoneNumber
49+
infoBannerContent={{
50+
localizedDescription: 'This is a description',
51+
localizedHeading: 'This is a heading',
52+
}}
53+
infoBannerLink={{
54+
localizedText: 'This is a link',
55+
path: '#',
56+
}}
57+
localizedCTAText="Confirm"
58+
submitPhoneNumber={mockSubmit}
59+
/>
2760
);

0 commit comments

Comments
 (0)