focus-trap-react 8.5.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 +12 -0
- package/README.md +4 -3
- package/dist/focus-trap-react.js +97 -35
- package/package.json +13 -13
- package/src/focus-trap-react.js +87 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
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
|
+
|
|
3
15
|
## 8.5.1
|
|
4
16
|
|
|
5
17
|
### 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.
|
|
@@ -186,20 +186,21 @@ In alphabetical order:
|
|
|
186
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>
|
|
187
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>
|
|
188
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>
|
|
189
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>
|
|
190
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>
|
|
191
|
-
<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>
|
|
192
192
|
</tr>
|
|
193
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>
|
|
194
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>
|
|
195
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>
|
|
196
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>
|
|
197
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>
|
|
198
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>
|
|
199
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>
|
|
200
|
-
<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>
|
|
201
201
|
</tr>
|
|
202
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>
|
|
203
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>
|
|
204
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>
|
|
205
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
|
@@ -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,21 +236,12 @@ 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
|
|
|
@@ -193,7 +251,7 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
193
251
|
}
|
|
194
252
|
|
|
195
253
|
var composedRefCallback = function composedRefCallback(element) {
|
|
196
|
-
var containerElements =
|
|
254
|
+
var containerElements = _this3.props.containerElements;
|
|
197
255
|
|
|
198
256
|
if (child) {
|
|
199
257
|
if (typeof child.ref === 'function') {
|
|
@@ -203,7 +261,7 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
203
261
|
}
|
|
204
262
|
}
|
|
205
263
|
|
|
206
|
-
|
|
264
|
+
_this3.focusTrapElements = containerElements ? containerElements : [element];
|
|
207
265
|
};
|
|
208
266
|
|
|
209
267
|
var childWithRef = React.cloneElement(child, {
|
|
@@ -226,7 +284,11 @@ FocusTrap.propTypes = {
|
|
|
226
284
|
paused: PropTypes.bool,
|
|
227
285
|
focusTrapOptions: PropTypes.shape({
|
|
228
286
|
onActivate: PropTypes.func,
|
|
287
|
+
onPostActivate: PropTypes.func,
|
|
288
|
+
checkCanFocusTrap: PropTypes.func,
|
|
229
289
|
onDeactivate: PropTypes.func,
|
|
290
|
+
onPostDeactivate: PropTypes.func,
|
|
291
|
+
checkCanReturnFocus: PropTypes.func,
|
|
230
292
|
initialFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]),
|
|
231
293
|
fallbackFocus: PropTypes.oneOfType([PropTypes.instanceOf(ElementType), PropTypes.string, PropTypes.func]),
|
|
232
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",
|
|
@@ -55,15 +55,15 @@
|
|
|
55
55
|
},
|
|
56
56
|
"homepage": "https://github.com/focus-trap/focus-trap-react#readme",
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@babel/cli": "^7.14.
|
|
59
|
-
"@babel/core": "^7.14.
|
|
58
|
+
"@babel/cli": "^7.14.5",
|
|
59
|
+
"@babel/core": "^7.14.6",
|
|
60
60
|
"@babel/plugin-proposal-class-properties": "^7.12.13",
|
|
61
|
-
"@babel/preset-env": "^7.14.
|
|
62
|
-
"@babel/preset-react": "^7.
|
|
61
|
+
"@babel/preset-env": "^7.14.5",
|
|
62
|
+
"@babel/preset-react": "^7.14.5",
|
|
63
63
|
"@changesets/cli": "^2.16.0",
|
|
64
64
|
"@testing-library/cypress": "^7.0.6",
|
|
65
|
-
"@testing-library/dom": "^7.31.
|
|
66
|
-
"@testing-library/jest-dom": "^5.
|
|
65
|
+
"@testing-library/dom": "^7.31.2",
|
|
66
|
+
"@testing-library/jest-dom": "^5.14.1",
|
|
67
67
|
"@testing-library/react": "^11.2.7",
|
|
68
68
|
"@testing-library/user-event": "^13.1.9",
|
|
69
69
|
"@types/jquery": "^3.5.5",
|
|
@@ -73,25 +73,25 @@
|
|
|
73
73
|
"babelify": "^10.0.0",
|
|
74
74
|
"browserify": "^17.0.0",
|
|
75
75
|
"budo": "^11.6.4",
|
|
76
|
-
"cypress": "^7.
|
|
76
|
+
"cypress": "^7.5.0",
|
|
77
77
|
"cypress-plugin-tab": "^1.0.5",
|
|
78
|
-
"eslint": "^7.
|
|
78
|
+
"eslint": "^7.28.0",
|
|
79
79
|
"eslint-config-prettier": "^8.3.0",
|
|
80
80
|
"eslint-plugin-cypress": "^2.11.3",
|
|
81
81
|
"eslint-plugin-react": "^7.24.0",
|
|
82
|
-
"jest": "^
|
|
82
|
+
"jest": "^27.0.4",
|
|
83
83
|
"jest-watch-typeahead": "^0.6.4",
|
|
84
84
|
"onchange": "^7.1.0",
|
|
85
|
-
"prettier": "^2.3.
|
|
85
|
+
"prettier": "^2.3.1",
|
|
86
86
|
"prop-types": "^15.7.2",
|
|
87
87
|
"react": "^17.0.2",
|
|
88
88
|
"react-dom": "^17.0.2",
|
|
89
89
|
"regenerator-runtime": "^0.13.7",
|
|
90
|
-
"start-server-and-test": "^1.12.
|
|
90
|
+
"start-server-and-test": "^1.12.5",
|
|
91
91
|
"typescript": "^4.3.2"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"focus-trap": "^6.5.
|
|
94
|
+
"focus-trap": "^6.5.1"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
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() {
|
|
@@ -183,7 +242,11 @@ FocusTrap.propTypes = {
|
|
|
183
242
|
paused: PropTypes.bool,
|
|
184
243
|
focusTrapOptions: PropTypes.shape({
|
|
185
244
|
onActivate: PropTypes.func,
|
|
245
|
+
onPostActivate: PropTypes.func,
|
|
246
|
+
checkCanFocusTrap: PropTypes.func,
|
|
186
247
|
onDeactivate: PropTypes.func,
|
|
248
|
+
onPostDeactivate: PropTypes.func,
|
|
249
|
+
checkCanReturnFocus: PropTypes.func,
|
|
187
250
|
initialFocus: PropTypes.oneOfType([
|
|
188
251
|
PropTypes.instanceOf(ElementType),
|
|
189
252
|
PropTypes.string,
|