@kaizen/components 1.81.0 → 1.81.2

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.
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var reactAriaComponents = require('react-aria-components');
4
+ Object.keys(reactAriaComponents).forEach(function (k) {
5
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
6
+ enumerable: true,
7
+ get: function () {
8
+ return reactAriaComponents[k];
9
+ }
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var reactAria = require('react-aria');
4
+ Object.keys(reactAria).forEach(function (k) {
5
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
6
+ enumerable: true,
7
+ get: function () {
8
+ return reactAria[k];
9
+ }
10
+ });
11
+ });
@@ -4,7 +4,7 @@ var React = require('react');
4
4
  var ReactDOM = require('react-dom');
5
5
  var react = require('@headlessui/react');
6
6
  var classnames = require('classnames');
7
- var FocusLock = require('react-focus-lock');
7
+ var reactFocusOn = require('react-focus-on');
8
8
  var useIsClientReady = require('../../utils/useIsClientReady/useIsClientReady.cjs');
9
9
  var console = require('../util/console.cjs');
10
10
  var ModalContext = require('./context/ModalContext.cjs');
@@ -16,7 +16,6 @@ function _interopDefault(e) {
16
16
  }
17
17
  var React__default = /*#__PURE__*/_interopDefault(React);
18
18
  var classnames__default = /*#__PURE__*/_interopDefault(classnames);
