@planningcenter/tapestry-react 2.0.0-rc.0 → 2.0.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/cjs/DataTable/hooks/useCollapsibleRows.js +1 -1
  2. package/dist/cjs/Dropdown/Dropdown.test.js +194 -19
  3. package/dist/cjs/Group/Group.js +6 -4
  4. package/dist/cjs/Modal/Modal.test.js +156 -0
  5. package/dist/cjs/Popover/Popover.js +4 -0
  6. package/dist/cjs/Popover/Popover.test.js +65 -0
  7. package/dist/cjs/Popover/rewireTabOrder.js +13 -30
  8. package/dist/cjs/Scrim/Scrim.js +1 -1
  9. package/dist/cjs/Select/Select.js +9 -0
  10. package/dist/cjs/Sidebar/Sidebar.js +4 -3
  11. package/dist/cjs/Table/Table.js +3 -5
  12. package/dist/cjs/ThemeProvider/ThemeProvider.js +5 -5
  13. package/dist/cjs/hooks/useConstant.js +23 -0
  14. package/dist/cjs/system/utils.js +2 -2
  15. package/dist/cjs/utils.js +3 -3
  16. package/dist/esm/DataTable/hooks/useCollapsibleRows.js +1 -1
  17. package/dist/esm/Dropdown/Dropdown.test.js +158 -17
  18. package/dist/esm/Group/Group.js +6 -4
  19. package/dist/esm/Modal/Modal.test.js +122 -0
  20. package/dist/esm/Popover/Popover.js +4 -0
  21. package/dist/esm/Popover/Popover.test.js +51 -0
  22. package/dist/esm/Popover/rewireTabOrder.js +13 -33
  23. package/dist/esm/Scrim/Scrim.js +1 -1
  24. package/dist/esm/Select/Select.js +9 -0
  25. package/dist/esm/Sidebar/Sidebar.js +4 -2
  26. package/dist/esm/Table/Table.js +1 -2
  27. package/dist/esm/ThemeProvider/ThemeProvider.js +1 -1
  28. package/dist/esm/hooks/useConstant.js +15 -0
  29. package/dist/esm/system/utils.js +1 -1
  30. package/dist/esm/utils.js +3 -3
  31. package/dist/types/Dropdown/Dropdown.test.d.ts +1 -1
  32. package/dist/types/Group/Group.d.ts +5 -1
  33. package/dist/types/Modal/Modal.test.d.ts +1 -0
  34. package/dist/types/Popover/Popover.test.d.ts +1 -0
  35. package/dist/types/hooks/useConstant.d.ts +1 -0
  36. package/package.json +15 -24
  37. package/src/DataTable/hooks/useCollapsibleRows.js +1 -1
  38. package/src/Dropdown/Dropdown.test.tsx +128 -17
  39. package/src/Group/Group.tsx +9 -3
  40. package/src/Modal/Modal.test.tsx +113 -0
  41. package/src/Popover/Popover.test.tsx +62 -0
  42. package/src/Popover/Popover.tsx +3 -0
  43. package/src/Popover/rewireTabOrder.ts +24 -42
  44. package/src/Scrim/Scrim.tsx +3 -3
  45. package/src/Select/Select.js +6 -0
  46. package/src/Sidebar/Sidebar.js +7 -5
  47. package/src/Table/Table.js +1 -2
  48. package/src/ThemeProvider/ThemeProvider.tsx +1 -1
  49. package/src/hooks/useConstant.ts +17 -0
  50. package/src/system/utils.js +1 -1
  51. package/src/utils.js +3 -3
  52. package/src/utils.test.js +29 -0
@@ -18,7 +18,7 @@ var _core = require("@emotion/core");
18
18
 
19
19
  var _cache = _interopRequireDefault(require("@emotion/cache"));
20
20
 
21
- var _deepmerge = _interopRequireDefault(require("deepmerge"));
21
+ var _lodash = require("lodash");
22
22
 
23
23
  var _defaultTheme = _interopRequireDefault(require("../system/default-theme"));
24
24
 
