Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
feat: adds back and resend email button to success screen
Browse files Browse the repository at this point in the history
* feat: adds back button to success screen

* feat: adds resend email button without functionality

* style: adds space between the buttons

* refactor: adds an empty handleResendEmail function

* refactor: adds button ids to selectors

* refactor: adds a handleBackButtonClick function

* feat: adds logic to back and resend email button for sign in

* feat: adds functionality to back button to sign up page

* fix: resets email validation if back click is used to sign in

* feat: disables resend email button if already pressed

* docs: adds comments to explain successView and resendEmail variables

* test: adds test for back button in auth

* fix: changes ids for successcontent

* fix: makes the ids unique

* Revert "fix: makes the ids unique"

This reverts commit 2090234.

* Revert "fix: changes ids for successcontent"

This reverts commit 5483884.

* test: adds intercept to sign in success screen

* feat: adds resend email functionality to resend button from sign up

* test: adds test for resend email button

* test: adds second graasp account to the tests

* feat: adds translations of back and resend email buttons

* refactor: puts resend email handling in successview

* style: changes back button into text style

* refactor: changes to awaits signup/signin to set successView

* refactor: splits successView tests into two describe blocks

* docs: corrects typos in comments

* test: adds test to check if resend email button gets disabled

* test: adds test to check if signup/signin title is visible after back button is used

* test: adds test for email field being filled/not filled after back button is used

* style: aligns buttons on same line

* refactor: adds buttons in stack component

* refactor: adds type props instead of proptypes

* fix: removes unused import statement
  • Loading branch information
MalinSvenberg authored Mar 16, 2023
1 parent 135a4c2 commit 407491a
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 20 deletions.
150 changes: 150 additions & 0 deletions cypress/e2e/SuccessContent.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { StatusCodes } from 'http-status-codes';

import { API_ROUTES } from '@graasp/query-client';

import { SIGN_IN_PATH, SIGN_UP_PATH } from '../../src/config/paths';
import {
BACK_BUTTON_ID,
EMAIL_SIGN_IN_FIELD_ID,
EMAIL_SIGN_UP_FIELD_ID,
RESEND_EMAIL_BUTTON_ID,
SIGN_IN_HEADER_ID,
SIGN_UP_HEADER_ID,
SUCCESS_CONTENT_ID,
} from '../../src/config/selectors';
import { MEMBERS } from '../fixtures/members';

describe('Sign In', () => {
it('Back Button', () => {
const { GRAASP, GRAASP_OTHER } = MEMBERS;
cy.visit(SIGN_IN_PATH);

cy.intercept(API_ROUTES.SIGN_IN_ROUTE, ({ reply }) => {
return reply({
statusCode: StatusCodes.NO_CONTENT,
});
});

cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');

// Signing in with a valid email
cy.signInAndCheck(GRAASP);

cy.get(`#${SUCCESS_CONTENT_ID}`).should('be.visible');

cy.get(`#${BACK_BUTTON_ID}`).click();

cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');
cy.url().should('include', SIGN_IN_PATH);
cy.get(`#${SIGN_IN_HEADER_ID}`).should('be.visible');
// checks so email is cleared
cy.get(`#${EMAIL_SIGN_IN_FIELD_ID}`).should('be.empty');

// check if it's possible to sign in and use back button again
cy.signInAndCheck(GRAASP_OTHER);

cy.get(`#${SUCCESS_CONTENT_ID}`).should('be.visible');

cy.get(`#${BACK_BUTTON_ID}`).click();

cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');
cy.url().should('include', SIGN_IN_PATH);
cy.get(`#${SIGN_IN_HEADER_ID}`).should('be.visible');
// checks so email is cleared
cy.get(`#${EMAIL_SIGN_IN_FIELD_ID}`).should('be.empty');
});

it('Resend email', () => {
const { GRAASP, GRAASP_OTHER } = MEMBERS;
cy.visit(SIGN_IN_PATH);

cy.intercept(API_ROUTES.SIGN_IN_ROUTE, ({ reply }) => {
return reply({
statusCode: StatusCodes.NO_CONTENT,
});
});

// Signing in with a valid email
cy.signInAndCheck(GRAASP_OTHER);
cy.get(`#${BACK_BUTTON_ID}`).click();

cy.signInAndCheck(GRAASP);

// checks so request body contains correct email
cy.intercept(API_ROUTES.SIGN_IN_ROUTE, ({ body }) => {
expect(body.email).to.eq(GRAASP.email);
});

// checks resend email button is disabled after one click
cy.get(`#${RESEND_EMAIL_BUTTON_ID}`).click().should('be.disabled');
});
});

