@carbon/react 1.100.0-rc.0 → 1.100.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.
Files changed (40) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +973 -973
  2. package/es/components/ComboBox/ComboBox.d.ts +3 -4
  3. package/es/components/ComboBox/ComboBox.js +2 -8
  4. package/es/components/Dropdown/Dropdown.d.ts +3 -4
  5. package/es/components/Dropdown/Dropdown.js +16 -13
  6. package/es/components/FluidComboBox/FluidComboBox.d.ts +2 -7
  7. package/es/components/FluidComboBox/FluidComboBox.js +1 -2
  8. package/es/components/FluidDropdown/FluidDropdown.d.ts +1 -6
  9. package/es/components/FluidDropdown/FluidDropdown.js +1 -2
  10. package/es/components/FluidMultiSelect/FluidMultiSelect.d.ts +1 -6
  11. package/es/components/FluidMultiSelect/FluidMultiSelect.js +1 -2
  12. package/es/components/InlineLoading/InlineLoading.js +5 -11
  13. package/es/components/MultiSelect/MultiSelect.d.ts +3 -4
  14. package/es/components/MultiSelect/MultiSelect.js +2 -9
  15. package/es/components/Notification/Notification.js +4 -3
  16. package/es/components/TextArea/TextArea.js +1 -3
  17. package/es/components/Tile/Tile.d.ts +1 -1
  18. package/es/components/Tile/Tile.js +37 -69
  19. package/es/components/UIShell/SideNav.d.ts +0 -1
  20. package/es/components/UIShell/SideNav.js +12 -3
  21. package/lib/components/ComboBox/ComboBox.d.ts +3 -4
  22. package/lib/components/ComboBox/ComboBox.js +2 -8
  23. package/lib/components/Dropdown/Dropdown.d.ts +3 -4
  24. package/lib/components/Dropdown/Dropdown.js +16 -13
  25. package/lib/components/FluidComboBox/FluidComboBox.d.ts +2 -7
  26. package/lib/components/FluidComboBox/FluidComboBox.js +1 -2
  27. package/lib/components/FluidDropdown/FluidDropdown.d.ts +1 -6
  28. package/lib/components/FluidDropdown/FluidDropdown.js +1 -2
  29. package/lib/components/FluidMultiSelect/FluidMultiSelect.d.ts +1 -6
  30. package/lib/components/FluidMultiSelect/FluidMultiSelect.js +1 -2
  31. package/lib/components/InlineLoading/InlineLoading.js +5 -11
  32. package/lib/components/MultiSelect/MultiSelect.d.ts +3 -4
  33. package/lib/components/MultiSelect/MultiSelect.js +2 -9
  34. package/lib/components/Notification/Notification.js +4 -3
  35. package/lib/components/TextArea/TextArea.js +1 -3
  36. package/lib/components/Tile/Tile.d.ts +1 -1
  37. package/lib/components/Tile/Tile.js +35 -67
  38. package/lib/components/UIShell/SideNav.d.ts +0 -1
  39. package/lib/components/UIShell/SideNav.js +11 -2
  40. package/package.json +7 -7
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import { UseComboboxProps, UseComboboxActions } from 'downshift';
8
- import { type ComponentType, type InputHTMLAttributes, type MouseEvent, type PropsWithChildren, type ReactElement, type ReactNode, type RefAttributes, type RefObject } from 'react';
8
+ import { type InputHTMLAttributes, type MouseEvent, type PropsWithChildren, type ReactElement, type ReactNode, type RefAttributes, type RefObject } from 'react';
9
9
  import { type ListBoxMenuIconTranslationKey, type ListBoxSelectionTranslationKey, type ListBoxSize } from '../ListBox';
10
10
  import type { TranslateWithId } from '../../types/common';
11
11
  type ExcludedAttributes = 'id' | 'onChange' | 'onClick' | 'type' | 'size';
@@ -98,10 +98,9 @@ export interface ComboBoxProps<ItemType> extends Omit<InputHTMLAttributes<HTMLIn
98
98
  */
99
99
  invalidText?: ReactNode;
100
100
  /**
101
- * Optional function to render items as custom components instead of strings.
102
- * Defaults to null and is overridden by a getter
101
+ * Renders an item as a custom React node instead of a string.
103
102
  */
