@carbon/react 1.107.1 → 1.108.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.
- package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +960 -960
- package/es/components/Button/Button.d.ts +2 -2
- package/es/components/Button/ButtonBase.js +5 -5
- package/es/components/ChatButton/ChatButton.d.ts +1 -1
- package/es/components/ChatButton/ChatButton.js +1 -1
- package/es/components/ComposedModal/ComposedModal.js +10 -5
- package/es/components/ComposedModal/ModalFooter.d.ts +5 -0
- package/es/components/ComposedModal/ModalFooter.js +3 -1
- package/es/components/Dialog/Dialog.d.ts +5 -0
- package/es/components/Dialog/Dialog.js +3 -1
- package/es/components/ErrorBoundary/ErrorBoundaryContext.d.ts +1 -1
- package/es/components/ErrorBoundary/ErrorBoundaryContext.js +1 -1
- package/es/components/FileUploader/FileUploader.d.ts +5 -0
- package/es/components/FileUploader/FileUploader.js +14 -6
- package/es/components/FileUploader/FileUploaderItem.d.ts +5 -1
- package/es/components/FileUploader/FileUploaderItem.js +4 -2
- package/es/components/FileUploader/Filename.d.ts +5 -1
- package/es/components/FileUploader/Filename.js +2 -1
- package/es/components/Menu/MenuItem.js +16 -7
- package/es/components/Modal/Modal.d.ts +5 -0
- package/es/components/Modal/Modal.js +26 -13
- package/es/components/Modal/isTopmostVisibleModal.d.ts +7 -0
- package/es/components/Modal/isTopmostVisibleModal.js +21 -0
- package/es/components/MultiSelect/FilterableMultiSelect.js +9 -8
- package/es/components/MultiSelect/MultiSelect.js +9 -8
- package/es/components/MultiSelect/tools/isSelectAllItem.d.ts +9 -0
- package/es/components/MultiSelect/tools/isSelectAllItem.js +17 -0
- package/es/components/MultiSelect/tools/sorting.js +1 -1
- package/es/components/OverflowMenuItem/OverflowMenuItem.js +3 -2
- package/es/components/PageHeader/PageHeader.d.ts +4 -0
- package/es/components/PageHeader/PageHeader.js +18 -0
- package/es/components/PageHeader/index.d.ts +4 -0
- package/es/components/Tabs/Tabs.d.ts +5 -1
- package/es/components/Tabs/Tabs.js +2 -1
- package/es/components/UIShell/SwitcherDivider.d.ts +2 -2
- package/es/components/UIShell/SwitcherDivider.js +2 -2
- package/es/internal/warning.d.ts +1 -1
- package/lib/components/Button/Button.d.ts +2 -2
- package/lib/components/Button/ButtonBase.js +5 -5
- package/lib/components/ChatButton/ChatButton.d.ts +1 -1
- package/lib/components/ChatButton/ChatButton.js +1 -1
- package/lib/components/ComposedModal/ComposedModal.js +10 -5
- package/lib/components/ComposedModal/ModalFooter.d.ts +5 -0
- package/lib/components/ComposedModal/ModalFooter.js +3 -1
- package/lib/components/Dialog/Dialog.d.ts +5 -0
- package/lib/components/Dialog/Dialog.js +3 -1
- package/lib/components/ErrorBoundary/ErrorBoundaryContext.d.ts +1 -1
- package/lib/components/ErrorBoundary/ErrorBoundaryContext.js +1 -1
- package/lib/components/FileUploader/FileUploader.d.ts +5 -0
- package/lib/components/FileUploader/FileUploader.js +14 -6
- package/lib/components/FileUploader/FileUploaderItem.d.ts +5 -1
- package/lib/components/FileUploader/FileUploaderItem.js +4 -2
- package/lib/components/FileUploader/Filename.d.ts +5 -1
- package/lib/components/FileUploader/Filename.js +2 -1
- package/lib/components/Menu/MenuItem.js +15 -6
- package/lib/components/Modal/Modal.d.ts +5 -0
- package/lib/components/Modal/Modal.js +26 -13
- package/lib/components/Modal/isTopmostVisibleModal.d.ts +7 -0
- package/lib/components/Modal/isTopmostVisibleModal.js +21 -0
- package/lib/components/MultiSelect/FilterableMultiSelect.js +9 -8
- package/lib/components/MultiSelect/MultiSelect.js +9 -8
- package/lib/components/MultiSelect/tools/isSelectAllItem.d.ts +9 -0
- package/lib/components/MultiSelect/tools/isSelectAllItem.js +17 -0
- package/lib/components/MultiSelect/tools/sorting.js +3 -3
- package/lib/components/OverflowMenuItem/OverflowMenuItem.js +3 -2
- package/lib/components/PageHeader/PageHeader.d.ts +4 -0
- package/lib/components/PageHeader/PageHeader.js +18 -0
- package/lib/components/PageHeader/index.d.ts +4 -0
- package/lib/components/Tabs/Tabs.d.ts +5 -1
- package/lib/components/Tabs/Tabs.js +2 -1
- package/lib/components/UIShell/SwitcherDivider.d.ts +2 -2
- package/lib/components/UIShell/SwitcherDivider.js +2 -2
- package/lib/internal/warning.d.ts +1 -1
- package/lib/internal/warning.js +1 -1
- package/package.json +10 -10
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import React
|
|
7
|
+
import React from 'react';
|
|
8
8
|
import { IconButtonKind } from '../IconButton';
|
|
9
9
|
import { PolymorphicComponentPropWithRef } from '../../internal/PolymorphicProps';
|
|
10
10
|
export declare const ButtonKinds: readonly ["primary", "secondary", "danger", "ghost", "danger--primary", "danger--ghost", "danger--tertiary", "tertiary"];
|
|
@@ -82,6 +82,6 @@ export interface ButtonBaseProps extends React.ButtonHTMLAttributes<HTMLButtonEl
|
|
|
82
82
|
tooltipPosition?: ButtonTooltipPosition;
|
|
83
83
|
}
|
|
84
84
|
export type ButtonProps<T extends React.ElementType> = PolymorphicComponentPropWithRef<T, ButtonBaseProps>;
|
|
85
|
-
export type ButtonComponent = <T extends React.ElementType = 'button'>(props: ButtonProps<T
|
|
85
|
+
export type ButtonComponent = <T extends React.ElementType = 'button'>(props: ButtonProps<T>, context?: any) => React.ReactElement | any;
|
|
86
86
|
declare const _default: ButtonComponent;
|
|
87
87
|
export default _default;
|
|
@@ -17,7 +17,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
17
17
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
18
18
|
* LICENSE file in the root directory of this source tree.
|
|
19
19
|
*/
|
|
20
|
-
const ButtonBase = React.forwardRef(function ButtonBase({ as, children, className, dangerDescription = "
|
|
20
|
+
const ButtonBase = React.forwardRef(function ButtonBase({ as, children, className, dangerDescription = "", disabled = false, hasIconOnly = false, href, iconDescription, isExpressive = false, isSelected, kind = "primary", onBlur, onClick, onFocus, onMouseEnter, onMouseLeave, renderIcon: ButtonImageElement, size, tabIndex, type = "button", ...rest }, ref) {
|
|
21
21
|
const prefix = usePrefix();
|
|
22
22
|
const commonProps = {
|
|
23
23
|
tabIndex,
|
|
@@ -43,23 +43,23 @@ const ButtonBase = React.forwardRef(function ButtonBase({ as, children, classNam
|
|
|
43
43
|
className: `${prefix}--btn__icon`,
|
|
44
44
|
"aria-hidden": "true"
|
|
45
45
|
});
|
|
46
|
-
const
|
|
46
|
+
const hasDangerDescription = [
|
|
47
47
|
"danger",
|
|
48
48
|
"danger--tertiary",
|
|
49
49
|
"danger--ghost"
|
|
50
|
-
];
|
|
50
|
+
].includes(kind) && Boolean(dangerDescription);
|
|
51
51
|
let component = "button";
|
|
52
52
|
const assistiveId = useId("danger-description");
|
|
53
53
|
const { "aria-pressed": ariaPressed, "aria-describedby": ariaDescribedBy } = rest;
|
|
54
54
|
let otherProps = {
|
|
55
55
|
disabled,
|
|
56
56
|
type,
|
|
57
|
-
"aria-describedby":
|
|
57
|
+
"aria-describedby": hasDangerDescription ? assistiveId : ariaDescribedBy || void 0,
|
|
58
58
|
"aria-pressed": ariaPressed ?? (hasIconOnly && kind === "ghost" ? isSelected : void 0)
|
|
59
59
|
};
|
|
60
60
|
const anchorProps = { href };
|
|
61
61
|
let assistiveText = null;
|
|
62
|
-
if (
|
|
62
|
+
if (hasDangerDescription) assistiveText = /* @__PURE__ */ jsx("span", {
|
|
63
63
|
id: assistiveId,
|
|
64
64
|
className: `${prefix}--visually-hidden`,
|
|
65
65
|
children: dangerDescription
|
|
@@ -13,7 +13,7 @@ import PropTypes from "prop-types";
|
|
|
13
13
|
import { jsx } from "react/jsx-runtime";
|
|
14
14
|
//#region src/components/ChatButton/ChatButton.tsx
|
|
15
15
|
/**
|
|
16
|
-
* Copyright IBM Corp. 2024,
|
|
16
|
+
* Copyright IBM Corp. 2024, 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.
|
|
@@ -26,6 +26,7 @@ import { elementOrParentIsFloatingMenu, wrapFocus, wrapFocusWithoutSentinels } f
|
|
|
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
|
+
import { isTopmostVisibleModal } from "../Modal/isTopmostVisibleModal.js";
|
|
29
30
|
import classNames from "classnames";
|
|
30
31
|
import React, { Children, cloneElement, useContext, useEffect, useRef } from "react";
|
|
31
32
|
import PropTypes from "prop-types";
|
|
@@ -98,9 +99,14 @@ const ComposedModalDialog = React.forwardRef(function ComposedModalDialog({ ["ar
|
|
|
98
99
|
const button = useRef(null);
|
|
99
100
|
const startSentinel = useRef(null);
|
|
100
101
|
const endSentinel = useRef(null);
|
|
102
|
+
const modalRef = useRef(null);
|
|
101
103
|
const onMouseDownTarget = useRef(null);
|
|
102
104
|
const presenceContext = useContext(ComposedModalPresenceContext);
|
|
103
|
-
const mergedRefs = useMergeRefs([
|
|
105
|
+
const mergedRefs = useMergeRefs([
|
|
106
|
+
modalRef,
|
|
107
|
+
ref,
|
|
108
|
+
presenceContext?.presenceRef
|
|
109
|
+
]);
|
|
104
110
|
const enablePresence = useFeatureFlag("enable-presence") || presenceContext?.autoEnablePresence;
|
|
105
111
|
const open = externalOpen || enablePresence;
|
|
106
112
|
const modalState = useComposedModalState(open);
|
|
@@ -184,15 +190,14 @@ const ComposedModalDialog = React.forwardRef(function ComposedModalDialog({ ["ar
|
|
|
184
190
|
useEffect(() => {
|
|
185
191
|
if (!open) return;
|
|
186
192
|
const handleEscapeKey = (event) => {
|
|
187
|
-
if (match(event, Escape)) {
|
|
193
|
+
if (match(event, Escape) && isTopmostVisibleModal(modalRef.current, prefix)) {
|
|
188
194
|
event.preventDefault();
|
|
189
|
-
event.stopPropagation();
|
|
190
195
|
closeModal(event);
|
|
191
196
|
}
|
|
192
197
|
};
|
|
193
|
-
document.addEventListener("keydown", handleEscapeKey
|
|
198
|
+
document.addEventListener("keydown", handleEscapeKey);
|
|
194
199
|
return () => {
|
|
195
|
-
document.removeEventListener("keydown", handleEscapeKey
|
|
200
|
+
document.removeEventListener("keydown", handleEscapeKey);
|
|
196
201
|
};
|
|
197
202
|
}, [open]);
|
|
198
203
|
useEffect(() => {
|
|
@@ -36,6 +36,11 @@ export interface ModalFooterProps {
|
|
|
36
36
|
* Note that this prop is not applied if you render primary/danger button by yourself
|
|
37
37
|
*/
|
|
38
38
|
danger?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Specify the message read by screen readers for the danger primary button.
|
|
41
|
+
* Defaults to an empty string; provide localized text to opt in.
|
|
42
|
+
*/
|
|
43
|
+
dangerDescription?: string;
|
|
39
44
|
/**
|
|
40
45
|
* The `ref` callback for the primary button.
|
|
41
46
|
*/
|
|
@@ -61,7 +61,7 @@ SecondaryButtonSet.propTypes = {
|
|
|
61
61
|
},
|
|
62
62
|
secondaryClassName: PropTypes.string
|
|
63
63
|
};
|
|
64
|
-
const ModalFooter = React.forwardRef(function ModalFooter({ children, className: customClassName, closeModal = noopFn, danger, inputref, onRequestClose = noopFn, onRequestSubmit = noopFn, primaryButtonDisabled, primaryButtonText, primaryClassName, secondaryButtonText, secondaryButtons, secondaryClassName, loadingStatus = "inactive", loadingDescription, loadingIconDescription, onLoadingSuccess = noopFn, ...rest }, ref) {
|
|
64
|
+
const ModalFooter = React.forwardRef(function ModalFooter({ children, className: customClassName, closeModal = noopFn, danger, dangerDescription = "", inputref, onRequestClose = noopFn, onRequestSubmit = noopFn, primaryButtonDisabled, primaryButtonText, primaryClassName, secondaryButtonText, secondaryButtons, secondaryClassName, loadingStatus = "inactive", loadingDescription, loadingIconDescription, onLoadingSuccess = noopFn, ...rest }, ref) {
|
|
65
65
|
const prefix = usePrefix();
|
|
66
66
|
const footerClass = classNames(`${prefix}--modal-footer`, customClassName, Array.isArray(secondaryButtons) && secondaryButtons.length === 2 ? `${prefix}--modal-footer--three-button` : null);
|
|
67
67
|
const primaryButtonClass = classNames(primaryClassName, loadingStatus !== "inactive" ? `${prefix}--btn--loading` : null);
|
|
@@ -85,6 +85,7 @@ const ModalFooter = React.forwardRef(function ModalFooter({ children, className:
|
|
|
85
85
|
onClick: onRequestSubmit,
|
|
86
86
|
className: primaryButtonClass,
|
|
87
87
|
disabled: loadingActive || primaryButtonDisabled,
|
|
88
|
+
dangerDescription,
|
|
88
89
|
kind: danger ? "danger" : "primary",
|
|
89
90
|
ref: inputref,
|
|
90
91
|
children: loadingStatus === "inactive" ? primaryButtonText : /* @__PURE__ */ jsx(InlineLoading_default, {
|
|
@@ -104,6 +105,7 @@ ModalFooter.propTypes = {
|
|
|
104
105
|
className: PropTypes.string,
|
|
105
106
|
closeModal: PropTypes.func,
|
|
106
107
|
danger: PropTypes.bool,
|
|
108
|
+
dangerDescription: PropTypes.string,
|
|
107
109
|
inputref: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({ current: PropTypes.any })]),
|
|
108
110
|
loadingDescription: PropTypes.string,
|
|
109
111
|
loadingIconDescription: PropTypes.string,
|
|
@@ -232,6 +232,11 @@ interface DialogFooterProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
232
232
|
* Specify whether the Dialog is for dangerous actions
|
|
233
233
|
*/
|
|
234
234
|
danger?: boolean;
|
|
235
|
+
/**
|
|
236
|
+
* Specify the message read by screen readers for the danger primary button.
|
|
237
|
+
* Defaults to an empty string; provide localized text to opt in.
|
|
238
|
+
*/
|
|
239
|
+
dangerDescription?: string;
|
|
235
240
|
/**
|
|
236
241
|
* Specify loading status
|
|
237
242
|
*/
|
|
@@ -248,7 +248,7 @@ DialogBody.propTypes = {
|
|
|
248
248
|
className: PropTypes.string,
|
|
249
249
|
hasScrollingContent: PropTypes.bool
|
|
250
250
|
};
|
|
251
|
-
const DialogFooter = React.forwardRef(({ children, className, onRequestClose = noopFn, onSecondarySubmit, onRequestSubmit = noopFn, primaryButtonText = "Save", primaryButtonDisabled = false, secondaryButtonText = "Cancel", secondaryButtons, loadingStatus = "inactive", loadingDescription, loadingIconDescription, onLoadingSuccess = noopFn, danger = false, ...rest }, ref) => {
|
|
251
|
+
const DialogFooter = React.forwardRef(({ children, className, onRequestClose = noopFn, onSecondarySubmit, onRequestSubmit = noopFn, primaryButtonText = "Save", primaryButtonDisabled = false, secondaryButtonText = "Cancel", secondaryButtons, loadingStatus = "inactive", loadingDescription, loadingIconDescription, onLoadingSuccess = noopFn, danger = false, dangerDescription = "", ...rest }, ref) => {
|
|
252
252
|
const prefix = usePrefix();
|
|
253
253
|
const button = useRef(null);
|
|
254
254
|
const { isOpen } = useContext(DialogContext);
|
|
@@ -296,6 +296,7 @@ const DialogFooter = React.forwardRef(({ children, className, onRequestClose = n
|
|
|
296
296
|
}), /* @__PURE__ */ jsx(Button_default, {
|
|
297
297
|
className: primaryButtonClass,
|
|
298
298
|
kind: danger ? "danger" : "primary",
|
|
299
|
+
dangerDescription,
|
|
299
300
|
disabled: loadingActive || primaryButtonDisabled,
|
|
300
301
|
onClick: onRequestSubmit,
|
|
301
302
|
ref: button,
|
|
@@ -333,6 +334,7 @@ DialogFooter.propTypes = {
|
|
|
333
334
|
return null;
|
|
334
335
|
},
|
|
335
336
|
danger: PropTypes.bool,
|
|
337
|
+
dangerDescription: PropTypes.string,
|
|
336
338
|
loadingStatus: PropTypes.oneOf([
|
|
337
339
|
"inactive",
|
|
338
340
|
"active",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { createContext } from "react";
|
|
9
9
|
//#region src/components/ErrorBoundary/ErrorBoundaryContext.ts
|
|
10
10
|
/**
|
|
11
|
-
* Copyright IBM Corp. 2016,
|
|
11
|
+
* Copyright IBM Corp. 2016, 2026
|
|
12
12
|
*
|
|
13
13
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
14
14
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -11,6 +11,7 @@ interface FileItem {
|
|
|
11
11
|
file: File & {
|
|
12
12
|
invalidFileType?: boolean;
|
|
13
13
|
};
|
|
14
|
+
disabled?: boolean;
|
|
14
15
|
}
|
|
15
16
|
export interface FileChangeData {
|
|
16
17
|
addedFiles: FileItem[];
|
|
@@ -113,6 +114,10 @@ export interface FileUploaderHandle {
|
|
|
113
114
|
* Get current files (only available when 'enable-enhanced-file-uploader' feature flag is enabled)
|
|
114
115
|
*/
|
|
115
116
|
getCurrentFiles?: () => FileItem[];
|
|
117
|
+
/**
|
|
118
|
+
* Set current files (only available when 'enable-enhanced-file-uploader' feature flag is enabled)
|
|
119
|
+
*/
|
|
120
|
+
setCurrentFiles?: (files: FileItem[]) => void;
|
|
116
121
|
}
|
|
117
122
|
declare const FileUploader: {
|
|
118
123
|
(props: FileUploaderProps): React.ReactElement;
|
|
@@ -154,9 +154,14 @@ const FileUploader = forwardRef(({ accept, buttonKind, buttonLabel, className, d
|
|
|
154
154
|
});
|
|
155
155
|
} else setLegacyFileNames([]);
|
|
156
156
|
},
|
|
157
|
-
...enhancedFileUploaderEnabled && {
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
...enhancedFileUploaderEnabled && {
|
|
158
|
+
getCurrentFiles() {
|
|
159
|
+
return [...fileItems];
|
|
160
|
+
},
|
|
161
|
+
setCurrentFiles: (files) => {
|
|
162
|
+
setFileItems(files);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
160
165
|
}), [
|
|
161
166
|
enhancedFileUploaderEnabled,
|
|
162
167
|
fileItems,
|
|
@@ -167,12 +172,14 @@ const FileUploader = forwardRef(({ accept, buttonKind, buttonLabel, className, d
|
|
|
167
172
|
[className]: className
|
|
168
173
|
});
|
|
169
174
|
const getHelperLabelClasses = (baseClass) => classNames(baseClass, { [`${prefix}--label-description--disabled`]: disabled });
|
|
170
|
-
const
|
|
175
|
+
const getSelectedFileClasses = (file) => classNames(`${prefix}--file__selected-file`, {
|
|
171
176
|
[`${prefix}--file__selected-file--md`]: size === "field" || size === "md",
|
|
172
|
-
[`${prefix}--file__selected-file--sm`]: size === "small" || size === "sm"
|
|
177
|
+
[`${prefix}--file__selected-file--sm`]: size === "small" || size === "sm",
|
|
178
|
+
[`${prefix}--file__selected-file--disabled`]: file.disabled
|
|
173
179
|
});
|
|
174
180
|
const displayFiles = enhancedFileUploaderEnabled ? fileItems.map((item, index) => ({
|
|
175
181
|
name: item.name,
|
|
182
|
+
disabled: item.disabled,
|
|
176
183
|
key: item.uuid,
|
|
177
184
|
index
|
|
178
185
|
})) : legacyFileNames.map((name, index) => ({
|
|
@@ -211,7 +218,7 @@ const FileUploader = forwardRef(({ accept, buttonKind, buttonLabel, className, d
|
|
|
211
218
|
/* @__PURE__ */ jsx("div", {
|
|
212
219
|
className: `${prefix}--file-container`,
|
|
213
220
|
children: displayFiles.length === 0 ? null : displayFiles.map((file) => /* @__PURE__ */ jsxs("span", {
|
|
214
|
-
className:
|
|
221
|
+
className: getSelectedFileClasses(file),
|
|
215
222
|
ref: (node) => {
|
|
216
223
|
nodes[file.index] = node;
|
|
217
224
|
},
|
|
@@ -225,6 +232,7 @@ const FileUploader = forwardRef(({ accept, buttonKind, buttonLabel, className, d
|
|
|
225
232
|
className: `${prefix}--file__state-container`,
|
|
226
233
|
children: /* @__PURE__ */ jsx(Filename, {
|
|
227
234
|
name: file.name,
|
|
235
|
+
disabled: file.disabled,
|
|
228
236
|
iconDescription,
|
|
229
237
|
status: filenameStatus,
|
|
230
238
|
onKeyDown: (evt) => {
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
8
|
import React, { type HTMLAttributes } from 'react';
|
|
9
9
|
export interface FileUploaderItemProps extends HTMLAttributes<HTMLSpanElement> {
|
|
10
|
+
/**
|
|
11
|
+
* Specify whether file uploader item is disabled
|
|
12
|
+
*/
|
|
13
|
+
disabled?: boolean;
|
|
10
14
|
/**
|
|
11
15
|
* Error message body for an invalid file upload
|
|
12
16
|
*/
|
|
@@ -52,7 +56,7 @@ export interface FileUploaderItemProps extends HTMLAttributes<HTMLSpanElement> {
|
|
|
52
56
|
*/
|
|
53
57
|
uuid?: string;
|
|
54
58
|
}
|
|
55
|
-
declare function FileUploaderItem({ uuid, name, status, iconDescription, onDelete, invalid, errorSubject, errorBody, size, className, ...other }: FileUploaderItemProps): import("react/jsx-runtime").JSX.Element;
|
|
59
|
+
declare function FileUploaderItem({ uuid, name, status, iconDescription, onDelete, invalid, errorSubject, errorBody, size, className, disabled, ...other }: FileUploaderItemProps): import("react/jsx-runtime").JSX.Element;
|
|
56
60
|
declare namespace FileUploaderItem {
|
|
57
61
|
var propTypes: {
|
|
58
62
|
/**
|
|
@@ -25,7 +25,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
25
25
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
26
26
|
* LICENSE file in the root directory of this source tree.
|
|
27
27
|
*/
|
|
28
|
-
function FileUploaderItem({ uuid, name, status = "uploading", iconDescription, onDelete = noopFn, invalid, errorSubject, errorBody, size, className, ...other }) {
|
|
28
|
+
function FileUploaderItem({ uuid, name, status = "uploading", iconDescription, onDelete = noopFn, invalid, errorSubject, errorBody, size, className, disabled, ...other }) {
|
|
29
29
|
const textRef = useRef(null);
|
|
30
30
|
const [isEllipsisApplied, setIsEllipsisApplied] = useState(false);
|
|
31
31
|
const prefix = usePrefix();
|
|
@@ -34,7 +34,8 @@ function FileUploaderItem({ uuid, name, status = "uploading", iconDescription, o
|
|
|
34
34
|
const classes = classNames(`${prefix}--file__selected-file`, className, {
|
|
35
35
|
[`${prefix}--file__selected-file--invalid`]: invalid,
|
|
36
36
|
[`${prefix}--file__selected-file--md`]: size === "md",
|
|
37
|
-
[`${prefix}--file__selected-file--sm`]: size === "sm"
|
|
37
|
+
[`${prefix}--file__selected-file--sm`]: size === "sm",
|
|
38
|
+
[`${prefix}--file__selected-file--disabled`]: disabled
|
|
38
39
|
});
|
|
39
40
|
const isInvalid = invalid ? `${prefix}--file-filename-container-wrap-invalid` : `${prefix}--file-filename-container-wrap`;
|
|
40
41
|
const filterSpaceName = (name) => {
|
|
@@ -89,6 +90,7 @@ function FileUploaderItem({ uuid, name, status = "uploading", iconDescription, o
|
|
|
89
90
|
className: `${prefix}--file__state-container`,
|
|
90
91
|
children: /* @__PURE__ */ jsx(Filename, {
|
|
91
92
|
name,
|
|
93
|
+
disabled,
|
|
92
94
|
iconDescription,
|
|
93
95
|
status,
|
|
94
96
|
invalid,
|
|
@@ -17,6 +17,10 @@ export interface FilenameProps extends Omit<HTMLAttributes<HTMLElement> & SVGAtt
|
|
|
17
17
|
* Provide a description of the SVG icon to denote file upload status
|
|
18
18
|
*/
|
|
19
19
|
iconDescription?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Specify whether the file uploader item is disabled
|
|
22
|
+
*/
|
|
23
|
+
disabled?: boolean;
|
|
20
24
|
/**
|
|
21
25
|
* Specify if the file is invalid
|
|
22
26
|
*/
|
|
@@ -34,7 +38,7 @@ export interface FilenameProps extends Omit<HTMLAttributes<HTMLElement> & SVGAtt
|
|
|
34
38
|
*/
|
|
35
39
|
tabIndex?: number;
|
|
36
40
|
}
|
|
37
|
-
declare function Filename({ iconDescription, status, invalid, name, tabIndex, ['aria-describedby']: ariaDescribedBy, ...rest }: FilenameProps): import("react/jsx-runtime").JSX.Element | null;
|
|
41
|
+
declare function Filename({ iconDescription, status, invalid, disabled, name, tabIndex, ['aria-describedby']: ariaDescribedBy, ...rest }: FilenameProps): import("react/jsx-runtime").JSX.Element | null;
|
|
38
42
|
declare namespace Filename {
|
|
39
43
|
var propTypes: {
|
|
40
44
|
/**
|
|
@@ -18,7 +18,7 @@ import { CheckmarkFilled, Close, WarningFilled } from "@carbon/icons-react";
|
|
|
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
|
-
function Filename({ iconDescription = "Uploading file", status = "uploading", invalid, name, tabIndex = 0, ["aria-describedby"]: ariaDescribedBy, ...rest }) {
|
|
21
|
+
function Filename({ iconDescription = "Uploading file", status = "uploading", invalid, disabled, name, tabIndex = 0, ["aria-describedby"]: ariaDescribedBy, ...rest }) {
|
|
22
22
|
const prefix = usePrefix();
|
|
23
23
|
switch (status) {
|
|
24
24
|
case "uploading": return /* @__PURE__ */ jsx(Loading_default, {
|
|
@@ -28,6 +28,7 @@ function Filename({ iconDescription = "Uploading file", status = "uploading", in
|
|
|
28
28
|
className: `${prefix}--file-loading`
|
|
29
29
|
});
|
|
30
30
|
case "edit": return /* @__PURE__ */ jsxs(Fragment, { children: [invalid && /* @__PURE__ */ jsx(WarningFilled, { className: `${prefix}--file-invalid` }), /* @__PURE__ */ jsx("button", {
|
|
31
|
+
disabled,
|
|
31
32
|
"aria-label": `${iconDescription} - ${name}`,
|
|
32
33
|
className: `${prefix}--file-close`,
|
|
33
34
|
type: "button",
|
|
@@ -21,7 +21,7 @@ import React, { forwardRef, useContext, useEffect, useRef, useState } from "reac
|
|
|
21
21
|
import PropTypes from "prop-types";
|
|
22
22
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
23
23
|
import { CaretLeft, CaretRight, Checkmark } from "@carbon/icons-react";
|
|
24
|
-
import { FloatingFocusManager, autoUpdate, offset, safePolygon, useFloating, useHover, useInteractions } from "@floating-ui/react";
|
|
24
|
+
import { FloatingFocusManager, autoUpdate, flip, offset, safePolygon, useFloating, useHover, useInteractions } from "@floating-ui/react";
|
|
25
25
|
//#region src/components/Menu/MenuItem.tsx
|
|
26
26
|
/**
|
|
27
27
|
* Copyright IBM Corp. 2023, 2026
|
|
@@ -29,18 +29,26 @@ import { FloatingFocusManager, autoUpdate, offset, safePolygon, useFloating, use
|
|
|
29
29
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
30
30
|
* LICENSE file in the root directory of this source tree.
|
|
31
31
|
*/
|
|
32
|
-
const MenuItem = forwardRef(function MenuItem({ children, className, dangerDescription = "
|
|
32
|
+
const MenuItem = forwardRef(function MenuItem({ children, className, dangerDescription = "", disabled, kind = "default", label, onClick, renderIcon: IconElement, shortcut, ...rest }, forwardRef) {
|
|
33
33
|
const [submenuOpen, setSubmenuOpen] = useState(false);
|
|
34
34
|
const [rtl, setRtl] = useState(false);
|
|
35
35
|
const { refs, floatingStyles, context: floatingContext } = useFloating({
|
|
36
36
|
open: submenuOpen,
|
|
37
|
-
onOpenChange:
|
|
37
|
+
onOpenChange: (open, event) => {
|
|
38
|
+
if (open) setSubmenuOpen(true);
|
|
39
|
+
else {
|
|
40
|
+
const relatedTarget = event && "relatedTarget" in event ? event.relatedTarget : null;
|
|
41
|
+
if (relatedTarget instanceof Node && menuItem.current?.contains(relatedTarget)) return;
|
|
42
|
+
setSubmenuOpen(false);
|
|
43
|
+
if (!(relatedTarget instanceof HTMLElement && relatedTarget.closest("[role=\"menuitem\"]")?.querySelector("[role=\"menu\"]"))) menuItem.current?.focus();
|
|
44
|
+
}
|
|
45
|
+
},
|
|
38
46
|
placement: rtl ? "left-start" : "right-start",
|
|
39
47
|
whileElementsMounted: autoUpdate,
|
|
40
|
-
middleware: [offset({
|
|
41
|
-
mainAxis: -6,
|
|
48
|
+
middleware: [flip(), offset(({ placement }) => ({
|
|
49
|
+
mainAxis: placement.startsWith("left") ? 10 : -6,
|
|
42
50
|
crossAxis: -6
|
|
43
|
-
})],
|
|
51
|
+
}))],
|
|
44
52
|
strategy: "fixed"
|
|
45
53
|
});
|
|
46
54
|
const { getReferenceProps, getFloatingProps } = useInteractions([useHover(floatingContext, {
|
|
@@ -59,6 +67,7 @@ const MenuItem = forwardRef(function MenuItem({ children, className, dangerDescr
|
|
|
59
67
|
const hasChildren = React.Children.toArray(children).length > 0;
|
|
60
68
|
const isDisabled = disabled && !hasChildren;
|
|
61
69
|
const isDanger = kind === "danger" && !hasChildren;
|
|
70
|
+
const hasDangerDescription = isDanger && Boolean(dangerDescription);
|
|
62
71
|
function registerItem() {
|
|
63
72
|
context.dispatch({
|
|
64
73
|
type: "registerItem",
|
|
@@ -161,7 +170,7 @@ const MenuItem = forwardRef(function MenuItem({ children, className, dangerDescr
|
|
|
161
170
|
className: `${prefix}--menu-item__label`,
|
|
162
171
|
children: label
|
|
163
172
|
}),
|
|
164
|
-
|
|
173
|
+
hasDangerDescription && /* @__PURE__ */ jsx("span", {
|
|
165
174
|
id: assistiveId,
|
|
166
175
|
className: `${prefix}--visually-hidden`,
|
|
167
176
|
children: dangerDescription
|
|
@@ -38,6 +38,11 @@ export interface ModalProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
38
38
|
* Specify whether the Modal is for dangerous actions
|
|
39
39
|
*/
|
|
40
40
|
danger?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Specify the message read by screen readers for the danger primary button.
|
|
43
|
+
* Defaults to an empty string; provide localized text to opt in.
|
|
44
|
+
*/
|
|
45
|
+
dangerDescription?: string;
|
|
41
46
|
/**
|
|
42
47
|
* **Experimental**: Provide a decorator component to be rendered inside the `Modal` component
|
|
43
48
|
*/
|
|
@@ -29,6 +29,7 @@ import { toggleClass } from "../../tools/toggleClass.js";
|
|
|
29
29
|
import { requiredIfGivenPropIsTruthy } from "../../prop-types/requiredIfGivenPropIsTruthy.js";
|
|
30
30
|
import { elementOrParentIsFloatingMenu, wrapFocus, wrapFocusWithoutSentinels } from "../../internal/wrapFocus.js";
|
|
31
31
|
import { Dialog } from "../Dialog/Dialog.js";
|
|
32
|
+
import { isTopmostVisibleModal } from "./isTopmostVisibleModal.js";
|
|
32
33
|
import { usePreviousValue } from "../../internal/usePreviousValue.js";
|
|
33
34
|
import { ModalPresence, ModalPresenceContext, useExclusiveModalPresenceContext } from "./ModalPresence.js";
|
|
34
35
|
import classNames from "classnames";
|
|
@@ -72,12 +73,13 @@ const Modal = React.forwardRef(function Modal({ open, ...props }, ref) {
|
|
|
72
73
|
...props
|
|
73
74
|
});
|
|
74
75
|
});
|
|
75
|
-
const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLabelProp, children, className, decorator, modalHeading = "", modalLabel = "", modalAriaLabel, passiveModal = false, secondaryButtonText, primaryButtonText, open: externalOpen, onRequestClose = noopFn, onRequestSubmit = noopFn, onSecondarySubmit, primaryButtonDisabled = false, danger, alert, secondaryButtons, selectorPrimaryFocus = "[data-modal-primary-focus]", selectorsFloatingMenus, shouldSubmitOnEnter, size, hasScrollingContent = false, closeButtonLabel = "Close", preventCloseOnClickOutside, isFullWidth, launcherButtonRef, loadingStatus = "inactive", loadingDescription, loadingIconDescription, onLoadingSuccess = noopFn, slug, ...rest }, ref) {
|
|
76
|
+
const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLabelProp, children, className, decorator, modalHeading = "", modalLabel = "", modalAriaLabel, passiveModal = false, secondaryButtonText, primaryButtonText, open: externalOpen, onRequestClose = noopFn, onRequestSubmit = noopFn, onSecondarySubmit, primaryButtonDisabled = false, danger, dangerDescription = "", alert, secondaryButtons, selectorPrimaryFocus = "[data-modal-primary-focus]", selectorsFloatingMenus, shouldSubmitOnEnter, size, hasScrollingContent = false, closeButtonLabel = "Close", preventCloseOnClickOutside, isFullWidth, launcherButtonRef, loadingStatus = "inactive", loadingDescription, loadingIconDescription, onLoadingSuccess = noopFn, slug, ...rest }, ref) {
|
|
76
77
|
const prefix = usePrefix();
|
|
77
78
|
const button = useRef(null);
|
|
78
79
|
const secondaryButton = useRef(null);
|
|
79
80
|
const contentRef = useRef(null);
|
|
80
81
|
const innerModal = useRef(null);
|
|
82
|
+
const modalRef = useRef(null);
|
|
81
83
|
const startTrap = useRef(null);
|
|
82
84
|
const endTrap = useRef(null);
|
|
83
85
|
const wrapFocusTimeout = useRef(null);
|
|
@@ -89,7 +91,11 @@ const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLa
|
|
|
89
91
|
const primaryButtonClass = classNames({ [`${prefix}--btn--loading`]: loadingStatus !== "inactive" });
|
|
90
92
|
const loadingActive = loadingStatus !== "inactive";
|
|
91
93
|
const presenceContext = useContext(ModalPresenceContext);
|
|
92
|
-
const mergedRefs = useMergedRefs([
|
|
94
|
+
const mergedRefs = useMergedRefs([
|
|
95
|
+
modalRef,
|
|
96
|
+
ref,
|
|
97
|
+
presenceContext?.presenceRef
|
|
98
|
+
]);
|
|
93
99
|
const enablePresence = useFeatureFlag("enable-presence") || presenceContext?.autoEnablePresence;
|
|
94
100
|
const open = externalOpen || enablePresence;
|
|
95
101
|
const prevOpen = usePreviousValue(open);
|
|
@@ -103,14 +109,19 @@ const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLa
|
|
|
103
109
|
}
|
|
104
110
|
function handleKeyDown(evt) {
|
|
105
111
|
const { target } = evt;
|
|
106
|
-
evt.stopPropagation();
|
|
107
112
|
if (open && target instanceof HTMLElement) {
|
|
108
|
-
if (match(evt, Enter) && shouldSubmitOnEnter && !isCloseButton(target) && document.activeElement !== button.current)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
if (match(evt, Enter) && shouldSubmitOnEnter && !isCloseButton(target) && document.activeElement !== button.current) {
|
|
114
|
+
evt.stopPropagation();
|
|
115
|
+
onRequestSubmit(evt);
|
|
116
|
+
}
|
|
117
|
+
if (focusTrapWithoutSentinels && !enableDialogElement && match(evt, Tab) && innerModal.current) {
|
|
118
|
+
evt.stopPropagation();
|
|
119
|
+
wrapFocusWithoutSentinels({
|
|
120
|
+
containerNode: innerModal.current,
|
|
121
|
+
currentActiveNode: target,
|
|
122
|
+
event: evt
|
|
123
|
+
});
|
|
124
|
+
}
|
|
114
125
|
}
|
|
115
126
|
}
|
|
116
127
|
function handleOnClick(evt) {
|
|
@@ -187,15 +198,14 @@ const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLa
|
|
|
187
198
|
useEffect(() => {
|
|
188
199
|
if (!open) return;
|
|
189
200
|
const handleEscapeKey = (event) => {
|
|
190
|
-
if (match(event, Escape)) {
|
|
201
|
+
if (match(event, Escape) && isTopmostVisibleModal(modalRef.current, prefix)) {
|
|
191
202
|
event.preventDefault();
|
|
192
|
-
event.stopPropagation();
|
|
193
203
|
onRequestClose(event);
|
|
194
204
|
}
|
|
195
205
|
};
|
|
196
|
-
document.addEventListener("keydown", handleEscapeKey
|
|
206
|
+
document.addEventListener("keydown", handleEscapeKey);
|
|
197
207
|
return () => {
|
|
198
|
-
document.removeEventListener("keydown", handleEscapeKey
|
|
208
|
+
document.removeEventListener("keydown", handleEscapeKey);
|
|
199
209
|
};
|
|
200
210
|
}, [open]);
|
|
201
211
|
useEffect(() => {
|
|
@@ -352,6 +362,7 @@ const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLa
|
|
|
352
362
|
}), /* @__PURE__ */ jsx(Button_default, {
|
|
353
363
|
className: primaryButtonClass,
|
|
354
364
|
kind: danger ? "danger" : "primary",
|
|
365
|
+
dangerDescription,
|
|
355
366
|
disabled: loadingActive || primaryButtonDisabled,
|
|
356
367
|
onClick: onRequestSubmit,
|
|
357
368
|
ref: button,
|
|
@@ -428,6 +439,7 @@ const ModalDialog = React.forwardRef(function ModalDialog({ "aria-label": ariaLa
|
|
|
428
439
|
}), /* @__PURE__ */ jsx(Button_default, {
|
|
429
440
|
className: primaryButtonClass,
|
|
430
441
|
kind: danger ? "danger" : "primary",
|
|
442
|
+
dangerDescription,
|
|
431
443
|
disabled: loadingActive || primaryButtonDisabled,
|
|
432
444
|
onClick: onRequestSubmit,
|
|
433
445
|
ref: button,
|
|
@@ -470,6 +482,7 @@ Modal.propTypes = {
|
|
|
470
482
|
className: PropTypes.string,
|
|
471
483
|
closeButtonLabel: PropTypes.string,
|
|
472
484
|
danger: PropTypes.bool,
|
|
485
|
+
dangerDescription: PropTypes.string,
|
|
473
486
|
decorator: PropTypes.node,
|
|
474
487
|
hasScrollingContent: PropTypes.bool,
|
|
475
488
|
id: PropTypes.string,
|
|
@@ -0,0 +1,7 @@
|
|
|
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 isTopmostVisibleModal: (node: HTMLElement | null, prefix: string) => boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
//#region src/components/Modal/isTopmostVisibleModal.ts
|
|
9
|
+
/**
|
|
10
|
+
* Copyright IBM Corp. 2026
|
|
11
|
+
*
|
|
12
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
13
|
+
* LICENSE file in the root directory of this source tree.
|
|
14
|
+
*/
|
|
15
|
+
const isTopmostVisibleModal = (node, prefix) => {
|
|
16
|
+
if (!node) return false;
|
|
17
|
+
const visibleModals = document.querySelectorAll(`.${prefix}--modal.is-visible`);
|
|
18
|
+
return visibleModals.item(visibleModals.length - 1) === node;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { isTopmostVisibleModal };
|