@khanacademy/wonder-blocks-accordion 1.3.7 → 1.3.8
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 +12 -0
- package/dist/components/accordion-section.d.ts +2 -2
- package/dist/components/accordion.d.ts +2 -2
- package/package.json +6 -6
- package/src/components/__tests__/accordion-section-header.test.tsx +0 -211
- package/src/components/__tests__/accordion-section.test.tsx +0 -361
- package/src/components/__tests__/accordion.test.tsx +0 -956
- package/src/components/accordion-section-header.tsx +0 -271
- package/src/components/accordion-section.tsx +0 -432
- package/src/components/accordion.tsx +0 -295
- package/src/index.ts +0 -4
- package/src/utils.test.ts +0 -59
- package/src/utils.ts +0 -58
- package/tsconfig-build.json +0 -15
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,956 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {render, screen} from "@testing-library/react";
|
|
3
|
-
import {userEvent} from "@testing-library/user-event";
|
|
4
|
-
|
|
5
|
-
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
|
|
6
|
-
|
|
7
|
-
import Accordion from "../accordion";
|
|
8
|
-
import AccordionSection from "../accordion-section";
|
|
9
|
-
|
|
10
|
-
describe("Accordion", () => {
|
|
11
|
-
test("renders", async () => {
|
|
12
|
-
// Arrange
|
|
13
|
-
|
|
14
|
-
// Act
|
|
15
|
-
render(
|
|
16
|
-
<Accordion>
|
|
17
|
-
<AccordionSection header="Section 1">
|
|
18
|
-
Section 1 content
|
|
19
|
-
</AccordionSection>
|
|
20
|
-
<AccordionSection header="Section 2">
|
|
21
|
-
Section 2 content
|
|
22
|
-
</AccordionSection>
|
|
23
|
-
</Accordion>,
|
|
24
|
-
{wrapper: RenderStateRoot},
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
// Assert
|
|
28
|
-
expect(await screen.findByText("Section 1")).toBeVisible();
|
|
29
|
-
expect(await screen.findByText("Section 2")).toBeVisible();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("opens sections when clicked", async () => {
|
|
33
|
-
// Arrange
|
|
34
|
-
render(
|
|
35
|
-
<Accordion>
|
|
36
|
-
<AccordionSection header="Section 1">
|
|
37
|
-
Section 1 content
|
|
38
|
-
</AccordionSection>
|
|
39
|
-
<AccordionSection header="Section 2">
|
|
40
|
-
Section 2 content
|
|
41
|
-
</AccordionSection>
|
|
42
|
-
</Accordion>,
|
|
43
|
-
{wrapper: RenderStateRoot},
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const button1 = await screen.findByRole("button", {name: "Section 1"});
|
|
47
|
-
const button2 = await screen.findByRole("button", {name: "Section 2"});
|
|
48
|
-
|
|
49
|
-
// Act
|
|
50
|
-
button1.click();
|
|
51
|
-
button2.click();
|
|
52
|
-
|
|
53
|
-
// Assert
|
|
54
|
-
expect(await screen.findByText("Section 1 content")).toBeVisible();
|
|
55
|
-
expect(await screen.findByText("Section 2 content")).toBeVisible();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("closes sections when clicked", async () => {
|
|
59
|
-
// Arrange
|
|
60
|
-
render(
|
|
61
|
-
<Accordion>
|
|
62
|
-
<AccordionSection header="Section 1">
|
|
63
|
-
Section 1 content
|
|
64
|
-
</AccordionSection>
|
|
65
|
-
<AccordionSection header="Section 2">
|
|
66
|
-
Section 2 content
|
|
67
|
-
</AccordionSection>
|
|
68
|
-
</Accordion>,
|
|
69
|
-
{wrapper: RenderStateRoot},
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const button1 = await screen.findByRole("button", {name: "Section 1"});
|
|
73
|
-
const button2 = await screen.findByRole("button", {name: "Section 2"});
|
|
74
|
-
|
|
75
|
-
// Act
|
|
76
|
-
// open
|
|
77
|
-
button1.click();
|
|
78
|
-
button2.click();
|
|
79
|
-
// close
|
|
80
|
-
button1.click();
|
|
81
|
-
button2.click();
|
|
82
|
-
|
|
83
|
-
// Assert
|
|
84
|
-
expect(screen.queryByText("Section 1 content")).not.toBeVisible();
|
|
85
|
-
expect(screen.queryByText("Section 2 content")).not.toBeVisible();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("initialExpandedIndex opens the correct section", async () => {
|
|
89
|
-
// Arrange
|
|
90
|
-
render(
|
|
91
|
-
<Accordion initialExpandedIndex={1}>
|
|
92
|
-
<AccordionSection header="Section 1">
|
|
93
|
-
Section 1 content
|
|
94
|
-
</AccordionSection>
|
|
95
|
-
<AccordionSection header="Section 2">
|
|
96
|
-
Section 2 content
|
|
97
|
-
</AccordionSection>
|
|
98
|
-
<AccordionSection header="Section 3">
|
|
99
|
-
Section 3 content
|
|
100
|
-
</AccordionSection>
|
|
101
|
-
</Accordion>,
|
|
102
|
-
{wrapper: RenderStateRoot},
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// Act
|
|
106
|
-
// Assert
|
|
107
|
-
expect(screen.queryByText("Section 1 content")).not.toBeVisible();
|
|
108
|
-
expect(await screen.findByText("Section 2 content")).toBeVisible();
|
|
109
|
-
expect(screen.queryByText("Section 3 content")).not.toBeVisible();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("only allows one section to be open at a time when allowMultipleExpanded is false", async () => {
|
|
113
|
-
// Arrange
|
|
114
|
-
render(
|
|
115
|
-
<Accordion initialExpandedIndex={1} allowMultipleExpanded={false}>
|
|
116
|
-
<AccordionSection header="Section 1">
|
|
117
|
-
Section 1 content
|
|
118
|
-
</AccordionSection>
|
|
119
|
-
<AccordionSection header="Section 2">
|
|
120
|
-
Section 2 content
|
|
121
|
-
</AccordionSection>
|
|
122
|
-
<AccordionSection header="Section 3">
|
|
123
|
-
Section 3 content
|
|
124
|
-
</AccordionSection>
|
|
125
|
-
</Accordion>,
|
|
126
|
-
{wrapper: RenderStateRoot},
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
// Act
|
|
130
|
-
const button = await screen.findByRole("button", {name: "Section 3"});
|
|
131
|
-
button.click();
|
|
132
|
-
|
|
133
|
-
// Assert
|
|
134
|
-
expect(screen.queryByText("Section 1 content")).not.toBeVisible();
|
|
135
|
-
expect(screen.queryByText("Section 2 content")).not.toBeVisible();
|
|
136
|
-
expect(await screen.findByText("Section 3 content")).toBeVisible();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("calls child's onToggle when section is clicked", async () => {
|
|
140
|
-
// Arrange
|
|
141
|
-
const onToggleSpy = jest.fn();
|
|
142
|
-
render(
|
|
143
|
-
<Accordion>
|
|
144
|
-
<AccordionSection header="Section 1" onToggle={onToggleSpy}>
|
|
145
|
-
Section 1 content
|
|
146
|
-
</AccordionSection>
|
|
147
|
-
<AccordionSection header="Section 2">
|
|
148
|
-
Section 2 content
|
|
149
|
-
</AccordionSection>
|
|
150
|
-
</Accordion>,
|
|
151
|
-
{wrapper: RenderStateRoot},
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
const button = await screen.findByRole("button", {name: "Section 1"});
|
|
155
|
-
|
|
156
|
-
// Act
|
|
157
|
-
button.click();
|
|
158
|
-
|
|
159
|
-
// Assert
|
|
160
|
-
expect(onToggleSpy).toHaveBeenCalledTimes(1);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("Other props are passed to the section", async () => {
|
|
164
|
-
// Arrange
|
|
165
|
-
render(
|
|
166
|
-
<Accordion>
|
|
167
|
-
<AccordionSection header="Section 1" testId="test-id-1">
|
|
168
|
-
Section 1 content
|
|
169
|
-
</AccordionSection>
|
|
170
|
-
<AccordionSection header="Section 2" testId="test-id-2">
|
|
171
|
-
Section 2 content
|
|
172
|
-
</AccordionSection>
|
|
173
|
-
</Accordion>,
|
|
174
|
-
{wrapper: RenderStateRoot},
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
// Act
|
|
178
|
-
const header1 = await screen.findByTestId("test-id-1-header");
|
|
179
|
-
const header2 = await screen.findByTestId("test-id-2-header");
|
|
180
|
-
|
|
181
|
-
// Assert
|
|
182
|
-
expect(header1).toBeVisible();
|
|
183
|
-
expect(header2).toBeVisible();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test("Styles the corners based on the square cornerKind", async () => {
|
|
187
|
-
// Arrange
|
|
188
|
-
render(
|
|
189
|
-
<Accordion cornerKind="square">
|
|
190
|
-
{[
|
|
191
|
-
<AccordionSection
|
|
192
|
-
key="section-id"
|
|
193
|
-
header="Title"
|
|
194
|
-
testId="section-test-id"
|
|
195
|
-
>
|
|
196
|
-
Section content
|
|
197
|
-
</AccordionSection>,
|
|
198
|
-
]}
|
|
199
|
-
</Accordion>,
|
|
200
|
-
{wrapper: RenderStateRoot},
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
// Act
|
|
204
|
-
const section = await screen.findByTestId("section-test-id");
|
|
205
|
-
|
|
206
|
-
// Assert
|
|
207
|
-
expect(section).toHaveStyle({
|
|
208
|
-
borderRadius: 0,
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
test("Styles the corners based on the rounded cornerKind", async () => {
|
|
213
|
-
// Arrange
|
|
214
|
-
render(
|
|
215
|
-
<Accordion cornerKind="rounded">
|
|
216
|
-
{[
|
|
217
|
-
<AccordionSection
|
|
218
|
-
key="section-id"
|
|
219
|
-
header="Title"
|
|
220
|
-
testId="section-test-id"
|
|
221
|
-
>
|
|
222
|
-
Section content
|
|
223
|
-
</AccordionSection>,
|
|
224
|
-
]}
|
|
225
|
-
</Accordion>,
|
|
226
|
-
{wrapper: RenderStateRoot},
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
// Act
|
|
230
|
-
const section = await screen.findByTestId("section-test-id");
|
|
231
|
-
|
|
232
|
-
// Assert
|
|
233
|
-
expect(section).toHaveStyle({
|
|
234
|
-
borderStartStartRadius: "12px",
|
|
235
|
-
borderStartEndRadius: "12px",
|
|
236
|
-
borderEndStartRadius: "12px",
|
|
237
|
-
borderEndEndRadius: "12px",
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
test("Styles the corners based on the rounded-per-section cornerKind", async () => {
|
|
242
|
-
// Arrange
|
|
243
|
-
render(
|
|
244
|
-
<Accordion cornerKind="rounded-per-section">
|
|
245
|
-
{[
|
|
246
|
-
<AccordionSection
|
|
247
|
-
key="section-id"
|
|
248
|
-
header="Title"
|
|
249
|
-
testId="section-test-id"
|
|
250
|
-
>
|
|
251
|
-
Section content
|
|
252
|
-
</AccordionSection>,
|
|
253
|
-
]}
|
|
254
|
-
</Accordion>,
|
|
255
|
-
{wrapper: RenderStateRoot},
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// Act
|
|
259
|
-
const section = await screen.findByTestId("section-test-id");
|
|
260
|
-
|
|
261
|
-
// Assert
|
|
262
|
-
expect(section).toHaveStyle({
|
|
263
|
-
borderRadius: "12px",
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test("prioritizes the child's cornerKind prop", async () => {
|
|
268
|
-
// Arrange
|
|
269
|
-
render(
|
|
270
|
-
<Accordion cornerKind="square">
|
|
271
|
-
{[
|
|
272
|
-
<AccordionSection
|
|
273
|
-
key="section-id"
|
|
274
|
-
header="Title"
|
|
275
|
-
cornerKind="rounded-per-section"
|
|
276
|
-
testId="section-test-id"
|
|
277
|
-
>
|
|
278
|
-
Section content
|
|
279
|
-
</AccordionSection>,
|
|
280
|
-
]}
|
|
281
|
-
</Accordion>,
|
|
282
|
-
{wrapper: RenderStateRoot},
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
// Act
|
|
286
|
-
const section = await screen.findByTestId("section-test-id");
|
|
287
|
-
|
|
288
|
-
// Assert
|
|
289
|
-
expect(section).toHaveStyle({
|
|
290
|
-
borderRadius: "12px",
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test("puts the caret first when caretPosition is start", async () => {
|
|
295
|
-
// Arrange
|
|
296
|
-
render(
|
|
297
|
-
<Accordion caretPosition="start">
|
|
298
|
-
{[
|
|
299
|
-
<AccordionSection
|
|
300
|
-
key="section-id"
|
|
301
|
-
header="Title"
|
|
302
|
-
testId="section-test-id"
|
|
303
|
-
>
|
|
304
|
-
Section content
|
|
305
|
-
</AccordionSection>,
|
|
306
|
-
]}
|
|
307
|
-
</Accordion>,
|
|
308
|
-
{wrapper: RenderStateRoot},
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
// Act
|
|
312
|
-
const sectionHeader = await screen.findByTestId(
|
|
313
|
-
"section-test-id-header",
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
// Assert
|
|
317
|
-
expect(sectionHeader).toHaveStyle({
|
|
318
|
-
flexDirection: "row-reverse",
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
test("puts the caret last when caretPosition is end", async () => {
|
|
323
|
-
// Arrange
|
|
324
|
-
render(
|
|
325
|
-
<Accordion caretPosition="end">
|
|
326
|
-
{[
|
|
327
|
-
<AccordionSection
|
|
328
|
-
key="section-id"
|
|
329
|
-
header="Title"
|
|
330
|
-
testId="section-test-id"
|
|
331
|
-
>
|
|
332
|
-
Section content
|
|
333
|
-
</AccordionSection>,
|
|
334
|
-
]}
|
|
335
|
-
</Accordion>,
|
|
336
|
-
{wrapper: RenderStateRoot},
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
// Act
|
|
340
|
-
const sectionHeader = await screen.findByTestId(
|
|
341
|
-
"section-test-id-header",
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
// Assert
|
|
345
|
-
expect(sectionHeader).toHaveStyle({
|
|
346
|
-
flexDirection: "row",
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
test("prioritizes the child's caretPosition prop", async () => {
|
|
351
|
-
// Arrange
|
|
352
|
-
render(
|
|
353
|
-
<Accordion caretPosition="end">
|
|
354
|
-
{[
|
|
355
|
-
<AccordionSection
|
|
356
|
-
key="section-id"
|
|
357
|
-
header="Title"
|
|
358
|
-
caretPosition="start"
|
|
359
|
-
testId="section-test-id"
|
|
360
|
-
>
|
|
361
|
-
Section content
|
|
362
|
-
</AccordionSection>,
|
|
363
|
-
]}
|
|
364
|
-
</Accordion>,
|
|
365
|
-
{wrapper: RenderStateRoot},
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
// Act
|
|
369
|
-
const sectionHeader = await screen.findByTestId(
|
|
370
|
-
"section-test-id-header",
|
|
371
|
-
);
|
|
372
|
-
|
|
373
|
-
// Assert
|
|
374
|
-
expect(sectionHeader).toHaveStyle({
|
|
375
|
-
flexDirection: "row-reverse",
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test("prioritizes the child's animated prop", async () => {
|
|
380
|
-
// Arrange
|
|
381
|
-
render(
|
|
382
|
-
<Accordion animated={false}>
|
|
383
|
-
{[
|
|
384
|
-
<AccordionSection
|
|
385
|
-
key="section-id"
|
|
386
|
-
header="Title"
|
|
387
|
-
animated={true}
|
|
388
|
-
testId="section-test-id"
|
|
389
|
-
>
|
|
390
|
-
Section content
|
|
391
|
-
</AccordionSection>,
|
|
392
|
-
]}
|
|
393
|
-
</Accordion>,
|
|
394
|
-
{wrapper: RenderStateRoot},
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
// Act
|
|
398
|
-
const sectionHeader = await screen.findByTestId(
|
|
399
|
-
"section-test-id-header",
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
// Assert
|
|
403
|
-
// The child has animated=true, so the parent's animated=false
|
|
404
|
-
// should be overridden.
|
|
405
|
-
expect(sectionHeader).toHaveStyle({
|
|
406
|
-
// The existence of the transition style means that the
|
|
407
|
-
// accordion is animated.
|
|
408
|
-
transition: "border-radius 300ms",
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
test("applies style to the wrapper", async () => {
|
|
413
|
-
// Arrange
|
|
414
|
-
render(
|
|
415
|
-
<Accordion style={{color: "red"}}>
|
|
416
|
-
<AccordionSection header="Section 1">
|
|
417
|
-
Section 1 content
|
|
418
|
-
</AccordionSection>
|
|
419
|
-
<AccordionSection header="Section 2">
|
|
420
|
-
Section 2 content
|
|
421
|
-
</AccordionSection>
|
|
422
|
-
</Accordion>,
|
|
423
|
-
{wrapper: RenderStateRoot},
|
|
424
|
-
);
|
|
425
|
-
|
|
426
|
-
// Act
|
|
427
|
-
const wrapper = await screen.findByRole("list");
|
|
428
|
-
|
|
429
|
-
// Assert
|
|
430
|
-
expect(wrapper).toHaveStyle({color: "red"});
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
test("applies region role to sections when there are 6 or fewer", async () => {
|
|
434
|
-
// Arrange
|
|
435
|
-
render(
|
|
436
|
-
<Accordion>
|
|
437
|
-
<AccordionSection header="Section 1" testId="section-1">
|
|
438
|
-
Section 1 content
|
|
439
|
-
</AccordionSection>
|
|
440
|
-
<AccordionSection header="Section 2">
|
|
441
|
-
Section 2 content
|
|
442
|
-
</AccordionSection>
|
|
443
|
-
<AccordionSection header="Section 3">
|
|
444
|
-
Section 3 content
|
|
445
|
-
</AccordionSection>
|
|
446
|
-
<AccordionSection header="Section 4">
|
|
447
|
-
Section 4 content
|
|
448
|
-
</AccordionSection>
|
|
449
|
-
<AccordionSection header="Section 5">
|
|
450
|
-
Section 5 content
|
|
451
|
-
</AccordionSection>
|
|
452
|
-
<AccordionSection header="Section 6">
|
|
453
|
-
Section 6 content
|
|
454
|
-
</AccordionSection>
|
|
455
|
-
</Accordion>,
|
|
456
|
-
{wrapper: RenderStateRoot},
|
|
457
|
-
);
|
|
458
|
-
|
|
459
|
-
// Act
|
|
460
|
-
const section1ContentPanel = await screen.findByTestId(
|
|
461
|
-
"section-1-content-panel",
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
// Assert
|
|
465
|
-
expect(section1ContentPanel).toHaveAttribute("role", "region");
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
test("does not apply region role to sections when there are more than 6", async () => {
|
|
469
|
-
// Arrange
|
|
470
|
-
render(
|
|
471
|
-
<Accordion>
|
|
472
|
-
<AccordionSection header="Section 1" testId="section-1">
|
|
473
|
-
Section 1 content
|
|
474
|
-
</AccordionSection>
|
|
475
|
-
<AccordionSection header="Section 2">
|
|
476
|
-
Section 2 content
|
|
477
|
-
</AccordionSection>
|
|
478
|
-
<AccordionSection header="Section 3">
|
|
479
|
-
Section 3 content
|
|
480
|
-
</AccordionSection>
|
|
481
|
-
<AccordionSection header="Section 4">
|
|
482
|
-
Section 4 content
|
|
483
|
-
</AccordionSection>
|
|
484
|
-
<AccordionSection header="Section 5">
|
|
485
|
-
Section 5 content
|
|
486
|
-
</AccordionSection>
|
|
487
|
-
<AccordionSection header="Section 6">
|
|
488
|
-
Section 6 content
|
|
489
|
-
</AccordionSection>
|
|
490
|
-
<AccordionSection header="Section 7">
|
|
491
|
-
Section 7 content
|
|
492
|
-
</AccordionSection>
|
|
493
|
-
</Accordion>,
|
|
494
|
-
{wrapper: RenderStateRoot},
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
// Act
|
|
498
|
-
const section1ContentPanel = await screen.findByTestId(
|
|
499
|
-
"section-1-content-panel",
|
|
500
|
-
);
|
|
501
|
-
|
|
502
|
-
// Assert
|
|
503
|
-
expect(section1ContentPanel).not.toHaveAttribute("role", "region");
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
test("appropriately sets aria-labelledby on the content panel", async () => {
|
|
507
|
-
// Arrange
|
|
508
|
-
render(
|
|
509
|
-
<Accordion>
|
|
510
|
-
<AccordionSection
|
|
511
|
-
id="accordion-section-id-for-test"
|
|
512
|
-
header="Section 1"
|
|
513
|
-
testId="section-1"
|
|
514
|
-
>
|
|
515
|
-
Section 1 content
|
|
516
|
-
</AccordionSection>
|
|
517
|
-
<AccordionSection header="Section 2">
|
|
518
|
-
Section 2 content
|
|
519
|
-
</AccordionSection>
|
|
520
|
-
</Accordion>,
|
|
521
|
-
{wrapper: RenderStateRoot},
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
// Act
|
|
525
|
-
const section1ContentPanel = await screen.findByTestId(
|
|
526
|
-
"section-1-content-panel",
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
// Assert
|
|
530
|
-
expect(section1ContentPanel).toHaveAttribute(
|
|
531
|
-
"aria-labelledby",
|
|
532
|
-
"accordion-section-id-for-test-header",
|
|
533
|
-
);
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
describe("keyboard navigation", () => {
|
|
537
|
-
test("can open a section with the enter key", async () => {
|
|
538
|
-
// Arrange
|
|
539
|
-
render(
|
|
540
|
-
<Accordion>
|
|
541
|
-
<AccordionSection header="Section 1">
|
|
542
|
-
Section 1 content
|
|
543
|
-
</AccordionSection>
|
|
544
|
-
<AccordionSection header="Section 2">
|
|
545
|
-
Section 2 content
|
|
546
|
-
</AccordionSection>
|
|
547
|
-
</Accordion>,
|
|
548
|
-
{wrapper: RenderStateRoot},
|
|
549
|
-
);
|
|
550
|
-
|
|
551
|
-
const button1 = await screen.findByRole("button", {
|
|
552
|
-
name: "Section 1",
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
// Act
|
|
556
|
-
// Confirm that the section is closed.
|
|
557
|
-
expect(screen.queryByText("Section 1 content")).not.toBeVisible();
|
|
558
|
-
|
|
559
|
-
button1.focus();
|
|
560
|
-
await userEvent.keyboard("{enter}");
|
|
561
|
-
|
|
562
|
-
// Assert
|
|
563
|
-
// Confirm that the section is now open.
|
|
564
|
-
expect(await screen.findByText("Section 1 content")).toBeVisible();
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
// TODO(FEI-5533): Key press events aren't working correctly with
|
|
568
|
-
// user-event v14. We need to investigate and fix this.
|
|
569
|
-
test.skip("can open a section with the space key", async () => {
|
|
570
|
-
// Arrange
|
|
571
|
-
render(
|
|
572
|
-
<Accordion>
|
|
573
|
-
<AccordionSection header="Section 1">
|
|
574
|
-
Section 1 content
|
|
575
|
-
</AccordionSection>
|
|
576
|
-
<AccordionSection header="Section 2">
|
|
577
|
-
Section 2 content
|
|
578
|
-
</AccordionSection>
|
|
579
|
-
</Accordion>,
|
|
580
|
-
{wrapper: RenderStateRoot},
|
|
581
|
-
);
|
|
582
|
-
|
|
583
|
-
const button1 = await screen.findByRole("button", {
|
|
584
|
-
name: "Section 1",
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
// Act
|
|
588
|
-
// Confirm that the section is closed.
|
|
589
|
-
expect(screen.queryByText("Section 1 content")).not.toBeVisible();
|
|
590
|
-
|
|
591
|
-
button1.focus();
|
|
592
|
-
await userEvent.keyboard("{space}");
|
|
593
|
-
|
|
594
|
-
// Assert
|
|
595
|
-
// Confirm that the section is now open.
|
|
596
|
-
expect(await screen.findByText("Section 1 content")).toBeVisible();
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
test("can close a section with the enter key", async () => {
|
|
600
|
-
// Arrange
|
|
601
|
-
render(
|
|
602
|
-
<Accordion>
|
|
603
|
-
<AccordionSection header="Section 1">
|
|
604
|
-
Section 1 content
|
|
605
|
-
</AccordionSection>
|
|
606
|
-
<AccordionSection header="Section 2">
|
|
607
|
-
Section 2 content
|
|
608
|
-
</AccordionSection>
|
|
609
|
-
</Accordion>,
|
|
610
|
-
{wrapper: RenderStateRoot},
|
|
611
|
-
);
|
|
612
|
-
|
|
613
|
-
const button1 = await screen.findByRole("button", {
|
|
614
|
-
name: "Section 1",
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
// Act
|
|
618
|
-
// Confirm that the section is open.
|
|
619
|
-
button1.click();
|
|
620
|
-
expect(await screen.findByText("Section 1 content")).toBeVisible();
|
|
621
|
-
|
|
622
|
-
button1.focus();
|
|
623
|
-
await userEvent.keyboard("{enter}");
|
|
624
|
-
|
|
625
|
-
// Assert
|
|
626
|
-
// Confirm that the section is now closed.
|
|
627
|
-
expect(screen.queryByText("Section 1 content")).not.toBeVisible();
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
// TODO(FEI-5533): Key press events aren't working correctly with
|
|
631
|
-
// user-event v14. We need to investigate and fix this.
|
|
632
|
-
test.skip("can close a section with the space key", async () => {
|
|
633
|
-
// Arrange
|
|
634
|
-
render(
|
|
635
|
-
<Accordion>
|
|
636
|
-
<AccordionSection header="Section 1">
|
|
637
|
-
Section 1 content
|
|
638
|
-
</AccordionSection>
|
|
639
|
-
<AccordionSection header="Section 2">
|
|
640
|
-
Section 2 content
|
|
641
|
-
</AccordionSection>
|
|
642
|
-
</Accordion>,
|
|
643
|
-
{wrapper: RenderStateRoot},
|
|
644
|
-
);
|
|
645
|
-
|
|
646
|
-
const button1 = await screen.findByRole("button", {
|
|
647
|
-
name: "Section 1",
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
// Act
|
|
651
|
-
// Confirm that the section is open.
|
|
652
|
-
button1.click();
|
|
653
|
-
expect(await screen.findByText("Section 1 content")).toBeVisible();
|
|
654
|
-
|
|
655
|
-
button1.focus();
|
|
656
|
-
await userEvent.keyboard("{space}");
|
|
657
|
-
|
|
658
|
-
// Assert
|
|
659
|
-
// Confirm that the section is now closed.
|
|
660
|
-
expect(screen.queryByText("Section 1 content")).not.toBeVisible();
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
test("can navigate to the next section with the tab key", async () => {
|
|
664
|
-
// Arrange
|
|
665
|
-
render(
|
|
666
|
-
<Accordion>
|
|
667
|
-
<AccordionSection header="Section 1">
|
|
668
|
-
Section 1 content
|
|
669
|
-
</AccordionSection>
|
|
670
|
-
<AccordionSection header="Section 2">
|
|
671
|
-
Section 2 content
|
|
672
|
-
</AccordionSection>
|
|
673
|
-
</Accordion>,
|
|
674
|
-
{wrapper: RenderStateRoot},
|
|
675
|
-
);
|
|
676
|
-
|
|
677
|
-
const button1 = await screen.findByRole("button", {
|
|
678
|
-
name: "Section 1",
|
|
679
|
-
});
|
|
680
|
-
const button2 = await screen.findByRole("button", {
|
|
681
|
-
name: "Section 2",
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
// Act
|
|
685
|
-
button1.focus();
|
|
686
|
-
await userEvent.tab();
|
|
687
|
-
|
|
688
|
-
// Assert
|
|
689
|
-
expect(button2).toHaveFocus();
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
test("can navigate to the previous section with the shift+tab key", async () => {
|
|
693
|
-
// Arrange
|
|
694
|
-
render(
|
|
695
|
-
<Accordion>
|
|
696
|
-
<AccordionSection header="Section 1">
|
|
697
|
-
Section 1 content
|
|
698
|
-
</AccordionSection>
|
|
699
|
-
<AccordionSection header="Section 2">
|
|
700
|
-
Section 2 content
|
|
701
|
-
</AccordionSection>
|
|
702
|
-
</Accordion>,
|
|
703
|
-
{wrapper: RenderStateRoot},
|
|
704
|
-
);
|
|
705
|
-
|
|
706
|
-
const button1 = await screen.findByRole("button", {
|
|
707
|
-
name: "Section 1",
|
|
708
|
-
});
|
|
709
|
-
const button2 = await screen.findByRole("button", {
|
|
710
|
-
name: "Section 2",
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
// Act
|
|
714
|
-
button2.focus();
|
|
715
|
-
await userEvent.tab({shift: true});
|
|
716
|
-
|
|
717
|
-
// Assert
|
|
718
|
-
expect(button1).toHaveFocus();
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
test("can navigate to the next section with the arrow down key", async () => {
|
|
722
|
-
// Arrange
|
|
723
|
-
render(
|
|
724
|
-
<Accordion>
|
|
725
|
-
<AccordionSection header="Section 1">
|
|
726
|
-
Section 1 content
|
|
727
|
-
</AccordionSection>
|
|
728
|
-
<AccordionSection header="Section 2">
|
|
729
|
-
Section 2 content
|
|
730
|
-
</AccordionSection>
|
|
731
|
-
</Accordion>,
|
|
732
|
-
{wrapper: RenderStateRoot},
|
|
733
|
-
);
|
|
734
|
-
|
|
735
|
-
const button1 = await screen.findByRole("button", {
|
|
736
|
-
name: "Section 1",
|
|
737
|
-
});
|
|
738
|
-
const button2 = await screen.findByRole("button", {
|
|
739
|
-
name: "Section 2",
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
// Act
|
|
743
|
-
button1.focus();
|
|
744
|
-
await userEvent.keyboard("{arrowdown}");
|
|
745
|
-
|
|
746
|
-
// Assert
|
|
747
|
-
expect(button2).toHaveFocus();
|
|
748
|
-
});
|
|
749
|
-
|
|
750
|
-
test("can cycle to the first section with the arrow down key from the last section", async () => {
|
|
751
|
-
// Arrange
|
|
752
|
-
render(
|
|
753
|
-
<Accordion>
|
|
754
|
-
<AccordionSection header="Section 1">
|
|
755
|
-
Section 1 content
|
|
756
|
-
</AccordionSection>
|
|
757
|
-
<AccordionSection header="Section 2" testId="section-2">
|
|
758
|
-
Section 2 content
|
|
759
|
-
</AccordionSection>
|
|
760
|
-
<AccordionSection header="Section 3">
|
|
761
|
-
Section 3 content
|
|
762
|
-
</AccordionSection>
|
|
763
|
-
</Accordion>,
|
|
764
|
-
{wrapper: RenderStateRoot},
|
|
765
|
-
);
|
|
766
|
-
|
|
767
|
-
const button1 = await screen.findByRole("button", {
|
|
768
|
-
name: "Section 1",
|
|
769
|
-
});
|
|
770
|
-
const button3 = await screen.findByRole("button", {
|
|
771
|
-
name: "Section 3",
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
// Act
|
|
775
|
-
button3.focus();
|
|
776
|
-
await userEvent.keyboard("{arrowdown}");
|
|
777
|
-
|
|
778
|
-
// Assert
|
|
779
|
-
expect(button1).toHaveFocus();
|
|
780
|
-
expect(button3).not.toHaveFocus();
|
|
781
|
-
});
|
|
782
|
-
|
|
783
|
-
test("can navigate to the previous section with the arrow up key", async () => {
|
|
784
|
-
// Arrange
|
|
785
|
-
render(
|
|
786
|
-
<Accordion>
|
|
787
|
-
<AccordionSection header="Section 1">
|
|
788
|
-
Section 1 content
|
|
789
|
-
</AccordionSection>
|
|
790
|
-
<AccordionSection header="Section 2">
|
|
791
|
-
Section 2 content
|
|
792
|
-
</AccordionSection>
|
|
793
|
-
</Accordion>,
|
|
794
|
-
{wrapper: RenderStateRoot},
|
|
795
|
-
);
|
|
796
|
-
|
|
797
|
-
const button1 = await screen.findByRole("button", {
|
|
798
|
-
name: "Section 1",
|
|
799
|
-
});
|
|
800
|
-
const button2 = await screen.findByRole("button", {
|
|
801
|
-
name: "Section 2",
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
// Act
|
|
805
|
-
button2.focus();
|
|
806
|
-
await userEvent.keyboard("{arrowup}");
|
|
807
|
-
|
|
808
|
-
// Assert
|
|
809
|
-
expect(button1).toHaveFocus();
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
test("can cycle to the last section with the arrow up key from the first section", async () => {
|
|
813
|
-
// Arrange
|
|
814
|
-
render(
|
|
815
|
-
<Accordion>
|
|
816
|
-
<AccordionSection header="Section 1">
|
|
817
|
-
Section 1 content
|
|
818
|
-
</AccordionSection>
|
|
819
|
-
<AccordionSection header="Section 2" testId="section-2">
|
|
820
|
-
Section 2 content
|
|
821
|
-
</AccordionSection>
|
|
822
|
-
<AccordionSection header="Section 3">
|
|
823
|
-
Section 3 content
|
|
824
|
-
</AccordionSection>
|
|
825
|
-
</Accordion>,
|
|
826
|
-
{wrapper: RenderStateRoot},
|
|
827
|
-
);
|
|
828
|
-
|
|
829
|
-
const button1 = await screen.findByRole("button", {
|
|
830
|
-
name: "Section 1",
|
|
831
|
-
});
|
|
832
|
-
const button3 = await screen.findByRole("button", {
|
|
833
|
-
name: "Section 3",
|
|
834
|
-
});
|
|
835
|
-
|
|
836
|
-
// Act
|
|
837
|
-
button1.focus();
|
|
838
|
-
await userEvent.keyboard("{arrowup}");
|
|
839
|
-
|
|
840
|
-
// Assert
|
|
841
|
-
expect(button3).toHaveFocus();
|
|
842
|
-
expect(button1).not.toHaveFocus();
|
|
843
|
-
});
|
|
844
|
-
|
|
845
|
-
test("can navigate to the first section with the home key", async () => {
|
|
846
|
-
// Arrange
|
|
847
|
-
render(
|
|
848
|
-
<Accordion>
|
|
849
|
-
<AccordionSection header="Section 1">
|
|
850
|
-
Section 1 content
|
|
851
|
-
</AccordionSection>
|
|
852
|
-
<AccordionSection header="Section 2">
|
|
853
|
-
Section 2 content
|
|
854
|
-
</AccordionSection>
|
|
855
|
-
<AccordionSection header="Section 3">
|
|
856
|
-
Section 3 content
|
|
857
|
-
</AccordionSection>
|
|
858
|
-
</Accordion>,
|
|
859
|
-
{wrapper: RenderStateRoot},
|
|
860
|
-
);
|
|
861
|
-
|
|
862
|
-
const button1 = await screen.findByRole("button", {
|
|
863
|
-
name: "Section 1",
|
|
864
|
-
});
|
|
865
|
-
const button2 = await screen.findByRole("button", {
|
|
866
|
-
name: "Section 2",
|
|
867
|
-
});
|
|
868
|
-
const button3 = await screen.findByRole("button", {
|
|
869
|
-
name: "Section 3",
|
|
870
|
-
});
|
|
871
|
-
|
|
872
|
-
// Act
|
|
873
|
-
button3.focus();
|
|
874
|
-
await userEvent.keyboard("{home}");
|
|
875
|
-
|
|
876
|
-
// Assert
|
|
877
|
-
expect(button1).toHaveFocus();
|
|
878
|
-
expect(button2).not.toHaveFocus();
|
|
879
|
-
expect(button3).not.toHaveFocus();
|
|
880
|
-
});
|
|
881
|
-
|
|
882
|
-
test("can navigate to the last section with the end key", async () => {
|
|
883
|
-
// Arrange
|
|
884
|
-
render(
|
|
885
|
-
<Accordion>
|
|
886
|
-
<AccordionSection header="Section 1">
|
|
887
|
-
Section 1 content
|
|
888
|
-
</AccordionSection>
|
|
889
|
-
<AccordionSection header="Section 2">
|
|
890
|
-
Section 2 content
|
|
891
|
-
</AccordionSection>
|
|
892
|
-
<AccordionSection header="Section 3">
|
|
893
|
-
Section 3 content
|
|
894
|
-
</AccordionSection>
|
|
895
|
-
</Accordion>,
|
|
896
|
-
{wrapper: RenderStateRoot},
|
|
897
|
-
);
|
|
898
|
-
|
|
899
|
-
const button1 = await screen.findByRole("button", {
|
|
900
|
-
name: "Section 1",
|
|
901
|
-
});
|
|
902
|
-
const button2 = await screen.findByRole("button", {
|
|
903
|
-
name: "Section 2",
|
|
904
|
-
});
|
|
905
|
-
const button3 = await screen.findByRole("button", {
|
|
906
|
-
name: "Section 3",
|
|
907
|
-
});
|
|
908
|
-
|
|
909
|
-
// Act
|
|
910
|
-
button1.focus();
|
|
911
|
-
await userEvent.keyboard("{end}");
|
|
912
|
-
|
|
913
|
-
// Assert
|
|
914
|
-
expect(button1).not.toHaveFocus();
|
|
915
|
-
expect(button2).not.toHaveFocus();
|
|
916
|
-
expect(button3).toHaveFocus();
|
|
917
|
-
});
|
|
918
|
-
|
|
919
|
-
test.each(["{end}", "{home}", "{arrowup}", "{arrowdown}"])(
|
|
920
|
-
"cannot navigate when header not currently focused",
|
|
921
|
-
async (key) => {
|
|
922
|
-
// Arrange
|
|
923
|
-
render(
|
|
924
|
-
<Accordion initialExpandedIndex={0}>
|
|
925
|
-
<AccordionSection header="Section 1">
|
|
926
|
-
<label>
|
|
927
|
-
Focus on this textbox!
|
|
928
|
-
<input />
|
|
929
|
-
</label>
|
|
930
|
-
</AccordionSection>
|
|
931
|
-
<AccordionSection header="Section 2">
|
|
932
|
-
Section 2 content
|
|
933
|
-
</AccordionSection>
|
|
934
|
-
</Accordion>,
|
|
935
|
-
{wrapper: RenderStateRoot},
|
|
936
|
-
);
|
|
937
|
-
|
|
938
|
-
const button1 = await screen.findByRole("button", {
|
|
939
|
-
name: "Section 1",
|
|
940
|
-
});
|
|
941
|
-
const button2 = await screen.findByRole("button", {
|
|
942
|
-
name: "Section 2",
|
|
943
|
-
});
|
|
944
|
-
|
|
945
|
-
// Act
|
|
946
|
-
const button = await screen.findByRole("textbox");
|
|
947
|
-
button.focus();
|
|
948
|
-
await userEvent.keyboard(key);
|
|
949
|
-
|
|
950
|
-
// Assert
|
|
951
|
-
expect(button1).not.toHaveFocus();
|
|
952
|
-
expect(button2).not.toHaveFocus();
|
|
953
|
-
},
|
|
954
|
-
);
|
|
955
|
-
});
|
|
956
|
-
});
|