@@ -78,12 +78,12 @@ function mergeThemes(a, b) {
78
78
 
79
79
  return _objectSpread(_objectSpread(_objectSpread({}, a), b), {}, {
80
80
  button: _objectSpread(_objectSpread(_objectSpread({}, a.button), b.button), {}, {
81
- themes: (0, _deepmerge["default"])(((_a$button = a.button) == null ? void 0 : _a$button.themes) || {}, ((_b$button = b.button) == null ? void 0 : _b$button.themes) || {})
81
+ themes: (0, _lodash.merge)(((_a$button = a.button) == null ? void 0 : _a$button.themes) || {}, ((_b$button = b.button) == null ? void 0 : _b$button.themes) || {})
82
82
  }),
83
- colors: (0, _deepmerge["default"])((0, _system.flattenPalette)(a.colors || {}), (0, _system.flattenPalette)(b.colors || {})),
83
+ colors: (0, _lodash.merge)((0, _system.flattenPalette)(a.colors || {}), (0, _system.flattenPalette)(b.colors || {})),
84
84
  spinner: _objectSpread(_objectSpread(_objectSpread({}, a.spinner), b.spinner), {}, {
85
- sizes: (0, _deepmerge["default"])(((_a$spinner = a.spinner) == null ? void 0 : _a$spinner.sizes) || {}, ((_b$spinner = b.spinner) == null ? void 0 : _b$spinner.sizes) || {}),
86
- thickness: (0, _deepmerge["default"])(((_a$spinner2 = a.spinner) == null ? void 0 : _a$spinner2.thickness) || {}, ((_b$spinner2 = b.spinner) == null ? void 0 : _b$spinner2.thickness) || {})
85
+ sizes: (0, _lodash.merge)(((_a$spinner = a.spinner) == null ? void 0 : _a$spinner.sizes) || {}, ((_b$spinner = b.spinner) == null ? void 0 : _b$spinner.sizes) || {}),
86
+ thickness: (0, _lodash.merge)(((_a$spinner2 = a.spinner) == null ? void 0 : _a$spinner2.thickness) || {}, ((_b$spinner2 = b.spinner) == null ? void 0 : _b$spinner2.thickness) || {})
87
87
  })
88
88
  });
89
89
  }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
+
5
+ exports.__esModule = true;
6
+ exports["default"] = useConstant;
7
+
8
+ var React = _interopRequireWildcard(require("react"));
9
+
10
+ // A copy/paste of the source of the `use-constant` package.
11
+ // https://github.com/Andarist/use-constant/blob/main/src/index.ts
12
+ // This is done to reduce dependencies.
13
+ function useConstant(fn) {
14
+ var ref = React.useRef();
15
+
16
+ if (!ref.current) {
17
+ ref.current = {
18
+ v: fn()
19
+ };
20
+ }
21
+
22
+ return ref.current.v;
23
+ }
@@ -24,7 +24,7 @@ var _core = require("@emotion/core");
24
24
 
25
25
  var _polished = require("polished");
26
26
 
27
- var _lodash = _interopRequireDefault(require("lodash.get"));
27
+ var _lodash = require("lodash");
28
28
 
29
29
  var _colors = require("./colors");
30
30
 
