@khanacademy/wonder-blocks-dropdown 5.3.5 → 5.3.7

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/dist/index.js CHANGED
@@ -20,7 +20,6 @@ var reactPopper = require('react-popper');
20
20
  var wonderBlocksModal = require('@khanacademy/wonder-blocks-modal');
21
21
  var core = require('@popperjs/core');
22
22
  var caretDownIcon = require('@phosphor-icons/core/bold/caret-down-bold.svg');
23
- var reactRouter = require('react-router');
24
23
 
25
24
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
26
25
 
@@ -1305,11 +1304,12 @@ class DropdownCore extends React__namespace.Component {
1305
1304
  open,
1306
1305
  opener,
1307
1306
  style,
1308
- className
1307
+ className,
1308
+ disabled
1309
1309
  } = this.props;
1310
1310
  return React__namespace.createElement(wonderBlocksCore.View, {
1311
- onKeyDown: this.handleKeyDown,
1312
- onKeyUp: this.handleKeyUp,
1311
+ onKeyDown: !disabled ? this.handleKeyDown : undefined,
1312
+ onKeyUp: !disabled ? this.handleKeyUp : undefined,
1313
1313
  style: [styles$4.menuWrapper, style],
1314
1314
  className: className
1315
1315
  }, this.renderLiveRegion(), opener, open && this.renderDropdown());
@@ -1660,16 +1660,37 @@ const styles$2 = aphrodite.StyleSheet.create({
1660
1660
  const _excluded$2 = ["children", "disabled", "error", "id", "isPlaceholder", "light", "open", "testId", "onOpenChanged"];
1661
1661
  const StyledButton = wonderBlocksCore.addStyle("button");
1662
1662
  class SelectOpener extends React__namespace.Component {
1663
- constructor(...args) {
1664
- super(...args);
1663
+ constructor(props) {
1664
+ super(props);
1665
1665
  this.handleClick = e => {
1666
1666
  const {
1667
1667
  open
1668
1668
  } = this.props;
1669
1669
  this.props.onOpenChanged(!open);
1670
1670
  };
1671
+ this.handleKeyDown = e => {
1672
+ const keyCode = e.key;
1673
+ if (keyCode === "Enter" || keyCode === " ") {
1674
+ this.setState({
1675
+ pressed: true
1676
+ });
1677
+ e.preventDefault();
1678
+ }
1679
+ };
1680
+ this.handleKeyUp = e => {
1681
+ const keyCode = e.key;
1682
+ if (keyCode === "Enter" || keyCode === " ") {
1683
+ this.setState({
1684
+ pressed: false
1685
+ });
1686
+ this.handleClick(e);
1687
+ }
1688
+ };
1689
+ this.state = {
1690
+ pressed: false
1691
+ };
1671
1692
  }
1672
- renderClickableBehavior(router) {
1693
+ render() {
1673
1694
  const _this$props = this.props,
1674
1695
  {
1675
1696
  children,
@@ -1682,40 +1703,29 @@ class SelectOpener extends React__namespace.Component {
1682
1703
  testId
1683
1704
  } = _this$props,
1684
1705
  sharedProps = _objectWithoutPropertiesLoose(_this$props, _excluded$2);
1685
- const ClickableBehavior = wonderBlocksClickable.getClickableBehavior(router);
1686
- return React__namespace.createElement(ClickableBehavior, {
1687
- disabled: disabled,
1688
- onClick: this.handleClick
1689
- }, (state, childrenProps) => {
1690
- const stateStyles = _generateStyles(light, isPlaceholder, error);
1691
- const {
1692
- hovered,
1693
- focused,
1694
- pressed
1695
- } = state;
1696
- const iconColor = light ? disabled || pressed ? "currentColor" : tokens__namespace.color.white : disabled ? tokens__namespace.color.offBlack32 : tokens__namespace.color.offBlack64;
1697
- const style = [styles$1.shared, stateStyles.default, disabled && stateStyles.disabled, !disabled && (pressed ? stateStyles.active : (hovered || focused) && stateStyles.focus)];
1698
- return React__namespace.createElement(StyledButton, _extends({}, sharedProps, {
1699
- "aria-expanded": open ? "true" : "false",
1700
- "aria-haspopup": "listbox",
1701
- "data-testid": testId,
1702
- disabled: disabled,
1703
- id: id,
1704
- style: style,
1705
- type: "button"
1706
- }, childrenProps), React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
1707
- style: styles$1.text
1708
- }, children || "\u00A0"), React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
1709
- icon: caretDownIcon__default["default"],
1710
- color: iconColor,
1711
- size: "small",
1712
- style: styles$1.caret,
1713
- "aria-hidden": "true"
1714
- }));
1715
- });
1716
- }
1717
- render() {
1718
- return React__namespace.createElement(reactRouter.__RouterContext.Consumer, null, router => this.renderClickableBehavior(router));
1706
+ const stateStyles = _generateStyles(light, isPlaceholder, error);
1707
+ const iconColor = light ? disabled || error ? "currentColor" : tokens__namespace.color.white : disabled ? tokens__namespace.color.offBlack32 : tokens__namespace.color.offBlack64;
1708
+ const style = [styles$1.shared, stateStyles.default, disabled && stateStyles.disabled, !disabled && this.state.pressed && stateStyles.pressed];
1709
+ return React__namespace.createElement(StyledButton, _extends({}, sharedProps, {
1710
+ "aria-disabled": disabled,
1711
+ "aria-expanded": open ? "true" : "false",
1712
+ "aria-haspopup": "listbox",
1713
+ "data-testid": testId,
1714
+ id: id,
1715
+ style: style,
1716
+ type: "button",
1717
+ onClick: !disabled ? this.handleClick : undefined,
1718
+ onKeyDown: !disabled ? this.handleKeyDown : undefined,
1719
+ onKeyUp: !disabled ? this.handleKeyUp : undefined
1720
+ }), React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
1721
+ style: styles$1.text
1722
+ }, children || "\u00A0"), React__namespace.createElement(wonderBlocksIcon.PhosphorIcon, {
1723
+ icon: caretDownIcon__default["default"],
1724
+ color: iconColor,
1725
+ size: "small",
1726
+ style: styles$1.caret,
1727
+ "aria-hidden": "true"
1728
+ }));
1719
1729
  }
