Portal
Renders contents outside of the normal DOM structure.
The Basics
What it is
A Portal is used to render content to document.body, outside of the normal HTML DOM structure. This is a component with no affiliated designs because the contents of the portal are left completely free-form.
Most importantly, contents within a Portal retain the styling present in the original application when the original application is mounted in a Shadow DOM.
How it works
- The Root component detects whether it is mounted in a shadow DOM and prepares a portal off of document.body for future use.
- The Portal component then attaches its content to this pre-made <div>.
When to use
- When blocking the entire page, like Modal or Lightbox.
- When positioning a floating component, like the dropdown menu of Multiselect
- The @floating-ui/react library may be used to help with positioning in this case.
When not to use
- If your needs can already be met with an existing Forge component.
Forge components that Portal
Use Multiselect to create a dropdown list where users select 1 or more options.
Use SingleSelect to create a dropdown list where users select a single option.
Use Modal to create a container that pops up over the page, forcing users to acknowledge or interact with its contents.
Use Lightbox to create a large container that pops up over the page, forcing users to acknowledge or interact with its contents.
Use Tooltip to create a small floating container that displays helpful text on hover or focus.
Use Toast to create a temporary notification or message that floats above page content.
Use Typeahead to create a field where users enter and edit data more efficiently using suggested text.
How to use
The Portal component must be a child of the Root component, otherwise its contents will never render.
<Root> <Portal> Portal Contents </Portal> </Root>
Style
Design details
There is no styling associated with a Portal. Application developers are on their own to create their own styling for whatever content appears inside the Portal.
Placement and hierarchy
Doesn’t apply to this component.
Content
Doesn’t apply to this component.
Demos
Coding
Developer tips
If access to the HTMLDivElement used as the mount point for the portal is required, there are two ways to get it:
- Supply a ref to Portal
- Use the withPortalData or withPortalDataAndRef higher order components to supply a non-null
portalData: { portalNode }
as a prop to your component.- Note, that this is how Portal is implemented.
You must define custom styles in order to position your content on the page appropriately.
Repository
Implementation links
Implementation details
It is strongly recommended to familiarize yourself with the Forge source code. While this documentation is a best effort to document the intent and usage of a component, sometimes some features only become clear when looking at the source code. Also, looking at Forge's source code may help identify and fix bugs in either your application or Forge itself.
Storybook files
Forge maintains at least one storybook file per component. While the primary audience for these files is typically the Forge team, these storybook files may cover usages of the component not covered by a demo. The storybook for the latest version of forge can be found at go/forge-storybook-lts.
Testing library
Forge strongly encourages using testing-library to write tests for your application.
"The more your tests resemble the way your software is used, the more confidence they can give you."
If you're having trouble testing a Forge component using testing-library, it would be a good idea to see how Forge tests its own components. For the most part, Forge tries to use screen.getByRole as much as it can, as that API provides the best feedback on a11y compliance. Forge discourages the use of document.querySelector and screen.getByTestId as both APIs encourage using implementation details to test your component, and discourage adding roles to your component.
With that being said, many of Forge's components were not built with accessibility in mind. These components do break the recommendations listed above.
Import statements
In Nimbus applications
athenaOne serves the Forge bundle independently from your application's bundle. Importing Forge components directly from '@athena/forge'
takes advantage of this feature.
import { Portal } from '@athena/forge'
In standalone applications
Importing components using the exact path to the module takes advantage of webpack's tree shaking feature. Webpack will include only that module and its dependencies.
import Portal from '@athena/forge/Portal';
To use this import guidance, Typescript applications must use typescript >= 4.7.3
, and should add this setting to their tsconfig.json file:
{"compilerOptions": {"moduleResolution": "Node16",}}
If this setting doesn't work for your application, use this import statement instead:
import Portal from '@athena/forge/dist/Portal';