@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.
@@ -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
  });