Skip to content

Commit

Permalink
feat: improve accessibility for single field components tckt-393 (#387)
Browse files Browse the repository at this point in the history
* feat: improve accessibility for single field components tckt-000

* refactor: improve accessibility and error handling for single field components tckt-393

---------

Co-authored-by: kalasgarov <[email protected]>
  • Loading branch information
kalasgarov and kalasgarov authored Dec 4, 2024
1 parent 47ea8ce commit 7af78d4
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 35 deletions.
56 changes: 42 additions & 14 deletions packages/design/src/Form/components/DateOfBirth/DateOfBirth.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import classNames from 'classnames';
import { useFormContext } from 'react-hook-form';
import { type DateOfBirthProps } from '@atj/forms';
import { type PatternComponent } from '../../index.js';
Expand All @@ -18,6 +19,14 @@ const months = [
{ value: '12', label: 'December' },
];

const getAriaDescribedBy = (
errorId: string | null,
hintId: string | null
): string | undefined => {
const ids = [errorId, hintId].filter(Boolean).join(' ');
return ids || undefined;
};

export const DateOfBirthPattern: PatternComponent<DateOfBirthProps> = ({
monthId,
dayId,
Expand All @@ -28,6 +37,8 @@ export const DateOfBirthPattern: PatternComponent<DateOfBirthProps> = ({
error,
}) => {
const { register } = useFormContext();
const errorId = `input-error-message-${monthId}`;
const hintId = `hint-${monthId}`;

return (
<fieldset className="usa-fieldset">
Expand All @@ -36,20 +47,32 @@ export const DateOfBirthPattern: PatternComponent<DateOfBirthProps> = ({
{required && <span className="required-indicator">*</span>}
</legend>
{hint && (
<span className="usa-hint" id="mdHint">
<span className="usa-hint" id={hintId}>
{hint}
</span>
)}
{error && (
<div className="usa-error-message" id={errorId} role="alert">
{error.message}
</div>
)}
<div className="usa-memorable-date">
<div className="usa-form-group usa-form-group--month usa-form-group--select">
<label className="usa-label" htmlFor={monthId}>
Month
</label>
<select
className="usa-select"
className={classNames('usa-input', {
'usa-input--error': !!error,
})}
id={monthId}
{...register(monthId)}
aria-describedby="mdHint"
aria-describedby={
getAriaDescribedBy(
error ? errorId : null,
hint ? hintId : null
) || undefined
}
>
<option key="default" value="">
- Select -
Expand All @@ -66,37 +89,42 @@ export const DateOfBirthPattern: PatternComponent<DateOfBirthProps> = ({
Day
</label>
<input
className="usa-input"
aria-describedby="mdHint"
className={classNames('usa-input', {
'usa-input--error': !!error,
})}
id={dayId}
{...register(dayId)}
{...register(dayId, { required })}
minLength={2}
maxLength={2}
pattern="[0-9]*"
inputMode="numeric"
aria-describedby={getAriaDescribedBy(
error ? `input-error-message-${dayId}` : null,
hint ? hintId : null
)}
/>
</div>
<div className="usa-form-group usa-form-group--year">
<label className="usa-label" htmlFor={yearId}>
Year
</label>
<input
className="usa-input"
aria-describedby="mdHint"
className={classNames('usa-input', {
'usa-input--error': !!error,
})}
id={yearId}
{...register(yearId)}
{...register(yearId, { required })}
minLength={4}
maxLength={4}
pattern="[0-9]*"
inputMode="numeric"
aria-describedby={getAriaDescribedBy(
error ? `input-error-message-${yearId}` : null,
hint ? hintId : null
)}
/>
</div>
</div>
{error && (
<span className="error-message" style={{ color: 'red' }}>
{error.message}
</span>
)}
</fieldset>
);
};
17 changes: 11 additions & 6 deletions packages/design/src/Form/components/EmailInput/EmailInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import classNames from 'classnames';
import { useFormContext } from 'react-hook-form';
import { type EmailInputProps } from '@atj/forms';
import { type PatternComponent } from '../../index.js';
Expand All @@ -10,6 +11,7 @@ export const EmailInputPattern: PatternComponent<EmailInputProps> = ({
error,
}) => {
const { register } = useFormContext();
const errorId = `input-error-message-${emailId}`;

return (
<fieldset className="usa-fieldset">
Expand All @@ -18,20 +20,23 @@ export const EmailInputPattern: PatternComponent<EmailInputProps> = ({
{label}
{required && <span className="required-indicator">*</span>}
</label>
{error && (
<div className="usa-error-message" id={errorId} role="alert">
{error.message}
</div>
)}
<input
className="usa-input margin-bottom-1"
className={classNames('usa-input margin-bottom-1', {
'usa-input--error': error,
})}
id={emailId}
type="email"
autoCapitalize="off"
autoCorrect="off"
{...register(emailId, { required })}
aria-describedby={error ? errorId : undefined}
/>
</div>
{error && (
<span className="error-message" style={{ color: 'red' }}>
{error.message}
</span>
)}
</fieldset>
);
};
16 changes: 8 additions & 8 deletions packages/design/src/Form/components/PhoneNumber/PhoneNumber.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const PhoneNumberPattern: PatternComponent<PhoneNumberProps> = ({
value,
}) => {
const { register } = useFormContext();
const errorId = `input-error-message-${phoneId}`;
const hintId = `hint-${phoneId}`;

return (
<fieldset className="usa-fieldset">
Expand All @@ -21,23 +23,18 @@ export const PhoneNumberPattern: PatternComponent<PhoneNumberProps> = ({
className={classNames('usa-label', {
'usa-label--error': error,
})}
id={`input-message-${phoneId}`}
htmlFor={phoneId}
>
{label}
{required && <span className="required-indicator">*</span>}
</label>
{hint && (
<div className="usa-hint" id="primaryPnHint">
<div className="usa-hint" id={hintId}>
{hint}
</div>
)}
{error && (
<div
className="usa-error-message"
id={`input-error-message-${phoneId}`}
role="alert"
>
<div className="usa-error-message" id={errorId} role="alert">
{error.message}
</div>
)}
Expand All @@ -49,7 +46,10 @@ export const PhoneNumberPattern: PatternComponent<PhoneNumberProps> = ({
type="tel"
defaultValue={value}
{...register(phoneId, { required })}
aria-describedby="primaryPnHint"
aria-describedby={
`${hint ? `${hintId}` : ''}${error ? ` ${errorId}` : ''}`.trim() ||
undefined
}
/>
</div>
</fieldset>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import { useFormContext } from 'react-hook-form';

import { type SelectDropdownProps } from '@atj/forms';
import { type PatternComponent } from '../../index.js';

Expand All @@ -12,13 +12,27 @@ export const SelectDropdownPattern: PatternComponent<SelectDropdownProps> = ({
error,
}) => {
const { register } = useFormContext();
const errorId = `input-error-message-${selectId}`;

return (
<div className="usa-fieldset padding-top-2">
<label className="usa-label" htmlFor={selectId}>
{label}
{required && <span className="required-indicator">*</span>}
</label>
<select className="usa-select" id={selectId} {...register(selectId)}>
{error && (
<div className="usa-error-message" id={errorId} role="alert">
{error.message}
</div>
)}
<select
className={classNames('usa-input', {
'usa-input--error': error,
})}
id={selectId}
{...register(selectId, { required })}
aria-describedby={error ? errorId : undefined}
>
<option key="default" value="">
- Select -
</option>
Expand All @@ -28,11 +42,6 @@ export const SelectDropdownPattern: PatternComponent<SelectDropdownProps> = ({
</option>
))}
</select>
{error && (
<span className="error-message" style={{ color: 'red' }}>
{error.message}
</span>
)}
</div>
);
};

0 comments on commit 7af78d4

Please sign in to comment.