@pingux/astro 1.1.0-alpha.15 → 1.1.0-alpha.16

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.
@@ -18,12 +18,16 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/e
18
18
 
19
19
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/defineProperty"));
20
20
 
21
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/slicedToArray"));
22
+
21
23
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/objectWithoutProperties"));
22
24
 
23
25
  var _react = _interopRequireWildcard(require("react"));
24
26
 
25
27
  var _propTypes = _interopRequireDefault(require("prop-types"));
26
28
 
29
+ var _focus = require("@react-aria/focus");
30
+
27
31
  var _Box = _interopRequireDefault(require("../Box"));
28
32
 
29
33
  var _hooks = require("../../hooks");
@@ -37,12 +41,25 @@ var OverlayPanel = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
37
41
 
38
42
  var children = props.children,
39
43
  isOpen = props.isOpen,
44
+ onCloseProp = props.onClose,
40
45
  className = props.className,
46
+ state = props.state,
41
47
  size = props.size,
42
- others = (0, _objectWithoutProperties2["default"])(props, ["children", "isOpen", "className", "size"]);
48
+ triggerRef = props.triggerRef,
49
+ others = (0, _objectWithoutProperties2["default"])(props, ["children", "isOpen", "onClose", "className", "state", "size", "triggerRef"]);
50
+
51
+ var _useOverlayPanelState = (0, _hooks.useOverlayPanelState)(),
52
+ onClose = _useOverlayPanelState.onClose;
53
+
43
54
  var overlayPanelRef = (0, _react.useRef)();
55
+
56
+ var _useState = (0, _react.useState)(true),
57
+ _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
58
+ contain = _useState2[0],
59
+ setIsContained = _useState2[1];
44
60
  /* istanbul ignore next */
45
61
 
62
+
46
63
  (0, _react.useImperativeHandle)(ref, function () {
47
64
  return overlayPanelRef.current;
48
65
  });
@@ -52,14 +69,32 @@ var OverlayPanel = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
52
69
  }, "is-".concat((props === null || props === void 0 ? void 0 : (_props$sx = props.sx) === null || _props$sx === void 0 ? void 0 : _props$sx.width) ? 'custom' : size), size)),
53
70
  classNames = _useStatusClasses.classNames;
54
71
 
55
- return (0, _react2.jsx)(_Box["default"], (0, _extends2["default"])({
56
- variant: "overlayPanel.overlayPanel"
72
+ var handleClose = function handleClose(e) {
73
+ e.stopPropagation();
74
+
75
+ if (e.key === 'Escape') {
76
+ setIsContained(false);
77
+ }
78
+ };
79
+
80
+ (0, _react.useEffect)(function () {
81
+ if (!contain && onClose) {
82
+ onClose(state, triggerRef, onCloseProp);
83
+ }
84
+ }, [contain]);
85
+ return (0, _react2.jsx)(_focus.FocusScope, {
86
+ autoFocus: true,
87
+ contain: contain
88
+ }, (0, _react2.jsx)(_Box["default"], (0, _extends2["default"])({
89
+ variant: "overlayPanel.overlayPanel",
90
+ ref: overlayPanelRef
57
91
  }, others, {
58
- className: classNames
92
+ className: classNames,
93
+ onKeyUp: handleClose
59
94
  }), (0, _react2.jsx)(_Box["default"], {
60
95
  variant: "overlayPanel.overlayPanelBody",
61
96
  className: classNames
62
- }, children));
97
+ }, children)));
63
98
  });
64
99
  OverlayPanel.propTypes = {
65
100
  /** Sets the open state of the menu. */
@@ -71,7 +106,19 @@ OverlayPanel.propTypes = {
71
106
  /** JSX styling that is passed into the component. */
72
107
  sx: _propTypes["default"].shape({
73
108
  width: _propTypes["default"].string
74
- })
109
+ }),
110
+
111
+ /** State object that is passed in from the useOverlayPanelState hook */
112
+ state: _propTypes["default"].shape({
113
+ close: _propTypes["default"].func
114
+ }),
115
+
116
+ /** Callback function that runs when the esc key is used to close the OverlayPanel. */
117
+ onClose: _propTypes["default"].func,
118
+
119
+ /** Ref that is connected to the button that triggers the overlay state.
120
+ Focus will return to this ref when the keyboard is used to close the OverlayPanel. */
121
+ triggerRef: _propTypes["default"].shape({})
75
122
  };
