Skip to content

Commit

Permalink
feat(onboarding-ui): a11y forms improvements (#1187)
Browse files Browse the repository at this point in the history
Co-authored-by: dougfabris <[email protected]>
  • Loading branch information
juliajforesti and dougfabris authored Oct 5, 2023
1 parent 695d42a commit 0885913
Show file tree
Hide file tree
Showing 11 changed files with 667 additions and 142 deletions.
5 changes: 5 additions & 0 deletions .changeset/big-apes-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/onboarding-ui": minor
---

feat(onboarding-ui): a11y forms improvements
2 changes: 1 addition & 1 deletion .changeset/hip-bats-stare.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@rocket.chat/onboarding-ui": patch
"@rocket.chat/onboarding-ui": minor
---

feat(onboarding-ui): Required fields adjustments
2 changes: 1 addition & 1 deletion .changeset/popular-adults-hope.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@rocket.chat/onboarding-ui": patch
"@rocket.chat/onboarding-ui": minor
---

feat(onboarding-ui): Remove standalone and introduce offline server registration
4 changes: 2 additions & 2 deletions .changeset/stupid-mayflies-look.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@rocket.chat/onboarding-ui": patch
"@rocket.chat/onboarding-ui": minor
---

refactor(onboarding-ui): Rewrite `AwaitingConfirmationPage` page
feat(onboarding-ui): Modify AwaitingConfirmationPage page
1 change: 1 addition & 0 deletions packages/onboarding-ui/.i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"pasteHere": "Paste here...",
"completeRegistration": "Complete registration"
},
"requiredField": "This field is required",
"termsAndConditions": "I agree with <1>Terms and Conditions</1> and <3>Privacy Policy</3>"
},
"emailCodeFallback": "Didn’t receive email? <1>Resend</1> or <3>Change email</3>.",
Expand Down
6 changes: 5 additions & 1 deletion packages/onboarding-ui/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ const config: StorybookConfig = {
features: {
postcss: false,
},
addons: ['@storybook/addon-essentials', 'storybook-dark-mode'],
addons: [
'@storybook/addon-a11y',
'@storybook/addon-essentials',
'storybook-dark-mode',
],
stories: ['../src/**/*.stories.tsx', '../src/**/stories.tsx'],
};

Expand Down
1 change: 1 addition & 0 deletions packages/onboarding-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@rocket.chat/logo": "workspace:~",
"@rocket.chat/prettier-config": "workspace:~",
"@rocket.chat/styled": "workspace:~",
"@storybook/addon-a11y": "~7.4.6",
"@storybook/addon-essentials": "~6.5.16",
"@storybook/addons": "~6.5.16",
"@storybook/builder-webpack5": "~6.5.16",
Expand Down
152 changes: 110 additions & 42 deletions packages/onboarding-ui/src/forms/AdminInfoForm/AdminInfoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { Form } from '@rocket.chat/layout';
import type { ReactElement } from 'react';
import { useEffect } from 'react';
import { useRef, useEffect } from 'react';
import type { SubmitHandler, Validate } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

export type AdminInfoPayload = {
Expand Down Expand Up @@ -54,33 +54,49 @@ const AdminInfoForm = ({
}: AdminInfoFormProps): ReactElement => {
const { t } = useTranslation();

const formId = useUniqueId();
const fullnameField = useUniqueId();
const usernameField = useUniqueId(); // lgtm [js/insecure-randomness]
const emailField = useUniqueId();
const passwordField = useUniqueId(); // lgtm [js/insecure-randomness]

const adminInfoFormRef = useRef<HTMLElement>(null);

const {
register,
handleSubmit,
formState: { isValid, isSubmitting, errors },
setFocus,
formState: { isValidating, isSubmitting, errors },
control,
} = useForm<AdminInfoPayload>({
defaultValues: {
...initialValues,
password: '',
},
mode: 'onBlur',
});

useEffect(() => {
setFocus('fullname');
}, [setFocus]);
if (adminInfoFormRef.current) {
adminInfoFormRef.current.focus();
}
}, []);

