components icon indicating copy to clipboard operation
components copied to clipboard

chore: Add internal component to transparently proxy focus through portals

Open avinashbot opened this issue 3 years ago • 2 comments

Description

The problem statement

There are a number of components that could render content in portals outside of the standard DOM order. Notably, these are popovers (with renderWithPortal) and dropdown-based components (with expandToViewport). When focus gets moved to these floating components, tabbing past the end causes focus to hit the end of the DOM instead of "resuming" past the trigger. Popovers normally render a focus trap, but the ones used in onboarding don't have a focus trap, so they're affected as well.

My proposal

A generic component, that behaves very much like a portal, but also moves focus through the portal'ed content as if it was rendered inside the DOM. It works by detecting tabbable elements before and after the portal and determining focus direction. Try it locally!

Maybe this logic could be integrated into the standard portal component, but I'm not sure yet.

Also

I renamed <TabTrap /> to <FocusDetector /> (cause it ain't a tab trap), and moved the tabbable utils out into the general utils folder. Also renamed focusables to tabbables because I'm explicitly ignoring elements with tabindex="-1".

In the future, if the portal is the final tabbable element, it would be nice to be able to disable the final tab trap so that the focus naturally moves into the address bar instead of back to the start of the DOM.

How has this been tested?

Only locally, and manually. An integration test is definitely in order, but let's validate the approach first.

Documentation changes

[Do the changes include any API documentation changes?]

  • [ ] Yes, this change contains documentation changes.
  • [x] No.

Related Links

[Attach any related links/pull request for this change]

Review checklist

The following items are to be evaluated by the author(s) and the reviewer(s).

Correctness

  • [x] Changes are backward-compatible if not indicated, see CONTRIBUTING.md.
  • [x] Changes do not include unsupported browser features, see CONTRIBUTING.md.
  • [ ] Changes were manually tested for accessibility, see accessibility guidelines.

Security

Testing

  • [ ] Changes are covered with new/existing unit tests?
  • [ ] Changes are covered with new/existing integration tests?

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

avinashbot avatar Aug 25 '22 14:08 avinashbot

Codecov Report

Base: 92.61% // Head: 92.55% // Decreases project coverage by -0.05% :warning:

Coverage data is based on head (ca3efc1) compared to base (a6e49a9). Patch coverage: 50.00% of modified lines in pull request are covered.

:exclamation: Current head ca3efc1 differs from pull request most recent head 6887662. Consider uploading reports for the commit 6887662 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #201      +/-   ##
==========================================
- Coverage   92.61%   92.55%   -0.06%     
==========================================
  Files         559      552       -7     
  Lines       15822    15712     -110     
  Branches     4328     4301      -27     
==========================================
- Hits        14654    14543     -111     
  Misses       1086     1086              
- Partials       82       83       +1     
Impacted Files Coverage Δ
src/internal/components/dropdown/index.tsx 91.90% <0.00%> (+0.04%) :arrow_up:
src/internal/components/focus-lock/index.tsx 83.33% <50.00%> (ø)
src/internal/utils/tabbables.ts 70.00% <60.00%> (ø)
src/internal/components/focus-detector/index.tsx 100.00% <100.00%> (ø)
src/breadcrumb-group/internal.tsx 83.33% <0.00%> (-6.92%) :arrow_down:
src/internal/utils/scrollable-containers.ts 93.75% <0.00%> (-6.25%) :arrow_down:
src/autosuggest/internal.tsx 89.36% <0.00%> (-4.19%) :arrow_down:
src/autosuggest/options-list.tsx 97.22% <0.00%> (-2.78%) :arrow_down:
src/autosuggest/virtual-list.tsx 90.90% <0.00%> (-2.43%) :arrow_down:
...rc/property-filter/property-filter-autosuggest.tsx 87.50% <0.00%> (-0.84%) :arrow_down:
... and 92 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

:umbrella: View full report at Codecov.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.

codecov[bot] avatar Aug 25 '22 14:08 codecov[bot]

"In the future, if the portal is the final tabbable element, it would be nice to be able to disable the final tab trap so that the focus naturally moves into the address bar instead of back to the start of the DOM."

What is the problem with adding this already?

pan-kot avatar Aug 26 '22 11:08 pan-kot

The more I update this component, the more I'm starting to feel that us mere mortals do not deserve to steal control of the natural tab order from the heavens this way. I'm no Prometheus, so I'll leave this back where I found it.

For portals, the focus behavior should probably decided on a component-by-component level. Modals and popovers are tab traps. I'll probably just take a "tab loop" approach with dropdowns. At least I gave this experiment a solid try...

avinashbot avatar Oct 07 '22 14:10 avinashbot