1720
1730
  }
1721
1731
  SelectOpener.defaultProps = {
@@ -1764,61 +1774,91 @@ const _generateStyles = (light, placeholder, error) => {
1764
1774
  }
1765
1775
  let newStyles = {};
1766
1776
  if (light) {
1777
+ const focusHoverStyling = {
1778
+ borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.white,
1779
+ borderWidth: tokens__namespace.spacing.xxxxSmall_2,
1780
+ paddingLeft: adjustedPaddingLeft,
1781
+ paddingRight: adjustedPaddingRight
1782
+ };
1783
+ const activePressedStyling = {
1784
+ paddingLeft: adjustedPaddingLeft,
1785
+ paddingRight: adjustedPaddingRight,
1786
+ borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.fadedBlue,
1787
+ borderWidth: tokens__namespace.border.width.thin,
1788
+ color: error ? tokens__namespace.color.offBlack64 : placeholder ? tokens.mix(tokens__namespace.color.white32, tokens__namespace.color.blue) : tokens__namespace.color.fadedBlue,
1789
+ backgroundColor: error ? tokens__namespace.color.fadedRed : tokens__namespace.color.activeBlue
1790
+ };
1767
1791
  newStyles = {
1768
1792
  default: {
1769
1793
  background: error ? tokens__namespace.color.fadedRed8 : "transparent",
1770
- color: placeholder ? tokens__namespace.color.white50 : tokens__namespace.color.white,
1794
+ color: error ? tokens__namespace.color.offBlack64 : placeholder ? tokens__namespace.color.white50 : tokens__namespace.color.white,
1771
1795
  borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.white50,
1772
- borderWidth: tokens__namespace.border.width.hairline
1773
- },
1774
- focus: {
1775
- borderColor: error ? tokens__namespace.color.fadedRed8 : tokens__namespace.color.white,
1776
- borderWidth: tokens__namespace.spacing.xxxxSmall_2,
1777
- paddingLeft: adjustedPaddingLeft,
1778
- paddingRight: adjustedPaddingRight
1779
- },
1780
- active: {
1781
- paddingLeft: adjustedPaddingLeft,
1782
- paddingRight: adjustedPaddingRight,
1783
- borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.fadedBlue,
1784
- borderWidth: tokens__namespace.border.width.thin,
1785
- color: placeholder ? tokens.mix(tokens__namespace.color.white32, tokens__namespace.color.blue) : tokens__namespace.color.fadedBlue,
1786
- backgroundColor: error ? tokens__namespace.color.fadedRed : tokens__namespace.color.activeBlue
1796
+ borderWidth: tokens__namespace.border.width.hairline,
1797
+ ":hover:not([aria-disabled=true])": focusHoverStyling,
1798
+ ["@media not (hover: hover)"]: {
1799
+ ":hover:not([aria-disabled=true])": {
1800
+ borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.white50,
1801
+ borderWidth: tokens__namespace.border.width.hairline,
1802
+ paddingLeft: tokens__namespace.spacing.medium_16,
1803
+ paddingRight: tokens__namespace.spacing.small_12
1804
+ }
1805
+ },
1806
+ ":focus-visible:not([aria-disabled=true])": focusHoverStyling,
1807
+ ":active:not([aria-disabled=true])": activePressedStyling
1787
1808
  },
1788
1809
  disabled: {
1789
1810
  background: "transparent",
1790
1811
  borderColor: tokens.mix(tokens__namespace.color.white32, tokens__namespace.color.blue),
1791
1812
  color: tokens.mix(tokens__namespace.color.white32, tokens__namespace.color.blue),
1792
- cursor: "auto"
1793
- }
1813
+ cursor: "not-allowed",
1814
+ ":focus-visible": {
1815
+ boxShadow: `0 0 0 1px ${tokens__namespace.color.offBlack32}, 0 0 0 3px ${tokens__namespace.color.fadedBlue}`
1816
+ }
1817
+ },
1818
+ pressed: activePressedStyling
1794
1819
  };
1795
1820
  } else {
1821
+ const focusHoverStyling = {
1822
+ borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.blue,
1823
+ borderWidth: tokens__namespace.border.width.thin,
1824
+ paddingLeft: adjustedPaddingLeft,
1825
+ paddingRight: adjustedPaddingRight
1826
+ };
1827
+ const activePressedStyling = {
1828
+ background: error ? tokens__namespace.color.fadedRed : tokens__namespace.color.fadedBlue,
1829
+ borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.activeBlue,
1830
+ borderWidth: tokens__namespace.border.width.thin,
1831
+ paddingLeft: adjustedPaddingLeft,
1832
+ paddingRight: adjustedPaddingRight
1833
+ };
1796
1834
  newStyles = {
1797
1835
  default: {
1798
1836
  background: error ? tokens__namespace.color.fadedRed8 : tokens__namespace.color.white,
1799
1837
  borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.offBlack16,
1800
1838
  borderWidth: tokens__namespace.border.width.hairline,
1801
- color: placeholder ? tokens__namespace.color.offBlack64 : tokens__namespace.color.offBlack
1802
- },
1803
- focus: {
1804
- borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.blue,
1805
- borderWidth: tokens__namespace.border.width.thin,
1806
- paddingLeft: adjustedPaddingLeft,
1807
- paddingRight: adjustedPaddingRight
1808
- },
1809
- active: {
1810
- background: error ? tokens__namespace.color.fadedRed : tokens__namespace.color.fadedBlue,
1811
- borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.activeBlue,
1812
- borderWidth: tokens__namespace.border.width.thin,
1813
- paddingLeft: adjustedPaddingLeft,
1814
- paddingRight: adjustedPaddingRight
1839
+ color: placeholder ? tokens__namespace.color.offBlack64 : tokens__namespace.color.offBlack,
1840
+ ":hover:not([aria-disabled=true])": focusHoverStyling,
1841
+ ["@media not (hover: hover)"]: {
1842
+ ":hover:not([aria-disabled=true])": {
1843
+ borderColor: error ? tokens__namespace.color.red : tokens__namespace.color.offBlack16,
1844
+ borderWidth: tokens__namespace.border.width.hairline,
1845
+ paddingLeft: tokens__namespace.spacing.medium_16,
1846
+ paddingRight: tokens__namespace.spacing.small_12
1847
+ }
1848
+ },
1849
+ ":focus-visible:not([aria-disabled=true])": focusHoverStyling,
1850
+ ":active:not([aria-disabled=true])": activePressedStyling
1815
1851
  },
1816
1852
  disabled: {
1817
1853
  background: tokens__namespace.color.offWhite,
1818
1854
  borderColor: tokens__namespace.color.offBlack16,
1819
1855
  color: tokens__namespace.color.offBlack64,
1820
- cursor: "auto"
1821
- }
1856
+ cursor: "not-allowed",
1857
+ ":focus-visible": {
1858
+ boxShadow: `0 0 0 1px ${tokens__namespace.color.white}, 0 0 0 3px ${tokens__namespace.color.offBlack32}`
1859
+ }
1860
+ },
1861
+ pressed: activePressedStyling
1822
1862
  };
