@carbon/react 1.63.0 → 1.63.2

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.
Files changed (30) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +930 -971
  2. package/es/components/ComboBox/ComboBox.d.ts +21 -3
  3. package/es/components/ComboBox/ComboBox.js +65 -9
  4. package/es/components/DataTable/TableSelectRow.js +2 -1
  5. package/es/components/DataTable/TableToolbarSearch.js +2 -2
  6. package/es/components/DatePicker/DatePicker.js +65 -14
  7. package/es/components/DatePicker/plugins/fixEventsPlugin.js +11 -0
  8. package/es/components/Dropdown/Dropdown.d.ts +6 -1
  9. package/es/components/Dropdown/Dropdown.js +25 -22
  10. package/es/components/FluidMultiSelect/FluidMultiSelect.js +6 -1
  11. package/es/components/MultiSelect/FilterableMultiSelect.d.ts +6 -1
  12. package/es/components/MultiSelect/FilterableMultiSelect.js +8 -3
  13. package/es/components/MultiSelect/MultiSelect.d.ts +6 -1
  14. package/es/components/MultiSelect/MultiSelect.js +8 -3
  15. package/es/internal/useId.js +39 -18
  16. package/lib/components/ComboBox/ComboBox.d.ts +21 -3
  17. package/lib/components/ComboBox/ComboBox.js +65 -9
  18. package/lib/components/DataTable/TableSelectRow.js +2 -1
  19. package/lib/components/DataTable/TableToolbarSearch.js +2 -2
  20. package/lib/components/DatePicker/DatePicker.js +65 -14
  21. package/lib/components/DatePicker/plugins/fixEventsPlugin.js +11 -0
  22. package/lib/components/Dropdown/Dropdown.d.ts +6 -1
  23. package/lib/components/Dropdown/Dropdown.js +25 -22
  24. package/lib/components/FluidMultiSelect/FluidMultiSelect.js +6 -1
  25. package/lib/components/MultiSelect/FilterableMultiSelect.d.ts +6 -1
  26. package/lib/components/MultiSelect/FilterableMultiSelect.js +8 -3
  27. package/lib/components/MultiSelect/MultiSelect.d.ts +6 -1
  28. package/lib/components/MultiSelect/MultiSelect.js +8 -3
  29. package/lib/internal/useId.js +39 -17
  30. package/package.json +2 -2
@@ -4,8 +4,8 @@
4
4
  * This source code is licensed under the Apache-2.0 license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import { UseComboboxProps } from 'downshift';
8
- import { type ReactNode, type ComponentType, type ReactElement, type RefAttributes, type PropsWithChildren, type PropsWithRef, type InputHTMLAttributes, type MouseEvent } from 'react';
7
+ import { UseComboboxProps, UseComboboxActions } from 'downshift';
8
+ import React, { type ReactNode, type ComponentType, type ReactElement, type RefAttributes, type PropsWithChildren, type PropsWithRef, type InputHTMLAttributes, type MouseEvent } from 'react';
9
9
  import { ListBoxSize } from '../ListBox';
10
10
  import { TranslateWithId } from '../../types/common';
11
11
  type ExcludedAttributes = 'id' | 'onChange' | 'onClick' | 'type' | 'size';
@@ -57,9 +57,27 @@ export interface ComboBoxProps<ItemType> extends Omit<InputHTMLAttributes<HTMLIn
57
57
  */
58
58
  disabled?: boolean;
59
59
  /**
60
- * Additional props passed to Downshift
60
+ * Additional props passed to Downshift.
61
+ *
62
+ * **Use with caution:** anything you define here overrides the components'
63
+ * internal handling of that prop. Downshift APIs and internals are subject to
64
+ * change, and in some cases they can not be shimmed by Carbon to shield you
65
+ * from potentially breaking changes.
66
+ *
61
67
  */
62
68
  downshiftProps?: Partial<UseComboboxProps<ItemType>>;
