Skip to content

Commit

Permalink
feat!: InPlayer register and update user flows (#202)
Browse files Browse the repository at this point in the history
* feat: inplayer update account details
* chore: disable update email button if the inplayer integration is active
* chore: refactor cleeng register, add inplayer register
* chore: merge with develop and handle consents
* fix: using readOnly to hide the edit button for the email instead of disabling edit button
* feat: registration flow
* chore: update json metadata for consents
* fix: using fullName insted of first and last name for the users
* fix: handle firstname and surname metadata parameters
* fix: trim name and handle multiple whitespaces
* fix: update InPlayer SDK version
* fix: added missing ?
* fix: update flow for update email feature
* fix: extend type for update account
* fix: account uninfied types
* Update src/stores/AccountController.ts
Co-authored-by: Danny Budzinski <dbudzins@users.noreply.github.com>
* Update src/stores/AccountController.ts
Co-authored-by: Danny Budzinski <dbudzins@users.noreply.github.com>
* Update src/stores/AccountController.ts
Co-authored-by: Danny Budzinski <dbudzins@users.noreply.github.com>
* fix: trim first and last names
* chore: handling not supported email update
* chore: refactor and type fixing
* chore: refactor from conversation
* fix: delete duplicates in cleeng type
* chore: conversation updates
* chore: i18next update
* chore: reset and forgot password implmentation
* fix: executed i18next
* fix: improved error response format
* fix: small register bug fix, update yarn.lock
* fix: fix account store loading bug with consents
* fix: remove void option from form section and fix missing return
* chore: skip cancel subscription tests due to cleeng bug
* chore: remove unnecessary async await
Co-authored-by: Darko <darkoatanasovski@gmail.com>
Co-authored-by: Danny Budzinski <dbudzinski@jwplayer.com>
Co-authored-by: Danny Budzinski <dbudzins@users.noreply.github.com>
BREAKING CHANGE: introduce InPlayer services
  • Loading branch information
kiremitrov123 authored Dec 14, 2022
1 parent 084498b commit 489d8e8
Show file tree
Hide file tree
Showing 31 changed files with 1,114 additions and 244 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"deploy:github": "node ./scripts/deploy-github.js"
},
"dependencies": {
"@inplayer-org/inplayer.js": "^3.13.1",
"@inplayer-org/inplayer.js": "^3.13.3",
"classnames": "^2.3.1",
"date-fns": "^2.28.0",
"dompurify": "^2.3.8",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Account/Account.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('<Account>', () => {
publisherConsents: Array.of({ name: 'marketing', label: 'Receive Marketing Emails' } as Consent),
});

const { container } = renderWithRouter(<Account panelClassName={'panel-class'} panelHeaderClassName={'header-class'} />);
const { container } = renderWithRouter(<Account panelClassName={'panel-class'} panelHeaderClassName={'header-class'} canUpdateEmail={true} />);

// todo
expect(container).toMatchSnapshot();
Expand Down
17 changes: 12 additions & 5 deletions src/components/Account/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { updateConsents, updateUser } from '#src/stores/AccountController';
type Props = {
panelClassName?: string;
panelHeaderClassName?: string;
canUpdateEmail?: boolean;
};

interface FormErrors {
Expand All @@ -33,17 +34,18 @@ interface FormErrors {
form?: string;
}

const Account = ({ panelClassName, panelHeaderClassName }: Props): JSX.Element => {
const Account = ({ panelClassName, panelHeaderClassName, canUpdateEmail = true }: Props): JSX.Element => {
const { t } = useTranslation('user');
const navigate = useNavigate();
const location = useLocation();
const [viewPassword, toggleViewPassword] = useToggle();

const { customer, customerConsents, publisherConsents } = useAccountStore(
({ user, customerConsents, publisherConsents }) => ({
const { customer, customerConsents, publisherConsents, canChangePasswordWithOldPassword } = useAccountStore(
({ user, customerConsents, publisherConsents, canChangePasswordWithOldPassword }) => ({
customer: user,
customerConsents,
publisherConsents,
canChangePasswordWithOldPassword,
}),
shallow,
);
Expand Down Expand Up @@ -73,7 +75,6 @@ const Account = ({ panelClassName, panelHeaderClassName }: Props): JSX.Element =

function translateErrors(errors?: string[]): FormErrors {
const formErrors: FormErrors = {};

// Some errors are combined in a single CSV string instead of one string per error
errors
?.flatMap((e) => e.split(','))
Expand All @@ -100,6 +101,10 @@ const Account = ({ panelClassName, panelHeaderClassName }: Props): JSX.Element =
formErrors.lastName = t('account.errors.last_name_too_long');
break;
}
case 'Email update not supported': {
formErrors.form = t('account.errors.email_update_not_supported');
break;
}
default: {
formErrors.form = t('account.errors.unknown_error');
logDev('Unknown error', error);
Expand Down Expand Up @@ -134,7 +139,8 @@ const Account = ({ panelClassName, panelHeaderClassName }: Props): JSX.Element =
}

const editPasswordClickHandler = () => {
navigate(addQueryParam(location, 'u', 'reset-password'));
const modal = canChangePasswordWithOldPassword ? 'edit-password' : 'reset-password';
navigate(addQueryParam(location, 'u', modal));
};

return (
Expand All @@ -149,6 +155,7 @@ const Account = ({ panelClassName, panelHeaderClassName }: Props): JSX.Element =
}),
canSave: (values) => !!(values.email && values.confirmationPassword),
editButton: t('account.edit_account'),
readOnly: !canUpdateEmail,
content: (section) => (
<>
<TextField
Expand Down
9 changes: 8 additions & 1 deletion src/components/EditPasswordForm/EditPasswordForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import EditPasswordForm from './EditPasswordForm';
describe('<EditPasswordForm>', () => {
test('renders and matches snapshot', () => {
const { container } = render(
<EditPasswordForm submitting={false} onSubmit={vi.fn()} onChange={vi.fn()} onBlur={vi.fn()} value={{ password: '' }} errors={{}} />,
<EditPasswordForm
submitting={false}
onSubmit={vi.fn()}
onChange={vi.fn()}
onBlur={vi.fn()}
value={{ password: '', passwordConfirmation: '' }}
errors={{}}
/>,
);

expect(container).toMatchSnapshot();
Expand Down
73 changes: 49 additions & 24 deletions src/components/EditPasswordForm/EditPasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,87 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

import PasswordField from '../PasswordField/PasswordField';
import TextField from '../TextField/TextField';

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

import type { FormErrors } from '#types/form';
import type { EditPasswordFormData } from '#types/account';
import FormFeedback from '#components/FormFeedback/FormFeedback';
import TextField from '#components/TextField/TextField';
import Button from '#components/Button/Button';
import IconButton from '#components/IconButton/IconButton';
import Visibility from '#src/icons/Visibility';
import VisibilityOff from '#src/icons/VisibilityOff';
import useToggle from '#src/hooks/useToggle';
import PasswordStrength from '#components/PasswordStrength/PasswordStrength';
import LoadingOverlay from '#components/LoadingOverlay/LoadingOverlay';
import { testId } from '#src/utils/common';

type Props = {
onSubmit: React.FormEventHandler<HTMLFormElement>;
onChange: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
setValue?: (key: keyof EditPasswordFormData, value: string) => void;
onBlur: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>;
error?: string;
errors: FormErrors<EditPasswordFormData>;
value: EditPasswordFormData;
submitting: boolean;
showOldPasswordField?: boolean;
showResetTokenField?: boolean;
};

const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, value, errors, submitting }: Props) => {
const EditPasswordForm: React.FC<Props> = ({ onSubmit, onChange, onBlur, showOldPasswordField, showResetTokenField, value, errors, submitting }: Props) => {
const { t } = useTranslation('account');
const [viewPassword, toggleViewPassword] = useToggle();

return (
<form onSubmit={onSubmit} data-testid={testId('forgot-password-form')} noValidate className={styles.forgotPasswordForm}>
<h2 className={styles.title}>{t('reset.password_reset')}</h2>
{errors.form ? <FormFeedback variant="error">{errors.form}</FormFeedback> : null}
<TextField
className={styles.textField}

{showOldPasswordField ? (
<PasswordField
value={value.oldPassword}
onChange={onChange}
onBlur={onBlur}
label={t('reset.old_password')}
placeholder={t('reset.old_password')}
error={!!errors.oldPassword}
name="oldPassword"
showToggleView={false}
showHelperText={false}
required
/>
) : showResetTokenField ? (
<TextField
className={styles.textField}
value={value.resetPasswordToken}
onChange={onChange}
onBlur={onBlur}
label={t('reset.reset_password_token')}
placeholder={t('reset.reset_password_token')}
name="resetPasswordToken"
type="text"
required
/>
) : null}

<PasswordField
value={value.password}
onChange={onChange}
onBlur={onBlur}
label={t('reset.new_password')}
placeholder={t('reset.password')}
error={!!errors.password || !!errors.form}
helperText={
<React.Fragment>
<PasswordStrength password={value.password} />
{t('reset.password_helper_text')}
</React.Fragment>
}
error={!!errors.password}
name="password"
type={viewPassword ? 'text' : 'password'}
rightControl={
<IconButton aria-label={viewPassword ? t('reset.hide_password') : t('reset.view_password')} onClick={() => toggleViewPassword()}>
{viewPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
}
required
/>

<PasswordField
value={value.passwordConfirmation}
onChange={onChange}
onBlur={onBlur}
label={t('reset.repeat_new_password')}
placeholder={t('reset.repeat_new_password')}
error={!!errors.passwordConfirmation}
name="passwordConfirmation"
required
/>

<Button type="submit" className={styles.button} fullWidth color="primary" disabled={submitting} label={t('reset.confirm')} />
{submitting && <LoadingOverlay transparentBackground inline />}
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ exports[`<EditPasswordForm> > renders and matches snapshot 1`] = `
reset.password_reset
</h2>
<div
class="_textField_e16c1b _rightControl_e16c1b _textField_d533f8"
class="_textField_e16c1b _rightControl_e16c1b _textField_d1ddfc"
>
<label
class="_label_e16c1b"
Expand All @@ -30,7 +30,57 @@ exports[`<EditPasswordForm> > renders and matches snapshot 1`] = `
placeholder="reset.password"
required=""
type="password"
value=""
/>
<div
class="_control_e16c1b"
>
<div
aria-label="reset.view_password"
class="_iconButton_0fef65"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
class="_icon_1e9999"
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
class="_helperText_da58e0"
>
reset.password_helper_text
</div>
</div>
<div
class="_textField_e16c1b _rightControl_e16c1b _textField_d1ddfc"
>
<label
class="_label_e16c1b"
for="text-field_1235_passwordConfirmation"
>
reset.repeat_new_password
</label>
<div
class="_container_e16c1b"
>
<input
class="_input_e16c1b"
id="text-field_1235_passwordConfirmation"
name="passwordConfirmation"
placeholder="reset.repeat_new_password"
required=""
type="password"
/>
<div
class="_control_e16c1b"
Expand Down
9 changes: 6 additions & 3 deletions src/components/Form/FormSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ export interface FormSectionProps<TData extends GenericFormValues, TErrors> {
saveButton?: string;
cancelButton?: string;
canSave?: (values: TData) => boolean;
onSubmit?: (values: TData) => Promise<{ errors?: string[] }> | void;
onSubmit?: (values: TData) => Promise<{ errors?: string[] }>;
content: (args: FormSectionContentArgs<TData, TErrors>) => ReactNode;
children?: never;
readOnly?: boolean;
}

export function FormSection<TData extends GenericFormValues>({
Expand All @@ -39,6 +40,7 @@ export function FormSection<TData extends GenericFormValues>({
canSave,
onSubmit,
content,
readOnly = false,
}: FormSectionProps<TData, string[]>): ReactElement<FormSectionProps<TData, string[]>> | null {
const sectionId = useOpaqueId(label);
const {
Expand Down Expand Up @@ -91,15 +93,15 @@ export function FormSection<TData extends GenericFormValues>({
event && event.preventDefault();

if (onSubmit) {
let response: { errors?: string[] } | void;
let response: { errors?: string[] };

try {
setFormState((s) => {
return { ...s, isBusy: true };
});
response = await onSubmit(values);
} catch (error: unknown) {
response = { errors: Array.of(error as string) };
response = { errors: Array.of(error instanceof Error ? error.message : (error as string)) };
}

// Don't leave edit mode if there are errors
Expand Down Expand Up @@ -167,6 +169,7 @@ export function FormSection<TData extends GenericFormValues>({
{cancelButton && <Button label={cancelButton} type="reset" variant="text" onClick={onCancel} />}
</>
) : (
!readOnly &&
editButton &&
(typeof editButton === 'object' ? (
(editButton as ReactElement)
Expand Down
Loading

0 comments on commit 489d8e8

Please sign in to comment.