@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.
- package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +902 -902
- package/es/components/ComboBox/ComboBox.js +28 -1
- package/es/components/ComposedModal/ComposedModal.d.ts +8 -0
- package/es/components/ComposedModal/ComposedModal.js +33 -18
- package/es/components/ComposedModal/ComposedModalContext.d.ts +12 -0
- package/es/components/ComposedModal/ComposedModalContext.js +18 -0
- package/es/components/ComposedModal/ModalHeader.js +29 -1
- package/es/components/DataTable/DataTable.d.ts +9 -3
- package/es/components/DataTable/DataTable.js +10 -8
- package/es/components/DataTable/TableToolbar.d.ts +8 -2
- package/es/components/DataTable/TableToolbar.js +21 -11
- package/es/components/DataTable/TableToolbarMenu.d.ts +5 -1
- package/es/components/DataTable/TableToolbarMenu.js +12 -2
- package/es/components/DataTable/TableToolbarSearch.d.ts +2 -6
- package/es/components/DataTable/TableToolbarSearch.js +5 -1
- package/es/components/Modal/Modal.js +14 -7
- package/es/components/Search/Search.js +1 -1
- package/es/index.d.ts +1 -0
- package/es/index.js +2 -1
- package/es/internal/FloatingMenu.js +5 -3
- package/lib/components/ComboBox/ComboBox.js +28 -1
- package/lib/components/ComposedModal/ComposedModal.d.ts +8 -0
- package/lib/components/ComposedModal/ComposedModal.js +32 -17
- package/lib/components/ComposedModal/ComposedModalContext.d.ts +12 -0
- package/lib/components/ComposedModal/ComposedModalContext.js +18 -0
- package/lib/components/ComposedModal/ModalHeader.js +28 -0
- package/lib/components/DataTable/DataTable.d.ts +9 -3
- package/lib/components/DataTable/DataTable.js +10 -8
- package/lib/components/DataTable/TableToolbar.d.ts +8 -2
- package/lib/components/DataTable/TableToolbar.js +20 -9
- package/lib/components/DataTable/TableToolbarMenu.d.ts +5 -1
- package/lib/components/DataTable/TableToolbarMenu.js +12 -2
- package/lib/components/DataTable/TableToolbarSearch.d.ts +2 -6
- package/lib/components/DataTable/TableToolbarSearch.js +5 -1
- package/lib/components/Modal/Modal.js +13 -6
- package/lib/components/Search/Search.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -0
- package/lib/internal/FloatingMenu.js +5 -3
- 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"]:
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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,
|
|
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,
|
|
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(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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([
|
|
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'
|
|
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
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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-
|
|
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';
|