carbon-react 123.9.1 → 123.10.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/esm/__internal__/checkable-input/hidden-checkable-input.component.js +1 -0
- package/esm/__internal__/focus-trap/focus-trap-utils.js +3 -2
- package/esm/__internal__/focus-trap/focus-trap.component.js +11 -10
- package/esm/__internal__/input/input.component.js +1 -0
- package/esm/components/alert/alert.component.d.ts +1 -1
- package/esm/components/alert/alert.component.js +3 -1
- package/esm/components/confirm/confirm.component.d.ts +1 -1
- package/esm/components/confirm/confirm.component.js +3 -1
- package/esm/components/dialog/dialog.component.js +5 -2
- package/esm/components/dialog-full-screen/dialog-full-screen.component.d.ts +1 -1
- package/esm/components/dialog-full-screen/dialog-full-screen.component.js +3 -1
- package/esm/components/modal/__internal__/modal-manager.d.ts +2 -1
- package/esm/components/modal/__internal__/modal-manager.js +8 -5
- package/esm/components/modal/modal.component.d.ts +3 -1
- package/esm/components/modal/modal.component.js +4 -1
- package/esm/components/modal/modal.style.d.ts +3 -1
- package/esm/components/modal/modal.style.js +3 -2
- package/esm/components/select/filterable-select/filterable-select.component.js +20 -11
- package/esm/components/select/multi-select/multi-select.component.js +23 -14
- package/esm/components/select/simple-select/simple-select.component.js +16 -7
- package/esm/components/sidebar/sidebar.component.d.ts +2 -1
- package/esm/components/sidebar/sidebar.component.js +4 -1
- package/esm/components/toast/toast.component.js +2 -1
- package/esm/hooks/__internal__/useModalManager/useModalManager.d.ts +2 -1
- package/esm/hooks/__internal__/useModalManager/useModalManager.js +4 -3
- package/lib/__internal__/checkable-input/hidden-checkable-input.component.js +1 -0
- package/lib/__internal__/focus-trap/focus-trap-utils.js +3 -2
- package/lib/__internal__/focus-trap/focus-trap.component.js +11 -10
- package/lib/__internal__/input/input.component.js +1 -0
- package/lib/components/alert/alert.component.d.ts +1 -1
- package/lib/components/alert/alert.component.js +3 -1
- package/lib/components/confirm/confirm.component.d.ts +1 -1
- package/lib/components/confirm/confirm.component.js +3 -1
- package/lib/components/dialog/dialog.component.js +5 -2
- package/lib/components/dialog-full-screen/dialog-full-screen.component.d.ts +1 -1
- package/lib/components/dialog-full-screen/dialog-full-screen.component.js +3 -1
- package/lib/components/modal/__internal__/modal-manager.d.ts +2 -1
- package/lib/components/modal/__internal__/modal-manager.js +8 -5
- package/lib/components/modal/modal.component.d.ts +3 -1
- package/lib/components/modal/modal.component.js +4 -1
- package/lib/components/modal/modal.style.d.ts +3 -1
- package/lib/components/modal/modal.style.js +3 -2
- package/lib/components/select/filterable-select/filterable-select.component.js +20 -11
- package/lib/components/select/multi-select/multi-select.component.js +23 -14
- package/lib/components/select/simple-select/simple-select.component.js +16 -7
- package/lib/components/sidebar/sidebar.component.d.ts +2 -1
- package/lib/components/sidebar/sidebar.component.js +4 -1
- package/lib/components/toast/toast.component.js +2 -1
- package/lib/hooks/__internal__/useModalManager/useModalManager.d.ts +2 -1
- package/lib/hooks/__internal__/useModalManager/useModalManager.js +4 -3
- package/package.json +1 -1
|
@@ -62,6 +62,7 @@ const HiddenCheckableInput = /*#__PURE__*/React.forwardRef(({
|
|
|
62
62
|
"aria-describedby": combinedDescription,
|
|
63
63
|
"aria-labelledby": ariaLabelledBy,
|
|
64
64
|
autoFocus: autoFocus,
|
|
65
|
+
"data-has-autofocus": autoFocus ? true : undefined,
|
|
65
66
|
"aria-checked": checked,
|
|
66
67
|
checked: checked,
|
|
67
68
|
name: name,
|
|
@@ -90,7 +90,8 @@ const onTabGuardFocus = (trapWrappers, focusableSelectors, position) => guardWra
|
|
|
90
90
|
const isTop = position === "top";
|
|
91
91
|
const currentIndex = trapWrappers.indexOf(guardWrapperRef);
|
|
92
92
|
let index = currentIndex;
|
|
93
|
-
let nextWrapper
|
|
93
|
+
let nextWrapper;
|
|
94
|
+
let allFocusableElementsInNextWrapper;
|
|
94
95
|
do {
|
|
95
96
|
index += isTop ? -1 : 1;
|
|
96
97
|
if (index < 0) {
|
|
@@ -100,7 +101,7 @@ const onTabGuardFocus = (trapWrappers, focusableSelectors, position) => guardWra
|
|
|
100
101
|
index -= trapWrappers.length;
|
|
101
102
|
}
|
|
102
103
|
nextWrapper = trapWrappers[index];
|
|
103
|
-
allFocusableElementsInNextWrapper = nextWrapper?.current?.querySelectorAll(focusableSelectors || defaultFocusableSelectors);
|
|
104
|
+
allFocusableElementsInNextWrapper = Array.from(nextWrapper?.current?.querySelectorAll(focusableSelectors || defaultFocusableSelectors) || /* istanbul ignore next */[]).filter(el => Number(el.tabIndex) !== -1);
|
|
104
105
|
} while (index !== currentIndex && !allFocusableElementsInNextWrapper?.length);
|
|
105
106
|
const toFocus = allFocusableElementsInNextWrapper?.[isTop ? allFocusableElementsInNextWrapper.length - 1 : 0];
|
|
106
107
|
if (isRadio(toFocus)) {
|
|
@@ -74,16 +74,6 @@ const FocusTrap = ({
|
|
|
74
74
|
}, [additionalWrapperRefs, onTabGuardTopFocus, onTabGuardBottomFocus]);
|
|
75
75
|
const shouldSetFocus = autoFocus && isOpen && isAnimationComplete;
|
|
76
76
|
const prevShouldSetFocus = usePrevious(shouldSetFocus);
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (shouldSetFocus && !prevShouldSetFocus) {
|
|
79
|
-
const candidateFirstElement = focusFirstElement && "current" in focusFirstElement ? focusFirstElement.current : focusFirstElement;
|
|
80
|
-
const elementToFocus = candidateFirstElement || wrapperRef.current;
|
|
81
|
-
// istanbul ignore else
|
|
82
|
-
if (elementToFocus) {
|
|
83
|
-
setElementFocus(elementToFocus);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}, [shouldSetFocus, prevShouldSetFocus, focusFirstElement, wrapperRef]);
|
|
87
77
|
const getFocusableElements = useCallback(selector => {
|
|
88
78
|
const elements = [];
|
|
89
79
|
trapWrappers.forEach(ref => {
|
|
@@ -94,6 +84,17 @@ const FocusTrap = ({
|
|
|
94
84
|
});
|
|
95
85
|
return elements;
|
|
96
86
|
}, [trapWrappers]);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (shouldSetFocus && !prevShouldSetFocus) {
|
|
89
|
+
const candidateFirstElement = focusFirstElement && "current" in focusFirstElement ? focusFirstElement.current : focusFirstElement;
|
|
90
|
+
const autoFocusedElement = getFocusableElements(defaultFocusableSelectors).find(el => el.getAttribute("data-has-autofocus") === "true");
|
|
91
|
+
const elementToFocus = candidateFirstElement || autoFocusedElement || wrapperRef.current;
|
|
92
|
+
// istanbul ignore else
|
|
93
|
+
if (elementToFocus) {
|
|
94
|
+
setElementFocus(elementToFocus);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}, [shouldSetFocus, prevShouldSetFocus, getFocusableElements, focusFirstElement, wrapperRef]);
|
|
97
98
|
useEffect(() => {
|
|
98
99
|
const trapFn = ev => {
|
|
99
100
|
// block focus trap from working if it's not the topmost trap, or is currently closed
|
|
@@ -131,6 +131,7 @@ const Input = /*#__PURE__*/React.forwardRef(({
|
|
|
131
131
|
}
|
|
132
132
|
const combinedDescription = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
|
|
133
133
|
return /*#__PURE__*/React.createElement(StyledInput, _extends({}, rest, {
|
|
134
|
+
"data-has-autofocus": autoFocus ? true : undefined,
|
|
134
135
|
"aria-describedby": combinedDescription,
|
|
135
136
|
"aria-labelledby": ariaLabelledBy,
|
|
136
137
|
align: align,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { DialogProps } from "../dialog";
|
|
3
|
-
export declare const Alert: ({ children, size, ...rest }: DialogProps) => React.JSX.Element;
|
|
3
|
+
export declare const Alert: ({ children, size, topModalOverride, ...rest }: DialogProps) => React.JSX.Element;
|
|
4
4
|
export default Alert;
|
|
@@ -5,10 +5,12 @@ import Dialog from "../dialog";
|
|
|
5
5
|
export const Alert = ({
|
|
6
6
|
children,
|
|
7
7
|
size = "extra-small",
|
|
8
|
+
topModalOverride,
|
|
8
9
|
...rest
|
|
9
10
|
}) => /*#__PURE__*/React.createElement(Dialog, _extends({
|
|
10
11
|
"data-component": "alert",
|
|
11
12
|
role: "alertdialog",
|
|
12
|
-
size: size
|
|
13
|
+
size: size,
|
|
14
|
+
topModalOverride: topModalOverride
|
|
13
15
|
}, rest), children);
|
|
14
16
|
export default Alert;
|
|
@@ -38,5 +38,5 @@ export interface ConfirmProps extends Omit<DialogProps, "className" | "disableFo
|
|
|
38
38
|
/** A custom event handler when a confirmation takes place */
|
|
39
39
|
onConfirm: (ev: React.MouseEvent<HTMLButtonElement>) => void;
|
|
40
40
|
}
|
|
41
|
-
export declare const Confirm: ({ "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, open, children, cancelButtonDestructive, confirmButtonDestructive, cancelButtonType, confirmButtonType, cancelButtonIconType, cancelButtonIconPosition, confirmButtonIconType, confirmButtonIconPosition, cancelButtonDataProps, confirmButtonDataProps, cancelLabel, onCancel, disableCancel, onConfirm, isLoadingConfirm, disableConfirm, confirmLabel, iconType, subtitle, title, size, showCloseIcon, ...rest }: ConfirmProps) => React.JSX.Element;
|
|
41
|
+
export declare const Confirm: ({ "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, open, children, cancelButtonDestructive, confirmButtonDestructive, cancelButtonType, confirmButtonType, cancelButtonIconType, cancelButtonIconPosition, confirmButtonIconType, confirmButtonIconPosition, cancelButtonDataProps, confirmButtonDataProps, cancelLabel, onCancel, disableCancel, onConfirm, isLoadingConfirm, disableConfirm, confirmLabel, iconType, subtitle, title, size, showCloseIcon, topModalOverride, ...rest }: ConfirmProps) => React.JSX.Element;
|
|
42
42
|
export default Confirm;
|
|
@@ -38,6 +38,7 @@ export const Confirm = ({
|
|
|
38
38
|
title,
|
|
39
39
|
size = "extra-small",
|
|
40
40
|
showCloseIcon = false,
|
|
41
|
+
topModalOverride,
|
|
41
42
|
...rest
|
|
42
43
|
}) => {
|
|
43
44
|
const l = useLocale();
|
|
@@ -114,7 +115,8 @@ export const Confirm = ({
|
|
|
114
115
|
"data-component": "confirm",
|
|
115
116
|
role: "alertdialog",
|
|
116
117
|
size: size,
|
|
117
|
-
showCloseIcon: showCloseIcon
|
|
118
|
+
showCloseIcon: showCloseIcon,
|
|
119
|
+
topModalOverride: topModalOverride
|
|
118
120
|
}, ariaProps, rest), children, /*#__PURE__*/React.createElement(StyledConfirmButtons, null, renderCancelButton(), renderConfirmButton()));
|
|
119
121
|
};
|
|
120
122
|
export default Confirm;
|
|
@@ -34,6 +34,7 @@ const Dialog = /*#__PURE__*/forwardRef(({
|
|
|
34
34
|
role = "dialog",
|
|
35
35
|
contentPadding = {},
|
|
36
36
|
focusableContainers,
|
|
37
|
+
topModalOverride,
|
|
37
38
|
...rest
|
|
38
39
|
}, ref) => {
|
|
39
40
|
const locale = useLocale();
|
|
@@ -154,7 +155,8 @@ const Dialog = /*#__PURE__*/forwardRef(({
|
|
|
154
155
|
onCancel: onCancel,
|
|
155
156
|
disableEscKey: disableEscKey,
|
|
156
157
|
disableClose: disableClose,
|
|
157
|
-
className: className ? `${className} carbon-dialog` : "carbon-dialog"
|
|
158
|
+
className: className ? `${className} carbon-dialog` : "carbon-dialog",
|
|
159
|
+
topModalOverride: topModalOverride
|
|
158
160
|
}, componentTags), /*#__PURE__*/React.createElement(FocusTrap, {
|
|
159
161
|
autoFocus: !disableAutoFocus,
|
|
160
162
|
focusFirstElement: focusFirstElement,
|
|
@@ -234,7 +236,8 @@ Dialog.propTypes = {
|
|
|
234
236
|
"size": PropTypes.oneOf(["auto", "extra-large", "extra-small", "large", "medium-large", "medium-small", "medium", "small"]),
|
|
235
237
|
"subtitle": PropTypes.string,
|
|
236
238
|
"timeout": PropTypes.number,
|
|
237
|
-
"title": PropTypes.node
|
|
239
|
+
"title": PropTypes.node,
|
|
240
|
+
"topModalOverride": PropTypes.bool
|
|
238
241
|
};
|
|
239
242
|
export { Dialog };
|
|
240
243
|
export default Dialog;
|
|
@@ -52,5 +52,5 @@ export interface DialogFullScreenProps extends ModalProps {
|
|
|
52
52
|
/** A custom close event handler */
|
|
53
53
|
onCancel?: (ev: React.KeyboardEvent<HTMLElement> | React.MouseEvent<HTMLButtonElement>) => void;
|
|
54
54
|
}
|
|
55
|
-
export declare const DialogFullScreen: ({ "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disableAutoFocus, focusFirstElement, bespokeFocusTrap, open, children, title, subtitle, pagesStyling, headerChildren, showCloseIcon, disableContentPadding, disableEscKey, onCancel, contentRef, help, role, focusableContainers, focusableSelectors, ...rest }: DialogFullScreenProps) => React.JSX.Element;
|
|
55
|
+
export declare const DialogFullScreen: ({ "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disableAutoFocus, focusFirstElement, bespokeFocusTrap, open, children, title, subtitle, pagesStyling, headerChildren, showCloseIcon, disableContentPadding, disableEscKey, onCancel, contentRef, help, role, focusableContainers, focusableSelectors, topModalOverride, ...rest }: DialogFullScreenProps) => React.JSX.Element;
|
|
56
56
|
export default DialogFullScreen;
|
|
@@ -35,6 +35,7 @@ export const DialogFullScreen = ({
|
|
|
35
35
|
role = "dialog",
|
|
36
36
|
focusableContainers,
|
|
37
37
|
focusableSelectors,
|
|
38
|
+
topModalOverride,
|
|
38
39
|
...rest
|
|
39
40
|
}) => {
|
|
40
41
|
const locale = useLocale();
|
|
@@ -83,7 +84,8 @@ export const DialogFullScreen = ({
|
|
|
83
84
|
return /*#__PURE__*/React.createElement(Modal, _extends({
|
|
84
85
|
open: open,
|
|
85
86
|
onCancel: onCancel,
|
|
86
|
-
disableEscKey: disableEscKey
|
|
87
|
+
disableEscKey: disableEscKey,
|
|
88
|
+
topModalOverride: topModalOverride
|
|
87
89
|
}, componentTags), /*#__PURE__*/React.createElement(FocusTrap, {
|
|
88
90
|
autoFocus: !disableAutoFocus,
|
|
89
91
|
focusFirstElement: focusFirstElement,
|
|
@@ -2,12 +2,13 @@ declare type SetTriggerRefocusFlag = (boolean: boolean) => void;
|
|
|
2
2
|
export declare type ModalList = {
|
|
3
3
|
modal: HTMLElement;
|
|
4
4
|
setTriggerRefocusFlag?: SetTriggerRefocusFlag;
|
|
5
|
+
topModalOverride?: boolean;
|
|
5
6
|
}[];
|
|
6
7
|
declare class ModalManagerInstance {
|
|
7
8
|
private modalList;
|
|
8
9
|
constructor();
|
|
9
10
|
private getTopModal;
|
|
10
|
-
addModal: (modal: HTMLElement | null, setTriggerRefocusFlag?: SetTriggerRefocusFlag) => void;
|
|
11
|
+
addModal: (modal: HTMLElement | null, setTriggerRefocusFlag?: SetTriggerRefocusFlag, topModalOverride?: boolean) => void;
|
|
11
12
|
isTopmost(modal: HTMLElement | null): boolean;
|
|
12
13
|
removeModal(modal: HTMLElement | null, triggerRefocusOnClose?: boolean): void;
|
|
13
14
|
clearList(): void;
|
|
@@ -8,20 +8,22 @@ let ModalManagerInstance = /*#__PURE__*/function () {
|
|
|
8
8
|
function ModalManagerInstance() {
|
|
9
9
|
_classCallCheck(this, ModalManagerInstance);
|
|
10
10
|
_defineProperty(this, "modalList", void 0);
|
|
11
|
-
_defineProperty(this, "addModal", (modal, setTriggerRefocusFlag) => {
|
|
11
|
+
_defineProperty(this, "addModal", (modal, setTriggerRefocusFlag, topModalOverride) => {
|
|
12
12
|
if (!modal) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
const {
|
|
16
16
|
modal: topModal,
|
|
17
|
-
setTriggerRefocusFlag: setTrapFlag
|
|
17
|
+
setTriggerRefocusFlag: setTrapFlag,
|
|
18
|
+
topModalOverride: topOverridden
|
|
18
19
|
} = this.getTopModal();
|
|
19
|
-
if (topModal && setTrapFlag) {
|
|
20
|
+
if (!topOverridden && topModal && setTrapFlag) {
|
|
20
21
|
setTrapFlag(false);
|
|
21
22
|
}
|
|
22
23
|
this.modalList.push({
|
|
23
24
|
modal,
|
|
24
|
-
setTriggerRefocusFlag
|
|
25
|
+
setTriggerRefocusFlag,
|
|
26
|
+
topModalOverride
|
|
25
27
|
});
|
|
26
28
|
this.callTopModalSetters();
|
|
27
29
|
});
|
|
@@ -38,7 +40,8 @@ let ModalManagerInstance = /*#__PURE__*/function () {
|
|
|
38
40
|
if (!this.modalList.length) {
|
|
39
41
|
return {};
|
|
40
42
|
}
|
|
41
|
-
|
|
43
|
+
const topModalOverride = this.modalList.slice().reverse().find(modal => modal.topModalOverride);
|
|
44
|
+
return topModalOverride || this.modalList[this.modalList.length - 1];
|
|
42
45
|
}
|
|
43
46
|
}, {
|
|
44
47
|
key: "isTopmost",
|
|
@@ -25,6 +25,8 @@ export interface ModalProps extends TagProps {
|
|
|
25
25
|
open: boolean;
|
|
26
26
|
/** Transition time */
|
|
27
27
|
timeout?: number;
|
|
28
|
+
/** Manually override the internal modal stacking order to set this as top */
|
|
29
|
+
topModalOverride?: boolean;
|
|
28
30
|
}
|
|
29
|
-
declare const Modal: ({ children, open, onCancel, disableEscKey, disableClose, enableBackgroundUI, timeout, ...rest }: ModalProps) => React.JSX.Element;
|
|
31
|
+
declare const Modal: ({ children, open, onCancel, disableEscKey, disableClose, enableBackgroundUI, timeout, topModalOverride, ...rest }: ModalProps) => React.JSX.Element;
|
|
30
32
|
export default Modal;
|
|
@@ -16,6 +16,7 @@ const Modal = ({
|
|
|
16
16
|
disableClose,
|
|
17
17
|
enableBackgroundUI = false,
|
|
18
18
|
timeout = 300,
|
|
19
|
+
topModalOverride,
|
|
19
20
|
...rest
|
|
20
21
|
}) => {
|
|
21
22
|
const ref = useRef(null);
|
|
@@ -54,7 +55,8 @@ const Modal = ({
|
|
|
54
55
|
open,
|
|
55
56
|
closeModal,
|
|
56
57
|
modalRef: ref,
|
|
57
|
-
setTriggerRefocusFlag
|
|
58
|
+
setTriggerRefocusFlag,
|
|
59
|
+
topModalOverride
|
|
58
60
|
});
|
|
59
61
|
let background;
|
|
60
62
|
let content;
|
|
@@ -71,6 +73,7 @@ const Modal = ({
|
|
|
71
73
|
"data-state": open ? "open" : "closed",
|
|
72
74
|
transitionName: "modal",
|
|
73
75
|
transitionTime: timeout,
|
|
76
|
+
topModalOverride: topModalOverride,
|
|
74
77
|
ref: ref
|
|
75
78
|
}, rest), /*#__PURE__*/React.createElement(TransitionGroup, null, background && /*#__PURE__*/React.createElement(CSSTransition, {
|
|
76
79
|
nodeRef: backgroundNodeRef,
|
|
@@ -3,5 +3,7 @@ declare type TransitionProps = {
|
|
|
3
3
|
transitionTime: number;
|
|
4
4
|
};
|
|
5
5
|
declare const StyledModalBackground: import("styled-components").StyledComponent<"div", any, TransitionProps, never>;
|
|
6
|
-
declare const StyledModal: import("styled-components").StyledComponent<"div", any, TransitionProps
|
|
6
|
+
declare const StyledModal: import("styled-components").StyledComponent<"div", any, TransitionProps & {
|
|
7
|
+
topModalOverride?: boolean | undefined;
|
|
8
|
+
}, never>;
|
|
7
9
|
export { StyledModal, StyledModalBackground };
|
|
@@ -37,8 +37,9 @@ const StyledModalBackground = styled.div`
|
|
|
37
37
|
const StyledModal = styled.div`
|
|
38
38
|
position: absolute;
|
|
39
39
|
z-index: ${({
|
|
40
|
-
theme
|
|
41
|
-
|
|
40
|
+
theme,
|
|
41
|
+
topModalOverride
|
|
42
|
+
}) => `${topModalOverride ? theme.zIndex.notification : theme.zIndex.modal}`};
|
|
42
43
|
|
|
43
44
|
${({
|
|
44
45
|
transitionName,
|
|
@@ -77,6 +77,7 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
77
77
|
id: inputId.current,
|
|
78
78
|
label
|
|
79
79
|
});
|
|
80
|
+
const focusTimer = useRef(null);
|
|
80
81
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
81
82
|
deprecateInputRefWarnTriggered = true;
|
|
82
83
|
Logger.deprecate("The `inputRef` prop in `Filterable Select` component is deprecated and will soon be removed. Please use `ref` instead.");
|
|
@@ -306,18 +307,26 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
306
307
|
function handleTextboxFocus(event) {
|
|
307
308
|
const triggerFocus = () => onFocus?.(event);
|
|
308
309
|
if (openOnFocus) {
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
if (focusTimer.current) {
|
|
311
|
+
clearTimeout(focusTimer.current);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
315
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
316
|
+
focusTimer.current = setTimeout(() => {
|
|
317
|
+
setOpen(isAlreadyOpen => {
|
|
318
|
+
if (isAlreadyOpen) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
if (onFocus && !isInputFocused.current) {
|
|
322
|
+
triggerFocus();
|
|
323
|
+
isInputFocused.current = true;
|
|
324
|
+
}
|
|
325
|
+
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
311
328
|
return true;
|
|
312
|
-
}
|
|
313
|
-
if (onFocus && !isInputFocused.current) {
|
|
314
|
-
triggerFocus();
|
|
315
|
-
isInputFocused.current = true;
|
|
316
|
-
}
|
|
317
|
-
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
318
|
-
return false;
|
|
319
|
-
}
|
|
320
|
-
return true;
|
|
329
|
+
});
|
|
321
330
|
});
|
|
322
331
|
} else if (onFocus && !isInputFocused.current) {
|
|
323
332
|
triggerFocus();
|
|
@@ -82,6 +82,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
82
82
|
id: inputId.current,
|
|
83
83
|
label
|
|
84
84
|
});
|
|
85
|
+
const focusTimer = useRef(null);
|
|
85
86
|
const actualValue = isControlled.current ? value : selectedValue;
|
|
86
87
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
87
88
|
deprecateInputRefWarnTriggered = true;
|
|
@@ -312,21 +313,29 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
312
313
|
function handleTextboxFocus(event) {
|
|
313
314
|
const triggerFocus = () => onFocus?.(event);
|
|
314
315
|
if (openOnFocus) {
|
|
315
|
-
|
|
316
|
-
|
|
316
|
+
if (focusTimer.current) {
|
|
317
|
+
clearTimeout(focusTimer.current);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
321
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
322
|
+
focusTimer.current = setTimeout(() => {
|
|
323
|
+
setOpenState(isAlreadyOpen => {
|
|
324
|
+
if (isAlreadyOpen) {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
if (onOpen) {
|
|
328
|
+
onOpen();
|
|
329
|
+
}
|
|
330
|
+
if (onFocus && !isInputFocused.current) {
|
|
331
|
+
triggerFocus();
|
|
332
|
+
isInputFocused.current = true;
|
|
333
|
+
}
|
|
334
|
+
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
317
337
|
return true;
|
|
318
|
-
}
|
|
319
|
-
if (onOpen) {
|
|
320
|
-
onOpen();
|
|
321
|
-
}
|
|
322
|
-
if (onFocus && !isInputFocused.current) {
|
|
323
|
-
triggerFocus();
|
|
324
|
-
isInputFocused.current = true;
|
|
325
|
-
}
|
|
326
|
-
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
return true;
|
|
338
|
+
});
|
|
330
339
|
});
|
|
331
340
|
} else if (onFocus && !isInputFocused.current) {
|
|
332
341
|
triggerFocus();
|
|
@@ -72,6 +72,7 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
72
72
|
id: inputId.current,
|
|
73
73
|
label
|
|
74
74
|
});
|
|
75
|
+
const focusTimer = useRef(null);
|
|
75
76
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
76
77
|
deprecateInputRefWarnTriggered = true;
|
|
77
78
|
Logger.deprecate("The `inputRef` prop in `Simple Select` component is deprecated and will soon be removed. Please use `ref` instead.");
|
|
@@ -235,14 +236,22 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
235
236
|
return;
|
|
236
237
|
}
|
|
237
238
|
if (openOnFocus) {
|
|
238
|
-
|
|
239
|
-
|
|
239
|
+
if (focusTimer.current) {
|
|
240
|
+
clearTimeout(focusTimer.current);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
244
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
245
|
+
focusTimer.current = setTimeout(() => {
|
|
246
|
+
setOpenState(isAlreadyOpen => {
|
|
247
|
+
if (isAlreadyOpen) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
if (onOpen) {
|
|
251
|
+
onOpen();
|
|
252
|
+
}
|
|
240
253
|
return true;
|
|
241
|
-
}
|
|
242
|
-
if (onOpen) {
|
|
243
|
-
onOpen();
|
|
244
|
-
}
|
|
245
|
-
return true;
|
|
254
|
+
});
|
|
246
255
|
});
|
|
247
256
|
}
|
|
248
257
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { PaddingProps, WidthProps } from "styled-system";
|
|
3
|
+
import { ModalProps } from "../modal";
|
|
3
4
|
import { TagProps } from "../../__internal__/utils/helpers/tags/tags";
|
|
4
5
|
declare type CustomRefObject<T> = {
|
|
5
6
|
current?: T | null;
|
|
@@ -8,7 +9,7 @@ export interface SidebarContextProps {
|
|
|
8
9
|
isInSidebar?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export declare const SidebarContext: React.Context<SidebarContextProps>;
|
|
11
|
-
export interface SidebarProps extends PaddingProps, TagProps, WidthProps {
|
|
12
|
+
export interface SidebarProps extends PaddingProps, TagProps, WidthProps, Pick<ModalProps, "topModalOverride"> {
|
|
12
13
|
/** Prop to specify the aria-describedby property of the component */
|
|
13
14
|
"aria-describedby"?: string;
|
|
14
15
|
/**
|
|
@@ -37,6 +37,7 @@ const Sidebar = /*#__PURE__*/React.forwardRef(({
|
|
|
37
37
|
focusableSelectors,
|
|
38
38
|
width,
|
|
39
39
|
headerPadding = {},
|
|
40
|
+
topModalOverride,
|
|
40
41
|
...rest
|
|
41
42
|
}, ref) => {
|
|
42
43
|
const locale = useLocale();
|
|
@@ -102,7 +103,8 @@ const Sidebar = /*#__PURE__*/React.forwardRef(({
|
|
|
102
103
|
open: open,
|
|
103
104
|
onCancel: onCancel,
|
|
104
105
|
disableEscKey: disableEscKey,
|
|
105
|
-
enableBackgroundUI: enableBackgroundUI
|
|
106
|
+
enableBackgroundUI: enableBackgroundUI,
|
|
107
|
+
topModalOverride: topModalOverride
|
|
106
108
|
}, componentTags), enableBackgroundUI ? sidebar : /*#__PURE__*/React.createElement(FocusTrap, {
|
|
107
109
|
wrapperRef: sidebarRef,
|
|
108
110
|
isOpen: open,
|
|
@@ -460,6 +462,7 @@ Sidebar.propTypes = {
|
|
|
460
462
|
}), PropTypes.string]),
|
|
461
463
|
"role": PropTypes.string,
|
|
462
464
|
"size": PropTypes.oneOf(["extra-large", "extra-small", "large", "medium-large", "medium-small", "medium", "small"]),
|
|
465
|
+
"topModalOverride": PropTypes.bool,
|
|
463
466
|
"width": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.number, PropTypes.shape({
|
|
464
467
|
"__@iterator": PropTypes.func.isRequired,
|
|
465
468
|
"anchor": PropTypes.func.isRequired,
|
|
@@ -5,6 +5,7 @@ declare type UseModalManagerArgs = {
|
|
|
5
5
|
modalRef: React.RefObject<HTMLElement>;
|
|
6
6
|
setTriggerRefocusFlag?: (flag: boolean) => void;
|
|
7
7
|
triggerRefocusOnClose?: boolean;
|
|
8
|
+
topModalOverride?: boolean;
|
|
8
9
|
};
|
|
9
|
-
declare const useModalManager: ({ open, closeModal, modalRef, setTriggerRefocusFlag, triggerRefocusOnClose, }: UseModalManagerArgs) => void;
|
|
10
|
+
declare const useModalManager: ({ open, closeModal, modalRef, setTriggerRefocusFlag, triggerRefocusOnClose, topModalOverride, }: UseModalManagerArgs) => void;
|
|
10
11
|
export default useModalManager;
|
|
@@ -5,7 +5,8 @@ const useModalManager = ({
|
|
|
5
5
|
closeModal,
|
|
6
6
|
modalRef,
|
|
7
7
|
setTriggerRefocusFlag,
|
|
8
|
-
triggerRefocusOnClose = true
|
|
8
|
+
triggerRefocusOnClose = true,
|
|
9
|
+
topModalOverride = false
|
|
9
10
|
}) => {
|
|
10
11
|
const listenerAdded = useRef(false);
|
|
11
12
|
const modalRegistered = useRef(false);
|
|
@@ -43,10 +44,10 @@ const useModalManager = ({
|
|
|
43
44
|
const registerModal = useCallback(ref => {
|
|
44
45
|
/* istanbul ignore else */
|
|
45
46
|
if (!modalRegistered.current) {
|
|
46
|
-
ModalManager.addModal(ref, setTriggerRefocusFlag);
|
|
47
|
+
ModalManager.addModal(ref, setTriggerRefocusFlag, topModalOverride);
|
|
47
48
|
modalRegistered.current = true;
|
|
48
49
|
}
|
|
49
|
-
}, [setTriggerRefocusFlag]);
|
|
50
|
+
}, [setTriggerRefocusFlag, topModalOverride]);
|
|
50
51
|
const unregisterModal = useCallback(ref => {
|
|
51
52
|
if (modalRegistered.current) {
|
|
52
53
|
ModalManager.removeModal(ref, triggerRefocusOnClose);
|
|
@@ -71,6 +71,7 @@ const HiddenCheckableInput = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
71
71
|
"aria-describedby": combinedDescription,
|
|
72
72
|
"aria-labelledby": ariaLabelledBy,
|
|
73
73
|
autoFocus: autoFocus,
|
|
74
|
+
"data-has-autofocus": autoFocus ? true : undefined,
|
|
74
75
|
"aria-checked": checked,
|
|
75
76
|
checked: checked,
|
|
76
77
|
name: name,
|
|
@@ -99,7 +99,8 @@ const onTabGuardFocus = (trapWrappers, focusableSelectors, position) => guardWra
|
|
|
99
99
|
const isTop = position === "top";
|
|
100
100
|
const currentIndex = trapWrappers.indexOf(guardWrapperRef);
|
|
101
101
|
let index = currentIndex;
|
|
102
|
-
let nextWrapper
|
|
102
|
+
let nextWrapper;
|
|
103
|
+
let allFocusableElementsInNextWrapper;
|
|
103
104
|
do {
|
|
104
105
|
index += isTop ? -1 : 1;
|
|
105
106
|
if (index < 0) {
|
|
@@ -109,7 +110,7 @@ const onTabGuardFocus = (trapWrappers, focusableSelectors, position) => guardWra
|
|
|
109
110
|
index -= trapWrappers.length;
|
|
110
111
|
}
|
|
111
112
|
nextWrapper = trapWrappers[index];
|
|
112
|
-
allFocusableElementsInNextWrapper = nextWrapper?.current?.querySelectorAll(focusableSelectors || defaultFocusableSelectors);
|
|
113
|
+
allFocusableElementsInNextWrapper = Array.from(nextWrapper?.current?.querySelectorAll(focusableSelectors || defaultFocusableSelectors) || /* istanbul ignore next */[]).filter(el => Number(el.tabIndex) !== -1);
|
|
113
114
|
} while (index !== currentIndex && !allFocusableElementsInNextWrapper?.length);
|
|
114
115
|
const toFocus = allFocusableElementsInNextWrapper?.[isTop ? allFocusableElementsInNextWrapper.length - 1 : 0];
|
|
115
116
|
if (isRadio(toFocus)) {
|
|
@@ -84,16 +84,6 @@ const FocusTrap = ({
|
|
|
84
84
|
}, [additionalWrapperRefs, onTabGuardTopFocus, onTabGuardBottomFocus]);
|
|
85
85
|
const shouldSetFocus = autoFocus && isOpen && isAnimationComplete;
|
|
86
86
|
const prevShouldSetFocus = (0, _usePrevious.default)(shouldSetFocus);
|
|
87
|
-
(0, _react.useEffect)(() => {
|
|
88
|
-
if (shouldSetFocus && !prevShouldSetFocus) {
|
|
89
|
-
const candidateFirstElement = focusFirstElement && "current" in focusFirstElement ? focusFirstElement.current : focusFirstElement;
|
|
90
|
-
const elementToFocus = candidateFirstElement || wrapperRef.current;
|
|
91
|
-
// istanbul ignore else
|
|
92
|
-
if (elementToFocus) {
|
|
93
|
-
(0, _focusTrapUtils.setElementFocus)(elementToFocus);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}, [shouldSetFocus, prevShouldSetFocus, focusFirstElement, wrapperRef]);
|
|
97
87
|
const getFocusableElements = (0, _react.useCallback)(selector => {
|
|
98
88
|
const elements = [];
|
|
99
89
|
trapWrappers.forEach(ref => {
|
|
@@ -104,6 +94,17 @@ const FocusTrap = ({
|
|
|
104
94
|
});
|
|
105
95
|
return elements;
|
|
106
96
|
}, [trapWrappers]);
|
|
97
|
+
(0, _react.useEffect)(() => {
|
|
98
|
+
if (shouldSetFocus && !prevShouldSetFocus) {
|
|
99
|
+
const candidateFirstElement = focusFirstElement && "current" in focusFirstElement ? focusFirstElement.current : focusFirstElement;
|
|
100
|
+
const autoFocusedElement = getFocusableElements(_focusTrapUtils.defaultFocusableSelectors).find(el => el.getAttribute("data-has-autofocus") === "true");
|
|
101
|
+
const elementToFocus = candidateFirstElement || autoFocusedElement || wrapperRef.current;
|
|
102
|
+
// istanbul ignore else
|
|
103
|
+
if (elementToFocus) {
|
|
104
|
+
(0, _focusTrapUtils.setElementFocus)(elementToFocus);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, [shouldSetFocus, prevShouldSetFocus, getFocusableElements, focusFirstElement, wrapperRef]);
|
|
107
108
|
(0, _react.useEffect)(() => {
|
|
108
109
|
const trapFn = ev => {
|
|
109
110
|
// block focus trap from working if it's not the topmost trap, or is currently closed
|
|
@@ -140,6 +140,7 @@ const Input = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
140
140
|
}
|
|
141
141
|
const combinedDescription = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
|
|
142
142
|
return /*#__PURE__*/_react.default.createElement(_input.default, _extends({}, rest, {
|
|
143
|
+
"data-has-autofocus": autoFocus ? true : undefined,
|
|
143
144
|
"aria-describedby": combinedDescription,
|
|
144
145
|
"aria-labelledby": ariaLabelledBy,
|
|
145
146
|
align: align,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { DialogProps } from "../dialog";
|
|
3
|
-
export declare const Alert: ({ children, size, ...rest }: DialogProps) => React.JSX.Element;
|
|
3
|
+
export declare const Alert: ({ children, size, topModalOverride, ...rest }: DialogProps) => React.JSX.Element;
|
|
4
4
|
export default Alert;
|
|
@@ -12,11 +12,13 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function
|
|
|
12
12
|
const Alert = ({
|
|
13
13
|
children,
|
|
14
14
|
size = "extra-small",
|
|
15
|
+
topModalOverride,
|
|
15
16
|
...rest
|
|
16
17
|
}) => /*#__PURE__*/_react.default.createElement(_dialog.default, _extends({
|
|
17
18
|
"data-component": "alert",
|
|
18
19
|
role: "alertdialog",
|
|
19
|
-
size: size
|
|
20
|
+
size: size,
|
|
21
|
+
topModalOverride: topModalOverride
|
|
20
22
|
}, rest), children);
|
|
21
23
|
exports.Alert = Alert;
|
|
22
24
|
var _default = Alert;
|
|
@@ -38,5 +38,5 @@ export interface ConfirmProps extends Omit<DialogProps, "className" | "disableFo
|
|
|
38
38
|
/** A custom event handler when a confirmation takes place */
|
|
39
39
|
onConfirm: (ev: React.MouseEvent<HTMLButtonElement>) => void;
|
|
40
40
|
}
|
|
41
|
-
export declare const Confirm: ({ "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, open, children, cancelButtonDestructive, confirmButtonDestructive, cancelButtonType, confirmButtonType, cancelButtonIconType, cancelButtonIconPosition, confirmButtonIconType, confirmButtonIconPosition, cancelButtonDataProps, confirmButtonDataProps, cancelLabel, onCancel, disableCancel, onConfirm, isLoadingConfirm, disableConfirm, confirmLabel, iconType, subtitle, title, size, showCloseIcon, ...rest }: ConfirmProps) => React.JSX.Element;
|
|
41
|
+
export declare const Confirm: ({ "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, open, children, cancelButtonDestructive, confirmButtonDestructive, cancelButtonType, confirmButtonType, cancelButtonIconType, cancelButtonIconPosition, confirmButtonIconType, confirmButtonIconPosition, cancelButtonDataProps, confirmButtonDataProps, cancelLabel, onCancel, disableCancel, onConfirm, isLoadingConfirm, disableConfirm, confirmLabel, iconType, subtitle, title, size, showCloseIcon, topModalOverride, ...rest }: ConfirmProps) => React.JSX.Element;
|
|
42
42
|
export default Confirm;
|
|
@@ -47,6 +47,7 @@ const Confirm = ({
|
|
|
47
47
|
title,
|
|
48
48
|
size = "extra-small",
|
|
49
49
|
showCloseIcon = false,
|
|
50
|
+
topModalOverride,
|
|
50
51
|
...rest
|
|
51
52
|
}) => {
|
|
52
53
|
const l = (0, _useLocale.default)();
|
|
@@ -123,7 +124,8 @@ const Confirm = ({
|
|
|
123
124
|
"data-component": "confirm",
|
|
124
125
|
role: "alertdialog",
|
|
125
126
|
size: size,
|
|
126
|
-
showCloseIcon: showCloseIcon
|
|
127
|
+
showCloseIcon: showCloseIcon,
|
|
128
|
+
topModalOverride: topModalOverride
|
|
127
129
|
}, ariaProps, rest), children, /*#__PURE__*/_react.default.createElement(_confirm.StyledConfirmButtons, null, renderCancelButton(), renderConfirmButton()));
|
|
128
130
|
};
|
|
129
131
|
exports.Confirm = Confirm;
|
|
@@ -43,6 +43,7 @@ const Dialog = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
|
43
43
|
role = "dialog",
|
|
44
44
|
contentPadding = {},
|
|
45
45
|
focusableContainers,
|
|
46
|
+
topModalOverride,
|
|
46
47
|
...rest
|
|
47
48
|
}, ref) => {
|
|
48
49
|
const locale = (0, _useLocale.default)();
|
|
@@ -163,7 +164,8 @@ const Dialog = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
|
163
164
|
onCancel: onCancel,
|
|
164
165
|
disableEscKey: disableEscKey,
|
|
165
166
|
disableClose: disableClose,
|
|
166
|
-
className: className ? `${className} carbon-dialog` : "carbon-dialog"
|
|
167
|
+
className: className ? `${className} carbon-dialog` : "carbon-dialog",
|
|
168
|
+
topModalOverride: topModalOverride
|
|
167
169
|
}, componentTags), /*#__PURE__*/_react.default.createElement(_focusTrap.default, {
|
|
168
170
|
autoFocus: !disableAutoFocus,
|
|
169
171
|
focusFirstElement: focusFirstElement,
|
|
@@ -244,7 +246,8 @@ Dialog.propTypes = {
|
|
|
244
246
|
"size": _propTypes.default.oneOf(["auto", "extra-large", "extra-small", "large", "medium-large", "medium-small", "medium", "small"]),
|
|
245
247
|
"subtitle": _propTypes.default.string,
|
|
246
248
|
"timeout": _propTypes.default.number,
|
|
247
|
-
"title": _propTypes.default.node
|
|
249
|
+
"title": _propTypes.default.node,
|
|
250
|
+
"topModalOverride": _propTypes.default.bool
|
|
248
251
|
};
|
|
249
252
|
var _default = Dialog;
|
|
250
253
|
exports.default = _default;
|
|
@@ -52,5 +52,5 @@ export interface DialogFullScreenProps extends ModalProps {
|
|
|
52
52
|
/** A custom close event handler */
|
|
53
53
|
onCancel?: (ev: React.KeyboardEvent<HTMLElement> | React.MouseEvent<HTMLButtonElement>) => void;
|
|
54
54
|
}
|
|
55
|
-
export declare const DialogFullScreen: ({ "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disableAutoFocus, focusFirstElement, bespokeFocusTrap, open, children, title, subtitle, pagesStyling, headerChildren, showCloseIcon, disableContentPadding, disableEscKey, onCancel, contentRef, help, role, focusableContainers, focusableSelectors, ...rest }: DialogFullScreenProps) => React.JSX.Element;
|
|
55
|
+
export declare const DialogFullScreen: ({ "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disableAutoFocus, focusFirstElement, bespokeFocusTrap, open, children, title, subtitle, pagesStyling, headerChildren, showCloseIcon, disableContentPadding, disableEscKey, onCancel, contentRef, help, role, focusableContainers, focusableSelectors, topModalOverride, ...rest }: DialogFullScreenProps) => React.JSX.Element;
|
|
56
56
|
export default DialogFullScreen;
|
|
@@ -44,6 +44,7 @@ const DialogFullScreen = ({
|
|
|
44
44
|
role = "dialog",
|
|
45
45
|
focusableContainers,
|
|
46
46
|
focusableSelectors,
|
|
47
|
+
topModalOverride,
|
|
47
48
|
...rest
|
|
48
49
|
}) => {
|
|
49
50
|
const locale = (0, _useLocale.default)();
|
|
@@ -92,7 +93,8 @@ const DialogFullScreen = ({
|
|
|
92
93
|
return /*#__PURE__*/_react.default.createElement(_modal.default, _extends({
|
|
93
94
|
open: open,
|
|
94
95
|
onCancel: onCancel,
|
|
95
|
-
disableEscKey: disableEscKey
|
|
96
|
+
disableEscKey: disableEscKey,
|
|
97
|
+
topModalOverride: topModalOverride
|
|
96
98
|
}, componentTags), /*#__PURE__*/_react.default.createElement(_focusTrap.default, {
|
|
97
99
|
autoFocus: !disableAutoFocus,
|
|
98
100
|
focusFirstElement: focusFirstElement,
|
|
@@ -2,12 +2,13 @@ declare type SetTriggerRefocusFlag = (boolean: boolean) => void;
|
|
|
2
2
|
export declare type ModalList = {
|
|
3
3
|
modal: HTMLElement;
|
|
4
4
|
setTriggerRefocusFlag?: SetTriggerRefocusFlag;
|
|
5
|
+
topModalOverride?: boolean;
|
|
5
6
|
}[];
|
|
6
7
|
declare class ModalManagerInstance {
|
|
7
8
|
private modalList;
|
|
8
9
|
constructor();
|
|
9
10
|
private getTopModal;
|
|
10
|
-
addModal: (modal: HTMLElement | null, setTriggerRefocusFlag?: SetTriggerRefocusFlag) => void;
|
|
11
|
+
addModal: (modal: HTMLElement | null, setTriggerRefocusFlag?: SetTriggerRefocusFlag, topModalOverride?: boolean) => void;
|
|
11
12
|
isTopmost(modal: HTMLElement | null): boolean;
|
|
12
13
|
removeModal(modal: HTMLElement | null, triggerRefocusOnClose?: boolean): void;
|
|
13
14
|
clearList(): void;
|
|
@@ -14,20 +14,22 @@ let ModalManagerInstance = /*#__PURE__*/function () {
|
|
|
14
14
|
function ModalManagerInstance() {
|
|
15
15
|
_classCallCheck(this, ModalManagerInstance);
|
|
16
16
|
_defineProperty(this, "modalList", void 0);
|
|
17
|
-
_defineProperty(this, "addModal", (modal, setTriggerRefocusFlag) => {
|
|
17
|
+
_defineProperty(this, "addModal", (modal, setTriggerRefocusFlag, topModalOverride) => {
|
|
18
18
|
if (!modal) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
const {
|
|
22
22
|
modal: topModal,
|
|
23
|
-
setTriggerRefocusFlag: setTrapFlag
|
|
23
|
+
setTriggerRefocusFlag: setTrapFlag,
|
|
24
|
+
topModalOverride: topOverridden
|
|
24
25
|
} = this.getTopModal();
|
|
25
|
-
if (topModal && setTrapFlag) {
|
|
26
|
+
if (!topOverridden && topModal && setTrapFlag) {
|
|
26
27
|
setTrapFlag(false);
|
|
27
28
|
}
|
|
28
29
|
this.modalList.push({
|
|
29
30
|
modal,
|
|
30
|
-
setTriggerRefocusFlag
|
|
31
|
+
setTriggerRefocusFlag,
|
|
32
|
+
topModalOverride
|
|
31
33
|
});
|
|
32
34
|
this.callTopModalSetters();
|
|
33
35
|
});
|
|
@@ -44,7 +46,8 @@ let ModalManagerInstance = /*#__PURE__*/function () {
|
|
|
44
46
|
if (!this.modalList.length) {
|
|
45
47
|
return {};
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
const topModalOverride = this.modalList.slice().reverse().find(modal => modal.topModalOverride);
|
|
50
|
+
return topModalOverride || this.modalList[this.modalList.length - 1];
|
|
48
51
|
}
|
|
49
52
|
}, {
|
|
50
53
|
key: "isTopmost",
|
|
@@ -25,6 +25,8 @@ export interface ModalProps extends TagProps {
|
|
|
25
25
|
open: boolean;
|
|
26
26
|
/** Transition time */
|
|
27
27
|
timeout?: number;
|
|
28
|
+
/** Manually override the internal modal stacking order to set this as top */
|
|
29
|
+
topModalOverride?: boolean;
|
|
28
30
|
}
|
|
29
|
-
declare const Modal: ({ children, open, onCancel, disableEscKey, disableClose, enableBackgroundUI, timeout, ...rest }: ModalProps) => React.JSX.Element;
|
|
31
|
+
declare const Modal: ({ children, open, onCancel, disableEscKey, disableClose, enableBackgroundUI, timeout, topModalOverride, ...rest }: ModalProps) => React.JSX.Element;
|
|
30
32
|
export default Modal;
|
|
@@ -26,6 +26,7 @@ const Modal = ({
|
|
|
26
26
|
disableClose,
|
|
27
27
|
enableBackgroundUI = false,
|
|
28
28
|
timeout = 300,
|
|
29
|
+
topModalOverride,
|
|
29
30
|
...rest
|
|
30
31
|
}) => {
|
|
31
32
|
const ref = (0, _react.useRef)(null);
|
|
@@ -64,7 +65,8 @@ const Modal = ({
|
|
|
64
65
|
open,
|
|
65
66
|
closeModal,
|
|
66
67
|
modalRef: ref,
|
|
67
|
-
setTriggerRefocusFlag
|
|
68
|
+
setTriggerRefocusFlag,
|
|
69
|
+
topModalOverride
|
|
68
70
|
});
|
|
69
71
|
let background;
|
|
70
72
|
let content;
|
|
@@ -81,6 +83,7 @@ const Modal = ({
|
|
|
81
83
|
"data-state": open ? "open" : "closed",
|
|
82
84
|
transitionName: "modal",
|
|
83
85
|
transitionTime: timeout,
|
|
86
|
+
topModalOverride: topModalOverride,
|
|
84
87
|
ref: ref
|
|
85
88
|
}, rest), /*#__PURE__*/_react.default.createElement(_reactTransitionGroup.TransitionGroup, null, background && /*#__PURE__*/_react.default.createElement(_reactTransitionGroup.CSSTransition, {
|
|
86
89
|
nodeRef: backgroundNodeRef,
|
|
@@ -3,5 +3,7 @@ declare type TransitionProps = {
|
|
|
3
3
|
transitionTime: number;
|
|
4
4
|
};
|
|
5
5
|
declare const StyledModalBackground: import("styled-components").StyledComponent<"div", any, TransitionProps, never>;
|
|
6
|
-
declare const StyledModal: import("styled-components").StyledComponent<"div", any, TransitionProps
|
|
6
|
+
declare const StyledModal: import("styled-components").StyledComponent<"div", any, TransitionProps & {
|
|
7
|
+
topModalOverride?: boolean | undefined;
|
|
8
|
+
}, never>;
|
|
7
9
|
export { StyledModal, StyledModalBackground };
|
|
@@ -47,8 +47,9 @@ exports.StyledModalBackground = StyledModalBackground;
|
|
|
47
47
|
const StyledModal = _styledComponents.default.div`
|
|
48
48
|
position: absolute;
|
|
49
49
|
z-index: ${({
|
|
50
|
-
theme
|
|
51
|
-
|
|
50
|
+
theme,
|
|
51
|
+
topModalOverride
|
|
52
|
+
}) => `${topModalOverride ? theme.zIndex.notification : theme.zIndex.modal}`};
|
|
52
53
|
|
|
53
54
|
${({
|
|
54
55
|
transitionName,
|
|
@@ -86,6 +86,7 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
86
86
|
id: inputId.current,
|
|
87
87
|
label
|
|
88
88
|
});
|
|
89
|
+
const focusTimer = (0, _react.useRef)(null);
|
|
89
90
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
90
91
|
deprecateInputRefWarnTriggered = true;
|
|
91
92
|
_logger.default.deprecate("The `inputRef` prop in `Filterable Select` component is deprecated and will soon be removed. Please use `ref` instead.");
|
|
@@ -315,18 +316,26 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
315
316
|
function handleTextboxFocus(event) {
|
|
316
317
|
const triggerFocus = () => onFocus?.(event);
|
|
317
318
|
if (openOnFocus) {
|
|
318
|
-
|
|
319
|
-
|
|
319
|
+
if (focusTimer.current) {
|
|
320
|
+
clearTimeout(focusTimer.current);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
324
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
325
|
+
focusTimer.current = setTimeout(() => {
|
|
326
|
+
setOpen(isAlreadyOpen => {
|
|
327
|
+
if (isAlreadyOpen) {
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
if (onFocus && !isInputFocused.current) {
|
|
331
|
+
triggerFocus();
|
|
332
|
+
isInputFocused.current = true;
|
|
333
|
+
}
|
|
334
|
+
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
320
337
|
return true;
|
|
321
|
-
}
|
|
322
|
-
if (onFocus && !isInputFocused.current) {
|
|
323
|
-
triggerFocus();
|
|
324
|
-
isInputFocused.current = true;
|
|
325
|
-
}
|
|
326
|
-
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
return true;
|
|
338
|
+
});
|
|
330
339
|
});
|
|
331
340
|
} else if (onFocus && !isInputFocused.current) {
|
|
332
341
|
triggerFocus();
|
|
@@ -91,6 +91,7 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
91
91
|
id: inputId.current,
|
|
92
92
|
label
|
|
93
93
|
});
|
|
94
|
+
const focusTimer = (0, _react.useRef)(null);
|
|
94
95
|
const actualValue = isControlled.current ? value : selectedValue;
|
|
95
96
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
96
97
|
deprecateInputRefWarnTriggered = true;
|
|
@@ -321,21 +322,29 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
321
322
|
function handleTextboxFocus(event) {
|
|
322
323
|
const triggerFocus = () => onFocus?.(event);
|
|
323
324
|
if (openOnFocus) {
|
|
324
|
-
|
|
325
|
-
|
|
325
|
+
if (focusTimer.current) {
|
|
326
|
+
clearTimeout(focusTimer.current);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
330
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
331
|
+
focusTimer.current = setTimeout(() => {
|
|
332
|
+
setOpenState(isAlreadyOpen => {
|
|
333
|
+
if (isAlreadyOpen) {
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
if (onOpen) {
|
|
337
|
+
onOpen();
|
|
338
|
+
}
|
|
339
|
+
if (onFocus && !isInputFocused.current) {
|
|
340
|
+
triggerFocus();
|
|
341
|
+
isInputFocused.current = true;
|
|
342
|
+
}
|
|
343
|
+
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
326
346
|
return true;
|
|
327
|
-
}
|
|
328
|
-
if (onOpen) {
|
|
329
|
-
onOpen();
|
|
330
|
-
}
|
|
331
|
-
if (onFocus && !isInputFocused.current) {
|
|
332
|
-
triggerFocus();
|
|
333
|
-
isInputFocused.current = true;
|
|
334
|
-
}
|
|
335
|
-
if (isMouseDownReported.current && !isMouseDownOnInput.current) {
|
|
336
|
-
return false;
|
|
337
|
-
}
|
|
338
|
-
return true;
|
|
347
|
+
});
|
|
339
348
|
});
|
|
340
349
|
} else if (onFocus && !isInputFocused.current) {
|
|
341
350
|
triggerFocus();
|
|
@@ -81,6 +81,7 @@ const SimpleSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
81
81
|
id: inputId.current,
|
|
82
82
|
label
|
|
83
83
|
});
|
|
84
|
+
const focusTimer = (0, _react.useRef)(null);
|
|
84
85
|
if (!deprecateInputRefWarnTriggered && inputRef) {
|
|
85
86
|
deprecateInputRefWarnTriggered = true;
|
|
86
87
|
_logger.default.deprecate("The `inputRef` prop in `Simple Select` component is deprecated and will soon be removed. Please use `ref` instead.");
|
|
@@ -244,14 +245,22 @@ const SimpleSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
244
245
|
return;
|
|
245
246
|
}
|
|
246
247
|
if (openOnFocus) {
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
if (focusTimer.current) {
|
|
249
|
+
clearTimeout(focusTimer.current);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// we need to use a timeout here as there is a race condition when rendered in a modal
|
|
253
|
+
// whereby the select list isn't visible when the select is auto focused straight away
|
|
254
|
+
focusTimer.current = setTimeout(() => {
|
|
255
|
+
setOpenState(isAlreadyOpen => {
|
|
256
|
+
if (isAlreadyOpen) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
if (onOpen) {
|
|
260
|
+
onOpen();
|
|
261
|
+
}
|
|
249
262
|
return true;
|
|
250
|
-
}
|
|
251
|
-
if (onOpen) {
|
|
252
|
-
onOpen();
|
|
253
|
-
}
|
|
254
|
-
return true;
|
|
263
|
+
});
|
|
255
264
|
});
|
|
256
265
|
}
|
|
257
266
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { PaddingProps, WidthProps } from "styled-system";
|
|
3
|
+
import { ModalProps } from "../modal";
|
|
3
4
|
import { TagProps } from "../../__internal__/utils/helpers/tags/tags";
|
|
4
5
|
declare type CustomRefObject<T> = {
|
|
5
6
|
current?: T | null;
|
|
@@ -8,7 +9,7 @@ export interface SidebarContextProps {
|
|
|
8
9
|
isInSidebar?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export declare const SidebarContext: React.Context<SidebarContextProps>;
|
|
11
|
-
export interface SidebarProps extends PaddingProps, TagProps, WidthProps {
|
|
12
|
+
export interface SidebarProps extends PaddingProps, TagProps, WidthProps, Pick<ModalProps, "topModalOverride"> {
|
|
12
13
|
/** Prop to specify the aria-describedby property of the component */
|
|
13
14
|
"aria-describedby"?: string;
|
|
14
15
|
/**
|
|
@@ -46,6 +46,7 @@ const Sidebar = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
46
46
|
focusableSelectors,
|
|
47
47
|
width,
|
|
48
48
|
headerPadding = {},
|
|
49
|
+
topModalOverride,
|
|
49
50
|
...rest
|
|
50
51
|
}, ref) => {
|
|
51
52
|
const locale = (0, _useLocale.default)();
|
|
@@ -111,7 +112,8 @@ const Sidebar = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
111
112
|
open: open,
|
|
112
113
|
onCancel: onCancel,
|
|
113
114
|
disableEscKey: disableEscKey,
|
|
114
|
-
enableBackgroundUI: enableBackgroundUI
|
|
115
|
+
enableBackgroundUI: enableBackgroundUI,
|
|
116
|
+
topModalOverride: topModalOverride
|
|
115
117
|
}, componentTags), enableBackgroundUI ? sidebar : /*#__PURE__*/_react.default.createElement(_focusTrap.default, {
|
|
116
118
|
wrapperRef: sidebarRef,
|
|
117
119
|
isOpen: open,
|
|
@@ -470,6 +472,7 @@ Sidebar.propTypes = {
|
|
|
470
472
|
}), _propTypes.default.string]),
|
|
471
473
|
"role": _propTypes.default.string,
|
|
472
474
|
"size": _propTypes.default.oneOf(["extra-large", "extra-small", "large", "medium-large", "medium-small", "medium", "small"]),
|
|
475
|
+
"topModalOverride": _propTypes.default.bool,
|
|
473
476
|
"width": _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.oneOf([null]), _propTypes.default.number, _propTypes.default.shape({
|
|
474
477
|
"__@iterator": _propTypes.default.func.isRequired,
|
|
475
478
|
"anchor": _propTypes.default.func.isRequired,
|
|
@@ -62,7 +62,8 @@ const Toast = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
62
62
|
(0, _useModalManager.default)({
|
|
63
63
|
open,
|
|
64
64
|
closeModal: dismissToast,
|
|
65
|
-
modalRef: refToPass
|
|
65
|
+
modalRef: refToPass,
|
|
66
|
+
topModalOverride: true
|
|
66
67
|
});
|
|
67
68
|
(0, _react.useEffect)(() => {
|
|
68
69
|
/* istanbul ignore next */
|
|
@@ -5,6 +5,7 @@ declare type UseModalManagerArgs = {
|
|
|
5
5
|
modalRef: React.RefObject<HTMLElement>;
|
|
6
6
|
setTriggerRefocusFlag?: (flag: boolean) => void;
|
|
7
7
|
triggerRefocusOnClose?: boolean;
|
|
8
|
+
topModalOverride?: boolean;
|
|
8
9
|
};
|
|
9
|
-
declare const useModalManager: ({ open, closeModal, modalRef, setTriggerRefocusFlag, triggerRefocusOnClose, }: UseModalManagerArgs) => void;
|
|
10
|
+
declare const useModalManager: ({ open, closeModal, modalRef, setTriggerRefocusFlag, triggerRefocusOnClose, topModalOverride, }: UseModalManagerArgs) => void;
|
|
10
11
|
export default useModalManager;
|
|
@@ -12,7 +12,8 @@ const useModalManager = ({
|
|
|
12
12
|
closeModal,
|
|
13
13
|
modalRef,
|
|
14
14
|
setTriggerRefocusFlag,
|
|
15
|
-
triggerRefocusOnClose = true
|
|
15
|
+
triggerRefocusOnClose = true,
|
|
16
|
+
topModalOverride = false
|
|
16
17
|
}) => {
|
|
17
18
|
const listenerAdded = (0, _react.useRef)(false);
|
|
18
19
|
const modalRegistered = (0, _react.useRef)(false);
|
|
@@ -50,10 +51,10 @@ const useModalManager = ({
|
|
|
50
51
|
const registerModal = (0, _react.useCallback)(ref => {
|
|
51
52
|
/* istanbul ignore else */
|
|
52
53
|
if (!modalRegistered.current) {
|
|
53
|
-
_modalManager.default.addModal(ref, setTriggerRefocusFlag);
|
|
54
|
+
_modalManager.default.addModal(ref, setTriggerRefocusFlag, topModalOverride);
|
|
54
55
|
modalRegistered.current = true;
|
|
55
56
|
}
|
|
56
|
-
}, [setTriggerRefocusFlag]);
|
|
57
|
+
}, [setTriggerRefocusFlag, topModalOverride]);
|
|
57
58
|
const unregisterModal = (0, _react.useCallback)(ref => {
|
|
58
59
|
if (modalRegistered.current) {
|
|
59
60
|
_modalManager.default.removeModal(ref, triggerRefocusOnClose);
|