-
Notifications
You must be signed in to change notification settings - Fork 616
Add a Portal component for rendering content outside of the current DOM element #1025
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
Conversation
|
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/primer/primer-components/Bex2xGEevgk3cLZujC2tzUqiTr2q |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like this idea, but I have some concerns about adding Portal to the public API right now:
-
Using a Portal with current behaviors that rely on checking for parent DOM elements such as the
closeOnClickOutside
behavior inSelectMenu
breaks that behavior. I don't think we should add Portal to the public API until we can make sure thatPortal
will work seamlessly with all of our components. -
We should add some more examples/instructions to the docs on how to position Portal components.
-
As you mentioned in your PR, it looks like because the
Portal
is registered above theThemeProvider
andBaseStyles
they appear with incorrect styles:
Note that some of the styles still work, that's because within each component we set up a default fallback to our theme in case the consumer of PRC didn't set up a ThemeProvider
.
We need to address this before we add this component to the public API as well.
- There should be few situations where a Portal is needed by a consumer and in those situations, if we do add this to the public API, I think we should add something to the docs explaining that you should only use Portal if you know what you are doing, as a last option. It might also be useful to explain some of the side effects to be aware of when using
Portals
such as loss of access to the theme, inability to write behaviors that depend on traditional DOM structure, and need to accurately position Portal components.
Overall, I think we should address some of the above points, and merge this in for internal use only for now. If we include Portals inside of our overlay components by default, I wonder how often consumers will even need to hook up Portals themselves?
My expectation is that SelectMenu (and related components) will be refactored to use
The
Correct. This is currently by design, although I think changing it would be fine. Right now, the default Regarding the question of whether or not to add Portal to the public API (i.e. exported from
Just to be sure, I take this to mean:
Does that seem correct? |
@T-Hugs yes that seems correct! We should also address the styling issues. Re:
Totally agree, but I think we should exclude Portal from the public API until those components are refactored. How do you feel about keeping this internal only, refactoring all those components during the behaviors epic, then revisiting whether or not Portal should be public at that time? As long as we fix the styling & behavior issues I am not totally against the idea of making it public once it's ready as long as we have the correct guidance needed written up in the docs! 🙌 |
Great, sounds good to me! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just left two more recommendations for the docs, otherwise this looks good to go! #1041 is merged now, might be good to include tests now just so we don't forget to add them later!
docs/content/Portal.mdx
Outdated
This Portal component will render all children into the portal root DOM node instead of as children of this Portal's parent DOM element. This is useful for breaking out of the current stacking context. For example, popup menus and tool tips may need to render on top of (read: covering up) other UI. The best way to guarantee this is to add these elements to top-level DOM, such as directly on document.body. These elements can then be moved to the correct location using absolute positioning. | ||
|
||
## Customizing the portal root | ||
By default, Primer will create a portal root for you as a child of `document.body`. If you would like to specify your own portal root, there are two options: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be updated to say that it's added by default to the BaseStyles
component which all applications should have as high up as possible in their component tree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just left a couple more suggestions for the docs (nit typos), feel free to merge!
Co-authored-by: emplums <emplums@github.com>
Thanks for the suggestions! I'm going to keep |
Portals allow you to create a separation between the logical React component hierarchy and the physical DOM. See the React documentation on portals for an in-depth explanation, or see the rendered documentation for this component.
Closes #1024
Screenshots
Please provide before/after screenshots for any visual changes

Storybook story
Note: An update to this PR changed the behavior so that default portals render into the nearest
<BaseStyles>
, so the screenshot is slightly out-of-date.Merge checklist
index.d.ts
) if necessaryOpen questions
Error: 'PortalProps' is not exported by src/Portal/Portal.tsx, imported by src/Portal/index.ts
.