focus-trap-react 9.0.0 → 10.0.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 +27 -0
- package/README.md +112 -10
- package/dist/focus-trap-react.js +24 -2
- package/package.json +31 -30
- package/src/focus-trap-react.js +28 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 10.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- af69c14: 🚨 **Breaking:** Underlying `tabbable` dependency has been updated to v6.0.0 and contains a breaking change related to detached nodes with its default `displayCheck` setting. See tabbable's [changelog](https://github.com/focus-trap/tabbable/blob/master/CHANGELOG.md#600) for more information.
|
|
8
|
+
- The `focus-trap` dependency has also be updated to v7.0.0 but only contains the underlying `tabbable` changes.
|
|
9
|
+
- The `tabbableOptions.displayCheck` prop type has been updated to include the new "legacy-full" option.
|
|
10
|
+
- 018732c: 🚨 **Breaking:** Dropped support of IE browsers, all versions.
|
|
11
|
+
- IE11 was [officially retired](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/) on June 15, 2022 (6 weeks ago). There are no longer any versions of IE that are still maintained or even supported by Microsoft.
|
|
12
|
+
- 018732c: Revised and clarified official browser support (still as broad and deep as _reasonably_ possible).
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- b0bbbd4: Update README with a note about the `children` prop stating that the trap requires a single child, and that if a component is used, it must be a **functional** component that forwards refs.
|
|
17
|
+
|
|
18
|
+
## 9.0.2
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- 4d8e041: Fix an issue when running in strict mode which has React immediately unmount/remount the trap, causing it to deactivate and then have to reactivate (per existing component state) on the remount. [#720](https://github.com/focus-trap/focus-trap-react/issues/720)
|
|
23
|
+
|
|
24
|
+
## 9.0.1
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- 2d6cd9b: Add explicit dependency on tabbable since the source directly requires it.
|
|
29
|
+
|
|
3
30
|
## 9.0.0
|
|
4
31
|
|
|
5
32
|
### Major 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.
|
|
@@ -33,13 +33,21 @@ npm install focus-trap-react
|
|
|
33
33
|
|
|
34
34
|
### React dependency
|
|
35
35
|
|
|
36
|
-
React `>= 16.
|
|
36
|
+
React `>= 16.3.0`
|
|
37
37
|
|
|
38
38
|
## Browser Support
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
As old and as broad as _reasonably_ possible, excluding browsers that are out of support or have nearly no user base.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Focused on desktop browsers, particularly Chrome, Edge, FireFox, Safari, and Opera.
|
|
43
|
+
|
|
44
|
+
Focus-trap-react is not officially tested on any mobile browsers or devices.
|
|
45
|
+
|
|
46
|
+
> ⚠️ Microsoft [no longer supports](https://blogs.windows.com/windowsexperience/2022/06/15/internet-explorer-11-has-retired-and-is-officially-out-of-support-what-you-need-to-know/) any version of IE, so IE is no longer supported by this library.
|
|
47
|
+
|
|
48
|
+
> 💬 Focus-trap-react relies on focus-trap so its browser support is at least [what focus-trap supports](https://github.com/focus-trap/focus-trap#browser-support).
|
|
49
|
+
|
|
50
|
+
> 💬 Keep in mind that performance optimization and old browser support are often at odds, so tabbable may not always be able to use the most optimal (typically modern) APIs in all cases.
|
|
43
51
|
|
|
44
52
|
## Usage
|
|
45
53
|
|
|
@@ -66,7 +74,7 @@ You can read further code examples in `demo/` (it's very simple), and [see how i
|
|
|
66
74
|
|
|
67
75
|
Here's one more simple example:
|
|
68
76
|
|
|
69
|
-
```
|
|
77
|
+
```jsx
|
|
70
78
|
const React = require('react');
|
|
71
79
|
const ReactDOM = require('react-dom'); // React 16-17
|
|
72
80
|
const { createRoot } = require('react-dom/client'); // React 18
|
|
@@ -139,6 +147,96 @@ createRoot(document.getElementById('root')).render(<Demo />); // React 18
|
|
|
139
147
|
|
|
140
148
|
### Props
|
|
141
149
|
|
|
150
|
+
#### children
|
|
151
|
+
|
|
152
|
+
> ⚠️ The `<FocusTrap>` component requires a __single__ child, and this child must __forward refs__ onto the element which will ultimately be considered the trap's container. Since React does not provide for a way to forward refs to class-based components, this means the child must be a __functional__ component that uses the `React.forwardRef()` API.
|
|
153
|
+
>
|
|
154
|
+
> If you must use a __class__-based component as the trap's container, then you will need to get your own ref to it upon render, and use the `containerElements` prop (initially set to an empty array `[]`) in order to provide the ref's element to it once updated by React (hint: use a [callback ref](https://reactjs.org/docs/refs-and-the-dom.html#callback-refs)).
|
|
155
|
+
|
|
156
|
+
> 💬 The child is ignored (but still rendered) if the `containerElements` prop is used to imperatively provide trap container elements.
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
|
|
160
|
+
```jsx
|
|
161
|
+
const React = require('react');
|
|
162
|
+
const { createRoot } = require('react-dom/client');
|
|
163
|
+
const propTypes = require('prop-types');
|
|
164
|
+
const FocusTrap = require('../../dist/focus-trap-react');
|
|
165
|
+
|
|
166
|
+
const container = document.getElementById('demo-function-child');
|
|
167
|
+
|
|
168
|
+
const TrapChild = React.forwardRef(function ({ onDeactivate }, ref) {
|
|
169
|
+
return (
|
|
170
|
+
<div ref={ref}>
|
|
171
|
+
<p>
|
|
172
|
+
Here is a focus trap <a href="#">with</a> <a href="#">some</a>{' '}
|
|
173
|
+
<a href="#">focusable</a> parts.
|
|
174
|
+
</p>
|
|
175
|
+
<p>
|
|
176
|
+
<button
|
|
177
|
+
onClick={onDeactivate}
|
|
178
|
+
aria-describedby="class-child-heading"
|
|
179
|
+
>
|
|
180
|
+
deactivate trap
|
|
181
|
+
</button>
|
|
182
|
+
</p>
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
TrapChild.displayName = 'TrapChild';
|
|
188
|
+
TrapChild.propTypes = {
|
|
189
|
+
onDeactivate: propTypes.func,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
class DemoFunctionChild extends React.Component {
|
|
193
|
+
constructor(props) {
|
|
194
|
+
super(props);
|
|
195
|
+
|
|
196
|
+
this.state = {
|
|
197
|
+
activeTrap: false,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
this.mountTrap = this.mountTrap.bind(this);
|
|
201
|
+
this.unmountTrap = this.unmountTrap.bind(this);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
mountTrap() {
|
|
205
|
+
this.setState({ activeTrap: true });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
unmountTrap() {
|
|
209
|
+
this.setState({ activeTrap: false });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
render() {
|
|
213
|
+
const trap = this.state.activeTrap && (
|
|
214
|
+
<FocusTrap
|
|
215
|
+
focusTrapOptions={{
|
|
216
|
+
onDeactivate: this.unmountTrap,
|
|
217
|
+
}}
|
|
218
|
+
>
|
|
219
|
+
<TrapChild />
|
|
220
|
+
</FocusTrap>
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
return (
|
|
224
|
+
<div>
|
|
225
|
+
<p>
|
|
226
|
+
<button onClick={this.mountTrap} aria-describedby="function-child-heading">
|
|
227
|
+
activate trap
|
|
228
|
+
</button>
|
|
229
|
+
</p>
|
|
230
|
+
{trap}
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const root = createRoot(container);
|
|
237
|
+
root.render(<DemoFunctionChild />);
|
|
238
|
+
```
|
|
239
|
+
|
|
142
240
|
#### focusTrapOptions
|
|
143
241
|
|
|
144
242
|
Type: `Object`, optional
|
|
@@ -165,9 +263,11 @@ If you would like to pause or unpause the focus trap (see [`focus-trap`'s docume
|
|
|
165
263
|
|
|
166
264
|
Type: `Array of HTMLElement`, optional
|
|
167
265
|
|
|
168
|
-
If
|
|
266
|
+
If specified, these elements will be used as the boundaries for the focus-trap, __instead of the child__. These get passed as arguments to `focus-trap`'s `updateContainerElements()` method.
|
|
169
267
|
|
|
170
|
-
> Note that when you use `containerElements`, the need for a child is eliminated as the child is __always__ ignored when the prop is specified, even if
|
|
268
|
+
> 💬 Note that when you use `containerElements`, the need for a child is eliminated as the child is __always__ ignored (though still rendered) when the prop is specified, even if this prop is `[]` (an empty array).
|
|
269
|
+
>
|
|
270
|
+
> Also note that if the refs you're putting into the array, like `containerElements={[ref1.current, ref2.current]}`, aren't resolved yet, resulting in `[null, null]` for example, the trap will not get created. The array must contain at least one valid `HTMLElement` in order for the trap to get created/updated.
|
|
171
271
|
|
|
172
272
|
If `containerElements` is subsequently updated (i.e. after the trap has been created) to an empty array (or an array of falsy values like `[null, null]`), the trap will still be active, but the TAB key will do nothing because the trap will not contain any tabbable groups of nodes. At this point, the trap can either be deactivated manually or by unmounting, or an updated set of elements can be given to `containerElements` to resume use of the TAB key.
|
|
173
273
|
|
|
@@ -214,19 +314,21 @@ In alphabetical order:
|
|
|
214
314
|
<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>
|
|
215
315
|
<td align="center"><a href="https://github.com/LoganDark"><img src="https://avatars.githubusercontent.com/u/4723091?v=4?s=100" width="100px;" alt=""/><br /><sub><b>LoganDark</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3ALoganDark" title="Bug reports">🐛</a></td>
|
|
216
316
|
<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>
|
|
317
|
+
<td align="center"><a href="https://www.moroshko.me"><img src="https://avatars.githubusercontent.com/u/259753?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Misha Moroshko</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Amoroshko" title="Bug reports">🐛</a></td>
|
|
217
318
|
<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>
|
|
218
|
-
<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>
|
|
219
319
|
</tr>
|
|
220
320
|
<tr>
|
|
321
|
+
<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>
|
|
221
322
|
<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>
|
|
222
323
|
<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>
|
|
324
|
+
<td align="center"><a href="http://smoores.dev"><img src="https://avatars.githubusercontent.com/u/5354254?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shane Moore</b></sub></a><br /><a href="#platform-SMores" title="Packaging/porting to new platform">📦</a></td>
|
|
223
325
|
<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> <a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3ASlapbox" title="Bug reports">🐛</a></td>
|
|
224
326
|
<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>
|
|
225
327
|
<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>
|
|
226
|
-
<td align="center"><a href="https://github.com/wandroll"><img src="https://avatars.githubusercontent.com/u/4492317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wandrille Verlut</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=wandroll" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=wandroll" title="Tests">⚠️</a></td>
|
|
227
|
-
<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>
|
|
228
328
|
</tr>
|
|
229
329
|
<tr>
|
|
330
|
+
<td align="center"><a href="https://github.com/wandroll"><img src="https://avatars.githubusercontent.com/u/4492317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wandrille Verlut</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/commits?author=wandroll" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap-react/commits?author=wandroll" title="Tests">⚠️</a></td>
|
|
331
|
+
<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>
|
|
230
332
|
<td align="center"><a href="https://github.com/syntactic-salt"><img src="https://avatars.githubusercontent.com/u/9385662?v=4?s=100" width="100px;" alt=""/><br /><sub><b>syntactic-salt</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap-react/issues?q=author%3Asyntactic-salt" title="Bug reports">🐛</a></td>
|
|
231
333
|
</tr>
|
|
232
334
|
</table>
|
package/dist/focus-trap-react.js
CHANGED
|
@@ -299,7 +299,28 @@ var FocusTrap = /*#__PURE__*/function (_React$Component) {
|
|
|
299
299
|
}, {
|
|
300
300
|
key: "setupFocusTrap",
|
|
301
301
|
value: function setupFocusTrap() {
|
|
302
|
-
if (
|
|
302
|
+
if (this.focusTrap) {
|
|
303
|
+
// trap already exists: it's possible we're in StrictMode and we're being remounted,
|
|
304
|
+
// in which case, we will have deactivated the trap when we got unmounted (remember,
|
|
305
|
+
// StrictMode, in development, purposely unmounts and remounts components after
|
|
306
|
+
// mounting them the first time to make sure they have reusable state,
|
|
307
|
+
// @see https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state) so now
|
|
308
|
+
// we need to restore the state of the trap according to our component state
|
|
309
|
+
// NOTE: Strict mode __violates__ assumptions about the `componentWillUnmount()` API
|
|
310
|
+
// which clearly states -- even for React 18 -- that, "Once a component instance is
|
|
311
|
+
// unmounted, __it will never be mounted again.__" (emphasis ours). So when we get
|
|
312
|
+
// unmounted, we assume we're gone forever and we deactivate the trap. But then
|
|
313
|
+
// we get remounted and we're supposed to restore state. But if you had paused,
|
|
314
|
+
// we've now deactivated (we don't know we're amount to get remounted again)
|
|
315
|
+
// which means we need to reactivate and then pause. Otherwise, do nothing.
|
|
316
|
+
if (this.props.active && !this.focusTrap.active) {
|
|
317
|
+
this.focusTrap.activate();
|
|
318
|
+
|
|
319
|
+
if (this.props.paused) {
|
|
320
|
+
this.focusTrap.pause();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
303
324
|
var nodesExist = this.focusTrapElements.some(Boolean);
|
|
304
325
|
|
|
305
326
|
if (nodesExist) {
|
|
@@ -443,11 +464,12 @@ FocusTrap.propTypes = {
|
|
|
443
464
|
allowOutsideClick: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
|
444
465
|
preventScroll: PropTypes.bool,
|
|
445
466
|
tabbableOptions: PropTypes.shape({
|
|
446
|
-
displayCheck: PropTypes.oneOf(['full', 'non-zero-area', 'none']),
|
|
467
|
+
displayCheck: PropTypes.oneOf(['full', 'legacy-full', 'non-zero-area', 'none']),
|
|
447
468
|
getShadowRoot: PropTypes.oneOfType([PropTypes.bool, PropTypes.func])
|
|
448
469
|
})
|
|
449
470
|
}),
|
|
450
471
|
containerElements: PropTypes.arrayOf(PropTypes.instanceOf(ElementType)),
|
|
472
|
+
// DOM element ONLY
|
|
451
473
|
children: PropTypes.oneOfType([PropTypes.element, // React element
|
|
452
474
|
PropTypes.instanceOf(ElementType) // DOM element
|
|
453
475
|
]) // NOTE: _createFocusTrap is internal, for testing purposes only, so we don't
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "focus-trap-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"description": "A React component that traps focus.",
|
|
5
5
|
"main": "dist/focus-trap-react.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"index.d.ts"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"demo-bundle": "browserify demo/js -t babelify --extension=.jsx -o demo/demo-bundle.js",
|
|
18
|
-
"start": "yarn build && budo demo/js/index.js:demo-bundle.js --dir demo --live -- -t babelify --extension=.jsx",
|
|
17
|
+
"demo-bundle": "NODE_ENV=production browserify demo/js -t babelify --extension=.jsx -o demo/demo-bundle.js",
|
|
18
|
+
"start": "yarn build && NODE_ENV=development budo demo/js/index.js:demo-bundle.js --dir demo --live -- -t babelify --extension=.jsx",
|
|
19
19
|
"lint": "eslint \"*.js\" \"src/**/*.js\" \"test/**/*.js\" \"demo/**/*.js\" \"cypress/**/*.js\"",
|
|
20
20
|
"format": "prettier --write \"{*,src/**/*,test/**/*,demo/js/**/*,.github/workflows/*,cypress/**/*}.+(js|yml)\"",
|
|
21
21
|
"format:check": "prettier --check \"{*,src/**/*,test/**/*,demo/js/**/*,.github/workflows/*,cypress/**/*}.+(js|yml)\"",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"test:types": "tsc index.d.ts",
|
|
25
25
|
"test:unit": "jest",
|
|
26
26
|
"test:coverage": "jest --coverage",
|
|
27
|
-
"test:
|
|
28
|
-
"test:
|
|
29
|
-
"test:
|
|
30
|
-
"test": "yarn format:check && yarn lint && yarn test:unit && yarn test:types &&
|
|
27
|
+
"test:e2e": "ELECTRON_ENABLE_LOGGING=1 start-server-and-test start 9966 'cypress run --browser $CYPRESS_BROWSER --headless'",
|
|
28
|
+
"test:e2e:chrome": "CYPRESS_BROWSER=chrome yarn test:e2e",
|
|
29
|
+
"test:e2e:dev": "ELECTRON_ENABLE_LOGGING=1 start-server-and-test start 9966 'cypress open --browser --e2e'",
|
|
30
|
+
"test": "yarn format:check && yarn lint && yarn test:unit && yarn test:types && yarn test:e2e:chrome",
|
|
31
31
|
"prepare": "yarn build",
|
|
32
32
|
"prepublishOnly": "yarn test && yarn build",
|
|
33
33
|
"release": "yarn build && changeset publish"
|
|
@@ -57,45 +57,46 @@
|
|
|
57
57
|
},
|
|
58
58
|
"homepage": "https://github.com/focus-trap/focus-trap-react#readme",
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@babel/cli": "^7.
|
|
61
|
-
"@babel/core": "^7.18.
|
|
62
|
-
"@babel/eslint-parser": "^7.18.
|
|
63
|
-
"@babel/plugin-proposal-class-properties": "^7.
|
|
64
|
-
"@babel/preset-env": "^7.18.
|
|
65
|
-
"@babel/preset-react": "^7.
|
|
66
|
-
"@changesets/cli": "^2.
|
|
60
|
+
"@babel/cli": "^7.18.10",
|
|
61
|
+
"@babel/core": "^7.18.13",
|
|
62
|
+
"@babel/eslint-parser": "^7.18.9",
|
|
63
|
+
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
64
|
+
"@babel/preset-env": "^7.18.10",
|
|
65
|
+
"@babel/preset-react": "^7.18.6",
|
|
66
|
+
"@changesets/cli": "^2.24.3",
|
|
67
67
|
"@testing-library/cypress": "^8.0.3",
|
|
68
|
-
"@testing-library/dom": "^8.
|
|
69
|
-
"@testing-library/jest-dom": "^5.16.
|
|
68
|
+
"@testing-library/dom": "^8.17.1",
|
|
69
|
+
"@testing-library/jest-dom": "^5.16.5",
|
|
70
70
|
"@testing-library/react": "^13.3.0",
|
|
71
|
-
"@testing-library/user-event": "^14.
|
|
71
|
+
"@testing-library/user-event": "^14.4.3",
|
|
72
72
|
"@types/jquery": "^3.5.14",
|
|
73
73
|
"all-contributors-cli": "^6.20.0",
|
|
74
|
-
"babel-jest": "^28.1.
|
|
74
|
+
"babel-jest": "^28.1.3",
|
|
75
75
|
"babelify": "^10.0.0",
|
|
76
76
|
"browserify": "^17.0.0",
|
|
77
77
|
"budo": "^11.7.0",
|
|
78
|
-
"cypress": "^
|
|
78
|
+
"cypress": "^10.6.0",
|
|
79
79
|
"cypress-plugin-tab": "^1.0.5",
|
|
80
|
-
"eslint": "^8.
|
|
80
|
+
"eslint": "^8.22.0",
|
|
81
81
|
"eslint-config-prettier": "^8.5.0",
|
|
82
82
|
"eslint-plugin-cypress": "^2.12.1",
|
|
83
|
-
"eslint-plugin-jest": "^26.
|
|
84
|
-
"eslint-plugin-react": "^7.30.
|
|
85
|
-
"jest": "^28.1.
|
|
86
|
-
"jest-environment-jsdom": "^28.1.
|
|
87
|
-
"jest-watch-typeahead": "^
|
|
83
|
+
"eslint-plugin-jest": "^26.8.7",
|
|
84
|
+
"eslint-plugin-react": "^7.30.1",
|
|
85
|
+
"jest": "^28.1.3",
|
|
86
|
+
"jest-environment-jsdom": "^28.1.3",
|
|
87
|
+
"jest-watch-typeahead": "^2.0.0",
|
|
88
88
|
"onchange": "^7.1.0",
|
|
89
|
-
"prettier": "^2.
|
|
89
|
+
"prettier": "^2.7.1",
|
|
90
90
|
"prop-types": "^15.8.1",
|
|
91
|
-
"react": "^18.
|
|
92
|
-
"react-dom": "^18.
|
|
91
|
+
"react": "^18.2.0",
|
|
92
|
+
"react-dom": "^18.2.0",
|
|
93
93
|
"regenerator-runtime": "^0.13.9",
|
|
94
94
|
"start-server-and-test": "^1.14.0",
|
|
95
|
-
"typescript": "^4.7.
|
|
95
|
+
"typescript": "^4.7.4"
|
|
96
96
|
},
|
|
97
97
|
"dependencies": {
|
|
98
|
-
"focus-trap": "^
|
|
98
|
+
"focus-trap": "^7.0.0",
|
|
99
|
+
"tabbable": "^6.0.0"
|
|
99
100
|
},
|
|
100
101
|
"peerDependencies": {
|
|
101
102
|
"prop-types": "^15.8.1",
|
package/src/focus-trap-react.js
CHANGED
|
@@ -270,7 +270,27 @@ class FocusTrap extends React.Component {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
setupFocusTrap() {
|
|
273
|
-
if (
|
|
273
|
+
if (this.focusTrap) {
|
|
274
|
+
// trap already exists: it's possible we're in StrictMode and we're being remounted,
|
|
275
|
+
// in which case, we will have deactivated the trap when we got unmounted (remember,
|
|
276
|
+
// StrictMode, in development, purposely unmounts and remounts components after
|
|
277
|
+
// mounting them the first time to make sure they have reusable state,
|
|
278
|
+
// @see https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state) so now
|
|
279
|
+
// we need to restore the state of the trap according to our component state
|
|
280
|
+
// NOTE: Strict mode __violates__ assumptions about the `componentWillUnmount()` API
|
|
281
|
+
// which clearly states -- even for React 18 -- that, "Once a component instance is
|
|
282
|
+
// unmounted, __it will never be mounted again.__" (emphasis ours). So when we get
|
|
283
|
+
// unmounted, we assume we're gone forever and we deactivate the trap. But then
|
|
284
|
+
// we get remounted and we're supposed to restore state. But if you had paused,
|
|
285
|
+
// we've now deactivated (we don't know we're amount to get remounted again)
|
|
286
|
+
// which means we need to reactivate and then pause. Otherwise, do nothing.
|
|
287
|
+
if (this.props.active && !this.focusTrap.active) {
|
|
288
|
+
this.focusTrap.activate();
|
|
289
|
+
if (this.props.paused) {
|
|
290
|
+
this.focusTrap.pause();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
274
294
|
const nodesExist = this.focusTrapElements.some(Boolean);
|
|
275
295
|
if (nodesExist) {
|
|
276
296
|
// eslint-disable-next-line react/prop-types -- _createFocusTrap is an internal prop
|
|
@@ -433,11 +453,16 @@ FocusTrap.propTypes = {
|
|
|
433
453
|
allowOutsideClick: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
|
434
454
|
preventScroll: PropTypes.bool,
|
|
435
455
|
tabbableOptions: PropTypes.shape({
|
|
436
|
-
displayCheck: PropTypes.oneOf([
|
|
456
|
+
displayCheck: PropTypes.oneOf([
|
|
457
|
+
'full',
|
|
458
|
+
'legacy-full',
|
|
459
|
+
'non-zero-area',
|
|
460
|
+
'none',
|
|
461
|
+
]),
|
|
437
462
|
getShadowRoot: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
|
438
463
|
}),
|
|
439
464
|
}),
|
|
440
|
-
containerElements: PropTypes.arrayOf(PropTypes.instanceOf(ElementType)),
|
|
465
|
+
containerElements: PropTypes.arrayOf(PropTypes.instanceOf(ElementType)), // DOM element ONLY
|
|
441
466
|
children: PropTypes.oneOfType([
|
|
442
467
|
PropTypes.element, // React element
|
|
443
468
|
PropTypes.instanceOf(ElementType), // DOM element
|