return (
<Form onSubmit={handleSubmit(onSubmit)}>
<Form
ref={adminInfoFormRef}
tabIndex={-1}
aria-labelledby={`${formId}-title`}
aria-describedby={`${formId}-description`}
onSubmit={handleSubmit(onSubmit)}
>
<Form.Header>
<Form.Steps currentStep={currentStep} stepCount={stepCount} />
<Form.Title>{t('form.adminInfoForm.title')}</Form.Title>
<Form.Subtitle>{t('form.adminInfoForm.subtitle')}</Form.Subtitle>
<Form.Title id={`${formId}-title`}>
{t('form.adminInfoForm.title')}
</Form.Title>
<Form.Subtitle id={`${formId}-description`}>
{t('form.adminInfoForm.subtitle')}
</Form.Subtitle>
</Form.Header>
<Form.Container>
<FieldGroup>
Expand All @@ -89,75 +105,127 @@ const AdminInfoForm = ({
{t('form.adminInfoForm.fields.fullName.label')}
</FieldLabel>
<FieldRow>
<TextInput
{...register('fullname', {
required: true,
})}
placeholder={t(
'form.adminInfoForm.fields.fullName.placeholder'
<Controller
name='fullname'
control={control}
rules={{ required: String(t('component.form.requiredField')) }}
render={({ field }) => (
<TextInput
{...field}
aria-describedby={`${fullnameField}-error}`}
aria-required='true'
aria-invalid={Boolean(errors.fullname)}
placeholder={t(
'form.adminInfoForm.fields.fullName.placeholder'
)}
id={fullnameField}
/>
)}
id={fullnameField}
/>
</FieldRow>
{errors.fullname && (
<FieldError>{errors.fullname.message}</FieldError>
<FieldError aria-live='assertive' id={`${fullnameField}-error}`}>
{errors.fullname.message}
</FieldError>
)}
</Field>
<Field>
<FieldLabel required htmlFor={usernameField}>
{t('form.adminInfoForm.fields.username.label')}
</FieldLabel>
<FieldRow>
<TextInput
{...register('username', {
required: true,
<Controller
name='username'
control={control}
rules={{
required: String(t('component.form.requiredField')),
validate: validateUsername,
})}
placeholder={t(
'form.adminInfoForm.fields.username.placeholder'
}}
render={({ field }) => (
<TextInput
{...field}
aria-describedby={`${usernameField}-error}`}
aria-required='true'
aria-invalid={Boolean(errors.username)}
placeholder={t(
'form.adminInfoForm.fields.username.placeholder'
)}
id={usernameField}
/>
)}
id={usernameField}
/>
</FieldRow>
{errors.username && (
<FieldError>{errors.username.message}</FieldError>
<FieldError aria-live='assertive' id={`${usernameField}-error}`}>
{errors.username.message}
</FieldError>
)}
</Field>
<Field>
<FieldLabel required htmlFor={emailField}>
{t('form.adminInfoForm.fields.email.label')}
</FieldLabel>
<FieldRow>
<EmailInput
{...register('email', {
required: true,
<Controller
name='email'
control={control}
rules={{
required: String(t('component.form.requiredField')),
validate: validateEmail,
})}
placeholder={t('form.adminInfoForm.fields.email.placeholder')}
id={emailField}
}}
render={({ field }) => (
<EmailInput
{...field}
aria-required='true'
aria-invalid={Boolean(errors.email)}
aria-describedby={`${emailField}-error}`}
placeholder={t(
'form.adminInfoForm.fields.email.placeholder'
)}
id={emailField}
/>
)}
/>
</FieldRow>
{errors.email && <FieldError>{errors.email.message}</FieldError>}
{errors.email && (
<FieldError aria-live='assertive' id={`${emailField}-error}`}>
{errors.email.message}
</FieldError>
)}
</Field>
<Field>
<FieldLabel required htmlFor={passwordField}>
{t('form.adminInfoForm.fields.password.label')}
</FieldLabel>
<FieldRow>
<PasswordInput
{...register('password', {
required: true,
<Controller
name='password'
control={control}
rules={{
required: String(t('component.form.requiredField')),
validate: validatePassword,
})}
placeholder={t(
'form.adminInfoForm.fields.password.placeholder'
}}
render={({ field }) => (
<PasswordInput
{...field}
aria-required='true'
aria-invalid={Boolean(errors.password)}
aria-describedby={`${passwordField}-hint ${passwordField}-error}`}
placeholder={t(
'form.adminInfoForm.fields.password.placeholder'
)}
id={passwordField}
/>
)}
id={passwordField}
/>
</FieldRow>
<FieldHint>{passwordRulesHint}</FieldHint>
<FieldHint id={`${passwordField}-hint`}>
{passwordRulesHint}
</FieldHint>
{errors.password && (
<FieldError>{errors.password.message}</FieldError>
<FieldError aria-live='assertive' id={`${passwordField}-error}`}>
{errors.password.message}
</FieldError>
)}
</Field>
{keepPosted && (
Expand All @@ -172,7 +240,7 @@ const AdminInfoForm = ({
</Form.Container>
<Form.Footer>
<ButtonGroup flexGrow={1}>
<Button type='submit' primary disabled={!isValid || isSubmitting}>
<Button type='submit' primary disabled={isValidating || isSubmitting}>
{t('component.form.action.next')}
</Button>
</ButtonGroup>
Expand Down
Loading

0 comments on commit 0885913

Please sign in to comment.