@atlaskit/popup 1.22.2 → 1.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/cjs/popper-wrapper.js +14 -4
- package/dist/cjs/popup.js +5 -1
- package/dist/cjs/use-close-manager.js +104 -15
- package/dist/cjs/use-focus-manager.js +27 -11
- package/dist/cjs/utils/is-element-interactive.js +16 -0
- package/dist/cjs/utils/use-animation-frame.js +32 -0
- package/dist/es2019/popper-wrapper.js +13 -4
- package/dist/es2019/popup.js +4 -1
- package/dist/es2019/use-close-manager.js +109 -17
- package/dist/es2019/use-focus-manager.js +28 -11
- package/dist/es2019/utils/is-element-interactive.js +10 -0
- package/dist/es2019/utils/use-animation-frame.js +22 -0
- package/dist/esm/popper-wrapper.js +14 -4
- package/dist/esm/popup.js +5 -1
- package/dist/esm/use-close-manager.js +104 -15
- package/dist/esm/use-focus-manager.js +27 -11
- package/dist/esm/utils/is-element-interactive.js +10 -0
- package/dist/esm/utils/use-animation-frame.js +26 -0
- package/dist/types/popper-wrapper.d.ts +1 -1
- package/dist/types/types.d.ts +14 -2
- package/dist/types/use-close-manager.d.ts +1 -1
- package/dist/types/use-focus-manager.d.ts +1 -1
- package/dist/types/utils/is-element-interactive.d.ts +1 -0
- package/dist/types/utils/use-animation-frame.d.ts +5 -0
- package/dist/types-ts4.5/popper-wrapper.d.ts +1 -1
- package/dist/types-ts4.5/types.d.ts +14 -2
- package/dist/types-ts4.5/use-close-manager.d.ts +1 -1
- package/dist/types-ts4.5/use-focus-manager.d.ts +1 -1
- package/dist/types-ts4.5/utils/is-element-interactive.d.ts +1 -0
- package/dist/types-ts4.5/utils/use-animation-frame.d.ts +5 -0
- package/package.json +9 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @atlaskit/popup
|
|
2
2
|
|
|
3
|
+
## 1.23.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#133686](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/133686)
|
|
8
|
+
[`462353527b0db`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/462353527b0db) -
|
|
9
|
+
Expose shouldReturnFocus on Popup component to allow consumers to prevent trigger refocusing on
|
|
10
|
+
popup close
|
|
11
|
+
|
|
12
|
+
## 1.23.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- [#128022](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/128022)
|
|
17
|
+
[`1495b8f9c9253`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/1495b8f9c9253) -
|
|
18
|
+
[ux] We are testing new focus behavior in non-dialog popup instances behind a feature flag. With
|
|
19
|
+
that in place, all popup instances that don't have role="dialog" applied will have focus traps
|
|
20
|
+
disabled by default. If this fix is successful, it will be available in a later release.
|
|
21
|
+
|
|
3
22
|
## 1.22.2
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -91,6 +91,8 @@ function PopperWrapper(_ref) {
|
|
|
91
91
|
shouldRenderToParent = _ref.shouldRenderToParent,
|
|
92
92
|
shouldFitContainer = _ref.shouldFitContainer,
|
|
93
93
|
shouldDisableFocusLock = _ref.shouldDisableFocusLock,
|
|
94
|
+
_ref$shouldReturnFocu = _ref.shouldReturnFocus,
|
|
95
|
+
shouldReturnFocus = _ref$shouldReturnFocu === void 0 ? true : _ref$shouldReturnFocu,
|
|
94
96
|
strategy = _ref.strategy,
|
|
95
97
|
role = _ref.role,
|
|
96
98
|
label = _ref.label,
|
|
@@ -105,13 +107,18 @@ function PopperWrapper(_ref) {
|
|
|
105
107
|
initialFocusRef = _useState4[0],
|
|
106
108
|
setInitialFocusRef = _useState4[1];
|
|
107
109
|
|
|
108
|
-
// We have cases
|
|
109
|
-
//
|
|
110
|
+
// We have cases where we need to close the Popup on Tab press.
|
|
111
|
+
// Example: DropdownMenu
|
|
110
112
|
var shouldCloseOnTab = shouldRenderToParent && shouldDisableFocusLock;
|
|
113
|
+
var shouldDisableFocusTrap = role !== 'dialog';
|
|
111
114
|
(0, _useFocusManager.useFocusManager)({
|
|
112
115
|
initialFocusRef: initialFocusRef,
|
|
113
116
|
popupRef: popupRef,
|
|
114
|
-
shouldCloseOnTab: shouldCloseOnTab
|
|
117
|
+
shouldCloseOnTab: shouldCloseOnTab,
|
|
118
|
+
triggerRef: triggerRef,
|
|
119
|
+
autoFocus: autoFocus,
|
|
120
|
+
shouldDisableFocusTrap: shouldDisableFocusTrap,
|
|
121
|
+
shouldReturnFocus: shouldReturnFocus
|
|
115
122
|
});
|
|
116
123
|
(0, _useCloseManager.useCloseManager)({
|
|
117
124
|
isOpen: isOpen,
|
|
@@ -119,7 +126,10 @@ function PopperWrapper(_ref) {
|
|
|
119
126
|
popupRef: popupRef,
|
|
120
127
|
triggerRef: triggerRef,
|
|
121
128
|
shouldUseCaptureOnOutsideClick: shouldUseCaptureOnOutsideClick,
|
|
122
|
-
shouldCloseOnTab: shouldCloseOnTab
|
|
129
|
+
shouldCloseOnTab: shouldCloseOnTab,
|
|
130
|
+
autoFocus: autoFocus,
|
|
131
|
+
shouldDisableFocusTrap: shouldDisableFocusTrap,
|
|
132
|
+
shouldRenderToParent: shouldRenderToParent
|
|
123
133
|
});
|
|
124
134
|
var _UNSAFE_useLayering = (0, _layering.UNSAFE_useLayering)(),
|
|
125
135
|
currentLevel = _UNSAFE_useLayering.currentLevel;
|
package/dist/cjs/popup.js
CHANGED
|
@@ -10,6 +10,7 @@ var _react = require("react");
|
|
|
10
10
|
var _react2 = require("@emotion/react");
|
|
11
11
|
var _reactUid = require("react-uid");
|
|
12
12
|
var _layering = require("@atlaskit/layering");
|
|
13
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
13
14
|
var _popper = require("@atlaskit/popper");
|
|
14
15
|
var _portal = _interopRequireDefault(require("@atlaskit/portal"));
|
|
15
16
|
var _primitives = require("@atlaskit/primitives");
|
|
@@ -57,6 +58,8 @@ var Popup = exports.Popup = /*#__PURE__*/(0, _react.memo)(function (_ref) {
|
|
|
57
58
|
shouldFitContainer = _ref$shouldFitContain === void 0 ? false : _ref$shouldFitContain,
|
|
58
59
|
_ref$shouldDisableFoc = _ref.shouldDisableFocusLock,
|
|
59
60
|
shouldDisableFocusLock = _ref$shouldDisableFoc === void 0 ? false : _ref$shouldDisableFoc,
|
|
61
|
+
_ref$shouldReturnFocu = _ref.shouldReturnFocus,
|
|
62
|
+
shouldReturnFocus = _ref$shouldReturnFocu === void 0 ? true : _ref$shouldReturnFocu,
|
|
60
63
|
strategy = _ref.strategy,
|
|
61
64
|
role = _ref.role,
|
|
62
65
|
label = _ref.label,
|
|
@@ -89,6 +92,7 @@ var Popup = exports.Popup = /*#__PURE__*/(0, _react.memo)(function (_ref) {
|
|
|
89
92
|
shouldRenderToParent: shouldRenderToParent || shouldFitContainer,
|
|
90
93
|
shouldFitContainer: shouldFitContainer,
|
|
91
94
|
shouldDisableFocusLock: shouldDisableFocusLock,
|
|
95
|
+
shouldReturnFocus: shouldReturnFocus,
|
|
92
96
|
triggerRef: triggerRef,
|
|
93
97
|
strategy: shouldFitContainer ? 'absolute' : strategy,
|
|
94
98
|
role: role,
|
|
@@ -102,7 +106,7 @@ var Popup = exports.Popup = /*#__PURE__*/(0, _react.memo)(function (_ref) {
|
|
|
102
106
|
ref: getMergedTriggerRef(ref, setTriggerRef, isOpen),
|
|
103
107
|
'aria-controls': isOpen ? id : undefined,
|
|
104
108
|
'aria-expanded': isOpen,
|
|
105
|
-
'aria-haspopup': true
|
|
109
|
+
'aria-haspopup': role === 'dialog' && (0, _platformFeatureFlags.fg)('platform_dst_popup-disable-focuslock') ? 'dialog' : true
|
|
106
110
|
});
|
|
107
111
|
}), isOpen && (shouldRenderToParent || shouldFitContainer ? renderPopperWrapper : (0, _react2.jsx)(_portal.default, {
|
|
108
112
|
zIndex: zIndex
|
|
@@ -9,6 +9,9 @@ var _react = require("react");
|
|
|
9
9
|
var _bindEventListener = require("bind-event-listener");
|
|
10
10
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
11
11
|
var _layering = require("@atlaskit/layering");
|
|
12
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
13
|
+
var _isElementInteractive = require("./utils/is-element-interactive");
|
|
14
|
+
var _useAnimationFrame2 = require("./utils/use-animation-frame");
|
|
12
15
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
13
16
|
|
|
14
17
|
var useCloseManager = exports.useCloseManager = function useCloseManager(_ref) {
|
|
@@ -16,11 +19,17 @@ var useCloseManager = exports.useCloseManager = function useCloseManager(_ref) {
|
|
|
16
19
|
onClose = _ref.onClose,
|
|
17
20
|
popupRef = _ref.popupRef,
|
|
18
21
|
triggerRef = _ref.triggerRef,
|
|
22
|
+
autoFocus = _ref.autoFocus,
|
|
23
|
+
shouldDisableFocusTrap = _ref.shouldDisableFocusTrap,
|
|
19
24
|
capture = _ref.shouldUseCaptureOnOutsideClick,
|
|
20
|
-
shouldCloseOnTab = _ref.shouldCloseOnTab
|
|
25
|
+
shouldCloseOnTab = _ref.shouldCloseOnTab,
|
|
26
|
+
shouldRenderToParent = _ref.shouldRenderToParent;
|
|
21
27
|
var _UNSAFE_useLayering = (0, _layering.UNSAFE_useLayering)(),
|
|
22
28
|
isLayerDisabled = _UNSAFE_useLayering.isLayerDisabled,
|
|
23
29
|
currentLevel = _UNSAFE_useLayering.currentLevel;
|
|
30
|
+
var _useAnimationFrame = (0, _useAnimationFrame2.useAnimationFrame)(),
|
|
31
|
+
requestFrame = _useAnimationFrame.requestFrame,
|
|
32
|
+
cancelAllFrames = _useAnimationFrame.cancelAllFrames;
|
|
24
33
|
(0, _react.useEffect)(function () {
|
|
25
34
|
if (!isOpen || !popupRef) {
|
|
26
35
|
return _noop.default;
|
|
@@ -29,6 +38,13 @@ var useCloseManager = exports.useCloseManager = function useCloseManager(_ref) {
|
|
|
29
38
|
if (onClose) {
|
|
30
39
|
onClose(event);
|
|
31
40
|
}
|
|
41
|
+
if (shouldDisableFocusTrap && (0, _platformFeatureFlags.fg)('platform_dst_popup-disable-focuslock')) {
|
|
42
|
+
// Restoring the normal focus order for trigger.
|
|
43
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.setAttribute('tabindex', '0');
|
|
44
|
+
if (popupRef && autoFocus) {
|
|
45
|
+
popupRef.setAttribute('tabindex', '0');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
32
48
|
};
|
|
33
49
|
|
|
34
50
|
// This check is required for cases where components like
|
|
@@ -44,23 +60,98 @@ var useCloseManager = exports.useCloseManager = function useCloseManager(_ref) {
|
|
|
44
60
|
if (!doesDomNodeExist) {
|
|
45
61
|
return;
|
|
46
62
|
}
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
if ((0, _platformFeatureFlags.fg)('platform_dst_popup-disable-focuslock')) {
|
|
64
|
+
var _document$activeEleme;
|
|
65
|
+
if (isLayerDisabled() && (_document$activeEleme = document.activeElement) !== null && _document$activeEleme !== void 0 && _document$activeEleme.closest('[aria-modal]')) {
|
|
66
|
+
//if it is a disabled layer, we need to disable its click listener.
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
if (isLayerDisabled()) {
|
|
71
|
+
//if it is a disabled layer, we need to disable its click listener.
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
50
74
|
}
|
|
51
75
|
var isClickOnPopup = popupRef && popupRef.contains(target);
|
|
52
76
|
var isClickOnTrigger = triggerRef && triggerRef.contains(target);
|
|
53
77
|
if (!isClickOnPopup && !isClickOnTrigger) {
|
|
54
78
|
closePopup(event);
|
|
79
|
+
// If there was an outside click on a non-interactive element, the focus should be on the trigger.
|
|
80
|
+
if (document.activeElement && !(0, _isElementInteractive.isInteractiveElement)(document.activeElement) && (0, _platformFeatureFlags.fg)('platform_dst_popup-disable-focuslock')) {
|
|
81
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.focus();
|
|
82
|
+
}
|
|
55
83
|
}
|
|
56
84
|
};
|
|
57
85
|
var onKeyDown = function onKeyDown(event) {
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
if ((0, _platformFeatureFlags.fg)('platform_dst_popup-disable-focuslock')) {
|
|
87
|
+
var key = event.key,
|
|
88
|
+
shiftKey = event.shiftKey;
|
|
89
|
+
if (shiftKey && key === 'Tab' && !shouldRenderToParent) {
|
|
90
|
+
if (isLayerDisabled()) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// We need to move the focus to the popup trigger when the popup is displayed in React.Portal.
|
|
94
|
+
requestFrame(function () {
|
|
95
|
+
var isPopupFocusOut = popupRef && !popupRef.contains(document.activeElement);
|
|
96
|
+
if (isPopupFocusOut) {
|
|
97
|
+
closePopup(event);
|
|
98
|
+
if (currentLevel === 1) {
|
|
99
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.focus();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (key === 'Tab') {
|
|
106
|
+
var _document$activeEleme2;
|
|
107
|
+
// We have cases where we need to close the Popup on Tab press.
|
|
108
|
+
// Example: DropdownMenu
|
|
109
|
+
if (shouldCloseOnTab) {
|
|
110
|
+
if (isLayerDisabled()) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
closePopup(event);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (isLayerDisabled() && (_document$activeEleme2 = document.activeElement) !== null && _document$activeEleme2 !== void 0 && _document$activeEleme2.closest('[aria-modal]')) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (shouldDisableFocusTrap) {
|
|
120
|
+
if (shouldRenderToParent) {
|
|
121
|
+
// We need to move the focus to the previous interactive element before popup trigger
|
|
122
|
+
requestFrame(function () {
|
|
123
|
+
var isPopupFocusOut = popupRef && !popupRef.contains(document.activeElement);
|
|
124
|
+
if (isPopupFocusOut) {
|
|
125
|
+
closePopup(event);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
requestFrame(function () {
|
|
130
|
+
if (!document.hasFocus()) {
|
|
131
|
+
closePopup(event);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (isLayerDisabled()) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (key === 'Escape' || key === 'Esc') {
|
|
142
|
+
if (triggerRef && autoFocus) {
|
|
143
|
+
triggerRef.focus();
|
|
144
|
+
}
|
|
145
|
+
closePopup(event);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
if (isLayerDisabled()) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
var _key = event.key;
|
|
152
|
+
if (_key === 'Escape' || _key === 'Esc' || shouldCloseOnTab && _key === 'Tab') {
|
|
153
|
+
closePopup(event);
|
|
154
|
+
}
|
|
64
155
|
}
|
|
65
156
|
};
|
|
66
157
|
var unbind = (0, _bindEventListener.bindAll)(window, [{
|
|
@@ -82,15 +173,13 @@ var useCloseManager = exports.useCloseManager = function useCloseManager(_ref) {
|
|
|
82
173
|
if (isLayerDisabled() || !(document.activeElement instanceof HTMLIFrameElement)) {
|
|
83
174
|
return;
|
|
84
175
|
}
|
|
85
|
-
|
|
86
|
-
if (!wrapper || currentLevel > Number(wrapper.getAttribute('data-ds--level'))) {
|
|
87
|
-
closePopup(e);
|
|
88
|
-
}
|
|
176
|
+
closePopup(e);
|
|
89
177
|
}
|
|
90
178
|
});
|
|
91
179
|
return function () {
|
|
180
|
+
cancelAllFrames();
|
|
92
181
|
unbind();
|
|
93
182
|
unbindBlur();
|
|
94
183
|
};
|
|
95
|
-
}, [isOpen, onClose, popupRef, triggerRef, capture, isLayerDisabled, shouldCloseOnTab, currentLevel]);
|
|
184
|
+
}, [isOpen, onClose, popupRef, triggerRef, autoFocus, shouldDisableFocusTrap, capture, isLayerDisabled, shouldCloseOnTab, currentLevel, shouldRenderToParent, requestFrame, cancelAllFrames]);
|
|
96
185
|
};
|
|
@@ -8,35 +8,51 @@ exports.useFocusManager = void 0;
|
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
var _focusTrap = _interopRequireDefault(require("focus-trap"));
|
|
10
10
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
11
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
12
|
+
var _useAnimationFrame2 = require("./utils/use-animation-frame");
|
|
11
13
|
var useFocusManager = exports.useFocusManager = function useFocusManager(_ref) {
|
|
12
14
|
var initialFocusRef = _ref.initialFocusRef,
|
|
13
15
|
popupRef = _ref.popupRef,
|
|
14
|
-
|
|
16
|
+
triggerRef = _ref.triggerRef,
|
|
17
|
+
autoFocus = _ref.autoFocus,
|
|
18
|
+
shouldCloseOnTab = _ref.shouldCloseOnTab,
|
|
19
|
+
shouldDisableFocusTrap = _ref.shouldDisableFocusTrap,
|
|
20
|
+
shouldReturnFocus = _ref.shouldReturnFocus;
|
|
21
|
+
var _useAnimationFrame = (0, _useAnimationFrame2.useAnimationFrame)(),
|
|
22
|
+
requestFrame = _useAnimationFrame.requestFrame,
|
|
23
|
+
cancelAllFrames = _useAnimationFrame.cancelAllFrames;
|
|
15
24
|
(0, _react.useEffect)(function () {
|
|
16
25
|
if (!popupRef || shouldCloseOnTab) {
|
|
17
26
|
return _noop.default;
|
|
18
27
|
}
|
|
28
|
+
if (shouldDisableFocusTrap && (0, _platformFeatureFlags.fg)('platform_dst_popup-disable-focuslock')) {
|
|
29
|
+
// Plucking trigger & popup content container from the tab order so that
|
|
30
|
+
// when we Shift+Tab, the focus moves to the element before trigger
|
|
31
|
+
requestFrame(function () {
|
|
32
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.setAttribute('tabindex', '-1');
|
|
33
|
+
if (popupRef && autoFocus) {
|
|
34
|
+
popupRef.setAttribute('tabindex', '-1');
|
|
35
|
+
}
|
|
36
|
+
(initialFocusRef || popupRef).focus();
|
|
37
|
+
});
|
|
38
|
+
return _noop.default;
|
|
39
|
+
}
|
|
19
40
|
var trapConfig = {
|
|
20
41
|
clickOutsideDeactivates: true,
|
|
21
42
|
escapeDeactivates: true,
|
|
22
43
|
initialFocus: initialFocusRef || popupRef,
|
|
23
44
|
fallbackFocus: popupRef,
|
|
24
|
-
returnFocusOnDeactivate:
|
|
45
|
+
returnFocusOnDeactivate: shouldReturnFocus
|
|
25
46
|
};
|
|
26
47
|
var focusTrap = (0, _focusTrap.default)(popupRef, trapConfig);
|
|
27
|
-
var frameId = null;
|
|
28
48
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
frameId = null;
|
|
49
|
+
// Wait for the popup to reposition itself before we focus
|
|
50
|
+
requestFrame(function () {
|
|
32
51
|
focusTrap.activate();
|
|
33
52
|
});
|
|
34
53
|
return function () {
|
|
35
|
-
|
|
36
|
-
cancelAnimationFrame(frameId);
|
|
37
|
-
frameId = null;
|
|
38
|
-
}
|
|
54
|
+
cancelAllFrames();
|
|
39
55
|
focusTrap.deactivate();
|
|
40
56
|
};
|
|
41
|
-
}, [popupRef, initialFocusRef, shouldCloseOnTab]);
|
|
57
|
+
}, [popupRef, triggerRef, autoFocus, initialFocusRef, shouldCloseOnTab, shouldDisableFocusTrap, requestFrame, cancelAllFrames, shouldReturnFocus]);
|
|
42
58
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isInteractiveElement = void 0;
|
|
7
|
+
var interactiveTags = ['button', 'a', 'input', 'select', 'textarea'];
|
|
8
|
+
var isInteractiveElement = exports.isInteractiveElement = function isInteractiveElement(element) {
|
|
9
|
+
if (interactiveTags.includes(element.tagName.toLowerCase())) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (element.getAttribute('tabindex') !== null || element.hasAttribute('contenteditable')) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useAnimationFrame = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var useAnimationFrame = exports.useAnimationFrame = function useAnimationFrame() {
|
|
9
|
+
var animationsRef = (0, _react.useRef)([]);
|
|
10
|
+
var requestFrame = (0, _react.useCallback)(function (callback) {
|
|
11
|
+
var id = requestAnimationFrame(callback);
|
|
12
|
+
animationsRef.current.push(id);
|
|
13
|
+
return id;
|
|
14
|
+
}, []);
|
|
15
|
+
var cancelFrame = (0, _react.useCallback)(function (id) {
|
|
16
|
+
cancelAnimationFrame(id);
|
|
17
|
+
animationsRef.current = animationsRef.current.filter(function (frameId) {
|
|
18
|
+
return frameId !== id;
|
|
19
|
+
});
|
|
20
|
+
}, []);
|
|
21
|
+
var cancelAllFrames = (0, _react.useCallback)(function () {
|
|
22
|
+
animationsRef.current.forEach(function (id) {
|
|
23
|
+
return cancelAnimationFrame(id);
|
|
24
|
+
});
|
|
25
|
+
animationsRef.current = [];
|
|
26
|
+
}, []);
|
|
27
|
+
return {
|
|
28
|
+
requestFrame: requestFrame,
|
|
29
|
+
cancelFrame: cancelFrame,
|
|
30
|
+
cancelAllFrames: cancelAllFrames
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -81,6 +81,7 @@ function PopperWrapper({
|
|
|
81
81
|
shouldRenderToParent,
|
|
82
82
|
shouldFitContainer,
|
|
83
83
|
shouldDisableFocusLock,
|
|
84
|
+
shouldReturnFocus = true,
|
|
84
85
|
strategy,
|
|
85
86
|
role,
|
|
86
87
|
label,
|
|
@@ -90,13 +91,18 @@ function PopperWrapper({
|
|
|
90
91
|
const [popupRef, setPopupRef] = useState(null);
|
|
91
92
|
const [initialFocusRef, setInitialFocusRef] = useState(null);
|
|
92
93
|
|
|
93
|
-
// We have cases
|
|
94
|
-
//
|
|
94
|
+
// We have cases where we need to close the Popup on Tab press.
|
|
95
|
+
// Example: DropdownMenu
|
|
95
96
|
const shouldCloseOnTab = shouldRenderToParent && shouldDisableFocusLock;
|
|
97
|
+
const shouldDisableFocusTrap = role !== 'dialog';
|
|
96
98
|
useFocusManager({
|
|
97
99
|
initialFocusRef,
|
|
98
100
|
popupRef,
|
|
99
|
-
shouldCloseOnTab
|
|
101
|
+
shouldCloseOnTab,
|
|
102
|
+
triggerRef,
|
|
103
|
+
autoFocus,
|
|
104
|
+
shouldDisableFocusTrap,
|
|
105
|
+
shouldReturnFocus
|
|
100
106
|
});
|
|
101
107
|
useCloseManager({
|
|
102
108
|
isOpen,
|
|
@@ -104,7 +110,10 @@ function PopperWrapper({
|
|
|
104
110
|
popupRef,
|
|
105
111
|
triggerRef,
|
|
106
112
|
shouldUseCaptureOnOutsideClick,
|
|
107
|
-
shouldCloseOnTab
|
|
113
|
+
shouldCloseOnTab,
|
|
114
|
+
autoFocus,
|
|
115
|
+
shouldDisableFocusTrap,
|
|
116
|
+
shouldRenderToParent
|
|
108
117
|
});
|
|
109
118
|
const {
|
|
110
119
|
currentLevel
|
package/dist/es2019/popup.js
CHANGED
|
@@ -9,6 +9,7 @@ import { memo, useState } from 'react';
|
|
|
9
9
|
import { jsx } from '@emotion/react';
|
|
10
10
|
import { useUID } from 'react-uid';
|
|
11
11
|
import { UNSAFE_LAYERING } from '@atlaskit/layering';
|
|
12
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
12
13
|
import { Manager, Reference } from '@atlaskit/popper';
|
|
13
14
|
import Portal from '@atlaskit/portal';
|
|
14
15
|
import { Box, xcss } from '@atlaskit/primitives';
|
|
@@ -39,6 +40,7 @@ export const Popup = /*#__PURE__*/memo(({
|
|
|
39
40
|
shouldRenderToParent = false,
|
|
40
41
|
shouldFitContainer = false,
|
|
41
42
|
shouldDisableFocusLock = false,
|
|
43
|
+
shouldReturnFocus = true,
|
|
42
44
|
strategy,
|
|
43
45
|
role,
|
|
44
46
|
label,
|
|
@@ -69,6 +71,7 @@ export const Popup = /*#__PURE__*/memo(({
|
|
|
69
71
|
shouldRenderToParent: shouldRenderToParent || shouldFitContainer,
|
|
70
72
|
shouldFitContainer: shouldFitContainer,
|
|
71
73
|
shouldDisableFocusLock: shouldDisableFocusLock,
|
|
74
|
+
shouldReturnFocus: shouldReturnFocus,
|
|
72
75
|
triggerRef: triggerRef,
|
|
73
76
|
strategy: shouldFitContainer ? 'absolute' : strategy,
|
|
74
77
|
role: role,
|
|
@@ -83,7 +86,7 @@ export const Popup = /*#__PURE__*/memo(({
|
|
|
83
86
|
ref: getMergedTriggerRef(ref, setTriggerRef, isOpen),
|
|
84
87
|
'aria-controls': isOpen ? id : undefined,
|
|
85
88
|
'aria-expanded': isOpen,
|
|
86
|
-
'aria-haspopup': true
|
|
89
|
+
'aria-haspopup': role === 'dialog' && fg('platform_dst_popup-disable-focuslock') ? 'dialog' : true
|
|
87
90
|
});
|
|
88
91
|
}), isOpen && (shouldRenderToParent || shouldFitContainer ? renderPopperWrapper : jsx(Portal, {
|
|
89
92
|
zIndex: zIndex
|
|
@@ -3,18 +3,28 @@ import { useEffect } from 'react';
|
|
|
3
3
|
import { bind, bindAll } from 'bind-event-listener';
|
|
4
4
|
import noop from '@atlaskit/ds-lib/noop';
|
|
5
5
|
import { UNSAFE_useLayering } from '@atlaskit/layering';
|
|
6
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
|
+
import { isInteractiveElement } from './utils/is-element-interactive';
|
|
8
|
+
import { useAnimationFrame } from './utils/use-animation-frame';
|
|
6
9
|
export const useCloseManager = ({
|
|
7
10
|
isOpen,
|
|
8
11
|
onClose,
|
|
9
12
|
popupRef,
|
|
10
13
|
triggerRef,
|
|
14
|
+
autoFocus,
|
|
15
|
+
shouldDisableFocusTrap,
|
|
11
16
|
shouldUseCaptureOnOutsideClick: capture,
|
|
12
|
-
shouldCloseOnTab
|
|
17
|
+
shouldCloseOnTab,
|
|
18
|
+
shouldRenderToParent
|
|
13
19
|
}) => {
|
|
14
20
|
const {
|
|
15
21
|
isLayerDisabled,
|
|
16
22
|
currentLevel
|
|
17
23
|
} = UNSAFE_useLayering();
|
|
24
|
+
const {
|
|
25
|
+
requestFrame,
|
|
26
|
+
cancelAllFrames
|
|
27
|
+
} = useAnimationFrame();
|
|
18
28
|
useEffect(() => {
|
|
19
29
|
if (!isOpen || !popupRef) {
|
|
20
30
|
return noop;
|
|
@@ -23,6 +33,13 @@ export const useCloseManager = ({
|
|
|
23
33
|
if (onClose) {
|
|
24
34
|
onClose(event);
|
|
25
35
|
}
|
|
36
|
+
if (shouldDisableFocusTrap && fg('platform_dst_popup-disable-focuslock')) {
|
|
37
|
+
// Restoring the normal focus order for trigger.
|
|
38
|
+
triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.setAttribute('tabindex', '0');
|
|
39
|
+
if (popupRef && autoFocus) {
|
|
40
|
+
popupRef.setAttribute('tabindex', '0');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
26
43
|
};
|
|
27
44
|
|
|
28
45
|
// This check is required for cases where components like
|
|
@@ -40,25 +57,102 @@ export const useCloseManager = ({
|
|
|
40
57
|
if (!doesDomNodeExist) {
|
|
41
58
|
return;
|
|
42
59
|
}
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
if (fg('platform_dst_popup-disable-focuslock')) {
|
|
61
|
+
var _document$activeEleme;
|
|
62
|
+
if (isLayerDisabled() && (_document$activeEleme = document.activeElement) !== null && _document$activeEleme !== void 0 && _document$activeEleme.closest('[aria-modal]')) {
|
|
63
|
+
//if it is a disabled layer, we need to disable its click listener.
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
if (isLayerDisabled()) {
|
|
68
|
+
//if it is a disabled layer, we need to disable its click listener.
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
46
71
|
}
|
|
47
72
|
const isClickOnPopup = popupRef && popupRef.contains(target);
|
|
48
73
|
const isClickOnTrigger = triggerRef && triggerRef.contains(target);
|
|
49
74
|
if (!isClickOnPopup && !isClickOnTrigger) {
|
|
50
75
|
closePopup(event);
|
|
76
|
+
// If there was an outside click on a non-interactive element, the focus should be on the trigger.
|
|
77
|
+
if (document.activeElement && !isInteractiveElement(document.activeElement) && fg('platform_dst_popup-disable-focuslock')) {
|
|
78
|
+
triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.focus();
|
|
79
|
+
}
|
|
51
80
|
}
|
|
52
81
|
};
|
|
53
82
|
const onKeyDown = event => {
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
if (fg('platform_dst_popup-disable-focuslock')) {
|
|
84
|
+
const {
|
|
85
|
+
key,
|
|
86
|
+
shiftKey
|
|
87
|
+
} = event;
|
|
88
|
+
if (shiftKey && key === 'Tab' && !shouldRenderToParent) {
|
|
89
|
+
if (isLayerDisabled()) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// We need to move the focus to the popup trigger when the popup is displayed in React.Portal.
|
|
93
|
+
requestFrame(() => {
|
|
94
|
+
const isPopupFocusOut = popupRef && !popupRef.contains(document.activeElement);
|
|
95
|
+
if (isPopupFocusOut) {
|
|
96
|
+
closePopup(event);
|
|
97
|
+
if (currentLevel === 1) {
|
|
98
|
+
triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.focus();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (key === 'Tab') {
|
|
105
|
+
var _document$activeEleme2;
|
|
106
|
+
// We have cases where we need to close the Popup on Tab press.
|
|
107
|
+
// Example: DropdownMenu
|
|
108
|
+
if (shouldCloseOnTab) {
|
|
109
|
+
if (isLayerDisabled()) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
closePopup(event);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (isLayerDisabled() && (_document$activeEleme2 = document.activeElement) !== null && _document$activeEleme2 !== void 0 && _document$activeEleme2.closest('[aria-modal]')) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (shouldDisableFocusTrap) {
|
|
119
|
+
if (shouldRenderToParent) {
|
|
120
|
+
// We need to move the focus to the previous interactive element before popup trigger
|
|
121
|
+
requestFrame(() => {
|
|
122
|
+
const isPopupFocusOut = popupRef && !popupRef.contains(document.activeElement);
|
|
123
|
+
if (isPopupFocusOut) {
|
|
124
|
+
closePopup(event);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
requestFrame(() => {
|
|
129
|
+
if (!document.hasFocus()) {
|
|
130
|
+
closePopup(event);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (isLayerDisabled()) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (key === 'Escape' || key === 'Esc') {
|
|
141
|
+
if (triggerRef && autoFocus) {
|
|
142
|
+
triggerRef.focus();
|
|
143
|
+
}
|
|
144
|
+
closePopup(event);
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
if (isLayerDisabled()) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const {
|
|
151
|
+
key
|
|
152
|
+
} = event;
|
|
153
|
+
if (key === 'Escape' || key === 'Esc' || shouldCloseOnTab && key === 'Tab') {
|
|
154
|
+
closePopup(event);
|
|
155
|
+
}
|
|
62
156
|
}
|
|
63
157
|
};
|
|
64
158
|
const unbind = bindAll(window, [{
|
|
@@ -80,15 +174,13 @@ export const useCloseManager = ({
|
|
|
80
174
|
if (isLayerDisabled() || !(document.activeElement instanceof HTMLIFrameElement)) {
|
|
81
175
|
return;
|
|
82
176
|
}
|
|
83
|
-
|
|
84
|
-
if (!wrapper || currentLevel > Number(wrapper.getAttribute('data-ds--level'))) {
|
|
85
|
-
closePopup(e);
|
|
86
|
-
}
|
|
177
|
+
closePopup(e);
|
|
87
178
|
}
|
|
88
179
|
});
|
|
89
180
|
return () => {
|
|
181
|
+
cancelAllFrames();
|
|
90
182
|
unbind();
|
|
91
183
|
unbindBlur();
|
|
92
184
|
};
|
|
93
|
-
}, [isOpen, onClose, popupRef, triggerRef, capture, isLayerDisabled, shouldCloseOnTab, currentLevel]);
|
|
185
|
+
}, [isOpen, onClose, popupRef, triggerRef, autoFocus, shouldDisableFocusTrap, capture, isLayerDisabled, shouldCloseOnTab, currentLevel, shouldRenderToParent, requestFrame, cancelAllFrames]);
|
|
94
186
|
};
|
|
@@ -1,36 +1,53 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import createFocusTrap from 'focus-trap';
|
|
3
3
|
import noop from '@atlaskit/ds-lib/noop';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { useAnimationFrame } from './utils/use-animation-frame';
|
|
4
6
|
export const useFocusManager = ({
|
|
5
7
|
initialFocusRef,
|
|
6
8
|
popupRef,
|
|
7
|
-
|
|
9
|
+
triggerRef,
|
|
10
|
+
autoFocus,
|
|
11
|
+
shouldCloseOnTab,
|
|
12
|
+
shouldDisableFocusTrap,
|
|
13
|
+
shouldReturnFocus
|
|
8
14
|
}) => {
|
|
15
|
+
const {
|
|
16
|
+
requestFrame,
|
|
17
|
+
cancelAllFrames
|
|
18
|
+
} = useAnimationFrame();
|
|
9
19
|
useEffect(() => {
|
|
10
20
|
if (!popupRef || shouldCloseOnTab) {
|
|
11
21
|
return noop;
|
|
12
22
|
}
|
|
23
|
+
if (shouldDisableFocusTrap && fg('platform_dst_popup-disable-focuslock')) {
|
|
24
|
+
// Plucking trigger & popup content container from the tab order so that
|
|
25
|
+
// when we Shift+Tab, the focus moves to the element before trigger
|
|
26
|
+
requestFrame(() => {
|
|
27
|
+
triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.setAttribute('tabindex', '-1');
|
|
28
|
+
if (popupRef && autoFocus) {
|
|
29
|
+
popupRef.setAttribute('tabindex', '-1');
|
|
30
|
+
}
|
|
31
|
+
(initialFocusRef || popupRef).focus();
|
|
32
|
+
});
|
|
33
|
+
return noop;
|
|
34
|
+
}
|
|
13
35
|
const trapConfig = {
|
|
14
36
|
clickOutsideDeactivates: true,
|
|
15
37
|
escapeDeactivates: true,
|
|
16
38
|
initialFocus: initialFocusRef || popupRef,
|
|
17
39
|
fallbackFocus: popupRef,
|
|
18
|
-
returnFocusOnDeactivate:
|
|
40
|
+
returnFocusOnDeactivate: shouldReturnFocus
|
|
19
41
|
};
|
|
20
42
|
const focusTrap = createFocusTrap(popupRef, trapConfig);
|
|
21
|
-
let frameId = null;
|
|
22
43
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
frameId = null;
|
|
44
|
+
// Wait for the popup to reposition itself before we focus
|
|
45
|
+
requestFrame(() => {
|
|
26
46
|
focusTrap.activate();
|
|
27
47
|
});
|
|
28
48
|
return () => {
|
|
29
|
-
|
|
30
|
-
cancelAnimationFrame(frameId);
|
|
31
|
-
frameId = null;
|
|
32
|
-
}
|
|
49
|
+
cancelAllFrames();
|
|
33
50
|
focusTrap.deactivate();
|
|
34
51
|
};
|
|
35
|
-
}, [popupRef, initialFocusRef, shouldCloseOnTab]);
|
|
52
|
+
}, [popupRef, triggerRef, autoFocus, initialFocusRef, shouldCloseOnTab, shouldDisableFocusTrap, requestFrame, cancelAllFrames, shouldReturnFocus]);
|
|
36
53
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const interactiveTags = ['button', 'a', 'input', 'select', 'textarea'];
|
|
2
|
+
export const isInteractiveElement = element => {
|
|
3
|
+
if (interactiveTags.includes(element.tagName.toLowerCase())) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
if (element.getAttribute('tabindex') !== null || element.hasAttribute('contenteditable')) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
export const useAnimationFrame = () => {
|
|
3
|
+
const animationsRef = useRef([]);
|
|
4
|
+
const requestFrame = useCallback(callback => {
|
|
5
|
+
const id = requestAnimationFrame(callback);
|
|
6
|
+
animationsRef.current.push(id);
|
|
7
|
+
return id;
|
|
8
|
+
}, []);
|
|
9
|
+
const cancelFrame = useCallback(id => {
|
|
10
|
+
cancelAnimationFrame(id);
|
|
11
|
+
animationsRef.current = animationsRef.current.filter(frameId => frameId !== id);
|
|
12
|
+
}, []);
|
|
13
|
+
const cancelAllFrames = useCallback(() => {
|
|
14
|
+
animationsRef.current.forEach(id => cancelAnimationFrame(id));
|
|
15
|
+
animationsRef.current = [];
|
|
16
|
+
}, []);
|
|
17
|
+
return {
|
|
18
|
+
requestFrame,
|
|
19
|
+
cancelFrame,
|
|
20
|
+
cancelAllFrames
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -85,6 +85,8 @@ function PopperWrapper(_ref) {
|
|
|
85
85
|
shouldRenderToParent = _ref.shouldRenderToParent,
|
|
86
86
|
shouldFitContainer = _ref.shouldFitContainer,
|
|
87
87
|
shouldDisableFocusLock = _ref.shouldDisableFocusLock,
|
|
88
|
+
_ref$shouldReturnFocu = _ref.shouldReturnFocus,
|
|
89
|
+
shouldReturnFocus = _ref$shouldReturnFocu === void 0 ? true : _ref$shouldReturnFocu,
|
|
88
90
|
strategy = _ref.strategy,
|
|
89
91
|
role = _ref.role,
|
|
90
92
|
label = _ref.label,
|
|
@@ -99,13 +101,18 @@ function PopperWrapper(_ref) {
|
|
|
99
101
|
initialFocusRef = _useState4[0],
|
|
100
102
|
setInitialFocusRef = _useState4[1];
|
|
101
103
|
|
|
102
|
-
// We have cases
|
|
103
|
-
//
|
|
104
|
+
// We have cases where we need to close the Popup on Tab press.
|
|
105
|
+
// Example: DropdownMenu
|
|
104
106
|
var shouldCloseOnTab = shouldRenderToParent && shouldDisableFocusLock;
|
|
107
|
+
var shouldDisableFocusTrap = role !== 'dialog';
|
|
105
108
|
useFocusManager({
|
|
106
109
|
initialFocusRef: initialFocusRef,
|
|
107
110
|
popupRef: popupRef,
|
|
108
|
-
shouldCloseOnTab: shouldCloseOnTab
|
|
111
|
+
shouldCloseOnTab: shouldCloseOnTab,
|
|
112
|
+
triggerRef: triggerRef,
|
|
113
|
+
autoFocus: autoFocus,
|
|
114
|
+
shouldDisableFocusTrap: shouldDisableFocusTrap,
|
|
115
|
+
shouldReturnFocus: shouldReturnFocus
|
|
109
116
|
});
|
|
110
117
|
useCloseManager({
|
|
111
118
|
isOpen: isOpen,
|
|
@@ -113,7 +120,10 @@ function PopperWrapper(_ref) {
|
|
|
113
120
|
popupRef: popupRef,
|
|
114
121
|
triggerRef: triggerRef,
|
|
115
122
|
shouldUseCaptureOnOutsideClick: shouldUseCaptureOnOutsideClick,
|
|
116
|
-
shouldCloseOnTab: shouldCloseOnTab
|
|
123
|
+
shouldCloseOnTab: shouldCloseOnTab,
|
|
124
|
+
autoFocus: autoFocus,
|
|
125
|
+
shouldDisableFocusTrap: shouldDisableFocusTrap,
|
|
126
|
+
shouldRenderToParent: shouldRenderToParent
|
|
117
127
|
});
|
|
118
128
|
var _UNSAFE_useLayering = UNSAFE_useLayering(),
|
|
119
129
|
currentLevel = _UNSAFE_useLayering.currentLevel;
|
package/dist/esm/popup.js
CHANGED
|
@@ -10,6 +10,7 @@ import { memo, useState } from 'react';
|
|
|
10
10
|
import { jsx } from '@emotion/react';
|
|
11
11
|
import { useUID } from 'react-uid';
|
|
12
12
|
import { UNSAFE_LAYERING } from '@atlaskit/layering';
|
|
13
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
13
14
|
import { Manager, Reference } from '@atlaskit/popper';
|
|
14
15
|
import Portal from '@atlaskit/portal';
|
|
15
16
|
import { Box, xcss } from '@atlaskit/primitives';
|
|
@@ -49,6 +50,8 @@ export var Popup = /*#__PURE__*/memo(function (_ref) {
|
|
|
49
50
|
shouldFitContainer = _ref$shouldFitContain === void 0 ? false : _ref$shouldFitContain,
|
|
50
51
|
_ref$shouldDisableFoc = _ref.shouldDisableFocusLock,
|
|
51
52
|
shouldDisableFocusLock = _ref$shouldDisableFoc === void 0 ? false : _ref$shouldDisableFoc,
|
|
53
|
+
_ref$shouldReturnFocu = _ref.shouldReturnFocus,
|
|
54
|
+
shouldReturnFocus = _ref$shouldReturnFocu === void 0 ? true : _ref$shouldReturnFocu,
|
|
52
55
|
strategy = _ref.strategy,
|
|
53
56
|
role = _ref.role,
|
|
54
57
|
label = _ref.label,
|
|
@@ -81,6 +84,7 @@ export var Popup = /*#__PURE__*/memo(function (_ref) {
|
|
|
81
84
|
shouldRenderToParent: shouldRenderToParent || shouldFitContainer,
|
|
82
85
|
shouldFitContainer: shouldFitContainer,
|
|
83
86
|
shouldDisableFocusLock: shouldDisableFocusLock,
|
|
87
|
+
shouldReturnFocus: shouldReturnFocus,
|
|
84
88
|
triggerRef: triggerRef,
|
|
85
89
|
strategy: shouldFitContainer ? 'absolute' : strategy,
|
|
86
90
|
role: role,
|
|
@@ -94,7 +98,7 @@ export var Popup = /*#__PURE__*/memo(function (_ref) {
|
|
|
94
98
|
ref: getMergedTriggerRef(ref, setTriggerRef, isOpen),
|
|
95
99
|
'aria-controls': isOpen ? id : undefined,
|
|
96
100
|
'aria-expanded': isOpen,
|
|
97
|
-
'aria-haspopup': true
|
|
101
|
+
'aria-haspopup': role === 'dialog' && fg('platform_dst_popup-disable-focuslock') ? 'dialog' : true
|
|
98
102
|
});
|
|
99
103
|
}), isOpen && (shouldRenderToParent || shouldFitContainer ? renderPopperWrapper : jsx(Portal, {
|
|
100
104
|
zIndex: zIndex
|
|
@@ -3,16 +3,25 @@ import { useEffect } from 'react';
|
|
|
3
3
|
import { bind, bindAll } from 'bind-event-listener';
|
|
4
4
|
import noop from '@atlaskit/ds-lib/noop';
|
|
5
5
|
import { UNSAFE_useLayering } from '@atlaskit/layering';
|
|
6
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
|
+
import { isInteractiveElement } from './utils/is-element-interactive';
|
|
8
|
+
import { useAnimationFrame } from './utils/use-animation-frame';
|
|
6
9
|
export var useCloseManager = function useCloseManager(_ref) {
|
|
7
10
|
var isOpen = _ref.isOpen,
|
|
8
11
|
onClose = _ref.onClose,
|
|
9
12
|
popupRef = _ref.popupRef,
|
|
10
13
|
triggerRef = _ref.triggerRef,
|
|
14
|
+
autoFocus = _ref.autoFocus,
|
|
15
|
+
shouldDisableFocusTrap = _ref.shouldDisableFocusTrap,
|
|
11
16
|
capture = _ref.shouldUseCaptureOnOutsideClick,
|
|
12
|
-
shouldCloseOnTab = _ref.shouldCloseOnTab
|
|
17
|
+
shouldCloseOnTab = _ref.shouldCloseOnTab,
|
|
18
|
+
shouldRenderToParent = _ref.shouldRenderToParent;
|
|
13
19
|
var _UNSAFE_useLayering = UNSAFE_useLayering(),
|
|
14
20
|
isLayerDisabled = _UNSAFE_useLayering.isLayerDisabled,
|
|
15
21
|
currentLevel = _UNSAFE_useLayering.currentLevel;
|
|
22
|
+
var _useAnimationFrame = useAnimationFrame(),
|
|
23
|
+
requestFrame = _useAnimationFrame.requestFrame,
|
|
24
|
+
cancelAllFrames = _useAnimationFrame.cancelAllFrames;
|
|
16
25
|
useEffect(function () {
|
|
17
26
|
if (!isOpen || !popupRef) {
|
|
18
27
|
return noop;
|
|
@@ -21,6 +30,13 @@ export var useCloseManager = function useCloseManager(_ref) {
|
|
|
21
30
|
if (onClose) {
|
|
22
31
|
onClose(event);
|
|
23
32
|
}
|
|
33
|
+
if (shouldDisableFocusTrap && fg('platform_dst_popup-disable-focuslock')) {
|
|
34
|
+
// Restoring the normal focus order for trigger.
|
|
35
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.setAttribute('tabindex', '0');
|
|
36
|
+
if (popupRef && autoFocus) {
|
|
37
|
+
popupRef.setAttribute('tabindex', '0');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
24
40
|
};
|
|
25
41
|
|
|
26
42
|
// This check is required for cases where components like
|
|
@@ -36,23 +52,98 @@ export var useCloseManager = function useCloseManager(_ref) {
|
|
|
36
52
|
if (!doesDomNodeExist) {
|
|
37
53
|
return;
|
|
38
54
|
}
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
if (fg('platform_dst_popup-disable-focuslock')) {
|
|
56
|
+
var _document$activeEleme;
|
|
57
|
+
if (isLayerDisabled() && (_document$activeEleme = document.activeElement) !== null && _document$activeEleme !== void 0 && _document$activeEleme.closest('[aria-modal]')) {
|
|
58
|
+
//if it is a disabled layer, we need to disable its click listener.
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
if (isLayerDisabled()) {
|
|
63
|
+
//if it is a disabled layer, we need to disable its click listener.
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
42
66
|
}
|
|
43
67
|
var isClickOnPopup = popupRef && popupRef.contains(target);
|
|
44
68
|
var isClickOnTrigger = triggerRef && triggerRef.contains(target);
|
|
45
69
|
if (!isClickOnPopup && !isClickOnTrigger) {
|
|
46
70
|
closePopup(event);
|
|
71
|
+
// If there was an outside click on a non-interactive element, the focus should be on the trigger.
|
|
72
|
+
if (document.activeElement && !isInteractiveElement(document.activeElement) && fg('platform_dst_popup-disable-focuslock')) {
|
|
73
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.focus();
|
|
74
|
+
}
|
|
47
75
|
}
|
|
48
76
|
};
|
|
49
77
|
var onKeyDown = function onKeyDown(event) {
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
if (fg('platform_dst_popup-disable-focuslock')) {
|
|
79
|
+
var key = event.key,
|
|
80
|
+
shiftKey = event.shiftKey;
|
|
81
|
+
if (shiftKey && key === 'Tab' && !shouldRenderToParent) {
|
|
82
|
+
if (isLayerDisabled()) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// We need to move the focus to the popup trigger when the popup is displayed in React.Portal.
|
|
86
|
+
requestFrame(function () {
|
|
87
|
+
var isPopupFocusOut = popupRef && !popupRef.contains(document.activeElement);
|
|
88
|
+
if (isPopupFocusOut) {
|
|
89
|
+
closePopup(event);
|
|
90
|
+
if (currentLevel === 1) {
|
|
91
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.focus();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (key === 'Tab') {
|
|
98
|
+
var _document$activeEleme2;
|
|
99
|
+
// We have cases where we need to close the Popup on Tab press.
|
|
100
|
+
// Example: DropdownMenu
|
|
101
|
+
if (shouldCloseOnTab) {
|
|
102
|
+
if (isLayerDisabled()) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
closePopup(event);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (isLayerDisabled() && (_document$activeEleme2 = document.activeElement) !== null && _document$activeEleme2 !== void 0 && _document$activeEleme2.closest('[aria-modal]')) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (shouldDisableFocusTrap) {
|
|
112
|
+
if (shouldRenderToParent) {
|
|
113
|
+
// We need to move the focus to the previous interactive element before popup trigger
|
|
114
|
+
requestFrame(function () {
|
|
115
|
+
var isPopupFocusOut = popupRef && !popupRef.contains(document.activeElement);
|
|
116
|
+
if (isPopupFocusOut) {
|
|
117
|
+
closePopup(event);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
requestFrame(function () {
|
|
122
|
+
if (!document.hasFocus()) {
|
|
123
|
+
closePopup(event);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (isLayerDisabled()) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (key === 'Escape' || key === 'Esc') {
|
|
134
|
+
if (triggerRef && autoFocus) {
|
|
135
|
+
triggerRef.focus();
|
|
136
|
+
}
|
|
137
|
+
closePopup(event);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
if (isLayerDisabled()) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
var _key = event.key;
|
|
144
|
+
if (_key === 'Escape' || _key === 'Esc' || shouldCloseOnTab && _key === 'Tab') {
|
|
145
|
+
closePopup(event);
|
|
146
|
+
}
|
|
56
147
|
}
|
|
57
148
|
};
|
|
58
149
|
var unbind = bindAll(window, [{
|
|
@@ -74,15 +165,13 @@ export var useCloseManager = function useCloseManager(_ref) {
|
|
|
74
165
|
if (isLayerDisabled() || !(document.activeElement instanceof HTMLIFrameElement)) {
|
|
75
166
|
return;
|
|
76
167
|
}
|
|
77
|
-
|
|
78
|
-
if (!wrapper || currentLevel > Number(wrapper.getAttribute('data-ds--level'))) {
|
|
79
|
-
closePopup(e);
|
|
80
|
-
}
|
|
168
|
+
closePopup(e);
|
|
81
169
|
}
|
|
82
170
|
});
|
|
83
171
|
return function () {
|
|
172
|
+
cancelAllFrames();
|
|
84
173
|
unbind();
|
|
85
174
|
unbindBlur();
|
|
86
175
|
};
|
|
87
|
-
}, [isOpen, onClose, popupRef, triggerRef, capture, isLayerDisabled, shouldCloseOnTab, currentLevel]);
|
|
176
|
+
}, [isOpen, onClose, popupRef, triggerRef, autoFocus, shouldDisableFocusTrap, capture, isLayerDisabled, shouldCloseOnTab, currentLevel, shouldRenderToParent, requestFrame, cancelAllFrames]);
|
|
88
177
|
};
|
|
@@ -1,35 +1,51 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import createFocusTrap from 'focus-trap';
|
|
3
3
|
import noop from '@atlaskit/ds-lib/noop';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { useAnimationFrame } from './utils/use-animation-frame';
|
|
4
6
|
export var useFocusManager = function useFocusManager(_ref) {
|
|
5
7
|
var initialFocusRef = _ref.initialFocusRef,
|
|
6
8
|
popupRef = _ref.popupRef,
|
|
7
|
-
|
|
9
|
+
triggerRef = _ref.triggerRef,
|
|
10
|
+
autoFocus = _ref.autoFocus,
|
|
11
|
+
shouldCloseOnTab = _ref.shouldCloseOnTab,
|
|
12
|
+
shouldDisableFocusTrap = _ref.shouldDisableFocusTrap,
|
|
13
|
+
shouldReturnFocus = _ref.shouldReturnFocus;
|
|
14
|
+
var _useAnimationFrame = useAnimationFrame(),
|
|
15
|
+
requestFrame = _useAnimationFrame.requestFrame,
|
|
16
|
+
cancelAllFrames = _useAnimationFrame.cancelAllFrames;
|
|
8
17
|
useEffect(function () {
|
|
9
18
|
if (!popupRef || shouldCloseOnTab) {
|
|
10
19
|
return noop;
|
|
11
20
|
}
|
|
21
|
+
if (shouldDisableFocusTrap && fg('platform_dst_popup-disable-focuslock')) {
|
|
22
|
+
// Plucking trigger & popup content container from the tab order so that
|
|
23
|
+
// when we Shift+Tab, the focus moves to the element before trigger
|
|
24
|
+
requestFrame(function () {
|
|
25
|
+
triggerRef === null || triggerRef === void 0 || triggerRef.setAttribute('tabindex', '-1');
|
|
26
|
+
if (popupRef && autoFocus) {
|
|
27
|
+
popupRef.setAttribute('tabindex', '-1');
|
|
28
|
+
}
|
|
29
|
+
(initialFocusRef || popupRef).focus();
|
|
30
|
+
});
|
|
31
|
+
return noop;
|
|
32
|
+
}
|
|
12
33
|
var trapConfig = {
|
|
13
34
|
clickOutsideDeactivates: true,
|
|
14
35
|
escapeDeactivates: true,
|
|
15
36
|
initialFocus: initialFocusRef || popupRef,
|
|
16
37
|
fallbackFocus: popupRef,
|
|
17
|
-
returnFocusOnDeactivate:
|
|
38
|
+
returnFocusOnDeactivate: shouldReturnFocus
|
|
18
39
|
};
|
|
19
40
|
var focusTrap = createFocusTrap(popupRef, trapConfig);
|
|
20
|
-
var frameId = null;
|
|
21
41
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
frameId = null;
|
|
42
|
+
// Wait for the popup to reposition itself before we focus
|
|
43
|
+
requestFrame(function () {
|
|
25
44
|
focusTrap.activate();
|
|
26
45
|
});
|
|
27
46
|
return function () {
|
|
28
|
-
|
|
29
|
-
cancelAnimationFrame(frameId);
|
|
30
|
-
frameId = null;
|
|
31
|
-
}
|
|
47
|
+
cancelAllFrames();
|
|
32
48
|
focusTrap.deactivate();
|
|
33
49
|
};
|
|
34
|
-
}, [popupRef, initialFocusRef, shouldCloseOnTab]);
|
|
50
|
+
}, [popupRef, triggerRef, autoFocus, initialFocusRef, shouldCloseOnTab, shouldDisableFocusTrap, requestFrame, cancelAllFrames, shouldReturnFocus]);
|
|
35
51
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var interactiveTags = ['button', 'a', 'input', 'select', 'textarea'];
|
|
2
|
+
export var isInteractiveElement = function isInteractiveElement(element) {
|
|
3
|
+
if (interactiveTags.includes(element.tagName.toLowerCase())) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
if (element.getAttribute('tabindex') !== null || element.hasAttribute('contenteditable')) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
export var useAnimationFrame = function useAnimationFrame() {
|
|
3
|
+
var animationsRef = useRef([]);
|
|
4
|
+
var requestFrame = useCallback(function (callback) {
|
|
5
|
+
var id = requestAnimationFrame(callback);
|
|
6
|
+
animationsRef.current.push(id);
|
|
7
|
+
return id;
|
|
8
|
+
}, []);
|
|
9
|
+
var cancelFrame = useCallback(function (id) {
|
|
10
|
+
cancelAnimationFrame(id);
|
|
11
|
+
animationsRef.current = animationsRef.current.filter(function (frameId) {
|
|
12
|
+
return frameId !== id;
|
|
13
|
+
});
|
|
14
|
+
}, []);
|
|
15
|
+
var cancelAllFrames = useCallback(function () {
|
|
16
|
+
animationsRef.current.forEach(function (id) {
|
|
17
|
+
return cancelAnimationFrame(id);
|
|
18
|
+
});
|
|
19
|
+
animationsRef.current = [];
|
|
20
|
+
}, []);
|
|
21
|
+
return {
|
|
22
|
+
requestFrame: requestFrame,
|
|
23
|
+
cancelFrame: cancelFrame,
|
|
24
|
+
cancelAllFrames: cancelAllFrames
|
|
25
|
+
};
|
|
26
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { jsx } from '@emotion/react';
|
|
2
2
|
import { type PopperWrapperProps } from './types';
|
|
3
|
-
declare function PopperWrapper({ isOpen, id, offset, testId, content, fallbackPlacements, onClose, boundary, rootBoundary, shouldFlip, placement, popupComponent: PopupContainer, autoFocus, triggerRef, shouldUseCaptureOnOutsideClick, shouldRenderToParent, shouldFitContainer, shouldDisableFocusLock, strategy, role, label, titleId, modifiers, }: PopperWrapperProps): jsx.JSX.Element;
|
|
3
|
+
declare function PopperWrapper({ isOpen, id, offset, testId, content, fallbackPlacements, onClose, boundary, rootBoundary, shouldFlip, placement, popupComponent: PopupContainer, autoFocus, triggerRef, shouldUseCaptureOnOutsideClick, shouldRenderToParent, shouldFitContainer, shouldDisableFocusLock, shouldReturnFocus, strategy, role, label, titleId, modifiers, }: PopperWrapperProps): jsx.JSX.Element;
|
|
4
4
|
export default PopperWrapper;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface TriggerProps {
|
|
|
4
4
|
ref: Ref<any>;
|
|
5
5
|
'aria-controls'?: string;
|
|
6
6
|
'aria-expanded': boolean;
|
|
7
|
-
'aria-haspopup': boolean;
|
|
7
|
+
'aria-haspopup': boolean | 'dialog';
|
|
8
8
|
}
|
|
9
9
|
export type PopupRef = HTMLDivElement | null;
|
|
10
10
|
export type TriggerRef = HTMLElement | HTMLButtonElement | null;
|
|
@@ -164,10 +164,15 @@ interface BaseProps {
|
|
|
164
164
|
*/
|
|
165
165
|
shouldFitContainer?: boolean;
|
|
166
166
|
/**
|
|
167
|
-
* This
|
|
167
|
+
* This makes the popup close on Tab key press. It will only work when `shouldRenderToParent` is `true`.
|
|
168
168
|
* The default is `false`.
|
|
169
169
|
*/
|
|
170
170
|
shouldDisableFocusLock?: boolean;
|
|
171
|
+
/**
|
|
172
|
+
* This determines whether the popup trigger will be focused when the popup content closes.
|
|
173
|
+
* The default is `true`.
|
|
174
|
+
*/
|
|
175
|
+
shouldReturnFocus?: boolean;
|
|
171
176
|
/**
|
|
172
177
|
* This controls the positioning strategy to use. Can vary between `absolute` and `fixed`.
|
|
173
178
|
* The default is `fixed`.
|
|
@@ -226,11 +231,18 @@ export type CloseManagerHook = Pick<PopupProps, 'isOpen' | 'onClose'> & {
|
|
|
226
231
|
triggerRef: TriggerRef;
|
|
227
232
|
shouldUseCaptureOnOutsideClick?: boolean;
|
|
228
233
|
shouldCloseOnTab?: boolean;
|
|
234
|
+
shouldDisableFocusTrap: boolean;
|
|
235
|
+
shouldRenderToParent?: boolean;
|
|
236
|
+
autoFocus: boolean;
|
|
229
237
|
};
|
|
230
238
|
export type FocusManagerHook = {
|
|
231
239
|
initialFocusRef: HTMLElement | null;
|
|
232
240
|
popupRef: PopupRef;
|
|
233
241
|
shouldCloseOnTab?: boolean;
|
|
242
|
+
triggerRef: TriggerRef;
|
|
243
|
+
autoFocus: boolean;
|
|
244
|
+
shouldDisableFocusTrap: boolean;
|
|
245
|
+
shouldReturnFocus: boolean;
|
|
234
246
|
};
|
|
235
247
|
export type RepositionOnUpdateProps = PropsWithChildren<{
|
|
236
248
|
update: PopperChildrenProps['update'];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type CloseManagerHook } from './types';
|
|
2
|
-
export declare const useCloseManager: ({ isOpen, onClose, popupRef, triggerRef, shouldUseCaptureOnOutsideClick: capture, shouldCloseOnTab, }: CloseManagerHook) => void;
|
|
2
|
+
export declare const useCloseManager: ({ isOpen, onClose, popupRef, triggerRef, autoFocus, shouldDisableFocusTrap, shouldUseCaptureOnOutsideClick: capture, shouldCloseOnTab, shouldRenderToParent, }: CloseManagerHook) => void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type FocusManagerHook } from './types';
|
|
2
|
-
export declare const useFocusManager: ({ initialFocusRef, popupRef, shouldCloseOnTab, }: FocusManagerHook) => void;
|
|
2
|
+
export declare const useFocusManager: ({ initialFocusRef, popupRef, triggerRef, autoFocus, shouldCloseOnTab, shouldDisableFocusTrap, shouldReturnFocus, }: FocusManagerHook) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isInteractiveElement: (element: HTMLElement) => boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { jsx } from '@emotion/react';
|
|
2
2
|
import { type PopperWrapperProps } from './types';
|
|
3
|
-
declare function PopperWrapper({ isOpen, id, offset, testId, content, fallbackPlacements, onClose, boundary, rootBoundary, shouldFlip, placement, popupComponent: PopupContainer, autoFocus, triggerRef, shouldUseCaptureOnOutsideClick, shouldRenderToParent, shouldFitContainer, shouldDisableFocusLock, strategy, role, label, titleId, modifiers, }: PopperWrapperProps): jsx.JSX.Element;
|
|
3
|
+
declare function PopperWrapper({ isOpen, id, offset, testId, content, fallbackPlacements, onClose, boundary, rootBoundary, shouldFlip, placement, popupComponent: PopupContainer, autoFocus, triggerRef, shouldUseCaptureOnOutsideClick, shouldRenderToParent, shouldFitContainer, shouldDisableFocusLock, shouldReturnFocus, strategy, role, label, titleId, modifiers, }: PopperWrapperProps): jsx.JSX.Element;
|
|
4
4
|
export default PopperWrapper;
|
|
@@ -4,7 +4,7 @@ export interface TriggerProps {
|
|
|
4
4
|
ref: Ref<any>;
|
|
5
5
|
'aria-controls'?: string;
|
|
6
6
|
'aria-expanded': boolean;
|
|
7
|
-
'aria-haspopup': boolean;
|
|
7
|
+
'aria-haspopup': boolean | 'dialog';
|
|
8
8
|
}
|
|
9
9
|
export type PopupRef = HTMLDivElement | null;
|
|
10
10
|
export type TriggerRef = HTMLElement | HTMLButtonElement | null;
|
|
@@ -167,10 +167,15 @@ interface BaseProps {
|
|
|
167
167
|
*/
|
|
168
168
|
shouldFitContainer?: boolean;
|
|
169
169
|
/**
|
|
170
|
-
* This
|
|
170
|
+
* This makes the popup close on Tab key press. It will only work when `shouldRenderToParent` is `true`.
|
|
171
171
|
* The default is `false`.
|
|
172
172
|
*/
|
|
173
173
|
shouldDisableFocusLock?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* This determines whether the popup trigger will be focused when the popup content closes.
|
|
176
|
+
* The default is `true`.
|
|
177
|
+
*/
|
|
178
|
+
shouldReturnFocus?: boolean;
|
|
174
179
|
/**
|
|
175
180
|
* This controls the positioning strategy to use. Can vary between `absolute` and `fixed`.
|
|
176
181
|
* The default is `fixed`.
|
|
@@ -229,11 +234,18 @@ export type CloseManagerHook = Pick<PopupProps, 'isOpen' | 'onClose'> & {
|
|
|
229
234
|
triggerRef: TriggerRef;
|
|
230
235
|
shouldUseCaptureOnOutsideClick?: boolean;
|
|
231
236
|
shouldCloseOnTab?: boolean;
|
|
237
|
+
shouldDisableFocusTrap: boolean;
|
|
238
|
+
shouldRenderToParent?: boolean;
|
|
239
|
+
autoFocus: boolean;
|
|
232
240
|
};
|
|
233
241
|
export type FocusManagerHook = {
|
|
234
242
|
initialFocusRef: HTMLElement | null;
|
|
235
243
|
popupRef: PopupRef;
|
|
236
244
|
shouldCloseOnTab?: boolean;
|
|
245
|
+
triggerRef: TriggerRef;
|
|
246
|
+
autoFocus: boolean;
|
|
247
|
+
shouldDisableFocusTrap: boolean;
|
|
248
|
+
shouldReturnFocus: boolean;
|
|
237
249
|
};
|
|
238
250
|
export type RepositionOnUpdateProps = PropsWithChildren<{
|
|
239
251
|
update: PopperChildrenProps['update'];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type CloseManagerHook } from './types';
|
|
2
|
-
export declare const useCloseManager: ({ isOpen, onClose, popupRef, triggerRef, shouldUseCaptureOnOutsideClick: capture, shouldCloseOnTab, }: CloseManagerHook) => void;
|
|
2
|
+
export declare const useCloseManager: ({ isOpen, onClose, popupRef, triggerRef, autoFocus, shouldDisableFocusTrap, shouldUseCaptureOnOutsideClick: capture, shouldCloseOnTab, shouldRenderToParent, }: CloseManagerHook) => void;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type FocusManagerHook } from './types';
|
|
2
|
-
export declare const useFocusManager: ({ initialFocusRef, popupRef, shouldCloseOnTab, }: FocusManagerHook) => void;
|
|
2
|
+
export declare const useFocusManager: ({ initialFocusRef, popupRef, triggerRef, autoFocus, shouldCloseOnTab, shouldDisableFocusTrap, shouldReturnFocus, }: FocusManagerHook) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isInteractiveElement: (element: HTMLElement) => boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/popup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.1",
|
|
4
4
|
"description": "A popup displays brief content in an overlay.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -40,14 +40,14 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@atlaskit/ds-lib": "^2.
|
|
43
|
+
"@atlaskit/ds-lib": "^2.5.0",
|
|
44
44
|
"@atlaskit/layering": "^0.4.0",
|
|
45
45
|
"@atlaskit/platform-feature-flags": "^0.3.0",
|
|
46
46
|
"@atlaskit/popper": "^6.2.0",
|
|
47
47
|
"@atlaskit/portal": "^4.9.0",
|
|
48
48
|
"@atlaskit/primitives": "^12.0.0",
|
|
49
49
|
"@atlaskit/theme": "^13.0.0",
|
|
50
|
-
"@atlaskit/tokens": "^1.
|
|
50
|
+
"@atlaskit/tokens": "^1.59.0",
|
|
51
51
|
"@babel/runtime": "^7.0.0",
|
|
52
52
|
"@emotion/react": "^11.7.1",
|
|
53
53
|
"bind-event-listener": "^3.0.0",
|
|
@@ -62,9 +62,10 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@af/accessibility-testing": "*",
|
|
65
|
+
"@af/integration-testing": "*",
|
|
65
66
|
"@af/visual-regression": "*",
|
|
66
|
-
"@atlaskit/button": "^
|
|
67
|
-
"@atlaskit/icon": "^22.
|
|
67
|
+
"@atlaskit/button": "^20.1.0",
|
|
68
|
+
"@atlaskit/icon": "^22.15.0",
|
|
68
69
|
"@atlaskit/ssr": "*",
|
|
69
70
|
"@atlaskit/textfield": "^6.5.0",
|
|
70
71
|
"@atlaskit/toggle": "^13.3.0",
|
|
@@ -109,6 +110,9 @@
|
|
|
109
110
|
"platform-feature-flags": {
|
|
110
111
|
"platform.design-system-team.iframe_gojiv": {
|
|
111
112
|
"type": "boolean"
|
|
113
|
+
},
|
|
114
|
+
"platform_dst_popup-disable-focuslock": {
|
|
115
|
+
"type": "boolean"
|
|
112
116
|
}
|
|
113
117
|
},
|
|
114
118
|
"homepage": "https://atlassian.design/components/popup/"
|