@@ -161,7 +161,7 @@ function getForegroundColor(color) {
161
161
 
162
162
  function useThemeValue(path, defaultValue) {
163
163
  var userTheme = (0, _react.useContext)(_core.ThemeContext);
164
- return path ? (0, _lodash["default"])(userTheme, path, defaultValue || (0, _lodash["default"])(_defaultTheme["default"], path)) : userTheme || _defaultTheme["default"];
164
+ return path ? (0, _lodash.get)(userTheme, path, defaultValue || (0, _lodash.get)(_defaultTheme["default"], path)) : userTheme || _defaultTheme["default"];
165
165
  }
166
166
 
167
167
  function useThemeProps(path, props) {
package/dist/cjs/utils.js CHANGED
@@ -46,7 +46,7 @@ var _react = require("react");
46
46
 
47
47
  var _tabbable = require("tabbable");
48
48
 
49
- var _camelToKebab = _interopRequireDefault(require("camel-to-kebab"));
49
+ var _lodash = require("lodash");
50
50
 
51
51
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
52
52
 
@@ -785,11 +785,11 @@ function createCSSProperty(key, value) {
785
785
  createdCSSProperties.add(key);
786
786
  }
787
787
 
788
- return "--" + (0, _camelToKebab["default"])(key) + "-" + (0, _camelToKebab["default"])(value);
788
+ return "--" + (0, _lodash.kebabCase)(key) + "-" + (0, _lodash.kebabCase)(value);
789
789
  }
790
790
 
791
791
  function getCSSProperty(key, value) {
792
- return key && value ? "var(--" + (0, _camelToKebab["default"])(key) + "-" + (0, _camelToKebab["default"])(value) + ")" : undefined;
792
+ return key && value ? "var(--" + (0, _lodash.kebabCase)(key) + "-" + (0, _lodash.kebabCase)(value) + ")" : undefined;
793
793
  }
794
794
 
795
795
  function objectToCSSProperties(themeKey, props) {
@@ -5,7 +5,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
5
5
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
6
6
 
7
7
  import { createContext, useCallback, useContext, useEffect } from 'react';
8
- import useConstant from 'use-constant';
8
+ import useConstant from '../../hooks/useConstant';
9
9
  import create from 'zustand';
10
10
  import { range } from '../../utils';
11
11
  export var CollapsibleRowsContext = /*#__PURE__*/createContext(null);
@@ -1,26 +1,167 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { fireEvent, render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import '@testing-library/jest-dom/extend-expect';
5
+ import { noop } from 'lodash';
6
+ import { Box, Button, Text, ThemeProvider } from '..';
3
7
  import Dropdown from './Dropdown';
4
- import ThemeProvider from '../ThemeProvider';
5
8
 
6
9
  var _ref = /*#__PURE__*/React.createElement(Dropdown, {
7
10
  title: "A menu"
8
11
  });
9
12
 
10
- it("should render title", function () {
11
- render(_ref);
12
- screen.getByText('A menu');
13
- });
14
- it("calls passed onSearch function when receiving keyboard input", function () {
15
- var customTextSearch = jest.fn();
16
- render( /*#__PURE__*/React.createElement(ThemeProvider, null, /*#__PURE__*/React.createElement(Dropdown, {
17
- title: "A menu",
18
- onSearch: customTextSearch
19
- })));
20
- var dropdown = screen.getByText('A menu');
21
- fireEvent.keyDown(dropdown, {
22
- key: 'A',
23
- code: 'KeyA'
13
+ describe('Dropdown', function () {
14
+ describe('Text search', function () {
15
+ it("should render title", function () {
16
+ render(_ref);
17
+ screen.getByText('A menu');
18
+ });
19
+ it("calls passed onSearch function when receiving keyboard input", function () {
20
+ var customTextSearch = jest.fn();
21
+ render( /*#__PURE__*/React.createElement(ThemeProvider, null, /*#__PURE__*/React.createElement(Dropdown, {
22
+ title: "A menu",
23
+ onSearch: customTextSearch
24
+ })));
25
+ var dropdown = screen.getByText('A menu');
26
+ fireEvent.keyDown(dropdown, {
27
+ key: 'A',
28
+ code: 'KeyA'
29
+ });
30
+ expect(customTextSearch).toHaveBeenCalled();
31
+ });
32
+ });
33
+ describe('Keyboard navigation', function () {
34
+ var StatefulDropdown = function StatefulDropdown() {
35
+ var _useState = useState(0),
36
+ count = _useState[0],
37
+ setCount = _useState[1];
38
+
39
+ return /*#__PURE__*/React.createElement(ThemeProvider, null, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Dropdown, null, /*#__PURE__*/React.createElement(Dropdown.Item, null, /*#__PURE__*/React.createElement(Button, {
40
+ title: "One",
41
+ onClick: function onClick() {
42
+ return noop();
43
+ }
44
+ })), /*#__PURE__*/React.createElement(Dropdown.Item, null, /*#__PURE__*/React.createElement(Button, {
45
+ title: "Two",
46
+ onClick: function onClick() {
47
+ return setCount(function (c) {
48
+ return c + 1;
49
+ });
50
+ }
51
+ }), /*#__PURE__*/React.createElement(Text, {
52
+ "data-testid": "count-text"
53
+ }, count)), /*#__PURE__*/React.createElement(Dropdown.Item, null, /*#__PURE__*/React.createElement(Button, {
54
+ title: "Three",
55
+ onClick: function onClick() {
56
+ return noop();
57
+ }
58
+ })))));
59
+ };
60
+
61
+ var _ref2 = /*#__PURE__*/React.createElement(StatefulDropdown, null);
62
+
63
+ it('should trap focus, tab forward through all elements', function () {
64
+ render(_ref2);
65
+ userEvent.tab();
66
+ userEvent.keyboard('{enter}');
67
+
68
+ var _screen$getAllByRole = screen.getAllByRole('button'),
69
+ openPopover = _screen$getAllByRole[0],
70
+ buttonOne = _screen$getAllByRole[1],
71
+ buttonTwo = _screen$getAllByRole[2],
72
+ buttonThree = _screen$getAllByRole[3];
73
+
74
+ expect(openPopover).toHaveFocus();
75
+ userEvent.tab();
76
+ expect(buttonOne).toHaveFocus();
77
+ userEvent.tab();
78
+ expect(buttonTwo).toHaveFocus();
79
+ userEvent.tab();
80
+ expect(buttonThree).toHaveFocus();
81
+ userEvent.tab();
82
+ expect(buttonOne).toHaveFocus();
83
+ });
84
+
85
+ var _ref3 = /*#__PURE__*/React.createElement(StatefulDropdown, null);
86
+
87
+ it('should trap focus, tab backwards from first element to last', function () {
88
+ render(_ref3);
89
+ userEvent.tab();
90
+ userEvent.keyboard('{enter}');
91
+
92
+ var _screen$getAllByRole2 = screen.getAllByRole('button'),
93
+ openPopover = _screen$getAllByRole2[0],
94
+ buttonOne = _screen$getAllByRole2[1],
95
+ _buttonTwo = _screen$getAllByRole2[2],
96
+ buttonThree = _screen$getAllByRole2[3];
97
+
98
+ expect(openPopover).toHaveFocus();
99
+ userEvent.tab();
100
+ expect(buttonOne).toHaveFocus();
101
+ userEvent.tab({
102
+ shift: true
103
+ });
104
+ expect(buttonThree).toHaveFocus();
105
+ });
106
+
107
+ var _ref4 = /*#__PURE__*/React.createElement(StatefulDropdown, null);
108
+
109
+ it('should retain tab order after rerender', function () {
110
+ render(_ref4);
111
+ userEvent.tab();
112
+ userEvent.keyboard('{enter}');
113
+
114
+ var _screen$getAllByRole3 = screen.getAllByRole('button'),
115
+ buttonOne = _screen$getAllByRole3[1],
116
+ buttonTwo = _screen$getAllByRole3[2],
117
+ buttonThree = _screen$getAllByRole3[3];
118
+
119
+ userEvent.tab();
120
+ userEvent.tab();
121
+ expect(screen.getByTestId('count-text')).toHaveTextContent('0');
122
+ userEvent.keyboard('{enter}');
123
+ expect(screen.getByTestId('count-text')).toHaveTextContent('1');
124
+ expect(buttonTwo).toHaveFocus();
125
+ userEvent.tab();
126
+ expect(buttonThree).toHaveFocus();
127
+ userEvent.tab({
128
+ shift: true
129
+ });
130
+ userEvent.tab({
131
+ shift: true
132
+ });
133
+ expect(buttonOne).toHaveFocus();
134
+ userEvent.tab();
135
+ userEvent.tab();
136
+ userEvent.tab();
137
+ expect(buttonOne).toHaveFocus();
138
+ userEvent.tab({
139
+ shift: true
140
+ });
141
+ userEvent.tab({
142
+ shift: true
143
+ });
144
+ userEvent.tab({
145
+ shift: true
146
+ });
147
+ expect(buttonOne).toHaveFocus();
148
+ });
149
+
150
+ var _ref5 = /*#__PURE__*/React.createElement(StatefulDropdown, null);
151
+
152
+ it('should focus dropdown toggle when a user presses escape key', function () {
153
+ render(_ref5);
154
+ userEvent.tab();
155
+ userEvent.keyboard('{enter}');
156
+
157
+ var _screen$getAllByRole4 = screen.getAllByRole('button'),
158
+ openPopover = _screen$getAllByRole4[0],
159
+ buttonOne = _screen$getAllByRole4[1];
160
+
161
+ userEvent.tab();
162
+ expect(buttonOne).toHaveFocus();
163
+ userEvent.keyboard("{esc}");
164
+ expect(openPopover).toHaveFocus();
165
+ });
24
166
  });
25
- expect(customTextSearch).toHaveBeenCalled();
26
167
  });
@@ -16,7 +16,8 @@ export function Group(_ref) {
16
16
  childProps = _ref.childProps,
17
17
  children = _ref.children,
18
18
  radius = _ref.radius,
19
- restProps = _objectWithoutPropertiesLoose(_ref, ["axis", "childProps", "children", "radius"]);
19
+ spacing = _ref.spacing,
20
+ restProps = _objectWithoutPropertiesLoose(_ref, ["axis", "childProps", "children", "radius", "spacing"]);
20
21
 
21
22
  var themeRadius = useThemeValue('group.radius');
22
23
  var radiusValue = radius != null ? radius : themeRadius;
@@ -26,15 +27,16 @@ export function Group(_ref) {
26
27
  var marginBottom = isHorizontalLayout ? 0 : '-1px';
27
28
  var marginRight = isHorizontalLayout ? '-1px' : 0;
28
29
  return /*#__PURE__*/React.createElement(StackView, _extends({
29
- axis: axis
30
+ axis: axis,
31
+ spacing: spacing
30
32
  }, restProps), cloneChildren(children, function (child, _ref2) {
31
33
  var _objectSpread2;
32
34
 
33
35
  var firstChild = _ref2.firstChild,
34
36
  lastChild = _ref2.lastChild;
35
- return _objectSpread(_objectSpread(_objectSpread({}, childProps), {}, (_objectSpread2 = {}, _objectSpread2[startRadius] = firstChild ? radiusValue : 0, _objectSpread2[endRadius] = lastChild ? radiusValue : 0, _objectSpread2), !lastChild && {
37
+ return _objectSpread(_objectSpread(_objectSpread({}, childProps), {}, (_objectSpread2 = {}, _objectSpread2[startRadius] = firstChild ? radiusValue : 0, _objectSpread2[endRadius] = lastChild ? radiusValue : 0, _objectSpread2), !spacing && !lastChild && {
36
38
  marginBottom: marginBottom
37
- }), !lastChild && {
39
+ }), !spacing && !lastChild && {
38
40
  marginRight: marginRight
39
41
  });
40
42
  }));
@@ -0,0 +1,122 @@
1
+ import React, { useState } from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import '@testing-library/jest-dom/extend-expect';
5
+ import Dropdown from '../Dropdown';
6
+ import Select from '../Select';
7
+ import { Button, Heading, Modal, ThemeProvider } from '../';
8
+ import { noop } from 'lodash';
9
+
10
+ var _ref = /*#__PURE__*/React.createElement(Heading, {
11
+ "data-testid": "modal-header"
12
+ }, "Hello");
13
+
14
+ describe('Modal', function () {
15
+ var TestModal = function TestModal() {
16
+ var _useState = useState(false),
17
+ open = _useState[0],
18
+ setOpen = _useState[1];
19
+
20
+ return /*#__PURE__*/React.createElement(ThemeProvider, null, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
21
+ "data-testid": "modal-toggle",
22
+ onClick: function onClick() {
23
+ return setOpen(!open);
24
+ },
25
+ title: "Toggle modal"
26
+ }), /*#__PURE__*/React.createElement(Modal, {
27
+ open: open,
28
+ onRequestClose: function onRequestClose() {
29
+ return setOpen(false);
30
+ }
31
+ }, _ref, /*#__PURE__*/React.createElement(Dropdown, null, [1, 2, 3].map(function (n) {
32
+ return /*#__PURE__*/React.createElement(Dropdown.Item, {
33
+ "data-testid": "dropdown-item-" + n,
34
+ key: n
35
+ }, n);
36
+ })), /*#__PURE__*/React.createElement(Select, {
37
+ onChange: noop
38
+ }, [1, 2, 3].map(function (n) {
39
+ return /*#__PURE__*/React.createElement(Select.Option, {
40
+ "data-testid": "select-option-" + n,
41
+ key: n,
42
+ value: n
43
+ }, n);
44
+ })))));
45
+ };
46
+
47
+ var _ref2 = /*#__PURE__*/React.createElement(TestModal, null);
48
+
49
+ var _ref3 = /*#__PURE__*/React.createElement(TestModal, null);
50
+
51
+ var _ref4 = /*#__PURE__*/React.createElement(TestModal, null);
52
+
53
+ var _ref5 = /*#__PURE__*/React.createElement(TestModal, null);
54
+
55
+ var _ref6 = /*#__PURE__*/React.createElement(TestModal, null);
56
+
57
+ var _ref7 = /*#__PURE__*/React.createElement(TestModal, null);
58
+
59
+ describe('Keyboard navigation', function () {
60
+ it('should close modal on escape keypress (dropdown button has focus)', function () {
61
+ render(_ref2);
62
+ expect(screen.queryByTestId('modal-header')).toBeNull();
63
+ userEvent.click(screen.getByTestId('modal-toggle'));
64
+ expect(screen.getByTestId('modal-header')).toBeInTheDocument();
65
+ userEvent.keyboard("{esc}");
66
+ expect(screen.queryByTestId('modal-header')).toBeNull();
67
+ });
68
+ it('should not close modal when escape key closes a dropdown within modal', function () {
69
+ render(_ref3);
70
+ userEvent.click(screen.getByTestId('modal-toggle'));
71
+
72
+ var _screen$getAllByRole = screen.getAllByRole('button'),
73
+ dropdown = _screen$getAllByRole[1];
74
+
75
+ expect(dropdown).toHaveFocus();
76
+ userEvent.keyboard('{arrowdown}');
77
+ userEvent.keyboard("{esc}");
78
+ expect(screen.getByTestId('modal-header')).toBeInTheDocument();
79
+ });
80
+ it('should close dropdown on escape & close modal on subsequent escape', function () {
81
+ render(_ref4);
82
+ userEvent.click(screen.getByTestId('modal-toggle'));
83
+ userEvent.keyboard('{arrowdown}');
84
+ userEvent.keyboard("{esc}");
85
+ expect(screen.getByTestId('modal-header')).toBeInTheDocument();
86
+ userEvent.keyboard("{esc}");
87
+ expect(screen.queryByTestId('modal-header')).toBeNull();
88
+ });
89
+ it('should hide select popover on escape, but select remains in dom', function () {
90
+ render(_ref5);
91
+ userEvent.click(screen.getByTestId('modal-toggle'));
92
+ userEvent.tab();
93
+ userEvent.keyboard('{arrowdown}');
94
+ userEvent.keyboard("{esc}");
95
+ expect(screen.queryByTestId('select-option-1')).toBeInTheDocument();
96
+ });
97
+ it('should hide select popover on escape, close (unmount) select popover on subsequent escape, persist modal', function () {
98
+ render(_ref6);
99
+ userEvent.click(screen.getByTestId('modal-toggle'));
100
+ userEvent.tab();
101
+ userEvent.keyboard('{arrowdown}');
102
+ userEvent.keyboard("{esc}");
103
+ expect(screen.queryByTestId('select-option-1')).toBeInTheDocument();
104
+ userEvent.keyboard("{esc}");
105
+ expect(screen.queryByTestId('select-option-1')).toBeNull();
106
+ expect(screen.queryByTestId('modal-header')).toBeInTheDocument();
107
+ });
108
+ it('should hide select popover on escape, close (unmount) select popover on subsequent escape, close modal on subsequent escape', function () {
109
+ render(_ref7);
110
+ userEvent.click(screen.getByTestId('modal-toggle'));
111
+ userEvent.tab();
112
+ userEvent.keyboard('{arrowdown}');
113
+ userEvent.keyboard("{esc}");
114
+ expect(screen.queryByTestId('select-option-1')).toBeInTheDocument();
115
+ userEvent.keyboard("{esc}");
116
+ expect(screen.queryByTestId('select-option-1')).toBeNull();
117
+ expect(screen.queryByTestId('modal-header')).toBeInTheDocument();
118
+ userEvent.keyboard("{esc}");
119
+ expect(screen.queryByTestId('modal-header')).toBeNull();
120
+ });
121
+ });
122
+ });
@@ -103,6 +103,10 @@ export var Popover = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
103
103
 
104
104
  var requestClose = function requestClose(event) {
105
105
  if (event.key === 'Escape' && onRequestClose) {
106
+ if (open) {
107
+ event.nativeEvent.stopImmediatePropagation();
108
+ }
109
+
106
110
  onRequestClose();
107
111
  }
108
112
 
@@ -0,0 +1,51 @@
1
+ import React, { useState } from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import '@testing-library/jest-dom/extend-expect';
5
+ import { Box, Text, Button, Popover } from '..';
6
+
7
+ var PopBasic = function PopBasic(_ref) {
8
+ var children = _ref.children;
9
+
10
+ var _useState = useState(false),
11
+ open = _useState[0],
12
+ setOpen = _useState[1];
13
+
14
+ return /*#__PURE__*/React.createElement(Box, {
15
+ id: "container"
16
+ }, /*#__PURE__*/React.createElement(Popover, {
17
+ open: open,
18
+ offset: 1,
19
+ renderTo: "#container",
20
+ anchorElement: /*#__PURE__*/React.createElement(Button, {
21
+ title: "Toggle",
22
+ onClick: function onClick() {
23
+ return setOpen(!open);
24
+ },
25
+ id: "anchor-element"
26
+ })
27
+ }, children));
28
+ };
29
+
30
+ var _ref2 = /*#__PURE__*/React.createElement(PopBasic, null, /*#__PURE__*/React.createElement(Box, null));
31
+
32
+ describe('Popover', function () {
33
+ it('should not render string in closed Popover', function () {
34
+ var string = 'Test Text';
35
+ render( /*#__PURE__*/React.createElement(PopBasic, null, /*#__PURE__*/React.createElement(Text, null, string)));
36
+ expect(screen.queryByText(string)).toBeNull();
37
+ });
38
+ it('should render string in open Popover', function () {
39
+ var string = 'Test Text';
40
+ render( /*#__PURE__*/React.createElement(PopBasic, null, /*#__PURE__*/React.createElement(Text, null, string)));
41
+ var openPopover = screen.getByRole('button');
42
+ userEvent.click(openPopover);
43
+ expect(screen.getByText(string)).toBeInTheDocument();
44
+ });
45
+ it('should focus open button on click', function () {
46
+ render(_ref2);
47
+ var openPopover = screen.getByRole('button');
48
+ userEvent.click(openPopover);
49
+ expect(openPopover).toHaveFocus();
50
+ });
51
+ });
@@ -32,43 +32,31 @@ var RewireTabOrder = /*#__PURE__*/function () {
32
32
 
33
33
  _defineProperty(this, "nextActiveElement", null);
34
34
 
35
- _defineProperty(this, "focusOriginalActiveElement", function () {
36
- _this.originalActiveElement.focus();
37
- });
38
-
39
- _defineProperty(this, "focusNextActiveElement", function () {
40
- _this.nextActiveElement.focus();
41
- });
42
-
43
35
  _defineProperty(this, "moveFocusToTargetFromTriggerElement", function (event) {
44
36
  if (!event.shiftKey && event.keyCode === TAB_KEY) {
45
37
  event.preventDefault();
46
38
 
47
- _this.firstFocusableElement.focus();
39
+ if (_this.tabbables.includes(_this.nextActiveElement)) {
40
+ _this.nextActiveElement.focus();
41
+ } else {
42
+ _this.firstFocusableElement.focus();
43
+ }
48
44
  }
49
45
  });
50
46
 
51
- _defineProperty(this, "moveFocusFromTargetToTriggerElement", function (event) {
47
+ _defineProperty(this, "moveFocusToTargetFromFirstElement", function (event) {
52
48
  if (event.target === _this.firstFocusableElement && event.shiftKey && event.keyCode === TAB_KEY) {
53
49
  event.preventDefault();
54
50
 
55
- _this.originalActiveElement.focus();
56
- }
57
- });
58
-
59
- _defineProperty(this, "moveFocusToTargetFromNextElement", function (event) {
60
- if (event.shiftKey && event.keyCode === TAB_KEY) {
61
- event.preventDefault();
62
-
63
51
  _this.lastFocusableElement.focus();
64
52
  }
65
53
  });
66
54
 
67
- _defineProperty(this, "moveFocusFromTargetToNextElement", function (event) {
68
- if (!event.shiftKey && event.keyCode === TAB_KEY) {
55
+ _defineProperty(this, "moveFocusToTargetFromLastElement", function (event) {
56
+ if (event.target === _this.lastFocusableElement && !event.shiftKey && event.keyCode === TAB_KEY) {
69
57
  event.preventDefault();
70
58
 
71
- _this.nextActiveElement.focus();
59
+ _this.firstFocusableElement.focus();
72
60
  }
73
61
  });
74
62
 
@@ -89,12 +77,8 @@ var RewireTabOrder = /*#__PURE__*/function () {
89
77
  this.originalActiveElement = document.activeElement;
90
78
  this.nextActiveElement = getNextActiveElement(this.originalActiveElement);
91
79
  this.originalActiveElement.addEventListener('keydown', this.moveFocusToTargetFromTriggerElement);
92
- this.firstFocusableElement.addEventListener('keydown', this.moveFocusFromTargetToTriggerElement);
93
-
94
- if (this.nextActiveElement) {
95
- this.nextActiveElement.addEventListener('keydown', this.moveFocusToTargetFromNextElement);
96
- this.lastFocusableElement.addEventListener('keydown', this.moveFocusFromTargetToNextElement);
97
- }
80
+ this.firstFocusableElement.addEventListener('keydown', this.moveFocusToTargetFromFirstElement);
81
+ this.lastFocusableElement.addEventListener('keydown', this.moveFocusToTargetFromLastElement);
98
82
  };
99
83
 
100
84
  _proto.destroy = function destroy() {
@@ -103,12 +87,8 @@ var RewireTabOrder = /*#__PURE__*/function () {
103
87
  }
104
88
 
105
89
  this.originalActiveElement.removeEventListener('keydown', this.moveFocusToTargetFromTriggerElement);
106
- this.firstFocusableElement.removeEventListener('keydown', this.moveFocusFromTargetToTriggerElement);
107
-
108
- if (this.nextActiveElement) {
109
- this.nextActiveElement.removeEventListener('keydown', this.moveFocusToTargetFromNextElement);
110
- this.lastFocusableElement.removeEventListener('keydown', this.moveFocusFromTargetToNextElement);
111
- }
90
+ this.firstFocusableElement.removeEventListener('keydown', this.moveFocusToTargetFromFirstElement);
91
+ this.lastFocusableElement.removeEventListener('keydown', this.moveFocusToTargetFromLastElement);
112
92
  };
113
93
 
114
94
  return RewireTabOrder;
@@ -1,7 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import StackView from '../StackView';
4
- var Scrim = /*#__PURE__*/React.forwardRef(function (props) {
4
+ var Scrim = /*#__PURE__*/React.forwardRef(function (props, _ref) {
5
5
  React.useLayoutEffect(function () {
6
6
  document.body.style.overflow = 'hidden';
7
7
  return function () {
@@ -358,6 +358,15 @@ var Select = /*#__PURE__*/function (_Component) {
358
358
 
359
359
  break;
360
360
 
361
+ case 'Escape':
362
+ if (isPopoverOpen && !isPopoverVisible) {
363
+ event.preventDefault();
364
+
365
+ _this2.closePopover();
366
+ }
367
+
368
+ break;
369
+
361
370
  case ' ':
362
371
  event.preventDefault();
363
372
 
@@ -7,7 +7,6 @@ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWith
7
7
  import { jsx } from '@emotion/core';
8
8
  import { Children } from 'react';
9
9
  import StickyBox from 'react-sticky-box';
10
- import warning from 'warning';
11
10
  import { css } from '../system';
12
11
  import StackView from '../StackView';
13
12
  import SidebarList from './SidebarList';
@@ -29,7 +28,10 @@ function Sidebar(_ref) {
29
28
  }, restProps), jsx(StickyBox, {
30
29
  css: flexColumnCss
31
30
  }, Children.map(children, function (child) {
32
- warning(child && child.type !== Sidebar.List || child && child.type !== Sidebar.Item, "Invalid component nesting. " + child.type + " cannot appear as a child of <Sidebar>. Only <Sidebar.List/> or <Sidebar.Item/> may be used.");
31
+ if (child && child.type !== Sidebar.List || child && child.type !== Sidebar.Item) {
32
+ console.warn("Invalid component nesting. " + child.type + " cannot appear as a child of <Sidebar>. Only <Sidebar.List/> or <Sidebar.Item/> may be used.");
33
+ }
34
+
33
35
  return child;
34
36
  })));
35
37
  }
@@ -10,8 +10,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
10
10
 
11
11
  import React, { PureComponent, Children, Fragment } from 'react';
12
12
  import { Global } from '@emotion/core';
13
- import camelCase from 'lodash.camelcase';
14
- import snakeCase from 'lodash.snakecase';
13
+ import { camelCase, snakeCase } from 'lodash';
15
14
  import Button from '../Button';
16
15
  import DragDrop from '../DragDrop';
17
16
  import Pagination from '../Pagination';
@@ -8,7 +8,7 @@ import React, { useLayoutEffect, useState } from 'react';
8
8
  import { ThemeProvider as EmotionThemeProvider } from 'emotion-theming';
9
9
  import { CacheProvider } from '@emotion/core';
10
10
  import createCache from '@emotion/cache';
11
- import merge from 'deepmerge';
11
+ import { merge } from 'lodash';
12
12
  import defaultTheme from '../system/default-theme';
13
13
  import { flattenPalette } from '../system';
14
14
  import { objectToCSSProperties, shallowEqual } from '../utils';