76
123
  OverlayPanel.defaultProps = {
77
124
  size: 'medium'
@@ -61,21 +61,29 @@ exports["default"] = _default;
61
61
 
62
62
  var Default = function Default(_ref) {
63
63
  var args = (0, _extends2["default"])({}, _ref);
64
- var state = (0, _hooks.useOverlayPanelState)();
64
+
65
+ var _useOverlayPanelState = (0, _hooks.useOverlayPanelState)(),
66
+ state = _useOverlayPanelState.state,
67
+ onClose = _useOverlayPanelState.onClose;
68
+
69
+ var triggerRef = (0, _react.useRef)();
65
70
  return (// Application must be wrapped in an OverlayProvider so that it can be hidden from screen
66
- // readers when an overlay opens.
71
+ // readers when an overlay is open.
67
72
  (0, _react2.jsx)(_index.OverlayProvider, null, (0, _react2.jsx)(_index.Text, null, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."), (0, _react2.jsx)("br", null), (0, _react2.jsx)(_index.Button, {
73
+ ref: triggerRef,
68
74
  onPress: state.open
69
- }, "Open Panel"), (0, _react2.jsx)(_OverlayPanel["default"], (0, _extends2["default"])({
70
- isOpen: state.isOpen
71
- }, args), (0, _react2.jsx)(_index.Box, null, (0, _react2.jsx)(_index.Button, {
72
- onPress: state.close
73
- }, "Close Panel"), (0, _react2.jsx)(_index.AccordionGroup, args, (0, _react2.jsx)(_collections.Item, {
74
- key: "accordionKey",
75
- textValue: "accordionKey",
76
- label: "Accordion Label",
77
- "data-id": "accordionItem"
78
- }, (0, _react2.jsx)(_index.Text, null, "Render me!"))))))
75
+ }, "Open Panel"), state.isOpen && (0, _react2.jsx)(_OverlayPanel["default"], (0, _extends2["default"])({
76
+ isOpen: state.isOpen,
77
+ state: state
78
+ }, args, {
79
+ triggerRef: triggerRef
80
+ }), (0, _react2.jsx)(_index.Box, null, (0, _react2.jsx)(_index.Button, {
81
+ onPress: function onPress() {
82
+ onClose(state, triggerRef);
83
+ }
84
+ }, "Close Panel"), (0, _react2.jsx)(_index.Text, {
85
+ pt: "md"
86
+ }, "Children render here."))))
79
87
  );
80
88
  };
81
89
 
@@ -83,8 +91,17 @@ exports.Default = Default;
83
91
 
84
92
  var InnerPanel = function InnerPanel(_ref2) {
85
93
  var args = (0, _extends2["default"])({}, _ref2);
86
- var state = (0, _hooks.useOverlayPanelState)();
87
- var innerState = (0, _hooks.useOverlayPanelState)();
94
+
95
+ var _useOverlayPanelState2 = (0, _hooks.useOverlayPanelState)(),
96
+ state = _useOverlayPanelState2.state,
97
+ onClose = _useOverlayPanelState2.onClose;
98
+
99
+ var _useOverlayPanelState3 = (0, _hooks.useOverlayPanelState)(),
100
+ innerState = _useOverlayPanelState3.state,
101
+ onCloseInner = _useOverlayPanelState3.onClose;
102
+
103
+ var outerTriggerRef = (0, _react.useRef)();
104
+ var innerTriggerRef = (0, _react.useRef)();
88
105
 
89
106
  var _useState = (0, _react.useState)(false),
90
107
  _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
@@ -100,15 +117,22 @@ var InnerPanel = function InnerPanel(_ref2) {
100
117
  innerState.close();
101
118
  }
102
119
 
103
- state.close();
120
+ onClose(state, outerTriggerRef);
121
+ };
122
+
123
+ var closeInnerPanel = function closeInnerPanel() {
124
+ onCloseInner(innerState, innerTriggerRef);
104
125
  };
105
126
 
