Skip to content

Commit 7710839

Browse files
authored
feat(accessibility): sr for error for autosuggest (#2139)
* add current state * undo changes in noOptionsMessage * undo changes in autoSuggest * add comment to explain necessity of custom props for cutom input * add test for aria-describedy for autosuggest
1 parent 4351a9e commit 7710839

2 files changed

Lines changed: 64 additions & 20 deletions

File tree

app/components/inputs/autoSuggestInput/__test__/AutoSuggestInput.test.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ const mockedValidate = vi.fn();
1313
const COMPONENT_NAME = "test-autoSuggestInput";
1414
const PLACEHOLDER_MOCK = "Test Placeholder";
1515

16-
beforeEach(() => {
17-
vi.mocked(useField).mockReturnValue({
16+
function getMockUseFieldReturnValue() {
17+
return {
1818
getInputProps: vi.fn().mockReturnValue({
1919
id: COMPONENT_NAME,
2020
placeholder: PLACEHOLDER_MOCK,
@@ -39,7 +39,11 @@ beforeEach(() => {
3939
clearError: vi.fn(),
4040
reset: vi.fn(),
4141
validate: mockedValidate,
42-
});
42+
};
43+
}
44+
45+
beforeEach(() => {
46+
vi.mocked(useField).mockReturnValue(getMockUseFieldReturnValue());
4347

4448
vi.spyOn(useDataListOptions, "default").mockReturnValue(
4549
getDataListOptions("airports"),
@@ -269,4 +273,27 @@ describe("AutoSuggestInput", () => {
269273
await user.tab();
270274
expect(input).not.toHaveFocus();
271275
});
276+
277+
it("adds aria-describedby to the input when there's an error", () => {
278+
const mockField = getMockUseFieldReturnValue();
279+
mockField.error.mockReturnValue("required");
280+
281+
vi.mocked(useField).mockReturnValue(mockField);
282+
const { getByRole } = render(
283+
<AutoSuggestInput
284+
name={COMPONENT_NAME}
285+
placeholder="placeholder"
286+
dataList="airports"
287+
errorMessages={[{ code: "required", text: "Field is required" }]}
288+
isDisabled={false}
289+
/>,
290+
);
291+
292+
const input = getByRole("combobox");
293+
294+
expect(input).toHaveAttribute(
295+
"aria-describedby",
296+
`${COMPONENT_NAME}-error`,
297+
);
298+
});
272299
});
Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
1-
import { components, type InputProps } from "react-select";
1+
import {
2+
components,
3+
type InputProps,
4+
type Props,
5+
type GroupBase,
6+
} from "react-select";
27
import type { DataListOptions } from "~/services/dataListOptions/getDataListOptions";
38
import { INPUT_CHAR_LIMIT } from "~/services/validation/inputlimits";
49

5-
const CustomInput = (props: InputProps<DataListOptions, false>) => (
6-
<components.Input
7-
// avoid to clear the previous auto suggestion input when press enter
8-
// this cannot be tested, thus manually tested with device(s)
9-
onKeyDown={(e) => {
10-
if (e.key === "Enter" && !props.selectProps.menuIsOpen) {
11-
e.preventDefault();
12-
}
13-
}}
14-
{...props}
15-
maxLength={INPUT_CHAR_LIMIT}
16-
aria-required={props.selectProps.className?.includes(
17-
"auto-suggest-input-required",
18-
)}
19-
/>
20-
);
10+
//Will become obsolete with the next version bump: https://github.com/JedWatson/react-select/pull/6016
11+
type CustomSelectProps = Props<
12+
DataListOptions,
13+
false,
14+
GroupBase<DataListOptions>
15+
> & {
16+
"aria-describedby": string;
17+
};
2118

19+
const CustomInput = (props: InputProps<DataListOptions, false>) => {
20+
const selectProps = props.selectProps as CustomSelectProps;
21+
return (
22+
<components.Input
23+
// avoid to clear the previous auto suggestion input when press enter
24+
// this cannot be tested, thus manually tested with device(s)
25+
onKeyDown={(e) => {
26+
if (e.key === "Enter" && !props.selectProps.menuIsOpen) {
27+
e.preventDefault();
28+
}
29+
}}
30+
{...props}
31+
maxLength={INPUT_CHAR_LIMIT}
32+
aria-describedby={selectProps["aria-describedby"]}
33+
aria-required={props.selectProps.className?.includes(
34+
"auto-suggest-input-required",
35+
)}
36+
/>
37+
);
38+
};
2239
export default CustomInput;

0 commit comments

Comments
 (0)