69
+ /**
70
+ * Provide a ref that will be mutated to contain an object of downshift
71
+ * action functions. These can be called to change the internal state of the
72
+ * downshift useCombobox hook.
73
+ *
74
+ * **Use with caution:** calling these actions modifies the internal state of
75
+ * downshift. It may conflict with or override the state management used within
76
+ * Combobox. Downshift APIs and internals are subject to change, and in some
77
+ * cases they can not be shimmed by Carbon to shield you from potentially breaking
78
+ * changes.
79
+ */
80
+ downshiftActions?: React.MutableRefObject<UseComboboxActions<ItemType> | undefined>;
63
81
  /**
64
82
  * Provide helper text that is used alongside the control label for
65
83
  * additional help
@@ -99,6 +99,7 @@ const ComboBox = /*#__PURE__*/forwardRef((props, ref) => {
99
99
  className: containerClassName,
100
100
  direction = 'bottom',
101
101
  disabled = false,
102
+ downshiftActions,
102
103
  downshiftProps,
103
104
  helperText,
104
105
  id,
@@ -317,19 +318,25 @@ const ComboBox = /*#__PURE__*/forwardRef((props, ref) => {
317
318
  });
318
319
  }
319
320
  const {
321
+ // Prop getters
320
322
  getInputProps,
321
323
  getItemProps,
322
324
  getLabelProps,
323
325
  getMenuProps,
324
326
  getToggleButtonProps,
327
+ // State
325
328
  isOpen,
326
329
  highlightedIndex,
327
- selectItem,
328
330
  selectedItem,
329
- toggleMenu,
330
- setHighlightedIndex
331
+ // Actions
332
+ closeMenu,
333
+ openMenu,
334
+ reset,
335
+ selectItem,
336
+ setHighlightedIndex,
337
+ setInputValue: downshiftSetInputValue,
338
+ toggleMenu
331
339
  } = useCombobox({
332
- ...downshiftProps,
333
340
  items: filterItems(items, itemToString, inputValue),
334
341
  inputValue: inputValue,
335
342
  itemToString: item => {
@@ -350,13 +357,45 @@ const ComboBox = /*#__PURE__*/forwardRef((props, ref) => {
350
357
  selectedItem
351
358
  });
352
359
  },
360
+ onHighlightedIndexChange: _ref5 => {
361
+ let {
362
+ highlightedIndex
363
+ } = _ref5;
364
+ if (highlightedIndex > -1 && typeof window !== undefined) {
365
+ const itemArray = document.querySelectorAll(`li.${prefix}--list-box__menu-item[role="option"]`);
366
+ const highlightedItem = itemArray[highlightedIndex];
367
+ if (highlightedItem) {
368
+ highlightedItem.scrollIntoView({
369
+ behavior: 'smooth',
370
+ block: 'nearest'
371
+ });
372
+ }
373
+ }
374
+ },
353
375
  initialSelectedItem: initialSelectedItem,
354
376
  inputId: id,
355
377
  stateReducer,
356
378
  isItemDisabled(item, _index) {
357
379
  return item.disabled;
358
- }
380
+ },
381
+ ...downshiftProps
359
382
  });
