Skip to content

Commit d033806

Browse files
authored
Fix aria attributes on error messages (#230)
* Fix aria attributes on error messages * Fix behaviour of aria-describedby in form components
1 parent 29e890b commit d033806

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

src/components/form-elements/checkboxes/__tests__/__snapshots__/Checkboxes.test.tsx.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ exports[`Checkboxes matches snapshot with boolean error 1`] = `
178178
class="nhsuk-form-group nhsuk-form-group--error"
179179
>
180180
<div
181+
aria-describedby="example--error-message"
181182
class="nhsuk-checkboxes"
182183
id="example"
183184
>
@@ -262,6 +263,7 @@ exports[`Checkboxes matches snapshot with string error 1`] = `
262263
Example error
263264
</span>
264265
<div
266+
aria-describedby="example--error-message"
265267
class="nhsuk-checkboxes"
266268
id="example"
267269
>

src/util/FormGroup.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,13 @@ const FormGroup = <T extends BaseFormElementRenderProps>(props: FormGroupProps<T
6262
const errorID = `${elementID}--error-message`;
6363
const hintID = `${elementID}--hint`;
6464

65+
const ariaDescribedBy = [
66+
hint ? hintID : undefined,
67+
error ? errorID : undefined,
68+
].filter(Boolean);
69+
6570
const childProps = {
66-
'aria-describedby': hint ? hintID : undefined,
67-
'aria-labelledby': label ? labelID : undefined,
71+
'aria-describedby': ariaDescribedBy.join(' ') || undefined,
6872
error,
6973
name: name || elementID,
7074
id: elementID,

src/util/__tests__/FormGroup.test.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,6 @@ describe('FormGroup', () => {
108108
expect(renderProps!.id).toHaveLength(11);
109109
expect(renderProps!.id).toContain('input');
110110

111-
expect(container.querySelector('input')?.getAttribute('aria-labelledby')).toBe(
112-
`${renderProps!.id}--label`,
113-
);
114111
expect(container.querySelector('.nhsuk-label')?.getAttribute('id')).toBe(
115112
`${renderProps!.id}--label`,
116113
);
@@ -133,7 +130,6 @@ describe('FormGroup', () => {
133130
expect(renderProps).not.toBe(null);
134131
expect(renderProps!.id).toBe('testID');
135132

136-
expect(container.querySelector('input')?.getAttribute('aria-labelledby')).toBe('testID--label');
137133
expect(container.querySelector('.nhsuk-label')?.getAttribute('id')).toBe('testID--label');
138134
expect(container.querySelector('.nhsuk-label')?.getAttribute('for')).toBe('testID');
139135
expect(container.querySelector('.nhsuk-label')?.textContent).toBe('This is a test label');
@@ -155,6 +151,7 @@ describe('FormGroup', () => {
155151
expect(renderProps).not.toBe(null);
156152
expect(renderProps!.id).toHaveLength(11);
157153
expect(renderProps!.id).toContain('input');
154+
expect(renderProps!['aria-describedby']).toBe(`${renderProps!.id}--error-message`);
158155

159156
expect(container.querySelector('.nhsuk-error-message')?.getAttribute('id')).toBe(
160157
`${renderProps!.id}--error-message`,
@@ -182,6 +179,8 @@ describe('FormGroup', () => {
182179

183180
expect(renderProps).not.toBe(null);
184181
expect(renderProps!.id).toBe('testID');
182+
expect(renderProps!['aria-describedby']).toBe(`testID--error-message`);
183+
185184

186185
expect(container.querySelector('.nhsuk-error-message')?.getAttribute('id')).toBe(
187186
'testID--error-message',
@@ -240,4 +239,32 @@ describe('FormGroup', () => {
240239

241240
expect(await axe(html)).toHaveNoViolations();
242241
});
242+
243+
it('should add hint ID and error ID to the aria-describedby of the input', () => {
244+
const { container } = renderFormGroupComponent({
245+
inputType: 'input',
246+
id: 'error-and-hint',
247+
error: 'This is an error',
248+
hint: 'This is a hint',
249+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
250+
children: ({ error, ...rest }) => <input {...rest} />,
251+
});
252+
253+
const inputElement = container.querySelector('input');
254+
expect(inputElement).not.toBeNull();
255+
expect(inputElement?.getAttribute('aria-describedby')).toBe('error-and-hint--hint error-and-hint--error-message');
256+
})
257+
258+
it('should have no aria-describedby when there is no hint or label', () => {
259+
const { container } = renderFormGroupComponent({
260+
inputType: 'input',
261+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
262+
children: ({ error, ...rest }) => <input {...rest} />,
263+
});
264+
265+
const inputElement = container.querySelector('input');
266+
expect(inputElement).not.toBeNull();
267+
268+
expect(inputElement?.getAttribute('aria-describedby')).toBe(null);
269+
});
243270
});

0 commit comments

Comments
 (0)