Skip to content

Commit 21677a1

Browse files
nehaaprasadheloiseluisangeethababu9223
authored
fix(a11y): use aria-errormessage for invalid text inputs (#21297)
* fix(a11y): use aria-errormessage for invalid text inputs * fix(a11y): use aria-errormessage for invalid text inputs and add tests * Apply suggestion from @sangeethababu9223 Co-authored-by: Sangeetha Babu <sangeetha9223@gmail.com> * fix(textinput): yarn format * fix(text-input): align invalid helper aria and add tests --------- Co-authored-by: Heloise Lui <71858203+heloiselui@users.noreply.github.com> Co-authored-by: Sangeetha Babu <sangeetha9223@gmail.com> Co-authored-by: “heloiselui” <helolui27@gmail.com>
1 parent 3a286e9 commit 21677a1

7 files changed

Lines changed: 168 additions & 5 deletions

File tree

packages/react/src/components/TextArea/TextArea-test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,42 @@ describe('TextArea', () => {
162162
expect(screen.getByRole('textbox')).toHaveAttribute('aria-invalid');
163163
});
164164

165+
it('should set aria-errormessage to the invalid message element id', () => {
166+
render(
167+
<TextArea
168+
id="textarea-1"
169+
labelText="TextArea"
170+
invalid
171+
invalidText="Some error"
172+
/>
173+
);
174+
175+
const input = screen.getByRole('textbox');
176+
expect(input).toHaveAttribute(
177+
'aria-errormessage',
178+
'textarea-1-error-msg'
179+
);
180+
expect(screen.getByText('Some error')).toHaveAttribute(
181+
'id',
182+
'textarea-1-error-msg'
183+
);
184+
});
185+
186+
it('should use aria-describedby for helper text when not invalid', () => {
187+
render(
188+
<TextArea
189+
id="textarea-1"
190+
labelText="TextArea"
191+
helperText="Helper copy"
192+
/>
193+
);
194+
195+
const helper = screen.getByText('Helper copy');
196+
const input = screen.getByRole('textbox');
197+
expect(input).toHaveAttribute('aria-describedby', helper.id);
198+
expect(input).not.toHaveAttribute('aria-errormessage');
199+
});
200+
165201
it('should respect `invalidText` prop', () => {
166202
render(
167203
<TextArea

packages/react/src/components/TextArea/TextArea.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,9 @@ const TextArea = frFn((props, forwardRef) => {
450450
) : null;
451451

452452
let ariaDescribedBy;
453+
let ariaErrorMessage;
453454
if (invalid) {
454-
ariaDescribedBy = errorId;
455+
ariaErrorMessage = errorId;
455456
} else if (warn && !isFluid) {
456457
ariaDescribedBy = warnId;
457458
} else {
@@ -513,6 +514,7 @@ const TextArea = frFn((props, forwardRef) => {
513514
className={textareaClasses}
514515
aria-invalid={invalid}
515516
aria-describedby={ariaDescribedBy}
517+
aria-errormessage={ariaErrorMessage}
516518
disabled={disabled}
517519
rows={rows}
518520
readOnly={other.readOnly}

packages/react/src/components/TextInput/TextInput.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,10 @@ const TextInput = forwardRef<unknown, TextInputProps>(
249249
title: placeholder,
250250
disabled: normalizedProps.disabled,
251251
readOnly,
252-
['aria-describedby']: hasHelperText(helperText)
253-
? normalizedProps.helperId
254-
: undefined,
252+
['aria-describedby']:
253+
hasHelperText(helperText) && !normalizedProps.invalid
254+
? normalizedProps.helperId
255+
: undefined,
255256
...rest,
256257
};
257258

packages/react/src/components/TextInput/__tests__/ControlledPasswordInput-test.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,62 @@ describe('ControlledPasswordInput Component', () => {
319319
expect(document.getElementById(helperId)).toHaveTextContent('0');
320320
});
321321

322+
it('should set `aria-errormessage` to the invalid message element id', () => {
323+
render(
324+
<ControlledPasswordInput
325+
id="password-input"
326+
labelText="Password"
327+
invalid
328+
invalidText="This is invalid text"
329+
/>
330+
);
331+
332+
const input = screen.getByLabelText('Password');
333+
expect(input).toHaveAttribute(
334+
'aria-errormessage',
335+
'password-input-error-msg'
336+
);
337+
expect(input).toHaveAttribute('aria-invalid', 'true');
338+
expect(screen.getByText('This is invalid text')).toHaveAttribute(
339+
'id',
340+
'password-input-error-msg'
341+
);
342+
});
343+
344+
it('should use `aria-describedby` for helper text when not invalid', () => {
345+
render(
346+
<ControlledPasswordInput
347+
id="password-input"
348+
labelText="Password"
349+
helperText="Helper copy"
350+
/>
351+
);
352+
353+
const helper = screen.getByText('Helper copy');
354+
const input = screen.getByLabelText('Password');
355+
expect(input).toHaveAttribute('aria-describedby', helper.id);
356+
expect(input).not.toHaveAttribute('aria-errormessage');
357+
});
358+
359+
it('should not set `aria-describedby` to helper id when invalid', () => {
360+
render(
361+
<ControlledPasswordInput
362+
id="password-input"
363+
labelText="Password"
364+
invalid
365+
invalidText="Error"
366+
helperText="Helper copy"
367+
/>
368+
);
369+
370+
const input = screen.getByLabelText('Password');
371+
expect(input).toHaveAttribute(
372+
'aria-errormessage',
373+
'password-input-error-msg'
374+
);
375+
expect(input).not.toHaveAttribute('aria-describedby');
376+
});
377+
322378
it('should render labelText with value 0', () => {
323379
render(<ControlledPasswordInput id="password-input" labelText={0} />);
324380
expect(screen.getByText('0')).toBeInTheDocument();

packages/react/src/components/TextInput/__tests__/PasswordInput-test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,24 @@ describe('PasswordInput', () => {
181181
);
182182
});
183183

184+
it('should set aria-errormessage to the invalid message element id', () => {
185+
render(
186+
<PasswordInput
187+
id="input-1"
188+
labelText="PasswordInput label"
189+
invalid
190+
invalidText="This is invalid text"
191+
/>
192+
);
193+
194+
const input = screen.getByLabelText('PasswordInput label');
195+
expect(input).toHaveAttribute('aria-errormessage', 'input-1-error-msg');
196+
expect(screen.getByText('This is invalid text')).toHaveAttribute(
197+
'id',
198+
'input-1-error-msg'
199+
);
200+
});
201+
184202
it('should respect labelText prop', () => {
185203
render(<PasswordInput id="input-1" labelText="TextInput label" />);
186204

packages/react/src/components/TextInput/__tests__/TextInput-test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,56 @@ describe('TextInput', () => {
156156
);
157157
});
158158

159+
it('should set aria-errormessage to the invalid message element id', () => {
160+
render(
161+
<TextInput
162+
id="input-1"
163+
labelText="TextInput"
164+
invalid
165+
invalidText="This is invalid text"
166+
/>
167+
);
168+
169+
const input = screen.getByRole('textbox');
170+
expect(input).toHaveAttribute('aria-errormessage', 'input-1-error-msg');
171+
expect(input).toHaveAttribute('aria-invalid', 'true');
172+
expect(screen.getByText('This is invalid text')).toHaveAttribute(
173+
'id',
174+
'input-1-error-msg'
175+
);
176+
});
177+
178+
it('should use aria-describedby for helper text when not invalid', () => {
179+
render(
180+
<TextInput
181+
id="input-1"
182+
labelText="TextInput"
183+
helperText="Helper copy"
184+
/>
185+
);
186+
187+
const helper = screen.getByText('Helper copy');
188+
const input = screen.getByRole('textbox');
189+
expect(input).toHaveAttribute('aria-describedby', helper.id);
190+
expect(input).not.toHaveAttribute('aria-errormessage');
191+
});
192+
193+
it('should not set aria-describedby to helper id when invalid', () => {
194+
render(
195+
<TextInput
196+
id="input-1"
197+
labelText="TextInput"
198+
invalid
199+
invalidText="Error"
200+
helperText="Helper copy"
201+
/>
202+
);
203+
204+
const input = screen.getByRole('textbox');
205+
expect(input).toHaveAttribute('aria-errormessage', 'input-1-error-msg');
206+
expect(input).not.toHaveAttribute('aria-describedby');
207+
});
208+
159209
it('should respect labelText prop', () => {
160210
render(<TextInput id="input-1" labelText="TextInput label" />);
161211

packages/react/src/components/TextInput/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
const invalidProps = (invalidId: string) => ({
99
'data-invalid': true,
1010
'aria-invalid': true,
11-
'aria-describedby': invalidId,
11+
'aria-errormessage': invalidId,
1212
});
1313

1414
const warnProps = (warnId: string | undefined) => ({

0 commit comments

Comments
 (0)