1823
1863
  }
1824
1864
  stateStyles[styleKey] = aphrodite.StyleSheet.create(newStyles);
@@ -1904,7 +1944,7 @@ class SingleSelect extends React__namespace.Component {
1904
1944
  }
1905
1945
  static getDerivedStateFromProps(props, state) {
1906
1946
  return {
1907
- open: typeof props.opened === "boolean" ? props.opened : state.open
1947
+ open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
1908
1948
  };
1909
1949
  }
1910
1950
  filterChildren(children) {
@@ -1971,7 +2011,8 @@ class SingleSelect extends React__namespace.Component {
1971
2011
  light,
1972
2012
  style,
1973
2013
  "aria-invalid": ariaInvalid,
1974
- "aria-required": ariaRequired
2014
+ "aria-required": ariaRequired,
2015
+ disabled
1975
2016
  } = this.props;
1976
2017
  const {
1977
2018
  searchText
@@ -2000,7 +2041,8 @@ class SingleSelect extends React__namespace.Component {
2000
2041
  searchText: isFilterable ? searchText : "",
2001
2042
  labels: labels,
2002
2043
  "aria-invalid": ariaInvalid,
2003
- "aria-required": ariaRequired
2044
+ "aria-required": ariaRequired,
2045
+ disabled: disabled
2004
2046
  });
2005
2047
  }
2006
2048
  }
@@ -2104,7 +2146,7 @@ class MultiSelect extends React__namespace.Component {
2104
2146
  }
2105
2147
  static getDerivedStateFromProps(props, state) {
2106
2148
  return {
2107
- open: typeof props.opened === "boolean" ? props.opened : state.open
2149
+ open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
2108
2150
  };
2109
2151
  }
2110
2152
  componentDidUpdate(prevProps) {
@@ -2269,7 +2311,8 @@ class MultiSelect extends React__namespace.Component {
2269
2311
  children,
2270
2312
  isFilterable,
2271
2313
  "aria-invalid": ariaInvalid,
2272
- "aria-required": ariaRequired
2314
+ "aria-required": ariaRequired,
2315
+ disabled
2273
2316
  } = this.props;
2274
2317
  const {
2275
2318
  open,
@@ -2308,7 +2351,8 @@ class MultiSelect extends React__namespace.Component {
2308
2351
  someResults: someSelected
2309
2352
  },
2310
2353
  "aria-invalid": ariaInvalid,
2311
- "aria-required": ariaRequired
2354
+ "aria-required": ariaRequired,
2355
+ disabled: disabled
2312
2356
  });
2313
2357
  }
2314
2358
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-dropdown",
3
- "version": "5.3.5",
3
+ "version": "5.3.7",
4
4
  "design": "v1",
5
5
  "description": "Dropdown variants for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -16,13 +16,13 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@babel/runtime": "^7.18.6",
19
- "@khanacademy/wonder-blocks-cell": "^3.3.6",
19
+ "@khanacademy/wonder-blocks-cell": "^3.3.7",
20
20
  "@khanacademy/wonder-blocks-clickable": "^4.2.2",
21
21
  "@khanacademy/wonder-blocks-core": "^6.4.1",
22
22
  "@khanacademy/wonder-blocks-icon": "^4.1.1",
