@carbon/react 1.100.0 → 1.101.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 (134) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +953 -953
  2. package/es/components/AILabel/index.d.ts +1 -1
  3. package/es/components/AILabel/index.js +1 -12
  4. package/es/components/Checkbox/Checkbox.js +5 -3
  5. package/es/components/CheckboxGroup/CheckboxGroup.js +4 -3
  6. package/es/components/CodeSnippet/CodeSnippet.d.ts +1 -1
  7. package/es/components/ComboBox/ComboBox.js +25 -12
  8. package/es/components/ComboButton/index.d.ts +1 -1
  9. package/es/components/ComboButton/index.js +3 -2
  10. package/es/components/ComposedModal/ComposedModal.js +17 -22
  11. package/es/components/ComposedModal/ModalHeader.d.ts +2 -2
  12. package/es/components/ComposedModal/ModalHeader.js +1 -1
  13. package/es/components/Copy/Copy.d.ts +1 -1
  14. package/es/components/CopyButton/CopyButton.d.ts +1 -1
  15. package/es/components/DataTable/DataTable.d.ts +2 -0
  16. package/es/components/DataTable/DataTable.js +6 -5
  17. package/es/components/DataTable/Table.d.ts +1 -1
  18. package/es/components/DataTable/Table.js +10 -4
  19. package/es/components/DataTable/state/sorting.d.ts +4 -2
  20. package/es/components/Dropdown/Dropdown.js +4 -4
  21. package/es/components/ExpandableSearch/ExpandableSearch.js +1 -1
  22. package/es/components/FileUploader/FileUploaderItem.d.ts +1 -1
  23. package/es/components/FileUploader/FileUploaderItem.js +3 -2
  24. package/es/components/ListBox/test-helpers.d.ts +71 -0
  25. package/es/components/Menu/Menu.js +8 -4
  26. package/es/components/Menu/MenuItem.d.ts +5 -1
  27. package/es/components/Menu/MenuItem.js +11 -1
  28. package/es/components/MenuButton/index.d.ts +1 -1
  29. package/es/components/MenuButton/index.js +3 -2
  30. package/es/components/MultiSelect/FilterableMultiSelect.d.ts +1 -1
  31. package/es/components/MultiSelect/FilterableMultiSelect.js +8 -6
  32. package/es/components/MultiSelect/MultiSelect.js +8 -4
  33. package/es/components/Notification/Notification.js +2 -1
  34. package/es/components/NumberInput/NumberInput.d.ts +1 -1
  35. package/es/components/NumberInput/NumberInput.js +6 -5
  36. package/es/components/OverflowMenu/OverflowMenu.d.ts +1 -0
  37. package/es/components/OverflowMenu/OverflowMenu.js +8 -4
  38. package/es/components/PageHeader/PageHeader.d.ts +1 -1
  39. package/es/components/PageHeader/PageHeader.js +5 -5
  40. package/es/components/Popover/index.js +1 -1
  41. package/es/components/ProgressIndicator/ProgressIndicator.d.ts +1 -1
  42. package/es/components/ProgressIndicator/ProgressIndicator.js +1 -1
  43. package/es/components/RadioButtonGroup/RadioButtonGroup.js +4 -3
  44. package/es/components/Search/Search.d.ts +1 -1
  45. package/es/components/Search/Search.js +1 -1
  46. package/es/components/Select/Select.js +3 -2
  47. package/es/components/StructuredList/StructuredList.d.ts +1 -1
  48. package/es/components/StructuredList/StructuredList.js +2 -4
  49. package/es/components/Tabs/Tabs.d.ts +2 -2
  50. package/es/components/Tabs/Tabs.js +20 -26
  51. package/es/components/Tag/DismissibleTag.js +3 -2
  52. package/es/components/Tag/OperationalTag.js +3 -2
  53. package/es/components/Tag/SelectableTag.js +3 -2
  54. package/es/components/Tag/Tag.js +3 -2
  55. package/es/components/TextArea/TextArea.d.ts +2 -2
  56. package/es/components/TextArea/TextArea.js +7 -6
  57. package/es/components/TextInput/ControlledPasswordInput.js +7 -6
  58. package/es/components/TextInput/PasswordInput.js +5 -6
  59. package/es/components/TextInput/TextInput.js +4 -4
  60. package/es/components/TimePicker/TimePicker.js +2 -2
  61. package/es/components/Tooltip/DefinitionTooltip.d.ts +1 -1
  62. package/es/components/Tooltip/DefinitionTooltip.js +3 -2
  63. package/es/internal/useId.js +3 -4
  64. package/es/internal/usePresence.js +3 -2
  65. package/es/internal/useResizeObserver.d.ts +1 -1
  66. package/es/internal/useResizeObserver.js +5 -7
  67. package/es/tools/events.d.ts +1 -1
  68. package/lib/components/AILabel/index.d.ts +1 -1
  69. package/lib/components/AILabel/index.js +1 -12
  70. package/lib/components/Checkbox/Checkbox.js +5 -3
  71. package/lib/components/CheckboxGroup/CheckboxGroup.js +4 -3
  72. package/lib/components/CodeSnippet/CodeSnippet.d.ts +1 -1
  73. package/lib/components/ComboBox/ComboBox.js +25 -12
  74. package/lib/components/ComboButton/index.d.ts +1 -1
  75. package/lib/components/ComboButton/index.js +2 -1
  76. package/lib/components/ComposedModal/ComposedModal.js +16 -21
  77. package/lib/components/ComposedModal/ModalHeader.d.ts +2 -2
  78. package/lib/components/ComposedModal/ModalHeader.js +1 -1
  79. package/lib/components/Copy/Copy.d.ts +1 -1
  80. package/lib/components/CopyButton/CopyButton.d.ts +1 -1
  81. package/lib/components/DataTable/DataTable.d.ts +2 -0
  82. package/lib/components/DataTable/DataTable.js +6 -5
  83. package/lib/components/DataTable/Table.d.ts +1 -1
  84. package/lib/components/DataTable/Table.js +10 -4
  85. package/lib/components/DataTable/state/sorting.d.ts +4 -2
  86. package/lib/components/Dropdown/Dropdown.js +4 -4
  87. package/lib/components/ExpandableSearch/ExpandableSearch.js +1 -1
  88. package/lib/components/FileUploader/FileUploaderItem.d.ts +1 -1
  89. package/lib/components/FileUploader/FileUploaderItem.js +2 -1
  90. package/lib/components/ListBox/test-helpers.d.ts +71 -0
  91. package/lib/components/Menu/Menu.js +7 -3
  92. package/lib/components/Menu/MenuItem.d.ts +5 -1
  93. package/lib/components/Menu/MenuItem.js +11 -1
  94. package/lib/components/MenuButton/index.d.ts +1 -1
  95. package/lib/components/MenuButton/index.js +2 -1
  96. package/lib/components/MultiSelect/FilterableMultiSelect.d.ts +1 -1
  97. package/lib/components/MultiSelect/FilterableMultiSelect.js +7 -5
  98. package/lib/components/MultiSelect/MultiSelect.js +7 -3
  99. package/lib/components/Notification/Notification.js +2 -1
  100. package/lib/components/NumberInput/NumberInput.d.ts +1 -1
  101. package/lib/components/NumberInput/NumberInput.js +6 -5
  102. package/lib/components/OverflowMenu/OverflowMenu.d.ts +1 -0
  103. package/lib/components/OverflowMenu/OverflowMenu.js +7 -3
  104. package/lib/components/PageHeader/PageHeader.d.ts +1 -1
  105. package/lib/components/PageHeader/PageHeader.js +4 -4
  106. package/lib/components/Popover/index.js +1 -1
  107. package/lib/components/ProgressIndicator/ProgressIndicator.d.ts +1 -1
  108. package/lib/components/ProgressIndicator/ProgressIndicator.js +1 -1
  109. package/lib/components/RadioButtonGroup/RadioButtonGroup.js +4 -3
  110. package/lib/components/Search/Search.d.ts +1 -1
  111. package/lib/components/Search/Search.js +1 -1
  112. package/lib/components/Select/Select.js +3 -2
  113. package/lib/components/StructuredList/StructuredList.d.ts +1 -1
  114. package/lib/components/StructuredList/StructuredList.js +2 -4
  115. package/lib/components/Tabs/Tabs.d.ts +2 -2
  116. package/lib/components/Tabs/Tabs.js +15 -21
  117. package/lib/components/Tag/DismissibleTag.js +2 -1
  118. package/lib/components/Tag/OperationalTag.js +2 -1
  119. package/lib/components/Tag/SelectableTag.js +2 -1
  120. package/lib/components/Tag/Tag.js +2 -1
  121. package/lib/components/TextArea/TextArea.d.ts +2 -2
  122. package/lib/components/TextArea/TextArea.js +7 -6
  123. package/lib/components/TextInput/ControlledPasswordInput.js +7 -6
  124. package/lib/components/TextInput/PasswordInput.js +5 -6
  125. package/lib/components/TextInput/TextInput.js +4 -4
  126. package/lib/components/TimePicker/TimePicker.js +2 -2
  127. package/lib/components/Tooltip/DefinitionTooltip.d.ts +1 -1
  128. package/lib/components/Tooltip/DefinitionTooltip.js +3 -2
  129. package/lib/internal/useId.js +2 -3
  130. package/lib/internal/usePresence.js +2 -1
  131. package/lib/internal/useResizeObserver.d.ts +1 -1
  132. package/lib/internal/useResizeObserver.js +4 -6
  133. package/lib/tools/events.d.ts +1 -1
  134. package/package.json +9 -9
