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.
Files changed (51) hide show
  1. package/esm/__internal__/checkable-input/hidden-checkable-input.component.js +1 -0
  2. package/esm/__internal__/focus-trap/focus-trap-utils.js +3 -2
  3. package/esm/__internal__/focus-trap/focus-trap.component.js +11 -10
  4. package/esm/__internal__/input/input.component.js +1 -0
  5. package/esm/components/alert/alert.component.d.ts +1 -1
  6. package/esm/components/alert/alert.component.js +3 -1
  7. package/esm/components/confirm/confirm.component.d.ts +1 -1
  8. package/esm/components/confirm/confirm.component.js +3 -1
  9. package/esm/components/dialog/dialog.component.js +5 -2
  10. package/esm/components/dialog-full-screen/dialog-full-screen.component.d.ts +1 -1
  11. package/esm/components/dialog-full-screen/dialog-full-screen.component.js +3 -1
  12. package/esm/components/modal/__internal__/modal-manager.d.ts +2 -1
  13. package/esm/components/modal/__internal__/modal-manager.js +8 -5
  14. package/esm/components/modal/modal.component.d.ts +3 -1
  15. package/esm/components/modal/modal.component.js +4 -1
  16. package/esm/components/modal/modal.style.d.ts +3 -1
  17. package/esm/components/modal/modal.style.js +3 -2
  18. package/esm/components/select/filterable-select/filterable-select.component.js +20 -11
  19. package/esm/components/select/multi-select/multi-select.component.js +23 -14
  20. package/esm/components/select/simple-select/simple-select.component.js +16 -7
  21. package/esm/components/sidebar/sidebar.component.d.ts +2 -1
  22. package/esm/components/sidebar/sidebar.component.js +4 -1
  23. package/esm/components/toast/toast.component.js +2 -1
  24. package/esm/hooks/__internal__/useModalManager/useModalManager.d.ts +2 -1
  25. package/esm/hooks/__internal__/useModalManager/useModalManager.js +4 -3
  26. package/lib/__internal__/checkable-input/hidden-checkable-input.component.js +1 -0
  27. package/lib/__internal__/focus-trap/focus-trap-utils.js +3 -2
  28. package/lib/__internal__/focus-trap/focus-trap.component.js +11 -10
  29. package/lib/__internal__/input/input.component.js +1 -0
  30. package/lib/components/alert/alert.component.d.ts +1 -1
  31. package/lib/components/alert/alert.component.js +3 -1
  32. package/lib/components/confirm/confirm.component.d.ts +1 -1
  33. package/lib/components/confirm/confirm.component.js +3 -1
  34. package/lib/components/dialog/dialog.component.js +5 -2
  35. package/lib/components/dialog-full-screen/dialog-full-screen.component.d.ts +1 -1
  36. package/lib/components/dialog-full-screen/dialog-full-screen.component.js +3 -1
  37. package/lib/components/modal/__internal__/modal-manager.d.ts +2 -1
  38. package/lib/components/modal/__internal__/modal-manager.js +8 -5
  39. package/lib/components/modal/modal.component.d.ts +3 -1
  40. package/lib/components/modal/modal.component.js +4 -1
  41. package/lib/components/modal/modal.style.d.ts +3 -1
  42. package/lib/components/modal/modal.style.js +3 -2
  43. package/lib/components/select/filterable-select/filterable-select.component.js +20 -11
  44. package/lib/components/select/multi-select/multi-select.component.js +23 -14
  45. package/lib/components/select/simple-select/simple-select.component.js +16 -7
  46. package/lib/components/sidebar/sidebar.component.d.ts +2 -1
  47. package/lib/components/sidebar/sidebar.component.js +4 -1
  48. package/lib/components/toast/toast.component.js +2 -1
  49. package/lib/hooks/__internal__/useModalManager/useModalManager.d.ts +2 -1
  50. package/lib/hooks/__internal__/useModalManager/useModalManager.js +4 -3
  51. 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, allFocusableElementsInNextWrapper;
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
- return this.modalList[this.modalList.length - 1];
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, never>;
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
- }) => theme.zIndex.modal};
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
- setOpen(isAlreadyOpen => {
310
- if (isAlreadyOpen) {
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
- setOpenState(isAlreadyOpen => {
316
- if (isAlreadyOpen) {
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
- setOpenState(isAlreadyOpen => {
239
- if (isAlreadyOpen) {
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,
@@ -53,7 +53,8 @@ const Toast = /*#__PURE__*/React.forwardRef(({
53
53
  useModalManager({
54
54
  open,
55
55
  closeModal: dismissToast,
56
- modalRef: refToPass
56
+ modalRef: refToPass,
57
+ topModalOverride: true
57
58
  });
58
59
  useEffect(() => {
59
60
  /* 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;
@@ -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, allFocusableElementsInNextWrapper;
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
- return this.modalList[this.modalList.length - 1];
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, never>;
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
- }) => theme.zIndex.modal};
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
- setOpen(isAlreadyOpen => {
319
- if (isAlreadyOpen) {
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
- setOpenState(isAlreadyOpen => {
325
- if (isAlreadyOpen) {
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
- setOpenState(isAlreadyOpen => {
248
- if (isAlreadyOpen) {
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "123.9.1",
3
+ "version": "123.10.1",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",