@khanacademy/wonder-blocks-dropdown 5.3.6 → 5.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @khanacademy/wonder-blocks-dropdown
2
2
 
3
+ ## 5.3.8
4
+
5
+ ### Patch Changes
6
+
7
+ - cb95286d: - SingleSelect and MultiSelect: Fixing bug where the components can be opened via keyboard when it is disabled because it has 0 items
8
+ - SingleSelect: If all option items are disabled, the SingleSelect is disabled, similar to the behaviour of the MultiSelect
9
+ - @khanacademy/wonder-blocks-modal@5.1.5
10
+ - @khanacademy/wonder-blocks-search-field@2.2.16
11
+
12
+ ## 5.3.7
13
+
14
+ ### Patch Changes
15
+
16
+ - f7ff9a77: SingleSelect and MultiSelect: Disable keyboard interactions to open the select if the `disabled` prop is set to `true`. Prevent select from being in an open state if
17
+ `disabled` prop is `true`.
18
+
3
19
  ## 5.3.6
4
20
 
5
21
  ### Patch Changes
@@ -77,6 +77,10 @@ declare const _default: React.ComponentType<Readonly<{
77
77
  * top. The items will be filtered by the input.
78
78
  */
79
79
  isFilterable?: boolean | undefined;
80
+ /**
81
+ * Whether the dropdown and it's interactions should be disabled.
82
+ */
83
+ disabled?: boolean | undefined;
80
84
  /**
81
85
  * Whether this menu should be left-aligned or right-aligned with the
82
86
  * opener component. Defaults to left-aligned.
@@ -196,7 +196,7 @@ export default class MultiSelect extends React.Component<Props, State> {
196
196
  handleOpenerRef: (node?: any) => void;
197
197
  handleSearchTextChanged: (searchText: string) => void;
198
198
  handleClick: (e: React.SyntheticEvent) => void;
199
- renderOpener(allChildren: React.ReactElement<React.ComponentProps<typeof OptionItem>>[]): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
199
+ renderOpener(allChildren: React.ReactElement<React.ComponentProps<typeof OptionItem>>[], isDisabled: boolean): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
200
200
  render(): React.ReactNode;
201
201
  }
202
202
  export {};
@@ -204,7 +204,7 @@ export default class SingleSelect extends React.Component<Props, State> {
204
204
  handleSearchTextChanged: (searchText: string) => void;
205
205
  handleOpenerRef: (node?: any) => void;
206
206
  handleClick: (e: React.SyntheticEvent) => void;
207
- renderOpener(numItems: number): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
207
+ renderOpener(isDisabled: boolean): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
208
208
  render(): React.ReactNode;
209
209
  }
210
210
  export {};
package/dist/es/index.js CHANGED
@@ -1274,11 +1274,12 @@ class DropdownCore extends React.Component {
1274
1274
  open,
1275
1275
  opener,
1276
1276
  style,
1277
- className
1277
+ className,
1278
+ disabled
1278
1279
  } = this.props;
1279
1280
  return React.createElement(View, {
1280
- onKeyDown: this.handleKeyDown,
1281
- onKeyUp: this.handleKeyUp,
1281
+ onKeyDown: !disabled ? this.handleKeyDown : undefined,
1282
+ onKeyUp: !disabled ? this.handleKeyUp : undefined,
1282
1283
  style: [styles$4.menuWrapper, style],
1283
1284
  className: className
1284
1285
  }, this.renderLiveRegion(), opener, open && this.renderDropdown());
@@ -1834,7 +1835,7 @@ const _generateStyles = (light, placeholder, error) => {
1834
1835
  return stateStyles[styleKey];
1835
1836
  };
1836
1837
 
1837
- const _excluded$1 = ["children", "disabled", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required"];
1838
+ const _excluded$1 = ["children", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required"];
1838
1839
  class SingleSelect extends React.Component {
1839
1840
  constructor(props) {
1840
1841
  super(props);
@@ -1913,7 +1914,7 @@ class SingleSelect extends React.Component {
1913
1914
  }
1914
1915
  static getDerivedStateFromProps(props, state) {
1915
1916
  return {
1916
- open: typeof props.opened === "boolean" ? props.opened : state.open
1917
+ open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
1917
1918
  };
1918
1919
  }
1919
1920
  filterChildren(children) {
@@ -1931,11 +1932,10 @@ class SingleSelect extends React.Component {
1931
1932
  } = this.props;
1932
1933
  return this.mapOptionItemsToDropdownItems(isFilterable ? this.filterChildren(children) : children);
1933
1934
  }
1934
- renderOpener(numItems) {
1935
+ renderOpener(isDisabled) {
1935
1936
  const _this$props = this.props,
1936
1937
  {
1937
1938
  children,
1938
- disabled,
1939
1939
  error,
1940
1940
  id,
1941
1941
  light,
@@ -1950,12 +1950,12 @@ class SingleSelect extends React.Component {
1950
1950
  const menuText = selectedItem ? getLabel(selectedItem.props) : placeholder;
1951
1951
  const dropdownOpener = opener ? React.createElement(DropdownOpener, {
1952
1952
  onClick: this.handleClick,
1953
- disabled: numItems === 0 || disabled,
1953
+ disabled: isDisabled,
1954
1954
  ref: this.handleOpenerRef,
1955
1955
  text: menuText,
1956
1956
  opened: this.state.open
1957
1957
  }, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
1958
- disabled: numItems === 0 || disabled,
1958
+ disabled: isDisabled,
1959
1959
  id: id,
1960
1960
  error: error,
1961
1961
  isPlaceholder: !selectedItem,
@@ -1980,14 +1980,17 @@ class SingleSelect extends React.Component {
1980
1980
  light,
1981
1981
  style,
1982
1982
  "aria-invalid": ariaInvalid,
1983
- "aria-required": ariaRequired
1983
+ "aria-required": ariaRequired,
1984
+ disabled
1984
1985
  } = this.props;
1985
1986
  const {
1986
1987
  searchText
1987
1988
  } = this.state;
1988
1989
  const allChildren = React.Children.toArray(children).filter(Boolean);
1990
+ const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
1989
1991
  const items = this.getMenuItems(allChildren);
1990
- const opener = this.renderOpener(allChildren.length);
1992
+ const isDisabled = numEnabledOptions === 0 || disabled;
1993
+ const opener = this.renderOpener(isDisabled);
1991
1994
  return React.createElement(DropdownCore$1, {
1992
1995
  role: "listbox",
1993
1996
  selectionType: "single",
@@ -2009,7 +2012,8 @@ class SingleSelect extends React.Component {
2009
2012
  searchText: isFilterable ? searchText : "",
2010
2013
  labels: labels,
2011
2014
  "aria-invalid": ariaInvalid,
2012
- "aria-required": ariaRequired
2015
+ "aria-required": ariaRequired,
2016
+ disabled: isDisabled
2013
2017
  });
2014
2018
  }
2015
2019
  }
@@ -2028,7 +2032,7 @@ SingleSelect.defaultProps = {
2028
2032
  }
2029
2033
  };
2030
2034
 
2031
- const _excluded = ["disabled", "id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required"];
2035
+ const _excluded = ["id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required"];
2032
2036
  class MultiSelect extends React.Component {
2033
2037
  constructor(props) {
2034
2038
  super(props);
@@ -2113,7 +2117,7 @@ class MultiSelect extends React.Component {
2113
2117
  }
2114
2118
  static getDerivedStateFromProps(props, state) {
2115
2119
  return {
2116
- open: typeof props.opened === "boolean" ? props.opened : state.open
2120
+ open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
2117
2121
  };
2118
2122
  }
2119
2123
  componentDidUpdate(prevProps) {
@@ -2235,10 +2239,9 @@ class MultiSelect extends React.Component {
2235
2239
  }
2236
2240
  return [...lastSelectedItems, ...restOfTheChildren.map(this.mapOptionItemToDropdownItem)];
2237
2241
  }
2238
- renderOpener(allChildren) {
2242
+ renderOpener(allChildren, isDisabled) {
2239
2243
  const _this$props = this.props,
2240
2244
  {
2241
- disabled,
2242
2245
  id,
2243
2246
  light,
2244
2247
  opener,
@@ -2249,15 +2252,14 @@ class MultiSelect extends React.Component {
2249
2252
  noneSelected
2250
2253
  } = this.state.labels;
2251
2254
  const menuText = this.getMenuText(allChildren);
2252
- const numOptions = allChildren.filter(option => !option.props.disabled).length;
2253
2255
  const dropdownOpener = opener ? React.createElement(DropdownOpener, {
2254
2256
  onClick: this.handleClick,
2255
- disabled: numOptions === 0 || disabled,
2257
+ disabled: isDisabled,
2256
2258
  ref: this.handleOpenerRef,
2257
2259
  text: menuText,
2258
2260
  opened: this.state.open
2259
2261
  }, opener) : React.createElement(SelectOpener, _extends({}, sharedProps, {
2260
- disabled: numOptions === 0 || disabled,
2262
+ disabled: isDisabled,
2261
2263
  id: id,
2262
2264
  isPlaceholder: menuText === noneSelected,
2263
2265
  light: light,
@@ -2278,7 +2280,8 @@ class MultiSelect extends React.Component {
2278
2280
  children,
2279
2281
  isFilterable,
2280
2282
  "aria-invalid": ariaInvalid,
2281
- "aria-required": ariaRequired
2283
+ "aria-required": ariaRequired,
2284
+ disabled
2282
2285
  } = this.props;
2283
2286
  const {
2284
2287
  open,
@@ -2293,7 +2296,8 @@ class MultiSelect extends React.Component {
2293
2296
  const allChildren = React.Children.toArray(children).filter(Boolean);
2294
2297
  const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
2295
2298
  const filteredItems = this.getMenuItems(allChildren);
2296
- const opener = this.renderOpener(allChildren);
2299
+ const isDisabled = numEnabledOptions === 0 || disabled;
2300
+ const opener = this.renderOpener(allChildren, isDisabled);
2297
2301
  return React.createElement(DropdownCore$1, {
2298
2302
  role: "listbox",
2299
2303
  alignment: alignment,
@@ -2317,7 +2321,8 @@ class MultiSelect extends React.Component {
2317
2321
  someResults: someSelected
2318
2322
  },
2319
2323
  "aria-invalid": ariaInvalid,
2320
- "aria-required": ariaRequired
2324
+ "aria-required": ariaRequired,
2325
+ disabled: isDisabled
2321
2326
  });
2322
2327
  }
2323
2328
  }
package/dist/index.js CHANGED
@@ -1304,11 +1304,12 @@ class DropdownCore extends React__namespace.Component {
1304
1304
  open,
1305
1305
  opener,
1306
1306
  style,
1307
- className
1307
+ className,
1308
+ disabled
1308
1309
  } = this.props;
1309
1310
  return React__namespace.createElement(wonderBlocksCore.View, {
1310
- onKeyDown: this.handleKeyDown,
1311
- onKeyUp: this.handleKeyUp,
1311
+ onKeyDown: !disabled ? this.handleKeyDown : undefined,
1312
+ onKeyUp: !disabled ? this.handleKeyUp : undefined,
1312
1313
  style: [styles$4.menuWrapper, style],
1313
1314
  className: className
1314
1315
  }, this.renderLiveRegion(), opener, open && this.renderDropdown());
@@ -1864,7 +1865,7 @@ const _generateStyles = (light, placeholder, error) => {
1864
1865
  return stateStyles[styleKey];
1865
1866
  };
1866
1867
 
1867
- const _excluded$1 = ["children", "disabled", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required"];
1868
+ const _excluded$1 = ["children", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required"];
1868
1869
  class SingleSelect extends React__namespace.Component {
1869
1870
  constructor(props) {
1870
1871
  super(props);
@@ -1943,7 +1944,7 @@ class SingleSelect extends React__namespace.Component {
1943
1944
  }
1944
1945
  static getDerivedStateFromProps(props, state) {
1945
1946
  return {
1946
- open: typeof props.opened === "boolean" ? props.opened : state.open
1947
+ open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
1947
1948
  };
1948
1949
  }
1949
1950
  filterChildren(children) {
@@ -1961,11 +1962,10 @@ class SingleSelect extends React__namespace.Component {
1961
1962
  } = this.props;
1962
1963
  return this.mapOptionItemsToDropdownItems(isFilterable ? this.filterChildren(children) : children);
1963
1964
  }
1964
- renderOpener(numItems) {
1965
+ renderOpener(isDisabled) {
1965
1966
  const _this$props = this.props,
1966
1967
  {
1967
1968
  children,
1968
- disabled,
1969
1969
  error,
1970
1970
  id,
1971
1971
  light,
@@ -1980,12 +1980,12 @@ class SingleSelect extends React__namespace.Component {
1980
1980
  const menuText = selectedItem ? getLabel(selectedItem.props) : placeholder;
1981
1981
  const dropdownOpener = opener ? React__namespace.createElement(DropdownOpener, {
1982
1982
  onClick: this.handleClick,
1983
- disabled: numItems === 0 || disabled,
1983
+ disabled: isDisabled,
1984
1984
  ref: this.handleOpenerRef,
1985
1985
  text: menuText,
1986
1986
  opened: this.state.open
1987
1987
  }, opener) : React__namespace.createElement(SelectOpener, _extends({}, sharedProps, {
1988
- disabled: numItems === 0 || disabled,
1988
+ disabled: isDisabled,
1989
1989
  id: id,
1990
1990
  error: error,
1991
1991
  isPlaceholder: !selectedItem,
@@ -2010,14 +2010,17 @@ class SingleSelect extends React__namespace.Component {
2010
2010
  light,
2011
2011
  style,
2012
2012
  "aria-invalid": ariaInvalid,
2013
- "aria-required": ariaRequired
2013
+ "aria-required": ariaRequired,
2014
+ disabled
2014
2015
  } = this.props;
2015
2016
  const {
2016
2017
  searchText
2017
2018
  } = this.state;
2018
2019
  const allChildren = React__namespace.Children.toArray(children).filter(Boolean);
2020
+ const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
2019
2021
  const items = this.getMenuItems(allChildren);
2020
- const opener = this.renderOpener(allChildren.length);
2022
+ const isDisabled = numEnabledOptions === 0 || disabled;
2023
+ const opener = this.renderOpener(isDisabled);
2021
2024
  return React__namespace.createElement(DropdownCore$1, {
2022
2025
  role: "listbox",
2023
2026
  selectionType: "single",
@@ -2039,7 +2042,8 @@ class SingleSelect extends React__namespace.Component {
2039
2042
  searchText: isFilterable ? searchText : "",
2040
2043
  labels: labels,
2041
2044
  "aria-invalid": ariaInvalid,
2042
- "aria-required": ariaRequired
2045
+ "aria-required": ariaRequired,
2046
+ disabled: isDisabled
2043
2047
  });
2044
2048
  }
2045
2049
  }
@@ -2058,7 +2062,7 @@ SingleSelect.defaultProps = {
2058
2062
  }
2059
2063
  };
2060
2064
 
2061
- const _excluded = ["disabled", "id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required"];
2065
+ const _excluded = ["id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required"];
2062
2066
  class MultiSelect extends React__namespace.Component {
2063
2067
  constructor(props) {
2064
2068
  super(props);
@@ -2143,7 +2147,7 @@ class MultiSelect extends React__namespace.Component {
2143
2147
  }
2144
2148
  static getDerivedStateFromProps(props, state) {
2145
2149
  return {
2146
- open: typeof props.opened === "boolean" ? props.opened : state.open
2150
+ open: props.disabled ? false : typeof props.opened === "boolean" ? props.opened : state.open
2147
2151
  };
2148
2152
  }
2149
2153
  componentDidUpdate(prevProps) {
@@ -2265,10 +2269,9 @@ class MultiSelect extends React__namespace.Component {
2265
2269
  }
2266
2270
  return [...lastSelectedItems, ...restOfTheChildren.map(this.mapOptionItemToDropdownItem)];
2267
2271
  }
2268
- renderOpener(allChildren) {
2272
+ renderOpener(allChildren, isDisabled) {
2269
2273
  const _this$props = this.props,
2270
2274
  {
2271
- disabled,
2272
2275
  id,
2273
2276
  light,
2274
2277
  opener,
@@ -2279,15 +2282,14 @@ class MultiSelect extends React__namespace.Component {
2279
2282
  noneSelected
2280
2283
  } = this.state.labels;
2281
2284
  const menuText = this.getMenuText(allChildren);
2282
- const numOptions = allChildren.filter(option => !option.props.disabled).length;
2283
2285
  const dropdownOpener = opener ? React__namespace.createElement(DropdownOpener, {
2284
2286
  onClick: this.handleClick,
2285
- disabled: numOptions === 0 || disabled,
2287
+ disabled: isDisabled,
2286
2288
  ref: this.handleOpenerRef,
2287
2289
  text: menuText,
2288
2290
  opened: this.state.open
2289
2291
  }, opener) : React__namespace.createElement(SelectOpener, _extends({}, sharedProps, {
2290
- disabled: numOptions === 0 || disabled,
2292
+ disabled: isDisabled,
2291
2293
  id: id,
2292
2294
  isPlaceholder: menuText === noneSelected,
2293
2295
  light: light,
@@ -2308,7 +2310,8 @@ class MultiSelect extends React__namespace.Component {
2308
2310
  children,
2309
2311
  isFilterable,
2310
2312
  "aria-invalid": ariaInvalid,
2311
- "aria-required": ariaRequired
2313
+ "aria-required": ariaRequired,
2314
+ disabled
2312
2315
  } = this.props;
2313
2316
  const {
2314
2317
  open,
@@ -2323,7 +2326,8 @@ class MultiSelect extends React__namespace.Component {
2323
2326
  const allChildren = React__namespace.Children.toArray(children).filter(Boolean);
2324
2327
  const numEnabledOptions = allChildren.filter(option => !option.props.disabled).length;
2325
2328
  const filteredItems = this.getMenuItems(allChildren);
2326
- const opener = this.renderOpener(allChildren);
2329
+ const isDisabled = numEnabledOptions === 0 || disabled;
2330
+ const opener = this.renderOpener(allChildren, isDisabled);
2327
2331
  return React__namespace.createElement(DropdownCore$1, {
2328
2332
  role: "listbox",
2329
2333
  alignment: alignment,
@@ -2347,7 +2351,8 @@ class MultiSelect extends React__namespace.Component {
2347
2351
  someResults: someSelected
2348
2352
  },
2349
2353
  "aria-invalid": ariaInvalid,
2350
- "aria-required": ariaRequired
2354
+ "aria-required": ariaRequired,
2355
+ disabled: isDisabled
2351
2356
  });
2352
2357
  }
2353
2358
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-dropdown",
3
- "version": "5.3.6",
3
+ "version": "5.3.8",
4
4
  "design": "v1",
5
5
  "description": "Dropdown variants for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -21,8 +21,8 @@
21
21
  "@khanacademy/wonder-blocks-core": "^6.4.1",
22
22
  "@khanacademy/wonder-blocks-icon": "^4.1.1",
23
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",
24
+ "@khanacademy/wonder-blocks-modal": "^5.1.5",
25
+ "@khanacademy/wonder-blocks-search-field": "^2.2.16",
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 {render, screen, waitFor} from "@testing-library/react";
4
+ import {fireEvent, render, screen, waitFor} from "@testing-library/react";
5
5
  import {
6
6
  userEvent as ue,
7
7
  PointerEventsCheckLevel,
@@ -1623,6 +1623,73 @@ describe("MultiSelect", () => {
1623
1623
  // Assert
1624
1624
  expect(multiSelect).not.toHaveAttribute("disabled");
1625
1625
  });
1626
+
1627
+ it("should not be opened if it is disabled and `open` prop is set to true", () => {
1628
+ // Arrange
1629
+
1630
+ // Act
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
+ );
1638
+
1639
+ // Assert
1640
+ expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
1641
+ });
1642
+
1643
+ it("should not be able to open the select using the keyboard if there are no items", async () => {
1644
+ // Arrange
1645
+ doRender(<MultiSelect onChange={jest.fn()} />);
1646
+
1647
+ // Act
1648
+ // Press the button
1649
+ const button = await screen.findByRole("button");
1650
+ // NOTE: we need to use fireEvent here because await userEvent doesn't
1651
+ // support keyUp/Down events and we use these handlers to override
1652
+ // the default behavior of the button.
1653
+ // eslint-disable-next-line testing-library/prefer-user-event
1654
+ fireEvent.keyDown(button, {
1655
+ keyCode: 40,
1656
+ });
1657
+ // eslint-disable-next-line testing-library/prefer-user-event
1658
+ fireEvent.keyUp(button, {
1659
+ keyCode: 40,
1660
+ });
1661
+
1662
+ // Assert
1663
+ expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
1664
+ });
1665
+
1666
+ it("should not be able to open the select using the keyboard if all items are disabled", async () => {
1667
+ // Arrange
1668
+ doRender(
1669
+ <MultiSelect onChange={jest.fn()}>
1670
+ <OptionItem label="item 1" value="1" disabled={true} />
1671
+ <OptionItem label="item 2" value="2" disabled={true} />
1672
+ </MultiSelect>,
1673
+ );
1674
+
1675
+ // Act
1676
+ // Press the button
1677
+ const button = await screen.findByRole("button");
1678
+ // NOTE: we need to use fireEvent here because await userEvent doesn't
1679
+ // support keyUp/Down events and we use these handlers to override
1680
+ // the default behavior of the button.
1681
+ // eslint-disable-next-line testing-library/prefer-user-event
1682
+ fireEvent.keyDown(button, {
1683
+ keyCode: 40,
1684
+ });
1685
+ // eslint-disable-next-line testing-library/prefer-user-event
1686
+ fireEvent.keyUp(button, {
1687
+ keyCode: 40,
1688
+ });
1689
+
1690
+ // Assert
1691
+ expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
1692
+ });
1626
1693
  });
1627
1694
 
1628
1695
  describe("a11y > Focusable", () => {