104
- itemToElement?: ComponentType<ItemType> | null;
103
+ itemToElement?: ((item: ItemType) => ReactNode) | null;
105
104
  /**
106
105
  * Helper function passed to downshift that allows the library to render a
107
106
  * given item to a string label. By default, it extracts the `label` field
@@ -450,9 +450,6 @@ const ComboBox = /*#__PURE__*/forwardRef((props, ref) => {
450
450
  [`${prefix}--combo-box--input--focus`]: isFocused
451
451
  });
452
452
 
453
- // needs to be Capitalized for react to render it correctly
454
- const ItemToElement = itemToElement;
455
-
456
453
  // AILabel always size `mini`
457
454
  const candidate = slug ?? decorator;
458
455
  const candidateIsAILabel = isComponentElement(candidate, AILabel);
@@ -784,9 +781,7 @@ const ComboBox = /*#__PURE__*/forwardRef((props, ref) => {
784
781
  isHighlighted: highlightedIndex === index,
785
782
  title: title,
786
783
  disabled: disabled
787
- }, modifiedItemProps), ItemToElement ? /*#__PURE__*/React.createElement(ItemToElement, _extends({
788
- key: itemProps.id
789
- }, item)) : itemToString(item), isEqual(currentSelectedItem, item) && /*#__PURE__*/React.createElement(Checkmark, {
784
+ }, modifiedItemProps), itemToElement ? itemToElement(item) : itemToString(item), isEqual(currentSelectedItem, item) && /*#__PURE__*/React.createElement(Checkmark, {
790
785
  className: `${prefix}--list-box__menu-item__selected-icon`
791
786
  }));
792
787
  }) : null)), helperText && !invalid && !warn && !isFluid && /*#__PURE__*/React.createElement(Text, {
@@ -882,8 +877,7 @@ ComboBox.propTypes = {
882
877
  */
883
878
  invalidText: PropTypes.node,
884
879
  /**
885
- * Optional function to render items as custom components instead of strings.
886
- * Defaults to null and is overridden by a getter
880
+ * Renders an item as a custom React node instead of a string.
887
881
  */
888
882
  itemToElement: PropTypes.func,
889
883
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2022, 2025
2
+ * Copyright IBM Corp. 2022, 2026
3
3
  *
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.
@@ -78,10 +78,9 @@ export interface DropdownProps<ItemType> extends Omit<HTMLAttributes<HTMLDivElem
78
78
  */
79
79
  invalidText?: ReactNode;
80
80
  /**
81
- * Function to render items as custom components instead of strings.
82
- * Defaults to null and is overridden by a getter
81
+ * Renders an item as a custom React node instead of a string.
83
82
  */
84
- itemToElement?: React.JSXElementConstructor<ItemType> | null;
83
+ itemToElement?: ((item: ItemType) => NonNullable<ReactNode>) | null;
85
84
  /**
86
85
  * Helper function passed to downshift that allows the library to render a
87
86
  * given item to a string label. By default, it extracts the `label` field
@@ -252,9 +252,6 @@ const Dropdown = /*#__PURE__*/React.forwardRef(({
252
252
  [`${prefix}--list-box__wrapper--slug`]: slug,
253
253
  [`${prefix}--list-box__wrapper--decorator`]: decorator
254
254
  });
255
-
256
- // needs to be Capitalized for react to render it correctly
257
- const ItemToElement = itemToElement;
258
255
  const toggleButtonProps = getToggleButtonProps({
259
256
  'aria-label': ariaLabel || deprecatedAriaLabel
260
257
  });
@@ -270,10 +267,20 @@ const Dropdown = /*#__PURE__*/React.forwardRef(({
270
267
  const [currTimer, setCurrTimer] = useState();
271
268
  const [isTyping, setIsTyping] = useState(false);
272
269
  const onKeyDownHandler = useCallback(evt => {
273
- if (evt.code !== 'Space' || !['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(evt.key)) {
270
+ const navigationKeys = ['ArrowDown', 'ArrowUp', ' ', 'Enter'];
271
+
272
+ // If the key is not a navigation key, the user is typing
273
+ if (!navigationKeys.includes(evt.key)) {
274
274
  setIsTyping(true);
275
- }
276
- if (isTyping && evt.code === 'Space' || !['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(evt.key)) {
275
+ // Reset the timer for typing timeout
276
+ if (currTimer) {
277
+ clearTimeout(currTimer);
278
+ }
279
+ setCurrTimer(setTimeout(() => {
280
+ setIsTyping(false);
281
+ }, 3000));
282
+ } else if (isTyping && evt.key === ' ') {
283
+ // If user is typing and presses space, reset the timer
277
284
  if (currTimer) {
278
285
  clearTimeout(currTimer);
279
286
  }
@@ -377,21 +384,18 @@ const Dropdown = /*#__PURE__*/React.forwardRef(({
377
384
  })), slug ? normalizedDecorator : decorator ? /*#__PURE__*/React.createElement("div", {
378
385
  className: `${prefix}--list-box__inner-wrapper--decorator`
379
386
  }, normalizedDecorator) : '', /*#__PURE__*/React.createElement(ListBox.Menu, menuProps, isOpen && items.map((item, index) => {
380
- const isObject = item !== null && typeof item === 'object';
381
387
  const itemProps = getItemProps({
382
388
  item,
383
389
  index
384
390
  });
385
- const title = isObject && 'text' in item && itemToElement ? item.text : itemToString(item);
391
+ const title = itemToString(item);
386
392
  return /*#__PURE__*/React.createElement(ListBox.MenuItem, _extends({
387
393
  key: itemProps.id,
388
394
  isActive: selectedItem === item,
389
395
  isHighlighted: highlightedIndex === index,
390
396
  title: title,
391
397
  disabled: itemProps['aria-disabled']
392
- }, itemProps), typeof item === 'object' && ItemToElement !== undefined && ItemToElement !== null ? /*#__PURE__*/React.createElement(ItemToElement, _extends({
393
- key: itemProps.id
394
- }, item)) : itemToString(item), selectedItem === item && /*#__PURE__*/React.createElement(Checkmark, {
398
+ }, itemProps), itemToElement ? itemToElement(item) : itemToString(item), selectedItem === item && /*#__PURE__*/React.createElement(Checkmark, {
395
399
  className: `${prefix}--list-box__menu-item__selected-icon`
396
400
  }));
397
401
  }))), !inline && !isFluid && !normalizedProps.validation && helper, !inline && !isFluid && normalizedProps.validation);
@@ -471,8 +475,7 @@ Dropdown.propTypes = {
471
475
  */
472
476
  invalidText: PropTypes.node,
473
477
  /**
474
- * Function to render items as custom components instead of strings.
475
- * Defaults to null and is overridden by a getter
478
+ * Renders an item as a custom React node instead of a string.
476
479
  */
477
480
  itemToElement: PropTypes.func,
478
481
  /**
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Copyright IBM Corp. 2022, 2025
2
+ * Copyright IBM Corp. 2022, 2026
3
3
  *
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 React, { ComponentType, type ComponentProps } from 'react';
7
+ import React, { type ComponentProps } from 'react';
8
8
  import ComboBox from '../ComboBox';
9
9
  import { ComboBoxProps } from '../ComboBox/ComboBox';
10
10
  type ItemToStringHandler<ItemType> = (item: ItemType | null) => string;
@@ -46,11 +46,6 @@ export interface FluidComboBoxProps<ItemType> extends ComboBoxProps<ItemType>, P
46
46
  * Specify if the `FluidComboBox` should render its menu items in condensed mode
47
47
  */
48
48
  isCondensed?: boolean;
49
- /**
50
- * Function to render items as custom components instead of strings.
51
- * Defaults to null and is overridden by a getter
52
- */
53
- itemToElement?: ComponentType<ItemType> | null;
54
49
  /**
55
50
  * Helper function passed to downshift that allows the library to render a
56
51
  * given item to a string label. By default, it extracts the `label` field
@@ -66,8 +66,7 @@ FluidComboBox.propTypes = {
66
66
  */
67
67
  isCondensed: PropTypes.bool,
68
68
  /**
69
- * Function to render items as custom components instead of strings.
70
- * Defaults to null and is overridden by a getter
69
+ * Renders an item as a custom React node instead of a string.
71
70
  */
72
71
  itemToElement: PropTypes.func,
73
72
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2022, 2025
2
+ * Copyright IBM Corp. 2022, 2026
3
3
  *
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.
@@ -43,11 +43,6 @@ export interface FluidDropdownProps<ItemType> extends DropdownProps<ItemType>, P
43
43
  * Specify if the `FluidDropdown` should render its menu items in condensed mode
44
44
  */
45
45
  isCondensed?: boolean;
46
- /**
47
- * Function to render items as custom components instead of strings.
48
- * Defaults to null and is overridden by a getter
49
- */
50
- itemToElement?: React.JSXElementConstructor<ItemType> | null;
51
46
  /**
52
47
  * Helper function passed to downshift that allows the library to render a
53
48
  * given item to a string label. By default, it extracts the `label` field
@@ -67,8 +67,7 @@ FluidDropdown.propTypes = {
67
67
  */
68
68
  isCondensed: PropTypes.bool,
69
69
  /**
70
- * Function to render items as custom components instead of strings.
71
- * Defaults to null and is overridden by a getter
70
+ * Renders an item as a custom React node instead of a string.
72
71
  */
73
72
  itemToElement: PropTypes.func,
74
73
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2022, 2025
2
+ * Copyright IBM Corp. 2022, 2026
3
3
  *
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.
@@ -66,11 +66,6 @@ export interface FluidMultiSelectProps<ItemType> extends MultiSelectProps<ItemTy
66
66
  * @deprecated This prop is deprecated in favor of new component called FluidFilterableMultiSelect and will be removed in the next major release
67
67
  */
68
68
  isFilterable?: boolean;
69
- /**
70
- * Function to render items as custom components instead of strings.
71
- * Defaults to null and is overridden by a getter
72
- */
73
- itemToElement?: React.JSXElementConstructor<ItemType>;
74
69
  /**
75
70
  * Helper function passed to downshift that allows the library to render a
76
71
  * given item to a string label. By default, it extracts the `label` field
@@ -106,8 +106,7 @@ FluidMultiSelect.propTypes = {
106
106
  */
107
107
  isFilterable: PropTypes.bool,
108
108
  /**
109
- * Function to render items as custom components instead of strings.
110
- * Defaults to null and is overridden by a getter
109
+ * Renders an item as a custom React node instead of a string.
111
110
  */
112
111
  itemToElement: PropTypes.func,
113
112
  /**
@@ -52,7 +52,10 @@ const InlineLoading = ({
52
52
  className: `${prefix}--inline-loading__checkmark-container`
53
53
  }, /*#__PURE__*/React.createElement("title", null, iconLabel));
54
54
  }
55
- if (status === 'active') {
55
+ if (status === 'inactive' || status === 'active') {
56
+ if (status === 'inactive') {
57
+ return undefined;
58
+ }
56
59
  if (!iconDescription) {
57
60
  iconLabel = 'loading';
58
61
  }
@@ -63,15 +66,6 @@ const InlineLoading = ({
63
66
  active: status === 'active'
64
67
  });
65
68
  }
66
- if (status === 'inactive') {
67
- if (!iconDescription) {
68
- iconLabel = 'not loading';
69
- }
70
- return /*#__PURE__*/React.createElement("title", {
71
- className: `${prefix}--inline-loading__inactive-status`
72
- }, iconLabel);
73
- }
74
- return undefined;
75
69
  };
76
70
  const loadingText = description && /*#__PURE__*/React.createElement("div", {
77
71
  className: `${prefix}--inline-loading__text`
@@ -83,7 +77,7 @@ const InlineLoading = ({
83
77
  return /*#__PURE__*/React.createElement("div", _extends({
84
78
  className: loadingClasses
85
79
  }, rest, {
86
- "aria-live": rest['aria-live'] ?? 'assertive'
80
+ "aria-live": rest['aria-live'] ?? (status === 'inactive' ? 'off' : 'assertive')
87
81
  }), loadingAnimation, loadingText);
88
82
  };
89
83
  InlineLoading.propTypes = {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2016, 2025
2
+ * Copyright IBM Corp. 2016, 2026
3
3
  *
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.
@@ -82,10 +82,9 @@ export interface MultiSelectProps<ItemType> extends MultiSelectSortingProps<Item
82
82
  */
83
83
  invalidText?: ReactNode;
84
84
  /**
85
- * Function to render items as custom components instead of strings.
86
- * Defaults to null and is overridden by a getter
85
+ * Renders an item as a custom React node instead of a string.
87
86
  */
88
- itemToElement?: React.JSXElementConstructor<ItemType>;
87
+ itemToElement?: ((item: ItemType) => NonNullable<ReactNode>) | null;
89
88
  /**
90
89
  * Helper function passed to downshift that allows the library to render a
91
90
  * given item to a string label. By default, it extracts the `label` field
@@ -294,10 +294,6 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
294
294
  [`${prefix}--autoalign`]: enableFloatingStyles,
295
295
  [`${prefix}--multi-select--selectall`]: selectAll
296
296
  });
297
-
298
- // needs to be capitalized for react to render it correctly
299
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
300
- const ItemToElement = itemToElement;
301
297
  if (selectionFeedback === 'fixed') {
302
298
  sortOptions.selectedItems = [];
303
299
  } else if (selectionFeedback === 'top-after-reopen') {
@@ -526,9 +522,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
526
522
  className: `${prefix}--checkbox-wrapper`
527
523
  }, /*#__PURE__*/React.createElement(Checkbox, {
528
524
  id: `${itemProps.id}__checkbox`,
529
- labelText: itemToElement ? /*#__PURE__*/React.createElement(ItemToElement, _extends({
530
- key: itemProps.id
531
- }, item)) : itemText,
525
+ labelText: itemToElement ? itemToElement(item) : itemText,
532
526
  checked: isChecked,
533
527
  title: useTitleInItem ? itemText : undefined,
534
528
  indeterminate: isIndeterminate,
@@ -619,8 +613,7 @@ MultiSelect.propTypes = {
619
613
  */
620
614
  invalidText: PropTypes.node,
621
615
  /**
622
- * Function to render items as custom components instead of strings.
623
- * Defaults to null and is overridden by a getter
616
+ * Renders an item as a custom React node instead of a string.
624
617
  */
625
618
  itemToElement: PropTypes.func,
626
619
  /**
@@ -572,6 +572,8 @@ function ActionableNotification({
572
572
  role: "link",
573
573
  className: `${prefix}--visually-hidden`
574
574
  }, "Focus sentinel"), /*#__PURE__*/React.createElement("div", {
575
+ ref: innerModal
576
+ }, /*#__PURE__*/React.createElement("div", {
575
577
  className: `${prefix}--actionable-notification__details`
576
578
  }, /*#__PURE__*/React.createElement(NotificationIcon, {
577
579
  notificationType: inline ? 'inline' : 'toast',
@@ -593,8 +595,7 @@ function ActionableNotification({
593
595
  as: "div",
594
596
  className: `${prefix}--actionable-notification__caption`
595
597
  }, caption), children))), /*#__PURE__*/React.createElement("div", {
596
- className: `${prefix}--actionable-notification__button-wrapper`,
597
- ref: innerModal
598
+ className: `${prefix}--actionable-notification__button-wrapper`
598
599
  }, actionButtonLabel && /*#__PURE__*/React.createElement(NotificationActionButton, {
599
600
  onClick: onActionButtonClick,
600
601
  inline: inline
@@ -602,7 +603,7 @@ function ActionableNotification({
602
603
  "aria-label": deprecatedAriaLabel || ariaLabel,
603
604
  notificationType: "actionable",
604
605
  onClick: handleCloseButtonClick
605
- })), !focusTrapWithoutSentinels && /*#__PURE__*/React.createElement("span", {
606
+ }))), !focusTrapWithoutSentinels && /*#__PURE__*/React.createElement("span", {
606
607
  ref: endTrap,
607
608
  tabIndex: 0,
608
609
  role: "link",
@@ -144,9 +144,7 @@ const TextArea = frFn((props, forwardRef) => {
144
144
  setTimeout(() => {
145
145
  setTextCount(0);
146
146
  }, 0);
147
- return;
148
- }
149
- if (enableCounter && typeof maxCount !== 'undefined' && textareaRef.current !== null) {
147
+ } else if (enableCounter && typeof maxCount !== 'undefined' && textareaRef.current !== null) {
150
148
  const matchedWords = evt.target?.value?.match(/\p{L}+/gu);
151
149
  if (matchedWords && matchedWords.length <= maxCount) {
152
150
  textareaRef.current.removeAttribute('maxLength');
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2019, 2025
2
+ * Copyright IBM Corp. 2019, 2026
3
3
  *
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,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
- import React, { useState, useEffect, useCallback, useRef, cloneElement } from 'react';
9
+ import React, { forwardRef, useState, useEffect, useCallback, useRef, cloneElement, Children } from 'react';
10
10
  import PropTypes from 'prop-types';
11
11
  import cx from 'classnames';
12
12
  import { AiLabel, CheckboxCheckedFilled, Checkbox, ChevronDown, ArrowRight, Error } from '@carbon/icons-react';
@@ -363,7 +363,7 @@ SelectableTile.propTypes = {
363
363
  */
364
364
  value: deprecate(PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 'The `value` property is no longer used. It will be removed in the next major release.`')
365
365
  };
366
- const ExpandableTile = /*#__PURE__*/React.forwardRef(({
366
+ const ExpandableTile = /*#__PURE__*/forwardRef(({
367
367
  tabIndex = 0,
368
368
  className,
369
369
  children,
@@ -382,11 +382,8 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
382
382
  hasRoundedCorners,
383
383
  ...rest
384
384
  }, forwardRef) => {
385
- const [isTileMaxHeight, setIsTileMaxHeight] = useState(tileMaxHeight);
386
- const [isTilePadding, setIsTilePadding] = useState(tilePadding);
387
- const [prevExpanded, setPrevExpanded] = useState(expanded);
388
- const [prevTileMaxHeight, setPrevTileMaxHeight] = useState(tileMaxHeight);
389
- const [prevTilePadding, setPrevTilePadding] = useState(tilePadding);
385
+ const [measuredAboveHeight, setMeasuredAboveHeight] = useState(0);
386
+ const [measuredPadding, setMeasuredPadding] = useState(0);
390
387
  const [isExpanded, setIsExpanded] = useState(expanded);
391
388
  const [interactive, setInteractive] = useState(true);
392
389
  const aboveTheFold = useRef(null);
@@ -396,105 +393,74 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
396
393
  const tile = useRef(null);
397
394
  const ref = useMergedRefs([forwardRef, tile]);
398
395
  const prefix = usePrefix();
399
- if (expanded !== prevExpanded) {
396
+ useEffect(() => {
400
397
  setIsExpanded(expanded);
401
- setPrevExpanded(expanded);
402
- setMaxHeight();
403
- }
404
- if (tileMaxHeight !== prevTileMaxHeight) {
405
- setIsTileMaxHeight(tileMaxHeight);
406
- setPrevTileMaxHeight(tileMaxHeight);
407
- }
408
- if (tilePadding !== prevTilePadding) {
409
- setIsTilePadding(tilePadding);
410
- setPrevTilePadding(tilePadding);
411
- }
412
- function setMaxHeight() {
413
- if (isExpanded && tileContent.current) {
414
- setIsTileMaxHeight(tileContent.current.getBoundingClientRect()?.height);
415
- }
416
- if (aboveTheFold.current) {
417
- setIsTileMaxHeight(aboveTheFold.current.getBoundingClientRect().height);
418
- }
419
- }
420
- function handleClick(evt) {
421
- evt?.persist?.();
422
- setIsExpanded(!isExpanded);
423
- setMaxHeight();
424
- }
425
- function handleKeyUp(evt) {
398
+ }, [expanded]);
399
+ const handleClick = () => {
400
+ setIsExpanded(prev => !prev);
401
+ };
402
+ const handleKeyUp = evt => {
426
403
  if (evt.target !== tile.current && evt.target !== chevronInteractiveRef.current) {
427
404
  if (matches(evt, [Enter, Space])) {
428
405
  evt.preventDefault();
429
406
  }
430
407
  }
431
- }
432
- function getChildren() {
433
- return React.Children.toArray(children);
434
- }
408
+ };
435
409
  const classNames = cx(`${prefix}--tile`, `${prefix}--tile--expandable`, {
436
410
  [`${prefix}--tile--is-expanded`]: isExpanded,
437
411
  [`${prefix}--tile--light`]: light
438
412
  }, className);
439
- const interactiveClassNames = cx(`${prefix}--tile`, `${prefix}--tile--expandable`, `${prefix}--tile--expandable--interactive`, {
440
- [`${prefix}--tile--is-expanded`]: isExpanded,
441
- [`${prefix}--tile--light`]: light,
413
+ const interactiveClassNames = cx(classNames, `${prefix}--tile--expandable--interactive`, {
442
414
  [`${prefix}--tile--slug`]: slug,
443
415
  [`${prefix}--tile--slug-rounded`]: slug && hasRoundedCorners,
444
416
  [`${prefix}--tile--decorator`]: decorator,
445
417
  [`${prefix}--tile--decorator-rounded`]: decorator && hasRoundedCorners
446
- }, className);
418
+ });
447
419
  const chevronInteractiveClassNames = cx(`${prefix}--tile__chevron`, `${prefix}--tile__chevron--interactive`);
448
- const childrenAsArray = getChildren();
420
+ const childrenAsArray = Children.toArray(children);
449
421
  useIsomorphicEffect(() => {
450
422
  if (!tile.current || !aboveTheFold.current) {
451
423
  return;
452
424
  }
453
- const getStyle = window.getComputedStyle(tile.current, null);
454
- const {
455
- current: node
456
- } = aboveTheFold;
457
- const {
458
- height
459
- } = node.getBoundingClientRect();
460
- const paddingTop = parseInt(getStyle.getPropertyValue('padding-top'), 10);
461
- const paddingBottom = parseInt(getStyle.getPropertyValue('padding-bottom'), 10);
462
- setIsTileMaxHeight(height);
463
- setIsTilePadding(paddingTop + paddingBottom);
464
- }, [isTileMaxHeight]);
425
+ const style = window.getComputedStyle(tile.current);
426
+ const paddingTop = parseInt(style.getPropertyValue('padding-top'), 10) || 0;
427
+ const paddingBottom = parseInt(style.getPropertyValue('padding-bottom'), 10) || 0;
428
+ setMeasuredPadding(paddingTop + paddingBottom);
429
+ setMeasuredAboveHeight(aboveTheFold.current.scrollHeight);
430
+ }, []);
465
431
  useIsomorphicEffect(() => {
466
432
  if (!aboveTheFold.current || !belowTheFold.current) {
467
433
  return;
468
434
  }
469
-
470
- // Interactive elements or elements that are given a role should be treated
471
- // the same because elements with a role can not be rendered inside a `button`
472
- if (!getInteractiveContent(belowTheFold.current) && !getRoleContent(belowTheFold.current) && !getInteractiveContent(aboveTheFold.current) && !getRoleContent(aboveTheFold.current) && !(slug || decorator)) {
473
- setInteractive(false);
474
- }
475
- }, [slug, decorator]);
435
+ const hasInteractive = Boolean(getInteractiveContent(aboveTheFold.current)) || Boolean(getRoleContent(aboveTheFold.current)) || Boolean(getInteractiveContent(belowTheFold.current)) || Boolean(getRoleContent(belowTheFold.current)) || Boolean(slug || decorator);
436
+ setInteractive(hasInteractive);
437
+ }, [slug, decorator, children]);
476
438
  useIsomorphicEffect(() => {
477
439
  if (!tile.current) {
478
440
  return;
479
441
  }
480
442
  if (isExpanded) {
481
443
  tile.current.style.maxHeight = '';
482
- } else {
483
- tile.current.style.maxHeight = isTileMaxHeight + isTilePadding + 'px';
444
+ return;
484
445
  }
485
- }, [isExpanded, isTileMaxHeight, isTilePadding]);
446
+ const measured = measuredAboveHeight || aboveTheFold.current?.scrollHeight || 0;
447
+ const baseHeight = tileMaxHeight > 0 ? tileMaxHeight : measured;
448
+ const pad = tilePadding > 0 ? tilePadding : measuredPadding;
449
+ tile.current.style.maxHeight = `${baseHeight + pad}px`;
450
+ }, [isExpanded, tileMaxHeight, tilePadding, measuredAboveHeight, measuredPadding]);
486
451
  useEffect(() => {
487
452
  if (!aboveTheFold.current) {
488
453
  return;
489
454
  }
490
- const resizeObserver = new ResizeObserver(entries => {
491
- const [aboveTheFold] = entries;
492
- setIsTileMaxHeight(aboveTheFold.contentRect.height);
455
+ const resizeObserver = new ResizeObserver(() => {
456
+ if (aboveTheFold.current) {
457
+ setMeasuredAboveHeight(aboveTheFold.current.scrollHeight);
458
+ }
493
459
  });
494
460
  resizeObserver.observe(aboveTheFold.current);
495
461
  return () => resizeObserver.disconnect();
496
- }, [isTileMaxHeight, isTilePadding]);
497
- const belowTheFoldId = useId('expandable-tile-interactive');
462
+ }, []);
463
+ const belowTheFoldId = useId(interactive ? 'expandable-tile-interactive' : 'expandable-tile');
498
464
 
499
465
  // AILabel is always size `xs`
500
466
  const candidate = slug ?? decorator;
@@ -529,6 +495,7 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
529
495
  type: "button",
530
496
  ref: ref,
531
497
  className: classNames,
498
+ "aria-controls": belowTheFoldId,
532
499
  "aria-expanded": isExpanded,
533
500
  title: isExpanded ? tileExpandedIconText : tileCollapsedIconText
534
501
  }, rest, {
@@ -544,6 +511,7 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
544
511
  className: `${prefix}--tile__chevron`
545
512
  }, /*#__PURE__*/React.createElement("span", null, isExpanded ? tileExpandedLabel : tileCollapsedLabel), _ChevronDown2 || (_ChevronDown2 = /*#__PURE__*/React.createElement(ChevronDown, null))), /*#__PURE__*/React.createElement("div", {
546
513
  ref: belowTheFold,
514
+ id: belowTheFoldId,
547
515
  className: `${prefix}--tile-content`
548
516
  }, childrenAsArray[1])));
549
517
  });
@@ -19,7 +19,6 @@ export interface SideNavProps {
19
19
  onOverlayClick?: MouseEventHandler<HTMLDivElement> | undefined;
20
20
  onSideNavBlur?: () => void;
21
21
  enterDelayMs?: number;
22
- inert?: boolean;
23
22
  }
24
23
  interface SideNavContextData {
25
24
  isRail?: boolean | undefined;
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
- import React, { forwardRef, useRef, isValidElement, createContext } from 'react';
9
+ import React, { forwardRef, useRef, isValidElement, useEffect, createContext } from 'react';
10
10
  import cx from 'classnames';
11
11
  import PropTypes from 'prop-types';
12
12
  import { AriaLabelPropType } from '../../prop-types/AriaPropTypes.js';
@@ -157,6 +157,16 @@ const SideNav = frFn((props, ref) => {
157
157
  });
158
158
  const lgMediaQuery = `(min-width: ${breakpoints.lg.width})`;
159
159
  const isLg = useMatchMedia(lgMediaQuery);
160
+ const inertEnabled = !isRail ? !(expanded || isLg) : false;
161
+ useEffect(() => {
162
+ const node = sideNavRef.current;
163
+ if (!node) return;
164
+ if (inertEnabled) {
165
+ node.setAttribute('inert', '');
166
+ } else {
167
+ node.removeAttribute('inert');
168
+ }
169
+ }, [inertEnabled]);
160
170
  return /*#__PURE__*/React.createElement(SideNavContext.Provider, {
161
171
  value: {
162
172
  isRail
@@ -170,8 +180,7 @@ const SideNav = frFn((props, ref) => {
170
180
  }), /*#__PURE__*/React.createElement("nav", _extends({
171
181
  tabIndex: -1,
172
182
  ref: navRef,
173
- className: `${prefix}--side-nav__navigation ${className}`,
174
- inert: !isRail ? !(expanded || isLg) : undefined
183
+ className: `${prefix}--side-nav__navigation ${className}`
175
184
  }, accessibilityLabel, eventHandlers, other), childrenToRender));
176
185
  });
177
186
  SideNav.displayName = 'SideNav';
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import { UseComboboxProps, UseComboboxActions } from 'downshift';
8
- import { type ComponentType, type InputHTMLAttributes, type MouseEvent, type PropsWithChildren, type ReactElement, type ReactNode, type RefAttributes, type RefObject } from 'react';
8
+ import { type InputHTMLAttributes, type MouseEvent, type PropsWithChildren, type ReactElement, type ReactNode, type RefAttributes, type RefObject } from 'react';
9
9
  import { type ListBoxMenuIconTranslationKey, type ListBoxSelectionTranslationKey, type ListBoxSize } from '../ListBox';
10
10
  import type { TranslateWithId } from '../../types/common';
11
11
  type ExcludedAttributes = 'id' | 'onChange' | 'onClick' | 'type' | 'size';
@@ -98,10 +98,9 @@ export interface ComboBoxProps<ItemType> extends Omit<InputHTMLAttributes<HTMLIn
98
98
  */
99
99
  invalidText?: ReactNode;
100
100
  /**
101
- * Optional function to render items as custom components instead of strings.
102
- * Defaults to null and is overridden by a getter
101
+ * Renders an item as a custom React node instead of a string.
103
102
  */
104
- itemToElement?: ComponentType<ItemType> | null;
103
+ itemToElement?: ((item: ItemType) => ReactNode) | null;
105
104
  /**
106
105
  * Helper function passed to downshift that allows the library to render a
107
106
  * given item to a string label. By default, it extracts the `label` field