23
- "@khanacademy/wonder-blocks-layout": "^2.0.33",
24
- "@khanacademy/wonder-blocks-modal": "^5.1.3",
25
- "@khanacademy/wonder-blocks-search-field": "^2.2.14",
23
+ "@khanacademy/wonder-blocks-layout": "^2.1.0",
24
+ "@khanacademy/wonder-blocks-modal": "^5.1.4",
25
+ "@khanacademy/wonder-blocks-search-field": "^2.2.15",
26
26
  "@khanacademy/wonder-blocks-timing": "^5.0.0",
27
27
  "@khanacademy/wonder-blocks-tokens": "^1.3.0",
28
28
  "@khanacademy/wonder-blocks-typography": "^2.1.12"
@@ -772,4 +772,80 @@ describe("DropdownCore", () => {
772
772
  expect(container).toHaveTextContent("3 items");
773
773
  });
774
774
  });
775
+
776
+ describe("onOpenChanged", () => {
777
+ it("Should be triggered when the down key is pressed and the menu is closed", async () => {
778
+ // Arrange
779
+ const onOpenMock = jest.fn();
780
+
781
+ render(
782
+ <DropdownCore
783
+ initialFocusedIndex={undefined}
784
+ onSearchTextChanged={jest.fn()}
785
+ // mock the items (3 options)
786
+ items={items}
787
+ role="listbox"
788
+ open={false}
789
+ // mock the opener elements
790
+ opener={<button />}
791
+ onOpenChanged={onOpenMock}
792
+ />,
793
+ );
794
+ // Act
795
+ // Press the button
796
+ const button = await screen.findByRole("button");
797
+ // NOTE: we need to use fireEvent here because await userEvent doesn't
798
+ // support keyUp/Down events and we use these handlers to override
799
+ // the default behavior of the button.
800
+ // eslint-disable-next-line testing-library/prefer-user-event
801
+ fireEvent.keyDown(button, {
802
+ keyCode: 40,
803
+ });
804
+ // eslint-disable-next-line testing-library/prefer-user-event
805
+ fireEvent.keyUp(button, {
806
+ keyCode: 40,
807
+ });
808
+
809
+ // Assert
810
+ expect(onOpenMock).toHaveBeenCalledTimes(1);
811
+ expect(onOpenMock).toHaveBeenCalledWith(true);
812
+ });
813
+
814
+ it("Should not be triggered when the dropdown is disabled and the down key is pressed and the menu is closed", async () => {
815
+ // Arrange
816
+ const onOpenMock = jest.fn();
817
+
818
+ render(
819
+ <DropdownCore
820
+ initialFocusedIndex={undefined}
821
+ onSearchTextChanged={jest.fn()}
822
+ // mock the items (3 options)
823
+ items={items}
824
+ role="listbox"
825
+ open={false}
826
+ // mock the opener elements
827
+ opener={<button />}
828
+ onOpenChanged={onOpenMock}
829
+ disabled={true}
830
+ />,
831
+ );
832
+ // Act
833
+ // Press the button
834
+ const button = await screen.findByRole("button");
835
+ // NOTE: we need to use fireEvent here because await userEvent doesn't
836
+ // support keyUp/Down events and we use these handlers to override
837
+ // the default behavior of the button.
838
+ // eslint-disable-next-line testing-library/prefer-user-event
839
+ fireEvent.keyDown(button, {
840
+ keyCode: 40,
841
+ });
842
+ // eslint-disable-next-line testing-library/prefer-user-event
843
+ fireEvent.keyUp(button, {
844
+ keyCode: 40,
845
+ });
846
+
847
+ // Assert
848
+ expect(onOpenMock).toHaveBeenCalledTimes(0);
849
+ });
850
+ });
775
851
  });
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-constant-condition */
2
2
  /* eslint-disable max-lines */
3
3
  import * as React from "react";
4
- import {fireEvent, render, screen, waitFor} from "@testing-library/react";
4
+ import {render, screen, waitFor} from "@testing-library/react";
5
5
  import {
6
6
  userEvent as ue,
7
7
  PointerEventsCheckLevel,
@@ -1539,55 +1539,191 @@ describe("MultiSelect", () => {
1539
1539
  });
1540
1540
  });
1541
1541
 
