Skip to content

Commit

Permalink
feat(user): add registrationform component
Browse files Browse the repository at this point in the history
  • Loading branch information
RCVZ committed Jul 23, 2021
1 parent dc80d85 commit a412129
Show file tree
Hide file tree
Showing 4 changed files with 328 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/components/RegistrationForm/RegistrationForm.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
@use '../../styles/variables';
@use '../../styles/theme';

.title {
margin-bottom: 24px;
font-family: theme.$body-alt-font-family;
font-weight: 700;
font-size: 24px;
}

.error {
margin-bottom: 24px;
padding: 16px;
color: variables.$white;
font-family: theme.$body-font-family;
font-size: 18px;
background-color: theme.$form-error-bg-color;
border-radius: 4px;
}

.continue {
margin: variables.$base-spacing 0;
}

.bottom {
padding: variables.$base-spacing 0;
text-align: center;
}

.alreadyAccount {
font-family: theme.$body-alt-font-family;
font-size: 16px;
}

.login {
margin-left: 6px;
font-family: theme.$body-alt-font-family;
font-weight: 700;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
20 changes: 20 additions & 0 deletions src/components/RegistrationForm/RegistrationForm.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { render } from '@testing-library/react';

import RegistrationForm from './RegistrationForm';

describe('<RegistrationForm>', () => {
test('renders and matches snapshot', () => {
const { container } = render(
<RegistrationForm
onSubmit={jest.fn()}
onChange={jest.fn()}
values={{ email: '', password: '', termsConditions: false, emailUpdates: false }}
errors={{}}
submitting={false}
/>,
);

expect(container).toMatchSnapshot();
});
});
118 changes: 118 additions & 0 deletions src/components/RegistrationForm/RegistrationForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React from 'react';
import { useHistory } from 'react-router';
import { useTranslation, Trans } from 'react-i18next';
import type { RegistrationFormData } from 'types/account';

import useToggle from '../../hooks/useToggle';
import { addQueryParam } from '../../utils/history';
import TextField from '../TextField/TextField';
import Button from '../Button/Button';
import IconButton from '../IconButton/IconButton';
import Visibility from '../../icons/Visibility';
import VisibilityOff from '../../icons/VisibilityOff';
import type { FormErrors } from '../../hooks/useForm';
import PasswordStrength from '../PasswordStrength/PasswordStrength';
import Checkbox from '../Checkbox/Checkbox';
import { termsConditionsUrl } from '../../config';

import styles from './RegistrationForm.module.scss';

type Props = {
onSubmit: React.FormEventHandler<HTMLFormElement>;
onChange: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
error?: string;
errors: FormErrors<RegistrationFormData>;
values: RegistrationFormData;
submitting: boolean;
};

const RegistrationForm: React.FC<Props> = ({ onSubmit, onChange, values, errors, submitting }: Props) => {
const [viewPassword, toggleViewPassword] = useToggle();

const { t } = useTranslation('account');
const history = useHistory();

const passwordStrength = (password: string) => {
let strength = 0;

if (password.match(/[a-z]+/)) {
strength += 1;
}
if (password.match(/[A-Z]+/)) {
strength += 1;
}
if (password.match(/[0-9|!@#$%^&*()_+-=]+/)) {
strength += 1;
}
if (password.length >= 6) {
strength += 1;
}

return strength;
};

const loginButtonClickHandler = () => {
history.push(addQueryParam(history, 'u', 'login'));
};

return (
<form onSubmit={onSubmit} data-testid="registration-form">
<h2 className={styles.title}>{t('registration.sign_up')}</h2>
{errors.form ? <div className={styles.error}>{errors.form}</div> : null}
<TextField
value={values.email}
onChange={onChange}
label={t('registration.email')}
placeholder={t('registration.email')}
error={!!errors.email || !!errors.form}
helperText={errors.email}
name="email"
type="email"
/>
<TextField
value={values.password}
onChange={onChange}
label={t('registration.password')}
placeholder={t('registration.password')}
error={!!errors.password || !!errors.form}
helperText={errors.password}
name="password"
type={viewPassword ? 'text' : 'password'}
rightControl={
<IconButton
aria-label={viewPassword ? t('registration.hide_password') : t('registration.view_password')}
onClick={() => toggleViewPassword()}
>
{viewPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
}
/>
<PasswordStrength strength={passwordStrength(values.password)} />
<Checkbox
onChange={onChange}
name="terms-conditions"
value={values.termsConditions}
label={<Trans t={t} i18nKey="registration.terms_conditions" values={{ link: termsConditionsUrl }} components={{ a: <a /> }} />}
/>
<Checkbox onChange={onChange} value={values.emailUpdates} name="email-updates" label={t('registration.email_updates')} />
<Button
className={styles.continue}
type="submit"
label={t('registration.continue')}
variant="contained"
color="primary"
size="large"
disabled={submitting}
fullWidth
/>
<div className={styles.bottom}>
<span className={styles.alreadyAccount}>{t('registration.already_account')}</span>
<button className={styles.login} onClick={loginButtonClickHandler}>
{t('login.sign_in')}
</button>
</div>
</form>
);
};

export default RegistrationForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<RegistrationForm> renders and matches snapshot 1`] = `
<div>
<form
data-testid="registration-form"
>
<h2
class="title"
>
registration.sign_up
</h2>
<div
class="textField"
>
<label
class="label"
for="text-field_1235_email"
>
registration.email
</label>
<div
class="container"
>
<input
class="input"
id="text-field_1235_email"
name="email"
placeholder="registration.email"
type="email"
value=""
/>
</div>
</div>
<div
class="textField rightControl"
>
<label
class="label"
for="text-field_1235_password"
>
registration.password
</label>
<div
class="container"
>
<input
class="input"
id="text-field_1235_password"
name="password"
placeholder="registration.password"
type="password"
value=""
/>
<div
class="control"
>
<div
aria-label="registration.view_password"
class="iconButton"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
class="icon"
focusable="false"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<path
d="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z"
/>
</g>
</svg>
</div>
</div>
</div>
</div>
<div
class="passwordStrength"
>
<div
class="passwordStrengthBar"
>
<div
class="passwordStrengthFill"
data-strength="0"
/>
</div>
<span>
Use a minimum of 6 characters (case sensitive) with at least one number or special character and one capital character
</span>
</div>
<div
class="checkbox"
>
<input
id="check-box_1235_terms-conditions"
name="terms-conditions"
type="checkbox"
/>
<label
for="check-box_1235_terms-conditions"
/>
</div>
<div
class="checkbox"
>
<input
id="check-box_1235_email-updates"
name="email-updates"
type="checkbox"
/>
<label
for="check-box_1235_email-updates"
>
registration.email_updates
</label>
</div>
<button
aria-disabled="false"
class="button continue primary fullWidth large"
type="submit"
>
<span>
registration.continue
</span>
</button>
<div
class="bottom"
>
<span
class="alreadyAccount"
>
registration.already_account
</span>
<button
class="login"
>
login.sign_in
</button>
</div>
</form>
</div>
`;

0 comments on commit a412129

Please sign in to comment.