@khanacademy/wonder-blocks-dropdown 2.7.4 → 2.8.0
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 +28 -0
- package/dist/es/index.js +167 -167
- package/dist/index.js +389 -360
- package/package.json +7 -7
- package/src/components/__docs__/action-menu.argtypes.js +44 -0
- package/src/components/__docs__/action-menu.stories.js +435 -0
- package/src/components/__docs__/base-select.argtypes.js +54 -0
- package/src/components/__docs__/multi-select.stories.js +509 -0
- package/src/components/__docs__/single-select.accessibility.stories.mdx +59 -0
- package/src/components/__docs__/single-select.argtypes.js +54 -0
- package/src/components/__docs__/single-select.stories.js +464 -0
- package/src/components/__tests__/dropdown-core-virtualized.test.js +0 -15
- package/src/components/__tests__/dropdown-core.test.js +113 -209
- package/src/components/__tests__/multi-select.test.js +49 -3
- package/src/components/__tests__/single-select.test.js +43 -50
- package/src/components/action-menu.js +11 -0
- package/src/components/dropdown-core-virtualized.js +0 -5
- package/src/components/dropdown-core.js +224 -130
- package/src/components/multi-select.js +18 -33
- package/src/components/single-select.js +16 -30
- package/src/util/__tests__/dropdown-menu-styles.test.js +0 -26
- package/src/util/__tests__/helpers.test.js +73 -0
- package/src/util/constants.js +0 -11
- package/src/util/dropdown-menu-styles.js +0 -5
- package/src/util/helpers.js +44 -0
- package/src/util/types.js +2 -5
- package/src/components/__tests__/search-text-input.test.js +0 -212
- package/src/components/action-menu.stories.js +0 -48
- package/src/components/multi-select.stories.js +0 -124
- package/src/components/search-text-input.js +0 -115
- package/src/components/single-select.stories.js +0 -247
|
@@ -4,46 +4,26 @@ import {fireEvent, render, screen, waitFor} from "@testing-library/react";
|
|
|
4
4
|
import userEvent from "@testing-library/user-event";
|
|
5
5
|
|
|
6
6
|
import OptionItem from "../option-item.js";
|
|
7
|
-
import SearchTextInput from "../search-text-input.js";
|
|
8
7
|
import DropdownCore from "../dropdown-core.js";
|
|
9
8
|
|
|
10
9
|
const items = [
|
|
11
10
|
{
|
|
12
|
-
component:
|
|
13
|
-
<OptionItem testId="item-0" label="item 0" value="0" key="0" />
|
|
14
|
-
),
|
|
11
|
+
component: <OptionItem label="item 0" value="0" key="0" />,
|
|
15
12
|
focusable: true,
|
|
16
13
|
populatedProps: {},
|
|
17
14
|
},
|
|
18
15
|
{
|
|
19
|
-
component:
|
|
20
|
-
<OptionItem testId="item-1" label="item 1" value="1" key="1" />
|
|
21
|
-
),
|
|
16
|
+
component: <OptionItem label="item 1" value="1" key="1" />,
|
|
22
17
|
focusable: true,
|
|
23
18
|
populatedProps: {},
|
|
24
19
|
},
|
|
25
20
|
{
|
|
26
|
-
component:
|
|
27
|
-
<OptionItem testId="item-2" label="item 2" value="2" key="2" />
|
|
28
|
-
),
|
|
21
|
+
component: <OptionItem label="item 2" value="2" key="2" />,
|
|
29
22
|
focusable: true,
|
|
30
23
|
populatedProps: {},
|
|
31
24
|
},
|
|
32
25
|
];
|
|
33
26
|
|
|
34
|
-
const searchFieldItem = {
|
|
35
|
-
component: (
|
|
36
|
-
<SearchTextInput
|
|
37
|
-
testId="search-text-input"
|
|
38
|
-
key="search-text-input"
|
|
39
|
-
onChange={jest.fn()}
|
|
40
|
-
searchText={""}
|
|
41
|
-
/>
|
|
42
|
-
),
|
|
43
|
-
focusable: true,
|
|
44
|
-
populatedProps: {},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
27
|
describe("DropdownCore", () => {
|
|
48
28
|
it("should throw for invalid role", () => {
|
|
49
29
|
// Arrange
|
|
@@ -90,7 +70,7 @@ describe("DropdownCore", () => {
|
|
|
90
70
|
);
|
|
91
71
|
|
|
92
72
|
// Act
|
|
93
|
-
const item = screen.
|
|
73
|
+
const item = screen.getByRole("option", {name: "item 0"});
|
|
94
74
|
|
|
95
75
|
// Assert
|
|
96
76
|
expect(item).toHaveFocus();
|
|
@@ -121,7 +101,7 @@ describe("DropdownCore", () => {
|
|
|
121
101
|
userEvent.keyboard("{arrowdown}"); // 1 -> 2
|
|
122
102
|
|
|
123
103
|
// Assert
|
|
124
|
-
expect(screen.
|
|
104
|
+
expect(screen.getByRole("option", {name: "item 2"})).toHaveFocus();
|
|
125
105
|
});
|
|
126
106
|
|
|
127
107
|
it("keyboard works backwards as expected", () => {
|
|
@@ -137,6 +117,7 @@ describe("DropdownCore", () => {
|
|
|
137
117
|
opener={<button />}
|
|
138
118
|
openerElement={null}
|
|
139
119
|
onOpenChanged={jest.fn()}
|
|
120
|
+
isFilterable={false}
|
|
140
121
|
/>,
|
|
141
122
|
);
|
|
142
123
|
|
|
@@ -151,7 +132,42 @@ describe("DropdownCore", () => {
|
|
|
151
132
|
userEvent.keyboard("{arrowup}"); // 2 -> 1
|
|
152
133
|
|
|
153
134
|
// Assert
|
|
154
|
-
expect(screen.
|
|
135
|
+
expect(screen.getByRole("option", {name: "item 1"})).toHaveFocus();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("keyboard works backwards with the search field included", () => {
|
|
139
|
+
// Arrange
|
|
140
|
+
render(
|
|
141
|
+
<DropdownCore
|
|
142
|
+
initialFocusedIndex={0}
|
|
143
|
+
onSearchTextChanged={jest.fn()}
|
|
144
|
+
searchText=""
|
|
145
|
+
isFilterable={true}
|
|
146
|
+
// mock the items
|
|
147
|
+
items={items}
|
|
148
|
+
role="listbox"
|
|
149
|
+
open={true}
|
|
150
|
+
// mock the opener elements
|
|
151
|
+
opener={<button />}
|
|
152
|
+
openerElement={null}
|
|
153
|
+
onOpenChanged={jest.fn()}
|
|
154
|
+
/>,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Act
|
|
158
|
+
// navigate down four times
|
|
159
|
+
userEvent.keyboard("{arrowdown}"); // 0 -> 1
|
|
160
|
+
userEvent.keyboard("{arrowdown}"); // 1 -> 2
|
|
161
|
+
userEvent.keyboard("{arrowdown}"); // 2 -> search field
|
|
162
|
+
userEvent.keyboard("{arrowdown}"); // search field -> 0
|
|
163
|
+
|
|
164
|
+
// navigate up back three times
|
|
165
|
+
userEvent.keyboard("{arrowup}"); // 0 -> search field
|
|
166
|
+
userEvent.keyboard("{arrowup}"); // search field -> 2
|
|
167
|
+
userEvent.keyboard("{arrowup}"); // 2 -> 1
|
|
168
|
+
|
|
169
|
+
// Assert
|
|
170
|
+
expect(screen.getByRole("option", {name: "item 1"})).toHaveFocus();
|
|
155
171
|
});
|
|
156
172
|
|
|
157
173
|
it("closes on tab as expected", () => {
|
|
@@ -280,7 +296,7 @@ describe("DropdownCore", () => {
|
|
|
280
296
|
/>,
|
|
281
297
|
);
|
|
282
298
|
|
|
283
|
-
const openerElement = screen.
|
|
299
|
+
const openerElement = screen.getByRole("button");
|
|
284
300
|
openerElement.focus();
|
|
285
301
|
|
|
286
302
|
// Act
|
|
@@ -307,44 +323,7 @@ describe("DropdownCore", () => {
|
|
|
307
323
|
);
|
|
308
324
|
|
|
309
325
|
// Assert
|
|
310
|
-
expect(screen.
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("selects correct item when starting off at an undefined index and a searchbox", () => {
|
|
314
|
-
// Arrange
|
|
315
|
-
render(
|
|
316
|
-
<DropdownCore
|
|
317
|
-
initialFocusedIndex={undefined}
|
|
318
|
-
searchText=""
|
|
319
|
-
// mock the items
|
|
320
|
-
items={[
|
|
321
|
-
{
|
|
322
|
-
component: (
|
|
323
|
-
<SearchTextInput
|
|
324
|
-
testId="item-0"
|
|
325
|
-
key="search-text-input"
|
|
326
|
-
onChange={jest.fn()}
|
|
327
|
-
searchText={""}
|
|
328
|
-
/>
|
|
329
|
-
),
|
|
330
|
-
focusable: true,
|
|
331
|
-
populatedProps: {},
|
|
332
|
-
},
|
|
333
|
-
]}
|
|
334
|
-
role="listbox"
|
|
335
|
-
open={true}
|
|
336
|
-
// mock the opener elements
|
|
337
|
-
opener={<button />}
|
|
338
|
-
openerElement={null}
|
|
339
|
-
onOpenChanged={jest.fn()}
|
|
340
|
-
/>,
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
// Act
|
|
344
|
-
const firstItem = screen.queryByTestId("item-0");
|
|
345
|
-
|
|
346
|
-
// Assert
|
|
347
|
-
expect(firstItem).toHaveFocus();
|
|
326
|
+
expect(screen.getByRole("option", {name: "item 0"})).toHaveFocus();
|
|
348
327
|
});
|
|
349
328
|
|
|
350
329
|
it("selects correct item when starting off at a different index and a searchbox", () => {
|
|
@@ -353,29 +332,19 @@ describe("DropdownCore", () => {
|
|
|
353
332
|
<DropdownCore
|
|
354
333
|
initialFocusedIndex={1}
|
|
355
334
|
searchText=""
|
|
335
|
+
isFilterable={true}
|
|
356
336
|
// mock the items
|
|
357
337
|
items={[
|
|
358
|
-
searchFieldItem,
|
|
359
338
|
{
|
|
360
339
|
component: (
|
|
361
|
-
<OptionItem
|
|
362
|
-
testId="item-0"
|
|
363
|
-
label="item 1"
|
|
364
|
-
value="1"
|
|
365
|
-
key="1"
|
|
366
|
-
/>
|
|
340
|
+
<OptionItem label="item 1" value="1" key="1" />
|
|
367
341
|
),
|
|
368
342
|
focusable: true,
|
|
369
343
|
populatedProps: {},
|
|
370
344
|
},
|
|
371
345
|
{
|
|
372
346
|
component: (
|
|
373
|
-
<OptionItem
|
|
374
|
-
testId="item-1"
|
|
375
|
-
label="item 2"
|
|
376
|
-
value="2"
|
|
377
|
-
key="2"
|
|
378
|
-
/>
|
|
347
|
+
<OptionItem label="item 2" value="2" key="2" />
|
|
379
348
|
),
|
|
380
349
|
focusable: true,
|
|
381
350
|
populatedProps: {},
|
|
@@ -391,7 +360,7 @@ describe("DropdownCore", () => {
|
|
|
391
360
|
);
|
|
392
361
|
|
|
393
362
|
// Act
|
|
394
|
-
const firstItem = screen.
|
|
363
|
+
const firstItem = screen.getByRole("option", {name: "item 2"});
|
|
395
364
|
|
|
396
365
|
// Assert
|
|
397
366
|
expect(firstItem).toHaveFocus();
|
|
@@ -418,7 +387,7 @@ describe("DropdownCore", () => {
|
|
|
418
387
|
userEvent.keyboard("{arrowdown}"); // 2 -> 0
|
|
419
388
|
|
|
420
389
|
// Assert
|
|
421
|
-
expect(screen.
|
|
390
|
+
expect(screen.getByRole("option", {name: "item 0"})).toHaveFocus();
|
|
422
391
|
});
|
|
423
392
|
|
|
424
393
|
it("focuses correct item with clicking/pressing with initial focused of not 0", () => {
|
|
@@ -438,12 +407,12 @@ describe("DropdownCore", () => {
|
|
|
438
407
|
);
|
|
439
408
|
|
|
440
409
|
// Act
|
|
441
|
-
userEvent.click(screen.
|
|
410
|
+
userEvent.click(screen.getByRole("option", {name: "item 1"}));
|
|
442
411
|
// navigate down
|
|
443
412
|
userEvent.keyboard("{arrowdown}"); // 1 -> 2
|
|
444
413
|
|
|
445
414
|
// Assert
|
|
446
|
-
expect(screen.
|
|
415
|
+
expect(screen.getByRole("option", {name: "item 2"})).toHaveFocus();
|
|
447
416
|
});
|
|
448
417
|
|
|
449
418
|
it("focuses correct item with a disabled item", () => {
|
|
@@ -455,12 +424,7 @@ describe("DropdownCore", () => {
|
|
|
455
424
|
items={[
|
|
456
425
|
{
|
|
457
426
|
component: (
|
|
458
|
-
<OptionItem
|
|
459
|
-
testId="item-0"
|
|
460
|
-
label="item 0"
|
|
461
|
-
value="0"
|
|
462
|
-
key="0"
|
|
463
|
-
/>
|
|
427
|
+
<OptionItem label="item 0" value="0" key="0" />
|
|
464
428
|
),
|
|
465
429
|
focusable: true,
|
|
466
430
|
populatedProps: {},
|
|
@@ -468,7 +432,6 @@ describe("DropdownCore", () => {
|
|
|
468
432
|
{
|
|
469
433
|
component: (
|
|
470
434
|
<OptionItem
|
|
471
|
-
testId="item-1"
|
|
472
435
|
label="item 1"
|
|
473
436
|
value="1"
|
|
474
437
|
key="1"
|
|
@@ -480,12 +443,7 @@ describe("DropdownCore", () => {
|
|
|
480
443
|
},
|
|
481
444
|
{
|
|
482
445
|
component: (
|
|
483
|
-
<OptionItem
|
|
484
|
-
testId="item-2"
|
|
485
|
-
label="item 2"
|
|
486
|
-
value="2"
|
|
487
|
-
key="2"
|
|
488
|
-
/>
|
|
446
|
+
<OptionItem label="item 2" value="2" key="2" />
|
|
489
447
|
),
|
|
490
448
|
focusable: true,
|
|
491
449
|
populatedProps: {},
|
|
@@ -505,7 +463,7 @@ describe("DropdownCore", () => {
|
|
|
505
463
|
userEvent.keyboard("{arrowdown}"); // 0 -> 2 (1 is disabled)
|
|
506
464
|
|
|
507
465
|
// Assert
|
|
508
|
-
expect(screen.
|
|
466
|
+
expect(screen.getByRole("option", {name: "item 2"})).toHaveFocus();
|
|
509
467
|
});
|
|
510
468
|
|
|
511
469
|
it("calls correct onclick for an option item", () => {
|
|
@@ -518,12 +476,7 @@ describe("DropdownCore", () => {
|
|
|
518
476
|
items={[
|
|
519
477
|
{
|
|
520
478
|
component: (
|
|
521
|
-
<OptionItem
|
|
522
|
-
label="item 0"
|
|
523
|
-
value="0"
|
|
524
|
-
key="0"
|
|
525
|
-
testId="item-0"
|
|
526
|
-
/>
|
|
479
|
+
<OptionItem label="item 0" value="0" key="0" />
|
|
527
480
|
),
|
|
528
481
|
focusable: true,
|
|
529
482
|
populatedProps: {},
|
|
@@ -534,7 +487,6 @@ describe("DropdownCore", () => {
|
|
|
534
487
|
label="item 1"
|
|
535
488
|
value="1"
|
|
536
489
|
key="1"
|
|
537
|
-
testId="item-1"
|
|
538
490
|
onClick={onClick1}
|
|
539
491
|
/>
|
|
540
492
|
),
|
|
@@ -543,12 +495,7 @@ describe("DropdownCore", () => {
|
|
|
543
495
|
},
|
|
544
496
|
{
|
|
545
497
|
component: (
|
|
546
|
-
<OptionItem
|
|
547
|
-
label="item 2"
|
|
548
|
-
testId="item-2"
|
|
549
|
-
value="2"
|
|
550
|
-
key="2"
|
|
551
|
-
/>
|
|
498
|
+
<OptionItem label="item 2" value="2" key="2" />
|
|
552
499
|
),
|
|
553
500
|
focusable: true,
|
|
554
501
|
populatedProps: {},
|
|
@@ -564,7 +511,7 @@ describe("DropdownCore", () => {
|
|
|
564
511
|
);
|
|
565
512
|
|
|
566
513
|
// Act
|
|
567
|
-
userEvent.click(screen.
|
|
514
|
+
userEvent.click(screen.getByRole("option", {name: "item 1"}));
|
|
568
515
|
|
|
569
516
|
// Assert
|
|
570
517
|
expect(onClick1).toHaveBeenCalledTimes(1);
|
|
@@ -579,19 +526,8 @@ describe("DropdownCore", () => {
|
|
|
579
526
|
<DropdownCore
|
|
580
527
|
onSearchTextChanged={handleSearchTextChanged}
|
|
581
528
|
searchText="ab"
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
component: (
|
|
585
|
-
<SearchTextInput
|
|
586
|
-
key="search-text-input"
|
|
587
|
-
onChange={handleSearchTextChanged}
|
|
588
|
-
searchText={""}
|
|
589
|
-
/>
|
|
590
|
-
),
|
|
591
|
-
focusable: true,
|
|
592
|
-
populatedProps: {},
|
|
593
|
-
},
|
|
594
|
-
]}
|
|
529
|
+
isFilterable={true}
|
|
530
|
+
items={[]}
|
|
595
531
|
role="listbox"
|
|
596
532
|
open={true}
|
|
597
533
|
// mock the opener elements
|
|
@@ -602,37 +538,21 @@ describe("DropdownCore", () => {
|
|
|
602
538
|
);
|
|
603
539
|
|
|
604
540
|
// Assert
|
|
605
|
-
expect(
|
|
606
|
-
screen.getByTestId("dropdown-core-no-results"),
|
|
607
|
-
).toBeInTheDocument();
|
|
541
|
+
expect(screen.getByText("No results")).toBeInTheDocument();
|
|
608
542
|
});
|
|
609
543
|
|
|
610
|
-
it("
|
|
544
|
+
it("SearchField should be focused when opened and there's no selection", () => {
|
|
611
545
|
// Arrange
|
|
612
|
-
const handleSearchTextChanged = jest.fn();
|
|
613
|
-
const handleOpen = jest.fn();
|
|
614
546
|
|
|
615
547
|
// Act
|
|
616
548
|
render(
|
|
617
549
|
<DropdownCore
|
|
618
|
-
initialFocusedIndex={
|
|
619
|
-
onOpenChanged={
|
|
620
|
-
onSearchTextChanged={
|
|
550
|
+
initialFocusedIndex={undefined}
|
|
551
|
+
onOpenChanged={jest.fn()}
|
|
552
|
+
onSearchTextChanged={jest.fn()}
|
|
621
553
|
searchText="ab"
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
component: (
|
|
625
|
-
<SearchTextInput
|
|
626
|
-
testId="item-0"
|
|
627
|
-
key="search-text-input"
|
|
628
|
-
onChange={handleSearchTextChanged}
|
|
629
|
-
searchText={""}
|
|
630
|
-
/>
|
|
631
|
-
),
|
|
632
|
-
focusable: true,
|
|
633
|
-
populatedProps: {},
|
|
634
|
-
},
|
|
635
|
-
]}
|
|
554
|
+
isFilterable={true}
|
|
555
|
+
items={[]}
|
|
636
556
|
role="listbox"
|
|
637
557
|
open={true}
|
|
638
558
|
// mock the opener elements
|
|
@@ -642,33 +562,48 @@ describe("DropdownCore", () => {
|
|
|
642
562
|
);
|
|
643
563
|
|
|
644
564
|
// Assert
|
|
645
|
-
expect(screen.
|
|
565
|
+
expect(screen.getByRole("textbox")).toHaveFocus();
|
|
646
566
|
});
|
|
647
567
|
|
|
648
|
-
it("
|
|
568
|
+
it("SearchField should trigger change when the user types in", () => {
|
|
569
|
+
// Arrange
|
|
570
|
+
const onSearchTextChangedMock = jest.fn();
|
|
571
|
+
|
|
572
|
+
render(
|
|
573
|
+
<DropdownCore
|
|
574
|
+
initialFocusedIndex={undefined}
|
|
575
|
+
onOpenChanged={jest.fn()}
|
|
576
|
+
onSearchTextChanged={onSearchTextChangedMock}
|
|
577
|
+
searchText=""
|
|
578
|
+
isFilterable={true}
|
|
579
|
+
items={[]}
|
|
580
|
+
role="listbox"
|
|
581
|
+
open={true}
|
|
582
|
+
// mock the opener elements
|
|
583
|
+
opener={<button />}
|
|
584
|
+
openerElement={null}
|
|
585
|
+
/>,
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
// Act
|
|
589
|
+
const searchField = screen.getByRole("textbox");
|
|
590
|
+
userEvent.type(searchField, "option 1");
|
|
591
|
+
|
|
592
|
+
// Assert
|
|
593
|
+
expect(onSearchTextChangedMock).toHaveBeenCalled();
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
it("When SearchField has input and focused, tab key should not close the select", async () => {
|
|
649
597
|
// Arrange
|
|
650
|
-
const handleSearchTextChanged = jest.fn();
|
|
651
598
|
const handleOpen = jest.fn();
|
|
652
599
|
|
|
653
600
|
render(
|
|
654
601
|
<DropdownCore
|
|
655
602
|
onOpenChanged={handleOpen}
|
|
656
|
-
onSearchTextChanged={
|
|
603
|
+
onSearchTextChanged={jest.fn()}
|
|
657
604
|
searchText="ab"
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
component: (
|
|
661
|
-
<SearchTextInput
|
|
662
|
-
testId="item-0"
|
|
663
|
-
key="search-text-input"
|
|
664
|
-
onChange={handleSearchTextChanged}
|
|
665
|
-
searchText={""}
|
|
666
|
-
/>
|
|
667
|
-
),
|
|
668
|
-
focusable: true,
|
|
669
|
-
populatedProps: {},
|
|
670
|
-
},
|
|
671
|
-
]}
|
|
605
|
+
isFilterable={true}
|
|
606
|
+
items={[]}
|
|
672
607
|
role="listbox"
|
|
673
608
|
open={true}
|
|
674
609
|
// mock the opener elements
|
|
@@ -682,32 +617,23 @@ describe("DropdownCore", () => {
|
|
|
682
617
|
|
|
683
618
|
// Assert
|
|
684
619
|
expect(handleOpen).toHaveBeenCalledTimes(0);
|
|
685
|
-
|
|
620
|
+
waitFor(() => {
|
|
621
|
+
expect(
|
|
622
|
+
screen.getByRole("button", {name: "Clear search"}),
|
|
623
|
+
).toHaveFocus();
|
|
624
|
+
});
|
|
686
625
|
});
|
|
687
626
|
|
|
688
|
-
it("When
|
|
627
|
+
it("When SearchField exists and focused, space key pressing should be allowed", () => {
|
|
689
628
|
// Arrange
|
|
690
|
-
const handleSearchTextChanged = jest.fn();
|
|
691
629
|
const preventDefaultMock = jest.fn();
|
|
692
630
|
|
|
693
631
|
render(
|
|
694
632
|
<DropdownCore
|
|
695
633
|
onSearchTextChanged={jest.fn()}
|
|
696
634
|
searchText="ab"
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
component: (
|
|
700
|
-
<SearchTextInput
|
|
701
|
-
testId="item-0"
|
|
702
|
-
key="search-text-input"
|
|
703
|
-
onChange={handleSearchTextChanged}
|
|
704
|
-
searchText={"ab"}
|
|
705
|
-
/>
|
|
706
|
-
),
|
|
707
|
-
focusable: true,
|
|
708
|
-
populatedProps: {},
|
|
709
|
-
},
|
|
710
|
-
]}
|
|
635
|
+
isFilterable={true}
|
|
636
|
+
items={[]}
|
|
711
637
|
role="listbox"
|
|
712
638
|
open={true}
|
|
713
639
|
// mock the opener elements
|
|
@@ -718,7 +644,7 @@ describe("DropdownCore", () => {
|
|
|
718
644
|
);
|
|
719
645
|
|
|
720
646
|
// Act
|
|
721
|
-
const searchInput = screen.
|
|
647
|
+
const searchInput = screen.getByRole("textbox");
|
|
722
648
|
// eslint-disable-next-line testing-library/prefer-user-event
|
|
723
649
|
fireEvent.keyDown(searchInput, {
|
|
724
650
|
keyCode: 32,
|
|
@@ -755,8 +681,9 @@ describe("DropdownCore", () => {
|
|
|
755
681
|
initialFocusedIndex={undefined}
|
|
756
682
|
onSearchTextChanged={jest.fn()}
|
|
757
683
|
searchText=""
|
|
684
|
+
isFilterable={true}
|
|
758
685
|
// mock the items
|
|
759
|
-
items={
|
|
686
|
+
items={optionItems}
|
|
760
687
|
role="listbox"
|
|
761
688
|
open={true}
|
|
762
689
|
// mock the opener elements
|
|
@@ -784,8 +711,9 @@ describe("DropdownCore", () => {
|
|
|
784
711
|
initialFocusedIndex={undefined}
|
|
785
712
|
onSearchTextChanged={jest.fn()}
|
|
786
713
|
searchText=""
|
|
714
|
+
isFilterable={true}
|
|
787
715
|
// mock the items
|
|
788
|
-
items={
|
|
716
|
+
items={optionItems}
|
|
789
717
|
role="listbox"
|
|
790
718
|
open={true}
|
|
791
719
|
// mock the opener elements
|
|
@@ -809,7 +737,7 @@ describe("DropdownCore", () => {
|
|
|
809
737
|
});
|
|
810
738
|
|
|
811
739
|
describe("a11y > Live region", () => {
|
|
812
|
-
it("should render a live region announcing the number of options",
|
|
740
|
+
it("should render a live region announcing the number of options", () => {
|
|
813
741
|
// Arrange
|
|
814
742
|
|
|
815
743
|
// Act
|
|
@@ -831,29 +759,5 @@ describe("DropdownCore", () => {
|
|
|
831
759
|
// Assert
|
|
832
760
|
expect(container).toHaveTextContent("3 items");
|
|
833
761
|
});
|
|
834
|
-
|
|
835
|
-
it("shouldn't include the search field as part of the options", async () => {
|
|
836
|
-
// Arrange
|
|
837
|
-
|
|
838
|
-
// Act
|
|
839
|
-
const {container} = render(
|
|
840
|
-
<DropdownCore
|
|
841
|
-
initialFocusedIndex={undefined}
|
|
842
|
-
onSearchTextChanged={jest.fn()}
|
|
843
|
-
searchText=""
|
|
844
|
-
// mock the items (3 options + search field)
|
|
845
|
-
items={[searchFieldItem, ...items]}
|
|
846
|
-
role="listbox"
|
|
847
|
-
open={true}
|
|
848
|
-
// mock the opener elements
|
|
849
|
-
opener={<button />}
|
|
850
|
-
openerElement={null}
|
|
851
|
-
onOpenChanged={jest.fn()}
|
|
852
|
-
/>,
|
|
853
|
-
);
|
|
854
|
-
|
|
855
|
-
// Assert
|
|
856
|
-
expect(container).toHaveTextContent("3 items");
|
|
857
|
-
});
|
|
858
762
|
});
|
|
859
763
|
});
|
|
@@ -19,6 +19,19 @@ const labels: $Shape<Labels> = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
describe("MultiSelect", () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
window.scrollTo = jest.fn();
|
|
24
|
+
|
|
25
|
+
// We mock console.error() because React logs a bunch of errors pertaining
|
|
26
|
+
// to the use href="javascript:void(0);".
|
|
27
|
+
jest.spyOn(console, "error").mockImplementation(() => {});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
window.scrollTo.mockClear();
|
|
32
|
+
jest.spyOn(console, "error").mockReset();
|
|
33
|
+
});
|
|
34
|
+
|
|
22
35
|
describe("uncontrolled", () => {
|
|
23
36
|
const onChange = jest.fn();
|
|
24
37
|
const uncontrolledSingleSelect = (
|
|
@@ -1078,9 +1091,7 @@ describe("MultiSelect", () => {
|
|
|
1078
1091
|
userEvent.type(screen.getByPlaceholderText("Filter"), "other");
|
|
1079
1092
|
|
|
1080
1093
|
// Assert
|
|
1081
|
-
expect(screen.
|
|
1082
|
-
"No hay resultados",
|
|
1083
|
-
);
|
|
1094
|
+
expect(screen.getByText("No hay resultados")).toBeInTheDocument();
|
|
1084
1095
|
});
|
|
1085
1096
|
|
|
1086
1097
|
it("passes the custom label to the select all shortcut", () => {
|
|
@@ -1188,6 +1199,41 @@ describe("MultiSelect", () => {
|
|
|
1188
1199
|
screen.getByText(updatedLabels.selectNoneLabel),
|
|
1189
1200
|
).toBeInTheDocument();
|
|
1190
1201
|
});
|
|
1202
|
+
|
|
1203
|
+
describe("keyboard", () => {
|
|
1204
|
+
beforeEach(() => {
|
|
1205
|
+
// Required due to the `debounce` call.
|
|
1206
|
+
jest.useFakeTimers();
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
it("should find and focus an item using the keyboard", () => {
|
|
1210
|
+
// Arrange
|
|
1211
|
+
const onChangeMock = jest.fn();
|
|
1212
|
+
|
|
1213
|
+
render(
|
|
1214
|
+
<MultiSelect onChange={onChangeMock}>
|
|
1215
|
+
<OptionItem label="Mercury" value="mercury" />
|
|
1216
|
+
<OptionItem label="Venus" value="venus" />
|
|
1217
|
+
<OptionItem label="Mars" value="mars" />
|
|
1218
|
+
</MultiSelect>,
|
|
1219
|
+
);
|
|
1220
|
+
userEvent.tab();
|
|
1221
|
+
|
|
1222
|
+
// Act
|
|
1223
|
+
// find first occurrence
|
|
1224
|
+
userEvent.keyboard("v");
|
|
1225
|
+
jest.advanceTimersByTime(501);
|
|
1226
|
+
|
|
1227
|
+
// Assert
|
|
1228
|
+
const filteredOption = screen.getByRole("option", {
|
|
1229
|
+
name: /Venus/i,
|
|
1230
|
+
});
|
|
1231
|
+
// Verify that the element found is focused.
|
|
1232
|
+
expect(filteredOption).toHaveFocus();
|
|
1233
|
+
// And also verify that the listbox is opened.
|
|
1234
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument();
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1191
1237
|
});
|
|
1192
1238
|
|
|
1193
1239
|
describe("a11y > Live region", () => {
|