1542
- describe("error state styles", () => {
1543
- it("should apply the error styles to the dropdown on hover", async () => {
1542
+ describe("a11y > Focusable", () => {
1543
+ it("should be focusable", () => {
1544
1544
  // Arrange
1545
- const {userEvent} = doRender(
1545
+ doRender(
1546
+ <MultiSelect onChange={jest.fn()} testId="select-focus-test">
1547
+ <OptionItem label="item 1" value="1" />
1548
+ <OptionItem label="item 2" value="2" />
1549
+ <OptionItem label="item 3" value="3" />
1550
+ </MultiSelect>,
1551
+ );
1552
+
1553
+ // Act
1554
+ const multiSelect = screen.getByTestId("select-focus-test");
1555
+ multiSelect.focus();
1556
+
1557
+ // Assert
1558
+ expect(multiSelect).toHaveFocus();
1559
+ });
1560
+
1561
+ it("should be focusable when disabled", () => {
1562
+ // Arrange
1563
+ doRender(
1546
1564
  <MultiSelect
1547
1565
  onChange={jest.fn()}
1548
- error={true}
1549
- testId="multiselect-error-hover"
1566
+ testId="select-focus-test"
1567
+ disabled={true}
1550
1568
  >
1551
- {[<OptionItem label="Banana" value="banana" />]}
1569
+ <OptionItem label="item 1" value="1" />
1570
+ <OptionItem label="item 2" value="2" />
1571
+ <OptionItem label="item 3" value="3" />
1552
1572
  </MultiSelect>,
1553
1573
  );
1554
- const dropdown = await screen.findByTestId(
1555
- "multiselect-error-hover",
1556
- );
1557
1574
 
1558
1575
  // Act
1559
- await userEvent.hover(dropdown);
1576
+ const multiSelect = screen.getByTestId("select-focus-test");
1577
+ multiSelect.focus();
1560
1578
 
1561
1579
  // Assert
1562
- expect(dropdown).toHaveStyle("border-color: #d92916");
1563
- expect(dropdown).toHaveStyle("border-width: 2px");
1580
+ expect(multiSelect).toHaveFocus();
1564
1581
  });
1582
+ });
1565
1583
 
1566
- it("should apply the error styles to the dropdown on mouse down", async () => {
1584
+ describe("Disabled state", () => {
1585
+ it("should set the `aria-disabled` attribute to `true` if `disabled` prop is `true`", () => {
1567
1586
  // Arrange
1587
+
1588
+ // Act
1568
1589
  doRender(
1569
1590
  <MultiSelect
1570
1591
  onChange={jest.fn()}
1571
- error={true}
1572
- testId="multiselect-error-active"
1592
+ testId="select-focus-test"
1593
+ disabled={true}
1573
1594
  >
1574
- {[<OptionItem label="Banana" value="banana" />]}
1595
+ <OptionItem label="item 1" value="1" />
1596
+ <OptionItem label="item 2" value="2" />
1597
+ <OptionItem label="item 3" value="3" />
1575
1598
  </MultiSelect>,
1576
1599
  );
1577
- const dropdown = await screen.findByTestId(
1578
- "multiselect-error-active",
1600
+ const multiSelect = screen.getByTestId("select-focus-test");
1601
+
1602
+ // Assert
1603
+ expect(multiSelect).toHaveAttribute("aria-disabled", "true");
1604
+ });
1605
+
1606
+ it("should not set the `disabled` attribute if `disabled` prop is `true` since `aria-disabled` is used instead", () => {
1607
+ // Arrange
1608
+
1609
+ // Act
1610
+ doRender(
1611
+ <MultiSelect
1612
+ onChange={jest.fn()}
1613
+ testId="select-focus-test"
1614
+ disabled={true}
1615
+ >
1616
+ <OptionItem label="item 1" value="1" />
1617
+ <OptionItem label="item 2" value="2" />
1618
+ <OptionItem label="item 3" value="3" />
1619
+ </MultiSelect>,
1579
1620
  );
1621
+ const multiSelect = screen.getByTestId("select-focus-test");
1622
+
1623
+ // Assert
1624
+ expect(multiSelect).not.toHaveAttribute("disabled");
1625
+ });
1626
+
1627
+ it("should not be opened if it is disabled and `open` prop is set to true", () => {
1628
+ // Arrange
1580
1629
 
1581
1630
  // Act
1582
- // eslint-disable-next-line testing-library/prefer-user-event
1583
- fireEvent.mouseDown(dropdown);
1631
+ doRender(
1632
+ <MultiSelect onChange={jest.fn()} disabled={true} opened={true}>
1633
+ <OptionItem label="item 1" value="1" />
1634
+ <OptionItem label="item 2" value="2" />
1635
+ <OptionItem label="item 3" value="3" />
1636
+ </MultiSelect>,
1637
+ );
1584
1638
 
1585
1639
  // Assert
1586
- expect(dropdown).toHaveStyle("border-color: #d92916");
1587
- expect(dropdown).toHaveStyle("border-width: 2px");
1588
- expect(dropdown).toHaveStyle(
1589
- "background-color: rgb(243, 187, 180)",
1640
+ expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
1641
+ });
1642
+ });
1643
+
1644
+ describe("a11y > Focusable", () => {
1645
+ it("should be focusable", () => {
1646
+ // Arrange
1647
+ doRender(
1648
+ <MultiSelect onChange={jest.fn()} testId="select-focus-test">
1649
+ <OptionItem label="item 1" value="1" />
1650
+ <OptionItem label="item 2" value="2" />
1651
+ <OptionItem label="item 3" value="3" />
1652
+ </MultiSelect>,
1653
+ );
1654
+
1655
+ // Act
1656
+ const multiSelect = screen.getByTestId("select-focus-test");
1657
+ multiSelect.focus();
1658
+
1659
+ // Assert
1660
+ expect(multiSelect).toHaveFocus();
1661
+ });
1662
+
1663
+ it("should be focusable when disabled", () => {
1664
+ // Arrange
1665
+ doRender(
1666
+ <MultiSelect
1667
+ onChange={jest.fn()}
1668
+ testId="select-focus-test"
1669
+ disabled={true}
1670
+ >
1671
+ <OptionItem label="item 1" value="1" />
1672
+ <OptionItem label="item 2" value="2" />
1673
+ <OptionItem label="item 3" value="3" />
1674
+ </MultiSelect>,
1675
+ );
1676
+
1677
+ // Act
1678
+ const multiSelect = screen.getByTestId("select-focus-test");
1679
+ multiSelect.focus();
1680
+
1681
+ // Assert
1682
+ expect(multiSelect).toHaveFocus();
1683
+ });
1684
+ });
1685
+
1686
+ describe("Disabled state", () => {
1687
+ it("should set the `aria-disabled` attribute to `true` if `disabled` prop is `true`", () => {
1688
+ // Arrange
1689
+
1690
+ // Act
1691
+ doRender(
1692
+ <MultiSelect
1693
+ onChange={jest.fn()}
1694
+ testId="select-focus-test"
1695
+ disabled={true}
1696
+ >
1697
+ <OptionItem label="item 1" value="1" />
1698
+ <OptionItem label="item 2" value="2" />
1699
+ <OptionItem label="item 3" value="3" />
1700
+ </MultiSelect>,
1701
+ );
1702
+ const multiSelect = screen.getByTestId("select-focus-test");
1703
+
1704
+ // Assert
1705
+ expect(multiSelect).toHaveAttribute("aria-disabled", "true");
1706
+ });
1707
+
1708
+ it("should not set the `disabled` attribute if `disabled` prop is `true` since `aria-disabled` is used instead", () => {
1709
+ // Arrange
1710
+
1711
+ // Act
1712
+ doRender(
1713
+ <MultiSelect
1714
+ onChange={jest.fn()}
1715
+ testId="select-focus-test"
1716
+ disabled={true}
1717
+ >
1718
+ <OptionItem label="item 1" value="1" />
1719
+ <OptionItem label="item 2" value="2" />
1720
+ <OptionItem label="item 3" value="3" />
1721
+ </MultiSelect>,
1590
1722
  );
1723
+ const multiSelect = screen.getByTestId("select-focus-test");
1724
+
1725
+ // Assert
1726
+ expect(multiSelect).not.toHaveAttribute("disabled");
1591
1727
  });
1592
1728
  });
1593
1729
  });