carbon-react 125.12.1 → 125.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
- import React, { useEffect, useMemo, useRef } from "react";
2
+ import React, { useCallback, useEffect, useMemo, useRef } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import DayPicker from "react-day-picker";
5
5
  import { flip, offset } from "@floating-ui/dom";
@@ -98,11 +98,16 @@ export const DatePicker = ({
98
98
  onDayClick?.(date, ev);
99
99
  }
100
100
  };
101
- const handleOnKeyDown = ev => {
102
- if (Events.isEscKey(ev)) {
101
+ const handleKeyUp = useCallback(ev => {
102
+ /* istanbul ignore else */
103
+ if (open && Events.isEscKey(ev)) {
103
104
  inputElement.current?.querySelector("input")?.focus();
104
105
  setOpen(false);
106
+ ev.stopPropagation();
105
107
  }
108
+ }, [inputElement, open, setOpen]);
109
+ const handleOnKeyDown = ev => {
110
+ /* istanbul ignore else */
106
111
  if (ref.current?.querySelector(".DayPicker-NavBar button") === document.activeElement && Events.isTabKey(ev) && Events.isShiftKey(ev)) {
107
112
  ev.preventDefault();
108
113
  setOpen(false);
@@ -145,6 +150,7 @@ export const DatePicker = ({
145
150
  }, /*#__PURE__*/React.createElement(StyledDayPicker, {
146
151
  ref: ref,
147
152
  onMouseDown: pickerMouseDown,
153
+ onKeyUp: handleKeyUp,
148
154
  onKeyDown: handleOnKeyDown
149
155
  }, /*#__PURE__*/React.createElement("div", {
150
156
  id: pickerTabGuardId
@@ -180,13 +180,17 @@ const DateInput = /*#__PURE__*/React.forwardRef(({
180
180
  onFocus(ev);
181
181
  }
182
182
  };
183
+ const handleKeyUp = useCallback(ev => {
184
+ /* istanbul ignore else */
185
+ if (open && Events.isEscKey(ev)) {
186
+ setOpen(false);
187
+ ev.stopPropagation();
188
+ }
189
+ }, [open]);
183
190
  const handleKeyDown = ev => {
184
191
  if (onKeyDown) {
185
192
  onKeyDown(ev);
186
193
  }
187
- if (Events.isEscKey(ev)) {
188
- setOpen(false);
189
- }
190
194
  if (open && Events.isTabKey(ev)) {
191
195
  if (Events.isShiftKey(ev)) {
192
196
  setOpen(false);
@@ -288,6 +292,7 @@ const DateInput = /*#__PURE__*/React.forwardRef(({
288
292
  onChange: handleChange,
289
293
  onClick: handleClick,
290
294
  onFocus: handleFocus,
295
+ onKeyUp: handleKeyUp,
291
296
  onKeyDown: handleKeyDown,
292
297
  iconOnClick: handleClick,
293
298
  onMouseDown: handleMouseDown,
@@ -12,6 +12,12 @@ export interface PortalProps {
12
12
  id?: string;
13
13
  /** Callback function triggered when parent element is scrolled or window resized. */
14
14
  onReposition?: () => void;
15
+ /** A flag to ensure the portal content will remain interactive with by both mouse
16
+ * users and screenreader users, even if a modal is opened outside of or on top of
17
+ * the portal.
18
+ * To be used with caution.
19
+ */
20
+ inertOptOut?: boolean;
15
21
  }
16
- declare const Portal: ({ children, className, id, onReposition }: PortalProps) => React.JSX.Element;
22
+ declare const Portal: ({ children, className, id, onReposition, inertOptOut, }: PortalProps) => React.JSX.Element;
17
23
  export default Portal;
@@ -1,14 +1,26 @@
1
1
  import React, { useContext, useEffect, useMemo, useState } from "react";
2
2
  import ReactDOM from "react-dom";
3
+ import styled, { css } from "styled-components";
3
4
  import guid from "../../__internal__/utils/helpers/guid";
4
5
  import CarbonScopedTokensProvider from "../../style/design-tokens/carbon-scoped-tokens-provider/carbon-scoped-tokens-provider.component";
5
6
  import StyledPortalEntrance from "./portal.style";
6
7
  export const PortalContext = /*#__PURE__*/React.createContext({});
8
+ const Container = styled.div`
9
+ ${({
10
+ theme
11
+ }) => css`
12
+ {
13
+ position: relative;
14
+ z-index: ${theme.zIndex.aboveAll};
15
+ }
16
+ `}
17
+ `;
7
18
  const Portal = ({
8
19
  children,
9
20
  className,
10
21
  id,
11
- onReposition
22
+ onReposition,
23
+ inertOptOut
12
24
  }) => {
13
25
  const [portalNode, setPortalNode] = useState(null);
14
26
  const uniqueId = useMemo(() => guid(), []);
@@ -51,6 +63,9 @@ const Portal = ({
51
63
  if (id !== undefined) {
52
64
  node.setAttribute("id", id);
53
65
  }
66
+ if (inertOptOut) {
67
+ node.setAttribute("data-not-inert", "true");
68
+ }
54
69
  setPortalNode(node);
55
70
  let mainNode = document.body;
56
71
  const rootDiv = document.getElementById("root");
@@ -64,8 +79,9 @@ const Portal = ({
64
79
  }
65
80
  return node;
66
81
  };
82
+ const portalContent = inertOptOut ? /*#__PURE__*/React.createElement(Container, null, children) : children;
67
83
  return /*#__PURE__*/React.createElement(StyledPortalEntrance, {
68
84
  "data-portal-entrance": uniqueId
69
- }, /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(CarbonScopedTokensProvider, null, children), getPortalContainer()));
85
+ }, /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(CarbonScopedTokensProvider, null, portalContent), getPortalContainer()));
70
86
  };
71
87
  export default Portal;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import TypeIcon from "../message/type-icon/type-icon.style";
3
3
  import { MessageVariant } from "../message/message.component";
4
- declare const StyledPortal: import("styled-components").StyledComponent<({ children, className, id, onReposition }: import("../portal/portal").PortalProps) => import("react").JSX.Element, any, {
4
+ declare const StyledPortal: import("styled-components").StyledComponent<({ children, className, id, onReposition, inertOptOut, }: import("../portal/portal").PortalProps) => import("react").JSX.Element, any, {
5
5
  align?: "left" | "right" | "center" | undefined;
6
6
  alignY?: "bottom" | "top" | "center" | undefined;
7
7
  isCenter?: boolean | undefined;
@@ -8,6 +8,10 @@ export default function useModalAria(containerRef) {
8
8
  useEffect(() => {
9
9
  const originalValues = [];
10
10
  const hideNonTopModalElements = rootElement => {
11
+ if (rootElement.dataset.notInert === "true") {
12
+ // stop recursing, and do nothing, if the container has the "data-not-inert" flag
13
+ return;
14
+ }
11
15
  if (!rootElement.contains(topModal)) {
12
16
  originalValues.push({
13
17
  element: rootElement,
@@ -17,13 +21,18 @@ export default function useModalAria(containerRef) {
17
21
  // need to manually call the blur event on any currently-focused element that might be inside the element
18
22
  // we're making inert, since Firefox fails to do this, which can result in the focus styles remaining on
19
23
  // an input that is no longer focused
20
- if (rootElement.contains(document.activeElement)) {
21
- document.activeElement?.blur();
24
+ if (rootElement.contains(document.activeElement) && document.activeElement instanceof HTMLElement) {
25
+ document.activeElement.blur();
22
26
  }
23
27
  rootElement.setAttribute("aria-hidden", "true");
24
28
  rootElement.setAttribute("inert", "");
25
29
  } else if (rootElement !== topModal) {
26
- Array.from(rootElement.children).forEach(hideNonTopModalElements);
30
+ Array.from(rootElement.children).forEach(node => {
31
+ // istanbul ignore else
32
+ if (node instanceof HTMLElement) {
33
+ hideNonTopModalElements(node);
34
+ }
35
+ });
27
36
  }
28
37
  };
29
38
  if (isTopModal) {
@@ -106,11 +106,16 @@ const DatePicker = ({
106
106
  onDayClick?.(date, ev);
107
107
  }
108
108
  };
109
- const handleOnKeyDown = ev => {
110
- if (_events.default.isEscKey(ev)) {
109
+ const handleKeyUp = (0, _react.useCallback)(ev => {
110
+ /* istanbul ignore else */
111
+ if (open && _events.default.isEscKey(ev)) {
111
112
  inputElement.current?.querySelector("input")?.focus();
112
113
  setOpen(false);
114
+ ev.stopPropagation();
113
115
  }
116
+ }, [inputElement, open, setOpen]);
117
+ const handleOnKeyDown = ev => {
118
+ /* istanbul ignore else */
114
119
  if (ref.current?.querySelector(".DayPicker-NavBar button") === document.activeElement && _events.default.isTabKey(ev) && _events.default.isShiftKey(ev)) {
115
120
  ev.preventDefault();
116
121
  setOpen(false);
@@ -153,6 +158,7 @@ const DatePicker = ({
153
158
  }, /*#__PURE__*/_react.default.createElement(_dayPicker.default, {
154
159
  ref: ref,
155
160
  onMouseDown: pickerMouseDown,
161
+ onKeyUp: handleKeyUp,
156
162
  onKeyDown: handleOnKeyDown
157
163
  }, /*#__PURE__*/_react.default.createElement("div", {
158
164
  id: pickerTabGuardId
@@ -189,13 +189,17 @@ const DateInput = exports.DateInput = /*#__PURE__*/_react.default.forwardRef(({
189
189
  onFocus(ev);
190
190
  }
191
191
  };
192
+ const handleKeyUp = (0, _react.useCallback)(ev => {
193
+ /* istanbul ignore else */
194
+ if (open && _events.default.isEscKey(ev)) {
195
+ setOpen(false);
196
+ ev.stopPropagation();
197
+ }
198
+ }, [open]);
192
199
  const handleKeyDown = ev => {
193
200
  if (onKeyDown) {
194
201
  onKeyDown(ev);
195
202
  }
196
- if (_events.default.isEscKey(ev)) {
197
- setOpen(false);
198
- }
199
203
  if (open && _events.default.isTabKey(ev)) {
200
204
  if (_events.default.isShiftKey(ev)) {
201
205
  setOpen(false);
@@ -297,6 +301,7 @@ const DateInput = exports.DateInput = /*#__PURE__*/_react.default.forwardRef(({
297
301
  onChange: handleChange,
298
302
  onClick: handleClick,
299
303
  onFocus: handleFocus,
304
+ onKeyUp: handleKeyUp,
300
305
  onKeyDown: handleKeyDown,
301
306
  iconOnClick: handleClick,
302
307
  onMouseDown: handleMouseDown,
@@ -12,6 +12,12 @@ export interface PortalProps {
12
12
  id?: string;
13
13
  /** Callback function triggered when parent element is scrolled or window resized. */
14
14
  onReposition?: () => void;
15
+ /** A flag to ensure the portal content will remain interactive with by both mouse
16
+ * users and screenreader users, even if a modal is opened outside of or on top of
17
+ * the portal.
18
+ * To be used with caution.
19
+ */
20
+ inertOptOut?: boolean;
15
21
  }
16
- declare const Portal: ({ children, className, id, onReposition }: PortalProps) => React.JSX.Element;
22
+ declare const Portal: ({ children, className, id, onReposition, inertOptOut, }: PortalProps) => React.JSX.Element;
17
23
  export default Portal;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = exports.PortalContext = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactDom = _interopRequireDefault(require("react-dom"));
9
+ var _styledComponents = _interopRequireWildcard(require("styled-components"));
9
10
  var _guid = _interopRequireDefault(require("../../__internal__/utils/helpers/guid"));
10
11
  var _carbonScopedTokensProvider = _interopRequireDefault(require("../../style/design-tokens/carbon-scoped-tokens-provider/carbon-scoped-tokens-provider.component"));
11
12
  var _portal = _interopRequireDefault(require("./portal.style"));
@@ -13,11 +14,22 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
13
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
16
  const PortalContext = exports.PortalContext = /*#__PURE__*/_react.default.createContext({});
17
+ const Container = _styledComponents.default.div`
18
+ ${({
19
+ theme
20
+ }) => (0, _styledComponents.css)`
21
+ {
22
+ position: relative;
23
+ z-index: ${theme.zIndex.aboveAll};
24
+ }
25
+ `}
26
+ `;
16
27
  const Portal = ({
17
28
  children,
18
29
  className,
19
30
  id,
20
- onReposition
31
+ onReposition,
32
+ inertOptOut
21
33
  }) => {
22
34
  const [portalNode, setPortalNode] = (0, _react.useState)(null);
23
35
  const uniqueId = (0, _react.useMemo)(() => (0, _guid.default)(), []);
@@ -60,6 +72,9 @@ const Portal = ({
60
72
  if (id !== undefined) {
61
73
  node.setAttribute("id", id);
62
74
  }
75
+ if (inertOptOut) {
76
+ node.setAttribute("data-not-inert", "true");
77
+ }
63
78
  setPortalNode(node);
64
79
  let mainNode = document.body;
65
80
  const rootDiv = document.getElementById("root");
@@ -73,8 +88,9 @@ const Portal = ({
73
88
  }
74
89
  return node;
75
90
  };
91
+ const portalContent = inertOptOut ? /*#__PURE__*/_react.default.createElement(Container, null, children) : children;
76
92
  return /*#__PURE__*/_react.default.createElement(_portal.default, {
77
93
  "data-portal-entrance": uniqueId
78
- }, /*#__PURE__*/_reactDom.default.createPortal( /*#__PURE__*/_react.default.createElement(_carbonScopedTokensProvider.default, null, children), getPortalContainer()));
94
+ }, /*#__PURE__*/_reactDom.default.createPortal( /*#__PURE__*/_react.default.createElement(_carbonScopedTokensProvider.default, null, portalContent), getPortalContainer()));
79
95
  };
80
96
  var _default = exports.default = Portal;
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import TypeIcon from "../message/type-icon/type-icon.style";
3
3
  import { MessageVariant } from "../message/message.component";
4
- declare const StyledPortal: import("styled-components").StyledComponent<({ children, className, id, onReposition }: import("../portal/portal").PortalProps) => import("react").JSX.Element, any, {
4
+ declare const StyledPortal: import("styled-components").StyledComponent<({ children, className, id, onReposition, inertOptOut, }: import("../portal/portal").PortalProps) => import("react").JSX.Element, any, {
5
5
  align?: "left" | "right" | "center" | undefined;
6
6
  alignY?: "bottom" | "top" | "center" | undefined;
7
7
  isCenter?: boolean | undefined;
@@ -15,6 +15,10 @@ function useModalAria(containerRef) {
15
15
  (0, _react.useEffect)(() => {
16
16
  const originalValues = [];
17
17
  const hideNonTopModalElements = rootElement => {
18
+ if (rootElement.dataset.notInert === "true") {
19
+ // stop recursing, and do nothing, if the container has the "data-not-inert" flag
20
+ return;
21
+ }
18
22
  if (!rootElement.contains(topModal)) {
19
23
  originalValues.push({
20
24
  element: rootElement,
@@ -24,13 +28,18 @@ function useModalAria(containerRef) {
24
28
  // need to manually call the blur event on any currently-focused element that might be inside the element
25
29
  // we're making inert, since Firefox fails to do this, which can result in the focus styles remaining on
26
30
  // an input that is no longer focused
27
- if (rootElement.contains(document.activeElement)) {
28
- document.activeElement?.blur();
31
+ if (rootElement.contains(document.activeElement) && document.activeElement instanceof HTMLElement) {
32
+ document.activeElement.blur();
29
33
  }
30
34
  rootElement.setAttribute("aria-hidden", "true");
31
35
  rootElement.setAttribute("inert", "");
32
36
  } else if (rootElement !== topModal) {
33
- Array.from(rootElement.children).forEach(hideNonTopModalElements);
37
+ Array.from(rootElement.children).forEach(node => {
38
+ // istanbul ignore else
39
+ if (node instanceof HTMLElement) {
40
+ hideNonTopModalElements(node);
41
+ }
42
+ });
34
43
  }
35
44
  };
36
45
  if (isTopModal) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "125.12.1",
3
+ "version": "125.13.0",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",