@@ -16,6 +16,7 @@ import { match } from '../../internal/keyboard/match.js';
16
16
  import { useControllableState } from '../../internal/useControllableState.js';
17
17
  import { useMergedRefs } from '../../internal/useMergedRefs.js';
18
18
  import { usePrefix } from '../../internal/usePrefix.js';
19
+ import { useId } from '../../internal/useId.js';
19
20
  import { Menu } from './Menu.js';
20
21
  import { MenuContext } from './MenuContext.js';
21
22
  import '../LayoutDirection/LayoutDirection.js';
@@ -28,6 +29,7 @@ var _Checkmark, _CaretLeft, _CaretRight;
28
29
  const MenuItem = /*#__PURE__*/forwardRef(function MenuItem({
29
30
  children,
30
31
  className,
32
+ dangerDescription = 'danger',
31
33
  disabled,
32
34
  kind = 'default',
33
35
  label,
@@ -164,6 +166,7 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem({
164
166
  }
165
167
  });
166
168
  }, [floatingStyles, refs.floating]);
169
+ const assistiveId = useId('danger-description');
167
170
  return /*#__PURE__*/React.createElement(FloatingFocusManager, {
168
171
  context: floatingContext,
169
172
  order: ['reference', 'floating'],
@@ -188,7 +191,10 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem({
188
191
  }, IconElement && /*#__PURE__*/React.createElement(IconElement, null)), /*#__PURE__*/React.createElement(Text, {
189
192
  as: "div",
190
193
  className: `${prefix}--menu-item__label`
191
- }, label), shortcut && !hasChildren && /*#__PURE__*/React.createElement("div", {
194
+ }, label), isDanger && /*#__PURE__*/React.createElement("span", {
195
+ id: assistiveId,
196
+ className: `${prefix}--visually-hidden`
197
+ }, dangerDescription), shortcut && !hasChildren && /*#__PURE__*/React.createElement("div", {
192
198
  className: `${prefix}--menu-item__shortcut`
193
199
  }, shortcut), hasChildren && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
194
200
  className: `${prefix}--menu-item__shortcut`
@@ -211,6 +217,10 @@ MenuItem.propTypes = {
211
217
  * Additional CSS class names.
212
218
  */
213
219
  className: PropTypes.string,
220
+ /**
221
+ * Specify the message read by screen readers for the danger menu item variant
222
+ */
223
+ dangerDescription: PropTypes.string,
214
224
  /**
215
225
  * Specify whether the MenuItem is disabled or not.
216
226
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2023, 2025
2
+ * Copyright IBM Corp. 2023, 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, { forwardRef, useRef, useLayoutEffect } from 'react';
9
+ import React, { forwardRef, useRef } from 'react';
10
10
  import PropTypes from 'prop-types';
11
11
  import cx from 'classnames';
12
12
  import { ChevronDown } from '@carbon/icons-react';
@@ -17,6 +17,7 @@ import '../Menu/MenuItem.js';
17
17
  import { useAttachedMenu } from '../../internal/useAttachedMenu.js';
18
18
  import { useId } from '../../internal/useId.js';
19
19
  import { usePrefix } from '../../internal/usePrefix.js';
20
+ import useIsomorphicEffect from '../../internal/useIsomorphicEffect.js';
20
21
  import { flip, size, useFloating, autoUpdate } from '@floating-ui/react';
21
22
  import { useFeatureFlag } from '../FeatureFlags/index.js';
22
23
  import { mergeRefs } from '../../tools/mergeRefs.js';
@@ -95,7 +96,7 @@ const MenuButton = /*#__PURE__*/forwardRef(({
95
96
  handleMousedown,
96
97
  handleClose
97
98
  } = useAttachedMenu(triggerRef);
98
- useLayoutEffect(() => {
99
+ useIsomorphicEffect(() => {
99
100
  Object.keys(floatingStyles).forEach(style => {
100
101
  if (refs.floating.current) {
101
102
  let value = floatingStyles[style];
@@ -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.
@@ -11,7 +11,7 @@ import cx from 'classnames';
11
11
  import Downshift, { useCombobox, useMultipleSelection } from 'downshift';
12
12
  import isEqual from 'react-fast-compare';
13
13
  import PropTypes from 'prop-types';
14
- import React, { forwardRef, useContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useEffect, cloneElement } from 'react';
14
+ import React, { forwardRef, useContext, useRef, useState, useMemo, useCallback, useEffect, cloneElement } from 'react';
15
15
  import { defaultFilterItems } from './filter.js';
16
16
  import { sortingPropTypes } from './MultiSelectPropTypes.js';
17
17
  import ListBox from '../ListBox/index.js';
@@ -34,6 +34,7 @@ import { AILabel } from '../AILabel/index.js';
34
34
  import { defaultItemToString } from '../../internal/defaultItemToString.js';
35
35
  import { isComponentElement } from '../../internal/utils.js';
36
36
  import { useNormalizedInputProps } from '../../internal/useNormalizedInputProps.js';
37
+ import useIsomorphicEffect from '../../internal/useIsomorphicEffect.js';
37
38
  import { ListBoxTypePropType, ListBoxSizePropType } from '../ListBox/ListBoxPropTypes.js';
38
39
 
39
40
  const {
@@ -189,7 +190,7 @@ const FilterableMultiSelect = /*#__PURE__*/forwardRef(function FilterableMultiSe
189
190
  }), hide()],
190
191
  whileElementsMounted: autoUpdate
191
192
  } : {});
192
- useLayoutEffect(() => {
193
+ useIsomorphicEffect(() => {
193
194
  if (autoAlign) {
194
195
  const updatedFloatingStyles = {
195
196
  ...floatingStyles,
@@ -259,7 +260,8 @@ const FilterableMultiSelect = /*#__PURE__*/forwardRef(function FilterableMultiSe
259
260
  [`${prefix}--list-box__wrapper--decorator`]: decorator,
260
261
  [`${prefix}--autoalign`]: autoAlign
261
262
  });
262
- const helperId = !helperText ? undefined : `filterablemultiselect-helper-text-${filterableMultiSelectInstanceId}`;
263
+ const hasHelper = typeof helperText !== 'undefined' && helperText !== null;
264
+ const helperId = !hasHelper ? undefined : `filterablemultiselect-helper-text-${filterableMultiSelectInstanceId}`;
263
265
  const labelId = `${id}-label`;
264
266
  const titleClasses = cx({
265
267
  [`${prefix}--label`]: true,
@@ -275,10 +277,10 @@ const FilterableMultiSelect = /*#__PURE__*/forwardRef(function FilterableMultiSe
275
277
  [`${prefix}--text-input--empty`]: !inputValue,
276
278
  [`${prefix}--text-input--light`]: light
277
279
  });
278
- const helper = helperText ? /*#__PURE__*/React.createElement("div", {
280
+ const helper = hasHelper && /*#__PURE__*/React.createElement("div", {
279
281
  id: helperId,
280
282
  className: helperClasses
281
- }, helperText) : null;
283
+ }, helperText);
282
284
  const menuId = `${id}__menu`;
283
285
  const inputId = `${id}-input`;
284
286
  useEffect(() => {
@@ -620,7 +622,7 @@ const FilterableMultiSelect = /*#__PURE__*/forwardRef(function FilterableMultiSe
620
622
  if (evt?.target.classList.contains(`${prefix}--tag__close-icon`) || evt?.target.classList.contains(`${prefix}--list-box__selection`)) {
621
623
  setIsFocused(false);
622
624
  } else {
623
- setIsFocused(evt?.type === 'focus' ? true : false);
625
+ setIsFocused(evt?.type === 'focus');
624
626
  }
625
627
  };
626
628
  const mergedRef = mergeRefs(textInput, inputProp.ref);
@@ -11,7 +11,7 @@ import cx from 'classnames';
11
11
  import { useSelect } from 'downshift';
12
12
  import isEqual from 'react-fast-compare';
13
13
  import PropTypes from 'prop-types';
14
- import React, { useMemo, useContext, useState, useLayoutEffect, useRef, isValidElement, useCallback, cloneElement } from 'react';
14
+ import React, { useMemo, useContext, useState, useRef, isValidElement, useCallback, cloneElement } from 'react';
15
15
  import ListBox from '../ListBox/index.js';
16
16
  import { sortingPropTypes } from './MultiSelectPropTypes.js';
17
17
  import { defaultSortItems, defaultCompareItems } from './tools/sorting.js';
@@ -33,6 +33,7 @@ import { AILabel } from '../AILabel/index.js';
33
33
  import { defaultItemToString } from '../../internal/defaultItemToString.js';
34
34
  import { isComponentElement } from '../../internal/utils.js';
35
35
  import { useNormalizedInputProps } from '../../internal/useNormalizedInputProps.js';
36
+ import useIsomorphicEffect from '../../internal/useIsomorphicEffect.js';
36
37
  import { ListBoxTypePropType, ListBoxSizePropType } from '../ListBox/ListBoxPropTypes.js';
37
38
 
38
39
  const {
@@ -143,7 +144,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
143
144
  }), autoAlign && hide()],
144
145
  whileElementsMounted: autoUpdate
145
146
  } : {});
146
- useLayoutEffect(() => {
147
+ useIsomorphicEffect(() => {
147
148
  if (enableFloatingStyles) {
148
149
  const updatedFloatingStyles = {
149
150
  ...floatingStyles,
@@ -386,8 +387,11 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
386
387
  [`${prefix}--list-box__field--wrapper--input-focused`]: inputFocused
387
388
  });
388
389
  const handleFocus = evt => {
389
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- https://github.com/carbon-design-system/carbon/issues/20452
390
- evt.target.classList.contains(`${prefix}--tag__close-icon`) ? setIsFocused(false) : setIsFocused(evt.type === 'focus' ? true : false);
390
+ if (evt.target.classList.contains(`${prefix}--tag__close-icon`)) {
391
+ setIsFocused(false);
392
+ } else {
393
+ setIsFocused(evt.type === 'focus');
394
+ }
391
395
  };
392
396
  const readOnlyEventHandlers = readOnly ? {
393
397
  onClick: evt => {
@@ -572,7 +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
575
+ ref: innerModal,
576
+ className: `${prefix}--actionable-notification__focus-wrapper`
576
577
  }, /*#__PURE__*/React.createElement("div", {
577
578
  className: `${prefix}--actionable-notification__details`
578
579
  }, /*#__PURE__*/React.createElement(NotificationIcon, {
@@ -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.
@@ -445,7 +445,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
445
445
  if ('type' in evt.target && evt.target.type === 'button') {
446
446
  setIsFocused(false);
447
447
  } else {
448
- setIsFocused(evt.type === 'focus' ? true : false);
448
+ setIsFocused(evt.type === 'focus');
449
449
  }
450
450
  };
451
451
  const outerElementClasses = cx(`${prefix}--form-item`, {
@@ -564,10 +564,11 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
564
564
  onKeyUp: onKeyUp,
565
565
  onKeyDown: e => {
566
566
  if (type === 'text') {
567
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- https://github.com/carbon-design-system/carbon/issues/20452
568
- match(e, ArrowUp) && handleStep(e, 'up');
569
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- https://github.com/carbon-design-system/carbon/issues/20452
570
- match(e, ArrowDown) && handleStep(e, 'down');
567
+ if (match(e, ArrowUp)) {
568
+ handleStep(e, 'up');
569
+ } else if (match(e, ArrowDown)) {
570
+ handleStep(e, 'down');
571
+ }
571
572
  }
572
573
  if (rest?.onKeyDown) {
573
574
  rest?.onKeyDown(e);
@@ -44,6 +44,7 @@ export interface OverflowMenuProps extends Omit<IconButtonProps, 'type' | 'aria-
44
44
  */
45
45
  flipped?: boolean;
46
46
  /**
47
+ * @deprecated Tab key is handled with event handler so no need for focus trap.
47
48
  * Enable or disable focus trap behavior
48
49
  */
49
50
  focusTrap?: boolean;
@@ -12,7 +12,7 @@ import cx from 'classnames';
12
12
  import invariant from 'invariant';
13
13
  import PropTypes from 'prop-types';
14
14
  import { DIRECTION_TOP, DIRECTION_BOTTOM, FloatingMenu } from '../../internal/FloatingMenu.js';
15
- import { ArrowUp, ArrowRight, ArrowDown, ArrowLeft, Escape } from '../../internal/keyboard/keys.js';
15
+ import { ArrowUp, ArrowRight, ArrowDown, ArrowLeft, Escape, Tab } from '../../internal/keyboard/keys.js';
16
16
  import { matches } from '../../internal/keyboard/match.js';
17
17
  import { noopFn } from '../../internal/noopFn.js';
18
18
  import { PrefixContext } from '../../internal/usePrefix.js';
@@ -97,7 +97,7 @@ const OverflowMenu = /*#__PURE__*/forwardRef(({
97
97
  className,
98
98
  direction = DIRECTION_BOTTOM,
99
99
  flipped = false,
100
- focusTrap = true,
100
+ focusTrap = false,
101
101
  iconClass,
102
102
  iconDescription = 'Options',
103
103
  id,
@@ -200,12 +200,15 @@ const OverflowMenu = /*#__PURE__*/forwardRef(({
200
200
  evt.preventDefault();
201
201
  }
202
202
 
203
- // Close the overflow menu on escape
204
- if (matches(evt, [Escape])) {
203
+ // Close the overflow menu on escape or tab.
204
+ if (matches(evt, [Escape, Tab])) {
205
205
  closeMenuOnEscape();
206
206
 
207
207
  // Stop the esc keypress from bubbling out and closing something it shouldn't
208
208
  evt.stopPropagation();
209
+
210
+ // Stop the tab key from making the browser focus somewhere else.
211
+ evt.preventDefault();
209
212
  }
210
213
  };
211
214
 
@@ -390,6 +393,7 @@ OverflowMenu.propTypes = {
390
393
  */
391
394
  flipped: PropTypes.bool,
392
395
  /**
396
+ * @deprecated Tab key is handled with event handler so no need for focus trap.
393
397
  * Enable or disable focus trap behavior
394
398
  */
395
399
  focusTrap: PropTypes.bool,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2025
2
+ * Copyright IBM Corp. 2025, 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, { useRef, useState, useLayoutEffect, useMemo, useEffect, useCallback } from 'react';
9
+ import React, { useRef, useState, useMemo, useEffect, useCallback } from 'react';
10
10
  import PropTypes from 'prop-types';
11
11
  import cx from 'classnames';
12
12
  import { usePrefix } from '../../internal/usePrefix.js';
@@ -35,6 +35,7 @@ import '../Grid/Row.js';
35
35
  import Column from '../Grid/Column.js';
36
36
  import '../Grid/ColumnHang.js';
37
37
  import '../Grid/GridContext.js';
38
+ import useIsomorphicEffect from '../../internal/useIsomorphicEffect.js';
38
39
 
39
40
  /**
40
41
  * ----------
@@ -130,9 +131,8 @@ const PageHeaderContent = /*#__PURE__*/React.forwardRef(({
130
131
  setIsEllipsisApplied(element.offsetHeight < element.scrollHeight);
131
132
  return element.offsetHeight < element.scrollHeight;
132
133
  };
133
- useLayoutEffect(() => {
134
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- https://github.com/carbon-design-system/carbon/issues/20452
135
- titleRef.current && isEllipsisActive(titleRef.current);
134
+ useIsomorphicEffect(() => {
135
+ if (titleRef.current) isEllipsisActive(titleRef.current);
136
136
  }, [title]);
137
137
  return /*#__PURE__*/React.createElement("div", _extends({
138
138
  className: classNames,
@@ -220,7 +220,7 @@ const PageHeaderContentPageActions = ({
220
220
 
221
221
  // need to set the grid columns width based on the menu button's width
222
222
  // to avoid overlapping when resizing
223
- useLayoutEffect(() => {
223
+ useIsomorphicEffect(() => {
224
224
  if (menuButtonVisibility && offsetRef.current) {
225
225
  const width = offsetRef.current.offsetWidth;
226
226
  document.documentElement.style.setProperty('--pageheader-title-grid-width', `${width}px`);
@@ -40,7 +40,7 @@ const Popover = /*#__PURE__*/React.forwardRef(function PopoverRenderFunction({
40
40
  autoAlign = false,
41
41
  autoAlignBoundary,
42
42
  backgroundToken = 'layer',
43
- caret = isTabTip ? false : true,
43
+ caret = !isTabTip,
44
44
  className: customClassName,
45
45
  children,
46
46
  border = false,
@@ -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.
@@ -65,7 +65,7 @@ function ProgressIndicator({
65
65
  if (index === currentIndex) {
66
66
  return /*#__PURE__*/React.cloneElement(child, {
67
67
  complete: child.props.complete,
68
- current: child.props.complete ? false : true,
68
+ current: !child.props.complete,
69
69
  index,
70
70
  onClick
71
71
  });
@@ -97,11 +97,12 @@ const RadioButtonGroup = /*#__PURE__*/React.forwardRef((props, ref) => {
97
97
  const helperClasses = cx(`${prefix}--form__helper-text`, {
98
98
  [`${prefix}--form__helper-text--disabled`]: disabled
99
99
  });
100
- const helperId = !helperText ? undefined : `radio-button-group-helper-text-${radioButtonGroupInstanceId}`;
101
- const helper = helperText ? /*#__PURE__*/React.createElement("div", {
100
+ const hasHelper = typeof helperText !== 'undefined' && helperText !== null;
101
+ const helperId = !hasHelper ? undefined : `radio-button-group-helper-text-${radioButtonGroupInstanceId}`;
102
+ const helper = hasHelper && /*#__PURE__*/React.createElement("div", {
102
103
  id: helperId,
103
104
  className: helperClasses
104
- }, helperText) : null;
105
+ }, helperText);
105
106
  const divRef = useRef(null);
106
107
 
107
108
  // AILabel is always size `mini`
@@ -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.
@@ -47,7 +47,7 @@ const Search = /*#__PURE__*/React.forwardRef(({
47
47
  value,
48
48
  ...rest
49
49
  }, forwardRef) => {
50
- const hasPropValue = value || defaultValue ? true : false;
50
+ const hasPropValue = Boolean(value || defaultValue);
51
51
  const prefix = usePrefix();
52
52
  const {
53
53
  isFluid
@@ -94,7 +94,8 @@ const Select = /*#__PURE__*/forwardRef(({
94
94
  const helperTextClasses = cx(`${prefix}--form__helper-text`, {
95
95
  [`${prefix}--form__helper-text--disabled`]: normalizedProps.disabled
96
96
  });
97
- const helper = helperText ? /*#__PURE__*/React.createElement(Text, {
97
+ const hasHelper = typeof helperText !== 'undefined' && helperText !== null;
98
+ const helper = hasHelper ? /*#__PURE__*/React.createElement(Text, {
98
99
  as: "div",
99
100
  id: normalizedProps.helperId,
100
101
  className: helperTextClasses
@@ -106,7 +107,7 @@ const Select = /*#__PURE__*/forwardRef(({
106
107
  ariaProps['aria-describedby'] = helper ? normalizedProps.helperId : undefined;
107
108
  }
108
109
  const handleFocus = evt => {
109
- setIsFocused(evt.type === 'focus' ? true : false);
110
+ setIsFocused(evt.type === 'focus');
110
111
  };
111
112
  const handleChange = evt => {
112
113
  const selectedOption = evt?.target?.options[evt.target.selectedIndex];
@@ -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.
@@ -184,8 +184,7 @@ function StructuredListRow(props) {
184
184
  ref: itemRef,
185
185
  onClick: event => {
186
186
  setSelectedRow?.(rowId);
187
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- https://github.com/carbon-design-system/carbon/issues/20452
188
- onClick && onClick(event);
187
+ onClick?.(event);
189
188
  if (selection) {
190
189
  // focus items only when selection is enabled
191
190
  setHasFocusWithin(true);
@@ -264,8 +263,7 @@ function StructuredListInput(props) {
264
263
  value: row?.id ?? '',
265
264
  onChange: event => {
266
265
  setSelectedRow?.(event.target.value);
267
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- https://github.com/carbon-design-system/carbon/issues/20452
268
- onChange && onChange(event);
266
+ onChange?.(event);
269
267
  },
270
268
  id: id ?? defaultId,
271
269
  className: classes,
@@ -1,11 +1,11 @@
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.
6
6
  */
7
7
  import PropTypes from 'prop-types';
8
- import React, { type ReactNode, type MouseEvent, type KeyboardEvent, type HTMLAttributes, type ComponentType, type HTMLElementType } from 'react';
8
+ import React, { type ComponentType, type HTMLAttributes, type HTMLElementType, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react';
9
9
  type DivAttributes = HTMLAttributes<HTMLDivElement>;
10
10
  /**
11
11
  * Tabs
@@ -10,14 +10,13 @@ import { Close, ChevronLeft, ChevronRight } from '@carbon/icons-react';
10
10
  import { breakpoints } from '@carbon/layout';
11
11
  import cx from 'classnames';
12
12
  import PropTypes from 'prop-types';
13
- import React, { forwardRef, createContext, useRef, useState, useMemo, isValidElement, useCallback, useEffect } from 'react';
13
+ import React, { forwardRef, createContext, useRef, useState, useMemo, isValidElement, Children, cloneElement, useCallback, useEffect } from 'react';
14
14
  import '../Grid/FlexGrid.js';
15
15
  import { Grid as GridAsGridComponent } from '../Grid/Grid.js';
16
16
  import '../Grid/Row.js';
17
17
  import '../Grid/Column.js';
18
18
  import '../Grid/ColumnHang.js';
19
19
  import '../Grid/GridContext.js';
20
- import { isElement } from 'react-is';
21
20
  import '../Tooltip/DefinitionTooltip.js';
22
21
  import { Tooltip } from '../Tooltip/Tooltip.js';
23
22
  import { useControllableState } from '../../internal/useControllableState.js';
@@ -34,6 +33,7 @@ import { useMatchMedia } from '../../internal/useMatchMedia.js';
34
33
  import { Text } from '../Text/Text.js';
35
34
  import '../Text/TextDirection.js';
36
35
  import { BadgeIndicator } from '../BadgeIndicator/index.js';
36
+ import { isComponentElement } from '../../internal/utils.js';
37
37
  import { debounce } from '../../node_modules/es-toolkit/dist/compat/function/debounce.js';
38
38
 
39
39
  var _ChevronLeft, _ChevronRight, _BadgeIndicator;
@@ -269,14 +269,7 @@ function TabList({
269
269
  const nextButton = useRef(null);
270
270
  const [isScrollable, setIsScrollable] = useState(false);
271
271
  const [scrollLeft, setScrollLeft] = useState(0);
272
- let hasSecondaryLabelTabs = false;
273
- if (contained) {
274
- hasSecondaryLabelTabs = React.Children.toArray(children).some(child => {
275
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
276
- const _child = child;
277
- return /*#__PURE__*/React.isValidElement(child) && !!_child.props.secondaryLabel;
278
- });
279
- }
272
+ const hasSecondaryLabelTabs = contained && Children.toArray(children).some(child => isComponentElement(child, Tab) && typeof child.props.secondaryLabel !== 'undefined');
280
273
  const isLg = useMatchMedia(lgMediaQuery);
281
274
  const distributeWidth = fullWidth && contained && isLg && React.Children.toArray(children).length < 9;
282
275
  const className = cx(`${prefix}--tabs`, {
@@ -487,17 +480,16 @@ function TabList({
487
480
  onScroll: debouncedOnScroll,
488
481
  onKeyDown: onKeyDown,
489
482
  onBlur: handleBlur
490
- }), React.Children.map(children, (child, index) => {
491
- return !isElement(child) ? null : /*#__PURE__*/React.createElement(TabContext.Provider, {
483
+ }), Children.map(children, (child, index) => {
484
+ return ! /*#__PURE__*/isValidElement(child) ? null : /*#__PURE__*/React.createElement(TabContext.Provider, {
492
485
  value: {
493
486
  index,
494
487
  hasSecondaryLabel: hasSecondaryLabelTabs,
495
488
  contained
496
489
  }
497
- }, /*#__PURE__*/React.cloneElement(
498
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
499
- child, {
490
+ }, /*#__PURE__*/cloneElement(child, {
500
491
  ref: node => {
492
+ if (!node) return;
501
493
  tabs.current[index] = node;
502
494
  }
503
495
  }));
@@ -641,8 +633,7 @@ function TabListVertical({
641
633
  if (containerTop && containerHeight) {
642
634
  // scrolls so selected tab is in view
643
635
  if (selectedPositionTop - halfTabHeight < containerTop || selectedPositionTop - containerTop + verticalTabHeight + halfTabHeight > containerHeight) {
644
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- https://github.com/carbon-design-system/carbon/issues/20452
645
- ref.current && ref.current.scrollTo({
636
+ ref.current?.scrollTo({
646
637
  top: (selectedIndex - 1) * verticalTabHeight,
647
638
  behavior: 'smooth'
648
639
  });
@@ -690,16 +681,15 @@ function TabListVertical({
690
681
  className: `${prefix}--tab--list`,
691
682
  onKeyDown: onKeyDown,
692
683
  onBlur: handleBlur
693
- }), React.Children.map(children, (child, index) => {
694
- return !isElement(child) ? null : /*#__PURE__*/React.createElement(TabContext.Provider, {
684
+ }), Children.map(children, (child, index) => {
685
+ return ! /*#__PURE__*/isValidElement(child) ? null : /*#__PURE__*/React.createElement(TabContext.Provider, {
695
686
  value: {
696
687
  index,
697
688
  hasSecondaryLabel: false
698
689
  }
699
- }, /*#__PURE__*/React.cloneElement(
700
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
701
- child, {
690
+ }, /*#__PURE__*/cloneElement(child, {
702
691
  ref: node => {
692
+ if (!node) return;
703
693
  tabs.current[index] = node;
704
694
  }
705
695
  }));
@@ -1045,7 +1035,11 @@ const IconTab = /*#__PURE__*/React.forwardRef(({
1045
1035
  const value = useMemo(() => ({
1046
1036
  badgeIndicator
1047
1037
  }), [badgeIndicator]);
1048
- const hasSize20 = /*#__PURE__*/isValidElement(children) && children.props.size === 20;
1038
+ const hasSize20 = /*#__PURE__*/isValidElement(children) &&
1039
+ // TODO: The interface allows `size` to be a string. Should this case be
1040
+ // handled here, or should the prop type be restricted to `number`
1041
+ // instead?
1042
+ children.props.size === 20;
1049
1043
  const classNames = cx(`${prefix}--tabs__nav-item--icon-only`, customClassName, {
1050
1044
  [`${prefix}--tabs__nav-item--icon-only__20`]: hasSize20
1051
1045
  });
@@ -1176,10 +1170,10 @@ function TabPanels({
1176
1170
  });
1177
1171
  }
1178
1172
  });
1179
- return /*#__PURE__*/React.createElement(React.Fragment, null, React.Children.map(children, (child, index) => {
1180
- return !isElement(child) ? null : /*#__PURE__*/React.createElement(TabPanelContext.Provider, {
1173
+ return /*#__PURE__*/React.createElement(React.Fragment, null, Children.map(children, (child, index) => {
1174
+ return ! /*#__PURE__*/isValidElement(child) ? null : /*#__PURE__*/React.createElement(TabPanelContext.Provider, {
1181
1175
  value: index
1182
- }, /*#__PURE__*/React.cloneElement(child, {
1176
+ }, /*#__PURE__*/cloneElement(child, {
1183
1177
  ref: element => {
1184
1178
  refs.current[index] = element;
1185
1179
  }
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
9
  import PropTypes from 'prop-types';
10
- import React, { forwardRef, useRef, useState, useLayoutEffect, cloneElement } from 'react';
10
+ import React, { forwardRef, useRef, useState, cloneElement } from 'react';
11
11
  import cx from 'classnames';
12
12
  import { useId } from '../../internal/useId.js';
13
13
  import { usePrefix } from '../../internal/usePrefix.js';
@@ -22,6 +22,7 @@ import { isEllipsisActive } from './isEllipsisActive.js';
22
22
  import { mergeRefs } from '../../tools/mergeRefs.js';
23
23
  import { AILabel } from '../AILabel/index.js';
24
24
  import { isComponentElement } from '../../internal/utils.js';
25
+ import useIsomorphicEffect from '../../internal/useIsomorphicEffect.js';
25
26
 
26
27
  var _Close;
27
28
  // eslint-disable-next-line react/display-name -- https://github.com/carbon-design-system/carbon/issues/20452
@@ -48,7 +49,7 @@ const DismissibleTag = /*#__PURE__*/forwardRef(({
48
49
  const tagId = id || `tag-${useId()}`;
49
50
  const tagClasses = cx(`${prefix}--tag--filter`, className);
50
51
  const [isEllipsisApplied, setIsEllipsisApplied] = useState(false);
51
- useLayoutEffect(() => {
52
+ useIsomorphicEffect(() => {
52
53
  const newElement = tagLabelRef.current?.getElementsByClassName(`${prefix}--tag__label`)[0];
53
54
  setIsEllipsisApplied(isEllipsisActive(newElement));
54
55
  }, [prefix, tagLabelRef]);
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
9
  import PropTypes from 'prop-types';
10
- import React, { forwardRef, useRef, useState, useLayoutEffect } from 'react';
10
+ import React, { forwardRef, useRef, useState } from 'react';
11
11
  import cx from 'classnames';
12
12
  import { useId } from '../../internal/useId.js';
13
13
  import { usePrefix } from '../../internal/usePrefix.js';
@@ -18,6 +18,7 @@ import { Text } from '../Text/Text.js';
18
18
  import '../Text/TextDirection.js';
19
19
  import { isEllipsisActive } from './isEllipsisActive.js';
20
20
  import { mergeRefs } from '../../tools/mergeRefs.js';
21
+ import useIsomorphicEffect from '../../internal/useIsomorphicEffect.js';
21
22
 
22
23
  const TYPES = {
23
24
  red: 'Red',
@@ -48,7 +49,7 @@ const OperationalTag = /*#__PURE__*/forwardRef(({
48
49
  const tagId = id || `tag-${useId()}`;
49
50
  const tagClasses = cx(`${prefix}--tag--operational`, className);
50
51
  const [isEllipsisApplied, setIsEllipsisApplied] = useState(false);
51
- useLayoutEffect(() => {
52
+ useIsomorphicEffect(() => {
52
53
  const newElement = tagRef.current?.getElementsByClassName(`${prefix}--tag__label`)[0];
53
54
  setIsEllipsisApplied(isEllipsisActive(newElement));
54
55
  }, [prefix, tagRef]);