-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
FocusScope not working when used inside shadowRoot #1472
Comments
Shadow DOM support is definitely something we're interested in. I'm sure quite a few things are broken at the moment beyond just FocusScope. If you're interested in contributing, that would be amazing! A few other places I can think of that will need work:
There are probably others as well. In general, is it safe to assume that we can traverse into a shadow root though? What about closed shadow roots? Is it ok to not support those you think? Or assume that the closed shadow roots will forward focus internally as necessary, for example? |
I love this project, I will contribute to adding support for shadow DOM. Any pointers on what should I keep in mind while working on it? |
Thank you! I think just the things @devongovett mentioned in the previous comment to start. Otherwise, make sure to check out the contribution page on our docs site https://react-spectrum.adobe.com/contribute.html |
Ok sure, I will check the contribution guide and work on the initial RFC. |
I would love to use this fix as well. I too need this for implementing micro-frontends in our application. Is there any pending work that needs an extra helping hand? |
If you want to have a look at it, that'd be a great help! We don't have any current work in the team going on for it and I don't think we've heard from the other people in this thread in a long while. |
@theomessin
If this is something you would be interested in, we would be open to discussing how you think a shadow dom solution in React Aria would work before committing any individual patches. |
I ran into the same issue where keyboard focus doesn't work when an app that uses |
@snowystinger I know that the scope of I'm wondering if a similar approach could be followed, but maybe using context instead (similar to Just an idea - I don't know my way around this codebase, so maybe there are problems with following a similar pattern? |
@snowystinger Shouldn't this be easier now that we already support dynamic iframe starting next release ? I guess the change needed will be mostly in |
Possibly? I do not know much about ShadowDOM or ShadowRoot. Here's a list of things we identified as potentially problematic with iframes. I'm not sure if it holds for ShadowDom as well. The biggest help for support would be unit tests demonstrating real life use cases and expected behaviors. Or an example minimal app. Something so we can discuss the approach here. If it's just FocusScope, we can also determine how much would be required to support it more easily this way. |
Thanks for the list. This is very helpful. We will be soon implementing react-aria inside shadow DOM so we will be able to look into this more. One good starting point will be to support iframes for all react aria hooks. It will make sure that the document extraction logic is at the same place, i.e Do you see any problem in replacing all instances of |
* Add `getRootNode` utility. * Update `getRootNode` util. Update domHelpers.test.js. * Update `getOwnerWindow` util. * Add tests for Shadow DOM handling using `getRootNode`. * Update comment. * Fix FocusScope.tsx in Shadow DOM. Add Tests for FocusScope.test.js. New helper util `getRootBody`. * Add more test for FocusScope.test.js. Fix `useRestoreFocus` issue. Add new DOM util `getDeepActiveElement`. * Fix another `useRestoreFocus` issue with restoring focus in `Keyboard navigation example`. * Add tests for `getDeepActiveElement` * Add `useFocus` shadow DOM tests. update `useFocus` - `useFocusWithin` - `usePress`. * Update `focusSafely`. Test for `focusSafely`. * Update `useInteractionOutside` for Shadow DOM support. * Update `useFocusVisible` for Shadow DOM support. * Add `useInteractOutside` tests. * Add test for use case mentioned in issue #1472. * Add tests for `usePress` hook. * Update the fix for `useInteractOutside` to use simpler one. * Update `useOverlay` to use composedPath. * Tests refactor. * Revert `useOverlay` changes as it works correctly without these changes. * Fix types. * Fix types. * lint. * lint. * Fix failing tests. * Fix failing tests. * Fix failing tests. * Test CI * Test CI * Fix shadow DOM tests * Fix shadow DOM tests. * Fix CI? * Fix CI? * Fix CI? * Re-add commented test. * Update `getRootNode` to handle iframes as well, and everything that `getOwnerDocument` used to handle. * Fix tests. * Fix tests? * Fix tests? * Fix tests. * Fix tests.? * Fix tests.? * Fix tests.? * Fix tests.? * Apply suggestions from code review Co-authored-by: Robert Snow <[email protected]> * Update packages/@react-aria/interactions/test/usePress.test.js Co-authored-by: Robert Snow <[email protected]> * - Update tests to use `createShadowRoot` util. - Update `getRootNode` to return null for disconnected nodes. - Update `usePress.test.js` shadow DOM test. - Test getting rid of reactDomRenderer. * - Update tests and remove reactCompat. * - Leftover. * - Revert changes to getFocusableTreeWalker. * - Remove casting. * - return null in case element is disconnected in `getRootNode`. * - Casting. * - Update unit test. * - Handle focus movements between shadow DOMs. * - TS fixes. * Update usePress.test.js * Refactors and TS errors. * Update fix. * Remove broken sandbox link. * Refactor `getRootNode` to improve root node handling. * Use `getDeepActiveElement` inside focusSafely.ts to get the active element. * Refactor event listener registration Introduce `createEventListener` function to streamline event listener registration. This enhances readability and maintainability, ensuring consistency across event handling logic. * Remove `ownerDocument` fallback in usePress.ts * Refactor `createEventListener` for type-safe caching. * - Test out the updated getOwnerWindow to fix iframe focus issues. * - Test out the updated getOwnerWindow to fix iframe focus issues. * - Test? * - Revert Focus scope changes, for testing. * - Fix tests? * - Fix tests? * - Fix tests? * - Revert the changes to getRootNode. * - Revert `isElementInScope` as well. * - Test out if instance check failure across context for iframes is what is causing the issue. * - Replace the use of `instanceof` with `nodeType` to correctly identify the node type across contexts. - Revert changes made for `usePress`. * - Fix ESlint errors. * - Update the usages of `instanceof` to use `nodeType` instead. * Update packages/@react-aria/interactions/src/useFocusVisible.ts Co-authored-by: Robert Snow <[email protected]> * - Update the usages of `instanceof` to use `nodeType` instead. - Introduce new helpers `isShadowRoot` and `isDocument`. * - Lint. * - Lint. * - Update `getDeepActiveElement` to accept an optional document or shadowRoot. - Fix an issue where opening any popover, the focus wasn't restored to the trigger element in shadow DOM. * - Add extra unit test for `getDeepActiveElement`. * - Update `getDeepActiveElement` to always rely on `getRootNode`. * - Update `getDeepActiveElement` to always rely on `getRootNode`. * refactor usePress to still have global listeners for cleanup across boundaries * fix lint and test * restore remaining document level listeners * fix tests * fix lint * simplify * Update packages/@react-aria/focus/src/FocusScope.tsx * fix autofocus * minor test updates to preserve test intent * review comments * fix esm test * fix lint * check in speed tests * fix lint * Add feature flag and fix a couple probable bugs * Update NOTICE.txt --------- Co-authored-by: Ritesh Kumar <[email protected]> Co-authored-by: Robert Snow <[email protected]> Co-authored-by: Robert Snow <[email protected]> Co-authored-by: Daniel Lu <[email protected]>
🐛 Bug Report
When used inside a
shadowRoot
theFocusScope
currently has some issues:document.activeElement
will refer to the parent node of the shadowRoot the currently focused element is in. The activeElement needs to be determined recursively traversing (open) shadowRoots (document.activeElement.shadowRoot.activeElement.shadowRoot.activeElement......). This leads to invalid detection whether an element is in scope and also restores focus wrongly.e.target
in focus/blur events is referring to the parent node of the shadowRoot of the focused element.e.composedPath()[0]
can be used to determine the actually focused element inside the custom element. ('composedPath' in e ? e.composedPath()[0] : e.target
)TypeError: Cannot read property 'focus' of null
in theonBlur
ofuseFocusContainment
due toe.target
being null inside the rAF (not sure this is really related to custom element / shadowRoot or a general bugI patched this in our libs using
patch-package
so I'm happy to file a PR if you'd like to support custom elements. I'm not sure how this affects other packages of the spectrum ecosystem and thus wanted to check in prior to opening a PR.🤔 Expected Behavior
FocusRing
can be used with custom elements / shadowRoots😯 Current Behavior
See Bug Report above
💁 Possible Solution
document.activeElement
useactiveElement()
e.target
use'composedPath' in e ? e.composedPath()[0] : e.target
🔦 Context
In our Microfrontend Framework we encapsulate modules inside custom elements. To escape them we use a
portal-root
custom element in the body where all modals will be rendered into. Inside this we useFocusScope
to contain focus.💻 Code Sample
https://codesandbox.io/s/vigilant-hofstadter-3wf4i?file=/src/index.js
🌍 Your Environment
🧢 Your Company/Team
Undisclosed
🕷 Tracking Issue (optional)
n/a
The text was updated successfully, but these errors were encountered: