focus-trap / focus-trap-react

A React component that traps focus
http://focus-trap.github.io/focus-trap-react/demo/
MIT License
710 stars 70 forks source link

Weird behaviour when used inside shadow DOM. Input loses focus when pressing "Delete" key #1315

Closed iamnottheway closed 1 month ago

iamnottheway commented 1 month ago

Hi!

First off, I want to thank you for making this library. It's really useful!

I'm using this library for my Chrome extension, but I'm facing a very odd behavior. My Extension injects React into a webpage using the ShadowDOM. Just to give you some context on what my extension does - it places a button next to any element (think p tag or li tag) and when the user clicks on the button, it opens up a custom-built popover dialog box (with fixed positioning) just above the button. This popup has an input field.

Now there are a couple of problems I'm facing

  1. When I open the popup box, even though it is in a focus state, it doesn't focus on the input element. Even after specifying the field in initialFocus property. I'm using a ref for the input, but I'm guessing this is because the ref is set to null initially and it doesn't update it again?
  2. Clicking on the input explicitly puts the input in focus, but the issue is that whenever I press "delete" to clear the input, it loses focus and the cursor goes to the parent element. And the parent element is a content editable div (like notion). So it clears the elements in the parent div instead.
  3. Another issue I'm encouraging - when I open the popup and use the tab to navigate the elements, it just ignores the input field. I have a button element inside it that it can focus on.

Here's my code.


 <FocusTrap
  focusTrapOptions={{
    clickOutsideDeactivates: true,
    initialFocus: inputRef?.current,
    onActivate: () => {
      setTrapped(true);
    },
    onDeactivate: () => {
      setTrapped(false);
    },

  active={isOpen}
>
  <div
    style={{
      top: activeBlock.rect.top - 160,
      right: activeBlock.rect.right,
      left: activeBlock.rect.left - 24 * 3,
      bottom: activeBlock.rect.bottom,
      zIndex: activeBlock.zIndex + 1,
    }}
    className="pointer-events-none fixed "
  >
    <div
      style={{ border: isTrapped ? '1px solid red' : '1px solid #fff' }}>

       <button>Test</button
       <input ref={inputRef} type="text"></input>

      </div>

</FocusTrap>

I've tried the shadow root option mentioned in the docs, but I don't think it's the right approach since the button I'm clicking on and the popup is part of the shadow DOM.


   tabbableOptions: {
          getShadowRoot(node) {
            console.log('[otto] tab -', node);
            // if (node === closedShadowHostRef.current) {
            //   return closedShadowHostRef.current;
            // }
          },
        },

Any help in pointing me in the right direction is appreciated! Thank you

edit: one more thing, If I like keep on clicking on the input field, then I can delete its contents. And now if I press tab, I can focus both on the button and input field.

iamnottheway commented 1 month ago

added a video to give more context.

https://github.com/user-attachments/assets/b8f871f1-85a8-424c-9287-418ba9c7de30

iamnottheway commented 1 month ago

Hi! It turns out this issue is unrelated to this library. I tried producing a barebones version of my feature on Codesandbox and saw no problems. I think it has to do with conflicting focus traps on my target website. So I'm closing this issue.

stefcameron commented 1 month ago

@iamnottheway Thank you for being proactive and trying a barebones repro, and then for coming back here and closing this issue after determining focus-trap-react wasn't the source of the problem. I was scratching my head with all sorts of Shadow DOM questions on this one. 🙂 I hope you get this working!

iamnottheway commented 1 month ago

@stefcameron Yeah, I also spent so much time trying to debug this one as well. But it turns out it is Notion specific. I just switched to using a range slider instead of an input field. Works for my case and it's better UX anyway. Thanks for your time :)