@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
@@ -454,9 +454,6 @@ const ComboBox = /*#__PURE__*/React.forwardRef((props, ref) => {
454
454
  [`${prefix}--combo-box--input--focus`]: isFocused
455
455
  });
456
456
 
457
- // needs to be Capitalized for react to render it correctly
458
- const ItemToElement = itemToElement;
459
-
460
457
  // AILabel always size `mini`
461
458
  const candidate = slug ?? decorator;
462
459
  const candidateIsAILabel = utils.isComponentElement(candidate, index$1.AILabel);
@@ -788,9 +785,7 @@ const ComboBox = /*#__PURE__*/React.forwardRef((props, ref) => {
788
785
  isHighlighted: highlightedIndex === index,
789
786
  title: title,
790
787
  disabled: disabled
791
- }, modifiedItemProps), ItemToElement ? /*#__PURE__*/React.createElement(ItemToElement, _rollupPluginBabelHelpers.extends({
792
- key: itemProps.id
793
- }, item)) : itemToString(item), isEqual(currentSelectedItem, item) && /*#__PURE__*/React.createElement(iconsReact.Checkmark, {
788
+ }, modifiedItemProps), itemToElement ? itemToElement(item) : itemToString(item), isEqual(currentSelectedItem, item) && /*#__PURE__*/React.createElement(iconsReact.Checkmark, {
794
789
  className: `${prefix}--list-box__menu-item__selected-icon`
795
790
  }));
796
791
  }) : null)), helperText && !invalid && !warn && !isFluid && /*#__PURE__*/React.createElement(Text.Text, {
@@ -886,8 +881,7 @@ ComboBox.propTypes = {
886
881
  */
887
882
  invalidText: PropTypes.node,
888
883
  /**
889
- * Optional function to render items as custom components instead of strings.
890
- * Defaults to null and is overridden by a getter
884
+ * Renders an item as a custom React node instead of a string.
891
885
  */
892
886
  itemToElement: PropTypes.func,
893
887
  /**
@@ -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
@@ -256,9 +256,6 @@ const Dropdown = /*#__PURE__*/React.forwardRef(({
256
256
  [`${prefix}--list-box__wrapper--slug`]: slug,
257
257
  [`${prefix}--list-box__wrapper--decorator`]: decorator
258
258
  });
259
-
260
- // needs to be Capitalized for react to render it correctly
261
- const ItemToElement = itemToElement;
262
259
  const toggleButtonProps = getToggleButtonProps({
263
260
  'aria-label': ariaLabel || deprecatedAriaLabel
264
261
  });
@@ -274,10 +271,20 @@ const Dropdown = /*#__PURE__*/React.forwardRef(({
274
271
  const [currTimer, setCurrTimer] = React.useState();
275
272
  const [isTyping, setIsTyping] = React.useState(false);
276
273
  const onKeyDownHandler = React.useCallback(evt => {
277
- if (evt.code !== 'Space' || !['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(evt.key)) {
274
+ const navigationKeys = ['ArrowDown', 'ArrowUp', ' ', 'Enter'];
275
+
276
+ // If the key is not a navigation key, the user is typing
277
+ if (!navigationKeys.includes(evt.key)) {
278
278
  setIsTyping(true);
279
- }
280
- if (isTyping && evt.code === 'Space' || !['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(evt.key)) {
279
+ // Reset the timer for typing timeout
280
+ if (currTimer) {
281
+ clearTimeout(currTimer);
282
+ }
283
+ setCurrTimer(setTimeout(() => {
284
+ setIsTyping(false);
285
+ }, 3000));
286
+ } else if (isTyping && evt.key === ' ') {
287
+ // If user is typing and presses space, reset the timer
281
288
  if (currTimer) {
282
289
  clearTimeout(currTimer);
283
290
  }
@@ -381,21 +388,18 @@ const Dropdown = /*#__PURE__*/React.forwardRef(({
381
388
  })), slug ? normalizedDecorator : decorator ? /*#__PURE__*/React.createElement("div", {
382
389
  className: `${prefix}--list-box__inner-wrapper--decorator`
383
390
  }, normalizedDecorator) : '', /*#__PURE__*/React.createElement(index$2.default.Menu, menuProps, isOpen && items.map((item, index) => {
384
- const isObject = item !== null && typeof item === 'object';
385
391
  const itemProps = getItemProps({
386
392
  item,
387
393
  index
388
394
  });
389
- const title = isObject && 'text' in item && itemToElement ? item.text : itemToString(item);
395
+ const title = itemToString(item);
390
396
  return /*#__PURE__*/React.createElement(index$2.default.MenuItem, _rollupPluginBabelHelpers.extends({
391
397
  key: itemProps.id,
392
398
  isActive: selectedItem === item,
393
399
  isHighlighted: highlightedIndex === index,
394
400
  title: title,
395
401
  disabled: itemProps['aria-disabled']
396
- }, itemProps), typeof item === 'object' && ItemToElement !== undefined && ItemToElement !== null ? /*#__PURE__*/React.createElement(ItemToElement, _rollupPluginBabelHelpers.extends({
397
- key: itemProps.id
398
- }, item)) : itemToString(item), selectedItem === item && /*#__PURE__*/React.createElement(iconsReact.Checkmark, {
402
+ }, itemProps), itemToElement ? itemToElement(item) : itemToString(item), selectedItem === item && /*#__PURE__*/React.createElement(iconsReact.Checkmark, {
399
403
  className: `${prefix}--list-box__menu-item__selected-icon`
400
404
  }));
401
405
  }))), !inline && !isFluid && !normalizedProps.validation && helper, !inline && !isFluid && normalizedProps.validation);
@@ -475,8 +479,7 @@ Dropdown.propTypes = {
475
479
  */
476
480
  invalidText: PropTypes.node,
477
481
  /**
478
- * Function to render items as custom components instead of strings.
479
- * Defaults to null and is overridden by a getter
482
+ * Renders an item as a custom React node instead of a string.
480
483
  */
481
484
  itemToElement: PropTypes.func,
482
485
  /**
@@ -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
@@ -70,8 +70,7 @@ FluidComboBox.propTypes = {
70
70
  */
71
71
  isCondensed: PropTypes.bool,
72
72
  /**
73
- * Function to render items as custom components instead of strings.
74
- * Defaults to null and is overridden by a getter
73
+ * Renders an item as a custom React node instead of a string.
75
74
  */
76
75
  itemToElement: PropTypes.func,
77
76
  /**
@@ -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
@@ -71,8 +71,7 @@ FluidDropdown.propTypes = {
71
71
  */
72
72
  isCondensed: PropTypes.bool,
73
73
  /**
74
- * Function to render items as custom components instead of strings.
75
- * Defaults to null and is overridden by a getter
74
+ * Renders an item as a custom React node instead of a string.
76
75
  */
77
76
  itemToElement: PropTypes.func,
78
77
  /**
@@ -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
@@ -110,8 +110,7 @@ FluidMultiSelect.propTypes = {
110
110
  */
111
111
  isFilterable: PropTypes.bool,
112
112
  /**
113
- * Function to render items as custom components instead of strings.
114
- * Defaults to null and is overridden by a getter
113
+ * Renders an item as a custom React node instead of a string.
115
114
  */
116
115
  itemToElement: PropTypes.func,
117
116
  /**
@@ -56,7 +56,10 @@ const InlineLoading = ({
56
56
  className: `${prefix}--inline-loading__checkmark-container`
57
57
  }, /*#__PURE__*/React.createElement("title", null, iconLabel));
58
58
  }
59
- if (status === 'active') {
59
+ if (status === 'inactive' || status === 'active') {
60
+ if (status === 'inactive') {
61
+ return undefined;
62
+ }
60
63
  if (!iconDescription) {
61
64
  iconLabel = 'loading';
62
65
  }
@@ -67,15 +70,6 @@ const InlineLoading = ({
67
70
  active: status === 'active'
68
71
  });
69
72
  }
70
- if (status === 'inactive') {
71
- if (!iconDescription) {
72
- iconLabel = 'not loading';
73
- }
74
- return /*#__PURE__*/React.createElement("title", {
75
- className: `${prefix}--inline-loading__inactive-status`
76
- }, iconLabel);
77
- }
78
- return undefined;
79
73
  };
80
74
  const loadingText = description && /*#__PURE__*/React.createElement("div", {
81
75
  className: `${prefix}--inline-loading__text`
@@ -87,7 +81,7 @@ const InlineLoading = ({
87
81
  return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({
88
82
  className: loadingClasses
89
83
  }, rest, {
90
- "aria-live": rest['aria-live'] ?? 'assertive'
84
+ "aria-live": rest['aria-live'] ?? (status === 'inactive' ? 'off' : 'assertive')
91
85
  }), loadingAnimation, loadingText);
92
86
  };
93
87
  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
@@ -296,10 +296,6 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
296
296
  [`${prefix}--autoalign`]: enableFloatingStyles,
297
297
  [`${prefix}--multi-select--selectall`]: selectAll
298
298
  });
299
-
300
- // needs to be capitalized for react to render it correctly
301
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
302
- const ItemToElement = itemToElement;
303
299
  if (selectionFeedback === 'fixed') {
304
300
  sortOptions.selectedItems = [];
305
301
  } else if (selectionFeedback === 'top-after-reopen') {
@@ -528,9 +524,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
528
524
  className: `${prefix}--checkbox-wrapper`
529
525
  }, /*#__PURE__*/React.createElement(Checkbox.default, {
530
526
  id: `${itemProps.id}__checkbox`,
531
- labelText: itemToElement ? /*#__PURE__*/React.createElement(ItemToElement, _rollupPluginBabelHelpers.extends({
532
- key: itemProps.id
533
- }, item)) : itemText,
527
+ labelText: itemToElement ? itemToElement(item) : itemText,
534
528
  checked: isChecked,
535
529
  title: useTitleInItem ? itemText : undefined,
536
530
  indeterminate: isIndeterminate,
@@ -621,8 +615,7 @@ MultiSelect.propTypes = {
621
615
  */
622
616
  invalidText: PropTypes.node,
623
617
  /**
624
- * Function to render items as custom components instead of strings.
625
- * Defaults to null and is overridden by a getter
618
+ * Renders an item as a custom React node instead of a string.
626
619
  */
627
620
  itemToElement: PropTypes.func,
628
621
  /**
@@ -574,6 +574,8 @@ function ActionableNotification({
574
574
  role: "link",
575
575
  className: `${prefix}--visually-hidden`
576
576
  }, "Focus sentinel"), /*#__PURE__*/React.createElement("div", {
577
+ ref: innerModal
578
+ }, /*#__PURE__*/React.createElement("div", {
577
579
  className: `${prefix}--actionable-notification__details`
578
580
  }, /*#__PURE__*/React.createElement(NotificationIcon, {
579
581
  notificationType: inline ? 'inline' : 'toast',
@@ -595,8 +597,7 @@ function ActionableNotification({
595
597
  as: "div",
596
598
  className: `${prefix}--actionable-notification__caption`
597
599
  }, caption), children))), /*#__PURE__*/React.createElement("div", {
598
- className: `${prefix}--actionable-notification__button-wrapper`,
599
- ref: innerModal
600
+ className: `${prefix}--actionable-notification__button-wrapper`
600
601
  }, actionButtonLabel && /*#__PURE__*/React.createElement(NotificationActionButton, {
601
602
  onClick: onActionButtonClick,
602
603
  inline: inline
@@ -604,7 +605,7 @@ function ActionableNotification({
604
605
  "aria-label": deprecatedAriaLabel || ariaLabel,
605
606
  notificationType: "actionable",
606
607
  onClick: handleCloseButtonClick
607
- })), !focusTrapWithoutSentinels && /*#__PURE__*/React.createElement("span", {
608
+ }))), !focusTrapWithoutSentinels && /*#__PURE__*/React.createElement("span", {
608
609
  ref: endTrap,
609
610
  tabIndex: 0,
610
611
  role: "link",
@@ -148,9 +148,7 @@ const TextArea = frFn((props, forwardRef) => {
148
148
  setTimeout(() => {
149
149
  setTextCount(0);
150
150
  }, 0);
151
- return;
152
- }
153
- if (enableCounter && typeof maxCount !== 'undefined' && textareaRef.current !== null) {
151
+ } else if (enableCounter && typeof maxCount !== 'undefined' && textareaRef.current !== null) {
154
152
  const matchedWords = evt.target?.value?.match(/\p{L}+/gu);
155
153
  if (matchedWords && matchedWords.length <= maxCount) {
156
154
  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.
@@ -384,11 +384,8 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
384
384
  hasRoundedCorners,
385
385
  ...rest
386
386
  }, forwardRef) => {
387
- const [isTileMaxHeight, setIsTileMaxHeight] = React.useState(tileMaxHeight);
388
- const [isTilePadding, setIsTilePadding] = React.useState(tilePadding);
389
- const [prevExpanded, setPrevExpanded] = React.useState(expanded);
390
- const [prevTileMaxHeight, setPrevTileMaxHeight] = React.useState(tileMaxHeight);
391
- const [prevTilePadding, setPrevTilePadding] = React.useState(tilePadding);
387
+ const [measuredAboveHeight, setMeasuredAboveHeight] = React.useState(0);
388
+ const [measuredPadding, setMeasuredPadding] = React.useState(0);
392
389
  const [isExpanded, setIsExpanded] = React.useState(expanded);
393
390
  const [interactive, setInteractive] = React.useState(true);
394
391
  const aboveTheFold = React.useRef(null);
@@ -398,105 +395,74 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
398
395
  const tile = React.useRef(null);
399
396
  const ref = useMergedRefs.useMergedRefs([forwardRef, tile]);
400
397
  const prefix = usePrefix.usePrefix();
401
- if (expanded !== prevExpanded) {
398
+ React.useEffect(() => {
402
399
  setIsExpanded(expanded);
403
- setPrevExpanded(expanded);
404
- setMaxHeight();
405
- }
406
- if (tileMaxHeight !== prevTileMaxHeight) {
407
- setIsTileMaxHeight(tileMaxHeight);
408
- setPrevTileMaxHeight(tileMaxHeight);
409
- }
410
- if (tilePadding !== prevTilePadding) {
411
- setIsTilePadding(tilePadding);
412
- setPrevTilePadding(tilePadding);
413
- }
414
- function setMaxHeight() {
415
- if (isExpanded && tileContent.current) {
416
- setIsTileMaxHeight(tileContent.current.getBoundingClientRect()?.height);
417
- }
418
- if (aboveTheFold.current) {
419
- setIsTileMaxHeight(aboveTheFold.current.getBoundingClientRect().height);
420
- }
421
- }
422
- function handleClick(evt) {
423
- evt?.persist?.();
424
- setIsExpanded(!isExpanded);
425
- setMaxHeight();
426
- }
427
- function handleKeyUp(evt) {
400
+ }, [expanded]);
401
+ const handleClick = () => {
402
+ setIsExpanded(prev => !prev);
403
+ };
404
+ const handleKeyUp = evt => {
428
405
  if (evt.target !== tile.current && evt.target !== chevronInteractiveRef.current) {
429
406
  if (match.matches(evt, [keys.Enter, keys.Space])) {
430
407
  evt.preventDefault();
431
408
  }
432
409
  }
433
- }
434
- function getChildren() {
435
- return React.Children.toArray(children);
436
- }
410
+ };
437
411
  const classNames = cx(`${prefix}--tile`, `${prefix}--tile--expandable`, {
438
412
  [`${prefix}--tile--is-expanded`]: isExpanded,
439
413
  [`${prefix}--tile--light`]: light
440
414
  }, className);
441
- const interactiveClassNames = cx(`${prefix}--tile`, `${prefix}--tile--expandable`, `${prefix}--tile--expandable--interactive`, {
442
- [`${prefix}--tile--is-expanded`]: isExpanded,
443
- [`${prefix}--tile--light`]: light,
415
+ const interactiveClassNames = cx(classNames, `${prefix}--tile--expandable--interactive`, {
444
416
  [`${prefix}--tile--slug`]: slug,
445
417
  [`${prefix}--tile--slug-rounded`]: slug && hasRoundedCorners,
446
418
  [`${prefix}--tile--decorator`]: decorator,
447
419
  [`${prefix}--tile--decorator-rounded`]: decorator && hasRoundedCorners
448
- }, className);
420
+ });
449
421
  const chevronInteractiveClassNames = cx(`${prefix}--tile__chevron`, `${prefix}--tile__chevron--interactive`);
450
- const childrenAsArray = getChildren();
422
+ const childrenAsArray = React.Children.toArray(children);
451
423
  useIsomorphicEffect.default(() => {
452
424
  if (!tile.current || !aboveTheFold.current) {
453
425
  return;
454
426
  }
455
- const getStyle = window.getComputedStyle(tile.current, null);
456
- const {
457
- current: node
458
- } = aboveTheFold;
459
- const {
460
- height
461
- } = node.getBoundingClientRect();
462
- const paddingTop = parseInt(getStyle.getPropertyValue('padding-top'), 10);
463
- const paddingBottom = parseInt(getStyle.getPropertyValue('padding-bottom'), 10);
464
- setIsTileMaxHeight(height);
465
- setIsTilePadding(paddingTop + paddingBottom);
466
- }, [isTileMaxHeight]);
427
+ const style = window.getComputedStyle(tile.current);
428
+ const paddingTop = parseInt(style.getPropertyValue('padding-top'), 10) || 0;
429
+ const paddingBottom = parseInt(style.getPropertyValue('padding-bottom'), 10) || 0;
430
+ setMeasuredPadding(paddingTop + paddingBottom);
431
+ setMeasuredAboveHeight(aboveTheFold.current.scrollHeight);
432
+ }, []);
467
433
  useIsomorphicEffect.default(() => {
468
434
  if (!aboveTheFold.current || !belowTheFold.current) {
469
435
  return;
470
436
  }
471
-
472
- // Interactive elements or elements that are given a role should be treated
473
- // the same because elements with a role can not be rendered inside a `button`
474
- if (!useNoInteractiveChildren.getInteractiveContent(belowTheFold.current) && !useNoInteractiveChildren.getRoleContent(belowTheFold.current) && !useNoInteractiveChildren.getInteractiveContent(aboveTheFold.current) && !useNoInteractiveChildren.getRoleContent(aboveTheFold.current) && !(slug || decorator)) {
475
- setInteractive(false);
476
- }
477
- }, [slug, decorator]);
437
+ const hasInteractive = Boolean(useNoInteractiveChildren.getInteractiveContent(aboveTheFold.current)) || Boolean(useNoInteractiveChildren.getRoleContent(aboveTheFold.current)) || Boolean(useNoInteractiveChildren.getInteractiveContent(belowTheFold.current)) || Boolean(useNoInteractiveChildren.getRoleContent(belowTheFold.current)) || Boolean(slug || decorator);
438
+ setInteractive(hasInteractive);
439
+ }, [slug, decorator, children]);
478
440
  useIsomorphicEffect.default(() => {
479
441
  if (!tile.current) {
480
442
  return;
481
443
  }
482
444
  if (isExpanded) {
483
445
  tile.current.style.maxHeight = '';
484
- } else {
485
- tile.current.style.maxHeight = isTileMaxHeight + isTilePadding + 'px';
446
+ return;
486
447
  }
487
- }, [isExpanded, isTileMaxHeight, isTilePadding]);
448
+ const measured = measuredAboveHeight || aboveTheFold.current?.scrollHeight || 0;
449
+ const baseHeight = tileMaxHeight > 0 ? tileMaxHeight : measured;
450
+ const pad = tilePadding > 0 ? tilePadding : measuredPadding;
451
+ tile.current.style.maxHeight = `${baseHeight + pad}px`;
452
+ }, [isExpanded, tileMaxHeight, tilePadding, measuredAboveHeight, measuredPadding]);
488
453
  React.useEffect(() => {
489
454
  if (!aboveTheFold.current) {
490
455
  return;
491
456
  }
492
- const resizeObserver = new ResizeObserver(entries => {
493
- const [aboveTheFold] = entries;
494
- setIsTileMaxHeight(aboveTheFold.contentRect.height);
457
+ const resizeObserver = new ResizeObserver(() => {
458
+ if (aboveTheFold.current) {
459
+ setMeasuredAboveHeight(aboveTheFold.current.scrollHeight);
460
+ }
495
461
  });
496
462
  resizeObserver.observe(aboveTheFold.current);
497
463
  return () => resizeObserver.disconnect();
498
- }, [isTileMaxHeight, isTilePadding]);
499
- const belowTheFoldId = useId.useId('expandable-tile-interactive');
464
+ }, []);
465
+ const belowTheFoldId = useId.useId(interactive ? 'expandable-tile-interactive' : 'expandable-tile');
500
466
 
501
467
  // AILabel is always size `xs`
502
468
  const candidate = slug ?? decorator;
@@ -531,6 +497,7 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
531
497
  type: "button",
532
498
  ref: ref,
533
499
  className: classNames,
500
+ "aria-controls": belowTheFoldId,
534
501
  "aria-expanded": isExpanded,
535
502
  title: isExpanded ? tileExpandedIconText : tileCollapsedIconText
536
503
  }, rest, {
@@ -546,6 +513,7 @@ const ExpandableTile = /*#__PURE__*/React.forwardRef(({
546
513
  className: `${prefix}--tile__chevron`
547
514
  }, /*#__PURE__*/React.createElement("span", null, isExpanded ? tileExpandedLabel : tileCollapsedLabel), _ChevronDown2 || (_ChevronDown2 = /*#__PURE__*/React.createElement(iconsReact.ChevronDown, null))), /*#__PURE__*/React.createElement("div", {
548
515
  ref: belowTheFold,
516
+ id: belowTheFoldId,
549
517
  className: `${prefix}--tile-content`
550
518
  }, childrenAsArray[1])));
551
519
  });
@@ -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;
@@ -161,6 +161,16 @@ const SideNav = frFn((props, ref) => {
161
161
  });
162
162
  const lgMediaQuery = `(min-width: ${layout.breakpoints.lg.width})`;
163
163
  const isLg = useMatchMedia.useMatchMedia(lgMediaQuery);
164
+ const inertEnabled = !isRail ? !(expanded || isLg) : false;
165
+ React.useEffect(() => {
166
+ const node = sideNavRef.current;
167
+ if (!node) return;
168
+ if (inertEnabled) {
169
+ node.setAttribute('inert', '');
170
+ } else {
171
+ node.removeAttribute('inert');
172
+ }
173
+ }, [inertEnabled]);
164
174
  return /*#__PURE__*/React.createElement(SideNavContext.Provider, {
165
175
  value: {
166
176
  isRail
@@ -174,8 +184,7 @@ const SideNav = frFn((props, ref) => {
174
184
  }), /*#__PURE__*/React.createElement("nav", _rollupPluginBabelHelpers.extends({
175
185
  tabIndex: -1,
176
186
  ref: navRef,
177
- className: `${prefix}--side-nav__navigation ${className}`,
178
- inert: !isRail ? !(expanded || isLg) : undefined
187
+ className: `${prefix}--side-nav__navigation ${className}`
179
188
  }, accessibilityLabel, eventHandlers, other), childrenToRender));
180
189
  });
181
190
  SideNav.displayName = 'SideNav';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/react",
3
3
  "description": "React components for the Carbon Design System",
4
- "version": "1.100.0-rc.0",
4
+ "version": "1.100.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -53,10 +53,10 @@
53
53
  "dependencies": {
54
54
  "@babel/runtime": "^7.27.3",
55
55
  "@carbon/feature-flags": ">=0.32.0",
56
- "@carbon/icons-react": "^11.74.0-rc.0",
57
- "@carbon/layout": "^11.47.0-rc.0",
58
- "@carbon/styles": "^1.99.0-rc.0",
59
- "@carbon/utilities": "^0.15.0-rc.0",
56
+ "@carbon/icons-react": "^11.74.0",
57
+ "@carbon/layout": "^11.47.0",
58
+ "@carbon/styles": "^1.99.0",
59
+ "@carbon/utilities": "^0.15.0",
60
60
  "@floating-ui/react": "^0.27.4",
61
61
  "@ibm/telemetry-js": "^1.5.0",
62
62
  "classnames": "2.5.1",
@@ -79,7 +79,7 @@
79
79
  "@babel/preset-react": "^7.27.1",
80
80
  "@babel/preset-typescript": "^7.27.1",
81
81
  "@carbon/test-utils": "^10.39.0",
82
- "@carbon/themes": "^11.67.0-rc.0",
82
+ "@carbon/themes": "^11.67.0",
83
83
  "@figma/code-connect": "^1.3.5",
84
84
  "@rollup/plugin-babel": "^6.0.0",
85
85
  "@rollup/plugin-commonjs": "^28.0.3",
@@ -131,5 +131,5 @@
131
131
  "**/*.scss",
132
132
  "**/*.css"
133
133
  ],
134
- "gitHead": "cbde6bd5df4774c2780411b3faa02ec3a0bb7585"
134
+ "gitHead": "f366448449df3d81c9bb63a8a842772d734c163a"
135
135
  }