@khanacademy/wonder-blocks-dropdown 5.3.8 → 5.4.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 +38 -0
- package/dist/components/action-menu.d.ts +15 -2
- package/dist/components/dropdown-core.d.ts +4 -0
- package/dist/components/dropdown-opener.d.ts +4 -0
- package/dist/components/multi-select.d.ts +9 -2
- package/dist/components/option-item.d.ts +1 -1
- package/dist/components/single-select.d.ts +9 -2
- package/dist/es/index.js +114 -61
- package/dist/hooks/use-listbox.d.ts +2 -2
- package/dist/index.js +113 -60
- package/package.json +6 -6
- package/src/components/__tests__/action-menu.test.tsx +630 -23
- package/src/components/__tests__/multi-select.test.tsx +293 -0
- package/src/components/__tests__/single-select.test.tsx +306 -0
- package/src/components/action-menu.tsx +85 -48
- package/src/components/dropdown-core.tsx +9 -2
- package/src/components/dropdown-opener.tsx +17 -1
- package/src/components/multi-select.tsx +94 -61
- package/src/components/option-item.tsx +1 -1
- package/src/components/select-opener.tsx +2 -2
- package/src/components/single-select.tsx +87 -57
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -1777,4 +1777,297 @@ describe("MultiSelect", () => {
|
|
|
1777
1777
|
expect(multiSelect).not.toHaveAttribute("disabled");
|
|
1778
1778
|
});
|
|
1779
1779
|
});
|
|
1780
|
+
|
|
1781
|
+
describe("Ids", () => {
|
|
1782
|
+
it("Should auto-generate an id for the opener if `id` prop is not provided", async () => {
|
|
1783
|
+
// Arrange
|
|
1784
|
+
doRender(
|
|
1785
|
+
<MultiSelect onChange={jest.fn()}>
|
|
1786
|
+
<OptionItem label="item 1" value="1" />
|
|
1787
|
+
<OptionItem label="item 2" value="2" />
|
|
1788
|
+
</MultiSelect>,
|
|
1789
|
+
);
|
|
1790
|
+
|
|
1791
|
+
// Act
|
|
1792
|
+
const opener = await screen.findByRole("button");
|
|
1793
|
+
|
|
1794
|
+
// Assert
|
|
1795
|
+
expect(opener).toHaveAttribute(
|
|
1796
|
+
"id",
|
|
1797
|
+
expect.stringMatching(/^uid-multi-select-opener-\d+-wb-id$/),
|
|
1798
|
+
);
|
|
1799
|
+
});
|
|
1800
|
+
it("Should use the `id` prop if provided", async () => {
|
|
1801
|
+
// Arrange
|
|
1802
|
+
const id = "test-id";
|
|
1803
|
+
doRender(
|
|
1804
|
+
<MultiSelect onChange={jest.fn()} id={id}>
|
|
1805
|
+
<OptionItem label="item 1" value="1" />
|
|
1806
|
+
<OptionItem label="item 2" value="2" />
|
|
1807
|
+
</MultiSelect>,
|
|
1808
|
+
);
|
|
1809
|
+
|
|
1810
|
+
// Act
|
|
1811
|
+
const opener = await screen.findByRole("button");
|
|
1812
|
+
|
|
1813
|
+
// Assert
|
|
1814
|
+
expect(opener).toHaveAttribute("id", id);
|
|
1815
|
+
});
|
|
1816
|
+
it("Should auto-generate an id for the dropdown if `dropdownId` prop is not provided", async () => {
|
|
1817
|
+
// Arrange
|
|
1818
|
+
const {userEvent} = doRender(
|
|
1819
|
+
<MultiSelect onChange={jest.fn()}>
|
|
1820
|
+
<OptionItem label="item 1" value="1" />
|
|
1821
|
+
<OptionItem label="item 2" value="2" />
|
|
1822
|
+
</MultiSelect>,
|
|
1823
|
+
);
|
|
1824
|
+
|
|
1825
|
+
// Act
|
|
1826
|
+
// Open the dropdown
|
|
1827
|
+
const opener = await screen.findByRole("button");
|
|
1828
|
+
await userEvent.click(opener);
|
|
1829
|
+
|
|
1830
|
+
// Assert
|
|
1831
|
+
expect(
|
|
1832
|
+
await screen.findByRole("listbox", {hidden: true}),
|
|
1833
|
+
).toHaveAttribute(
|
|
1834
|
+
"id",
|
|
1835
|
+
expect.stringMatching(/^uid-multi-select-dropdown-\d+-wb-id$/),
|
|
1836
|
+
);
|
|
1837
|
+
});
|
|
1838
|
+
it("Should use the `dropdownId` prop if provided", async () => {
|
|
1839
|
+
// Arrange
|
|
1840
|
+
const dropdownId = "test-id";
|
|
1841
|
+
const {userEvent} = doRender(
|
|
1842
|
+
<MultiSelect onChange={jest.fn()} dropdownId={dropdownId}>
|
|
1843
|
+
<OptionItem label="item 1" value="1" />
|
|
1844
|
+
<OptionItem label="item 2" value="2" />
|
|
1845
|
+
</MultiSelect>,
|
|
1846
|
+
);
|
|
1847
|
+
|
|
1848
|
+
// Act
|
|
1849
|
+
// Open the dropdown
|
|
1850
|
+
const opener = await screen.findByRole("button");
|
|
1851
|
+
await userEvent.click(opener);
|
|
1852
|
+
|
|
1853
|
+
// Assert
|
|
1854
|
+
expect(
|
|
1855
|
+
await screen.findByRole("listbox", {hidden: true}),
|
|
1856
|
+
).toHaveAttribute("id", dropdownId);
|
|
1857
|
+
});
|
|
1858
|
+
});
|
|
1859
|
+
|
|
1860
|
+
describe("a11y > aria-controls", () => {
|
|
1861
|
+
it("Should set the `aria-controls` attribute on the default opener to the provided dropdownId prop", async () => {
|
|
1862
|
+
// Arrange
|
|
1863
|
+
const dropdownId = "test-id";
|
|
1864
|
+
const {userEvent} = doRender(
|
|
1865
|
+
<MultiSelect onChange={jest.fn()} dropdownId={dropdownId}>
|
|
1866
|
+
<OptionItem label="item 1" value="1" />
|
|
1867
|
+
<OptionItem label="item 2" value="2" />
|
|
1868
|
+
</MultiSelect>,
|
|
1869
|
+
);
|
|
1870
|
+
|
|
1871
|
+
// Act
|
|
1872
|
+
const opener = await screen.findByRole("button");
|
|
1873
|
+
await userEvent.click(opener);
|
|
1874
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1875
|
+
|
|
1876
|
+
// Assert
|
|
1877
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1878
|
+
expect(opener).toHaveAttribute("aria-controls", dropdownId);
|
|
1879
|
+
});
|
|
1880
|
+
|
|
1881
|
+
it("Should set the `aria-controls` attribute on the default opener to the auto-generated dropdownId", async () => {
|
|
1882
|
+
// Arrange
|
|
1883
|
+
const {userEvent} = doRender(
|
|
1884
|
+
<MultiSelect onChange={jest.fn()}>
|
|
1885
|
+
<OptionItem label="item 1" value="1" />
|
|
1886
|
+
<OptionItem label="item 2" value="2" />
|
|
1887
|
+
</MultiSelect>,
|
|
1888
|
+
);
|
|
1889
|
+
|
|
1890
|
+
// Act
|
|
1891
|
+
const opener = await screen.findByRole("button");
|
|
1892
|
+
await userEvent.click(opener);
|
|
1893
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1894
|
+
|
|
1895
|
+
// Assert
|
|
1896
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1897
|
+
expect(opener).toHaveAttribute(
|
|
1898
|
+
"aria-controls",
|
|
1899
|
+
expect.stringMatching(/^uid-multi-select-dropdown-\d+-wb-id$/),
|
|
1900
|
+
);
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
it("Should set the `aria-controls` attribute on the custom opener to the provided dropdownId prop", async () => {
|
|
1904
|
+
// Arrange
|
|
1905
|
+
const dropdownId = "test-id";
|
|
1906
|
+
const {userEvent} = doRender(
|
|
1907
|
+
<MultiSelect
|
|
1908
|
+
onChange={jest.fn()}
|
|
1909
|
+
dropdownId={dropdownId}
|
|
1910
|
+
opener={() => (
|
|
1911
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1912
|
+
)}
|
|
1913
|
+
>
|
|
1914
|
+
<OptionItem label="item 1" value="1" />
|
|
1915
|
+
<OptionItem label="item 2" value="2" />
|
|
1916
|
+
</MultiSelect>,
|
|
1917
|
+
);
|
|
1918
|
+
|
|
1919
|
+
// Act
|
|
1920
|
+
const opener = await screen.findByLabelText("Search");
|
|
1921
|
+
await userEvent.click(opener);
|
|
1922
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1923
|
+
|
|
1924
|
+
// Assert
|
|
1925
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1926
|
+
expect(opener).toHaveAttribute("aria-controls", dropdownId);
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1929
|
+
it("Should set the `aria-controls` attribute on the custom opener to the auto-generated dropdownId", async () => {
|
|
1930
|
+
// Arrange
|
|
1931
|
+
const {userEvent} = doRender(
|
|
1932
|
+
<MultiSelect
|
|
1933
|
+
onChange={jest.fn()}
|
|
1934
|
+
opener={() => (
|
|
1935
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1936
|
+
)}
|
|
1937
|
+
>
|
|
1938
|
+
<OptionItem label="item 1" value="1" />
|
|
1939
|
+
<OptionItem label="item 2" value="2" />
|
|
1940
|
+
</MultiSelect>,
|
|
1941
|
+
);
|
|
1942
|
+
|
|
1943
|
+
// Act
|
|
1944
|
+
const opener = await screen.findByLabelText("Search");
|
|
1945
|
+
await userEvent.click(opener);
|
|
1946
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1947
|
+
|
|
1948
|
+
// Assert
|
|
1949
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1950
|
+
expect(opener).toHaveAttribute(
|
|
1951
|
+
"aria-controls",
|
|
1952
|
+
expect.stringMatching(/^uid-multi-select-dropdown-\d+-wb-id$/),
|
|
1953
|
+
);
|
|
1954
|
+
});
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
describe("a11y > aria-haspopup", () => {
|
|
1958
|
+
it("should have aria-haspopup set on the opener", async () => {
|
|
1959
|
+
// Arrange
|
|
1960
|
+
doRender(
|
|
1961
|
+
<MultiSelect onChange={jest.fn()}>
|
|
1962
|
+
<OptionItem label="item 1" value="1" />
|
|
1963
|
+
<OptionItem label="item 2" value="2" />
|
|
1964
|
+
</MultiSelect>,
|
|
1965
|
+
);
|
|
1966
|
+
|
|
1967
|
+
// Act
|
|
1968
|
+
const opener = await screen.findByRole("button");
|
|
1969
|
+
|
|
1970
|
+
// Assert
|
|
1971
|
+
expect(opener).toHaveAttribute("aria-haspopup", "listbox");
|
|
1972
|
+
});
|
|
1973
|
+
|
|
1974
|
+
it("should have aria-haspopup set on the custom opener", async () => {
|
|
1975
|
+
// Arrange
|
|
1976
|
+
doRender(
|
|
1977
|
+
<MultiSelect
|
|
1978
|
+
onChange={jest.fn()}
|
|
1979
|
+
opener={() => (
|
|
1980
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1981
|
+
)}
|
|
1982
|
+
>
|
|
1983
|
+
<OptionItem label="item 1" value="1" />
|
|
1984
|
+
<OptionItem label="item 2" value="2" />
|
|
1985
|
+
</MultiSelect>,
|
|
1986
|
+
);
|
|
1987
|
+
|
|
1988
|
+
// Act
|
|
1989
|
+
const opener = await screen.findByLabelText("Search");
|
|
1990
|
+
|
|
1991
|
+
// Assert
|
|
1992
|
+
expect(opener).toHaveAttribute("aria-haspopup", "listbox");
|
|
1993
|
+
});
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
describe("a11y > aria-expanded", () => {
|
|
1997
|
+
it("should have aria-expanded=false when closed", async () => {
|
|
1998
|
+
// Arrange
|
|
1999
|
+
doRender(
|
|
2000
|
+
<MultiSelect onChange={jest.fn()}>
|
|
2001
|
+
<OptionItem label="item 1" value="1" />
|
|
2002
|
+
<OptionItem label="item 2" value="2" />
|
|
2003
|
+
</MultiSelect>,
|
|
2004
|
+
);
|
|
2005
|
+
|
|
2006
|
+
// Act
|
|
2007
|
+
const opener = await screen.findByRole("button");
|
|
2008
|
+
|
|
2009
|
+
// Assert
|
|
2010
|
+
expect(opener).toHaveAttribute("aria-expanded", "false");
|
|
2011
|
+
});
|
|
2012
|
+
|
|
2013
|
+
it("updates the aria-expanded value when opening", async () => {
|
|
2014
|
+
// Arrange
|
|
2015
|
+
const {userEvent} = doRender(
|
|
2016
|
+
<MultiSelect onChange={jest.fn()}>
|
|
2017
|
+
<OptionItem label="item 1" value="1" />
|
|
2018
|
+
<OptionItem label="item 2" value="2" />
|
|
2019
|
+
</MultiSelect>,
|
|
2020
|
+
);
|
|
2021
|
+
|
|
2022
|
+
// Act
|
|
2023
|
+
const opener = await screen.findByRole("button");
|
|
2024
|
+
await userEvent.click(opener);
|
|
2025
|
+
|
|
2026
|
+
// Assert
|
|
2027
|
+
expect(opener).toHaveAttribute("aria-expanded", "true");
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
it("should have aria-expanded=false when closed and using a custom opener", async () => {
|
|
2031
|
+
// Arrange
|
|
2032
|
+
doRender(
|
|
2033
|
+
<MultiSelect
|
|
2034
|
+
onChange={jest.fn()}
|
|
2035
|
+
opener={() => (
|
|
2036
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
2037
|
+
)}
|
|
2038
|
+
>
|
|
2039
|
+
<OptionItem label="item 1" value="1" />
|
|
2040
|
+
<OptionItem label="item 2" value="2" />
|
|
2041
|
+
</MultiSelect>,
|
|
2042
|
+
);
|
|
2043
|
+
|
|
2044
|
+
// Act
|
|
2045
|
+
const opener = await screen.findByLabelText("Search");
|
|
2046
|
+
|
|
2047
|
+
// Assert
|
|
2048
|
+
expect(opener).toHaveAttribute("aria-expanded", "false");
|
|
2049
|
+
});
|
|
2050
|
+
|
|
2051
|
+
it("updates the aria-expanded value when opening and using a custom opener", async () => {
|
|
2052
|
+
// Arrange
|
|
2053
|
+
const {userEvent} = doRender(
|
|
2054
|
+
<MultiSelect
|
|
2055
|
+
onChange={jest.fn()}
|
|
2056
|
+
opener={() => (
|
|
2057
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
2058
|
+
)}
|
|
2059
|
+
>
|
|
2060
|
+
<OptionItem label="item 1" value="1" />
|
|
2061
|
+
<OptionItem label="item 2" value="2" />
|
|
2062
|
+
</MultiSelect>,
|
|
2063
|
+
);
|
|
2064
|
+
|
|
2065
|
+
// Act
|
|
2066
|
+
const opener = await screen.findByLabelText("Search");
|
|
2067
|
+
await userEvent.click(opener);
|
|
2068
|
+
|
|
2069
|
+
// Assert
|
|
2070
|
+
expect(opener).toHaveAttribute("aria-expanded", "true");
|
|
2071
|
+
});
|
|
2072
|
+
});
|
|
1780
2073
|
});
|
|
@@ -1340,4 +1340,310 @@ describe("SingleSelect", () => {
|
|
|
1340
1340
|
expect(singleSelect).not.toHaveAttribute("disabled");
|
|
1341
1341
|
});
|
|
1342
1342
|
});
|
|
1343
|
+
|
|
1344
|
+
describe("Ids", () => {
|
|
1345
|
+
it("Should auto-generate an id for the opener if `id` prop is not provided", async () => {
|
|
1346
|
+
// Arrange
|
|
1347
|
+
doRender(
|
|
1348
|
+
<SingleSelect placeholder="Choose" onChange={jest.fn()}>
|
|
1349
|
+
<OptionItem label="item 1" value="1" />
|
|
1350
|
+
<OptionItem label="item 2" value="2" />
|
|
1351
|
+
</SingleSelect>,
|
|
1352
|
+
);
|
|
1353
|
+
|
|
1354
|
+
// Act
|
|
1355
|
+
const opener = await screen.findByRole("button");
|
|
1356
|
+
|
|
1357
|
+
// Assert
|
|
1358
|
+
expect(opener).toHaveAttribute(
|
|
1359
|
+
"id",
|
|
1360
|
+
expect.stringMatching(/^uid-single-select-opener-\d+-wb-id$/),
|
|
1361
|
+
);
|
|
1362
|
+
});
|
|
1363
|
+
it("Should use the `id` prop if provided", async () => {
|
|
1364
|
+
// Arrange
|
|
1365
|
+
const id = "test-id";
|
|
1366
|
+
doRender(
|
|
1367
|
+
<SingleSelect placeholder="Choose" onChange={jest.fn()} id={id}>
|
|
1368
|
+
<OptionItem label="item 1" value="1" />
|
|
1369
|
+
<OptionItem label="item 2" value="2" />
|
|
1370
|
+
</SingleSelect>,
|
|
1371
|
+
);
|
|
1372
|
+
|
|
1373
|
+
// Act
|
|
1374
|
+
const opener = await screen.findByRole("button");
|
|
1375
|
+
|
|
1376
|
+
// Assert
|
|
1377
|
+
expect(opener).toHaveAttribute("id", id);
|
|
1378
|
+
});
|
|
1379
|
+
it("Should auto-generate an id for the dropdown if `dropdownId` prop is not provided", async () => {
|
|
1380
|
+
// Arrange
|
|
1381
|
+
const userEvent = doRender(
|
|
1382
|
+
<SingleSelect placeholder="Choose" onChange={jest.fn()}>
|
|
1383
|
+
<OptionItem label="item 1" value="1" />
|
|
1384
|
+
<OptionItem label="item 2" value="2" />
|
|
1385
|
+
</SingleSelect>,
|
|
1386
|
+
);
|
|
1387
|
+
|
|
1388
|
+
// Act
|
|
1389
|
+
// Open the dropdown
|
|
1390
|
+
const opener = await screen.findByRole("button");
|
|
1391
|
+
await userEvent.click(opener);
|
|
1392
|
+
|
|
1393
|
+
// Assert
|
|
1394
|
+
expect(
|
|
1395
|
+
await screen.findByRole("listbox", {hidden: true}),
|
|
1396
|
+
).toHaveAttribute(
|
|
1397
|
+
"id",
|
|
1398
|
+
expect.stringMatching(/^uid-single-select-dropdown-\d+-wb-id$/),
|
|
1399
|
+
);
|
|
1400
|
+
});
|
|
1401
|
+
it("Should use the `dropdownId` prop if provided", async () => {
|
|
1402
|
+
// Arrange
|
|
1403
|
+
const dropdownId = "test-id";
|
|
1404
|
+
const userEvent = doRender(
|
|
1405
|
+
<SingleSelect
|
|
1406
|
+
placeholder="Choose"
|
|
1407
|
+
onChange={jest.fn()}
|
|
1408
|
+
dropdownId={dropdownId}
|
|
1409
|
+
>
|
|
1410
|
+
<OptionItem label="item 1" value="1" />
|
|
1411
|
+
<OptionItem label="item 2" value="2" />
|
|
1412
|
+
</SingleSelect>,
|
|
1413
|
+
);
|
|
1414
|
+
|
|
1415
|
+
// Act
|
|
1416
|
+
// Open the dropdown
|
|
1417
|
+
const opener = await screen.findByRole("button");
|
|
1418
|
+
await userEvent.click(opener);
|
|
1419
|
+
|
|
1420
|
+
// Assert
|
|
1421
|
+
expect(
|
|
1422
|
+
await screen.findByRole("listbox", {hidden: true}),
|
|
1423
|
+
).toHaveAttribute("id", dropdownId);
|
|
1424
|
+
});
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
describe("a11y > aria-controls", () => {
|
|
1428
|
+
it("Should set the `aria-controls` attribute on the default opener to the provided dropdownId prop", async () => {
|
|
1429
|
+
// Arrange
|
|
1430
|
+
const dropdownId = "test-id";
|
|
1431
|
+
const userEvent = doRender(
|
|
1432
|
+
<SingleSelect
|
|
1433
|
+
placeholder="Choose"
|
|
1434
|
+
onChange={jest.fn()}
|
|
1435
|
+
dropdownId={dropdownId}
|
|
1436
|
+
>
|
|
1437
|
+
<OptionItem label="item 1" value="1" />
|
|
1438
|
+
<OptionItem label="item 2" value="2" />
|
|
1439
|
+
</SingleSelect>,
|
|
1440
|
+
);
|
|
1441
|
+
|
|
1442
|
+
// Act
|
|
1443
|
+
const opener = await screen.findByRole("button");
|
|
1444
|
+
await userEvent.click(opener);
|
|
1445
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1446
|
+
|
|
1447
|
+
// Assert
|
|
1448
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1449
|
+
expect(opener).toHaveAttribute("aria-controls", dropdownId);
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
it("Should set the `aria-controls` attribute on the default opener to the auto-generated dropdownId", async () => {
|
|
1453
|
+
// Arrange
|
|
1454
|
+
const userEvent = doRender(
|
|
1455
|
+
<SingleSelect placeholder="Choose" onChange={jest.fn()}>
|
|
1456
|
+
<OptionItem label="item 1" value="1" />
|
|
1457
|
+
<OptionItem label="item 2" value="2" />
|
|
1458
|
+
</SingleSelect>,
|
|
1459
|
+
);
|
|
1460
|
+
|
|
1461
|
+
// Act
|
|
1462
|
+
const opener = await screen.findByRole("button");
|
|
1463
|
+
await userEvent.click(opener);
|
|
1464
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1465
|
+
|
|
1466
|
+
// Assert
|
|
1467
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1468
|
+
expect(opener).toHaveAttribute(
|
|
1469
|
+
"aria-controls",
|
|
1470
|
+
expect.stringMatching(/^uid-single-select-dropdown-\d+-wb-id$/),
|
|
1471
|
+
);
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
it("Should set the `aria-controls` attribute on the custom opener to the provided dropdownId prop", async () => {
|
|
1475
|
+
// Arrange
|
|
1476
|
+
const dropdownId = "test-id";
|
|
1477
|
+
const userEvent = doRender(
|
|
1478
|
+
<SingleSelect
|
|
1479
|
+
placeholder="Choose"
|
|
1480
|
+
onChange={jest.fn()}
|
|
1481
|
+
dropdownId={dropdownId}
|
|
1482
|
+
opener={() => (
|
|
1483
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1484
|
+
)}
|
|
1485
|
+
>
|
|
1486
|
+
<OptionItem label="item 1" value="1" />
|
|
1487
|
+
<OptionItem label="item 2" value="2" />
|
|
1488
|
+
</SingleSelect>,
|
|
1489
|
+
);
|
|
1490
|
+
|
|
1491
|
+
// Act
|
|
1492
|
+
const opener = await screen.findByLabelText("Search");
|
|
1493
|
+
await userEvent.click(opener);
|
|
1494
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1495
|
+
|
|
1496
|
+
// Assert
|
|
1497
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1498
|
+
expect(opener).toHaveAttribute("aria-controls", dropdownId);
|
|
1499
|
+
});
|
|
1500
|
+
|
|
1501
|
+
it("Should set the `aria-controls` attribute on the custom opener to the auto-generated dropdownId", async () => {
|
|
1502
|
+
// Arrange
|
|
1503
|
+
const userEvent = doRender(
|
|
1504
|
+
<SingleSelect
|
|
1505
|
+
placeholder="Choose"
|
|
1506
|
+
onChange={jest.fn()}
|
|
1507
|
+
opener={() => (
|
|
1508
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1509
|
+
)}
|
|
1510
|
+
>
|
|
1511
|
+
<OptionItem label="item 1" value="1" />
|
|
1512
|
+
<OptionItem label="item 2" value="2" />
|
|
1513
|
+
</SingleSelect>,
|
|
1514
|
+
);
|
|
1515
|
+
|
|
1516
|
+
// Act
|
|
1517
|
+
const opener = await screen.findByLabelText("Search");
|
|
1518
|
+
await userEvent.click(opener);
|
|
1519
|
+
const dropdown = await screen.findByRole("listbox", {hidden: true});
|
|
1520
|
+
|
|
1521
|
+
// Assert
|
|
1522
|
+
expect(opener).toHaveAttribute("aria-controls", dropdown.id);
|
|
1523
|
+
expect(opener).toHaveAttribute(
|
|
1524
|
+
"aria-controls",
|
|
1525
|
+
expect.stringMatching(/^uid-single-select-dropdown-\d+-wb-id$/),
|
|
1526
|
+
);
|
|
1527
|
+
});
|
|
1528
|
+
});
|
|
1529
|
+
|
|
1530
|
+
describe("a11y > aria-haspopup", () => {
|
|
1531
|
+
it("should have aria-haspopup set on the opener", async () => {
|
|
1532
|
+
// Arrange
|
|
1533
|
+
doRender(
|
|
1534
|
+
<SingleSelect placeholder="Choose" onChange={jest.fn()}>
|
|
1535
|
+
<OptionItem label="item 1" value="1" />
|
|
1536
|
+
<OptionItem label="item 2" value="2" />
|
|
1537
|
+
</SingleSelect>,
|
|
1538
|
+
);
|
|
1539
|
+
|
|
1540
|
+
// Act
|
|
1541
|
+
const opener = await screen.findByRole("button");
|
|
1542
|
+
|
|
1543
|
+
// Assert
|
|
1544
|
+
expect(opener).toHaveAttribute("aria-haspopup", "listbox");
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
it("should have aria-haspopup set on the custom opener", async () => {
|
|
1548
|
+
// Arrange
|
|
1549
|
+
doRender(
|
|
1550
|
+
<SingleSelect
|
|
1551
|
+
placeholder="Choose"
|
|
1552
|
+
onChange={jest.fn()}
|
|
1553
|
+
opener={() => (
|
|
1554
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1555
|
+
)}
|
|
1556
|
+
>
|
|
1557
|
+
<OptionItem label="item 1" value="1" />
|
|
1558
|
+
<OptionItem label="item 2" value="2" />
|
|
1559
|
+
</SingleSelect>,
|
|
1560
|
+
);
|
|
1561
|
+
|
|
1562
|
+
// Act
|
|
1563
|
+
const opener = await screen.findByLabelText("Search");
|
|
1564
|
+
|
|
1565
|
+
// Assert
|
|
1566
|
+
expect(opener).toHaveAttribute("aria-haspopup", "listbox");
|
|
1567
|
+
});
|
|
1568
|
+
});
|
|
1569
|
+
|
|
1570
|
+
describe("a11y > aria-expanded", () => {
|
|
1571
|
+
it("should have aria-expanded=false when closed", async () => {
|
|
1572
|
+
// Arrange
|
|
1573
|
+
doRender(
|
|
1574
|
+
<SingleSelect placeholder="Choose" onChange={jest.fn()}>
|
|
1575
|
+
<OptionItem label="item 1" value="1" />
|
|
1576
|
+
<OptionItem label="item 2" value="2" />
|
|
1577
|
+
</SingleSelect>,
|
|
1578
|
+
);
|
|
1579
|
+
|
|
1580
|
+
// Act
|
|
1581
|
+
const opener = await screen.findByRole("button");
|
|
1582
|
+
|
|
1583
|
+
// Assert
|
|
1584
|
+
expect(opener).toHaveAttribute("aria-expanded", "false");
|
|
1585
|
+
});
|
|
1586
|
+
|
|
1587
|
+
it("updates the aria-expanded value when opening", async () => {
|
|
1588
|
+
// Arrange
|
|
1589
|
+
const userEvent = doRender(
|
|
1590
|
+
<SingleSelect placeholder="Choose" onChange={jest.fn()}>
|
|
1591
|
+
<OptionItem label="item 1" value="1" />
|
|
1592
|
+
<OptionItem label="item 2" value="2" />
|
|
1593
|
+
</SingleSelect>,
|
|
1594
|
+
);
|
|
1595
|
+
|
|
1596
|
+
// Act
|
|
1597
|
+
const opener = await screen.findByRole("button");
|
|
1598
|
+
await userEvent.click(opener);
|
|
1599
|
+
|
|
1600
|
+
// Assert
|
|
1601
|
+
expect(opener).toHaveAttribute("aria-expanded", "true");
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1604
|
+
it("should have aria-expanded=false when closed and using a custom opener", async () => {
|
|
1605
|
+
// Arrange
|
|
1606
|
+
doRender(
|
|
1607
|
+
<SingleSelect
|
|
1608
|
+
placeholder="Choose"
|
|
1609
|
+
onChange={jest.fn()}
|
|
1610
|
+
opener={() => (
|
|
1611
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1612
|
+
)}
|
|
1613
|
+
>
|
|
1614
|
+
<OptionItem label="item 1" value="1" />
|
|
1615
|
+
<OptionItem label="item 2" value="2" />
|
|
1616
|
+
</SingleSelect>,
|
|
1617
|
+
);
|
|
1618
|
+
|
|
1619
|
+
// Act
|
|
1620
|
+
const opener = await screen.findByLabelText("Search");
|
|
1621
|
+
|
|
1622
|
+
// Assert
|
|
1623
|
+
expect(opener).toHaveAttribute("aria-expanded", "false");
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
it("updates the aria-expanded value when opening and using a custom opener", async () => {
|
|
1627
|
+
// Arrange
|
|
1628
|
+
const userEvent = doRender(
|
|
1629
|
+
<SingleSelect
|
|
1630
|
+
placeholder="Choose"
|
|
1631
|
+
onChange={jest.fn()}
|
|
1632
|
+
opener={() => (
|
|
1633
|
+
<button aria-label="Search" onClick={jest.fn()} />
|
|
1634
|
+
)}
|
|
1635
|
+
>
|
|
1636
|
+
<OptionItem label="item 1" value="1" />
|
|
1637
|
+
<OptionItem label="item 2" value="2" />
|
|
1638
|
+
</SingleSelect>,
|
|
1639
|
+
);
|
|
1640
|
+
|
|
1641
|
+
// Act
|
|
1642
|
+
const opener = await screen.findByLabelText("Search");
|
|
1643
|
+
await userEvent.click(opener);
|
|
1644
|
+
|
|
1645
|
+
// Assert
|
|
1646
|
+
expect(opener).toHaveAttribute("aria-expanded", "true");
|
|
1647
|
+
});
|
|
1648
|
+
});
|
|
1343
1649
|
});
|