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

Use correct ownerDocument when using internal <Portal/> #3594

Merged
merged 3 commits into from
Dec 12, 2024

Conversation

RobinMalfait
Copy link
Member

@RobinMalfait RobinMalfait commented Dec 12, 2024

This PR improves the internal <Portal> component by allowing to pass in a custom ownerDocument.

This fixes an issue if you do something like this:

import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import { useState } from 'react'
import { createPortal } from 'react-dom'

export default function App() {
  let [target, setTarget] = useState(null)

  return (
    <div className="grid min-h-full place-content-center">
      <iframe
        ref={(iframe) => {
          if (!iframe) return
          if (target) return

          let el = iframe.contentDocument.createElement('div')
          iframe.contentDocument.body.appendChild(el)
          setTarget(el)
        }}
        className="h-[50px] w-[75px] border-black bg-white"
      >
        {target && createPortal(<MenuExample />, target)}
      </iframe>
    </div>
  )
}

function MenuExample() {
  return (
    <Menu>
      <MenuButton>Open</MenuButton>
      <MenuItems
        anchor="bottom"
        className="flex min-w-[var(--button-width)] flex-col bg-white shadow"
      >
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/settings">
            Settings
          </a>
        </MenuItem>
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/support">
            Support
          </a>
        </MenuItem>
        <MenuItem>
          <a className="block data-[focus]:bg-blue-100" href="/license">
            License
          </a>
        </MenuItem>
      </MenuItems>
    </Menu>
  )
}

Here is a little reproduction video. The <Menu/> you see is rendered in an <iframe>, the goal is that <MenuItems/> also render inside of the <iframe>.

In the video below we start with the fix where you can see that the items are inside the iframe (and unstyled because I didn't load any styles). The second part of the video is the before, where you can see that the <MenuItems/> escape the <iframe> and are styled. That's not what we want.

Screen.Recording.2024-12-12.at.16.22.01.mov

This allows us to use a custom `ownerDocument`. This is useful if a
`<Menu>` is rendered in an `<iframe>`, then we can use the
`ownerDocument` from the `<MenuButton>` to know where to render the
`<MenuItems>` (when portalling)
Copy link

vercel bot commented Dec 12, 2024

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

Name Status Preview Comments Updated (UTC)
headlessui-react ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 12, 2024 3:44pm
headlessui-vue ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 12, 2024 3:44pm

The `instanceof Node` ia not a good idea because `Node` is different
depending on where it runs (main document vs iframe)
@RobinMalfait RobinMalfait force-pushed the fix/use-owner-document-for-portals branch from 59e88a9 to ad62617 Compare December 12, 2024 15:43
@RobinMalfait RobinMalfait marked this pull request as ready for review December 12, 2024 15:43
@RobinMalfait RobinMalfait enabled auto-merge (squash) December 12, 2024 15:44
@RobinMalfait RobinMalfait merged commit 03fe3c5 into main Dec 12, 2024
8 checks passed
@RobinMalfait RobinMalfait deleted the fix/use-owner-document-for-portals branch December 12, 2024 15:45
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.

1 participant