@simplybusiness/mobius 5.5.0 → 5.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/cjs/components/Combobox/Combobox.js +56 -33
- package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
- package/dist/cjs/components/Combobox/Listbox.js +58 -0
- package/dist/cjs/components/Combobox/Listbox.js.map +1 -0
- package/dist/cjs/components/Combobox/Option.js +44 -0
- package/dist/cjs/components/Combobox/Option.js.map +1 -0
- package/dist/cjs/components/Combobox/fixtures.js +115 -0
- package/dist/cjs/components/Combobox/fixtures.js.map +1 -1
- package/dist/cjs/components/Combobox/useComboboxHighlight.js +86 -0
- package/dist/cjs/components/Combobox/useComboboxHighlight.js.map +1 -0
- package/dist/cjs/components/Combobox/utils.js +46 -0
- package/dist/cjs/components/Combobox/utils.js.map +1 -0
- package/dist/cjs/components/TextArea/TextArea.js +4 -4
- package/dist/cjs/components/TextArea/TextArea.js.map +1 -1
- package/dist/cjs/components/TextAreaInput/TextAreaInput.js +6 -3
- package/dist/cjs/components/TextAreaInput/TextAreaInput.js.map +1 -1
- package/dist/cjs/components/TextField/TextField.js +4 -3
- package/dist/cjs/components/TextField/TextField.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/components/Combobox/Combobox.js +55 -32
- package/dist/esm/components/Combobox/Combobox.js.map +1 -1
- package/dist/esm/components/Combobox/Listbox.js +43 -0
- package/dist/esm/components/Combobox/Listbox.js.map +1 -0
- package/dist/esm/components/Combobox/Option.js +29 -0
- package/dist/esm/components/Combobox/Option.js.map +1 -0
- package/dist/esm/components/Combobox/fixtures.js +109 -0
- package/dist/esm/components/Combobox/fixtures.js.map +1 -1
- package/dist/esm/components/Combobox/types.js.map +1 -1
- package/dist/esm/components/Combobox/useComboboxHighlight.js +76 -0
- package/dist/esm/components/Combobox/useComboboxHighlight.js.map +1 -0
- package/dist/esm/components/Combobox/utils.js +20 -0
- package/dist/esm/components/Combobox/utils.js.map +1 -0
- package/dist/esm/components/TextArea/TextArea.js +4 -4
- package/dist/esm/components/TextArea/TextArea.js.map +1 -1
- package/dist/esm/components/TextAreaInput/TextAreaInput.js +6 -3
- package/dist/esm/components/TextAreaInput/TextAreaInput.js.map +1 -1
- package/dist/esm/components/TextField/TextField.js +4 -3
- package/dist/esm/components/TextField/TextField.js.map +1 -1
- package/dist/types/components/Combobox/Combobox.d.ts +1 -1
- package/dist/types/components/Combobox/Combobox.stories.d.ts +4 -1
- package/dist/types/components/Combobox/Listbox.d.ts +10 -0
- package/dist/types/components/Combobox/Option.d.ts +2 -0
- package/dist/types/components/Combobox/fixtures.d.ts +5 -0
- package/dist/types/components/Combobox/types.d.ts +17 -2
- package/dist/types/components/Combobox/useComboboxHighlight.d.ts +10 -0
- package/dist/types/components/Combobox/useComboboxHighlight.test.d.ts +1 -0
- package/dist/types/components/Combobox/utils.d.ts +6 -0
- package/dist/types/components/Combobox/utils.test.d.ts +1 -0
- package/dist/types/components/TextArea/TextArea.d.ts +2 -2
- package/dist/types/components/TextAreaInput/TextAreaInput.d.ts +3 -1
- package/package.json +1 -1
- package/src/components/Combobox/Combobox.css +51 -4
- package/src/components/Combobox/Combobox.mdx +147 -0
- package/src/components/Combobox/Combobox.stories.tsx +47 -2
- package/src/components/Combobox/Combobox.test.tsx +535 -316
- package/src/components/Combobox/Combobox.tsx +78 -58
- package/src/components/Combobox/Listbox.tsx +74 -0
- package/src/components/Combobox/Option.tsx +41 -0
- package/src/components/Combobox/fixtures.tsx +111 -0
- package/src/components/Combobox/types.tsx +22 -4
- package/src/components/Combobox/useComboboxHighlight.test.tsx +242 -0
- package/src/components/Combobox/useComboboxHighlight.tsx +88 -0
- package/src/components/Combobox/utils.test.tsx +120 -0
- package/src/components/Combobox/utils.tsx +50 -0
- package/src/components/TextArea/TextArea.tsx +6 -6
- package/src/components/TextAreaInput/TextAreaInput.tsx +16 -4
- package/src/components/TextField/TextField.test.tsx +8 -0
- package/src/components/TextField/TextField.tsx +3 -1
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
1
|
+
import { act, render, screen, within } from "@testing-library/react";
|
|
2
2
|
import userEvent from "@testing-library/user-event";
|
|
3
3
|
import { Combobox } from "./Combobox";
|
|
4
|
-
import { FRUITS, FRUITS_OBJECTS } from "./fixtures";
|
|
4
|
+
import { FRUITS, FRUITS_OBJECTS, FRUITS_GROUPS } from "./fixtures";
|
|
5
5
|
|
|
6
6
|
describe("Combobox with string options", () => {
|
|
7
|
+
// Basic rendering and setup
|
|
7
8
|
it("should render without crashing", () => {
|
|
8
9
|
render(<Combobox options={FRUITS} />);
|
|
9
10
|
});
|
|
10
11
|
|
|
11
|
-
it("should use the default value provided", () => {
|
|
12
|
-
render(<Combobox options={FRUITS} defaultValue="Apple" />);
|
|
13
|
-
const input = screen.getByRole("combobox") as HTMLInputElement;
|
|
14
|
-
expect(input.value).toBe("Apple");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
12
|
it("should render a combobox", () => {
|
|
18
13
|
render(<Combobox options={FRUITS} />);
|
|
19
14
|
const input = screen.getByRole("combobox");
|
|
20
15
|
expect(input).toBeInTheDocument();
|
|
21
16
|
});
|
|
22
17
|
|
|
18
|
+
it("should use the default value provided", () => {
|
|
19
|
+
render(<Combobox options={FRUITS} defaultValue="Apple" />);
|
|
20
|
+
const input = screen.getByRole("combobox") as HTMLInputElement;
|
|
21
|
+
expect(input.value).toBe("Apple");
|
|
22
|
+
});
|
|
23
|
+
|
|
23
24
|
it("should accept a ref and forward it to the input", () => {
|
|
24
25
|
const ref = { current: null };
|
|
25
26
|
render(<Combobox options={FRUITS} ref={ref} />);
|
|
@@ -32,169 +33,260 @@ describe("Combobox with string options", () => {
|
|
|
32
33
|
expect(listbox).not.toBeInTheDocument();
|
|
33
34
|
});
|
|
34
35
|
|
|
35
|
-
it("should
|
|
36
|
-
render(<Combobox options={FRUITS} />);
|
|
36
|
+
it("should set placeholder in input if provided", async () => {
|
|
37
|
+
render(<Combobox options={FRUITS} placeholder="Please pick a fruit" />);
|
|
37
38
|
const input = screen.getByRole("combobox");
|
|
38
|
-
|
|
39
|
-
const listbox = screen.getByRole("listbox");
|
|
40
|
-
expect(listbox).toBeInTheDocument();
|
|
39
|
+
expect(input).toHaveAttribute("placeholder", "Please pick a fruit");
|
|
41
40
|
});
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
render(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
describe("mouse interactions", () => {
|
|
43
|
+
it("should render a listbox when the input is focused", async () => {
|
|
44
|
+
render(<Combobox options={FRUITS} />);
|
|
45
|
+
const input = screen.getByRole("combobox");
|
|
46
|
+
await userEvent.click(input);
|
|
47
|
+
const listbox = screen.getByRole("listbox");
|
|
48
|
+
expect(listbox).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
it("should call the onSelected callback when an option is selected", async () => {
|
|
52
|
+
const onSelected = jest.fn();
|
|
53
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
54
|
+
const input = screen.getByRole("combobox");
|
|
55
|
+
await userEvent.click(input);
|
|
56
|
+
const options = screen.getAllByRole("option");
|
|
57
|
+
await userEvent.click(options[0]);
|
|
58
|
+
expect(onSelected).toHaveBeenCalledWith(FRUITS[0]);
|
|
59
|
+
});
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
it("should update the input value when an option is selected", async () => {
|
|
62
|
+
render(<Combobox options={FRUITS} />);
|
|
63
|
+
const input = screen.getByRole("combobox");
|
|
64
|
+
await userEvent.click(input);
|
|
65
|
+
const options = screen.getAllByRole("option");
|
|
66
|
+
await userEvent.click(options[0]);
|
|
67
|
+
expect(input).toHaveValue(FRUITS[0]);
|
|
68
|
+
});
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
it("should close the listbox when the input is blurred", async () => {
|
|
71
|
+
jest.useFakeTimers({
|
|
72
|
+
advanceTimers: true,
|
|
73
|
+
timerLimit: 1000,
|
|
74
|
+
});
|
|
75
|
+
render(<Combobox options={FRUITS} />);
|
|
76
|
+
const input = screen.getByRole("combobox");
|
|
77
|
+
await userEvent.click(input);
|
|
78
|
+
await act(async () => {
|
|
79
|
+
await userEvent.tab();
|
|
80
|
+
jest.advanceTimersByTime(200);
|
|
81
|
+
});
|
|
82
|
+
const listbox = screen.queryByRole("listbox");
|
|
83
|
+
expect(listbox).not.toBeInTheDocument();
|
|
84
|
+
});
|
|
78
85
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
it("should close the listbox when an option is selected", async () => {
|
|
87
|
+
render(<Combobox options={FRUITS} />);
|
|
88
|
+
const input = screen.getByRole("combobox");
|
|
89
|
+
await userEvent.click(input);
|
|
90
|
+
const options = screen.getAllByRole("option");
|
|
91
|
+
await userEvent.click(options[0]);
|
|
92
|
+
const listbox = screen.queryByRole("listbox");
|
|
93
|
+
expect(listbox).not.toBeInTheDocument();
|
|
94
|
+
});
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
it("should close the listbox when clicking outside", async () => {
|
|
97
|
+
jest.useFakeTimers({
|
|
98
|
+
advanceTimers: true,
|
|
99
|
+
timerLimit: 1000,
|
|
100
|
+
});
|
|
101
|
+
render(
|
|
102
|
+
<div>
|
|
103
|
+
<Combobox options={FRUITS} />
|
|
104
|
+
<button type="button">Outside</button>
|
|
105
|
+
</div>,
|
|
106
|
+
);
|
|
107
|
+
const input = screen.getByRole("combobox");
|
|
108
|
+
const outside = screen.getByRole("button");
|
|
109
|
+
await userEvent.click(input);
|
|
110
|
+
await userEvent.click(outside);
|
|
111
|
+
await act(async () => {
|
|
112
|
+
await userEvent.tab();
|
|
113
|
+
jest.advanceTimersByTime(200);
|
|
114
|
+
});
|
|
115
|
+
const listbox = screen.queryByRole("listbox");
|
|
116
|
+
expect(listbox).not.toBeInTheDocument();
|
|
117
|
+
});
|
|
96
118
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
119
|
+
it("should not close the listbox on right click", async () => {
|
|
120
|
+
render(<Combobox options={FRUITS} />);
|
|
121
|
+
const input = screen.getByRole("combobox");
|
|
122
|
+
await userEvent.click(input);
|
|
123
|
+
await userEvent.pointer({ target: input, keys: "[MouseRight]" });
|
|
124
|
+
const listbox = screen.getByRole("listbox");
|
|
125
|
+
expect(listbox).toBeInTheDocument();
|
|
126
|
+
});
|
|
103
127
|
});
|
|
104
128
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
129
|
+
describe("keyboard interactions", () => {
|
|
130
|
+
it("should select the top option when pressing Enter", async () => {
|
|
131
|
+
const onSelected = jest.fn();
|
|
132
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
133
|
+
const input = screen.getByRole("combobox");
|
|
134
|
+
await userEvent.type(input, "Ap");
|
|
135
|
+
await userEvent.type(input, "{enter}");
|
|
136
|
+
expect(onSelected).toHaveBeenCalledWith("Apple");
|
|
137
|
+
});
|
|
113
138
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
139
|
+
it("should not select an option when pressing Enter with no options", async () => {
|
|
140
|
+
const onSelected = jest.fn();
|
|
141
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
142
|
+
const input = screen.getByRole("combobox");
|
|
143
|
+
await userEvent.type(input, "Zz");
|
|
144
|
+
await userEvent.type(input, "{enter}");
|
|
145
|
+
expect(onSelected).not.toHaveBeenCalled();
|
|
146
|
+
});
|
|
122
147
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
148
|
+
it("should select the highlighted option when pressing Enter", async () => {
|
|
149
|
+
const onSelected = jest.fn();
|
|
150
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
151
|
+
const input = screen.getByRole("combobox");
|
|
152
|
+
await userEvent.click(input);
|
|
153
|
+
await userEvent.keyboard("{arrowdown}");
|
|
154
|
+
await userEvent.keyboard("{enter}");
|
|
155
|
+
expect(onSelected).toHaveBeenCalledWith(FRUITS[0]);
|
|
156
|
+
});
|
|
132
157
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
158
|
+
it("should open the listbox when pressing ArrowDown", async () => {
|
|
159
|
+
render(<Combobox options={FRUITS} />);
|
|
160
|
+
screen.getByRole("combobox");
|
|
161
|
+
await userEvent.tab();
|
|
162
|
+
await userEvent.keyboard("{arrowdown}");
|
|
163
|
+
const listbox = screen.getByRole("listbox");
|
|
164
|
+
expect(listbox).toBeInTheDocument();
|
|
165
|
+
});
|
|
141
166
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
it("should highlight the first option when pressing ArrowDown", async () => {
|
|
168
|
+
render(<Combobox options={FRUITS} />);
|
|
169
|
+
screen.getByRole("combobox");
|
|
170
|
+
await userEvent.tab();
|
|
171
|
+
await userEvent.keyboard("{arrowdown}");
|
|
172
|
+
const options = screen.getAllByRole("option");
|
|
173
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
|
174
|
+
});
|
|
150
175
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
176
|
+
it("should open the listbox when pressing ArrowUp", async () => {
|
|
177
|
+
render(<Combobox options={FRUITS} />);
|
|
178
|
+
screen.getByRole("combobox");
|
|
179
|
+
await userEvent.tab();
|
|
180
|
+
await userEvent.keyboard("{arrowup}");
|
|
181
|
+
const listbox = screen.getByRole("listbox");
|
|
182
|
+
expect(listbox).toBeInTheDocument();
|
|
183
|
+
});
|
|
159
184
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
"true",
|
|
169
|
-
);
|
|
170
|
-
});
|
|
185
|
+
it("should close the listbox when pressing Escape", async () => {
|
|
186
|
+
render(<Combobox options={FRUITS} />);
|
|
187
|
+
const input = screen.getByRole("combobox");
|
|
188
|
+
await userEvent.click(input);
|
|
189
|
+
await userEvent.keyboard("{escape}");
|
|
190
|
+
const listbox = screen.queryByRole("listbox");
|
|
191
|
+
expect(listbox).not.toBeInTheDocument();
|
|
192
|
+
});
|
|
171
193
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
194
|
+
it("should not call onSelected when pressing Escape", async () => {
|
|
195
|
+
const onSelected = jest.fn();
|
|
196
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
197
|
+
const input = screen.getByRole("combobox");
|
|
198
|
+
await userEvent.click(input);
|
|
199
|
+
await userEvent.keyboard("{escape}");
|
|
200
|
+
expect(onSelected).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
180
202
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
203
|
+
it("should reset the highlighted index when the input value changes", async () => {
|
|
204
|
+
render(<Combobox options={FRUITS} />);
|
|
205
|
+
const input = screen.getByRole("combobox");
|
|
206
|
+
await userEvent.click(input);
|
|
207
|
+
await userEvent.keyboard("{arrowdown}");
|
|
208
|
+
await userEvent.type(input, "Ap");
|
|
209
|
+
const options = screen.getAllByRole("option");
|
|
210
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should select the first option when pressing Home", async () => {
|
|
214
|
+
const onSelected = jest.fn();
|
|
215
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
216
|
+
const input = screen.getByRole("combobox");
|
|
217
|
+
await userEvent.click(input);
|
|
218
|
+
await userEvent.keyboard("{arrowdown}{arrowdown}");
|
|
219
|
+
await userEvent.keyboard("{home}");
|
|
220
|
+
const options = screen.getAllByRole("option");
|
|
221
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("should select the last option when pressing End", async () => {
|
|
225
|
+
const onSelected = jest.fn();
|
|
226
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
227
|
+
const input = screen.getByRole("combobox");
|
|
228
|
+
await userEvent.click(input);
|
|
229
|
+
await userEvent.keyboard("{end}");
|
|
230
|
+
const options = screen.getAllByRole("option");
|
|
231
|
+
expect(options[options.length - 1]).toHaveAttribute(
|
|
232
|
+
"aria-selected",
|
|
233
|
+
"true",
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe("Home/End with grouped options", () => {
|
|
238
|
+
it("should select the first option of first group when pressing Home", async () => {
|
|
239
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
240
|
+
const input = screen.getByRole("combobox");
|
|
241
|
+
await userEvent.click(input);
|
|
242
|
+
await userEvent.keyboard("{arrowdown}{arrowdown}");
|
|
243
|
+
await userEvent.keyboard("{home}");
|
|
244
|
+
const firstGroup = screen.getAllByRole("group")[0];
|
|
245
|
+
const firstOption = within(firstGroup).getAllByRole("option")[0];
|
|
246
|
+
expect(firstOption).toHaveAttribute("aria-selected", "true");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("should select the last option of last group when pressing End", async () => {
|
|
250
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
251
|
+
const input = screen.getByRole("combobox");
|
|
252
|
+
await userEvent.click(input);
|
|
253
|
+
await userEvent.keyboard("{end}");
|
|
254
|
+
const lastGroup =
|
|
255
|
+
screen.getAllByRole("group")[FRUITS_GROUPS.length - 1];
|
|
256
|
+
const options = within(lastGroup).getAllByRole("option");
|
|
257
|
+
const lastOption = options[options.length - 1];
|
|
258
|
+
expect(lastOption).toHaveAttribute("aria-selected", "true");
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it.todo("should select the first option when pressing Home");
|
|
263
|
+
it.todo("should select the last option when pressing End");
|
|
188
264
|
});
|
|
189
265
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
266
|
+
describe("filtering", () => {
|
|
267
|
+
it("should render all of the options in the listbox", async () => {
|
|
268
|
+
render(<Combobox options={FRUITS} />);
|
|
269
|
+
const input = screen.getByRole("combobox");
|
|
270
|
+
await userEvent.click(input);
|
|
271
|
+
const options = screen.getAllByRole("option");
|
|
272
|
+
expect(options).toHaveLength(FRUITS.length);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should filter the options based on the input value", async () => {
|
|
276
|
+
render(<Combobox options={FRUITS} />);
|
|
277
|
+
const input = screen.getByRole("combobox");
|
|
278
|
+
await userEvent.type(input, "Ap");
|
|
279
|
+
const options = screen.getAllByRole("option");
|
|
280
|
+
expect(options).toHaveLength(6);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("should ignore case when filtering options", async () => {
|
|
284
|
+
render(<Combobox options={FRUITS} />);
|
|
285
|
+
const input = screen.getByRole("combobox");
|
|
286
|
+
await userEvent.type(input, "AP");
|
|
287
|
+
const options = screen.getAllByRole("option");
|
|
288
|
+
expect(options).toHaveLength(6);
|
|
289
|
+
});
|
|
198
290
|
});
|
|
199
291
|
|
|
200
292
|
describe("classnames", () => {
|
|
@@ -235,6 +327,15 @@ describe("Combobox with string options", () => {
|
|
|
235
327
|
const options = screen.getAllByRole("option");
|
|
236
328
|
expect(options[0]).toHaveClass("mobius-combobox__option--is-highlighted");
|
|
237
329
|
});
|
|
330
|
+
|
|
331
|
+
it("should support custom class names", async () => {
|
|
332
|
+
const customClassName = "my-class";
|
|
333
|
+
const { container } = render(
|
|
334
|
+
<Combobox options={FRUITS} className={customClassName} />,
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
expect(container.firstChild).toHaveClass(customClassName);
|
|
338
|
+
});
|
|
238
339
|
});
|
|
239
340
|
|
|
240
341
|
describe("ARIA", () => {
|
|
@@ -271,7 +372,7 @@ describe("Combobox with string options", () => {
|
|
|
271
372
|
expect(input).toHaveAttribute("aria-expanded", "true");
|
|
272
373
|
});
|
|
273
374
|
|
|
274
|
-
it("should set aria-selected to false for all options", async () => {
|
|
375
|
+
it("should set aria-selected to false for all options except first option", async () => {
|
|
275
376
|
render(<Combobox options={FRUITS} />);
|
|
276
377
|
const input = screen.getByRole("combobox");
|
|
277
378
|
await userEvent.click(input);
|
|
@@ -286,17 +387,11 @@ describe("Combobox with string options", () => {
|
|
|
286
387
|
const input = screen.getByRole("combobox");
|
|
287
388
|
await userEvent.click(input);
|
|
288
389
|
const options = screen.getAllByRole("option");
|
|
289
|
-
await userEvent.keyboard("{arrowdown}
|
|
290
|
-
expect(options[
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it("should set aria-activedescendant to undefined by default", () => {
|
|
294
|
-
render(<Combobox options={FRUITS} />);
|
|
295
|
-
const input = screen.getByRole("combobox");
|
|
296
|
-
expect(input).not.toHaveAttribute("aria-activedescendant");
|
|
390
|
+
await userEvent.keyboard("{arrowdown}");
|
|
391
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
|
297
392
|
});
|
|
298
393
|
|
|
299
|
-
it("should set aria-activedescendant based on the highlightedIndex", async () => {
|
|
394
|
+
it("should set aria-activedescendant based on the highlightedIndex and highlightedGroupIndex", async () => {
|
|
300
395
|
render(<Combobox options={FRUITS} />);
|
|
301
396
|
const input = screen.getByRole("combobox");
|
|
302
397
|
await userEvent.tab();
|
|
@@ -311,22 +406,23 @@ describe("Combobox with string options", () => {
|
|
|
311
406
|
});
|
|
312
407
|
|
|
313
408
|
describe("Combobox with object options", () => {
|
|
409
|
+
// Basic rendering and setup
|
|
314
410
|
it("should render without crashing", () => {
|
|
315
411
|
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
316
412
|
});
|
|
317
413
|
|
|
318
|
-
it("should use the default value provided", () => {
|
|
319
|
-
render(<Combobox options={FRUITS_OBJECTS} defaultValue="apple" />);
|
|
320
|
-
const input = screen.getByRole("combobox") as HTMLInputElement;
|
|
321
|
-
expect(input.value).toBe("apple");
|
|
322
|
-
});
|
|
323
|
-
|
|
324
414
|
it("should render a combobox", () => {
|
|
325
415
|
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
326
416
|
const input = screen.getByRole("combobox");
|
|
327
417
|
expect(input).toBeInTheDocument();
|
|
328
418
|
});
|
|
329
419
|
|
|
420
|
+
it("should use the default value provided", () => {
|
|
421
|
+
render(<Combobox options={FRUITS_OBJECTS} defaultValue="apple" />);
|
|
422
|
+
const input = screen.getByRole("combobox") as HTMLInputElement;
|
|
423
|
+
expect(input.value).toBe("apple");
|
|
424
|
+
});
|
|
425
|
+
|
|
330
426
|
it("should accept a ref and forward it to the input", () => {
|
|
331
427
|
const ref = { current: null };
|
|
332
428
|
render(<Combobox options={FRUITS_OBJECTS} ref={ref} />);
|
|
@@ -339,169 +435,220 @@ describe("Combobox with object options", () => {
|
|
|
339
435
|
expect(listbox).not.toBeInTheDocument();
|
|
340
436
|
});
|
|
341
437
|
|
|
342
|
-
|
|
343
|
-
render(
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
438
|
+
describe("mouse interactions", () => {
|
|
439
|
+
it("should render a listbox when the input is focused", async () => {
|
|
440
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
441
|
+
const input = screen.getByRole("combobox");
|
|
442
|
+
await userEvent.click(input);
|
|
443
|
+
const listbox = screen.getByRole("listbox");
|
|
444
|
+
expect(listbox).toBeInTheDocument();
|
|
445
|
+
});
|
|
349
446
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
447
|
+
it("should call the onSelected callback when an option is selected", async () => {
|
|
448
|
+
const onSelected = jest.fn();
|
|
449
|
+
render(<Combobox options={FRUITS_OBJECTS} onSelected={onSelected} />);
|
|
450
|
+
const input = screen.getByRole("combobox");
|
|
451
|
+
await userEvent.click(input);
|
|
452
|
+
const options = screen.getAllByRole("option");
|
|
453
|
+
await userEvent.click(options[0]);
|
|
454
|
+
expect(onSelected).toHaveBeenCalledWith(FRUITS_OBJECTS[0].value);
|
|
455
|
+
});
|
|
357
456
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
});
|
|
457
|
+
it("should update the input value when an option is selected", async () => {
|
|
458
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
459
|
+
const input = screen.getByRole("combobox");
|
|
460
|
+
await userEvent.click(input);
|
|
461
|
+
const options = screen.getAllByRole("option");
|
|
462
|
+
await userEvent.click(options[0]);
|
|
463
|
+
expect(input).toHaveValue(FRUITS_OBJECTS[0].value);
|
|
464
|
+
});
|
|
367
465
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
466
|
+
it("should close the listbox when the input is blurred", async () => {
|
|
467
|
+
jest.useFakeTimers({
|
|
468
|
+
advanceTimers: true,
|
|
469
|
+
timerLimit: 1000,
|
|
470
|
+
});
|
|
471
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
472
|
+
const input = screen.getByRole("combobox");
|
|
473
|
+
await userEvent.click(input);
|
|
474
|
+
await act(async () => {
|
|
475
|
+
await userEvent.tab();
|
|
476
|
+
jest.advanceTimersByTime(200);
|
|
477
|
+
});
|
|
478
|
+
const listbox = screen.queryByRole("listbox");
|
|
479
|
+
expect(listbox).not.toBeInTheDocument();
|
|
480
|
+
});
|
|
376
481
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
482
|
+
it("should close the listbox when an option is selected", async () => {
|
|
483
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
484
|
+
const input = screen.getByRole("combobox");
|
|
485
|
+
await userEvent.click(input);
|
|
486
|
+
const options = screen.getAllByRole("option");
|
|
487
|
+
await userEvent.click(options[0]);
|
|
488
|
+
const listbox = screen.queryByRole("listbox");
|
|
489
|
+
expect(listbox).not.toBeInTheDocument();
|
|
490
|
+
});
|
|
385
491
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
492
|
+
it("should close the listbox when clicking outside", async () => {
|
|
493
|
+
jest.useFakeTimers({
|
|
494
|
+
advanceTimers: true,
|
|
495
|
+
timerLimit: 1000,
|
|
496
|
+
});
|
|
497
|
+
render(
|
|
498
|
+
<div>
|
|
499
|
+
<Combobox options={FRUITS_OBJECTS} />
|
|
500
|
+
<button type="button">Outside</button>
|
|
501
|
+
</div>,
|
|
502
|
+
);
|
|
503
|
+
const input = screen.getByRole("combobox");
|
|
504
|
+
const outside = screen.getByRole("button");
|
|
505
|
+
await userEvent.click(input);
|
|
506
|
+
await userEvent.click(outside);
|
|
507
|
+
await act(async () => {
|
|
508
|
+
await userEvent.tab();
|
|
509
|
+
jest.advanceTimersByTime(200);
|
|
510
|
+
});
|
|
511
|
+
const listbox = screen.queryByRole("listbox");
|
|
512
|
+
expect(listbox).not.toBeInTheDocument();
|
|
513
|
+
});
|
|
394
514
|
});
|
|
395
515
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
516
|
+
describe("keyboard interactions", () => {
|
|
517
|
+
it("should select the top option when pressing Enter", async () => {
|
|
518
|
+
const onSelected = jest.fn();
|
|
519
|
+
render(<Combobox options={FRUITS_OBJECTS} onSelected={onSelected} />);
|
|
520
|
+
const input = screen.getByRole("combobox");
|
|
521
|
+
await userEvent.type(input, "Ap");
|
|
522
|
+
await userEvent.type(input, "{enter}");
|
|
523
|
+
expect(onSelected).toHaveBeenCalledWith(FRUITS_OBJECTS[0].value);
|
|
524
|
+
});
|
|
403
525
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
526
|
+
it("should not select an option when pressing Enter with no options", async () => {
|
|
527
|
+
const onSelected = jest.fn();
|
|
528
|
+
render(<Combobox options={FRUITS_OBJECTS} onSelected={onSelected} />);
|
|
529
|
+
const input = screen.getByRole("combobox");
|
|
530
|
+
await userEvent.type(input, "Zz");
|
|
531
|
+
await userEvent.type(input, "{enter}");
|
|
532
|
+
expect(onSelected).not.toHaveBeenCalled();
|
|
533
|
+
});
|
|
411
534
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
535
|
+
it("should select the highlighted option when pressing Enter", async () => {
|
|
536
|
+
const onSelected = jest.fn();
|
|
537
|
+
render(<Combobox options={FRUITS_OBJECTS} onSelected={onSelected} />);
|
|
538
|
+
const input = screen.getByRole("combobox");
|
|
539
|
+
await userEvent.click(input);
|
|
540
|
+
await userEvent.keyboard("{arrowdown}");
|
|
541
|
+
await userEvent.keyboard("{enter}");
|
|
542
|
+
expect(onSelected).toHaveBeenCalledWith(FRUITS_OBJECTS[0].value);
|
|
543
|
+
});
|
|
420
544
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
545
|
+
it("should open the listbox when pressing ArrowDown", async () => {
|
|
546
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
547
|
+
screen.getByRole("combobox");
|
|
548
|
+
await userEvent.tab();
|
|
549
|
+
await userEvent.keyboard("{arrowdown}");
|
|
550
|
+
const listbox = screen.getByRole("listbox");
|
|
551
|
+
expect(listbox).toBeInTheDocument();
|
|
552
|
+
});
|
|
429
553
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
});
|
|
554
|
+
it("should highlight the second option when pressing ArrowDown", async () => {
|
|
555
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
556
|
+
screen.getByRole("combobox");
|
|
557
|
+
await userEvent.tab();
|
|
558
|
+
await userEvent.keyboard("{arrowdown}");
|
|
559
|
+
const options = screen.getAllByRole("option");
|
|
560
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
|
561
|
+
});
|
|
439
562
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
563
|
+
it("should open the listbox when pressing ArrowUp", async () => {
|
|
564
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
565
|
+
screen.getByRole("combobox");
|
|
566
|
+
await userEvent.tab();
|
|
567
|
+
await userEvent.keyboard("{arrowup}");
|
|
568
|
+
const listbox = screen.getByRole("listbox");
|
|
569
|
+
expect(listbox).toBeInTheDocument();
|
|
570
|
+
});
|
|
448
571
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
572
|
+
it("should select the first option when pressing Home", async () => {
|
|
573
|
+
const onSelected = jest.fn();
|
|
574
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
575
|
+
const input = screen.getByRole("combobox");
|
|
576
|
+
await userEvent.click(input);
|
|
577
|
+
await userEvent.keyboard("{arrowdown}{arrowdown}");
|
|
578
|
+
await userEvent.keyboard("{home}");
|
|
579
|
+
const options = screen.getAllByRole("option");
|
|
580
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
|
581
|
+
});
|
|
457
582
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
583
|
+
it("should select the last option when pressing End", async () => {
|
|
584
|
+
const onSelected = jest.fn();
|
|
585
|
+
render(<Combobox options={FRUITS} onSelected={onSelected} />);
|
|
586
|
+
const input = screen.getByRole("combobox");
|
|
587
|
+
await userEvent.click(input);
|
|
588
|
+
await userEvent.keyboard("{end}");
|
|
589
|
+
const options = screen.getAllByRole("option");
|
|
590
|
+
expect(options[options.length - 1]).toHaveAttribute(
|
|
591
|
+
"aria-selected",
|
|
592
|
+
"true",
|
|
593
|
+
);
|
|
594
|
+
});
|
|
466
595
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
screen.getByRole("combobox");
|
|
470
|
-
await userEvent.tab();
|
|
471
|
-
await userEvent.keyboard("{arrowup}");
|
|
472
|
-
const options = screen.getAllByRole("option");
|
|
473
|
-
expect(options[options.length - 1]).toHaveAttribute(
|
|
474
|
-
"aria-selected",
|
|
475
|
-
"true",
|
|
476
|
-
);
|
|
477
|
-
});
|
|
596
|
+
it.todo("should select the first option when pressing Home");
|
|
597
|
+
it.todo("should select the last option when pressing End");
|
|
478
598
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
599
|
+
it("should close the listbox when pressing Escape", async () => {
|
|
600
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
601
|
+
const input = screen.getByRole("combobox");
|
|
602
|
+
await userEvent.click(input);
|
|
603
|
+
await userEvent.keyboard("{escape}");
|
|
604
|
+
const listbox = screen.queryByRole("listbox");
|
|
605
|
+
expect(listbox).not.toBeInTheDocument();
|
|
606
|
+
});
|
|
487
607
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
608
|
+
it("should not call onSelected when pressing Escape", async () => {
|
|
609
|
+
const onSelected = jest.fn();
|
|
610
|
+
render(<Combobox options={FRUITS_OBJECTS} onSelected={onSelected} />);
|
|
611
|
+
const input = screen.getByRole("combobox");
|
|
612
|
+
await userEvent.click(input);
|
|
613
|
+
await userEvent.keyboard("{escape}");
|
|
614
|
+
expect(onSelected).not.toHaveBeenCalled();
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it("should reset the highlighted index when the input value changes", async () => {
|
|
618
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
619
|
+
const input = screen.getByRole("combobox");
|
|
620
|
+
await userEvent.click(input);
|
|
621
|
+
await userEvent.keyboard("{arrowdown}");
|
|
622
|
+
await userEvent.type(input, "Ap");
|
|
623
|
+
const options = screen.getAllByRole("option");
|
|
624
|
+
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
|
625
|
+
});
|
|
495
626
|
});
|
|
496
627
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
628
|
+
describe("filtering", () => {
|
|
629
|
+
it("should render all of the options in the listbox", async () => {
|
|
630
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
631
|
+
const input = screen.getByRole("combobox");
|
|
632
|
+
await userEvent.click(input);
|
|
633
|
+
const options = screen.getAllByRole("option");
|
|
634
|
+
expect(options).toHaveLength(FRUITS_OBJECTS.length);
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("should filter the options based on the input value", async () => {
|
|
638
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
639
|
+
const input = screen.getByRole("combobox");
|
|
640
|
+
await userEvent.type(input, "Ap");
|
|
641
|
+
const options = screen.getAllByRole("option");
|
|
642
|
+
expect(options).toHaveLength(6);
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
it("should ignore case when filtering options", async () => {
|
|
646
|
+
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
647
|
+
const input = screen.getByRole("combobox");
|
|
648
|
+
await userEvent.type(input, "AP");
|
|
649
|
+
const options = screen.getAllByRole("option");
|
|
650
|
+
expect(options).toHaveLength(6);
|
|
651
|
+
});
|
|
505
652
|
});
|
|
506
653
|
|
|
507
654
|
describe("ARIA", () => {
|
|
@@ -557,12 +704,6 @@ describe("Combobox with object options", () => {
|
|
|
557
704
|
expect(options[1]).toHaveAttribute("aria-selected", "true");
|
|
558
705
|
});
|
|
559
706
|
|
|
560
|
-
it("should set aria-activedescendant to undefined by default", () => {
|
|
561
|
-
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
562
|
-
const input = screen.getByRole("combobox");
|
|
563
|
-
expect(input).not.toHaveAttribute("aria-activedescendant");
|
|
564
|
-
});
|
|
565
|
-
|
|
566
707
|
it("should set aria-activedescendant based on the highlightedIndex", async () => {
|
|
567
708
|
render(<Combobox options={FRUITS_OBJECTS} />);
|
|
568
709
|
const input = screen.getByRole("combobox");
|
|
@@ -576,3 +717,81 @@ describe("Combobox with object options", () => {
|
|
|
576
717
|
});
|
|
577
718
|
});
|
|
578
719
|
});
|
|
720
|
+
|
|
721
|
+
describe("Combobox with grouped options", () => {
|
|
722
|
+
// Basic rendering and setup
|
|
723
|
+
it("should render without crashing", () => {
|
|
724
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
describe("rendering", () => {
|
|
728
|
+
it("should render a listbox when the input is focused", async () => {
|
|
729
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
730
|
+
const input = screen.getByRole("combobox");
|
|
731
|
+
await userEvent.click(input);
|
|
732
|
+
const listbox = screen.getByRole("listbox");
|
|
733
|
+
expect(listbox).toBeInTheDocument();
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it("should render all of the groups in the listbox", async () => {
|
|
737
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
738
|
+
const input = screen.getByRole("combobox");
|
|
739
|
+
await userEvent.click(input);
|
|
740
|
+
const groups = screen.getAllByRole("group");
|
|
741
|
+
expect(groups).toHaveLength(FRUITS_GROUPS.length);
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it("should render all of the options in each group", async () => {
|
|
745
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
746
|
+
const input = screen.getByRole("combobox");
|
|
747
|
+
await userEvent.click(input);
|
|
748
|
+
FRUITS_GROUPS.forEach(group => {
|
|
749
|
+
const groupElement = screen.getByRole("group", { name: group.heading });
|
|
750
|
+
const options = within(groupElement).queryAllByRole("option");
|
|
751
|
+
expect(options).toHaveLength(group.options.length);
|
|
752
|
+
});
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
describe("filtering", () => {
|
|
757
|
+
it("should filter the options based on the input value", async () => {
|
|
758
|
+
render(<Combobox options={FRUITS} />);
|
|
759
|
+
const input = screen.getByRole("combobox");
|
|
760
|
+
await userEvent.type(input, "Ap");
|
|
761
|
+
const options = screen.getAllByRole("option");
|
|
762
|
+
expect(options).toHaveLength(6);
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it("should ignore case when filtering options", async () => {
|
|
766
|
+
render(<Combobox options={FRUITS} />);
|
|
767
|
+
const input = screen.getByRole("combobox");
|
|
768
|
+
await userEvent.type(input, "AP");
|
|
769
|
+
const options = screen.getAllByRole("option");
|
|
770
|
+
expect(options).toHaveLength(6);
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
describe("classnames", () => {
|
|
775
|
+
it("should have the correct classnames for the groups", async () => {
|
|
776
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
777
|
+
const input = screen.getByRole("combobox");
|
|
778
|
+
await userEvent.click(input);
|
|
779
|
+
const groups = screen.getAllByRole("group");
|
|
780
|
+
groups.forEach(group => {
|
|
781
|
+
expect(group).toHaveClass("mobius-combobox__group");
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
it("should have the correct classnames for the group labels", async () => {
|
|
786
|
+
render(<Combobox options={FRUITS_GROUPS} />);
|
|
787
|
+
const input = screen.getByRole("combobox");
|
|
788
|
+
await userEvent.click(input);
|
|
789
|
+
const groupLabels = screen
|
|
790
|
+
.getAllByRole("group")
|
|
791
|
+
.map(group => group.firstChild);
|
|
792
|
+
groupLabels.forEach(label => {
|
|
793
|
+
expect(label).toHaveClass("mobius-combobox__group-label");
|
|
794
|
+
});
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
});
|