Skip to content

Commit

Permalink
feat(passcodes): use new sign in ux for passcodes behind flag
Browse files Browse the repository at this point in the history
  • Loading branch information
coldlink committed Jan 13, 2025
1 parent 041d6e5 commit 64e74a2
Show file tree
Hide file tree
Showing 17 changed files with 250 additions and 147 deletions.
10 changes: 9 additions & 1 deletion cypress/integration/ete/sign_in_passcode.8.cy.ts
Original file line number Diff line number Diff line change
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
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',
};
43 changes: 41 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,28 @@ export const PasscodeEmailSent = ({
disableOnSubmit
shortRequestId={shortRequestId}
>
<PasscodeInput passcode={passcode} fieldErrors={fieldErrors} />
<PasscodeInput
passcode={passcode}
fieldErrors={fieldErrors}
label={text.passcodeInputLabel}
/>
</MainForm>
{showSignInWithPasswordOption && (
// <LinkButton
// cssOverrides={secondaryButtonStyles()}
// priority="tertiary"
// href={`${buildUrl('/signin/password')}${queryString}`}
// >
// Sign in with password instead
// </LinkButton>
<>
<MainBodyText>
<ThemedLink href={`${buildUrl('/signin/password')}${queryString}`}>
Sign in with password instead
</ThemedLink>
</MainBodyText>
</>
)}
<EmailSentInformationBox
setRecaptchaErrorContext={setRecaptchaErrorContext}
setRecaptchaErrorMessage={setRecaptchaErrorMessage}
Expand Down
58 changes: 15 additions & 43 deletions src/client/pages/SignIn.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,66 +126,38 @@ IsReauthenticate.story = {
name: 'showing /reauthenticate page',
};

export const WithPasscodeSelectedDefaultPassword = (args: SignInProps) => (
<SignIn
{...{
...args,
defaultView: 'password',
currentView: 'passcode',
usePasscodeSignIn: true,
}}
/>
export const SignInWithPasscode = (args: SignInProps) => (
<SignIn {...{ ...args, usePasscodeSignIn: true }} />
);
WithPasscodeSelectedDefaultPassword.story = {
name: 'with passcode checkbox checked',
SignInWithPasscode.story = {
name: 'sign in with passcode',
};

export const WithPasscodeSelectedDefaultPasswordError = (args: SignInProps) => (
export const SignInWithPasscodeError = (args: SignInProps) => (
<SignIn
{...{
...args,
defaultView: 'password',
currentView: 'passcode',
usePasscodeSignIn: true,
pageError: SignInErrors.PASSCODE_EXPIRED,
}}
/>
);
WithPasscodeSelectedDefaultPassword.story = {
name: 'with passcode checkbox checked',
SignInWithPasscodeError.story = {
name: 'sign in with passcode error',
};

export const WithPasscodeSelectedDefaultPasscode = (args: SignInProps) => (
<SignIn {...{ ...args, defaultView: 'passcode', usePasscodeSignIn: true }} />
);
WithPasscodeSelectedDefaultPasscode.story = {
name: 'with passcode checkbox checked',
};

export const WithPasscodeSelectedDefaultPasscodeError = (args: SignInProps) => (
<SignIn
{...{
...args,
defaultView: 'passcode',
usePasscodeSignIn: true,
pageError: SignInErrors.PASSCODE_EXPIRED,
}}
/>
export const NoSocialButtons = (args: SignInProps) => (
<SignIn {...{ ...args, hideSocialButtons: true }} />
);
WithPasscodeSelectedDefaultPasscodeError.story = {
name: 'with passcode checkbox checked',
NoSocialButtons.story = {
name: 'no social buttons',
};

export const WithPasswordSelectedDefaultPasscode = (args: SignInProps) => (
export const NoSocialButtonsEmail = (args: SignInProps) => (
<SignIn
{...{
...args,
defaultView: 'passcode',
currentView: 'password',
usePasscodeSignIn: true,
}}
{...{ ...args, hideSocialButtons: true, email: '[email protected]' }}
/>
);
WithPasswordSelectedDefaultPasscode.story = {
name: 'with password checkbox checked',
NoSocialButtonsEmail.story = {
name: 'no social buttons with email',
};
Loading

0 comments on commit 64e74a2

Please sign in to comment.