@carbon/react 1.108.0 → 1.109.0-rc.1

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 +902 -902
  2. package/es/components/ComboBox/ComboBox.js +28 -1
  3. package/es/components/ComposedModal/ComposedModal.d.ts +8 -0
  4. package/es/components/ComposedModal/ComposedModal.js +33 -18
  5. package/es/components/ComposedModal/ComposedModalContext.d.ts +12 -0
  6. package/es/components/ComposedModal/ComposedModalContext.js +18 -0
  7. package/es/components/ComposedModal/ModalHeader.js +29 -1
  8. package/es/components/DataTable/DataTable.d.ts +9 -3
  9. package/es/components/DataTable/DataTable.js +10 -8
  10. package/es/components/DataTable/TableToolbar.d.ts +8 -2
  11. package/es/components/DataTable/TableToolbar.js +21 -11
  12. package/es/components/DataTable/TableToolbarMenu.d.ts +5 -1
  13. package/es/components/DataTable/TableToolbarMenu.js +12 -2
  14. package/es/components/DataTable/TableToolbarSearch.d.ts +2 -6
  15. package/es/components/DataTable/TableToolbarSearch.js +5 -1
  16. package/es/components/Modal/Modal.js +14 -7
  17. package/es/components/Search/Search.js +1 -1
  18. package/es/index.d.ts +1 -0
  19. package/es/index.js +2 -1
  20. package/es/internal/FloatingMenu.js +5 -3
  21. package/lib/components/ComboBox/ComboBox.js +28 -1
  22. package/lib/components/ComposedModal/ComposedModal.d.ts +8 -0
  23. package/lib/components/ComposedModal/ComposedModal.js +32 -17
  24. package/lib/components/ComposedModal/ComposedModalContext.d.ts +12 -0
  25. package/lib/components/ComposedModal/ComposedModalContext.js +18 -0
  26. package/lib/components/ComposedModal/ModalHeader.js +28 -0
  27. package/lib/components/DataTable/DataTable.d.ts +9 -3
  28. package/lib/components/DataTable/DataTable.js +10 -8
  29. package/lib/components/DataTable/TableToolbar.d.ts +8 -2
  30. package/lib/components/DataTable/TableToolbar.js +20 -9
  31. package/lib/components/DataTable/TableToolbarMenu.d.ts +5 -1
  32. package/lib/components/DataTable/TableToolbarMenu.js +12 -2
  33. package/lib/components/DataTable/TableToolbarSearch.d.ts +2 -6
  34. package/lib/components/DataTable/TableToolbarSearch.js +5 -1
  35. package/lib/components/Modal/Modal.js +13 -6
  36. package/lib/components/Search/Search.js +1 -1
  37. package/lib/index.d.ts +1 -0
  38. package/lib/index.js +2 -0
  39. package/lib/internal/FloatingMenu.js +5 -3
  40. package/package.json +6 -6
