Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix FocusTrap escape due to strange tabindex values #2093

Merged
merged 4 commits into from Dec 14, 2022
Merged

Conversation

RobinMalfait
Copy link
Collaborator

This PR will now ensure that we can't escape the FocusTrap if you use <tab> and you happen to tab to an element outside of the FocusTrap because the next item in line happens to be outside of the FocusTrap and we never hit any of the focus guard elements.

How it works is as follows:

  1. The onBlur is implemented on the FocusTrap itself, this will give us some information in the event itself.
    • e.target is the element that is being blurred (think of it as from)
    • e.currentTarget is the element with the event listener (the dialog)
    • e.relatedTarget is the element we are going to (think of it as to)
  2. If the blur happened due to a <tab> or <shift>+<tab>, then we will move focus back inside the FocusTrap, and go from the e.target to the next or previous value.
  3. If the blur happened programmatically (so no tab keys are involved, aka no direction is known), then the focus is restored to the e.target value.

Fixes: #1656

It will still keep the same DOM order if tabIndex matches, thanks to
stable sorts!
All the arguments resulted in usage like `focusIn(container,
Focus.First, true, null)`, and to make things worse, we need to add
something else to this list in the future.

Instead, let's keep the `container` and the type of `Focus` as known
params, all the other things can sit in an options object.
This code will now ensure that we can't escape the FocusTrap if you use
`<tab>` and you happen to tab to an element outside of the FocusTrap
because the next item in line happens to be outside of the FocusTrap and
we never hit any of the focus guard elements.

How it works is as follows:

1. The `onBlur` is implemented on the `FocusTrap` itself, this will give
   us some information in the event itself.
   - `e.target` is the element that is being blurred (think of it as `from`)
   - `e.currentTarget` is the element with the event listener (the dialog)
   - `e.relatedTarget` is the element we are going to (think of it as `to`)
2. If the blur happened due to a `<tab>` or `<shift>+<tab>`, then we
   will move focus back inside the FocusTrap, and go from the `e.target`
   to the next or previous value.
3. If the blur happened programmatically (so no tab keys are involved,
   aka no direction is known), then the focus is restored to the
   `e.target` value.

Fixes: #1656
@vercel
Copy link

vercel bot commented Dec 14, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
headlessui-react ✅ Ready (Inspect) Visit Preview Dec 14, 2022 at 3:15PM (UTC)
headlessui-vue ✅ Ready (Inspect) Visit Preview Dec 14, 2022 at 3:15PM (UTC)

@thecrypticace
Copy link
Contributor

bruh what a fix 😅

@RobinMalfait
Copy link
Collaborator Author

@thecrypticace

@RobinMalfait RobinMalfait merged commit d31bb5c into main Dec 14, 2022
@RobinMalfait RobinMalfait deleted the fix/issue-1656 branch December 14, 2022 15:26
ahuth added a commit to chanzuckerberg/edu-design-system that referenced this pull request Jan 5, 2023
Resolves e2e test failures in Traject (see FB-PLP/traject#13040).

Issue starts in 1.7.6, possibly from tailwindlabs/headlessui#2093.
ahuth added a commit to chanzuckerberg/edu-design-system that referenced this pull request Jan 5, 2023
Resolves e2e test failures in Traject (see FB-PLP/traject#13040).

Issue starts in 1.7.6, possibly from tailwindlabs/headlessui#2093.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(bug) [React]: Focus trap doesn't keep focus when tabbing to element with tabIndex outside of the Dialog
2 participants