Skip to content

Commit

Permalink
Merge pull request #18159 from mozilla/FXA-10237
Browse files Browse the repository at this point in the history
feat(settings): Create frontend components for recovery phone setup from Settings
  • Loading branch information
vpomerleau authored Jan 6, 2025
2 parents 9634c11 + c6f1042 commit 4e7c414
Show file tree
Hide file tree
Showing 37 changed files with 1,179 additions and 135 deletions.
6 changes: 5 additions & 1 deletion packages/functional-tests/pages/resetPassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ export class ResetPasswordPage extends BaseLayout {
});
}

get statusBar() {
get successBanner() {
return this.page.getByRole('status');
}

get errorBanner() {
return this.page.getByRole('alert');
}

get createNewPasswordHeading() {
return this.page.getByRole('heading', { name: 'Create new password' });
}
Expand Down
2 changes: 1 addition & 1 deletion packages/functional-tests/tests/oauth/totp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test.describe('severity-1 #smoke', () => {
const { secret } = await totp.fillOutTotpForms();
await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');
await settings.signOut();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ test.describe('severity-1 #smoke', () => {

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

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

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

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

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ test.describe('severity-1 #smoke', () => {

resetPassword.resendButton.click();

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

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

await expect(resetPassword.statusBar).toHaveText('Unknown account');
await expect(resetPassword.errorBanner).toHaveText('Unknown account');
});

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

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

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

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

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

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

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

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

Expand Down
6 changes: 4 additions & 2 deletions packages/functional-tests/tests/settings/totp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ test.describe('severity-1 #smoke', () => {

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText(
'Two-step authentication enabled'
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

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

await expect(settings.settingsHeading).toBeVisible();
await expect(settings.alertBar).toHaveText('Two-step authentication enabled');
await expect(settings.alertBar).toHaveText(
'Two-step authentication has been enabled'
);
await expect(settings.totp.status).toHaveText('Enabled');

return totpCredentials;
Expand Down
15 changes: 15 additions & 0 deletions packages/fxa-settings/src/components/Banner/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ export const TypeInfo = () => (
</AppLayout>
);

export const TypeInfoFancy = () => (
<AppLayout>
<Banner
type="info"
isFancy
content={{
localizedHeading: sampleHeading,
localizedDescription: sampleDescription,
}}
link={{ url: '#', localizedText: sampleCtaText }}
dismissButton={{ action: () => alert('Dismiss clicked') }}
/>
</AppLayout>
);

export const TypeSuccess = () => (
<AppLayout>
<Banner
Expand Down
8 changes: 0 additions & 8 deletions packages/fxa-settings/src/components/Banner/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,24 @@ describe('Banner Component', () => {
render(<Banner {...getDefaultProps()} />);
expect(screen.getByText('Heading')).toBeInTheDocument();
expect(screen.getByText('This is a description')).toBeInTheDocument();
expect(
screen.getByRole('img', { name: /Information/i })
).toBeInTheDocument();
});

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

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

it('renders the component with warning type', () => {
render(<Banner {...getDefaultProps('warning')} />);
expect(screen.getByText('Heading')).toBeInTheDocument();
expect(screen.getByText('This is a description')).toBeInTheDocument();
expect(
screen.getByRole('img', { name: 'Attention' })
).toBeInTheDocument();
});
});

Expand Down
32 changes: 26 additions & 6 deletions packages/fxa-settings/src/components/Banner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
InformationOutlineCurrentIcon as InfoIcon,
AlertOutlineCurrentIcon as WarningIcon,
ErrorOutlineCurrentIcon as ErrorIcon,
InformationOutlineBlueIcon,
} from '../Icons';
import classNames from 'classnames';
import { useFtlMsgResolver } from '../../models';
Expand All @@ -23,31 +24,45 @@ export const Banner = ({
animation,
dismissButton,
link,
isFancy,
bannerId,
}: BannerProps) => {
return (
<div
id={bannerId || ''}
className={classNames(
'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',
type === 'error' && 'bg-red-100',
type === 'info' && 'bg-blue-50',
type === 'info' && !isFancy && 'bg-blue-50',
type === 'info' &&
isFancy &&
'bg-gradient-to-tr from-blue-600/10 to-purple-500/10',
type === 'success' && 'bg-green-200',
type === 'warning' && 'bg-orange-50',
animation?.animate && animation?.className
)}
role="status"
role={type === 'error' ? 'alert' : 'status'}
aria-live={type === 'error' ? 'assertive' : 'polite'}
onAnimationEnd={animation?.handleAnimationEnd}
tabIndex={-1}
>
{/* Icon fills use 'currentColor' (from text color) for better accessibility in HCM mode */}
{type === 'error' && <ErrorIcon className="shrink-0" />}
{type === 'info' && <InfoIcon className="shrink-0" />}
{type === 'error' && <ErrorIcon className="shrink-0" ariaHidden />}
{type === 'info' && !isFancy && (
<InfoIcon className="shrink-0" ariaHidden />
)}
{type === 'info' && isFancy && (
<InformationOutlineBlueIcon className="shrink-0" ariaHidden />
)}
{type === 'success' && (
<CheckmarkCircleOutlineCurrentIcon
className="shrink-0"
mode="success"
ariaHidden
/>
)}
{type === 'warning' && (
<WarningIcon className="shrink-0" mode="attention" />
<WarningIcon className="shrink-0" mode="attention" ariaHidden />
)}

<div className="flex flex-col grow ">
Expand Down Expand Up @@ -86,7 +101,12 @@ export const Banner = ({
className={classNames(
'shrink-0 self-start hover:backdrop-saturate-150 focus:backdrop-saturate-200',
type === 'error' && 'hover:bg-red-200 focus:bg-red-300',
type === 'info' && 'hover:bg-blue-100 focus:bg-blue-200',
type === 'info' &&
!isFancy &&
'hover:bg-blue-100 focus:bg-blue-200',
type === 'info' &&
isFancy &&
'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',
type === 'success' && 'hover:bg-green-400 focus:bg-green-500',
type === 'warning' && 'hover:bg-orange-100 focus:bg-orange-200'
)}
Expand Down
2 changes: 2 additions & 0 deletions packages/fxa-settings/src/components/Banner/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export type BannerProps = {
animation?: Animation;
dismissButton?: DismissButtonProps;
link?: BannerLinkProps;
isFancy?: boolean;
bannerId?: string;
};

export type Animation = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,55 @@ import React from 'react';
import FormPhoneNumber from '.';
import { withLocalization } from 'fxa-react/lib/storybooks';
import { Meta } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import AppLayout from '../AppLayout';

export default {
title: 'Components/FormPhoneNumber',
component: FormPhoneNumber,
decorators: [withLocalization],
decorators: [
(Story) => (
<AppLayout>
<Story />
</AppLayout>
),
withLocalization,
],
} as Meta;

const mockSubmit = async (phoneNumber: string) => {
action('submitPhoneNumber')(phoneNumber);
return { hasErrors: false };
};

export const Default = () => (
<AppLayout>
<FormPhoneNumber localizedCTAText="Send code" />
</AppLayout>
<FormPhoneNumber
localizedCTAText="Send code"
submitPhoneNumber={mockSubmit}
/>
);

export const WithError = () => (
<FormPhoneNumber
localizedCTAText="Send code"
submitPhoneNumber={async () => {
action('submitPhoneNumber')();
return { hasErrors: true };
}}
/>
);

export const WithInfo = () => (
<AppLayout>
<FormPhoneNumber showInfo localizedCTAText="Confirm" />
</AppLayout>
export const WithInfoBanner = () => (
<FormPhoneNumber
infoBannerContent={{
localizedDescription: 'This is a description',
localizedHeading: 'This is a heading',
}}
infoBannerLink={{
localizedText: 'This is a link',
path: '#',
}}
localizedCTAText="Confirm"
submitPhoneNumber={mockSubmit}
/>
);
Loading

0 comments on commit 4e7c414

Please sign in to comment.