Skip to content

Commit

Permalink
feat(user): add consent validation
Browse files Browse the repository at this point in the history
  • Loading branch information
royschut committed Jul 23, 2021
1 parent bd72479 commit 497bd49
Show file tree
Hide file tree
Showing 20 changed files with 503 additions and 237 deletions.
68 changes: 67 additions & 1 deletion src/components/Account/Account.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,75 @@ button {
}

.checkbox {
display: flex;
position: relative;
display: block;
align-items: center;
margin-top: variables.$base-spacing / 2;
margin-bottom: 12px;
padding-left: 35px;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;

> input {
position: absolute;
width: 0;
height: 0;
margin-right: 10px;
opacity: 0;
:not(&:disabled) {
&:hover ~ .checkmark:hover {
background-color: theme.$forms-checkbox-bg-hover;
cursor: pointer;
}
&:hover ~ .checkLabel {
cursor: pointer;
}
&:checked ~ .checkmark:hover {
background-color: theme.$forms-primary-color-hover;
}
}
&:checked ~ .checkmark {
background-color: theme.$forms-primary-color;
}
&:checked ~ .checkmark::after {
display: block;
}

&:disabled ~ .checkmark,
&:disabled ~ .checkLabel {
cursor: default;
opacity: 0.6;
}
}
}

