focus-trap-react 8.4.1 → 8.6.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.
- package/CHANGELOG.md +30 -0
- package/README.md +12 -13
- package/dist/focus-trap-react.js +102 -36
- package/package.json +27 -29
- package/src/focus-trap-react.js +93 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 8.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5292ae8:
|
|
8
|
+
- Adding support for new focus-trap options from focus-trap v6.5.0: `checkCanFocusTrap()`, `onPostActivate()`, `checkCanReturnFocus()`, and `onPostDeactivate()`.
|
|
9
|
+
- Adding support (bug fix) for existing focus-trap `setReturnFocus` option that had thus far been ignored, with focus-trap-react always returning focus to the previously-focused element prior to activation regardless of the use of the `setReturnFocus` option. The option is now respected the same as it is when using focus-trap directly.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 24704c7: Bump focus-trap dependency to 6.5.1 for bug fix to onPostDeactivate.
|
|
14
|
+
|
|
15
|
+
## 8.5.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- b8d7071: Bump focus-trap dependency to 6.5.0 to get new features
|
|
20
|
+
|
|
21
|
+
## 8.5.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 6ee37fb: Bump focus-trap from 6.3.0 to 6.4.0. There should be no changes in behavior as a result of this upgrade.
|
|
26
|
+
|
|
27
|
+
## 8.4.2
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- f9a6d1a: Throw an error if a Fragment is given as the child container (currently, it appears to work, but the trap is actually not activated because focus-trap can't find the DOM element for the Fragment "container"). (Fixes #268)
|
|
32
|
+
|
|
3
33
|
## 8.4.1
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# focus-trap-react [](https://github.com/focus-trap/focus-trap-react/actions?query=workflow:CI+branch:master) [](https://codecov.io/gh/focus-trap/focus-trap-react) [](./LICENSE)
|
|
2
2
|
|
|
3
3
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
4
|
-
[](#contributors)
|
|
5
5
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
6
6
|
|
|
7
7
|
A React component that traps focus.
|
|
@@ -43,7 +43,7 @@ Why? Because this module's core functionality comes from focus-trap, which uses
|
|
|
43
43
|
|
|
44
44
|
## Usage
|
|
45
45
|
|
|
46
|
-
You wrap any element that you want to act as a focus trap with the `<FocusTrap>` component. `<FocusTrap>` expects exactly one child element which can be any HTML element or other React component that contains focusable elements.
|
|
46
|
+
You wrap any element that you want to act as a focus trap with the `<FocusTrap>` component. `<FocusTrap>` expects exactly one child element which can be any HTML element or other React component that contains focusable elements. __It cannot be a Fragment__ because `<FocusTrap>` needs to be able to get a reference to the underlying HTML element, and Fragments do not have any representation in the DOM.
|
|
47
47
|
|
|
48
48
|
For example:
|
|
49
49
|
|
|
@@ -69,11 +69,9 @@ Here's one more simple example:
|
|
|
69
69
|
```js
|
|
70
70
|
const React = require('react');
|
|
71
71
|
const ReactDOM = require('react-dom');
|
|
72
|
-
const FocusTrap = require('
|
|
72
|
+
const FocusTrap = require('focus-trap-react');
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class DemoOne extends React.Component {
|
|
74
|
+
class Demo extends React.Component {
|
|
77
75
|
constructor(props) {
|
|
78
76
|
super(props);
|
|
79
77
|
|
|
@@ -85,13 +83,13 @@ class DemoOne extends React.Component {
|
|
|
85
83
|
this.unmountTrap = this.unmountTrap.bind(this);
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
mountTrap() {
|
|
86
|
+
mountTrap = () => {
|
|
89
87
|
this.setState({ activeTrap: true });
|
|
90
|
-
}
|
|
88
|
+
};
|
|
91
89
|
|
|
92
|
-
unmountTrap() {
|
|
90
|
+
unmountTrap = () => {
|
|
93
91
|
this.setState({ activeTrap: false });
|
|
94
|
-
}
|
|
92
|
+
};
|
|
95
93
|
|
|
96
94
|
render() {
|
|
97
95
|
const trap = this.state.activeTrap
|
|
@@ -134,7 +132,7 @@ class DemoOne extends React.Component {
|
|
|
134
132
|
}
|
|
135
133
|
}
|
|
136
134
|
|
|
137
|
-
ReactDOM.render(<
|
|
135
|
+
ReactDOM.render(<Demo />, document.getElementById('root'));
|
|
138
136
|
```
|
|
139
137
|
|
|
140
138
|
### Props
|
|
@@ -188,20 +186,21 @@ In alphabetical order:
|
|
|
188
186
|
<td align="center"><a href="https://ofcr.se/"><img src="https://avatars1.githubusercontent.com/u/813865?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benjamin Tan</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=bnjmnt4n" title="Documentation">📖</a></td>
|
|
189
187
|
<td align="center"><a href="https://clintgoodman.com"><img src="https://avatars3.githubusercontent.com/u/5473697?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Clint Goodman</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=cgood92" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=cgood92" title="Documentation">📖</a> <a href="#example-cgood92" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=cgood92" title="Tests">⚠️</a></td>
|
|
190
188
|
<td align="center"><a href="https://github.com/DSil"><img src="https://avatars1.githubusercontent.com/u/6265045?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel</b></sub></a><br /><a href="#maintenance-DSil" title="Maintenance">🚧</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=DSil" title="Tests">⚠️</a></td>
|
|
189
|
+
<td align="center"><a href="https://github.com/Dan503"><img src="https://avatars.githubusercontent.com/u/10610368?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Tonon</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=Dan503" title="Documentation">📖</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=Dan503" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=Dan503" title="Tests">⚠️</a></td>
|
|
191
190
|
<td align="center"><a href="http://davidtheclark.com/"><img src="https://avatars2.githubusercontent.com/u/628431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Clark</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=davidtheclark" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Adavidtheclark" title="Bug reports">🐛</a> <a href="#infra-davidtheclark" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=davidtheclark" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=davidtheclark" title="Documentation">📖</a> <a href="#maintenance-davidtheclark" title="Maintenance">🚧</a></td>
|
|
192
191
|
<td align="center"><a href="https://github.com/features/security"><img src="https://avatars1.githubusercontent.com/u/27347476?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dependabot</b></sub></a><br /><a href="#maintenance-dependabot" title="Maintenance">🚧</a></td>
|
|
193
|
-
<td align="center"><a href="http://josuzuki.me"><img src="https://avatars1.githubusercontent.com/u/9583920?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Suzuki</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3AJoSuzuki" title="Bug reports">🐛</a></td>
|
|
194
192
|
</tr>
|
|
195
193
|
<tr>
|
|
194
|
+
<td align="center"><a href="http://josuzuki.me"><img src="https://avatars1.githubusercontent.com/u/9583920?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Suzuki</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3AJoSuzuki" title="Bug reports">🐛</a></td>
|
|
196
195
|
<td align="center"><a href="http://kathleenmcmahon.dev/"><img src="https://avatars1.githubusercontent.com/u/11621935?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kathleen McMahon</b></sub></a><br /><a href="#maintenance-resource11" title="Maintenance">🚧</a></td>
|
|
197
196
|
<td align="center"><a href="https://marais.io/"><img src="https://avatars2.githubusercontent.com/u/599459?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marais Rossouw</b></sub></a><br /><a href="#infra-maraisr" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
|
198
197
|
<td align="center"><a href="https://github.com/liunate"><img src="https://avatars2.githubusercontent.com/u/38996291?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nate Liu</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=liunate" title="Tests">⚠️</a></td>
|
|
199
198
|
<td align="center"><a href="https://www.linkedin.com/in/rivajunior/"><img src="https://avatars1.githubusercontent.com/u/11370172?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rivaldo Junior</b></sub></a><br /><a href="#maintenance-rivajunior" title="Maintenance">🚧</a></td>
|
|
200
199
|
<td align="center"><a href="https://scottrippey.github.io/"><img src="https://avatars3.githubusercontent.com/u/430608?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Scott Rippey</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=scottrippey" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Ascottrippey" title="Bug reports">🐛</a></td>
|
|
201
200
|
<td align="center"><a href="https://seanmcp.com/"><img src="https://avatars1.githubusercontent.com/u/6360367?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sean McPherson</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=SeanMcP" title="Code">💻</a></td>
|
|
202
|
-
<td align="center"><a href="https://recollectr.io"><img src="https://avatars2.githubusercontent.com/u/6835891?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Slapbox</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=Slapbox" title="Documentation">📖</a></td>
|
|
203
201
|
</tr>
|
|
204
202
|
<tr>
|
|
203
|
+
<td align="center"><a href="https://recollectr.io"><img src="https://avatars2.githubusercontent.com/u/6835891?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Slapbox</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=Slapbox" title="Documentation">📖</a></td>
|
|
205
204
|
<td align="center"><a href="https://stefancameron.com/"><img src="https://avatars3.githubusercontent.com/u/2855350?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Cameron</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=stefcameron" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Astefcameron" title="Bug reports">🐛</a> <a href="#infra-stefcameron" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=stefcameron" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=stefcameron" title="Documentation">📖</a> <a href="#maintenance-stefcameron" title="Maintenance">🚧</a></td>
|
|
206
205
|
<td align="center"><a href="http://tylerhawkins.info/201R/"><img src="https://avatars0.githubusercontent.com/u/13806458?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tyler Hawkins</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=thawkin3" title="Documentation">📖</a> <a href="#example-thawkin3" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=thawkin3" title="Tests">⚠️</a> <a href="#tool-thawkin3" title="Tools">🔧</a></td>
|
|
207
206
|
<td align="center"><a href="https://github.com/krikienoid"><img src="https://avatars3.githubusercontent.com/u/8528227?v=4?s=100" width="100px;" alt=""/><br /><sub><b>krikienoid</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Akrikienoid" title="Bug reports">🐛</a></td>
|
package/dist/focus-trap-react.js
CHANGED
|
@@ -18,7 +18,7 @@ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) ===
|
|
|
18
18
|
|
|
19
19
|
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
|
20
20
|
|
|
21
|
-
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try {
|
|
21
|
+
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
|
22
22
|
|
|
23
23
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
|
24
24
|
|
|
@@ -70,6 +70,11 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
70
70
|
continue;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
if (optionName === 'onPostDeactivate') {
|
|
74
|
+
_this.onPostDeactivate = focusTrapOptions[optionName];
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
73
78
|
_this.tailoredFocusTrapOptions[optionName] = focusTrapOptions[optionName];
|
|
74
79
|
} // elements from which to create the focus trap on mount; if a child is used
|
|
75
80
|
// instead of the `containerElements` prop, we'll get the child's related
|
|
@@ -81,24 +86,87 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
81
86
|
_this.updatePreviousElement();
|
|
82
87
|
|
|
83
88
|
return _this;
|
|
84
|
-
}
|
|
85
|
-
/** Update the previously focused element with the currently focused element. */
|
|
89
|
+
} // TODO: Need more test coverage for this function
|
|
86
90
|
|
|
87
91
|
|
|
88
92
|
_createClass(FocusTrap, [{
|
|
93
|
+
key: "getNodeForOption",
|
|
94
|
+
value: function getNodeForOption(optionName) {
|
|
95
|
+
var optionValue = this.tailoredFocusTrapOptions[optionName];
|
|
96
|
+
|
|
97
|
+
if (!optionValue) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
var node = optionValue;
|
|
102
|
+
|
|
103
|
+
if (typeof optionValue === 'string') {
|
|
104
|
+
node = document.querySelector(optionValue);
|
|
105
|
+
|
|
106
|
+
if (!node) {
|
|
107
|
+
throw new Error("`".concat(optionName, "` refers to no known node"));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (typeof optionValue === 'function') {
|
|
112
|
+
node = optionValue();
|
|
113
|
+
|
|
114
|
+
if (!node) {
|
|
115
|
+
throw new Error("`".concat(optionName, "` did not return a node"));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return node;
|
|
120
|
+
}
|
|
121
|
+
}, {
|
|
122
|
+
key: "getReturnFocusNode",
|
|
123
|
+
value: function getReturnFocusNode() {
|
|
124
|
+
var node = this.getNodeForOption('setReturnFocus');
|
|
125
|
+
return node ? node : this.previouslyFocusedElement;
|
|
126
|
+
}
|
|
127
|
+
/** Update the previously focused element with the currently focused element. */
|
|
128
|
+
|
|
129
|
+
}, {
|
|
89
130
|
key: "updatePreviousElement",
|
|
90
131
|
value: function updatePreviousElement() {
|
|
91
132
|
if (typeof document !== 'undefined') {
|
|
92
133
|
this.previouslyFocusedElement = document.activeElement;
|
|
93
134
|
}
|
|
94
135
|
}
|
|
95
|
-
/** Returns focus to the element that had focus when the trap was activated. */
|
|
96
|
-
|
|
97
136
|
}, {
|
|
98
|
-
key: "
|
|
99
|
-
value: function
|
|
100
|
-
|
|
101
|
-
|
|
137
|
+
key: "deactivateTrap",
|
|
138
|
+
value: function deactivateTrap() {
|
|
139
|
+
var _this2 = this;
|
|
140
|
+
|
|
141
|
+
var checkCanReturnFocus = this.tailoredFocusTrapOptions.checkCanReturnFocus;
|
|
142
|
+
|
|
143
|
+
if (this.focusTrap) {
|
|
144
|
+
// NOTE: we never let the trap return the focus since we do that ourselves
|
|
145
|
+
this.focusTrap.deactivate({
|
|
146
|
+
returnFocus: false
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
var finishDeactivation = function finishDeactivation() {
|
|
151
|
+
var returnFocusNode = _this2.getReturnFocusNode();
|
|
152
|
+
|
|
153
|
+
var canReturnFocus = (returnFocusNode === null || returnFocusNode === void 0 ? void 0 : returnFocusNode.focus) && _this2.returnFocusOnDeactivate;
|
|
154
|
+
|
|
155
|
+
if (canReturnFocus) {
|
|
156
|
+
/** Returns focus to the element that had focus when the trap was activated. */
|
|
157
|
+
returnFocusNode.focus();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (_this2.onPostDeactivate) {
|
|
161
|
+
_this2.onPostDeactivate.call(null); // don't call it in context of "this"
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (checkCanReturnFocus) {
|
|
167
|
+
checkCanReturnFocus(this.getReturnFocusNode()).then(finishDeactivation, finishDeactivation);
|
|
168
|
+
} else {
|
|
169
|
+
finishDeactivation();
|
|
102
170
|
}
|
|
103
171
|
}
|
|
104
172
|
}, {
|
|
@@ -138,28 +206,27 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
138
206
|
this.focusTrap.updateContainerElements(this.props.containerElements);
|
|
139
207
|
}
|
|
140
208
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
});
|
|
209
|
+
var hasActivated = !prevProps.active && this.props.active;
|
|
210
|
+
var hasDeactivated = prevProps.active && !this.props.active;
|
|
211
|
+
var hasPaused = !prevProps.paused && this.props.paused;
|
|
212
|
+
var hasUnpaused = prevProps.paused && !this.props.paused;
|
|
146
213
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
214
|
+
if (hasActivated) {
|
|
215
|
+
this.updatePreviousElement();
|
|
216
|
+
this.focusTrap.activate();
|
|
217
|
+
}
|
|
150
218
|
|
|
219
|
+
if (hasDeactivated) {
|
|
220
|
+
this.deactivateTrap();
|
|
151
221
|
return; // un/pause does nothing on an inactive trap
|
|
152
222
|
}
|
|
153
223
|
|
|
154
|
-
if (
|
|
155
|
-
this.
|
|
156
|
-
this.focusTrap.activate();
|
|
224
|
+
if (hasPaused) {
|
|
225
|
+
this.focusTrap.pause();
|
|
157
226
|
}
|
|
158
227
|
|
|
159
|
-
if (
|
|
228
|
+
if (hasUnpaused) {
|
|
160
229
|
this.focusTrap.unpause();
|
|
161
|
-
} else if (!prevProps.paused && this.props.paused) {
|
|
162
|
-
this.focusTrap.pause();
|
|
163
230
|
}
|
|
164
231
|
} else if (prevProps.containerElements !== this.props.containerElements) {
|
|
165
232
|
this.focusTrapElements = this.props.containerElements;
|
|
@@ -169,27 +236,22 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
169
236
|
}, {
|
|
170
237
|
key: "componentWillUnmount",
|
|
171
238
|
value: function componentWillUnmount() {
|
|
172
|
-
|
|
173
|
-
// NOTE: we never let the trap return the focus since we do that ourselves
|
|
174
|
-
this.focusTrap.deactivate({
|
|
175
|
-
returnFocus: false
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (this.returnFocusOnDeactivate) {
|
|
180
|
-
this.returnFocus();
|
|
181
|
-
}
|
|
239
|
+
this.deactivateTrap();
|
|
182
240
|
}
|
|
183
241
|
}, {
|
|
184
242
|
key: "render",
|
|
185
243
|
value: function render() {
|
|
186
|
-
var
|
|
244
|
+
var _this3 = this;
|
|
187
245
|
|
|
188
246
|
var child = this.props.children ? React.Children.only(this.props.children) : undefined;
|
|
189
247
|
|
|
190
248
|
if (child) {
|
|
249
|
+
if (child.type && child.type === React.Fragment) {
|
|
250
|
+
throw new Error('A focus-trap cannot use a Fragment as its child container. Try replacing it with a <div> element.');
|
|
251
|
+
}
|
|
252
|
+
|
|
191
253
|
var composedRefCallback = function composedRefCallback(element) {
|
|
192
|
-
var containerElements =
|
|
254
|
+
var containerElements = _this3.props.containerElements;
|
|
193
255
|
|
|
194
256
|
if (child) {
|
|
195
257
|
if (typeof child.ref === 'function') {
|
|
@@ -199,7 +261,7 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
199
261
|
}
|
|
200
262
|
}
|
|
201
263
|
|
|
202
|
-
|
|
264
|
+
_this3.focusTrapElements = containerElements ? containerElements : [element];
|
|
203
265
|
};
|
|
204
266
|
|
|
205
267
|
var childWithRef = React.cloneElement(child, {
|
|
@@ -222,7 +284,11 @@ FocusTrap.propTypes = {
|
|
|
222
284
|
paused: PropTypes.bool,
|
|
223
285
|
focusTrapOptions: PropTypes.shape({
|
|
224
286
|
onActivate: PropTypes.func,
|
|
287
|
+
onPostActivate: PropTypes.func,
|
|
288
|
+
checkCanFocusTrap: PropTypes.func,
|
|
225
289
|
onDeactivate: PropTypes.func,
|
|
290
|
+
onPostDeactivate: PropTypes.func,
|
|
291
|
+
checkCanReturnFocus: PropTypes.func,
|
|
226
292
|
initialFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]),
|
|
227
293
|
fallbackFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]),
|
|
228
294
|
escapeDeactivates: PropTypes.bool,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "focus-trap-react",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.0",
|
|
4
4
|
"description": "A React component that traps focus.",
|
|
5
5
|
"main": "dist/focus-trap-react.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
"test:types": "tsc index.d.ts",
|
|
25
25
|
"test:unit": "jest",
|
|
26
26
|
"test:coverage": "jest --coverage",
|
|
27
|
-
"test:coverage:ci": "yarn test:coverage && codecov",
|
|
28
27
|
"test:cypress": "start-server-and-test start 9966 'cypress open'",
|
|
29
28
|
"test:cypress:ci": "start-server-and-test start 9966 'cypress run --browser $CYPRESS_BROWSER --headless'",
|
|
30
29
|
"test": "yarn format:check && yarn lint && yarn test:unit && yarn test:types && CYPRESS_BROWSER=chrome yarn test:cypress:ci",
|
|
@@ -56,44 +55,43 @@
|
|
|
56
55
|
},
|
|
57
56
|
"homepage": "https://github.com/focus-trap/focus-trap-react#readme",
|
|
58
57
|
"devDependencies": {
|
|
59
|
-
"@babel/cli": "^7.
|
|
60
|
-
"@babel/core": "^7.
|
|
61
|
-
"@babel/plugin-proposal-class-properties": "^7.
|
|
62
|
-
"@babel/preset-env": "^7.
|
|
63
|
-
"@babel/preset-react": "^7.
|
|
64
|
-
"@changesets/cli": "^2.
|
|
65
|
-
"@testing-library/cypress": "^7.0.
|
|
66
|
-
"@testing-library/dom": "^7.
|
|
67
|
-
"@testing-library/jest-dom": "^5.
|
|
68
|
-
"@testing-library/react": "^11.2.
|
|
69
|
-
"@testing-library/user-event": "^
|
|
58
|
+
"@babel/cli": "^7.14.5",
|
|
59
|
+
"@babel/core": "^7.14.6",
|
|
60
|
+
"@babel/plugin-proposal-class-properties": "^7.12.13",
|
|
61
|
+
"@babel/preset-env": "^7.14.5",
|
|
62
|
+
"@babel/preset-react": "^7.14.5",
|
|
63
|
+
"@changesets/cli": "^2.16.0",
|
|
64
|
+
"@testing-library/cypress": "^7.0.6",
|
|
65
|
+
"@testing-library/dom": "^7.31.2",
|
|
66
|
+
"@testing-library/jest-dom": "^5.14.1",
|
|
67
|
+
"@testing-library/react": "^11.2.7",
|
|
68
|
+
"@testing-library/user-event": "^13.1.9",
|
|
70
69
|
"@types/jquery": "^3.5.5",
|
|
71
|
-
"all-contributors-cli": "^6.
|
|
70
|
+
"all-contributors-cli": "^6.20.0",
|
|
72
71
|
"babel-eslint": "^10.1.0",
|
|
73
|
-
"babel-jest": "^
|
|
72
|
+
"babel-jest": "^27.0.2",
|
|
74
73
|
"babelify": "^10.0.0",
|
|
75
74
|
"browserify": "^17.0.0",
|
|
76
75
|
"budo": "^11.6.4",
|
|
77
|
-
"
|
|
78
|
-
"cypress": "^6.2.1",
|
|
76
|
+
"cypress": "^7.5.0",
|
|
79
77
|
"cypress-plugin-tab": "^1.0.5",
|
|
80
|
-
"eslint": "^7.
|
|
81
|
-
"eslint-config-prettier": "^
|
|
82
|
-
"eslint-plugin-cypress": "^2.11.
|
|
83
|
-
"eslint-plugin-react": "^7.
|
|
84
|
-
"jest": "^
|
|
85
|
-
"jest-watch-typeahead": "^0.6.
|
|
78
|
+
"eslint": "^7.28.0",
|
|
79
|
+
"eslint-config-prettier": "^8.3.0",
|
|
80
|
+
"eslint-plugin-cypress": "^2.11.3",
|
|
81
|
+
"eslint-plugin-react": "^7.24.0",
|
|
82
|
+
"jest": "^27.0.4",
|
|
83
|
+
"jest-watch-typeahead": "^0.6.4",
|
|
86
84
|
"onchange": "^7.1.0",
|
|
87
|
-
"prettier": "^2.
|
|
85
|
+
"prettier": "^2.3.1",
|
|
88
86
|
"prop-types": "^15.7.2",
|
|
89
|
-
"react": "^17.0.
|
|
90
|
-
"react-dom": "^17.0.
|
|
87
|
+
"react": "^17.0.2",
|
|
88
|
+
"react-dom": "^17.0.2",
|
|
91
89
|
"regenerator-runtime": "^0.13.7",
|
|
92
|
-
"start-server-and-test": "^1.
|
|
93
|
-
"typescript": "^4.
|
|
90
|
+
"start-server-and-test": "^1.12.5",
|
|
91
|
+
"typescript": "^4.3.2"
|
|
94
92
|
},
|
|
95
93
|
"dependencies": {
|
|
96
|
-
"focus-trap": "^6.
|
|
94
|
+
"focus-trap": "^6.5.1"
|
|
97
95
|
},
|
|
98
96
|
"peerDependencies": {
|
|
99
97
|
"prop-types": "^15.7.2",
|
package/src/focus-trap-react.js
CHANGED
|
@@ -36,6 +36,11 @@ class FocusTrap extends React.Component {
|
|
|
36
36
|
continue;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
if (optionName === 'onPostDeactivate') {
|
|
40
|
+
this.onPostDeactivate = focusTrapOptions[optionName];
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
this.tailoredFocusTrapOptions[optionName] = focusTrapOptions[optionName];
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -48,6 +53,38 @@ class FocusTrap extends React.Component {
|
|
|
48
53
|
this.updatePreviousElement();
|
|
49
54
|
}
|
|
50
55
|
|
|
56
|
+
// TODO: Need more test coverage for this function
|
|
57
|
+
getNodeForOption(optionName) {
|
|
58
|
+
const optionValue = this.tailoredFocusTrapOptions[optionName];
|
|
59
|
+
if (!optionValue) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let node = optionValue;
|
|
64
|
+
|
|
65
|
+
if (typeof optionValue === 'string') {
|
|
66
|
+
node = document.querySelector(optionValue);
|
|
67
|
+
if (!node) {
|
|
68
|
+
throw new Error(`\`${optionName}\` refers to no known node`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof optionValue === 'function') {
|
|
73
|
+
node = optionValue();
|
|
74
|
+
if (!node) {
|
|
75
|
+
throw new Error(`\`${optionName}\` did not return a node`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return node;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getReturnFocusNode() {
|
|
83
|
+
const node = this.getNodeForOption('setReturnFocus');
|
|
84
|
+
|
|
85
|
+
return node ? node : this.previouslyFocusedElement;
|
|
86
|
+
}
|
|
87
|
+
|
|
51
88
|
/** Update the previously focused element with the currently focused element. */
|
|
52
89
|
updatePreviousElement() {
|
|
53
90
|
if (typeof document !== 'undefined') {
|
|
@@ -55,10 +92,36 @@ class FocusTrap extends React.Component {
|
|
|
55
92
|
}
|
|
56
93
|
}
|
|
57
94
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
95
|
+
deactivateTrap() {
|
|
96
|
+
const { checkCanReturnFocus } = this.tailoredFocusTrapOptions;
|
|
97
|
+
|
|
98
|
+
if (this.focusTrap) {
|
|
99
|
+
// NOTE: we never let the trap return the focus since we do that ourselves
|
|
100
|
+
this.focusTrap.deactivate({ returnFocus: false });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const finishDeactivation = () => {
|
|
104
|
+
const returnFocusNode = this.getReturnFocusNode();
|
|
105
|
+
const canReturnFocus =
|
|
106
|
+
returnFocusNode?.focus && this.returnFocusOnDeactivate;
|
|
107
|
+
|
|
108
|
+
if (canReturnFocus) {
|
|
109
|
+
/** Returns focus to the element that had focus when the trap was activated. */
|
|
110
|
+
returnFocusNode.focus();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (this.onPostDeactivate) {
|
|
114
|
+
this.onPostDeactivate.call(null); // don't call it in context of "this"
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (checkCanReturnFocus) {
|
|
119
|
+
checkCanReturnFocus(this.getReturnFocusNode()).then(
|
|
120
|
+
finishDeactivation,
|
|
121
|
+
finishDeactivation
|
|
122
|
+
);
|
|
123
|
+
} else {
|
|
124
|
+
finishDeactivation();
|
|
62
125
|
}
|
|
63
126
|
}
|
|
64
127
|
|
|
@@ -100,25 +163,28 @@ class FocusTrap extends React.Component {
|
|
|
100
163
|
this.focusTrap.updateContainerElements(this.props.containerElements);
|
|
101
164
|
}
|
|
102
165
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
this.returnFocus();
|
|
108
|
-
}
|
|
109
|
-
return; // un/pause does nothing on an inactive trap
|
|
110
|
-
}
|
|
166
|
+
const hasActivated = !prevProps.active && this.props.active;
|
|
167
|
+
const hasDeactivated = prevProps.active && !this.props.active;
|
|
168
|
+
const hasPaused = !prevProps.paused && this.props.paused;
|
|
169
|
+
const hasUnpaused = prevProps.paused && !this.props.paused;
|
|
111
170
|
|
|
112
|
-
if (
|
|
171
|
+
if (hasActivated) {
|
|
113
172
|
this.updatePreviousElement();
|
|
114
173
|
this.focusTrap.activate();
|
|
115
174
|
}
|
|
116
175
|
|
|
117
|
-
if (
|
|
118
|
-
this.
|
|
119
|
-
|
|
176
|
+
if (hasDeactivated) {
|
|
177
|
+
this.deactivateTrap();
|
|
178
|
+
return; // un/pause does nothing on an inactive trap
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (hasPaused) {
|
|
120
182
|
this.focusTrap.pause();
|
|
121
183
|
}
|
|
184
|
+
|
|
185
|
+
if (hasUnpaused) {
|
|
186
|
+
this.focusTrap.unpause();
|
|
187
|
+
}
|
|
122
188
|
} else if (prevProps.containerElements !== this.props.containerElements) {
|
|
123
189
|
this.focusTrapElements = this.props.containerElements;
|
|
124
190
|
this.setupFocusTrap();
|
|
@@ -126,14 +192,7 @@ class FocusTrap extends React.Component {
|
|
|
126
192
|
}
|
|
127
193
|
|
|
128
194
|
componentWillUnmount() {
|
|
129
|
-
|
|
130
|
-
// NOTE: we never let the trap return the focus since we do that ourselves
|
|
131
|
-
this.focusTrap.deactivate({ returnFocus: false });
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (this.returnFocusOnDeactivate) {
|
|
135
|
-
this.returnFocus();
|
|
136
|
-
}
|
|
195
|
+
this.deactivateTrap();
|
|
137
196
|
}
|
|
138
197
|
|
|
139
198
|
render() {
|
|
@@ -142,6 +201,12 @@ class FocusTrap extends React.Component {
|
|
|
142
201
|
: undefined;
|
|
143
202
|
|
|
144
203
|
if (child) {
|
|
204
|
+
if (child.type && child.type === React.Fragment) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
'A focus-trap cannot use a Fragment as its child container. Try replacing it with a <div> element.'
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
145
210
|
const composedRefCallback = (element) => {
|
|
146
211
|
const { containerElements } = this.props;
|
|
147
212
|
|
|
@@ -177,7 +242,11 @@ FocusTrap.propTypes = {
|
|
|
177
242
|
paused: PropTypes.bool,
|
|
178
243
|
focusTrapOptions: PropTypes.shape({
|
|
179
244
|
onActivate: PropTypes.func,
|
|
245
|
+
onPostActivate: PropTypes.func,
|
|
246
|
+
checkCanFocusTrap: PropTypes.func,
|
|
180
247
|
onDeactivate: PropTypes.func,
|
|
248
|
+
onPostDeactivate: PropTypes.func,
|
|
249
|
+
checkCanReturnFocus: PropTypes.func,
|
|
181
250
|
initialFocus: PropTypes.oneOfType([
|
|
182
251
|
PropTypes.instanceOf(ElementType),
|
|
183
252
|
PropTypes.string,
|