106
127
  var inner = (0, _react2.jsx)(_react["default"].Fragment, null, innerState.isOpen && (0, _react2.jsx)(_OverlayPanel["default"], (0, _extends2["default"])({
107
128
  variant: "overlayPanel.overlayPanelInner" // applies higher z-index
108
129
  ,
109
130
  isOpen: innerState.isOpen
110
- }, args), (0, _react2.jsx)(_index.Box, null, (0, _react2.jsx)(_index.Button, {
111
- onPress: innerState.close
131
+ }, args, {
132
+ state: innerState,
133
+ triggerRef: innerTriggerRef
134
+ }), (0, _react2.jsx)(_index.Box, null, (0, _react2.jsx)(_index.Button, {
135
+ onPress: closeInnerPanel
112
136
  }, "Close Inner Panel"), (0, _react2.jsx)(_index.Text, {
113
137
  pt: "md"
114
138
  }, "Children render here."))));
@@ -118,7 +142,10 @@ var InnerPanel = function InnerPanel(_ref2) {
118
142
  sx: {
119
143
  p: '0px'
120
144
  }
121
- }, args), (0, _react2.jsx)(_index.Box, {
145
+ }, args, {
146
+ state: state,
147
+ triggerRef: outerTriggerRef
148
+ }), (0, _react2.jsx)(_index.Box, {
122
149
  sx: {
123
150
  p: '12px'
124
151
  }
@@ -141,38 +168,16 @@ var InnerPanel = function InnerPanel(_ref2) {
141
168
  mr: "auto"
142
169
  }, "Form 2")), (0, _react2.jsx)(_index.Separator, {
143
170
  margin: "0"
144
- }), (0, _react2.jsx)(_index.ListItem, {
145
- title: "Form 3"
146
- }, (0, _react2.jsx)(_index.Text, {
147
- variant: "itemTitle",
148
- alignSelf: "center",
149
- mr: "auto"
150
- }, "Form 3")), (0, _react2.jsx)(_index.Separator, {
151
- margin: "0"
152
- }), (0, _react2.jsx)(_index.ListItem, {
153
- title: "Form 4"
154
- }, (0, _react2.jsx)(_index.Text, {
155
- variant: "itemTitle",
156
- alignSelf: "center",
157
- mr: "auto"
158
- }, "Form 4")), (0, _react2.jsx)(_index.Separator, {
159
- margin: "0"
160
- }), (0, _react2.jsx)(_index.ListItem, {
161
- title: "Form 5"
162
- }, (0, _react2.jsx)(_index.Text, {
163
- variant: "itemTitle",
164
- alignSelf: "center",
165
- mr: "auto"
166
- }, "Form 5")), (0, _react2.jsx)(_index.Separator, {
167
- margin: "0"
168
171
  })), (0, _react2.jsx)("br", null), (0, _react2.jsx)(_index.Button, {
169
172
  onPress: toggleMessagesOpen
170
173
  }, "Toggle Messages"), (0, _react2.jsx)("br", null), (0, _react2.jsx)(_index.Button, {
174
+ ref: innerTriggerRef,
171
175
  onPress: innerState.open
172
176
  }, "Open Inner Panel"), inner)));
