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

When not to use

  • If your needs can already be met with an existing Forge component.

Forge components that Portal

Select

Use Select to create a dropdown list where users select 1 or more options.

Modal

Use Modal to create a container that pops up over the page, forcing users to acknowledge or interact with its contents.

Tooltip

Use Tooltip to create a small floating container that displays helpful text on hover or focus.

Toast

Use Toast to create a temporary notification or message that floats above page content.

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

Portal Basic Share

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

Portal directory in Bitbucket

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.

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';

Props