focus-trap 6.8.0-beta.1 → 6.8.1

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 CHANGED
@@ -1,12 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 7c86111:
8
+ - Bump tabbable to `^5.3.1` (fixing previous update which was incorrectly set to `5.3.0`).
9
+ - Fix `tabbableOptions` not being used in all internal uses of tabbable APIs.
10
+ - Expose `displayCheck` option in `tabbableOptions` typings and pass it through to tabbable APIs.
11
+ - Add info to README about testing traps in JSDom (which is not officially supported).
12
+
13
+ ## 6.8.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 21458c9: Bumps tabbable to v5.3.0 and includes all changes from the past v6.8.0 beta releases. The big new feature is opt-in Shadow DOM support in tabbable, and a new `getShadowRoot` tabbable option exposed in a new `tabbableOptions` focus-trap config option.
18
+
19
+ ## 6.8.0-beta.2
20
+
21
+ - When updating tabbable nodes, make sure that `getShadowRoot` tabbable option is also passed to `focusable()`.
22
+ - Fix bug where having a tabbable node inside a web component in the middle of a tab sequence would cause the tab key to seemingly stop working just before focus should move to it ((#643)[https://github.com/focus-trap/focus-trap/issues/643]).
23
+ - Bumps tabbable to `v5.3.0-beta.1`
24
+
3
25
  ## 6.8.0-beta.1
4
26
 
5
- - Rebased onto `v6.7.3`.
27
+ - Previous beta didn't include new source. This one does.
6
28
 
7
29
  ## 6.8.0-beta.0
8
30
 
9
31
  - Adds new `tabbableOptions` configuration option, which allows specifically for the new `getShadowRoot` Tabbable configuration option: `focusTrap.createFocusTrap(rootElement, { tabbableOptions: { getShadowRoot: (node) => closedShadowRoot } })`, for example (where your code has the reference to `closedShadowRoot` previously created on `node` which Tabbable cannot find on its own).
32
+ - Bumps tabbable to `v5.3.0-beta.0`
10
33
 
11
34
  ## 6.7.3
12
35
 
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # focus-trap [![CI](https://github.com/focus-trap/focus-trap/workflows/CI/badge.svg?branch=master&event=push)](https://github.com/focus-trap/focus-trap/actions?query=workflow:CI+branch:master) [![license](https://badgen.now.sh/badge/license/MIT)](./LICENSE)
2
2
 
3
3
  <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
4
- [![All Contributors](https://img.shields.io/badge/all_contributors-21-orange.svg?style=flat-square)](#contributors)
4
+ [![All Contributors](https://img.shields.io/badge/all_contributors-22-orange.svg?style=flat-square)](#contributors)
5
5
  <!-- ALL-CONTRIBUTORS-BADGE:END -->
6
6
 
7
7
  Trap focus within a DOM node.
@@ -106,8 +106,8 @@ Returns a new focus trap on `element` (one or more "containers" of tabbable node
106
106
  - **preventScroll** `{boolean}`: By default, focus() will scroll to the element if not in viewport. It can produce unintended effects like scrolling back to the top of a modal. If set to `true`, no scroll will happen.
107
107
  - **delayInitialFocus** `{boolean}`: Default: `true`. Delays the autofocus to the next execution frame when the focus trap is activated. This prevents elements within the focusable element from capturing the event that triggered the focus trap activation.
108
108
  - **document** {Document}: Default: `window.document`. Document where the focus trap will be active. This allows to use FocusTrap in an iFrame context.
109
- - **tabbableOptions**: (optional) Specific [tabbable](https://github.com/focus-trap/tabbable) options that are configurable on FocusTrap.
110
- - **tabbableOptions.getShadowRoot**: See [getShadowRoot](https://github.com/focus-trap/tabbable#getshadowroot) on Tabbable for more details.
109
+ - **tabbableOptions**: (optional) [tabbable options](https://github.com/focus-trap/tabbable#common-options) configurable on FocusTrap (all the _common options_).
110
+ - ⚠️ See notes about __[testing in JSDom](#testing-in-jsdom)__ (e.g. using Jest).
111
111
 
112
112
  #### Shadow DOM
113
113
 
@@ -241,6 +241,20 @@ If you find yourself in this situation, you should give you container `tabindex=
241
241
 
242
242
  Because of the nature of the functionality, involving keyboard and click and (especially) focus events, JavaScript unit tests don't make sense. After all, JSDom does not fully support focus events. Since the demo was developed to also be the test, we use Cypress to automate running through all demos in the demo page.
243
243
 
244
+ ## Help
245
+
246
+ ### Testing in JSDom
247
+
248
+ > ⚠️ JSDom is not officially supported. Your mileage may vary, and tests may break from one release to the next (even a patch or minor release).
249
+ >
250
+ > This topic is just here to help with what we know may affect your tests.
251
+
252
+ In general, a focus trap is best tested in a full browser environment such as Cypress, Playwright, or Nightwatch where a full DOM is available.
253
+
254
+ Sometimes, that's not entirely desirable, and depending on what you're testing, you may be able to get away with using JSDom (e.g. via Jest), but you'll have to configure your traps using the `tabbableOptions.displayCheck: 'none'` option.
255
+
256
+ See [Testing tabbable in JSDom](https://github.com/focus-trap/tabbable#testing-in-jsdom) for more details.
257
+
244
258
  # Contributing
245
259
 
246
260
  See [CONTRIBUTING](CONTRIBUTING.md).
@@ -258,26 +272,29 @@ In alphabetical order:
258
272
  <td align="center"><a href="https://github.com/bparish628"><img src="https://avatars1.githubusercontent.com/u/8492971?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benjamin Parish</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Abparish628" title="Bug reports">🐛</a></td>
259
273
  <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/commits?author=cgood92" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Documentation">📖</a> <a href="#example-cgood92" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=cgood92" title="Tests">⚠️</a></td>
260
274
  <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/commits?author=Dan503" title="Documentation">📖</a> <a href="#tool-Dan503" title="Tools">🔧</a> <a href="#a11y-Dan503" title="Accessibility">️️️️♿️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=Dan503" title="Code">💻</a></td>
275
+ <td align="center"><a href="https://github.com/DaviDevMod"><img src="https://avatars.githubusercontent.com/u/98312056?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DaviDevMod</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=DaviDevMod" title="Documentation">📖</a></td>
261
276
  <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/commits?author=davidtheclark" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/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/commits?author=davidtheclark" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=davidtheclark" title="Documentation">📖</a> <a href="#maintenance-davidtheclark" title="Maintenance">🚧</a></td>
262
277
  <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>
263
- <td align="center"><a href="https://github.com/michael-ar"><img src="https://avatars3.githubusercontent.com/u/18557997?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Reynolds</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Amichael-ar" title="Bug reports">🐛</a></td>
264
278
  </tr>
265
279
  <tr>
280
+ <td align="center"><a href="https://github.com/michael-ar"><img src="https://avatars3.githubusercontent.com/u/18557997?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Reynolds</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Amichael-ar" title="Bug reports">🐛</a></td>
266
281
  <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/commits?author=liunate" title="Tests">⚠️</a></td>
267
282
  <td align="center"><a href="https://github.com/far-fetched"><img src="https://avatars.githubusercontent.com/u/11621383?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piotr Panek</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Afar-fetched" title="Bug reports">🐛</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Documentation">📖</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=far-fetched" title="Tests">⚠️</a></td>
268
283
  <td align="center"><a href="https://github.com/randypuro"><img src="https://avatars2.githubusercontent.com/u/2579?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Randy Puro</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Arandypuro" title="Bug reports">🐛</a></td>
269
284
  <td align="center"><a href="https://github.com/sadick254"><img src="https://avatars2.githubusercontent.com/u/5238135?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sadick</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=sadick254" title="Documentation">📖</a></td>
270
285
  <td align="center"><a href="https://scottblinch.me/"><img src="https://avatars2.githubusercontent.com/u/4682114?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Scott Blinch</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=scottblinch" title="Documentation">📖</a></td>
271
286
  <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/commits?author=SeanMcP" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=SeanMcP" title="Documentation">📖</a></td>
272
- <td align="center"><a href="https://github.com/skriems"><img src="https://avatars.githubusercontent.com/u/15573317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sebastian Kriems</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Askriems" title="Bug reports">🐛</a></td>
273
287
  </tr>
274
288
  <tr>
289
+ <td align="center"><a href="https://github.com/skriems"><img src="https://avatars.githubusercontent.com/u/15573317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sebastian Kriems</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Askriems" title="Bug reports">🐛</a></td>
275
290
  <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/issues?q=author%3ASlapbox" title="Bug reports">🐛</a></td>
276
291
  <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/commits?author=stefcameron" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/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/commits?author=stefcameron" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=stefcameron" title="Documentation">📖</a> <a href="#maintenance-stefcameron" title="Maintenance">🚧</a></td>
277
292
  <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="#tool-thawkin3" title="Tools">🔧</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=thawkin3" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=thawkin3" title="Documentation">📖</a></td>
278
293
  <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/commits?author=wandroll" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Tests">⚠️</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=wandroll" title="Documentation">📖</a> <a href="#tool-wandroll" title="Tools">🔧</a></td>
279
294
  <td align="center"><a href="http://willmruzek.com/"><img src="https://avatars.githubusercontent.com/u/108522?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Will Mruzek</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Code">💻</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Documentation">📖</a> <a href="#example-mruzekw" title="Examples">💡</a> <a href="https://github.com/focus-trap/focus-trap/commits?author=mruzekw" title="Tests">⚠️</a> <a href="#question-mruzekw" title="Answering Questions">💬</a></td>
280
295
  <td align="center"><a href="https://github.com/zioth"><img src="https://avatars3.githubusercontent.com/u/945603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zioth</b></sub></a><br /><a href="#ideas-zioth" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Azioth" title="Bug reports">🐛</a></td>
296
+ </tr>
297
+ <tr>
281
298
  <td align="center"><a href="https://github.com/jpveooys"><img src="https://avatars.githubusercontent.com/u/66470099?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jpveooys</b></sub></a><br /><a href="https://github.com/focus-trap/focus-trap/issues?q=author%3Ajpveooys" title="Bug reports">🐛</a></td>
282
299
  </tr>
283
300
  </table>
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * focus-trap 6.8.0-beta.1
2
+ * focus-trap 6.8.1
3
3
  * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
4
4
  */
5
5
  import { tabbable, focusable, isTabbable, isFocusable } from 'tabbable';
@@ -151,20 +151,28 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
151
151
  }, userOptions);
152
152
 
153
153
  var state = {
154
+ // containers given to createFocusTrap()
154
155
  // @type {Array<HTMLElement>}
155
156
  containers: [],
156
- // list of objects identifying the first and last tabbable nodes in all containers/groups in
157
- // the trap
157
+ // list of objects identifying tabbable nodes in `containers` in the trap
158
158
  // NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap
159
159
  // is active, but the trap should never get to a state where there isn't at least one group
160
160
  // with at least one tabbable node in it (that would lead to an error condition that would
161
161
  // result in an error being thrown)
162
162
  // @type {Array<{
163
163
  // container: HTMLElement,
164
+ // tabbableNodes: Array<HTMLElement>, // empty if none
165
+ // focusableNodes: Array<HTMLElement>, // empty if none
164
166
  // firstTabbableNode: HTMLElement|null,
165
167
  // lastTabbableNode: HTMLElement|null,
166
168
  // nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
167
169
  // }>}
170
+ containerGroups: [],
171
+ // same order/length as `containers` list
172
+ // references to objects in `containerGroups`, but only those that actually have
173
+ // tabbable nodes in them
174
+ // NOTE: same order as `containers` and `containerGroups`, but __not necessarily__
175
+ // the same length
168
176
  tabbableGroups: [],
169
177
  nodeFocusedBeforeActivation: null,
170
178
  mostRecentlyFocusedNode: null,
@@ -188,11 +196,30 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
188
196
  var getOption = function getOption(configOverrideOptions, optionName, configOptionName) {
189
197
  return configOverrideOptions && configOverrideOptions[optionName] !== undefined ? configOverrideOptions[optionName] : config[configOptionName || optionName];
190
198
  };
199
+ /**
200
+ * Finds the index of the container that contains the element.
201
+ * @param {HTMLElement} element
202
+ * @returns {number} Index of the container in either `state.containers` or
203
+ * `state.containerGroups` (the order/length of these lists are the same); -1
204
+ * if the element isn't found.
205
+ */
191
206
 
192
- var containersContain = function containersContain(element) {
193
- return !!(element && state.containers.some(function (container) {
194
- return container.contains(element);
195
- }));
207
+
208
+ var findContainerIndex = function findContainerIndex(element) {
209
+ // NOTE: search `containerGroups` because it's possible a group contains no tabbable
210
+ // nodes, but still contains focusable nodes (e.g. if they all have `tabindex=-1`)
211
+ // and we still need to find the element in there
212
+ return state.containerGroups.findIndex(function (_ref) {
213
+ var container = _ref.container,
214
+ tabbableNodes = _ref.tabbableNodes;
215
+ return container.contains(element) || // fall back to explicit tabbable search which will take into consideration any
216
+ // web components if the `tabbableOptions.getShadowRoot` option was used for
217
+ // the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
218
+ // look inside web components even if open)
219
+ tabbableNodes.find(function (node) {
220
+ return node === element;
221
+ });
222
+ });
196
223
  };
197
224
  /**
198
225
  * Gets the node for the given option, which is expected to be an option that
@@ -251,7 +278,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
251
278
 
252
279
  if (node === undefined) {
253
280
  // option not specified: use fallback options
254
- if (containersContain(doc.activeElement)) {
281
+ if (findContainerIndex(doc.activeElement) >= 0) {
255
282
  node = doc.activeElement;
256
283
  } else {
257
284
  var firstTabbableGroup = state.tabbableGroups[0];
@@ -269,64 +296,61 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
269
296
  };
270
297
 
271
298
  var updateTabbableNodes = function updateTabbableNodes() {
272
- state.tabbableGroups = state.containers.map(function (container) {
273
- var _config$tabbableOptio;
274
-
275
- var tabbableNodes = tabbable(container, {
276
- getShadowRoot: (_config$tabbableOptio = config.tabbableOptions) === null || _config$tabbableOptio === void 0 ? void 0 : _config$tabbableOptio.getShadowRoot
277
- }); // NOTE: if we have tabbable nodes, we must have focusable nodes; focusable nodes
299
+ state.containerGroups = state.containers.map(function (container) {
300
+ var tabbableNodes = tabbable(container, config.tabbableOptions); // NOTE: if we have tabbable nodes, we must have focusable nodes; focusable nodes
278
301
  // are a superset of tabbable nodes
279
302
 
280
- var focusableNodes = focusable(container);
281
-
282
- if (tabbableNodes.length > 0) {
283
- return {
284
- container: container,
285
- firstTabbableNode: tabbableNodes[0],
286
- lastTabbableNode: tabbableNodes[tabbableNodes.length - 1],
287
-
288
- /**
289
- * Finds the __tabbable__ node that follows the given node in the specified direction,
290
- * in this container, if any.
291
- * @param {HTMLElement} node
292
- * @param {boolean} [forward] True if going in forward tab order; false if going
293
- * in reverse.
294
- * @returns {HTMLElement|undefined} The next tabbable node, if any.
295
- */
296
- nextTabbableNode: function nextTabbableNode(node) {
297
- var forward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
298
- // NOTE: If tabindex is positive (in order to manipulate the tab order separate
299
- // from the DOM order), this __will not work__ because the list of focusableNodes,
300
- // while it contains tabbable nodes, does not sort its nodes in any order other
301
- // than DOM order, because it can't: Where would you place focusable (but not
302
- // tabbable) nodes in that order? They have no order, because they aren't tabbale...
303
- // Support for positive tabindex is already broken and hard to manage (possibly
304
- // not supportable, TBD), so this isn't going to make things worse than they
305
- // already are, and at least makes things better for the majority of cases where
306
- // tabindex is either 0/unset or negative.
307
- // FYI, positive tabindex issue: https://github.com/focus-trap/focus-trap/issues/375
308
- var nodeIdx = focusableNodes.findIndex(function (n) {
309
- return n === node;
310
- });
311
-
312
- if (forward) {
313
- return focusableNodes.slice(nodeIdx + 1).find(function (n) {
314
- return isTabbable(n);
315
- });
316
- }
303
+ var focusableNodes = focusable(container, config.tabbableOptions);
304
+ return {
305
+ container: container,
306
+ tabbableNodes: tabbableNodes,
307
+ focusableNodes: focusableNodes,
308
+ firstTabbableNode: tabbableNodes.length > 0 ? tabbableNodes[0] : null,
309
+ lastTabbableNode: tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : null,
310
+
311
+ /**
312
+ * Finds the __tabbable__ node that follows the given node in the specified direction,
313
+ * in this container, if any.
314
+ * @param {HTMLElement} node
315
+ * @param {boolean} [forward] True if going in forward tab order; false if going
316
+ * in reverse.
317
+ * @returns {HTMLElement|undefined} The next tabbable node, if any.
318
+ */
319
+ nextTabbableNode: function nextTabbableNode(node) {
320
+ var forward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
321
+ // NOTE: If tabindex is positive (in order to manipulate the tab order separate
322
+ // from the DOM order), this __will not work__ because the list of focusableNodes,
323
+ // while it contains tabbable nodes, does not sort its nodes in any order other
324
+ // than DOM order, because it can't: Where would you place focusable (but not
325
+ // tabbable) nodes in that order? They have no order, because they aren't tabbale...
326
+ // Support for positive tabindex is already broken and hard to manage (possibly
327
+ // not supportable, TBD), so this isn't going to make things worse than they
328
+ // already are, and at least makes things better for the majority of cases where
329
+ // tabindex is either 0/unset or negative.
330
+ // FYI, positive tabindex issue: https://github.com/focus-trap/focus-trap/issues/375
331
+ var nodeIdx = focusableNodes.findIndex(function (n) {
332
+ return n === node;
333
+ });
334
+
335
+ if (nodeIdx < 0) {
336
+ return undefined;
337
+ }
317
338
 
318
- return focusableNodes.slice(0, nodeIdx).reverse().find(function (n) {
319
- return isTabbable(n);
339
+ if (forward) {
340
+ return focusableNodes.slice(nodeIdx + 1).find(function (n) {
341
+ return isTabbable(n, config.tabbableOptions);
320
342
  });
321
343
  }
322
- };
323
- }
324
344
 
325
- return undefined;
326
- }).filter(function (group) {
327
- return !!group;
328
- }); // remove groups with no tabbable nodes
329
- // throw if no groups have tabbable nodes and we don't have a fallback focus node either
345
+ return focusableNodes.slice(0, nodeIdx).reverse().find(function (n) {
346
+ return isTabbable(n, config.tabbableOptions);
347
+ });
348
+ }
349
+ };
350
+ });
351
+ state.tabbableGroups = state.containerGroups.filter(function (group) {
352
+ return group.tabbableNodes.length > 0;
353
+ }); // throw if no groups have tabbable nodes and we don't have a fallback focus node either
330
354
 
331
355
  if (state.tabbableGroups.length <= 0 && !getNodeForOption('fallbackFocus') // returning false not supported for this option
332
356
  ) {
@@ -368,7 +392,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
368
392
  var checkPointerDown = function checkPointerDown(e) {
369
393
  var target = getActualTarget(e);
370
394
 
371
- if (containersContain(target)) {
395
+ if (findContainerIndex(target) >= 0) {
372
396
  // allow the click since it ocurred inside the trap
373
397
  return;
374
398
  }
@@ -387,7 +411,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
387
411
  // that was clicked, whether it's focusable or not; by setting
388
412
  // `returnFocus: true`, we'll attempt to re-focus the node originally-focused
389
413
  // on activation (or the configured `setReturnFocus` node)
390
- returnFocus: config.returnFocusOnDeactivate && !isFocusable(target)
414
+ returnFocus: config.returnFocusOnDeactivate && !isFocusable(target, config.tabbableOptions)
391
415
  });
392
416
  return;
393
417
  } // This is needed for mobile devices.
@@ -407,7 +431,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
407
431
 
408
432
  var checkFocusIn = function checkFocusIn(e) {
409
433
  var target = getActualTarget(e);
410
- var targetContained = containersContain(target); // In Firefox when you Tab out of an iframe the Document is briefly focused.
434
+ var targetContained = findContainerIndex(target) >= 0; // In Firefox when you Tab out of an iframe the Document is briefly focused.
411
435
 
412
436
  if (targetContained || target instanceof Document) {
413
437
  if (targetContained) {
@@ -433,11 +457,8 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
433
457
  // make sure the target is actually contained in a group
434
458
  // NOTE: the target may also be the container itself if it's focusable
435
459
  // with tabIndex='-1' and was given initial focus
436
- var containerIndex = findIndex(state.tabbableGroups, function (_ref) {
437
- var container = _ref.container;
438
- return container.contains(target);
439
- });
440
- var containerGroup = containerIndex >= 0 ? state.tabbableGroups[containerIndex] : undefined;
460
+ var containerIndex = findContainerIndex(target);
461
+ var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : undefined;
441
462
 
442
463
  if (containerIndex < 0) {
443
464
  // target not found in any group: quite possible focus has escaped the trap,
@@ -457,7 +478,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
457
478
  return target === firstTabbableNode;
458
479
  });
459
480
 
460
- if (startOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target) && !isTabbable(target) && !containerGroup.nextTabbableNode(target, false))) {
481
+ if (startOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) {
461
482
  // an exception case where the target is either the container itself, or
462
483
  // a non-tabbable node that was given focus (i.e. tabindex is negative
463
484
  // and user clicked on it or node was programmatically given focus)
@@ -483,7 +504,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
483
504
  return target === lastTabbableNode;
484
505
  });
485
506
 
486
- if (lastOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target) && !isTabbable(target) && !containerGroup.nextTabbableNode(target))) {
507
+ if (lastOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) {
487
508
  // an exception case where the target is the container itself, or
488
509
  // a non-tabbable node that was given focus (i.e. tabindex is negative
489
510
  // and user clicked on it or node was programmatically given focus)
@@ -535,7 +556,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
535
556
 
536
557
  var target = getActualTarget(e);
537
558
 
538
- if (containersContain(target)) {
559
+ if (findContainerIndex(target) >= 0) {
539
560
  return;
540
561
  }
541
562