feat(settings): Enable collapsing/expanding reset password warning

Because:

* We want to collapse the warning message on mobile by default to preserve real estate

This commit:

* Move the warning into a separate component
* Convert to use the details/summary elements
* Styling tweaks
* Copy updates to match latest design for complete reset password
* Remove unused old version of WarningMessage

Closes #FXA-10459
This commit is contained in:
Valerie Pomerleau 2024-09-25 12:43:22 -07:00
Родитель 8dd18028ff
Коммит e57626ef20
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 33A451F0BB2180B4
19 изменённых файлов: 292 добавлений и 252 удалений

Просмотреть файл

@ -83,7 +83,7 @@ export class ResetPasswordPage extends BaseLayout {
}
get reenterPasswordTextbox() {
return this.page.getByRole('textbox', { name: 'Re-enter password' });
return this.page.getByRole('textbox', { name: 'Confirm password' });
}
get resetPasswordButton() {
@ -115,14 +115,12 @@ export class ResetPasswordPage extends BaseLayout {
}
get dataLossWarning() {
return this.page.getByText(
'Resetting your password may delete your encrypted browser data.'
);
return this.page.getByText('Your browser data may not be recovered');
}
get resetPasswordWithRecoveryKey() {
return this.page.getByRole('link', {
name: 'Reset your password with your recovery key.',
name: 'Reset your password and keep your data',
});
}

Просмотреть файл

@ -9,8 +9,8 @@ form-password-with-inline-criteria-signup-submit-button = Create account
form-password-with-inline-criteria-reset-new-password =
.label = New password
form-password-with-inline-criteria-confirm-password =
.label = Re-enter password
form-password-with-inline-criteria-reset-submit-button-2 = Create new password
.label = Confirm password
form-password-with-inline-criteria-reset-submit-button = Create new password
form-password-with-inline-criteria-match-error = Passwords do not match
form-password-with-inline-criteria-sr-too-short-message = Password must contain at least 8 characters.

Просмотреть файл

@ -27,7 +27,7 @@ describe('FormPasswordWithInlineCriteria component', () => {
await waitFor(() => {
screen.getByLabelText('New password');
});
screen.getByLabelText('Re-enter password');
screen.getByLabelText('Confirm password');
screen.getByRole('button', { name: 'Create new password' });
});

Просмотреть файл

@ -54,9 +54,9 @@ const getTemplateValues = (passwordFormType: PasswordFormType) => {
templateValues.passwordLabel = 'New password';
templateValues.confirmPasswordFtlId =
'form-password-with-inline-criteria-confirm-password';
templateValues.confirmPasswordLabel = 'Re-enter password';
templateValues.confirmPasswordLabel = 'Confirm password';
templateValues.buttonFtlId =
'form-password-with-inline-criteria-reset-submit-button-2';
'form-password-with-inline-criteria-reset-submit-button';
templateValues.buttonText = 'Create new password';
break;
}

Просмотреть файл

@ -34,7 +34,7 @@ export const PasswordStrengthInline = ({
}: PasswordStrengthInlineProps) => {
return (
<div
className="leading-5 text-sm"
className="text-sm mb-2"
id="password-strength-inline"
aria-live="polite"
>
@ -42,23 +42,17 @@ export const PasswordStrengthInline = ({
Pick a strong password you havent used on other sites. Ensure it meets
the security requirements:
</p>
<ul className="mt-2 mb-2">
<li data-testid="password-min-char-req" className="flex ">
<ul className="mt-2 mb-2 text-grey-400">
<li data-testid="password-min-char-req" className="flex -mb-1">
<span className="w-7 h-7 text-center">
{isPasswordEmpty && '•'}
{!isPasswordEmpty && <ValidationIcon hasError={isTooShort} />}
</span>
<FtlMsg id="password-strength-inline-min-length">
<span
className={`ps-2 ${
!isPasswordEmpty && isTooShort ? 'text-red-700' : ''
}`}
>
At least 8 characters
</span>
<span className="ps-2">At least 8 characters</span>
</FtlMsg>
</li>
<li data-testid="password-not-email-req" className="flex ">
<li data-testid="password-not-email-req" className="flex -mb-1">
<span className="w-7 h-7 text-center">
{isPasswordEmpty && '•'}
{!isPasswordEmpty && (
@ -66,34 +60,20 @@ export const PasswordStrengthInline = ({
)}
</span>
<FtlMsg id="password-strength-inline-not-email">
<span
className={`ps-2 ${
!isPasswordEmpty && !isTooShort && isSameAsEmail
? 'text-red-700'
: ''
}`}
>
Not your email address
</span>
<span className="ps-2">Not your email address</span>
</FtlMsg>
</li>
<li data-testid="password-not-common-req" className="flex ">
<li data-testid="password-not-common-req" className="flex -mb-1">
<span className="w-7 h-7 text-center">
{isPasswordEmpty && '•'}
{!isPasswordEmpty && <ValidationIcon hasError={isCommon} />}
</span>
<FtlMsg id="password-strength-inline-not-common">
<span
className={`ps-2 ${
!isPasswordEmpty && isCommon ? 'text-red-700' : ''
}`}
>
Not a commonly used password
</span>
<span className="ps-2">Not a commonly used password</span>
</FtlMsg>
</li>
{isUnconfirmed !== undefined && (
<li data-testid="passwords-match" className="flex ">
<li data-testid="passwords-match" className="flex">
<span className="w-7 h-7 text-center">
{(isPasswordEmpty || isConfirmedPasswordEmpty) && '•'}
{!(isPasswordEmpty || isConfirmedPasswordEmpty) && (
@ -101,13 +81,7 @@ export const PasswordStrengthInline = ({
)}
</span>
<FtlMsg id="password-strength-inline-confirmed-must-match">
<span
className={`ps-2 ${
!isPasswordEmpty && !isConfirmedPasswordEmpty && isUnconfirmed
? 'text-red-700'
: ''
}`}
>
<span className="ps-2">
Confirmation matches the new password
</span>
</FtlMsg>

Просмотреть файл

@ -0,0 +1,3 @@
<svg width="12" height="7" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.35201 6.99891L11.818 1.53391C11.8762 1.47594 11.9224 1.40702 11.9539 1.33113C11.9854 1.25524 12.0016 1.17387 12.0015 1.0917C12.0014 1.00954 11.985 0.928201 11.9534 0.852379C11.9217 0.776558 11.8754 0.707747 11.817 0.649909C11.6994 0.533089 11.5403 0.467529 11.3745 0.467529C11.2087 0.467529 11.0497 0.533089 10.932 0.649909L5.99801 5.58491L1.06801 0.650908C0.949644 0.53853 0.79207 0.476823 0.628869 0.47894C0.465668 0.481056 0.309748 0.546828 0.194338 0.662238C0.0789275 0.777648 0.0131553 0.933569 0.011039 1.09677C0.0089227 1.25997 0.0706296 1.41754 0.183008 1.53591L5.64801 6.99991L6.35201 6.99891Z" fill="#15141A"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 734 B

Просмотреть файл

@ -0,0 +1,13 @@
## ResetPasswordWarning component
## Warning shown to sync users that reset their password without using an account recovery key
password-reset-warning-icon = Warning
password-reset-chevron-expanded = Collapse warning
password-reset-chevron-collapsed = Expand warning
password-reset-data-may-not-be-recovered = Your browser data may not be recovered
password-reset-previously-signed-in-device = Have a device where you previously signed in?
password-reset-data-may-be-saved-locally = Your browser data may be locally saved on that device. Sign in there with your new password to restore and sync.
password-reset-no-old-device = Have a new device but dont have your old one?
password-reset-encrypted-data-cannot-be-recovered = Were sorry, but your encrypted browser data on Firefox servers cant be recovered. However, you can still access your local data on any device where you have previously signed in.
password-reset-learn-about-restoring-account-data = Learn more about restoring account data

Просмотреть файл

@ -1,6 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="16 - Non-Sync / device / non-synce device-16">
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M2.1406 3.26192C2.04987 3.49028 2 3.73932 2 4V10C2 11.1046 2.89543 12 4 12H7.25V13.25H4.5C4.08579 13.25 3.75 13.5858 3.75 14C3.75 14.4142 4.08579 14.75 4.5 14.75H8H11.5C11.9142 14.75 12.25 14.4142 12.25 14C12.25 13.5858 11.9142 13.25 11.5 13.25H8.75V12H10.8787L9.37868 10.5H4C3.72386 10.5 3.5 10.2761 3.5 10V4.62132L2.1406 3.26192ZM12.5 9.37868V4C12.5 3.72386 12.2761 3.5 12 3.5H6.62132L5.12132 2H12C13.1046 2 14 2.89543 14 4V10C14 10.2607 13.9501 10.5097 13.8594 10.7381L12.5 9.37868Z" fill="#15141A"/>
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M2.1406 3.26192C2.04987 3.49028 2 3.73932 2 4V10C2 11.1046 2.89543 12 4 12H7.25V13.25H4.5C4.08579 13.25 3.75 13.5858 3.75 14C3.75 14.4142 4.08579 14.75 4.5 14.75H8H11.5C11.9142 14.75 12.25 14.4142 12.25 14C12.25 13.5858 11.9142 13.25 11.5 13.25H8.75V12H10.8787L9.37868 10.5H4C3.72386 10.5 3.5 10.2761 3.5 10V4.62132L2.1406 3.26192ZM12.5 9.37868V4C12.5 3.72386 12.2761 3.5 12 3.5H6.62132L5.12132 2H12C13.1046 2 14 2.89543 14 4V10C14 10.2607 13.9501 10.5097 13.8594 10.7381L12.5 9.37868Z" fill="currentColor"/>
<path id="Vector 470" d="M3 2L13 12" stroke="#15141A" stroke-width="1.5" stroke-linecap="round"/>
</g>
</svg>

До

Ширина:  |  Высота:  |  Размер: 827 B

После

Ширина:  |  Высота:  |  Размер: 832 B

Просмотреть файл

@ -1,6 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id=" 16 - Sync / device / synce device-16">
<path id="Vector 474" d="M5.5 6.5L7.5 8.5L11 5" stroke="#15141A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M4 3.5H12C12.2761 3.5 12.5 3.72386 12.5 4V10C12.5 10.2761 12.2761 10.5 12 10.5H4C3.72386 10.5 3.5 10.2761 3.5 10V4C3.5 3.72386 3.72386 3.5 4 3.5ZM2 4C2 2.89543 2.89543 2 4 2H12C13.1046 2 14 2.89543 14 4V10C14 11.1046 13.1046 12 12 12H8.75V13.25H11.5C11.9142 13.25 12.25 13.5858 12.25 14C12.25 14.4142 11.9142 14.75 11.5 14.75H8H4.5C4.08579 14.75 3.75 14.4142 3.75 14C3.75 13.5858 4.08579 13.25 4.5 13.25H7.25V12H4C2.89543 12 2 11.1046 2 10V4Z" fill="#15141A"/>
<path id="Vector 474" d="M5.5 6.5L7.5 8.5L11 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M4 3.5H12C12.2761 3.5 12.5 3.72386 12.5 4V10C12.5 10.2761 12.2761 10.5 12 10.5H4C3.72386 10.5 3.5 10.2761 3.5 10V4C3.5 3.72386 3.72386 3.5 4 3.5ZM2 4C2 2.89543 2.89543 2 4 2H12C13.1046 2 14 2.89543 14 4V10C14 11.1046 13.1046 12 12 12H8.75V13.25H11.5C11.9142 13.25 12.25 13.5858 12.25 14C12.25 14.4142 11.9142 14.75 11.5 14.75H8H4.5C4.08579 14.75 3.75 14.4142 3.75 14C3.75 13.5858 4.08579 13.25 4.5 13.25H7.25V12H4C2.89543 12 2 11.1046 2 10V4Z" fill="currentColor"/>
</g>
</svg>

До

Ширина:  |  Высота:  |  Размер: 809 B

После

Ширина:  |  Высота:  |  Размер: 819 B

Просмотреть файл

До

Ширина:  |  Высота:  |  Размер: 893 B

После

Ширина:  |  Высота:  |  Размер: 893 B

Просмотреть файл

@ -3,29 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import WarningMessage from '.';
import AppLayout from '../AppLayout';
import { Meta } from '@storybook/react';
import {
MOCK_WARNING_MESSAGE_FTL_ID,
MOCK_WARNING_MESSAGE,
MOCK_WARNING_TYPE,
} from './mocks';
import { withLocalization } from 'fxa-react/lib/storybooks';
import ResetPasswordWarning from '.';
import AppLayout from '../AppLayout';
export default {
title: 'Components/WarningMessage',
component: WarningMessage,
title: 'Components/ResetPasswordWarning',
component: ResetPasswordWarning,
decorators: [withLocalization],
} as Meta;
export const Default = () => (
<AppLayout>
<WarningMessage
warningMessageFtlId={MOCK_WARNING_MESSAGE_FTL_ID}
warningType={MOCK_WARNING_TYPE}
>
{MOCK_WARNING_MESSAGE}
</WarningMessage>
<ResetPasswordWarning />
</AppLayout>
);

Просмотреть файл

@ -0,0 +1,110 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
import { screen, waitFor } from '@testing-library/react';
import ResetPasswordWarning from '.';
import userEvent from '@testing-library/user-event';
describe('ResetPasswordWarning component', () => {
it('renders as expected', async () => {
renderWithLocalizationProvider(<ResetPasswordWarning />);
expect(
screen.getByRole('img', {
name: 'Warning',
})
).toBeVisible();
expect(
screen.getByText('Your browser data may not be recovered')
).toBeVisible();
expect(screen.getByRole('img', { name: 'Collapse warning' })).toBeVisible();
expect(
screen.getByText('Have a device where you previously signed in?')
).toBeVisible();
expect(
screen.getByText(
'Your browser data may be locally saved on that device. Sign in there with your new password to restore and sync.'
)
).toBeVisible();
expect(
screen.getByText('Have a new device but dont have your old one?')
).toBeVisible();
expect(
screen.getByText(
'Were sorry, but your encrypted browser data on Firefox servers cant be recovered. However, you can still access your local data on any device where you have previously signed in.'
)
).toBeVisible();
expect(
screen.getByRole('link', {
name: 'Learn more about restoring account data',
})
).toBeVisible();
expect(screen.getByRole('link')).toHaveAttribute(
'href',
'https://support.mozilla.org/kb/how-reset-your-password-without-account-recovery-keys-access-data'
);
});
it('renders as expected with mobile width', async () => {
global.innerWidth = 375; // Set mobile width
global.dispatchEvent(new Event('resize'));
renderWithLocalizationProvider(<ResetPasswordWarning />);
expect(
screen.getByRole('img', {
name: 'Warning',
})
).toBeVisible();
expect(
screen.getByText('Your browser data may not be recovered')
).toBeVisible();
expect(screen.getByRole('img', { name: 'Expand warning' })).toBeVisible();
expect(
screen.queryByText('Have a device where you previously signed in?')
).not.toBeVisible();
});
it('handles click/toggle as expected', async () => {
const user = userEvent.setup();
global.innerWidth = 375; // Set mobile width
global.dispatchEvent(new Event('resize'));
renderWithLocalizationProvider(<ResetPasswordWarning />);
user.click(screen.getByRole('img', { name: 'Expand warning' }));
await waitFor(() => {
expect(screen.getByRole('group')).toHaveAttribute('open');
expect(
screen.getByRole('img', { name: 'Collapse warning' })
).toBeVisible();
expect(
screen.queryByRole('img', { name: 'Expand warning' })
).not.toBeInTheDocument();
});
user.click(screen.getByRole('img', { name: 'Collapse warning' }));
await waitFor(() => {
expect(screen.getByRole('group')).not.toHaveAttribute('open');
expect(screen.getByRole('img', { name: 'Expand warning' })).toBeVisible();
expect(
screen.queryByRole('img', { name: 'Collapse warning' })
).not.toBeInTheDocument();
});
});
});

Просмотреть файл

@ -0,0 +1,103 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React, { useState } from 'react';
import { ReactComponent as WarnIcon } from './icon-warn.svg';
import { ReactComponent as IconNonSyncDevice } from './icon-non-sync-device.svg';
import { ReactComponent as IconSyncDevice } from './icon-sync-device.svg';
import { ReactComponent as Chevron } from './chevron.svg';
import { FtlMsg } from 'fxa-react/lib/utils';
import { useFtlMsgResolver } from '../../models';
const ResetPasswordWarning = () => {
const ftlMsgResolver = useFtlMsgResolver();
// component is expanded by default on desktop
// and collapsed by default on mobile
const defaultOpenState = window.innerWidth > 480;
const [expanded, setExpanded] = useState(defaultOpenState);
return (
<details
className="p-4 bg-orange-50 rounded-lg text-sm text-start border border-transparent"
data-testid="warning-message-container"
open={defaultOpenState}
onToggle={(e) =>
setExpanded((e.currentTarget as HTMLDetailsElement).open)
}
>
{/* Arbitrary varaite [&::-webkit-details-marker]:hidden removes the list arrow on webkit based browsers */}
<summary className="flex items-center cursor-pointer list-none [&::-webkit-details-marker]:hidden">
<WarnIcon
role="img"
className="flex-initial me-4"
aria-label={ftlMsgResolver.getMsg(
'reset-password-warning-icon',
'Warning'
)}
/>
<p className="flex-1 font-semibold">
<FtlMsg id="password-reset-data-may-not-be-recovered">
Your browser data may not be recovered
</FtlMsg>
</p>
<Chevron
role="img"
className={`ms-2 ${expanded ? '-rotate-180' : ''}`}
aria-label={expanded ? 'Collapse warning' : 'Expand warning'}
/>
</summary>
<div className="flex flex-col ps-8 pt-4 pb-2 gap-4">
<div className="flex items-start gap-2">
<IconSyncDevice
role="img"
className="flex-initial"
aria-hidden={true}
/>
<div className="flex flex-col flex-1 -mt-1 gap-1">
<FtlMsg id="password-reset-previously-signed-in-device">
<p className="font-semibold">
Have a device where you previously signed in?
</p>
</FtlMsg>
<p className="text-grey-500 text-xs">
<FtlMsg id="password-reset-data-may-be-saved-locally">
Your browser data may be locally saved on that device. Sign in
there with your new password to restore and sync.
</FtlMsg>
</p>
</div>
</div>
<div className="flex items-start gap-2">
<IconNonSyncDevice role="img" aria-hidden={true} />
<div className="flex flex-col flex-1 -mt-1 gap-1">
<FtlMsg id="password-reset-no-old-device">
<p className="font-semibold">
Have a new device but dont have your old one?
</p>
</FtlMsg>
<FtlMsg id="password-reset-encrypted-data-cannot-be-recovered">
<p className="text-grey-500 text-xs">
Were sorry, but your encrypted browser data on Firefox servers
cant be recovered. However, you can still access your local
data on any device where you have previously signed in.
</p>
</FtlMsg>
<FtlMsg id="password-reset-learn-about-restoring-account-data">
<a
href="https://support.mozilla.org/kb/how-reset-your-password-without-account-recovery-keys-access-data"
className="link-blue"
>
Learn more about restoring account data
</a>
</FtlMsg>
</div>
</div>
</div>
</details>
);
};
export default ResetPasswordWarning;

Просмотреть файл

@ -1,39 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { screen } from '@testing-library/react';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider'; // import { getFtlBundle, testAllL10n } from 'fxa-react/lib/test-utils';
// import { FluentBundle } from '@fluent/bundle';
import WarningMessage from '.';
import {
MOCK_WARNING_MESSAGE_FTL_ID,
MOCK_WARNING_MESSAGE,
MOCK_WARNING_TYPE,
} from './mocks';
describe('WarningMessage', () => {
// TODO: Enable l10n testing with id passed as prop
// let bundle: FluentBundle;
// beforeAll(async () => {
// bundle = await getFtlBundle('settings');
// });
it('renders as expected', () => {
renderWithLocalizationProvider(
<WarningMessage
warningMessageFtlId={MOCK_WARNING_MESSAGE_FTL_ID}
warningType={MOCK_WARNING_TYPE}
>
{MOCK_WARNING_MESSAGE}
</WarningMessage>
);
// testAllL10n(screen, bundle);
expect(screen.getByTestId('warning-message-container')).toHaveTextContent(
'Beware: If you eat too many cookies, you might feel very sick.'
);
});
});

Просмотреть файл

@ -1,36 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { FtlMsg } from 'fxa-react/lib/utils';
type WarningMessageProps = {
children: string;
warningMessageFtlId: string;
warningType: string;
};
const WarningMessage = ({
children,
warningMessageFtlId,
warningType,
}: WarningMessageProps) => {
return (
<div className="mt-5 mb-8 text-xs" data-testid="warning-message-container">
<FtlMsg
id={warningMessageFtlId}
elems={{
span: <span className="text-red-600 uppercase">{warningType}</span>,
}}
>
<p>
<span className="text-red-600 uppercase">{warningType}</span>{' '}
{children}
</p>
</FtlMsg>
</div>
);
};
export default WarningMessage;

Просмотреть файл

@ -1,8 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export const MOCK_WARNING_MESSAGE_FTL_ID = 'warning-message-id';
export const MOCK_WARNING_TYPE = 'Beware:';
export const MOCK_WARNING_MESSAGE =
'If you eat too many cookies, you might feel very sick.';

Просмотреть файл

@ -9,11 +9,5 @@ complete-reset-password-success-alert = Password set
# Displayed in an alert bar
complete-reset-password-error-alert = Sorry, there was a problem setting your password
password-reset-data-may-not-be-recovered = Resetting your password may delete your encrypted browser data.
password-reset-could-not-determine-account-recovery-key = Have an account recovery key?
password-reset-use-account-recovery-key = Reset your password with your recovery key.
password-reset-previously-signed-in-device = Have a device where you previously signed in?
password-reset-data-may-be-saved-locally = Your browser data may be locally saved on that device. Sign in there with your new password to restore and sync.
password-reset-no-old-device = Have a new device but dont have your old one?
password-reset-encrypted-data-cannot-be-recovered = Were sorry, but your encrypted browser data on Firefox servers cant be recovered.
password-reset-learn-about-restoring-account-data = Learn more about restoring account data.
password-reset-could-not-determine-account-recovery-key = Got your account recovery key?
password-reset-use-account-recovery-key = Reset your password and keep your data

Просмотреть файл

@ -48,15 +48,13 @@ describe('CompleteResetPassword page', () => {
// Warning message about data loss should should be displayed
expect(
screen.getByText(
'Resetting your password may delete your encrypted browser data.'
)
screen.getByText('Your browser data may not be recovered')
).toBeVisible();
const inputs = screen.getAllByRole('textbox');
expect(inputs).toHaveLength(2);
expect(screen.getByLabelText('New password')).toBeVisible();
expect(screen.getByLabelText('Re-enter password')).toBeVisible();
expect(screen.getByLabelText('Confirm password')).toBeVisible();
expect(
screen.getByRole('button', { name: 'Create new password' })
).toBeVisible();
@ -84,14 +82,12 @@ describe('CompleteResetPassword page', () => {
// Warning messages about data loss should not be displayed.
expect(
screen.queryByText(
'Resetting your password may delete your encrypted browser data.'
)
screen.queryByText('Your browser data may not be recovered')
).not.toBeInTheDocument();
// Warning message about using recovery ke should not be displayed
expect(
screen.queryByText('Reset your password with your recovery key.')
screen.queryByText('Reset your password and keep your data')
).not.toBeInTheDocument();
});
@ -117,20 +113,18 @@ describe('CompleteResetPassword page', () => {
// Warning messages about data loss should not be displayed.
expect(
screen.queryByText(
'Resetting your password may delete your encrypted browser data.'
)
screen.queryByText('Your browser data may not be recovered')
).not.toBeInTheDocument();
// Warning message about using recovery key should not be displayed
expect(
screen.queryByText('Reset your password with your recovery key.')
screen.queryByText('Reset your password and keep your data')
).not.toBeInTheDocument();
const inputs = screen.getAllByRole('textbox');
expect(inputs).toHaveLength(2);
expect(screen.getByLabelText('New password')).toBeVisible();
expect(screen.getByLabelText('Re-enter password')).toBeVisible();
expect(screen.getByLabelText('Confirm password')).toBeVisible();
expect(
screen.getByRole('button', { name: 'Create new password' })
).toBeVisible();
@ -151,7 +145,7 @@ describe('CompleteResetPassword page', () => {
});
});
describe('reset with unconfimred account recovery key', () => {
describe('reset with unconfirmed account recovery key', () => {
it('renders as expected', async () => {
renderWithLocalizationProvider(
<Subject
@ -171,14 +165,12 @@ describe('CompleteResetPassword page', () => {
// Warning messages about data loss should not be displayed.
expect(
screen.queryByText(
'Resetting your password may delete your encrypted browser data.'
)
screen.queryByText('Your browser data may not be recovered')
).toBeInTheDocument();
// Warning message about using recovery key should be displayed
expect(
screen.queryByText('Reset your password with your recovery key.')
screen.queryByText('Reset your password and keep your data')
).not.toBeInTheDocument();
});
});
@ -203,14 +195,12 @@ describe('CompleteResetPassword page', () => {
// Warning messages about data loss should not be displayed.
expect(
screen.queryByText(
'Resetting your password may delete your encrypted browser data.'
)
screen.queryByText('Your browser data may not be recovered')
).not.toBeInTheDocument();
// Warning message about using recovery key should be displayed
expect(
screen.getByText('Reset your password with your recovery key.')
screen.getByText('Reset your password and keep your data')
).toBeVisible();
});
});
@ -225,7 +215,7 @@ describe('CompleteResetPassword page', () => {
user.type(screen.getByLabelText('New password'), MOCK_PASSWORD)
);
await waitFor(() =>
user.type(screen.getByLabelText('Re-enter password'), MOCK_PASSWORD)
user.type(screen.getByLabelText('Confirm password'), MOCK_PASSWORD)
);
const button = screen.getByRole('button', { name: 'Create new password' });
expect(button).toBeEnabled();

Просмотреть файл

@ -11,9 +11,6 @@ import Banner, { BannerType } from '../../../components/Banner';
import FormPasswordWithInlineCriteria from '../../../components/FormPasswordWithInlineCriteria';
import LinkRememberPassword from '../../../components/LinkRememberPassword';
import { ReactComponent as BangIcon } from './icon-bang.svg';
import { ReactComponent as WarnIcon } from './icon-warn.svg';
import { ReactComponent as IconNonSyncDevice } from './icon-non-sync-device.svg';
import { ReactComponent as IconSyncDevice } from './icon-sync-device.svg';
import {
CompleteResetPasswordFormData,
@ -21,6 +18,7 @@ import {
} from './interfaces';
import { FtlMsg } from 'fxa-react/lib/utils';
import { Link, useLocation } from '@reach/router';
import ResetPasswordWarning from '../../../components/ResetPasswordWarning';
const CompleteResetPassword = ({
email,
@ -61,9 +59,11 @@ const CompleteResetPassword = ({
return (
<AppLayout>
<p className="text-start text-grey-400 text-sm">
<FtlMsg id="password-reset-flow-heading">Reset your password</FtlMsg>
</p>
<FtlMsg id="password-reset-flow-heading">
<p className="text-start text-grey-400 text-sm mb-6">
Reset your password
</p>
</FtlMsg>
{/*
In the event of serious error. A bright red banner will be displayed indicating
@ -78,16 +78,15 @@ const CompleteResetPassword = ({
*/}
{hasConfirmedRecoveryKey === false && recoveryKeyExists === undefined && (
<div
className={`bg-red-100 rounded-lg text-sm mt-6 text-left rtl:text-right p-3`}
className="flex bg-red-50 rounded-sm text-xs text-start p-3"
data-testid="warning-message-container"
>
<div className="flex">
<BangIcon role="img" className="flex-initial me-2 mt-1" />
<div className="flex-1 text-xs">
<FtlMsg id="password-reset-could-not-determine-account-recovery-key">
Have an account recovery key?
</FtlMsg>{' '}
<br />
<BangIcon role="img" className="flex-initial me-2 mt-1" />
<div className="flex-1">
<FtlMsg id="password-reset-could-not-determine-account-recovery-key">
<p>Got your account recovery key?</p>
</FtlMsg>
<FtlMsg id="password-reset-use-account-recovery-key">
<Link
to={`/account_recovery_confirm_key${location.search}`}
state={locationState}
@ -96,69 +95,16 @@ const CompleteResetPassword = ({
GleanMetrics.passwordReset.createNewRecoveryKeyMessageClick()
}
>
<FtlMsg id="password-reset-use-account-recovery-key">
Reset your password with your recovery key.
</FtlMsg>
</Link>{' '}
</div>
Reset your password and keep your data
</Link>
</FtlMsg>
</div>
</div>
)}
{hasConfirmedRecoveryKey === false &&
recoveryKeyExists !== undefined &&
hasSyncDevices && (
<div
className={`bg-orange-50 rounded-lg text-sm mt-6 text-left rtl:text-right p-4 border-transparent`}
data-testid="warning-message-container"
>
<div className="flex font-semibold">
<WarnIcon role="img" className="flex-initial me-2 mt-1" />
<h1 className="flex-1">
<FtlMsg id="password-reset-data-may-not-be-recovered">
Resetting your password may delete your encrypted browser
data.
</FtlMsg>
</h1>
</div>
<div className="ps-4">
<p className="font-semibold pt-4">
<IconSyncDevice role="img" className="inline-block mr-2" />
<FtlMsg id="password-reset-previously-signed-in-device">
Have a device where you previously signed in?
</FtlMsg>
</p>
<p className="ps-6 text-xs">
<FtlMsg id="password-reset-data-maybe-saved-locally">
Your browser data may be locally saved on that device. Sign in
there with your new password to restore and sync.
</FtlMsg>
</p>
<p className="font-semibold pt-4">
<IconNonSyncDevice role="img" className="inline-block mr-2" />
<FtlMsg id="password-reset-no-old-device">
Have a new device but dont have your old one?
</FtlMsg>
</p>
<p className="ps-6 text-xs">
<FtlMsg id="password-reset-encrypted-data-cannot-be-recovered">
Were sorry, but your encrypted browser data on Firefox
servers cant be recovered.
</FtlMsg>
</p>
<p className="ps-6 text-xs mt-4">
<a
href="https://support.mozilla.org/en-US/kb/how-reset-your-password-without-account-recovery-keys-access-data"
className="link-blue"
>
<FtlMsg id="password-reset-learn-about-restoring-account-data">
Learn more about restoring account data.
</FtlMsg>
</a>
</p>
</div>
</div>
)}
hasSyncDevices && <ResetPasswordWarning />}
{/*
Hidden email field is to allow Fx password manager
to correctly save the updated password. Without it,
@ -167,9 +113,11 @@ const CompleteResetPassword = ({
*/}
<input type="email" value={email} className="hidden" readOnly />
<h1 className="font-semibold text-xl text-start mt-6">
<FtlMsg id="complete-reset-pw-header-v2">Create a new password</FtlMsg>
</h1>
<FtlMsg id="complete-reset-pw-header-v2">
<h1 className="font-semibold text-xl text-start mt-6">
Create a new password
</h1>
</FtlMsg>
<section className="text-start mt-2">
<FormPasswordWithInlineCriteria
{...{