@@ -126,12 +126,21 @@ const ComboBox = forwardRef((props, ref) => {
126
126
  const [isFocused, setIsFocused] = useState(false);
127
127
  const prevInputValue = useRef(inputValue);
128
128
  const prevSelectedItemProp = useRef(selectedItemProp);
129
+ const isSyncingControlledSelectionRef = useRef(false);
130
+ const pendingControlledSelectionRef = useRef({
131
+ pending: false,
132
+ value: void 0
133
+ });
129
134
  useEffect(() => {
130
135
  isManualClearingRef.current = isClearing;
131
136
  if (isClearing) setIsClearing(false);
132
137
  }, [isClearing]);
133
138
  useEffect(() => {
134
139
  if (prevSelectedItemProp.current !== selectedItemProp) {
140
+ pendingControlledSelectionRef.current = {
141
+ pending: true,
142
+ value: selectedItemProp
143
+ };
135
144
  const currentInputValue = getInputValue({
136
145
  initialSelectedItem,
137
146
  itemToString,
@@ -346,11 +355,14 @@ const ComboBox = forwardRef((props, ref) => {
346
355
  isItemDisabled: isDisabledItem,
347
356
  ...downshiftProps,
348
357
  onStateChange: ({ type, selectedItem: newSelectedItem }) => {
358
+ if (isManualClearingRef.current || isSyncingControlledSelectionRef.current) {
359
+ isSyncingControlledSelectionRef.current = false;
360
+ return;
361
+ }
349
362
  downshiftProps?.onStateChange?.({
350
363
  type,
351
364
  selectedItem: newSelectedItem
352
365
  });
353
- if (isManualClearingRef.current) return;
354
366
  if ((type === ItemClick || type === FunctionSelectItem || type === InputKeyDownEnter || !allowCustomValue && type === InputBlur) && typeof newSelectedItem !== "undefined" && !isEqual(currentSelectedItem, newSelectedItem)) {
355
367
  if (items.some((item) => isEqual(item, newSelectedItem))) committedCustomValueRef.current = "";
356
368
  onChange({ selectedItem: newSelectedItem });
@@ -358,6 +370,21 @@ const ComboBox = forwardRef((props, ref) => {
358
370
  }
359
371
  });
360
372
  const currentSelectedItem = typeof selectedItemProp !== "undefined" ? selectedItemProp : selectedItem;
373
+ useEffect(() => {
374
+ if (pendingControlledSelectionRef.current.pending) {
375
+ const { value } = pendingControlledSelectionRef.current;
376
+ const nextSelectedItem = typeof value === "undefined" ? null : value;
377
+ pendingControlledSelectionRef.current.pending = false;
378
+ if (!isEqual(selectedItem, nextSelectedItem)) {
379
+ isSyncingControlledSelectionRef.current = true;
380
+ selectItem(nextSelectedItem);
381
+ }
382
+ }
383
+ }, [
384
+ selectedItem,
385
+ selectedItemProp,
386
+ selectItem
387
+ ]);
361
388
  useEffect(() => {
362
389
  if (downshiftActions) downshiftActions.current = {
363
390
  closeMenu,
@@ -6,6 +6,14 @@
6
6
  */
7
7
  import React, { type HTMLAttributes, type KeyboardEvent, type MouseEvent, type ReactNode, type RefObject } from 'react';
8
8
  export interface ModalBodyProps extends HTMLAttributes<HTMLDivElement> {
9
+ /**
10
+ * Specify the aria-label for the modal body when it is scrollable
11
+ */
12
+ 'aria-label'?: string;
13
+ /**
14
+ * Specify the aria-labelledby for the modal body when it is scrollable
15
+ */
16
+ 'aria-labelledby'?: string;
9
17
  /** Specify the content to be placed in the ModalBody. */
10
18
  children?: ReactNode;
11
19
  /**
@@ -18,17 +18,17 @@ import { useResizeObserver } from "../../internal/useResizeObserver.js";
18
18
  import { composeEventHandlers } from "../../tools/events.js";
19
19
  import { mergeRefs } from "../../tools/mergeRefs.js";
20
20
  import { Layer } from "../Layer/index.js";
21
+ import { ComposedModalContext } from "./ComposedModalContext.js";
21
22
  import { ModalHeader } from "./ModalHeader.js";
22
23
  import { ModalFooter } from "./ModalFooter.js";
23
24
  import { toggleClass } from "../../tools/toggleClass.js";
24
- import { requiredIfGivenPropIsTruthy } from "../../prop-types/requiredIfGivenPropIsTruthy.js";
25
25
  import { elementOrParentIsFloatingMenu, wrapFocus, wrapFocusWithoutSentinels } from "../../internal/wrapFocus.js";
26
26
  import { Dialog } from "../Dialog/Dialog.js";
27
27
  import { useComposedModalState } from "./useComposedModalState.js";
28
28
  import { ComposedModalPresence, ComposedModalPresenceContext, useExclusiveComposedModalPresenceContext } from "./ComposedModalPresence.js";
29
29
  import { isTopmostVisibleModal } from "../Modal/isTopmostVisibleModal.js";
30
30
  import classNames from "classnames";
31
- import React, { Children, cloneElement, useContext, useEffect, useRef } from "react";
31
+ import React, { Children, cloneElement, useContext, useEffect, useRef, useState } from "react";
32
32
  import PropTypes from "prop-types";
33
33
  import { jsx, jsxs } from "react/jsx-runtime";
34
34
  import { useMergeRefs } from "@floating-ui/react";
@@ -39,9 +39,10 @@ import { useMergeRefs } from "@floating-ui/react";
39
39
  * This source code is licensed under the Apache-2.0 license found in the
40
40
  * LICENSE file in the root directory of this source tree.
41
41
  */
42
- const ModalBody = React.forwardRef(function ModalBody({ className: customClassName, children, hasForm, hasScrollingContent, ...rest }, ref) {
42
+ const ModalBody = React.forwardRef(function ModalBody({ ["aria-label"]: ariaLabelProp, ["aria-labelledby"]: ariaLabelledByProp, className: customClassName, children, hasForm, hasScrollingContent, ...rest }, ref) {
43
43
  const prefix = usePrefix();
44
44
  const contentRef = useRef(null);
45
+ const { labelId, titleId } = useContext(ComposedModalContext);
45
46
  const { height } = useResizeObserver({ ref: contentRef });
46
47
  /**
47
48
  * isScrollable is implicitly dependent on height, when height gets updated
@@ -57,7 +58,9 @@ const ModalBody = React.forwardRef(function ModalBody({ className: customClassNa
57
58
  }, customClassName),
58
59
  ...hasScrollingContent || isScrollable ? {
59
60
  tabIndex: 0,
60
- role: "region"
61
+ role: "region",
62
+ "aria-label": ariaLabelProp,
63
+ "aria-labelledby": ariaLabelledByProp || labelId || titleId
61
64
  } : {},
62
65
  ...rest,
63
66
  ref: mergeRefs(contentRef, ref),
@@ -65,7 +68,8 @@ const ModalBody = React.forwardRef(function ModalBody({ className: customClassNa
65
68
  });
66
69
  });
67
70
  ModalBody.propTypes = {
68
- ["aria-label"]: requiredIfGivenPropIsTruthy("hasScrollingContent", PropTypes.string),
71
+ ["aria-label"]: PropTypes.string,
72
+ ["aria-labelledby"]: PropTypes.string,
69
73
  children: PropTypes.node,
70
74
  className: PropTypes.string,
71
75
  hasForm: PropTypes.bool,
@@ -95,6 +99,8 @@ const ComposedModal = React.forwardRef(function ComposedModal({ open, ...props }
95
99
  });
96
100
  const ComposedModalDialog = React.forwardRef(function ComposedModalDialog({ ["aria-labelledby"]: ariaLabelledBy, ["aria-label"]: ariaLabel, children, className: customClassName, containerClassName, danger, decorator, isFullWidth, onClose, onKeyDown, open: externalOpen, preventCloseOnClickOutside, selectorPrimaryFocus = "[data-modal-primary-focus]", selectorsFloatingMenus, size, launcherButtonRef, slug, ...rest }, ref) {
97
101
  const prefix = usePrefix();
102
+ const [labelId, setLabelId] = useState(void 0);
103
+ const [titleId, setTitleId] = useState(void 0);
98
104
  const innerModal = useRef(null);
99
105
  const button = useRef(null);
100
106
  const startSentinel = useRef(null);
@@ -286,19 +292,28 @@ const ComposedModalDialog = React.forwardRef(function ComposedModalDialog({ ["ar
286
292
  })
287
293
  ]
288
294
  });
289
- return /* @__PURE__ */ jsx(Layer, {
290
- ...rest,
291
- level: 0,
292
- role: "presentation",
293
- ref: mergedRefs,
294
- "aria-hidden": !open,
295
- onBlur: handleBlur,
296
- onClick: composeEventHandlers([rest?.onClick, handleOnClick]),
297
- onMouseDown: composeEventHandlers([rest?.onMouseDown, handleOnMouseDown]),
298
- onKeyDown: handleKeyDown,
299
- className: modalClass,
300
- "data-exiting": presenceContext?.isExiting || void 0,
301
- children: modalBody
295
+ const contextValue = {
296
+ labelId,
297
+ titleId,
298
+ setLabelId,
299
+ setTitleId
300
+ };
301
+ return /* @__PURE__ */ jsx(ComposedModalContext.Provider, {
302
+ value: contextValue,
303
+ children: /* @__PURE__ */ jsx(Layer, {
304
+ ...rest,
305
+ level: 0,
306
+ role: "presentation",
307
+ ref: mergedRefs,
308
+ "aria-hidden": !open,
309
+ onBlur: handleBlur,
310
+ onClick: composeEventHandlers([rest?.onClick, handleOnClick]),
311
+ onMouseDown: composeEventHandlers([rest?.onMouseDown, handleOnMouseDown]),
312
+ onKeyDown: handleKeyDown,
313
+ className: modalClass,
314
+ "data-exiting": presenceContext?.isExiting || void 0,
315
+ children: modalBody
316
+ })
302
317
  });
303
318
  });
304
319
  ComposedModal.propTypes = {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright IBM Corp. 2026
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ export declare const ComposedModalContext: import("react").Context<{
8
+ labelId?: string;
9
+ titleId?: string;
10
+ setLabelId?: (id: string | undefined) => void;
11
+ setTitleId?: (id: string | undefined) => void;
12
+ }>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright IBM Corp. 2016, 2026
3
+ *
4
+ * This source code is licensed under the Apache-2.0 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import { createContext } from "react";
9
+ //#region src/components/ComposedModal/ComposedModalContext.ts
10
+ /**
11
+ * Copyright IBM Corp. 2026
12
+ *
13
+ * This source code is licensed under the Apache-2.0 license found in the
14
+ * LICENSE file in the root directory of this source tree.
15
+ */
16
+ const ComposedModalContext = createContext({});
17
+ //#endregion
18
+ export { ComposedModalContext };
@@ -6,9 +6,11 @@
6
6
  */
7
7
 
8
8
  import { usePrefix } from "../../internal/usePrefix.js";
9
+ import { useId } from "../../internal/useId.js";
9
10
  import { IconButton } from "../IconButton/index.js";
11
+ import { ComposedModalContext } from "./ComposedModalContext.js";
10
12
  import classNames from "classnames";
11
- import React from "react";
13
+ import React, { useContext, useEffect } from "react";
12
14
  import PropTypes from "prop-types";
13
15
  import { jsx, jsxs } from "react/jsx-runtime";
14
16
  import { Close } from "@carbon/icons-react";
@@ -21,6 +23,30 @@ import { Close } from "@carbon/icons-react";
21
23
  */
22
24
  const ModalHeader = React.forwardRef(function ModalHeader({ buttonOnClick, children, className: customClassName, closeClassName, closeIconClassName, closeModal, iconDescription = "Close", label, labelClassName, title, titleClassName, ...rest }, ref) {
23
25
  const prefix = usePrefix();
26
+ const modalId = useId();
27
+ const { setLabelId, setTitleId } = useContext(ComposedModalContext);
28
+ const generatedLabelId = `${prefix}--modal-header__label--${modalId}`;
29
+ const generatedTitleId = `${prefix}--modal-header__heading--${modalId}`;
30
+ useEffect(() => {
31
+ if (label && setLabelId) {
32
+ setLabelId(generatedLabelId);
33
+ return () => setLabelId(void 0);
34
+ }
35
+ }, [
36
+ label,
37
+ generatedLabelId,
38
+ setLabelId
39
+ ]);
40
+ useEffect(() => {
41
+ if (title && setTitleId) {
42
+ setTitleId(generatedTitleId);
43
+ return () => setTitleId(void 0);
44
+ }
45
+ }, [
46
+ title,
47
+ generatedTitleId,
48
+ setTitleId
49
+ ]);
24
50
  function handleCloseButtonClick(evt) {
25
51
  closeModal?.(evt);
26
52
  buttonOnClick?.(evt);
@@ -36,10 +62,12 @@ const ModalHeader = React.forwardRef(function ModalHeader({ buttonOnClick, child
36
62
  ref,
37
63
  children: [
38
64
  label && /* @__PURE__ */ jsx("h2", {
65
+ id: generatedLabelId,
39
66
  className: labelClass,
40
67
  children: label
41
68
  }),
42
69
  title && /* @__PURE__ */ jsx("h2", {
70
+ id: generatedTitleId,
43
71
  className: titleClass,
44
72
  children: title
45
73
  }),
@@ -69,6 +69,7 @@ export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
69
69
  selectedRows: (DataTableRow<ColTypes> & RowType)[];
70
70
  getHeaderProps: (options: {
71
71
  header: DataTableHeader;
72
+ id?: string;
72
73
  isSortable?: boolean;
73
74
  onClick?: (event: MouseEvent<HTMLButtonElement>, sortState: {
74
75
  sortHeaderKey: string;
@@ -76,6 +77,7 @@ export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
76
77
  }) => void;
77
78
  [key: string]: unknown;
78
79
  }) => {
80
+ id: string;
79
81
  isSortable: boolean | undefined;
80
82
  isSortHeader: boolean;
81
83
  key: string;
@@ -135,7 +137,7 @@ export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
135
137
  getToolbarProps: (options?: {
136
138
  [key: string]: unknown;
137
139
  }) => {
138
- size: 'sm' | undefined;
140
+ size: 'xs' | 'sm' | undefined;
139
141
  [key: string]: unknown;
140
142
  };
141
143
  getBatchActionProps: (options?: {
@@ -163,9 +165,12 @@ export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
163
165
  };
164
166
  getCellProps: (options: {
165
167
  cell: DataTableCell<ColTypes>;
168
+ headers?: string;
169
+ [key: string]: unknown;
166
170
  }) => {
167
171
  [key: string]: unknown;
168
172
  hasAILabelHeader?: boolean;
173
+ headers: string;
169
174
  key: string;
170
175
  };
171
176
  /**
@@ -384,7 +389,7 @@ export declare const DataTable: {
384
389
  TableToolbarAction: import("react").ForwardRefExoticComponent<import("./TableToolbarAction").TableToolbarActionProps & import("react").RefAttributes<HTMLDivElement>>;
385
390
  TableToolbarContent: (props: import("react").HTMLAttributes<"div">) => ReactElement;
386
391
  TableToolbarSearch: {
387
- ({ className, searchContainerClass, onChange: onChangeProp, onClear, translateWithId: t, placeholder, labelText, expanded: expandedProp, defaultExpanded, defaultValue, disabled, onExpand, persistent, id, onBlur, onFocus, size, tabIndex, ...rest }: import("./TableToolbarSearch").TableToolbarSearchProps): import("react/jsx-runtime").JSX.Element;
392
+ ({ className, searchContainerClass, onChange: onChangeProp, onClear, translateWithId: t, placeholder, labelText, expanded: expandedProp, defaultExpanded, defaultValue, disabled, onExpand, persistent, id, onBlur, onFocus, size: sizeProp, tabIndex, ...rest }: import("./TableToolbarSearch").TableToolbarSearchProps): import("react/jsx-runtime").JSX.Element;
388
393
  propTypes: {
389
394
  children: PropTypes.Requireable<PropTypes.ReactNodeLike>;
390
395
  className: PropTypes.Requireable<string>;
@@ -408,13 +413,14 @@ export declare const DataTable: {
408
413
  };
409
414
  };
410
415
  TableToolbarMenu: {
411
- ({ className, renderIcon, iconDescription, children, menuOptionsClass, ...rest }: import("./TableToolbarMenu").TableToolbarMenuProps): import("react/jsx-runtime").JSX.Element;
416
+ ({ className, renderIcon, iconDescription, children, menuOptionsClass, size: sizeProp, ...rest }: import("./TableToolbarMenu").TableToolbarMenuProps): import("react/jsx-runtime").JSX.Element;
412
417
  propTypes: {
413
418
  children: PropTypes.Validator<NonNullable<PropTypes.ReactNodeLike>>;
414
419
  className: PropTypes.Requireable<string>;
415
420
  iconDescription: PropTypes.Requireable<string>;
416
421
  menuOptionsClass: PropTypes.Requireable<string>;
417
422
  renderIcon: PropTypes.Requireable<object>;
423
+ size: PropTypes.Requireable<string>;
418
424
  };
419
425
  };
420
426
  propTypes: {
@@ -103,8 +103,10 @@ const DataTable = (props) => {
103
103
  const getHeaderProps = ({ header, onClick, isSortable: headerIsSortable, ...rest }) => {
104
104
  const { sortDirection, sortHeaderKey } = state;
105
105
  const { key, slug, decorator } = header;
106
+ const id = typeof rest.id === "string" && rest.id.length ? rest.id : getHeaderId(key);
106
107
  return {
107
108
  ...rest,
109
+ id,
108
110
  key,
109
111
  sortDirection,
110
112
  isSortable: headerIsSortable ?? header.isSortable ?? isSortable,
@@ -204,13 +206,10 @@ const DataTable = (props) => {
204
206
  onSelect: composeEventHandlers([handleSelectAll, onClick])
205
207
  };
206
208
  };
207
- const getToolbarProps = (props) => {
208
- const isSmall = size === "xs" || size === "sm";
209
- return {
210
- ...props,
211
- size: isSmall ? "sm" : void 0
212
- };
213
- };
209
+ const getToolbarProps = (props) => ({
210
+ ...props,
211
+ size: size === "xs" || size === "sm" ? size : void 0
212
+ });
214
213
  const getBatchActionProps = (props) => {
215
214
  const { shouldShowBatchActions } = state;
216
215
  const selectedRowCount = selectedRows.length;
@@ -240,10 +239,12 @@ const DataTable = (props) => {
240
239
  useStaticWidth
241
240
  };
242
241
  };
243
- const getCellProps = ({ cell: { hasAILabelHeader, id }, ...rest }) => {
242
+ const getCellProps = ({ cell: { hasAILabelHeader, id, info: { header } }, headers: customHeaders, ...rest }) => {
243
+ const headers = typeof customHeaders === "string" && customHeaders.length ? customHeaders : getHeaderId(header);
244
244
  return {
245
245
  ...rest,
246
246
  hasAILabelHeader,
247
+ headers,
247
248
  key: id
248
249
  };
249
250
  };
@@ -265,6 +266,7 @@ const DataTable = (props) => {
265
266
  * Generates a prefix for table related IDs.
266
267
  */
267
268
  const getTablePrefix = () => `data-table-${instanceId}`;
269
+ const getHeaderId = (headerKey) => `${getTablePrefix()}__header-${headerKey}`;
268
270
  /**
269
271
  * Generates a new `rowsById` object with updated selection state.
270
272
  */
@@ -1,11 +1,17 @@
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
8
  import React from 'react';
9
+ export declare const TableToolbarContext: React.Context<{
10
+ size?: "xs" | "sm" | "lg";
11
+ }>;
12
+ export declare const useTableToolbar: () => {
13
+ size?: "xs" | "sm" | "lg";
14
+ };
9
15
  export interface TableToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
10
16
  /**
11
17
  * Specify a label to be read by screen readers on the container node
@@ -24,7 +30,7 @@ export interface TableToolbarProps extends React.HTMLAttributes<HTMLDivElement>
24
30
  /**
25
31
  * `lg` Change the row height of table
26
32
  */
27
- size?: 'sm' | 'lg';
33
+ size?: 'xs' | 'sm' | 'lg';
28
34
  }
29
35
  declare const TableToolbar: {
30
36
  ({ ["aria-label"]: ariaLabel, ariaLabel: deprecatedAriaLabel, children, size, ...rest }: TableToolbarProps): import("react/jsx-runtime").JSX.Element;
@@ -8,35 +8,45 @@
8
8
  import { usePrefix } from "../../internal/usePrefix.js";
9
9
  import { deprecate } from "../../prop-types/deprecate.js";
10
10
  import classNames from "classnames";
11
- import "react";
11
+ import { createContext, useContext } from "react";
12
12
  import PropTypes from "prop-types";
13
13
  import { jsx } from "react/jsx-runtime";
14
14
  //#region src/components/DataTable/TableToolbar.tsx
15
15
  /**
16
- * Copyright IBM Corp. 2016, 2025
16
+ * Copyright IBM Corp. 2016, 2026
17
17
  *
18
18
  * This source code is licensed under the Apache-2.0 license found in the
19
19
  * LICENSE file in the root directory of this source tree.
20
20
  */
21
+ const TableToolbarContext = createContext({});
22
+ const useTableToolbar = () => useContext(TableToolbarContext);
21
23
  const TableToolbar = ({ ["aria-label"]: ariaLabel = "data table toolbar", ariaLabel: deprecatedAriaLabel, children, size, ...rest }) => {
22
24
  const prefix = usePrefix();
23
25
  const className = classNames({
24
26
  [`${prefix}--table-toolbar`]: true,
25
- [`${prefix}--table-toolbar--${size}`]: size
27
+ [`${prefix}--table-toolbar--${size}`]: size,
28
+ [`${prefix}--layout--size-${size}`]: size
26
29
  });
27
- return /* @__PURE__ */ jsx("section", {
28
- role: "group",
29
- "aria-label": deprecatedAriaLabel || ariaLabel,
30
- ...rest,
31
- className,
32
- children
30
+ return /* @__PURE__ */ jsx(TableToolbarContext.Provider, {
31
+ value: { size },
32
+ children: /* @__PURE__ */ jsx("section", {
33
+ role: "group",
34
+ "aria-label": deprecatedAriaLabel || ariaLabel,
35
+ ...rest,
36
+ className,
37
+ children
38
+ })
33
39
  });
34
40
  };
35
41
  TableToolbar.propTypes = {
36
42
  ["aria-label"]: PropTypes.string,
37
43
  ariaLabel: deprecate(PropTypes.string, "This prop syntax has been deprecated. Please use the new `aria-label`."),
38
44
  children: PropTypes.node,
39
- size: PropTypes.oneOf(["sm", "lg"])
45
+ size: PropTypes.oneOf([
46
+ "xs",
47
+ "sm",
48
+ "lg"
49
+ ])
40
50
  };
41
51
  //#endregion
42
- export { TableToolbar as default };
52
+ export { TableToolbar as default, useTableToolbar };
@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
8
8
  import { OverflowMenuProps } from '../OverflowMenu';
9
9
  export type TableToolbarMenuProps = OverflowMenuProps;
10
10
  declare const TableToolbarMenu: {
11
- ({ className, renderIcon, iconDescription, children, menuOptionsClass, ...rest }: TableToolbarMenuProps): import("react/jsx-runtime").JSX.Element;
11
+ ({ className, renderIcon, iconDescription, children, menuOptionsClass, size: sizeProp, ...rest }: TableToolbarMenuProps): import("react/jsx-runtime").JSX.Element;
12
12
  propTypes: {
13
13
  children: PropTypes.Validator<NonNullable<PropTypes.ReactNodeLike>>;
14
14
  /**
@@ -27,6 +27,10 @@ declare const TableToolbarMenu: {
27
27
  * A component used to render an icon.
28
28
  */
29
29
  renderIcon: PropTypes.Requireable<object>;
30
+ /**
31
+ * Specify the size of the ToolbarMenu.
32
+ */
33
+ size: PropTypes.Requireable<string>;
30
34
  };
31
35
  };
32
36
  export default TableToolbarMenu;
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import { usePrefix } from "../../internal/usePrefix.js";
9
+ import { useTableToolbar } from "./TableToolbar.js";
9
10
  import OverflowMenu from "../OverflowMenu/index.js";
10
11
  import classNames from "classnames";
11
12
  import "react";
@@ -20,13 +21,16 @@ import { Settings } from "@carbon/icons-react";
20
21
  * LICENSE file in the root directory of this source tree.
21
22
  */
22
23
  const defaultIconDescription = "Settings";
23
- const TableToolbarMenu = ({ className, renderIcon = Settings, iconDescription = defaultIconDescription, children, menuOptionsClass, ...rest }) => {
24
+ const TableToolbarMenu = ({ className, renderIcon = Settings, iconDescription = defaultIconDescription, children, menuOptionsClass, size: sizeProp, ...rest }) => {
25
+ const toolbarContext = useTableToolbar();
26
+ const size = sizeProp ?? toolbarContext.size;
24
27
  const prefix = usePrefix();
25
28
  return /* @__PURE__ */ jsx(OverflowMenu, {
26
29
  renderIcon,
27
30
  className: classNames(className, `${prefix}--toolbar-action ${prefix}--overflow-menu`),
28
31
  iconDescription,
29
32
  menuOptionsClass: classNames(menuOptionsClass, `${prefix}--toolbar-action__menu`),
33
+ size,
30
34
  flipped: true,
31
35
  ...rest,
32
36
  children
@@ -37,7 +41,13 @@ TableToolbarMenu.propTypes = {
37
41
  className: PropTypes.string,
38
42
  iconDescription: PropTypes.string,
39
43
  menuOptionsClass: PropTypes.string,
40
- renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
44
+ renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
45
+ size: PropTypes.oneOf([
46
+ "xs",
47
+ "sm",
48
+ "md",
49
+ "lg"
50
+ ])
41
51
  };
42
52
  //#endregion
43
53
  export { TableToolbarMenu as default };
@@ -13,7 +13,7 @@ declare const translationIds: {
13
13
  readonly 'carbon.table.toolbar.search.placeholder': "carbon.table.toolbar.search.placeholder";
14
14
  };
15
15
  type TranslationKey = keyof typeof translationIds;
16
- type ExcludedInheritedProps = 'defaultValue' | 'labelText' | 'onBlur' | 'onChange' | 'onExpand' | 'onFocus' | 'tabIndex' | 'size';
16
+ type ExcludedInheritedProps = 'defaultValue' | 'labelText' | 'onBlur' | 'onChange' | 'onExpand' | 'onFocus' | 'tabIndex';
17
17
  export type TableToolbarSearchHandleExpand = (event: FocusEvent<HTMLInputElement>, newValue?: boolean) => void;
18
18
  /**
19
19
  * @deprecated Passing `''` as the event sentinel is legacy compatibility
@@ -69,14 +69,10 @@ export interface TableToolbarSearchProps extends Omit<SearchProps, ExcludedInher
69
69
  * Provide an optional className for the overall container of the Search
70
70
  */
71
71
  searchContainerClass?: string;
72
- /**
73
- * Specify the size of the Search
74
- */
75
- size?: 'sm' | 'md' | 'lg';
76
72
  tabIndex?: number | string;
77
73
  }
78
74
  declare const TableToolbarSearch: {
79
- ({ className, searchContainerClass, onChange: onChangeProp, onClear, translateWithId: t, placeholder, labelText, expanded: expandedProp, defaultExpanded, defaultValue, disabled, onExpand, persistent, id, onBlur, onFocus, size, tabIndex, ...rest }: TableToolbarSearchProps): import("react/jsx-runtime").JSX.Element;
75
+ ({ className, searchContainerClass, onChange: onChangeProp, onClear, translateWithId: t, placeholder, labelText, expanded: expandedProp, defaultExpanded, defaultValue, disabled, onExpand, persistent, id, onBlur, onFocus, size: sizeProp, tabIndex, ...rest }: TableToolbarSearchProps): import("react/jsx-runtime").JSX.Element;
80
76
  propTypes: {
81
77
  children: PropTypes.Requireable<PropTypes.ReactNodeLike>;
82
78
  /**
@@ -9,6 +9,7 @@ import { usePrefix } from "../../internal/usePrefix.js";
9
9
  import { useId } from "../../internal/useId.js";
10
10
  import { noopFn } from "../../internal/noopFn.js";
11
11
  import Search_default from "../Search/index.js";
12
+ import { useTableToolbar } from "./TableToolbar.js";
12
13
  import classNames from "classnames";
13
14
  import { useEffect, useRef, useState } from "react";
14
15
  import PropTypes from "prop-types";
@@ -31,7 +32,9 @@ const defaultTranslations = {
31
32
  const defaultTranslateWithId = (messageId) => {
32
33
  return defaultTranslations[messageId];
33
34
  };
34
- const TableToolbarSearch = ({ className, searchContainerClass, onChange: onChangeProp, onClear = noopFn, translateWithId: t = defaultTranslateWithId, placeholder, labelText, expanded: expandedProp, defaultExpanded, defaultValue, disabled, onExpand, persistent = false, id, onBlur, onFocus, size = "lg", tabIndex = "0", ...rest }) => {
35
+ const TableToolbarSearch = ({ className, searchContainerClass, onChange: onChangeProp, onClear = noopFn, translateWithId: t = defaultTranslateWithId, placeholder, labelText, expanded: expandedProp, defaultExpanded, defaultValue, disabled, onExpand, persistent = false, id, onBlur, onFocus, size: sizeProp, tabIndex = "0", ...rest }) => {
36
+ const toolbarContext = useTableToolbar();
37
+ const size = sizeProp ?? toolbarContext.size;
35
38
  const { current: controlled } = useRef(expandedProp !== void 0);
36
39
  const [expandedState, setExpandedState] = useState(Boolean(defaultExpanded || defaultValue));
37
40
  const expanded = controlled ? expandedProp : expandedState;
@@ -100,6 +103,7 @@ TableToolbarSearch.propTypes = {
100
103
  placeholder: PropTypes.string,
101
104
  searchContainerClass: PropTypes.string,
102
105
  size: PropTypes.oneOf([
106
+ "xs",
103
107
  "sm",
104
108
  "md",
105
109
  "lg"
@@ -26,14 +26,14 @@ import { composeEventHandlers } from "../../tools/events.js";
26
26
  import { Layer } from "../Layer/index.js";
27
27
  import InlineLoading_default from "../InlineLoading/index.js";
28
28
  import { toggleClass } from "../../tools/toggleClass.js";
29
- import { requiredIfGivenPropIsTruthy } from "../../prop-types/requiredIfGivenPropIsTruthy.js";
30
29
  import { elementOrParentIsFloatingMenu, wrapFocus, wrapFocusWithoutSentinels } from "../../internal/wrapFocus.js";
31
30
  import { Dialog } from "../Dialog/Dialog.js";
32
31
  import { isTopmostVisibleModal } from "./isTopmostVisibleModal.js";
32
+ import { requiredIfGivenPropIsTruthy } from "../../prop-types/requiredIfGivenPropIsTruthy.js";
33
33
  import { usePreviousValue } from "../../internal/usePreviousValue.js";
34
34
  import { ModalPresence, ModalPresenceContext, useExclusiveModalPresenceContext } from "./ModalPresence.js";
35
35
  import classNames from "classnames";
36
- import React, { cloneElement, useCallback, useContext, useEffect, useRef } from "react";
36
+ import React, { cloneElement, useCallback, useContext, useEffect, useRef, useState } from "react";
37
37
  import PropTypes from "prop-types";
38
38
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
39
39
  import { Close } from "@carbon/icons-react";
@@ -167,11 +167,18 @@ const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLa
167
167
  [`${prefix}--modal-container--${size}`]: size,
168
168
  [`${prefix}--modal-container--full-width`]: isFullWidth
169
169
  });
170
- /**
171
- * isScrollable is implicitly dependent on height, when height gets updated
172
- * via `useResizeObserver`, clientHeight and scrollHeight get updated too
173
- */
174
- const isScrollable = !!contentRef.current && contentRef?.current?.scrollHeight > contentRef?.current?.clientHeight;
170
+ const currentScrollHeight = contentRef.current?.scrollHeight || 0;
171
+ const currentClientHeight = contentRef.current?.clientHeight || 0;
172
+ const [isScrollable, setIsScrollable] = useState(currentScrollHeight > currentClientHeight);
173
+ useEffect(() => {
174
+ if (!contentRef.current) {
175
+ setIsScrollable(false);
176
+ return;
177
+ }
178
+ const diff = contentRef.current.scrollHeight - contentRef.current.clientHeight;
179
+ if (diff > 5) setIsScrollable(true);
180
+ else if (diff < -5) setIsScrollable(false);
181
+ }, [currentScrollHeight, currentClientHeight]);
175
182
  const contentClasses = classNames(`${prefix}--modal-content`, {
176
183
  [`${prefix}--modal-scroll-content`]: hasScrollingContent || isScrollable,
177
184
  [`${prefix}--modal-scroll-content--no-fade`]: height <= 300
@@ -110,7 +110,7 @@ const Search$1 = React.forwardRef(({ autoComplete = "off", className, closeButto
110
110
  });
111
111
  return /* @__PURE__ */ jsxs("div", {
112
112
  role: "search",
113
- "aria-label": placeholder,
113
+ "aria-labelledby": searchId,
114
114
  className: searchClasses,
115
115
  children: [
116
116
  onExpand && !isExpanded ? /* @__PURE__ */ jsx(Tooltip, {
package/es/index.d.ts CHANGED
@@ -46,6 +46,7 @@ export * from './components/FormLabel';
46
46
  export * from './components/Grid';
47
47
  export * from './components/Icon/Icon.Skeleton';
48
48
  export * from './components/IdPrefix';
49
+ export { InlineCheckbox } from './components/InlineCheckbox';
49
50
  export * from './components/InlineLoading';
50
51
  export * from './components/Link';
51
52
  export * from './components/ListItem';