Skip to content

Commit

Permalink
Merge pull request #3025 from guardian/mm/passcode-signin-ux-changes
Browse files Browse the repository at this point in the history
Passcodes | Use new sign in ux for passcodes behind flag
  • Loading branch information
coldlink authored Jan 14, 2025
2 parents 041d6e5 + 4fbf374 commit f422359
Show file tree
Hide file tree
Showing 20 changed files with 292 additions and 157 deletions.
20 changes: 14 additions & 6 deletions cypress/integration/ete/sign_in_passcode.8.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('Sign In flow, with passcode', () => {
expect(code).to.match(/^\d{6}$/);

cy.get('input[name=code]').type(code!);
cy.contains('Submit one-time code').click();
cy.contains('Sign in').click();

cy.url().should('include', expectedReturnUrl);

Expand All @@ -79,14 +79,14 @@ describe('Sign In flow, with passcode', () => {
case 'passcode-incorrect':
cy.get('input[name=code]').type(`${+code! + 1}`);

cy.contains('Submit one-time code').click();
cy.contains('Sign in').click();

cy.url().should('include', '/signin/code');

cy.contains('Incorrect code');
cy.get('input[name=code]').clear().type(code!);

cy.contains('Submit one-time code').click();
cy.contains('Sign in').click();

cy.url().should('include', expectedReturnUrl);

Expand All @@ -97,7 +97,7 @@ describe('Sign In flow, with passcode', () => {
break;
default: {
cy.get('input[name=code]').type(code!);
cy.contains('Submit one-time code').click();
cy.contains('Sign in').click();

cy.url().should('include', expectedReturnUrl);

Expand Down Expand Up @@ -178,7 +178,15 @@ describe('Sign In flow, with passcode', () => {
?.then(({ emailAddress, finalPassword }) => {
cy.visit(`/signin?usePasscodeSignIn=true`);
cy.get('input[name=email]').type(emailAddress);
cy.contains('Use a password to sign in instead').click();
cy.get('[data-cy="main-form-submit-button"]').click();

// passcode page
cy.url().should('include', '/signin/code');
cy.contains('Enter your one-time code');
cy.contains('Sign in with password instead').click();

cy.url().should('include', '/signin/password');
cy.get('input[name=email]').should('have.value', emailAddress);
cy.get('input[name=password]').type(finalPassword);
cy.get('[data-cy="main-form-submit-button"]').click();
cy.url().should('include', 'https://m.code.dev-theguardian.com/');
Expand Down Expand Up @@ -361,7 +369,7 @@ describe('Sign In flow, with passcode', () => {
cy.contains('Don’t have an account?');

cy.get('input[name=code]').clear().type('123456');
cy.contains('Submit one-time code').click();
cy.contains('Sign in').click();

cy.url().should('include', '/signin/code');
cy.contains('Enter your one-time code');
Expand Down
12 changes: 9 additions & 3 deletions src/client/components/PasscodeInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,26 @@ export default {
} as Meta;

export const Default = () => {
return <PasscodeInput />;
return <PasscodeInput label="Verification code" />;
};
Default.storyName = 'default';

export const WithFieldError = () => (
<PasscodeInput fieldErrors={[{ field: 'code', message: 'Invalid code' }]} />
<PasscodeInput
label="Verification code"
fieldErrors={[{ field: 'code', message: 'Invalid code' }]}
/>
);
WithFieldError.storyName = 'with field error';

export const WithDefaultPasscode = () => <PasscodeInput passcode="424242" />;
export const WithDefaultPasscode = () => (
<PasscodeInput label="Verification code" passcode="424242" />
);
WithDefaultPasscode.storyName = 'with default passcode';

export const WithFieldErrorAndDefaultPasscode = () => (
<PasscodeInput
label="Verification code"
passcode="424242"
fieldErrors={[{ field: 'code', message: 'Invalid code' }]}
/>
Expand Down
5 changes: 3 additions & 2 deletions src/client/components/PasscodeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { css } from '@emotion/react';
import ThemedTextInput from '@/client/components/ThemedTextInput';
import { remSpace } from '@guardian/source/foundations';

interface PasscodeInputProps extends Omit<TextInputProps, 'label'> {
interface PasscodeInputProps extends TextInputProps {
passcode?: string;
fieldErrors?: FieldError[];
}
Expand All @@ -16,6 +16,7 @@ const passcodeInputStyles = css`
`;

export const PasscodeInput = ({
label,
passcode = '',
fieldErrors,
}: PasscodeInputProps) => {
Expand Down Expand Up @@ -57,7 +58,7 @@ export const PasscodeInput = ({
return (
<div>
<ThemedTextInput
label="Verification code"
label={label}
type="text"
pattern="\d{6}"
name="code"
Expand Down
122 changes: 122 additions & 0 deletions src/client/pages/PasscodeEmailSent.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,125 @@ export const WithErrorMessageSecurity = () => (
WithErrorMessageVerification.story = {
name: 'with error message - security',
};

export const DefaultsSignIn = () => (
<PasscodeEmailSent passcodeAction="#" expiredPage="#" textType="signin" />
);
DefaultsVerification.story = {
name: 'with defaults - signin',
};

export const ChangeEmailSignIn = () => (
<PasscodeEmailSent
passcodeAction="#"
expiredPage="#"
changeEmailPage="#"
textType="signin"
/>
);
ChangeEmailVerification.story = {
name: 'with changeEmailPage - signin',
};

export const WithEmailSignIn = () => (
<PasscodeEmailSent
passcodeAction="#"
expiredPage="#"
changeEmailPage="#"
email="[email protected]"
textType="signin"
/>
);
WithEmailVerification.story = {
name: 'with email - signin',
};

export const WithPasscodeSignIn = () => (
<PasscodeEmailSent
passcodeAction="#"
expiredPage="#"
changeEmailPage="#"
email="[email protected]"
passcode="123456"
textType="signin"
/>
);
WithPasscodeVerification.story = {
name: 'with passcode - signin',
};

export const WithPasscodeErrorSignIn = () => (
<PasscodeEmailSent
shortRequestId="123e4567"
passcodeAction="#"
expiredPage="#"
changeEmailPage="#"
email="[email protected]"
passcode="123456"
fieldErrors={[
{
field: 'code',
message: 'Invalid code',
},
]}
textType="signin"
/>
);
WithPasscodeErrorVerification.story = {
name: 'with passcode error - signin',
};

export const WithRecaptchaErrorSignIn = () => (
<PasscodeEmailSent
shortRequestId="123e4567"
passcodeAction="#"
expiredPage="#"
changeEmailPage="#"
email="[email protected]"
recaptchaSiteKey="invalid-key"
textType="signin"
/>
);
WithRecaptchaErrorVerification.story = {
name: 'with reCAPTCHA error - signin',
};

export const WithSuccessMessageSignIn = () => (
<PasscodeEmailSent
passcodeAction="#"
expiredPage="#"
showSuccess={true}
textType="signin"
/>
);
WithSuccessMessageVerification.story = {
name: 'with success message - signin',
};

export const WithErrorMessageSignIn = () => (
<PasscodeEmailSent
shortRequestId="123e4567"
passcodeAction="#"
expiredPage="#"
errorMessage="•⩊• UwU"
textType="signin"
/>
);
WithErrorMessageVerification.story = {
name: 'with error message - signin',
};

export const WithSignInWithPasswordOption = () => (
<PasscodeEmailSent
passcodeAction="#"
expiredPage="#"
changeEmailPage="#"
email="[email protected]"
noAccountInfo
showSignInWithPasswordOption
textType="signin"
/>
);
WithSignInWithPasswordOption.story = {
name: 'with sign in with password option - signin',
};
34 changes: 32 additions & 2 deletions src/client/pages/PasscodeEmailSent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { MinimalLayout } from '@/client/layouts/MinimalLayout';
import { PasscodeInput } from '@/client/components/PasscodeInput';
import { EmailSentInformationBox } from '@/client/components/EmailSentInformationBox';
import { EmailSentProps } from '@/client/pages/EmailSent';
import { buildUrl } from '@/shared/lib/routeUtils';
import ThemedLink from '@/client/components/ThemedLink';

type TextType = 'verification' | 'security' | 'generic';
type TextType = 'verification' | 'security' | 'generic' | 'signin';

type Props = {
passcodeAction: string;
Expand All @@ -19,6 +21,7 @@ type Props = {
timeUntilTokenExpiry?: number;
noAccountInfo?: boolean;
textType?: TextType;
showSignInWithPasswordOption?: boolean;
};

type PasscodeEmailSentProps = EmailSentProps & Props;
Expand All @@ -29,6 +32,7 @@ type Text = {
sentTextWithEmail: string;
sentTextWithoutEmail: string;
securityText: string;
passcodeInputLabel: string;
submitButtonText: string;
};

Expand All @@ -43,6 +47,7 @@ const getText = (textType: TextType): Text => {
'We’ve sent you a temporary verification code. Please check your inbox.',
securityText:
'For your security, the verification code will expire in 30 minutes.',
passcodeInputLabel: 'Verification code',
submitButtonText: 'Submit verification code',
};
case 'security':
Expand All @@ -54,8 +59,20 @@ const getText = (textType: TextType): Text => {
sentTextWithoutEmail:
'For security reasons we need you to change your password. We’ve sent you a 6-digit verification code. Please check your inbox.',
securityText: 'For your security, the code will expire in 30 minutes.',
passcodeInputLabel: 'Verification code',
submitButtonText: 'Submit verification code',
};
case 'signin':
return {
title: 'Enter your one-time code to sign in',
successOverride: 'Email with one time code sent',
sentTextWithEmail: 'We’ve sent a 6-digit code to',
sentTextWithoutEmail:
'We’ve sent you a 6-digit code. Please check your inbox.',
securityText: 'For your security, the code will expire in 30 minutes.',
passcodeInputLabel: 'One-time code',
submitButtonText: 'Sign in',
};
case 'generic':
default:
return {
Expand All @@ -65,6 +82,7 @@ const getText = (textType: TextType): Text => {
sentTextWithoutEmail:
'We’ve sent you a 6-digit code. Please check your inbox.',
securityText: 'For your security, the code will expire in 30 minutes.',
passcodeInputLabel: 'One-time code',
submitButtonText: 'Submit one-time code',
};
}
Expand All @@ -87,6 +105,7 @@ export const PasscodeEmailSent = ({
shortRequestId,
noAccountInfo,
textType = 'generic',
showSignInWithPasswordOption,
}: PasscodeEmailSentProps) => {
const [recaptchaErrorMessage, setRecaptchaErrorMessage] = useState('');
const [recaptchaErrorContext, setRecaptchaErrorContext] =
Expand Down Expand Up @@ -153,8 +172,19 @@ export const PasscodeEmailSent = ({
disableOnSubmit
shortRequestId={shortRequestId}
>
<PasscodeInput passcode={passcode} fieldErrors={fieldErrors} />
<PasscodeInput
passcode={passcode}
fieldErrors={fieldErrors}
label={text.passcodeInputLabel}
/>
</MainForm>
{showSignInWithPasswordOption && (
<MainBodyText>
<ThemedLink href={`${buildUrl('/signin/password')}${queryString}`}>
Sign in with password instead
</ThemedLink>
</MainBodyText>
)}
<EmailSentInformationBox
setRecaptchaErrorContext={setRecaptchaErrorContext}
setRecaptchaErrorMessage={setRecaptchaErrorMessage}
Expand Down
Loading

0 comments on commit f422359

Please sign in to comment.