19
- var FocusLock__default = /*#__PURE__*/_interopDefault(FocusLock);
20
19
  var GenericModal = function (_a) {
21
20
  var propsId = _a.id,
22
21
  children = _a.children,
@@ -33,87 +32,60 @@ var GenericModal = function (_a) {
33
32
  var labelledByID = React.useId();
34
33
  var describedByID = React.useId();
35
34
  var isClientReady = useIsClientReady.useIsClientReady();
36
- var _c = React.useState(null),
37
- scrollLayer = _c[0],
38
- setScrollLayer = _c[1];
39
- var _d = React.useState(null),
40
- modalLayer = _d[0],
41
- setModalLayer = _d[1];
35
+ var scrollLayerRef = React.useRef(null);
36
+ var modalLayerRef = React.useRef(null);
42
37
  var scrollModalToTop = function () {
43
38
  // If we have a really long modal, the autofocus could land on an element down below
44
39
  // causing the modal to scroll down and skipping over the content near the modal's top.
45
40
  // Ensure that when the modal opens, we are at the top of its content.
46
41
  requestAnimationFrame(function () {
47
- if (!scrollLayer) return;
48
- scrollLayer.scrollTop = 0;
42
+ var scrollElement = scrollLayerRef.current;
43
+ // This little verbose of a check but this ensures that the element is attached to the DOM as it animates in. This additional check aims to avoid race conditions
44
+ if (!(scrollElement === null || scrollElement === void 0 ? void 0 : scrollElement.isConnected)) return;
45
+ scrollElement.scrollTop = 0;
49
46
  });
50
47
  };
51
- var outsideModalClickHandler = function (event) {
52
- if (event.target === scrollLayer || event.target === modalLayer) {
53
- onOutsideModalClick === null || onOutsideModalClick === void 0 ? void 0 : onOutsideModalClick(event);
48
+ var a11yWarn = function () {
49
+ if (!isClientReady) return;
50
+ // Ensure that consumers have provided an element that labels the modal
51
+ // to meet ARIA accessibility guidelines.
52
+ if (!document.getElementById(labelledByID)) {
53
+ console.warn("When using the Modal component, you must provide a label for the modal.\n Make sure you have a <ModalAccessibleLabel /> component with content that labels the modal.");
54
54
  }
55
55
  };
56
56
  var focusOnAccessibleLabel = function () {
57
57
  if (!isClientReady) return;
58
+ var modalElement = modalLayerRef.current;
59
+ if (!(modalElement === null || modalElement === void 0 ? void 0 : modalElement.isConnected)) return;
58
60
  // Check if focus already exists within the modal
59
- if (modalLayer === null || modalLayer === void 0 ? void 0 : modalLayer.contains(document.activeElement)) {
61
+ if (modalElement.contains(document.activeElement)) {
60
62
  return;
61
63
  }
62
64
  var labelElement = document.getElementById(labelledByID);
63
- labelElement === null || labelElement === void 0 ? void 0 : labelElement.focus();
64
- };
65
- var a11yWarn = function () {
66
- if (!isClientReady) return;
67
- // Ensure that consumers have provided an element that labels the modal
68
- // to meet ARIA accessibility guidelines.
69
- if (!document.getElementById(labelledByID)) {
70
- console.warn("When using the Modal component, you must provide a label for the modal.\n Make sure you have a <ModalAccessibleLabel /> component with content that labels the modal.");
65
+ if (labelElement === null || labelElement === void 0 ? void 0 : labelElement.isConnected) {
66
+ labelElement.focus();
71
67
  }
72
68
  };
73
- var preventBodyScroll = function () {
74
- var _a;
75
- if (!isClientReady) return;
76
- var hasScrollbar = window.innerWidth > document.documentElement.clientWidth;
77
- var scrollStyles = [GenericModal_module.unscrollable];
78
- if (hasScrollbar) {
79
- scrollStyles.push(GenericModal_module.pseudoScrollbar);
69
+ var onEscapeKeyHandler = function (e) {
70
+ if (e instanceof KeyboardEvent) {
71
+ onEscapeKeyup === null || onEscapeKeyup === void 0 ? void 0 : onEscapeKeyup(e);
80
72
  }
81
- (_a = document.documentElement.classList).add.apply(_a, scrollStyles);
82
73
  };
83
74
  var onAfterEnterHandler = function () {
84
75
  scrollModalToTop();
85
- if (modalLayer) {
76
+ var modalElement = modalLayerRef.current;
77
+ if (modalElement) {
86
78
  onAfterEnter === null || onAfterEnter === void 0 ? void 0 : onAfterEnter();
87
79
  focusOnAccessibleLabel();
88
80
  a11yWarn();
89
81
  }
90
82
  };
91
- var escapeKeyHandler = React.useCallback(function (event) {
92
- if (event.key === 'Escape') {
93
- onEscapeKeyup === null || onEscapeKeyup === void 0 ? void 0 : onEscapeKeyup(event);
94
- }
95
- }, [onEscapeKeyup]);
96
- var onBeforeEnterHandler = function () {
97
- preventBodyScroll();
98
- if (onEscapeKeyup && isClientReady) {
99
- document.addEventListener('keyup', escapeKeyHandler);
83
+ var outsideModalClickHandler = function (e) {
84
+ if (e.target === scrollLayerRef.current || e.target === modalLayerRef.current) {
85
+ onOutsideModalClick === null || onOutsideModalClick === void 0 ? void 0 : onOutsideModalClick(e);
100
86
  }
101
87
  };
102
- var cleanUpAfterClose = React.useCallback(function () {
103
- if (!isClientReady) return;
104
- document.documentElement.classList.remove(GenericModal_module.unscrollable, GenericModal_module.pseudoScrollbar);
105
- if (onEscapeKeyup) {
106
- document.removeEventListener('keyup', escapeKeyHandler);
107
- }
108
- }, [escapeKeyHandler, onEscapeKeyup, isClientReady]);
109
- /* Ensure sure add-on styles (e.g. unscrollable) and key event is cleaned up when the modal is unmounted*/
110
- React.useEffect(function () {
111
- return function () {
112
- return cleanUpAfterClose();
113
- };
114
- }, [cleanUpAfterClose]);
115
88
  var onAfterLeaveHandler = function () {
116
- cleanUpAfterClose();
117
89
  propsOnAfterLeave === null || propsOnAfterLeave === void 0 ? void 0 : propsOnAfterLeave();
118
90
  };
119
91
  // Don't render portal during SSR
@@ -123,7 +95,6 @@ var GenericModal = function (_a) {
123
95
  return ReactDOM.createPortal(React__default.default.createElement(react.Transition, {
124
96
  appear: true,
125
97
  show: isOpen,
126
- beforeEnter: onBeforeEnterHandler,
127
98
  afterEnter: onAfterEnterHandler,
128
99
  afterLeave: onAfterLeaveHandler,
129
100
  "data-generic-modal-transition-wrapper": true,
@@ -131,9 +102,10 @@ var GenericModal = function (_a) {
131
102
  leave: GenericModal_module.animatingLeave,
132
103
  as: "div",
133
104
  className: classnames__default.default(GenericModal_module.transitionLayer, className)
134
- }, React__default.default.createElement(FocusLock__default.default, {
135
- disabled: focusLockDisabled,
105
+ }, React__default.default.createElement(reactFocusOn.FocusOn, {
106
+ focusLock: focusLockDisabled,
136
107
  returnFocus: true,
108
+ onEscapeKey: onEscapeKeyHandler,
137
109
  // Disabling false positive
138
110
  // eslint-disable-next-line jsx-a11y/no-autofocus
139
111
  autoFocus: false
@@ -141,11 +113,9 @@ var GenericModal = function (_a) {
141
113
  className: GenericModal_module.backdropLayer
142
114
  }), React__default.default.createElement("div", {
143
115
  className: GenericModal_module.scrollLayer,
144
- ref: function (scrollLayerRef) {
145
- setScrollLayer(scrollLayerRef);
146
- },
147
- onClick: outsideModalClickHandler,
148
- "data-testid": "".concat(id, "-scrollLayer")
116
+ ref: scrollLayerRef,
117
+ "data-testid": "".concat(id, "-scrollLayer"),
118
+ onClick: outsideModalClickHandler
149
119
  }, React__default.default.createElement(ModalContext.ModalContext.Provider, {
150
120
  value: {
151
121
  labelledByID: labelledByID,
@@ -156,9 +126,7 @@ var GenericModal = function (_a) {
156
126
  className: GenericModal_module.modalLayer,
157
127
  "aria-labelledby": labelledByID,
158
128
  "aria-describedby": describedByID,
159
- ref: function (modalLayerRef) {
160
- return setModalLayer(modalLayerRef);
161
- },
129
+ ref: modalLayerRef,
162
130
  "data-testid": id
163
131
  }, children))))), document.body);
164
132
  };
@@ -6,8 +6,6 @@ var styles = {
6
6
  "modalLayer": "GenericModal-module_modalLayer__WfD1U",
7
7
  "transitionLayer": "GenericModal-module_transitionLayer__zTH-C",
8
8
  "animatingEnter": "GenericModal-module_animatingEnter__P3wuk",
9
- "animatingLeave": "GenericModal-module_animatingLeave__rNkKX",
10
- "unscrollable": "GenericModal-module_unscrollable__HjRaW",
11
- "pseudoScrollbar": "GenericModal-module_pseudoScrollbar__BhRqh"
9
+ "animatingLeave": "GenericModal-module_animatingLeave__rNkKX"
12
10
  };
13
11
  module.exports = styles;
@@ -0,0 +1 @@
1
+ export * from 'react-aria-components';
@@ -0,0 +1 @@
1
+ export * from 'react-aria';
@@ -1,8 +1,8 @@
1
- import React, { useId, useState, useCallback, useEffect } from 'react';
1
+ import React, { useId, useRef } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
  import { Transition } from '@headlessui/react';
4
4
  import classnames from 'classnames';
5
- import FocusLock from 'react-focus-lock';
5
+ import { FocusOn } from 'react-focus-on';
6
6
  import { useIsClientReady } from '../../utils/useIsClientReady/useIsClientReady.mjs';
7
7
  import { warn } from '../util/console.mjs';
8
8
  import { ModalContext } from './context/ModalContext.mjs';
@@ -24,87 +24,60 @@ const GenericModal = /*#__PURE__*/function () {
24
24
  var labelledByID = useId();
25
25
  var describedByID = useId();
26
26
  var isClientReady = useIsClientReady();
27
- var _c = useState(null),
28
- scrollLayer = _c[0],
29
- setScrollLayer = _c[1];
30
- var _d = useState(null),
31
- modalLayer = _d[0],
32
- setModalLayer = _d[1];
27
+ var scrollLayerRef = useRef(null);
28
+ var modalLayerRef = useRef(null);
33
29
  var scrollModalToTop = function () {
34
30
  // If we have a really long modal, the autofocus could land on an element down below
35
31
  // causing the modal to scroll down and skipping over the content near the modal's top.
36
32
  // Ensure that when the modal opens, we are at the top of its content.
37
33
  requestAnimationFrame(function () {
38
- if (!scrollLayer) return;
39
- scrollLayer.scrollTop = 0;
34
+ var scrollElement = scrollLayerRef.current;
35
+ // This little verbose of a check but this ensures that the element is attached to the DOM as it animates in. This additional check aims to avoid race conditions
36
+ if (!(scrollElement === null || scrollElement === void 0 ? void 0 : scrollElement.isConnected)) return;
37
+ scrollElement.scrollTop = 0;
40
38
  });
41
39
  };
42
- var outsideModalClickHandler = function (event) {
43
- if (event.target === scrollLayer || event.target === modalLayer) {
44
- onOutsideModalClick === null || onOutsideModalClick === void 0 ? void 0 : onOutsideModalClick(event);
40
+ var a11yWarn = function () {
41
+ if (!isClientReady) return;
42
+ // Ensure that consumers have provided an element that labels the modal
43
+ // to meet ARIA accessibility guidelines.
44
+ if (!document.getElementById(labelledByID)) {
45
+ warn("When using the Modal component, you must provide a label for the modal.\n Make sure you have a <ModalAccessibleLabel /> component with content that labels the modal.");
45
46
  }
46
47
  };
47
48
  var focusOnAccessibleLabel = function () {
48
49
  if (!isClientReady) return;
50
+ var modalElement = modalLayerRef.current;
51
+ if (!(modalElement === null || modalElement === void 0 ? void 0 : modalElement.isConnected)) return;
49
52
  // Check if focus already exists within the modal
50
- if (modalLayer === null || modalLayer === void 0 ? void 0 : modalLayer.contains(document.activeElement)) {
53
+ if (modalElement.contains(document.activeElement)) {
51
54
  return;
52
55
  }
53
56
  var labelElement = document.getElementById(labelledByID);
54
- labelElement === null || labelElement === void 0 ? void 0 : labelElement.focus();
55
- };
56
- var a11yWarn = function () {
57
- if (!isClientReady) return;
58
- // Ensure that consumers have provided an element that labels the modal
59
- // to meet ARIA accessibility guidelines.
60
- if (!document.getElementById(labelledByID)) {
61
- warn("When using the Modal component, you must provide a label for the modal.\n Make sure you have a <ModalAccessibleLabel /> component with content that labels the modal.");
57
+ if (labelElement === null || labelElement === void 0 ? void 0 : labelElement.isConnected) {
58
+ labelElement.focus();
62
59
  }
63
60
  };
64
- var preventBodyScroll = function () {
65
- var _a;
66
- if (!isClientReady) return;
67
- var hasScrollbar = window.innerWidth > document.documentElement.clientWidth;
68
- var scrollStyles = [styles.unscrollable];
69
- if (hasScrollbar) {
70
- scrollStyles.push(styles.pseudoScrollbar);
61
+ var onEscapeKeyHandler = function (e) {
62
+ if (e instanceof KeyboardEvent) {
63
+ onEscapeKeyup === null || onEscapeKeyup === void 0 ? void 0 : onEscapeKeyup(e);
71
64
  }
72
- (_a = document.documentElement.classList).add.apply(_a, scrollStyles);
73
65
  };
74
66
  var onAfterEnterHandler = function () {
75
67
  scrollModalToTop();
76
- if (modalLayer) {
68
+ var modalElement = modalLayerRef.current;
69
+ if (modalElement) {
77
70
  onAfterEnter === null || onAfterEnter === void 0 ? void 0 : onAfterEnter();
78
71
  focusOnAccessibleLabel();
79
72
  a11yWarn();
80
73
  }
81
74
  };
82
- var escapeKeyHandler = useCallback(function (event) {
83
- if (event.key === 'Escape') {
84
- onEscapeKeyup === null || onEscapeKeyup === void 0 ? void 0 : onEscapeKeyup(event);
85
- }
86
- }, [onEscapeKeyup]);
87
- var onBeforeEnterHandler = function () {
88
- preventBodyScroll();
89
- if (onEscapeKeyup && isClientReady) {
90
- document.addEventListener('keyup', escapeKeyHandler);
75
+ var outsideModalClickHandler = function (e) {
76
+ if (e.target === scrollLayerRef.current || e.target === modalLayerRef.current) {
77
+ onOutsideModalClick === null || onOutsideModalClick === void 0 ? void 0 : onOutsideModalClick(e);
91
78
  }
92
79
  };
93
- var cleanUpAfterClose = useCallback(function () {
94
- if (!isClientReady) return;
95
- document.documentElement.classList.remove(styles.unscrollable, styles.pseudoScrollbar);
96
- if (onEscapeKeyup) {
97
- document.removeEventListener('keyup', escapeKeyHandler);
98
- }
99
- }, [escapeKeyHandler, onEscapeKeyup, isClientReady]);
100
- /* Ensure sure add-on styles (e.g. unscrollable) and key event is cleaned up when the modal is unmounted*/
101
- useEffect(function () {
102
- return function () {
103
- return cleanUpAfterClose();
104
- };
105
- }, [cleanUpAfterClose]);
106
80
  var onAfterLeaveHandler = function () {
107
- cleanUpAfterClose();
108
81
  propsOnAfterLeave === null || propsOnAfterLeave === void 0 ? void 0 : propsOnAfterLeave();
109
82
  };
110
83
  // Don't render portal during SSR
@@ -114,7 +87,6 @@ const GenericModal = /*#__PURE__*/function () {
114
87
  return /*#__PURE__*/createPortal(/*#__PURE__*/React.createElement(Transition, {
115
88
  appear: true,
116
89
  show: isOpen,
117
- beforeEnter: onBeforeEnterHandler,
118
90
  afterEnter: onAfterEnterHandler,
119
91
  afterLeave: onAfterLeaveHandler,
120
92
  "data-generic-modal-transition-wrapper": true,
@@ -122,9 +94,10 @@ const GenericModal = /*#__PURE__*/function () {
122
94
  leave: styles.animatingLeave,
123
95
  as: "div",
124
96
  className: classnames(styles.transitionLayer, className)
125
- }, /*#__PURE__*/React.createElement(FocusLock, {
126
- disabled: focusLockDisabled,
97
+ }, /*#__PURE__*/React.createElement(FocusOn, {
98
+ focusLock: focusLockDisabled,
127
99
  returnFocus: true,
100
+ onEscapeKey: onEscapeKeyHandler,
128
101
  // Disabling false positive
129
102
  // eslint-disable-next-line jsx-a11y/no-autofocus
130
103
  autoFocus: false
@@ -132,11 +105,9 @@ const GenericModal = /*#__PURE__*/function () {
132
105
  className: styles.backdropLayer
133
106
  }), /*#__PURE__*/React.createElement("div", {
134
107
  className: styles.scrollLayer,
135
- ref: function (scrollLayerRef) {
136
- setScrollLayer(scrollLayerRef);
137
- },
138
- onClick: outsideModalClickHandler,
139
- "data-testid": "".concat(id, "-scrollLayer")
108
+ ref: scrollLayerRef,
109
+ "data-testid": "".concat(id, "-scrollLayer"),
110
+ onClick: outsideModalClickHandler
140
111
  }, /*#__PURE__*/React.createElement(ModalContext.Provider, {
141
112
  value: {
142
113
  labelledByID: labelledByID,
@@ -147,9 +118,7 @@ const GenericModal = /*#__PURE__*/function () {
147
118
  className: styles.modalLayer,
148
119
  "aria-labelledby": labelledByID,
149
120
  "aria-describedby": describedByID,
150
- ref: function (modalLayerRef) {
151
- return setModalLayer(modalLayerRef);
152
- },
121
+ ref: modalLayerRef,
153
122
  "data-testid": id
154
123
  }, children))))), document.body);
155
124
  };
@@ -4,8 +4,6 @@ var styles = {
4
4
  "modalLayer": "GenericModal-module_modalLayer__WfD1U",
5
5
  "transitionLayer": "GenericModal-module_transitionLayer__zTH-C",
6
6
  "animatingEnter": "GenericModal-module_animatingEnter__P3wuk",
7
- "animatingLeave": "GenericModal-module_animatingLeave__rNkKX",
8
- "unscrollable": "GenericModal-module_unscrollable__HjRaW",
9
- "pseudoScrollbar": "GenericModal-module_pseudoScrollbar__BhRqh"
7
+ "animatingLeave": "GenericModal-module_animatingLeave__rNkKX"
10
8
  };
11
9
  export { styles as default };
package/dist/styles.css CHANGED
@@ -1,83 +1,4 @@
1
1
  @layer tokens, normalize, reset, kz-components;@layer tokens{:root{--theme-key:heart;--animation-easing-function-ease-in-out:cubic-bezier(0.455,0.03,0.515,0.955);--animation-easing-function-ease-in:cubic-bezier(0.55,0.085,0.68,0.53);--animation-easing-function-ease-out:cubic-bezier(0.25,0.46,0.45,0.94);--animation-easing-function-linear:linear;--animation-easing-function-bounce-in:cubic-bezier(0.485,0.155,0.24,1.245);--animation-easing-function-bounce-out:cubic-bezier(0.485,0.155,0.515,0.845);--animation-easing-function-bounce-in-out:cubic-bezier(0.76,-0.245,0.24,1.245);--animation-duration-instant:0ms;--animation-duration-immediate:100ms;--animation-duration-rapid:200ms;--animation-duration-fast:300ms;--animation-duration-slow:400ms;--animation-duration-deliberate:700ms;--border-solid-border-width:2px;--border-solid-border-radius:7px;--border-solid-border-style:solid;--border-solid-border-color:#e1e2ea;--border-solid-border-color-rgb:225,226,234;--border-dashed-border-width:2px;--border-dashed-border-radius:7px;--border-dashed-border-style:dashed;--border-borderless-border-width:2px;--border-borderless-border-radius:7px;--border-borderless-border-style:solid;--border-borderless-border-color:transparent;--border-borderless-border-color-rgb:0,0,0;--border-focus-ring-border-width:2px;--border-focus-ring-border-radius:10px;--border-focus-ring-border-style:solid;--border-width-1:1px;--color-purple-100:#f4edf8;--color-purple-100-rgb:244,237,248;--color-purple-200:#dfc9ea;--color-purple-200-rgb:223,201,234;--color-purple-300:#c9a5dd;--color-purple-300-rgb:201,165,221;--color-purple-400:#ae67b1;--color-purple-400-rgb:174,103,177;--color-purple-500:#844587;--color-purple-500-rgb:132,69,135;--color-purple-600:#5f3361;--color-purple-600-rgb:95,51,97;--color-purple-700:#4a234d;--color-purple-700-rgb:74,35,77;--color-purple-800:#2f2438;--color-purple-800-rgb:47,36,56;--color-blue-100:#e6f6ff;--color-blue-100-rgb:230,246,255;--color-blue-200:#bde2f5;--color-blue-200-rgb:189,226,245;--color-blue-300:#73c0e8;--color-blue-300-rgb:115,192,232;--color-blue-400:#008bd6;--color-blue-400-rgb:0,139,214;--color-blue-500:#0168b3;--color-blue-500-rgb:1,104,179;--color-blue-600:#004970;--color-blue-600-rgb:0,73,112;--color-blue-700:#003157;--color-blue-700-rgb:0,49,87;--color-green-100:#e8f8f4;--color-green-100-rgb:232,248,244;--color-green-200:#c4ede2;--color-green-200-rgb:196,237,226;--color-green-300:#8fdbc7;--color-green-300-rgb:143,219,199;--color-green-400:#5dcaad;--color-green-400-rgb:93,202,173;--color-green-500:#3f9a86;--color-green-500-rgb:63,154,134;--color-green-600:#2c7d67;--color-green-600-rgb:44,125,103;--color-green-700:#22594a;--color-green-700-rgb:34,89,74;--color-yellow-100:#fff9e4;--color-yellow-100-rgb:255,249,228;--color-yellow-200:#ffeeb3;--color-yellow-200-rgb:255,238,179;--color-yellow-300:#ffe36e;--color-yellow-300-rgb:255,227,110;--color-yellow-400:#ffca4d;--color-yellow-400-rgb:255,202,77;--color-yellow-500:#ffb600;--color-yellow-500-rgb:255,182,0;--color-yellow-600:#c68600;--color-yellow-600-rgb:198,134,0;--color-yellow-700:#876400;--color-yellow-700-rgb:135,100,0;--color-red-100:#fdeaee;--color-red-100-rgb:253,234,238;--color-red-200:#f9c2cb;--color-red-200-rgb:249,194,203;--color-red-300:#f597a8;--color-red-300-rgb:245,151,168;--color-red-400:#e0707d;--color-red-400-rgb:224,112,125;--color-red-500:#c93b55;--color-red-500-rgb:201,59,85;--color-red-600:#a82433;--color-red-600-rgb:168,36,51;--color-red-700:#6c1e20;--color-red-700-rgb:108,30,32;--color-orange-100:#fff0e8;--color-orange-100-rgb:255,240,232;--color-orange-200:#ffd1b9;--color-orange-200-rgb:255,209,185;--color-orange-300:#ffb08a;--color-orange-300-rgb:255,176,138;--color-orange-400:#ff9461;--color-orange-400-rgb:255,148,97;--color-orange-500:#e96c2f;--color-orange-500-rgb:233,108,47;--color-orange-600:#b74302;--color-orange-600-rgb:183,67,2;--color-orange-700:#903c00;--color-orange-700-rgb:144,60,0;--color-gray-100:#f9f9f9;--color-gray-100-rgb:249,249,249;--color-gray-200:#f4f4f5;--color-gray-200-rgb:244,244,245;--color-gray-300:#eaeaec;--color-gray-300-rgb:234,234,236;--color-gray-400:#cdcdd0;--color-gray-400-rgb:205,205,208;--color-gray-500:#878792;--color-gray-500-rgb:135,135,146;--color-gray-600:#524e56;--color-gray-600-rgb:82,78,86;--color-white:#fff;--color-white-rgb:255,255,255;--color-black:#000;--color-black-rgb:0,0,0;--data-viz-favorable:#7dd5bd;--data-viz-favorable-rgb:125,213,189;--data-viz-unfavorable:#e68d97;--data-viz-unfavorable-rgb:230,141,151;--layout-content-max-width:1392px;--layout-content-max-width-with-sidebar:1080px;--layout-content-side-margin:72px;--layout-mobile-actions-drawer-height:60px;--layout-navigation-bar-height:72px;--layout-breakpoints-medium:768px;--layout-breakpoints-large:1080px;--shadow-small-box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 3px 16px 0 rgba(0,0,0,.06);--shadow-large-box-shadow:0 3px 9px 0 rgba(0,0,0,.1),0 8px 40px 0 rgba(0,0,0,.08);--spacing-0:0;--spacing-1:.0625rem;--spacing-2:.125rem;--spacing-4:.25rem;--spacing-6:.375rem;--spacing-8:.5rem;--spacing-12:.75rem;--spacing-16:1rem;--spacing-20:1.25rem;--spacing-24:1.5rem;--spacing-32:2rem;--spacing-40:2.5rem;--spacing-48:3rem;--spacing-56:3.5rem;--spacing-64:4rem;--spacing-72:4.5rem;--spacing-80:5rem;--spacing-96:6rem;--spacing-112:7rem;--spacing-128:8rem;--spacing-160:10rem;--spacing-200:12.5rem;--spacing-240:15rem;--spacing-280:17.5rem;--spacing-320:20rem;--spacing-xs:0.375rem;--spacing-sm:0.75rem;--spacing-md:1.5rem;--spacing-lg:2.25rem;--spacing-xl:3rem;--spacing-xxl:3.75rem;--spacing-xxxl:4.5rem;--spacing-xxxxl:5.25rem;--spacing-xxxxxl:6rem;--typography-data-large-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-large-font-weight:700;--typography-data-large-font-size:5.25rem;--typography-data-large-line-height:5.25rem;--typography-data-large-letter-spacing:normal;--typography-data-large-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-large-units-font-weight:700;--typography-data-large-units-font-size:2.625rem;--typography-data-large-units-line-height:5.25rem;--typography-data-large-units-letter-spacing:normal;--typography-data-medium-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-medium-font-weight:700;--typography-data-medium-font-size:3rem;--typography-data-medium-line-height:5rem;--typography-data-medium-letter-spacing:normal;--typography-data-medium-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-medium-units-font-weight:700;--typography-data-medium-units-font-size:1.5rem;--typography-data-medium-units-line-height:5rem;--typography-data-medium-units-letter-spacing:normal;--typography-data-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-small-font-weight:700;--typography-data-small-font-size:1.5rem;--typography-data-small-line-height:1.5rem;--typography-data-small-letter-spacing:normal;--typography-data-small-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-small-units-font-weight:700;--typography-data-small-units-font-size:1.125rem;--typography-data-small-units-line-height:1.5rem;--typography-data-small-units-letter-spacing:normal;--typography-display-0-font-family:"Tiempos Headline",Georgia,serif;--typography-display-0-font-weight:800;--typography-display-0-font-size:4.5rem;--typography-display-0-line-height:5.25rem;--typography-display-0-letter-spacing:0em;--typography-heading-1-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-1-font-weight:500;--typography-heading-1-font-size:2.125rem;--typography-heading-1-line-height:2.625rem;--typography-heading-1-letter-spacing:normal;--typography-heading-2-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-2-font-weight:600;--typography-heading-2-font-size:1.75rem;--typography-heading-2-line-height:2.25rem;--typography-heading-2-letter-spacing:normal;--typography-heading-3-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-3-font-weight:600;--typography-heading-3-font-size:1.375rem;--typography-heading-3-line-height:1.875rem;--typography-heading-3-letter-spacing:normal;--typography-heading-4-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-4-font-weight:600;--typography-heading-4-font-size:1.125rem;--typography-heading-4-line-height:1.5rem;--typography-heading-4-letter-spacing:normal;--typography-heading-5-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-5-font-weight:600;--typography-heading-5-font-size:1rem;--typography-heading-5-line-height:1.5rem;--typography-heading-5-letter-spacing:normal;--typography-heading-6-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-6-font-weight:600;--typography-heading-6-font-size:0.875rem;--typography-heading-6-line-height:1.5rem;--typography-heading-6-letter-spacing:normal;--typography-paragraph-intro-lede-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-intro-lede-font-weight:400;--typography-paragraph-intro-lede-font-size:1.25rem;--typography-paragraph-intro-lede-line-height:1.875rem;--typography-paragraph-intro-lede-letter-spacing:0;--typography-paragraph-intro-lede-max-width:975px;--typography-paragraph-body-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-body-font-weight:400;--typography-paragraph-body-font-size:1rem;--typography-paragraph-body-line-height:1.5rem;--typography-paragraph-body-letter-spacing:normal;--typography-paragraph-body-max-width:780px;--typography-paragraph-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-small-font-weight:400;--typography-paragraph-small-font-size:0.875rem;--typography-paragraph-small-line-height:1.125rem;--typography-paragraph-small-letter-spacing:normal;--typography-paragraph-small-max-width:680px;--typography-paragraph-extra-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-extra-small-font-weight:400;--typography-paragraph-extra-small-font-size:0.75rem;--typography-paragraph-extra-small-line-height:1.125rem;--typography-paragraph-extra-small-letter-spacing:normal;--typography-paragraph-extra-small-max-width:600px;--typography-paragraph-bold-font-weight:600;--typography-button-primary-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-button-primary-font-weight:500;--typography-button-primary-font-size:1.125rem;--typography-button-primary-line-height:1.5rem;--typography-button-primary-letter-spacing:normal;--typography-button-secondary-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-button-secondary-font-weight:500;--typography-button-secondary-font-size:1rem;--typography-button-secondary-line-height:1.5rem;--typography-button-secondary-letter-spacing:normal}}@layer normalize{html{text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{appearance:auto}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{appearance:none}::-webkit-file-upload-button{appearance:auto;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}}@layer reset{@font-face{font-family:Tiempos Headline;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-bold.woff)}@font-face{font-family:Tiempos Headline;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-medium.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-medium.woff)}@font-face{font-family:Greycliff CF;font-weight:300;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-light.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:400;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-regular.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-medium.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:600;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-demi-bold.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:700;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-bold.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-extra-bold.woff) format("woff")}@font-face{font-family:Inter;font-weight:300;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-light.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-light.woff)}@font-face{font-family:Inter;font-weight:400;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-regular.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-regular.woff)}@font-face{font-family:Inter;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-medium.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-medium.woff)}@font-face{font-family:Inter;font-weight:600;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-demi-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-demi-bold.woff)}@font-face{font-family:Inter;font-weight:700;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-bold.woff)}@font-face{font-family:Inter;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-extra-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-extra-bold.woff)}@font-face{font-family:IBM Plex Mono;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/ibm-plex-mono/ibm-plex-mono-regular.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/ibm-plex-mono/ibm-plex-mono-regular.woff)}}@layer reset{*,:after,:before{border-color:var(--border-solid-border-color,"currentColor");border-style:solid;border-width:0}}
2
- @layer kz-components {
3
- .ListSection-module_listSectionHeader__bptHg {
4
- font-family: var(--typography-heading-5-font-family);
5
- font-weight: var(--typography-heading-5-font-weight);
6
- font-size: var(--typography-heading-5-font-size);
7
- line-height: var(--typography-heading-5-line-height);
8
- letter-spacing: var(--typography-heading-5-letter-spacing);
9
- }
10
- }
11
-
12
- @layer kz-components {
13
- .ListItem-module_listItem__xGr6A {
14
- font-family: var(--typography-paragraph-body-font-family);
15
- font-weight: var(--typography-paragraph-body-font-weight);
16
- font-size: var(--typography-paragraph-body-font-size);
17
- line-height: var(--typography-paragraph-body-line-height);
18
- letter-spacing: var(--typography-paragraph-body-letter-spacing);
19
- padding: var(--spacing-8) var(--spacing-16);
20
- }
21
-
22
- .ListItem-module_listItem__xGr6A:focus-visible {
23
- background-color: var(--color-blue-200);
24
- outline: none;
25
- border-color: white;
26
- }
27
- }
28
-
29
- @layer kz-components {
30
- .List-module_list__bbFPn {
31
- display: flex;
32
- flex-direction: column;
33
- }
34
- }
35
-
36
- @layer kz-components {
37
- .Trigger-module_button__giSqA {
38
- anchor-name: var(--anchor-name);
39
- display: flex;
40
- align-items: center;
41
- justify-content: space-between;
42
- font-family: var(--typography-paragraph-body-font-family);
43
- font-weight: var(--typography-paragraph-body-font-weight);
44
- font-size: var(--typography-paragraph-body-font-size);
45
- line-height: var(--typography-paragraph-body-line-height);
46
- letter-spacing: var(--typography-paragraph-body-letter-spacing);
47
- padding: var(--spacing-12);
48
- min-height: var(--spacing-48);
49
- min-width: var(--spacing-200);
50
- background-color: var(--color-white);
51
- border-radius: var(--spacing-8);
52
- border: 1px solid var(--color-gray-500);
53
- }
54
- }
55
-
56
- @layer kz-components {
57
- .Popover-module_popover__BjY2S {
58
- position: absolute;
59
- height: auto;
60
- background-color: var(--color-white);
61
- border-radius: var(--spacing-8);
62
- padding: 0;
63
- box-shadow: var(--shadow-small-box-shadow);
64
- overflow: hidden auto;
65
- margin: 0;
66
- box-sizing: border-box;
67
-
68
- /* TODO: update width based on design */
69
- width: 200px;
70
-
71
- @supports (anchor-name: --anchor) {
72
- position-anchor: var(--position-anchor);
73
- margin-block: var(--spacing-4);
74
- position-area: var(--position-area) center;
75
- /* stylelint-disable-next-line declaration-property-value-no-unknown */
76
- width: anchor-size(width);
77
- }
78
- }
79
- }
80
-
81
2
  @layer kz-components {
82
3
  /*
83
4
  * This is taken from the Material Symbols CDN
@@ -12093,3 +12014,82 @@
12093
12014
  }
12094
12015
  }
12095
12016
 
12017
+ @layer kz-components {
12018
+ .List-module_list__bbFPn {
12019
+ display: flex;
12020
+ flex-direction: column;
12021
+ }
12022
+ }
12023
+
12024
+ @layer kz-components {
12025
+ .ListSection-module_listSectionHeader__bptHg {
12026
+ font-family: var(--typography-heading-5-font-family);
12027
+ font-weight: var(--typography-heading-5-font-weight);
12028
+ font-size: var(--typography-heading-5-font-size);
12029
+ line-height: var(--typography-heading-5-line-height);
12030
+ letter-spacing: var(--typography-heading-5-letter-spacing);
12031
+ }
12032
+ }
12033
+
12034
+ @layer kz-components {
12035
+ .ListItem-module_listItem__xGr6A {
12036
+ font-family: var(--typography-paragraph-body-font-family);
12037
+ font-weight: var(--typography-paragraph-body-font-weight);
12038
+ font-size: var(--typography-paragraph-body-font-size);
12039
+ line-height: var(--typography-paragraph-body-line-height);
12040
+ letter-spacing: var(--typography-paragraph-body-letter-spacing);
12041
+ padding: var(--spacing-8) var(--spacing-16);
12042
+ }
12043
+
12044
+ .ListItem-module_listItem__xGr6A:focus-visible {
12045
+ background-color: var(--color-blue-200);
12046
+ outline: none;
12047
+ border-color: white;
12048
+ }
12049
+ }
12050
+
12051
+ @layer kz-components {
12052
+ .Trigger-module_button__giSqA {
12053
+ anchor-name: var(--anchor-name);
12054
+ display: flex;
12055
+ align-items: center;
12056
+ justify-content: space-between;
12057
+ font-family: var(--typography-paragraph-body-font-family);
12058
+ font-weight: var(--typography-paragraph-body-font-weight);
12059
+ font-size: var(--typography-paragraph-body-font-size);
12060
+ line-height: var(--typography-paragraph-body-line-height);
12061
+ letter-spacing: var(--typography-paragraph-body-letter-spacing);
12062
+ padding: var(--spacing-12);
12063
+ min-height: var(--spacing-48);
12064
+ min-width: var(--spacing-200);
12065
+ background-color: var(--color-white);
12066
+ border-radius: var(--spacing-8);
12067
+ border: 1px solid var(--color-gray-500);
12068
+ }
12069
+ }
12070
+
12071
+ @layer kz-components {
12072
+ .Popover-module_popover__BjY2S {
12073
+ position: absolute;
12074
+ height: auto;
12075
+ background-color: var(--color-white);
12076
+ border-radius: var(--spacing-8);
12077
+ padding: 0;
12078
+ box-shadow: var(--shadow-small-box-shadow);
12079
+ overflow: hidden auto;
12080
+ margin: 0;
12081
+ box-sizing: border-box;
12082
+
12083
+ /* TODO: update width based on design */
12084
+ width: 200px;
12085
+
12086
+ @supports (anchor-name: --anchor) {
12087
+ position-anchor: var(--position-anchor);
12088
+ margin-block: var(--spacing-4);
12089
+ position-area: var(--position-area) center;
12090
+ /* stylelint-disable-next-line declaration-property-value-no-unknown */
12091
+ width: anchor-size(width);
12092
+ }
12093
+ }
12094
+ }
12095
+
@@ -0,0 +1 @@
1
+ export * from 'react-aria';
@@ -0,0 +1 @@
1
+ export * from 'react-aria-components';
package/libs/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Entrypoint: libs
2
+
3
+ This directory is here to redirect any imports from `@kaizen/components/libs` to the correct distribution of compiled code to allow for treeshaking of external libraries like `react-aria`.
4
+
5
+ These are exported to reduce compatibility issues when using libs within our components.
6
+
7
+ More details: [https://github.com/theKashey/multiple-entry-points-example](https://github.com/theKashey/multiple-entry-points-example)
@@ -0,0 +1,5 @@
1
+ {
2
+ "main": "../../dist/cjs/reactAriaKZ.cjs",
3
+ "module": "../../dist/esm/reactAriaKZ.mjs",
4
+ "types": "../../dist/types/__libs__/react-aria/index.d.ts"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "main": "../../dist/cjs/reactAriaComponentsKZ.cjs",
3
+ "module": "../../dist/esm/reactAriaComponentsKZ.mjs",
4
+ "types": "../../dist/types/__libs__/react-aria-components/index.d.ts"
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.81.0",
3
+ "version": "1.81.2",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -20,6 +20,7 @@
20
20
  "v1",
21
21
  "v2",
22
22
  "v3",
23
+ "libs",
23
24
  "dist",
24
25
  "icons",
25
26
  "locales",
@@ -47,6 +48,16 @@
47
48
  "import": "./dist/esm/next.mjs",
48
49
  "require": "./dist/cjs/next.cjs"
49
50
  },
51
+ "./libs/react-aria": {
52
+ "types": "./dist/types/__libs__/react-aria/index.d.ts",
53
+ "import": "./dist/esm/reactAriaKZ.mjs",
54
+ "require": "./dist/cjs/reactAriaKZ.cjs"
55
+ },
56
+ "./libs/react-aria-components": {
57
+ "types": "./dist/types/__libs__/react-aria-components/index.d.ts",
58
+ "import": "./dist/esm/reactAriaComponentsKZ.mjs",
59
+ "require": "./dist/cjs/reactAriaComponentsKZ.cjs"
60
+ },
50
61
  "./v1/actions": {
51
62
  "types": "./dist/types/v1-actions.d.ts",
52
63
  "import": "./dist/esm/actionsV1.mjs",
@@ -71,7 +71,7 @@ describe('<GenericModal />', () => {
71
71
 
72
72
  it('closes the modal when a click is outside of the modal content', async () => {
73
73
  const handleDismiss = vi.fn()
74
- render(<GenericModalWrapper onOutsideModalClick={handleDismiss} />)
74
+ render(<GenericModalWrapper onOutsideModalClick={handleDismiss()} />)
75
75
 
76
76
  await user.click(screen.getByTestId('GenericModalTestId-scrollLayer'))
77
77
  await waitFor(() => {
@@ -1,9 +1,10 @@
1
- import React, { useCallback, useEffect, useId, useState } from 'react'
1
+ import React, { useId, useRef } from 'react'
2
2
  import { createPortal } from 'react-dom'
3
3
  import { Transition } from '@headlessui/react'
4
4
  import classnames from 'classnames'
5
- import FocusLock from 'react-focus-lock'
5
+ import { FocusOn } from 'react-focus-on'
6
6
  import { useIsClientReady } from '../../utils/useIsClientReady'
7
+
7
8
  import { warn } from '../util/console'
8
9
  import { ModalContext } from './context/ModalContext'
9
10
  import styles from './GenericModal.module.scss'
@@ -38,39 +39,22 @@ export const GenericModal = ({
38
39
 
39
40
  const labelledByID = useId()
40
41
  const describedByID = useId()
41
-
42
42
  const isClientReady = useIsClientReady()
43
43
 
44
- const [scrollLayer, setScrollLayer] = useState<HTMLDivElement | null>(null)
45
- const [modalLayer, setModalLayer] = useState<HTMLDivElement | null>(null)
44
+ const scrollLayerRef = useRef<HTMLDivElement | null>(null)
45
+ const modalLayerRef = useRef<HTMLDivElement | null>(null)
46
46
 
47
47
  const scrollModalToTop = (): void => {
48
48
  // If we have a really long modal, the autofocus could land on an element down below
49
49
  // causing the modal to scroll down and skipping over the content near the modal's top.
50
50
  // Ensure that when the modal opens, we are at the top of its content.
51
51
  requestAnimationFrame(() => {
52
- if (!scrollLayer) return
53
- scrollLayer.scrollTop = 0
54
- })
55
- }
56
-
57
- const outsideModalClickHandler = (event: React.MouseEvent): void => {
58
- if (event.target === scrollLayer || event.target === modalLayer) {
59
- onOutsideModalClick?.(event)
60
- }
61
- }
62
-
63
- const focusOnAccessibleLabel = (): void => {
64
- if (!isClientReady) return
65
-
66
- // Check if focus already exists within the modal
67
- if (modalLayer?.contains(document.activeElement)) {
68
- return
69
- }
52
+ const scrollElement = scrollLayerRef.current
70
53
 
71
- const labelElement: HTMLElement | null = document.getElementById(labelledByID)
72
-
73
- labelElement?.focus()
54
+ // This little verbose of a check but this ensures that the element is attached to the DOM as it animates in. This additional check aims to avoid race conditions
55
+ if (!scrollElement?.isConnected) return
56
+ scrollElement.scrollTop = 0
57
+ })
74
58
  }
75
59
 
76
60
  const a11yWarn = (): void => {
@@ -86,60 +70,46 @@ export const GenericModal = ({
86
70
  }
87
71
  }
88
72
 
89
- const preventBodyScroll = (): void => {
73
+ const focusOnAccessibleLabel = (): void => {
90
74
  if (!isClientReady) return
75
+ const modalElement = modalLayerRef.current
76
+ if (!modalElement?.isConnected) return
91
77
 
92
- const hasScrollbar = window.innerWidth > document.documentElement.clientWidth
93
- const scrollStyles = [styles.unscrollable]
78
+ // Check if focus already exists within the modal
79
+ if (modalElement.contains(document.activeElement)) {
80
+ return
81
+ }
82
+
83
+ const labelElement: HTMLElement | null = document.getElementById(labelledByID)
94
84
 
95
- if (hasScrollbar) {
96
- scrollStyles.push(styles.pseudoScrollbar)
85
+ if (labelElement?.isConnected) {
86
+ labelElement.focus()
97
87
  }
88
+ }
98
89
 
99
- document.documentElement.classList.add(...scrollStyles)
90
+ const onEscapeKeyHandler = (e: Event): void => {
91
+ if (e instanceof KeyboardEvent) {
92
+ onEscapeKeyup?.(e)
93
+ }
100
94
  }
101
95
 
102
96
  const onAfterEnterHandler = (): void => {
103
97
  scrollModalToTop()
104
- if (modalLayer) {
98
+ const modalElement = modalLayerRef.current
99
+ if (modalElement) {
105
100
  onAfterEnter?.()
106
101
  focusOnAccessibleLabel()
107
102
  a11yWarn()
108
103
  }
109
104
  }
110
105
 
111
- const escapeKeyHandler = useCallback(
112
- (event: KeyboardEvent): void => {
113
- if (event.key === 'Escape') {
114
- onEscapeKeyup?.(event)
115
- }
116
- },
117
- [onEscapeKeyup],
118
- )
119
-
120
- const onBeforeEnterHandler = (): void => {
121
- preventBodyScroll()
122
-
123
- if (onEscapeKeyup && isClientReady) {
124
- document.addEventListener('keyup', escapeKeyHandler)
106
+ const outsideModalClickHandler = (e: React.MouseEvent): void => {
107
+ if (e.target === scrollLayerRef.current || e.target === modalLayerRef.current) {
108
+ onOutsideModalClick?.(e)
125
109
  }
126
110
  }
127
111
 
128
- const cleanUpAfterClose = useCallback(() => {
129
- if (!isClientReady) return
130
-
131
- document.documentElement.classList.remove(styles.unscrollable, styles.pseudoScrollbar)
132
-
133
- if (onEscapeKeyup) {
134
- document.removeEventListener('keyup', escapeKeyHandler)
135
- }
136
- }, [escapeKeyHandler, onEscapeKeyup, isClientReady])
137
-
138
- /* Ensure sure add-on styles (e.g. unscrollable) and key event is cleaned up when the modal is unmounted*/
139
- useEffect(() => () => cleanUpAfterClose(), [cleanUpAfterClose])
140
-
141
112
  const onAfterLeaveHandler = (): void => {
142
- cleanUpAfterClose()
143
113
  propsOnAfterLeave?.()
144
114
  }
145
115
 
@@ -152,7 +122,6 @@ export const GenericModal = ({
152
122
  <Transition
153
123
  appear={true}
154
124
  show={isOpen}
155
- beforeEnter={onBeforeEnterHandler}
156
125
  afterEnter={onAfterEnterHandler}
157
126
  afterLeave={onAfterLeaveHandler}
158
127
  data-generic-modal-transition-wrapper
@@ -161,9 +130,10 @@ export const GenericModal = ({
161
130
  as="div"
162
131
  className={classnames(styles.transitionLayer, className)}
163
132
  >
164
- <FocusLock
165
- disabled={focusLockDisabled}
133
+ <FocusOn
134
+ focusLock={focusLockDisabled}
166
135
  returnFocus={true}
136
+ onEscapeKey={onEscapeKeyHandler}
167
137
  // Disabling false positive
168
138
  // eslint-disable-next-line jsx-a11y/no-autofocus
169
139
  autoFocus={false}
@@ -174,11 +144,9 @@ export const GenericModal = ({
174
144
  {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
175
145
  <div
176
146
  className={styles.scrollLayer}
177
- ref={(scrollLayerRef): void => {
178
- setScrollLayer(scrollLayerRef)
179
- }}
180
- onClick={outsideModalClickHandler}
147
+ ref={scrollLayerRef}
181
148
  data-testid={`${id}-scrollLayer`}
149
+ onClick={outsideModalClickHandler}
182
150
  >
183
151
  <ModalContext.Provider
184
152
  value={{
@@ -191,14 +159,14 @@ export const GenericModal = ({
191
159
  className={styles.modalLayer}
192
160
  aria-labelledby={labelledByID}
193
161
  aria-describedby={describedByID}
194
- ref={(modalLayerRef): void => setModalLayer(modalLayerRef)}
162
+ ref={modalLayerRef}
195
163
  data-testid={id}
196
164
  >
197
165
  {children}
198
166
  </div>
199
167
  </ModalContext.Provider>
200
168
  </div>
201
- </FocusLock>
169
+ </FocusOn>
202
170
  </Transition>,
203
171
  document.body,
204
172
  )
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from 'react'
2
2
  import { type StoryObj } from '@storybook/react'
3
- import { expect, userEvent, within } from '@storybook/test'
3
+ import { expect, userEvent, waitFor, within } from '@storybook/test'
4
4
  import { type EditorContentArray } from '../types'
5
5
  import { RichTextEditor, type RichTextEditorProps } from './RichTextEditor'
6
6
 
@@ -149,8 +149,9 @@ export const CreateALink: Story = {
149
149
  name: 'Create a link',
150
150
  play: async (context) => {
151
151
  const { canvasElement, step } = context
152
- const { getByRole, getByText } = within(canvasElement)
152
+ const { getByRole, getByText, queryByRole, findByRole } = within(canvasElement)
153
153
  const editor = getByRole('textbox')
154
+
154
155
  await step('Focus on editor', async () => {
155
156
  await userEvent.click(editor)
156
157
  expect(editor).toHaveFocus()
@@ -185,8 +186,14 @@ export const CreateALink: Story = {
185
186
  await userEvent.keyboard('{Tab}{Enter}')
186
187
  })
187
188
 
189
+ await step('The Link Modal closes', async () => {
190
+ await waitFor(() => {
191
+ expect(queryByRole('dialog')).not.toBeInTheDocument()
192
+ })
193
+ })
194
+
188
195
  await step('Link exists in the RTE', async () => {
189
- const link = getByRole('link', { name: 'Link' })
196
+ const link = await findByRole('link', { name: 'Link' })
190
197
  expect(link).toBeInTheDocument()
191
198
  })
192
199
  },
@@ -0,0 +1 @@
1
+ export * from 'react-aria'
@@ -0,0 +1 @@
1
+ export * from 'react-aria-components'