383
+ useEffect(() => {
384
+ // Used to expose the downshift actions to consumers for use with downshiftProps
385
+ // An odd pattern, here we mutate the value stored in the ref provided from the consumer.
386
+ // A riff of https://gist.github.com/gaearon/1a018a023347fe1c2476073330cc5509
387
+ if (downshiftActions) {
388
+ downshiftActions.current = {
389
+ closeMenu,
390
+ openMenu,
391
+ reset,
392
+ selectItem,
393
+ setHighlightedIndex,
394
+ setInputValue: downshiftSetInputValue,
395
+ toggleMenu
396
+ };
397
+ }
398
+ }, [closeMenu, openMenu, reset, selectItem, setHighlightedIndex, downshiftSetInputValue, toggleMenu]);
360
399
  const buttonProps = getToggleButtonProps({
361
400
  disabled: disabled || readOnly,
362
401
  onClick: handleToggleClick(isOpen),
@@ -429,9 +468,7 @@ const ComboBox = /*#__PURE__*/forwardRef((props, ref) => {
429
468
  }, getInputProps({
430
469
  'aria-controls': isOpen ? undefined : menuProps.id,
431
470
  placeholder,
432
- ref: {
433
- ...mergeRefs(textInput, ref)
434
- },
471
+ ref: mergeRefs(textInput, ref),
435
472
  onKeyDown: event => {
436
473
  if (match(event, Space)) {
437
474
  event.stopPropagation();
@@ -571,9 +608,28 @@ ComboBox.propTypes = {
571
608
  */
572
609
  disabled: PropTypes.bool,
573
610
  /**
574
- * Additional props passed to Downshift
611
+ * Additional props passed to Downshift.
612
+ *
613
+ * **Use with caution:** anything you define here overrides the components'
614
+ * internal handling of that prop. Downshift APIs and internals are subject to
615
+ * change, and in some cases they can not be shimmed by Carbon to shield you
616
+ * from potentially breaking changes.
575
617
  */
576
618
  downshiftProps: PropTypes.object,
619
+ /**
620
+ * Provide a ref that will be mutated to contain an object of downshift
621
+ * action functions. These can be called to change the internal state of the
622
+ * downshift useCombobox hook.
623
+ *
624
+ * **Use with caution:** calling these actions modifies the internal state of
625
+ * downshift. It may conflict with or override the state management used within
626
+ * Combobox. Downshift APIs and internals are subject to change, and in some
627
+ * cases they can not be shimmed by Carbon to shield you from potentially breaking
628
+ * changes.
629
+ */
630
+ downshiftActions: PropTypes.exact({
631
+ current: PropTypes.any
632
+ }),
577
633
  /**
578
634
  * Provide helper text that is used alongside the control label for
579
635
  * additional help
@@ -7,10 +7,11 @@
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
9
  import PropTypes from 'prop-types';
10
- import React__default, { useId } from 'react';
10
+ import React__default from 'react';
11
11
  import cx from 'classnames';
12
12
  import InlineCheckbox from '../InlineCheckbox/InlineCheckbox.js';
13
13
  import RadioButton from '../RadioButton/RadioButton.js';
14
+ import { useId } from '../../internal/useId.js';
14
15
  import { usePrefix } from '../../internal/usePrefix.js';
15
16
  import deprecate from '../../prop-types/deprecate.js';
16
17
 
@@ -54,7 +54,7 @@ const TableToolbarSearch = _ref => {
54
54
  const [expandedState, setExpandedState] = useState(Boolean(defaultExpanded || defaultValue));
55
55
  const expanded = controlled ? expandedProp : expandedState;
56
56
  const [value, setValue] = useState(defaultValue || '');
57
- const uniqueId = useId();
57
+ const uniqueId = useId('table-toolbar-search');
58
58
  const [focusTarget, setFocusTarget] = useState(null);
59
59
  const prefix = usePrefix();
60
60
  useEffect(() => {
@@ -101,7 +101,7 @@ const TableToolbarSearch = _ref => {
101
101
  disabled: disabled,
102
102
  className: searchClasses,
103
103
  value: value,
104
- id: typeof id !== 'undefined' ? id : `table-toolbar-search-${uniqueId.toString()}`,
104
+ id: typeof id !== 'undefined' ? id : uniqueId,
105
105
  labelText: labelText || t('carbon.table.toolbar.search.label'),
106
106
  placeholder: placeholder || t('carbon.table.toolbar.search.placeholder'),
107
107
  onChange: onChange,
@@ -173,25 +173,42 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
173
173
  }
174
174
  }, []);
175
175
  const lastStartValue = useRef('');
176
+ const [calendarCloseEvent, setCalendarCloseEvent] = useState(null);
176
177
 
177
178
  // fix datepicker deleting the selectedDate when the calendar closes
178
- const onCalendarClose = (selectedDates, dateStr) => {
179
- endInputField?.current?.focus();
180
- calendarRef?.current?.calendarContainer?.classList.remove('open');
181
- setTimeout(() => {
182
- if (lastStartValue.current && selectedDates[0] && !startInputField.current.value) {
183
- startInputField.current.value = lastStartValue.current;
184
- calendarRef.current.setDate([startInputField.current.value, endInputField?.current?.value], true, calendarRef.current.config.dateFormat);
185
- }
186
- if (onClose) {
187
- onClose(calendarRef.current.selectedDates, dateStr, calendarRef.current);
188
- }
179
+ const handleCalendarClose = useCallback((selectedDates, dateStr, instance) => {
180
+ if (lastStartValue.current && selectedDates[0] && !startInputField.current.value) {
181
+ startInputField.current.value = lastStartValue.current;
182
+ calendarRef.current.setDate([startInputField.current.value, endInputField?.current?.value], true, calendarRef.current.config.dateFormat);
183
+ }
184
+ if (onClose) {
185
+ onClose(selectedDates, dateStr, instance);
186
+ }
187
+ }, [onClose]);
188
+ const onCalendarClose = (selectedDates, dateStr, instance, e) => {
189
+ if (e && e.type === 'clickOutside') {
190
+ return;
191
+ }
192
+ setCalendarCloseEvent({
193
+ selectedDates,
194
+ dateStr,
195
+ instance
189
196
  });
190
197
  };
198
+ useEffect(() => {
199
+ if (calendarCloseEvent) {
200
+ const {
201
+ selectedDates,
202
+ dateStr,
203
+ instance
204
+ } = calendarCloseEvent;
205
+ handleCalendarClose(selectedDates, dateStr, instance);
206
+ setCalendarCloseEvent(null);
207
+ }
208
+ }, [calendarCloseEvent, handleCalendarClose]);
191
209
  const endInputField = useRef(null);
192
210
  const calendarRef = useRef(null);
193
211
  const savedOnChange = useSavedCallback(onChange);
194
- const savedOnClose = useSavedCallback(datePickerType === 'range' ? onCalendarClose : onClose);
195
212
  const savedOnOpen = useSavedCallback(onOpen);
196
213
  const datePickerClasses = cx(`${prefix}--date-picker`, {
197
214
  [`${prefix}--date-picker--short`]: short,
@@ -309,6 +326,7 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
309
326
  } = endInputField;
310
327
  const flatpickerconfig = {
311
328
  inline: inline ?? false,
329
+ onClose: onCalendarClose,
312
330
  disableMobile: true,
313
331
  defaultDate: value,
314
332
  closeOnSelect: closeOnSelect,
@@ -343,7 +361,6 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
343
361
  savedOnChange(...arguments);
344
362
  }
345
363
  },
346
- onClose: savedOnClose,
347
364
  onReady: onHook,
348
365
  onMonthChange: onHook,
349
366
  onYearChange: onHook,
@@ -440,7 +457,7 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
440
457
  }
441
458
  };
442
459
  // eslint-disable-next-line react-hooks/exhaustive-deps
443
- }, [savedOnChange, savedOnClose, savedOnOpen, readOnly, closeOnSelect, hasInput]);
460
+ }, [savedOnChange, savedOnOpen, readOnly, closeOnSelect, hasInput]);
444
461
 
445
462
  // this hook allows consumers to access the flatpickr calendar
446
463
  // instance for cases where functions like open() or close()
@@ -487,6 +504,40 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
487
504
  calendarRef.current.set('inline', inline);
488
505
  }
489
506
  }, [inline]);
507
+ useEffect(() => {
508
+ //when value prop is set to empty, this clears the faltpicker's calendar instance and text input
509
+ if (value === '') {
510
+ calendarRef.current?.clear();
511
+ if (startInputField.current) {
512
+ startInputField.current.value = '';
513
+ }
514
+ if (endInputField.current) {
515
+ endInputField.current.value = '';
516
+ }
517
+ }
518
+ }, [value]);
519
+ useEffect(() => {
520
+ const handleMouseDown = event => {
521
+ if (calendarRef.current && calendarRef.current.isOpen && !calendarRef.current.calendarContainer.contains(event.target) && !startInputField.current.contains(event.target) && !endInputField.current?.contains(event.target)) {
522
+ // Close the calendar immediately on mousedown
523
+ closeCalendar();
524
+ }
525
+ };
526
+ const closeCalendar = event => {
527
+ calendarRef.current.close();
528
+ // Remove focus from endDate calendar input
529
+ if (document.activeElement instanceof HTMLElement) {
530
+ document.activeElement.blur();
531
+ }
532
+ onCalendarClose(calendarRef.current.selectedDates, '', calendarRef.current, {
533
+ type: 'clickOutside'
534
+ });
535
+ };
536
+ document.addEventListener('mousedown', handleMouseDown, true);
537
+ return () => {
538
+ document.removeEventListener('mousedown', handleMouseDown, true);
539
+ };
540
+ }, [calendarRef, startInputField, endInputField, onCalendarClose]);
490
541
  useEffect(() => {
491
542
  if (calendarRef?.current?.set) {
492
543
  if (value !== undefined) {
@@ -18,6 +18,15 @@ var carbonFlatpickrFixEventsPlugin = (config => fp => {
18
18
  inputTo,
19
19
  lastStartValue
20
20
  } = config;
21
+ /**
22
+ * Handles `click` outside to close calendar
23
+ */
24
+ const handleClickOutside = event => {
25
+ if (!fp.isOpen || fp.calendarContainer.contains(event.target) || event.target === inputFrom || event.target === inputTo) {
26
+ return;
27
+ }
28
+ fp.close();
29
+ };
21
30
  /**
22
31
  * Handles `keydown` event.
23
32
  */
@@ -111,6 +120,7 @@ var carbonFlatpickrFixEventsPlugin = (config => fp => {
111
120
  inputTo.removeEventListener('blur', handleBlur, true);
112
121
  }
113
122
  inputFrom.removeEventListener('keydown', handleKeydown, true);
123
+ document.removeEventListener('click', handleClickOutside, true);
114
124
  };
115
125
 
116
126
  /**
@@ -127,6 +137,7 @@ var carbonFlatpickrFixEventsPlugin = (config => fp => {
127
137
  inputTo.addEventListener('keydown', handleKeydown, true);
128
138
  inputTo.addEventListener('blur', handleBlur, true);
129
139
  }
140
+ document.addEventListener('click', handleClickOutside, true);
130
141
  };
131
142
 
132
143
  /**
@@ -36,7 +36,12 @@ export interface DropdownProps<ItemType> extends Omit<ReactAttr<HTMLDivElement>,
36
36
  */
37
37
  disabled?: boolean;
38
38
  /**
39
- * Additional props passed to Downshift
39
+ * Additional props passed to Downshift.
40
+ *
41
+ * **Use with caution:** anything you define here overrides the components'
42
+ * internal handling of that prop. Downshift APIs and internals are subject to
43
+ * change, and in some cases they can not be shimmed by Carbon to shield you
44
+ * from potentially breaking changes.
40
45
  */
41
46
  downshiftProps?: Partial<UseSelectProps<ItemType>>;
42
47
  /**
@@ -22,10 +22,6 @@ import { useFloating, size, flip, autoUpdate } from '@floating-ui/react';
22
22
  import { ListBoxSize, ListBoxType } from '../ListBox/ListBoxPropTypes.js';
23
23
 
24
24
  const {
25
- ToggleButtonKeyDownArrowDown,
26
- ToggleButtonKeyDownArrowUp,
27
- ToggleButtonKeyDownHome,
28
- ToggleButtonKeyDownEnd,
29
25
  ItemMouseMove,
30
26
  MenuMouseLeave
31
27
  } = useSelect.stateChangeTypes;
@@ -113,7 +109,6 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
113
109
  isFluid
114
110
  } = useContext(FormContext);
115
111
  const selectProps = {
116
- ...downshiftProps,
117
112
  items,
118
113
  itemToString,
119
114
  initialSelectedItem,
@@ -122,28 +117,31 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
122
117
  isItemDisabled(item, _index) {
123
118
  const isObject = item !== null && typeof item === 'object';
124
119
  return isObject && 'disabled' in item && item.disabled === true;
125
- }
120
+ },
121
+ onHighlightedIndexChange: _ref3 => {
122
+ let {
123
+ highlightedIndex
124
+ } = _ref3;
125
+ if (highlightedIndex > -1 && typeof window !== undefined) {
126
+ const itemArray = document.querySelectorAll(`li.${prefix}--list-box__menu-item[role="option"]`);
127
+ const highlightedItem = itemArray[highlightedIndex];
128
+ if (highlightedItem) {
129
+ highlightedItem.scrollIntoView({
130
+ behavior: 'smooth',
131
+ block: 'nearest'
132
+ });
133
+ }
134
+ }
135
+ },
136
+ ...downshiftProps
126
137
  };
127
138
  const dropdownInstanceId = useId();
128
139
  function stateReducer(state, actionAndChanges) {
129
140
  const {
130
141
  changes,
131
- props,
132
142
  type
133
143
  } = actionAndChanges;
134
- const {
135
- highlightedIndex
136
- } = changes;
137
144
  switch (type) {
138
- case ToggleButtonKeyDownArrowDown:
139
- case ToggleButtonKeyDownArrowUp:
140
- case ToggleButtonKeyDownHome:
141
- case ToggleButtonKeyDownEnd:
142
- if (highlightedIndex > -1) {
143
- const itemArray = document.querySelectorAll(`li.${prefix}--list-box__menu-item[role="option"]`);
144
- props.scrollIntoView(itemArray[highlightedIndex]);
145
- }
146
- return changes;
147
145
  case ItemMouseMove:
148
146
  case MenuMouseLeave:
149
147
  return {
@@ -208,10 +206,10 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
208
206
  id: helperId,
209
207
  className: helperClasses
210
208
  }, helperText) : null;
211
- function onSelectedItemChange(_ref3) {
209
+ function onSelectedItemChange(_ref4) {
212
210
  let {
213
211
  selectedItem
214
- } = _ref3;
212
+ } = _ref4;
215
213
  if (onChange) {
216
214
  onChange({
217
215
  selectedItem: selectedItem ?? null
@@ -362,7 +360,12 @@ Dropdown.propTypes = {
362
360
  */
363
361
  disabled: PropTypes.bool,
364
362
  /**
365
- * Additional props passed to Downshift
363
+ * Additional props passed to Downshift.
364
+ *
365
+ * **Use with caution:** anything you define here overrides the components'
366
+ * internal handling of that prop. Downshift APIs and internals are subject to
367
+ * change, and in some cases they can not be shimmed by Carbon to shield you
368
+ * from potentially breaking changes.
366
369
  */
367
370
  downshiftProps: PropTypes.object,
368
371
  /**
@@ -65,7 +65,12 @@ FluidMultiSelect.propTypes = {
65
65
  */
66
66
  disabled: PropTypes.bool,
67
67
  /**
68
- * Additional props passed to Downshift
68
+ * Additional props passed to Downshift.
69
+ *
70
+ * **Use with caution:** anything you define here overrides the components'
71
+ * internal handling of that prop. Downshift APIs and internals are subject to
72
+ * change, and in some cases they can not be shimmed by Carbon to shield you
73
+ * from potentially breaking changes.
69
74
  */
70
75
  downshiftProps: PropTypes.object,
71
76
  /**
@@ -48,7 +48,12 @@ export interface FilterableMultiSelectProps<ItemType> extends MultiSelectSorting
48
48
  */
49
49
  disabled?: boolean;
50
50
  /**
51
- * Additional props passed to Downshift
51
+ * Additional props passed to Downshift.
52
+ *
53
+ * **Use with caution:** anything you define here overrides the components'
54
+ * internal handling of that prop. Downshift APIs and internals are subject to
55
+ * change, and in some cases they can not be shimmed by Carbon to shield you
56
+ * from potentially breaking changes.
52
57
  */
53
58
  downshiftProps?: UseMultipleSelectionProps<ItemType>;
54
59
  /**
@@ -348,7 +348,6 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
348
348
  const {
349
349
  getDropdownProps
350
350
  } = useMultipleSelection({
351
- ...downshiftProps,
352
351
  activeIndex: highlightedIndex,
353
352
  initialSelectedItems,
354
353
  selectedItems: controlledSelectedItems,
@@ -364,7 +363,8 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
364
363
  break;
365
364
  }
366
365
  }
367
- }
366
+ },
367
+ ...downshiftProps
368
368
  });
369
369
  useEffect(() => {
370
370
  if (isOpen && !isMenuOpen) {
@@ -622,7 +622,12 @@ FilterableMultiSelect.propTypes = {
622
622
  */
623
623
  disabled: PropTypes.bool,
624
624
  /**
625
- * Additional props passed to Downshift
625
+ * Additional props passed to Downshift.
626
+ *
627
+ * **Use with caution:** anything you define here overrides the components'
628
+ * internal handling of that prop. Downshift APIs and internals are subject to
629
+ * change, and in some cases they can not be shimmed by Carbon to shield you
630
+ * from potentially breaking changes.
626
631
  */
627
632
  // @ts-ignore
628
633
  downshiftProps: PropTypes.shape(Downshift.propTypes),
@@ -42,7 +42,12 @@ export interface MultiSelectProps<ItemType> extends MultiSelectSortingProps<Item
42
42
  */
43
43
  disabled?: ListBoxProps['disabled'];
44
44
  /**
45
- * Additional props passed to Downshift
45
+ * Additional props passed to Downshift.
46
+ *
47
+ * **Use with caution:** anything you define here overrides the components'
48
+ * internal handling of that prop. Downshift APIs and internals are subject to
49
+ * change, and in some cases they can not be shimmed by Carbon to shield you
50
+ * from potentially breaking changes.
46
51
  */
47
52
  downshiftProps?: Partial<UseSelectProps<ItemType>>;
48
53
  /**
@@ -166,7 +166,6 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
166
166
  });
167
167
  }, [items]);
168
168
  const selectProps = {
169
- ...downshiftProps,
170
169
  stateReducer,
171
170
  isOpen,
172
171
  itemToString: filteredItems => {
@@ -178,7 +177,8 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
178
177
  items: filteredItems,
179
178
  isItemDisabled(item, _index) {
180
179
  return item.disabled;
181
- }
180
+ },
181
+ ...downshiftProps
182
182
  };
183
183
  const {
184
184
  getToggleButtonProps,
@@ -518,7 +518,12 @@ MultiSelect.propTypes = {
518
518
  */
519
519
  disabled: PropTypes.bool,
520
520
  /**
521
- * Additional props passed to Downshift
521
+ * Additional props passed to Downshift.
522
+ *
523
+ * **Use with caution:** anything you define here overrides the components'
524
+ * internal handling of that prop. Downshift APIs and internals are subject to
525
+ * change, and in some cases they can not be shimmed by Carbon to shield you
526
+ * from potentially breaking changes.
522
527
  */
523
528
  downshiftProps: PropTypes.object,
524
529
  /**
@@ -10,49 +10,70 @@ import setupGetInstanceId from '../tools/setupGetInstanceId.js';
10
10
  import { canUseDOM } from './environment.js';
11
11
  import { useIdPrefix } from './useIdPrefix.js';
12
12
 
13
- // This file was heavily inspired by Reach UI and their work on their auto-id
14
- const getId = setupGetInstanceId();
13
+ // This file was heavily inspired by:
14
+
15
+ // This tricks bundlers so they can't statically analyze this and produce
16
+ // compilation warnings/errors.
17
+ // https://github.com/webpack/webpack/issues/14814
18
+ // https://github.com/mui/material-ui/issues/41190
19
+ const _React = {
20
+ ...React__default
21
+ };
22
+ const instanceId = setupGetInstanceId();
15
23
  const useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;
16
24
  let serverHandoffCompleted = false;
25
+ const defaultId = 'id';
17
26
 
18
27
  /**
19
- * Generate a unique ID with an optional prefix prepended to it
28
+ * Generate a unique ID for React <=17 with an optional prefix prepended to it.
29
+ * This is an internal utility, not intended for public usage.
20
30
  * @param {string} [prefix]
21
31
  * @returns {string}
22
32
  */
23
- function useId() {
24
- let prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'id';
25
- const _prefix = useIdPrefix();
33
+ function useCompatibleId() {
34
+ let prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultId;
35
+ const contextPrefix = useIdPrefix();
26
36
  const [id, setId] = useState(() => {
27
37
  if (serverHandoffCompleted) {
28
- return `${_prefix ? `${_prefix}-` : ``}${prefix}-${getId()}`;
38
+ return `${contextPrefix ? `${contextPrefix}-` : ``}${prefix}-${instanceId()}`;
29
39
  }
30
40
  return null;
31
41
  });
32
42
  useIsomorphicLayoutEffect(() => {
33
43
  if (id === null) {
34
- setId(`${_prefix ? `${_prefix}-` : ``}${prefix}-${getId()}`);
44
+ setId(`${contextPrefix ? `${contextPrefix}-` : ``}${prefix}-${instanceId()}`);
35
45
  }
36
- }, [getId]);
46
+ }, [instanceId]);
37
47
  useEffect(() => {
38
48
  if (serverHandoffCompleted === false) {
39
49
  serverHandoffCompleted = true;
40
50
  }
41
51
  }, []);
42
- if (typeof React__default['useId'] === 'function') {
43
- const id = nativeReactUseId(_prefix, prefix);
44
- return id;
45
- }
46
52
  return id;
47
53
  }
48
- function nativeReactUseId(_prefix, prefix) {
49
- const getId = React__default['useId']();
50
- const id = `${_prefix ? `${_prefix}-` : ``}${prefix}-${getId}`;
51
- return id;
54
+
55
+ /**
56
+ * Generate a unique ID for React >=18 with an optional prefix prepended to it.
57
+ * This is an internal utility, not intended for public usage.
58
+ * @param {string} [prefix]
59
+ * @returns {string}
60
+ */
61
+ function useReactId() {
62
+ let prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultId;
63
+ const contextPrefix = useIdPrefix();
64
+ return `${contextPrefix ? `${contextPrefix}-` : ``}${prefix}-${_React.useId()}`;
52
65
  }
53
66
 
67
+ /**
68
+ * Uses React 18's built-in `useId()` when available, or falls back to a
69
+ * slightly less performant (requiring a double render) implementation for
70
+ * earlier React versions.
71
+ */
72
+ const useId = _React.useId ? useReactId : useCompatibleId;
73
+
54
74
  /**
55
75
  * Generate a unique id if a given `id` is not provided
76
+ * This is an internal utility, not intended for public usage.
56
77
  * @param {string|undefined} id
57
78
  * @returns {string}
58
79
  */
@@ -61,4 +82,4 @@ function useFallbackId(id) {
61
82
  return id ?? fallback;
62
83
  }
63
84
 
64
- export { useFallbackId, useId };
85
+ export { useCompatibleId, useFallbackId, useId };