173
177
  return (// Application must be wrapped in an OverlayProvider so that it can be hidden from screen
174
178
  // readers when an overlay opens.
175
179
  (0, _react2.jsx)(_react["default"].Fragment, null, (0, _react2.jsx)(_index.OverlayProvider, null, (0, _react2.jsx)(_index.Button, {
180
+ ref: outerTriggerRef,
176
181
  onPress: state.open
177
182
  }, "Open Panel"), outer), messagesOpen && (0, _react2.jsx)(_index.Messages, {
178
183
  sx: {
@@ -188,20 +193,27 @@ var InnerPanel = function InnerPanel(_ref2) {
188
193
  exports.InnerPanel = InnerPanel;
189
194
 
190
195
  var CustomWidth = function CustomWidth() {
191
- var state = (0, _hooks.useOverlayPanelState)();
196
+ var _useOverlayPanelState4 = (0, _hooks.useOverlayPanelState)(),
197
+ state = _useOverlayPanelState4.state,
198
+ onClose = _useOverlayPanelState4.onClose;
199
+
200
+ var triggerRef = (0, _react.useRef)();
192
201
  return (// Application must be wrapped in an OverlayProvider so that it can be hidden from screen
193
202
  // readers when an overlay opens.
194
- //
195
- // For a custom width, provide the width via the 'sx' prop
196
203
  (0, _react2.jsx)(_index.OverlayProvider, null, (0, _react2.jsx)(_index.Text, null, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."), (0, _react2.jsx)("br", null), (0, _react2.jsx)(_index.Button, {
204
+ ref: triggerRef,
197
205
  onPress: state.open
198
- }, "Open Panel"), (0, _react2.jsx)(_OverlayPanel["default"], {
206
+ }, "Open Panel"), state.isOpen && (0, _react2.jsx)(_OverlayPanel["default"], {
199
207
  isOpen: state.isOpen,
208
+ state: state,
209
+ triggerRef: triggerRef,
200
210
  sx: {
201
211
  width: '720px'
202
212
  }
203
213
  }, (0, _react2.jsx)(_index.Box, null, (0, _react2.jsx)(_index.Button, {
204
- onPress: state.close
214
+ onPress: function onPress() {
215
+ onClose(state, triggerRef);
216
+ }
205
217
  }, "Close Panel"), (0, _react2.jsx)(_index.Text, {
206
218
  pt: "md"
207
219
  }, "Children render here."))))
@@ -53,4 +53,88 @@ test('custom overlayPanel gets custom width', function () {
53
53
 
54
54
  expect(overlayPanel).toBeInTheDocument();
55
55
  expect(overlayPanel).toHaveStyleRule('width', '240px');
56
+ });
57
+ test('onClose callback fires when provided', function () {
58
+ var onClose = jest.fn();
59
+ getComponent({
60
+ onClose: onClose,
61
+ children: (0, _react2.jsx)("div", null, "Test")
62
+ });
63
+
64
+ var overlayPanel = _testWrapper.screen.getByTestId(testId);
65
+
66
+ _testWrapper.fireEvent.keyDown(overlayPanel, {
67
+ key: 'Escape',
68
+ code: 'Escape',
69
+ keyCode: 27,
70
+ charCode: 27
71
+ });
72
+
73
+ _testWrapper.fireEvent.keyUp(overlayPanel, {
74
+ key: 'Escape',
75
+ code: 'Escape',
76
+ keyCode: 27,
77
+ charCode: 27
78
+ });
79
+
80
+ expect(onClose).toHaveBeenCalled();
81
+ });
82
+ test('neither callback fires when not provided', function () {
83
+ var onClose = jest.fn();
84
+ getComponent({
85
+ children: (0, _react2.jsx)("div", null, "Test")
86
+ });
87
+
88
+ var overlayPanel = _testWrapper.screen.getByTestId(testId);
89
+
90
+ _testWrapper.fireEvent.keyDown(overlayPanel, {
91
+ key: 'Escape',
92
+ code: 'Escape',
93
+ keyCode: 27,
94
+ charCode: 27
95
+ });
96
+
97
+ _testWrapper.fireEvent.keyUp(overlayPanel, {
98
+ key: 'Escape',
99
+ code: 'Escape',
100
+ keyCode: 27,
101
+ charCode: 27
102
+ });
103
+
104
+ expect(onClose).not.toHaveBeenCalled();
105
+ });
106
+ test('triggerRef.current.focus() fires when provided', function () {
107
+ var onClose = jest.fn();
108
+ var focusFunction = jest.fn();
109
+ var state = {
110
+ close: onClose
111
+ };
112
+ var triggerRef = {
113
+ current: {
114
+ focus: focusFunction
115
+ }
116
+ };
117
+ getComponent({
118
+ state: state,
119
+ children: (0, _react2.jsx)("div", null, "Test"),
120
+ triggerRef: triggerRef
121
+ });
122
+
123
+ var overlayPanel = _testWrapper.screen.getByTestId(testId);
124
+
125
+ _testWrapper.fireEvent.keyDown(overlayPanel, {
126
+ key: 'Escape',
127
+ code: 'Escape',
128
+ keyCode: 27,
129
+ charCode: 27
130
+ });
131
+
132
+ _testWrapper.fireEvent.keyUp(overlayPanel, {
133
+ key: 'Escape',
134
+ code: 'Escape',
135
+ keyCode: 27,
136
+ charCode: 27
137
+ });
138
+
139
+ expect(focusFunction).toHaveBeenCalled();
56
140
  });
@@ -24,11 +24,30 @@ var useOverlayPanelState = function useOverlayPanelState() {
24
24
  var isDefaultOpen = props.isDefaultOpen,
25
25
  isOpen = props.isOpen,
26
26
  onOpenChange = props.onOpenChange;
27
- return (0, _overlays.useOverlayTriggerState)({
27
+ var state = (0, _overlays.useOverlayTriggerState)({
28
28
  defaultOpen: isDefaultOpen,
29
29
  isOpen: isOpen,
30
30
  onOpenChange: onOpenChange
31
31
  });
32
+
33
+ var onClose = function onClose(stateProp, triggerRef, onCloseProp) {
34
+ if (stateProp) {
35
+ stateProp.close();
36
+ }
37
+
38
+ if (triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.current) {
39
+ triggerRef.current.focus();
40
+ }
41
+
42
+ if (onCloseProp) {
43
+ onCloseProp();
44
+ }
45
+ };
46
+
47
+ return {
48
+ state: state,
49
+ onClose: onClose
50
+ };
32
51
  };
33
52
 
34
53
  var _default = useOverlayPanelState;
@@ -13,8 +13,12 @@ test('default useOverlayPanelState', function () {
13
13
  result = _renderHook.result;
14
14
 
15
15
  expect(result.current).toEqual(expect.objectContaining({
16
- open: expect.any(Function),
17
- close: expect.any(Function),
18
- toggle: expect.any(Function)
16
+ state: {
17
+ open: expect.any(Function),
18
+ close: expect.any(Function),
19
+ toggle: expect.any(Function),
20
+ isOpen: expect.any(Boolean)
21
+ },
22
+ onClose: expect.any(Function)
19
23
  }));
20
24
  });
@@ -1,11 +1,13 @@
1
1
  import _Object$values from "@babel/runtime-corejs3/core-js-stable/object/values";
2
2
  import _extends from "@babel/runtime-corejs3/helpers/esm/extends";
3
3
  import _defineProperty from "@babel/runtime-corejs3/helpers/esm/defineProperty";
4
+ import _slicedToArray from "@babel/runtime-corejs3/helpers/esm/slicedToArray";
4
5
  import _objectWithoutProperties from "@babel/runtime-corejs3/helpers/esm/objectWithoutProperties";
5
- import React, { forwardRef, useRef, useImperativeHandle } from 'react';
6
+ import React, { forwardRef, useRef, useImperativeHandle, useEffect, useState } from 'react';
6
7
  import PropTypes from 'prop-types';
8
+ import { FocusScope } from '@react-aria/focus';
7
9
  import Box from '../Box';
8
- import { useStatusClasses } from '../../hooks';
10
+ import { useStatusClasses, useOverlayPanelState } from '../../hooks';
9
11
  import { panelSizes } from '../../utils/devUtils/constants/panelSizes';
10
12
  import { jsx as ___EmotionJSX } from "@emotion/react";
11
13
  var OverlayPanel = /*#__PURE__*/forwardRef(function (props, ref) {
@@ -13,13 +15,25 @@ var OverlayPanel = /*#__PURE__*/forwardRef(function (props, ref) {
13
15
 
14
16
  var children = props.children,
15
17
  isOpen = props.isOpen,
18
+ onCloseProp = props.onClose,
16
19
  className = props.className,
20
+ state = props.state,
17
21
  size = props.size,
18
- others = _objectWithoutProperties(props, ["children", "isOpen", "className", "size"]);
22
+ triggerRef = props.triggerRef,
23
+ others = _objectWithoutProperties(props, ["children", "isOpen", "onClose", "className", "state", "size", "triggerRef"]);
24
+
25
+ var _useOverlayPanelState = useOverlayPanelState(),
26
+ onClose = _useOverlayPanelState.onClose;
19
27
 
20
28
  var overlayPanelRef = useRef();
29
+
30
+ var _useState = useState(true),
31
+ _useState2 = _slicedToArray(_useState, 2),
32
+ contain = _useState2[0],
33
+ setIsContained = _useState2[1];
21
34
  /* istanbul ignore next */
22
35
 
36
+
23
37
  useImperativeHandle(ref, function () {
24
38
  return overlayPanelRef.current;
25
39
  });
@@ -29,14 +43,32 @@ var OverlayPanel = /*#__PURE__*/forwardRef(function (props, ref) {
29
43
  }, "is-".concat((props === null || props === void 0 ? void 0 : (_props$sx = props.sx) === null || _props$sx === void 0 ? void 0 : _props$sx.width) ? 'custom' : size), size)),
30
44
  classNames = _useStatusClasses.classNames;
31
45
 
32
- return ___EmotionJSX(Box, _extends({
33
- variant: "overlayPanel.overlayPanel"
46
+ var handleClose = function handleClose(e) {
47
+ e.stopPropagation();
48
+
49
+ if (e.key === 'Escape') {
50
+ setIsContained(false);
51
+ }
52
+ };
53
+
54
+ useEffect(function () {
55
+ if (!contain && onClose) {
56
+ onClose(state, triggerRef, onCloseProp);
57
+ }
58
+ }, [contain]);
59
+ return ___EmotionJSX(FocusScope, {
60
+ autoFocus: true,
61
+ contain: contain
62
+ }, ___EmotionJSX(Box, _extends({
63
+ variant: "overlayPanel.overlayPanel",
64
+ ref: overlayPanelRef
34
65
  }, others, {
35
- className: classNames
66
+ className: classNames,
67
+ onKeyUp: handleClose
36
68
  }), ___EmotionJSX(Box, {
37
69
  variant: "overlayPanel.overlayPanelBody",
38
70
  className: classNames
39
- }, children));
71
+ }, children)));
40
72
  });
41
73
  OverlayPanel.propTypes = {
42
74
  /** Sets the open state of the menu. */
@@ -48,7 +80,19 @@ OverlayPanel.propTypes = {
48
80
  /** JSX styling that is passed into the component. */
49
81
  sx: PropTypes.shape({
50
82
  width: PropTypes.string
51
- })
83
+ }),
84
+
85
+ /** State object that is passed in from the useOverlayPanelState hook */
86
+ state: PropTypes.shape({
87
+ close: PropTypes.func
88
+ }),
89
+
90
+ /** Callback function that runs when the esc key is used to close the OverlayPanel. */
91
+ onClose: PropTypes.func,
92
+
93
+ /** Ref that is connected to the button that triggers the overlay state.
94
+ Focus will return to this ref when the keyboard is used to close the OverlayPanel. */
95
+ triggerRef: PropTypes.shape({})
52
96
  };
53
97
  OverlayPanel.defaultProps = {
54
98
  size: 'medium'
@@ -1,10 +1,10 @@
1
1
  import _slicedToArray from "@babel/runtime-corejs3/helpers/esm/slicedToArray";
2
2
  import _extends from "@babel/runtime-corejs3/helpers/esm/extends";
3
- import React, { useState } from 'react';
3
+ import React, { useState, useRef } from 'react';
4
4
  import { Item } from '@react-stately/collections';
5
5
  import { useOverlayPanelState } from '../../hooks';
6
6
  import OverlayPanel from './OverlayPanel';
7
- import { OverlayProvider, Box, Text, List, ListItem, Separator, Messages, Button, AccordionGroup } from '../../index';
7
+ import { OverlayProvider, Box, Text, List, ListItem, Separator, Messages, Button } from '../../index';
8
8
  import { panelSizes } from '../../utils/devUtils/constants/panelSizes';
9
9
  import { jsx as ___EmotionJSX } from "@emotion/react";
10
10
  export default {
@@ -37,28 +37,43 @@ export default {
37
37
  export var Default = function Default(_ref) {
38
38
  var args = _extends({}, _ref);
39
39
 
40
- var state = useOverlayPanelState();
40
+ var _useOverlayPanelState = useOverlayPanelState(),
41
+ state = _useOverlayPanelState.state,
42
+ onClose = _useOverlayPanelState.onClose;
43
+
44
+ var triggerRef = useRef();
41
45
  return (// Application must be wrapped in an OverlayProvider so that it can be hidden from screen
42
- // readers when an overlay opens.
46
+ // readers when an overlay is open.
43
47
  ___EmotionJSX(OverlayProvider, null, ___EmotionJSX(Text, null, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."), ___EmotionJSX("br", null), ___EmotionJSX(Button, {
48
+ ref: triggerRef,
44
49
  onPress: state.open
45
- }, "Open Panel"), ___EmotionJSX(OverlayPanel, _extends({
46
- isOpen: state.isOpen
47
- }, args), ___EmotionJSX(Box, null, ___EmotionJSX(Button, {
48
- onPress: state.close
49
- }, "Close Panel"), ___EmotionJSX(AccordionGroup, args, ___EmotionJSX(Item, {
50
- key: "accordionKey",
51
- textValue: "accordionKey",
52
- label: "Accordion Label",
53
- "data-id": "accordionItem"
54
- }, ___EmotionJSX(Text, null, "Render me!"))))))
50
+ }, "Open Panel"), state.isOpen && ___EmotionJSX(OverlayPanel, _extends({
51
+ isOpen: state.isOpen,
52
+ state: state
53
+ }, args, {
54
+ triggerRef: triggerRef
55
+ }), ___EmotionJSX(Box, null, ___EmotionJSX(Button, {
56
+ onPress: function onPress() {
57
+ onClose(state, triggerRef);
58
+ }
59
+ }, "Close Panel"), ___EmotionJSX(Text, {
60
+ pt: "md"
61
+ }, "Children render here."))))
55
62
  );
56
63
  };
57
64
  export var InnerPanel = function InnerPanel(_ref2) {
58
65
  var args = _extends({}, _ref2);
59
66
 
60
- var state = useOverlayPanelState();
61
- var innerState = useOverlayPanelState();
67
+ var _useOverlayPanelState2 = useOverlayPanelState(),
68
+ state = _useOverlayPanelState2.state,
69
+ onClose = _useOverlayPanelState2.onClose;
70
+
71
+ var _useOverlayPanelState3 = useOverlayPanelState(),
72
+ innerState = _useOverlayPanelState3.state,
73
+ onCloseInner = _useOverlayPanelState3.onClose;
74
+
75
+ var outerTriggerRef = useRef();
76
+ var innerTriggerRef = useRef();
62
77
 
63
78
  var _useState = useState(false),
64
79
  _useState2 = _slicedToArray(_useState, 2),
@@ -74,15 +89,22 @@ export var InnerPanel = function InnerPanel(_ref2) {
74
89
  innerState.close();
75
90
  }
76
91
 
77
- state.close();
92
+ onClose(state, outerTriggerRef);
93
+ };
94
+
95
+ var closeInnerPanel = function closeInnerPanel() {
96
+ onCloseInner(innerState, innerTriggerRef);
78
97
  };
79
98
 
80
99
  var inner = ___EmotionJSX(React.Fragment, null, innerState.isOpen && ___EmotionJSX(OverlayPanel, _extends({
81
100
  variant: "overlayPanel.overlayPanelInner" // applies higher z-index
82
101
  ,
83
102
  isOpen: innerState.isOpen
84
- }, args), ___EmotionJSX(Box, null, ___EmotionJSX(Button, {
85
- onPress: innerState.close
103
+ }, args, {
104
+ state: innerState,
105
+ triggerRef: innerTriggerRef
106
+ }), ___EmotionJSX(Box, null, ___EmotionJSX(Button, {
107
+ onPress: closeInnerPanel
86
108
  }, "Close Inner Panel"), ___EmotionJSX(Text, {
87
109
  pt: "md"
88
110
  }, "Children render here."))));
@@ -93,7 +115,10 @@ export var InnerPanel = function InnerPanel(_ref2) {
93
115
  sx: {
94
116
  p: '0px'
95
117
  }
96
- }, args), ___EmotionJSX(Box, {
118
+ }, args, {
119
+ state: state,
120
+ triggerRef: outerTriggerRef
121
+ }), ___EmotionJSX(Box, {
97
122
  sx: {
98
123
  p: '12px'
99
124
  }
@@ -116,39 +141,17 @@ export var InnerPanel = function InnerPanel(_ref2) {
116
141
  mr: "auto"
117
142
  }, "Form 2")), ___EmotionJSX(Separator, {
118
143
  margin: "0"
119
- }), ___EmotionJSX(ListItem, {
120
- title: "Form 3"
121
- }, ___EmotionJSX(Text, {
122
- variant: "itemTitle",
123
- alignSelf: "center",
124
- mr: "auto"
125
- }, "Form 3")), ___EmotionJSX(Separator, {
126
- margin: "0"
127
- }), ___EmotionJSX(ListItem, {
128
- title: "Form 4"
129
- }, ___EmotionJSX(Text, {
130
- variant: "itemTitle",
131
- alignSelf: "center",
132
- mr: "auto"
133
- }, "Form 4")), ___EmotionJSX(Separator, {
134
- margin: "0"
135
- }), ___EmotionJSX(ListItem, {
136
- title: "Form 5"
137
- }, ___EmotionJSX(Text, {
138
- variant: "itemTitle",
139
- alignSelf: "center",
140
- mr: "auto"
141
- }, "Form 5")), ___EmotionJSX(Separator, {
142
- margin: "0"
143
144
  })), ___EmotionJSX("br", null), ___EmotionJSX(Button, {
144
145
  onPress: toggleMessagesOpen
145
146
  }, "Toggle Messages"), ___EmotionJSX("br", null), ___EmotionJSX(Button, {
147
+ ref: innerTriggerRef,
146
148
  onPress: innerState.open
147
149
  }, "Open Inner Panel"), inner)));
148
150
 
149
151
  return (// Application must be wrapped in an OverlayProvider so that it can be hidden from screen
150
152
  // readers when an overlay opens.
151
153
  ___EmotionJSX(React.Fragment, null, ___EmotionJSX(OverlayProvider, null, ___EmotionJSX(Button, {
154
+ ref: outerTriggerRef,
152
155
  onPress: state.open
153
156
  }, "Open Panel"), outer), messagesOpen && ___EmotionJSX(Messages, {
154
157
  sx: {
@@ -161,20 +164,27 @@ export var InnerPanel = function InnerPanel(_ref2) {
161
164
  );
162
165
  };
163
166
  export var CustomWidth = function CustomWidth() {
164
- var state = useOverlayPanelState();
167
+ var _useOverlayPanelState4 = useOverlayPanelState(),
168
+ state = _useOverlayPanelState4.state,
169
+ onClose = _useOverlayPanelState4.onClose;
170
+
171
+ var triggerRef = useRef();
165
172
  return (// Application must be wrapped in an OverlayProvider so that it can be hidden from screen
166
173
  // readers when an overlay opens.
167
- //
168
- // For a custom width, provide the width via the 'sx' prop
169
174
  ___EmotionJSX(OverlayProvider, null, ___EmotionJSX(Text, null, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."), ___EmotionJSX("br", null), ___EmotionJSX(Button, {
175
+ ref: triggerRef,
170
176
  onPress: state.open
171
- }, "Open Panel"), ___EmotionJSX(OverlayPanel, {
177
+ }, "Open Panel"), state.isOpen && ___EmotionJSX(OverlayPanel, {
172
178
  isOpen: state.isOpen,
179
+ state: state,
180
+ triggerRef: triggerRef,
173
181
  sx: {
174
182
  width: '720px'
175
183
  }
176
184
  }, ___EmotionJSX(Box, null, ___EmotionJSX(Button, {
177
- onPress: state.close
185
+ onPress: function onPress() {
186
+ onClose(state, triggerRef);
187
+ }
178
188
  }, "Close Panel"), ___EmotionJSX(Text, {
179
189
  pt: "md"
180
190
  }, "Children render here."))))
@@ -1,7 +1,7 @@
1
1
  import _extends from "@babel/runtime-corejs3/helpers/esm/extends";
2
2
  import React from 'react';
3
3
  import axeTest from '../../utils/testUtils/testAxe';
4
- import { render, screen } from '../../utils/testUtils/testWrapper';
4
+ import { fireEvent, render, screen } from '../../utils/testUtils/testWrapper';
5
5
  import OverlayPanel from './OverlayPanel';
6
6
  import { jsx as ___EmotionJSX } from "@emotion/react";
7
7
  var testId = 'test-overlayPanel';
@@ -38,4 +38,76 @@ test('custom overlayPanel gets custom width', function () {
38
38
  var overlayPanel = screen.getByTestId(testId);
39
39
  expect(overlayPanel).toBeInTheDocument();
40
40
  expect(overlayPanel).toHaveStyleRule('width', '240px');
41
+ });
42
+ test('onClose callback fires when provided', function () {
43
+ var onClose = jest.fn();
44
+ getComponent({
45
+ onClose: onClose,
46
+ children: ___EmotionJSX("div", null, "Test")
47
+ });
48
+ var overlayPanel = screen.getByTestId(testId);
49
+ fireEvent.keyDown(overlayPanel, {
50
+ key: 'Escape',
51
+ code: 'Escape',
52
+ keyCode: 27,
53
+ charCode: 27
54
+ });
55
+ fireEvent.keyUp(overlayPanel, {
56
+ key: 'Escape',
57
+ code: 'Escape',
58
+ keyCode: 27,
59
+ charCode: 27
60
+ });
61
+ expect(onClose).toHaveBeenCalled();
62
+ });
63
+ test('neither callback fires when not provided', function () {
64
+ var onClose = jest.fn();
65
+ getComponent({
66
+ children: ___EmotionJSX("div", null, "Test")
67
+ });
68
+ var overlayPanel = screen.getByTestId(testId);
69
+ fireEvent.keyDown(overlayPanel, {
70
+ key: 'Escape',
71
+ code: 'Escape',
72
+ keyCode: 27,
73
+ charCode: 27
74
+ });
75
+ fireEvent.keyUp(overlayPanel, {
76
+ key: 'Escape',
77
+ code: 'Escape',
78
+ keyCode: 27,
79
+ charCode: 27
80
+ });
81
+ expect(onClose).not.toHaveBeenCalled();
82
+ });
83
+ test('triggerRef.current.focus() fires when provided', function () {
84
+ var onClose = jest.fn();
85
+ var focusFunction = jest.fn();
86
+ var state = {
87
+ close: onClose
88
+ };
89
+ var triggerRef = {
90
+ current: {
91
+ focus: focusFunction
92
+ }
93
+ };
94
+ getComponent({
95
+ state: state,
96
+ children: ___EmotionJSX("div", null, "Test"),
97
+ triggerRef: triggerRef
98
+ });
99
+ var overlayPanel = screen.getByTestId(testId);
100
+ fireEvent.keyDown(overlayPanel, {
101
+ key: 'Escape',
102
+ code: 'Escape',
103
+ keyCode: 27,
104
+ charCode: 27
105
+ });
106
+ fireEvent.keyUp(overlayPanel, {
107
+ key: 'Escape',
108
+ code: 'Escape',
109
+ keyCode: 27,
110
+ charCode: 27
111
+ });
112
+ expect(focusFunction).toHaveBeenCalled();
41
113
  });
@@ -14,11 +14,30 @@ var useOverlayPanelState = function useOverlayPanelState() {
14
14
  var isDefaultOpen = props.isDefaultOpen,
15
15
  isOpen = props.isOpen,
16
16
  onOpenChange = props.onOpenChange;
17
- return useOverlayTriggerState({
17
+ var state = useOverlayTriggerState({
18
18
  defaultOpen: isDefaultOpen,
19
19
  isOpen: isOpen,
20
20
  onOpenChange: onOpenChange
21
21
  });
22
+
23
+ var onClose = function onClose(stateProp, triggerRef, onCloseProp) {
24
+ if (stateProp) {
25
+ stateProp.close();
26
+ }
27
+
28
+ if (triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.current) {
29
+ triggerRef.current.focus();
30
+ }
31
+
32
+ if (onCloseProp) {
33
+ onCloseProp();
34
+ }
35
+ };
36
+
37
+ return {
38
+ state: state,
39
+ onClose: onClose
40
+ };
22
41
  };
23
42
 
24
43
  export default useOverlayPanelState;
@@ -7,8 +7,12 @@ test('default useOverlayPanelState', function () {
7
7
  result = _renderHook.result;
8
8
 
9
9
  expect(result.current).toEqual(expect.objectContaining({
10
- open: expect.any(Function),
11
- close: expect.any(Function),
12
- toggle: expect.any(Function)
10
+ state: {
11
+ open: expect.any(Function),
12
+ close: expect.any(Function),
13
+ toggle: expect.any(Function),
14
+ isOpen: expect.any(Boolean)
15
+ },
16
+ onClose: expect.any(Function)
13
17
  }));
14
18
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pingux/astro",
3
- "version": "1.1.0-alpha.15",
3
+ "version": "1.1.0-alpha.16",
4
4
  "description": "PingUX themeable React component library",
5
5
  "author": "uxdev@pingidentity.com",
6
6
  "license": "Apache-2.0",
@@ -78,7 +78,7 @@
78
78
  "@react-aria/color": "^3.0.0-beta.2",
79
79
  "@react-aria/combobox": "^3.0.0",
80
80
  "@react-aria/dialog": "^3.1.2",
81
- "@react-aria/focus": "^3.2.3",
81
+ "@react-aria/focus": "^3.5.0",
82
82
  "@react-aria/grid": "^3.0.0-beta.1",
83
83
  "@react-aria/i18n": "^3.3.0",
84
84
  "@react-aria/interactions": "^3.5.0",