describe('Sign Up', () => {
it('Back Button', () => {
const { GRAASP, GRAASP_OTHER } = MEMBERS;
cy.visit(SIGN_UP_PATH);

cy.intercept(API_ROUTES.SIGN_UP_ROUTE, ({ reply }) => {
return reply({
statusCode: StatusCodes.NO_CONTENT,
});
});

cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');

// Signing up with a valid email
cy.signUpAndCheck(GRAASP);

cy.get(`#${SUCCESS_CONTENT_ID}`).should('be.visible');

cy.get(`#${BACK_BUTTON_ID}`).click();

cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');
cy.url().should('include', SIGN_UP_PATH);
cy.get(`#${SIGN_UP_HEADER_ID}`).should('be.visible');
// checks so email is still filled
cy.get(`#${EMAIL_SIGN_UP_FIELD_ID}`).should('have.value', GRAASP.email);

// check if it's possible to sign up and use back button again
cy.signUpAndCheck(GRAASP_OTHER);

cy.get(`#${SUCCESS_CONTENT_ID}`).should('be.visible');

cy.get(`#${BACK_BUTTON_ID}`).click();

cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');
cy.url().should('include', SIGN_UP_PATH);
cy.get(`#${SIGN_UP_HEADER_ID}`).should('be.visible');
// checks so email is still filled
cy.get(`#${EMAIL_SIGN_UP_FIELD_ID}`).should(
'have.value',
GRAASP_OTHER.email,
);
});

it('Resend email', () => {
const { GRAASP, GRAASP_OTHER } = MEMBERS;
cy.visit(SIGN_UP_PATH);

cy.intercept(API_ROUTES.SIGN_UP_ROUTE, ({ reply }) => {
return reply({
statusCode: StatusCodes.NO_CONTENT,
});
});

// Signing up with a valid email
cy.signUpAndCheck(GRAASP_OTHER);
cy.get(`#${BACK_BUTTON_ID}`).click();

cy.signUpAndCheck(GRAASP);

// checks so request body contains correct email
cy.intercept(API_ROUTES.SIGN_IN_ROUTE, ({ body }) => {
expect(body.email).to.eq(GRAASP.email);
});

// checks resend email button is disabled after one click
cy.get(`#${RESEND_EMAIL_BUTTON_ID}`).click().should('be.disabled');
});
});
13 changes: 13 additions & 0 deletions cypress/fixtures/members.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ export const MEMBERS: {
updatedAt: new Date().toISOString(),
extra: {},
},
GRAASP_OTHER: {
id: 'graasp_other-id',
name: 'graasp_other',
email: '[email protected]',
password: 'test',
nameValid: true,
emailValid: true,
passwordValid: true,
type: MemberType.Individual,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
extra: {},
},
WRONG_NAME: {
id: 'id1',
name: 'w',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@emotion/styled": "11.10.5",
"@graasp/query-client": "0.2.0",
"@graasp/sdk": "0.4.1",
"@graasp/translations": "1.4.0",
"@graasp/translations": "1.8.0",
"@graasp/ui": "0.11.1",
"@mui/icons-material": "5.11.0",
"@mui/lab": "5.0.0-alpha.119",
Expand Down
24 changes: 19 additions & 5 deletions src/components/SignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
PASSWORD_SIGN_IN_FIELD_ID,
PASSWORD_SIGN_IN_METHOD_BUTTON_ID,
SIGN_IN_BUTTON_ID,
SIGN_IN_HEADER_ID,
} from '../config/selectors';
import { SIGN_IN_METHODS } from '../types/signInMethod';
import { emailValidator, passwordValidator } from '../utils/validation';
Expand All @@ -45,10 +46,11 @@ const SignIn: FC = () => {
const [password, setPassword] = useState('');
const [passwordError, setPasswordError] = useState<string | null>(null);
const [signInMethod, setSignInMethod] = useState(SIGN_IN_METHODS.EMAIL);
const [successView, setSuccessView] = useState(false);
// enable validation after first click
const [shouldValidate, setShouldValidate] = useState(false);

const { mutate: signIn, isSuccess: signInSuccess } = useMutation<
const { mutateAsync: signIn, isSuccess: signInSuccess } = useMutation<
unknown,
unknown,
{ email: string }
Expand All @@ -68,7 +70,8 @@ const SignIn: FC = () => {
if (checkingEmail) {
setShouldValidate(true);
} else {
signIn({ email: lowercaseEmail });
await signIn({ email: lowercaseEmail });
setSuccessView(true);
}
};

Expand All @@ -89,6 +92,7 @@ const SignIn: FC = () => {
if (data.resource) {
window.location.href = data.resource;
}
setSuccessView(true);
}
};

Expand Down Expand Up @@ -133,6 +137,12 @@ const SignIn: FC = () => {
}
};

const handleBackButtonClick = () => {
setSuccessView(false);
setEmail('');
setShouldValidate(false);
};