.checkmark {
position: absolute;
top: 0;
left: 0;
width: 15px;
height: 15px;
background-color: theme.$forms-checkbox-bg;
&::after {
content: '';
position: absolute;
top: 2px;
left: 4px;
display: none;

width: 6px;
height: 10px;
border: solid black;
border-width: 0 2px 2px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
}

.submitConsents {
margin-top: variables.$base-spacing;
}
7 changes: 5 additions & 2 deletions src/components/Account/Account.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ describe('<Account>', () => {
const { container } = render(
<Account
customer={customer}
onUpdateEmailSubmit={(values) => console.info(values)}
onUpdateInfoSubmit={(values) => console.info(values)}
isLoading={false}
consentsLoading={false}
onUpdateEmailSubmit={() => null}
onUpdateInfoSubmit={() => null}
onUpdateConsentsSubmit={() => null}
onDeleteAccountClick={() => null}
/>,
);
Expand Down
112 changes: 76 additions & 36 deletions src/components/Account/Account.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,87 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import type { Customer, UpdateCustomerPayload } from 'types/account';
import type { Consent, Customer, CustomerConsent, UpdateCustomerPayload } from 'types/account';
import type { CustomerFormValues, FormErrors, GenericFormValues } from 'types/form';

import { formatConsentsFromValues, formatConsentValues } from '../../utils/collection';
import Visibility from '../../icons/Visibility';
import VisibilityOff from '../../icons/VisibilityOff';
import type { FormErrors } from '../../hooks/useForm';
import useToggle from '../../hooks/useToggle';
import Button from '../../components/Button/Button';
import Spinner from '../../components/Spinner/Spinner';
import Form from '../Form/Form';
import IconButton from '../IconButton/IconButton';
import LoadingOverlay from '../LoadingOverlay/LoadingOverlay';
import TextField from '../TextField/TextField';
import MarkdownComponent from '../MarkdownComponent/MarkdownComponent';

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

type Props = {
customer: Customer;
errors: FormErrors<UpdateCustomerPayload>;
errors?: FormErrors<UpdateCustomerPayload>;
isLoading: boolean;
onUpdateEmailSubmit: (data: Record<string, string>) => void;
onUpdateInfoSubmit: (data: Record<string, string>) => void;
consentsLoading: boolean;
publisherConsents?: Consent[];
customerConsents?: CustomerConsent[];
onUpdateEmailSubmit: (data: CustomerFormValues) => void;
onUpdateInfoSubmit: (data: CustomerFormValues) => void;
onDeleteAccountClick: () => void;
onReset: () => void;
onUpdateConsentsSubmit: (consents: CustomerConsent[]) => void;
onReset?: () => void;
panelClassName?: string;
panelHeaderClassName?: string;
};

type Editing = 'none' | 'account' | 'password' | 'info';
type FormValues = Record<string, string>;
type Editing = 'none' | 'account' | 'password' | 'info' | 'consents';

const Account = ({
customer,
errors,
isLoading,
consentsLoading,
publisherConsents,
customerConsents,
panelClassName,
panelHeaderClassName,
onUpdateEmailSubmit,
onUpdateInfoSubmit,
onDeleteAccountClick,
onUpdateConsentsSubmit,
onReset,
}: Props): JSX.Element => {
const { t } = useTranslation('user');
const [editing, setEditing] = useState<Editing>('none');
const [viewPassword, toggleViewPassword] = useToggle();
const consentValues = useMemo(() => formatConsentValues(publisherConsents, customerConsents), [publisherConsents, customerConsents]);
const initialValues = useMemo(() => ({ ...customer, consents: consentValues }), [customer, consentValues]);

const handleSubmit = (values: FormValues) => {
const handleSubmit = (values: GenericFormValues) => {
switch (editing) {
case 'account':
return onUpdateEmailSubmit(values);
return onUpdateEmailSubmit(values as CustomerFormValues);
case 'info':
return onUpdateInfoSubmit(values);
return onUpdateInfoSubmit(values as CustomerFormValues);
case 'consents':
return onUpdateConsentsSubmit(formatConsentsFromValues(publisherConsents, values));
break;
default:
return;
}
};
const onCancelClick = (formResetHandler?: () => void): void => {
formResetHandler && formResetHandler();
setEditing('none');
onReset();
onReset && onReset();
};

useEffect(() => {
!isLoading && setEditing('none');
}, [isLoading]);

return (
<Form initialValues={customer} onSubmit={handleSubmit} editing={editing !== 'none'}>
{({ values, handleChange, handleSubmit, handleReset }) => (
<Form initialValues={initialValues} onSubmit={handleSubmit}>
{({ values, handleChange, handleReset, handleSubmit, hasChanged }) => (
<>
{isLoading && <LoadingOverlay transparentBackground />}
<div className={panelClassName}>
Expand All @@ -78,7 +93,7 @@ const Account = ({
<TextField
name="email"
label={t('account.email')}
value={values.email}
value={values.email as string}
onChange={handleChange}
error={!!errors?.email}
helperText={errors?.email}
Expand All @@ -91,7 +106,7 @@ const Account = ({
<TextField
name="confirmationPassword"
label={t('account.confirm_password')}
value={values.confirmationPassword}
value={values.confirmationPassword as string}
onChange={handleChange}
error={!!errors?.confirmationPassword}
helperText={errors?.confirmationPassword}
Expand All @@ -110,12 +125,17 @@ const Account = ({
<div className={styles.controls}>
{editing === 'account' ? (
<>
<Button label={t('account.save')} onClick={handleSubmit} disabled={isLoading || !values.email || !values.confirmationPassword} />
<Button variant="text" label={t('account.cancel')} onClick={() => onCancelClick(handleReset)} />
<Button label={t('account.delete_account')} onClick={onDeleteAccountClick} />
<Button
label={t('account.save')}
type="submit"
onClick={handleSubmit}
disabled={isLoading || !values.email || !values.confirmationPassword}
/>
<Button label={t('account.cancel')} type="reset" variant="text" onClick={() => onCancelClick(handleReset)} />
<Button label={t('account.delete_account')} type="button" onClick={onDeleteAccountClick} />
</>
) : (
<Button label={t('account.edit_account')} onClick={() => setEditing('account')} />
<Button label={t('account.edit_account')} type="button" onClick={() => setEditing('account')} />
)}
</div>
</div>
Expand All @@ -127,7 +147,7 @@ const Account = ({
<div>
<strong>{t('account.password')}</strong>
<p>****************</p>
<Button label={t('account.edit_password')} onClick={() => setEditing('password')} />
<Button label={t('account.edit_password')} type="button" onClick={() => setEditing('password')} />
</div>
</div>
<div className={panelClassName}>
Expand All @@ -139,7 +159,7 @@ const Account = ({
<TextField
name="firstName"
label={t('account.firstname')}
value={values.firstName}
value={values.firstName as string}
onChange={handleChange}
error={!!errors?.firstName}
helperText={errors?.firstName}
Expand All @@ -149,7 +169,7 @@ const Account = ({
<TextField
name="lastName"
label={t('account.lastname')}
value={values.lastName}
value={values.lastName as string}
onChange={handleChange}
error={!!errors?.lastName}
helperText={errors?.lastName}
Expand All @@ -159,11 +179,11 @@ const Account = ({
<div className={styles.controls}>
{editing === 'info' ? (
<>
<Button label={t('account.save')} onClick={handleSubmit} />
<Button variant="text" label={t('account.cancel')} onClick={() => setEditing('none')} />
<Button label={t('account.save')} type="submit" disabled={!hasChanged} onClick={handleSubmit} />
<Button type="reset" variant="text" label={t('account.cancel')} onClick={() => setEditing('none')} />
</>
) : (
<Button label={t('account.edit_information')} onClick={() => setEditing('info')} />
<Button type="button" label={t('account.edit_information')} onClick={() => setEditing('info')} />
)}
</div>
</div>
Expand All @@ -173,15 +193,35 @@ const Account = ({
<div className={panelHeaderClassName}>
<h3>{'Terms & tracking'}</h3>
</div>
<div>
<label className={styles.checkbox}>
<input type="checkbox" id="terms1" name="terms 1" value="Bike" />
<p>
I accept the <strong>Terms of Conditions</strong> of {'<platform name>'}
</p>
</label>
<Button label={t('account.update_consents')} />
</div>
{consentsLoading ? (
<Spinner size="small" />
) : publisherConsents ? (
<div>
{publisherConsents.map((consent, index) => (
<label className={styles.checkbox} key={index}>
<input
type="checkbox"
name={consent.name}
checked={(values.consents?.[consent.name] as boolean) || false}
value={values.consents?.[consent.name] || ''}
onChange={(event) => (handleChange ? handleChange(event, { nestInto: 'consents' }) : null)}
disabled={consent.required}
/>
<span className={styles.checkmark} />
<MarkdownComponent markdownString={consent.label} className={styles.checkLabel} />
</label>
))}
<Button
className={styles.submitConsents}
type="button"
label={t('account.update_consents')}
onClick={() => {
setEditing('consents');
handleSubmit && handleSubmit();
}}
/>
</div>
) : null}
</div>
</>
)}
Expand Down
Loading

0 comments on commit 497bd49

Please sign in to comment.