const renderSignInForm = () => (
<>
<FormControl>
Expand Down Expand Up @@ -182,11 +192,15 @@ const SignIn: FC = () => {
<FullscreenContainer>
{
// eslint-disable-next-line no-constant-condition
signInSuccess || signInWithPasswordSuccess ? (
<SuccessContent title={t(AUTH.SIGN_IN_SUCCESS_TITLE)} email={email} />
(signInSuccess || signInWithPasswordSuccess) && successView ? (
<SuccessContent
title={t(AUTH.SIGN_IN_SUCCESS_TITLE)}
email={email}
handleBackButtonClick={handleBackButtonClick}
/>
) : (
<>
<Typography variant="h2" component="h2">
<Typography variant="h2" component="h2" id={SIGN_IN_HEADER_ID}>
{t(SIGN_IN_HEADER)}
</Typography>
{renderSignInForm()}
Expand Down
21 changes: 16 additions & 5 deletions src/components/SignUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
EMAIL_SIGN_UP_FIELD_ID,
NAME_SIGN_UP_FIELD_ID,
SIGN_UP_BUTTON_ID,
SIGN_UP_HEADER_ID,
} from '../config/selectors';
import { emailValidator, nameValidator } from '../utils/validation';
import EmailInput from './EmailInput';
Expand All @@ -32,10 +33,11 @@ const SignUp = () => {
const [email, setEmail] = useState<string>('');
const [name, setName] = useState<string>('');
const [nameError, setNameError] = useState<string | null>(null);
const [successView, setSuccessView] = useState(false);
// enable validation after first click
const [shouldValidate, setShouldValidate] = useState(false);

const { mutate: signUp, isSuccess: signUpSuccess } = useMutation<
const { mutateAsync: signUp, isSuccess: signUpSuccess } = useMutation<
unknown,
unknown,
{ email: string; name: string }
Expand Down Expand Up @@ -77,10 +79,15 @@ const SignUp = () => {
setNameError(checkingUsername);
setShouldValidate(true);
} else {
signUp({ name, email: lowercaseEmail });
await signUp({ name, email: lowercaseEmail });
setSuccessView(true);
}
};

const handleBackButtonClick = () => {
setSuccessView(false);
};

const renderForm = () => (
<>
<FormControl>
Expand Down Expand Up @@ -114,11 +121,15 @@ const SignUp = () => {

return (
<FullscreenContainer>
{signUpSuccess ? (
<SuccessContent title={t(AUTH.SIGN_UP_SUCCESS_TITLE)} email={email} />
{signUpSuccess && successView ? (
<SuccessContent
title={t(AUTH.SIGN_UP_SUCCESS_TITLE)}
email={email}
handleBackButtonClick={handleBackButtonClick}
/>
) : (
<>
<Typography variant="h2" component="h2">
<Typography variant="h2" component="h2" id={SIGN_UP_HEADER_ID}>
{t(SIGN_UP_HEADER)}
</Typography>
{renderForm()}
Expand Down
62 changes: 54 additions & 8 deletions src/components/SuccessContent.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,47 @@
import propTypes from 'prop-types';
import { useState } from 'react';
import { Trans } from 'react-i18next';

import { MUTATION_KEYS } from '@graasp/query-client';
import { AUTH, namespaces } from '@graasp/translations';
import { Button } from '@graasp/ui';

import MailOutlineIcon from '@mui/icons-material/MailOutline';
import { Container, Typography } from '@mui/material';
import { Container, Stack, Typography } from '@mui/material';

import { useAuthTranslation } from '../config/i18n';
import { useMutation } from '../config/queryClient';
import { SUCCESS_CONTENT_ID } from '../config/selectors';
import { BACK_BUTTON_ID, RESEND_EMAIL_BUTTON_ID } from '../config/selectors';

const SuccessContent = ({ title, email }) => {
type Props = {
title: string;
email: string;
handleBackButtonClick?: () => void;
};

const SuccessContent = ({
title,
email,
handleBackButtonClick = null,
}: Props) => {
const { t } = useAuthTranslation();
const [isEmailSent, setIsEmailSent] = useState(false);

// used for resend email
const { mutate: signIn } = useMutation<unknown, unknown, { email: string }>(
MUTATION_KEYS.SIGN_IN,
);

// used for resend email
const handleResendEmail = async () => {
const lowercaseEmail = email.toLowerCase();
signIn({ email: lowercaseEmail });
};

const onClickResendEmail = () => {
setIsEmailSent(true);
handleResendEmail();
};

return (
<Container id={SUCCESS_CONTENT_ID}>
Expand All @@ -35,13 +66,28 @@ const SuccessContent = ({ title, email }) => {
<Typography variant="body1">
{t(AUTH.SIGN_IN_SUCCESS_EMAIL_PROBLEM)}
</Typography>
<br />
<Stack direction="row" justifyContent="center" spacing={1}>
<Button
variant="text"
color="primary"
id={BACK_BUTTON_ID}
onClick={handleBackButtonClick}
>
{t(AUTH.BACK_BUTTON)}
</Button>
<Button
variant="outlined"
color="primary"
onClick={onClickResendEmail}
id={RESEND_EMAIL_BUTTON_ID}
disabled={isEmailSent}
>
{t(AUTH.RESEND_EMAIL_BUTTON)}
</Button>
</Stack>
</Container>
);
};

SuccessContent.propTypes = {
title: propTypes.string.isRequired,
email: propTypes.string.isRequired,
};

export default SuccessContent;
Loading

0 comments on commit 407491a

Please sign in to comment.