Skip to content

Commit

Permalink
fix(app): Allow portal to re-check for root element (#2440)
Browse files Browse the repository at this point in the history
If the portal modal was used during the first render (e.g. for the analytics
opt-in modal), the modal root would not be present in the DOM (since the root
itself is created by the App component), causing the modal to not appear. This
change allows the Portal component to:

- Cache the root instead of getting it from the document on every render
- Retry getting the root after mounting, when we're (pretty) sure the root is
  also actually mounted in the DOM
  • Loading branch information
mcous authored Oct 8, 2018
1 parent 6591104 commit 5930a34
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 18 deletions.
3 changes: 2 additions & 1 deletion app/src/components/controls/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
width: 100%;
padding: 1rem;

&:not(:last-child) {
/* TODO(mc, 2018-10-08): update to :last-of-type in components lib */
&:not(:last-of-type) {
border-bottom: 1px solid var(--c-light-gray);
}
}
Expand Down
42 changes: 25 additions & 17 deletions app/src/components/portal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@
import * as React from 'react'
import ReactDom from 'react-dom'

const PORTAL_ROOT_ID = 'main-modal-portal-root'
type Props = {children: React.Node}

type State = {hasRoot: boolean}

const PORTAL_ROOT_ID = '__otAppModalPortalRoot'
const getPortalRoot = () => global.document.getElementById(PORTAL_ROOT_ID)

export function PortalRoot () {
return <div id={PORTAL_ROOT_ID} />
}

export function getPortalElem () {
return document.getElementById(PORTAL_ROOT_ID)
}

type Props = {children: React.Node}
// the children of Portal are rendered into the PortalRoot if it exists in DOM
export class Portal extends React.Component<Props, State> {
$root: ?Element

/** The children of Portal are rendered into the
* PortalRoot, if the PortalRoot exists in the DOM */
export function Portal (props: Props): React.Node {
const modalRootElem = getPortalElem()
constructor (props: Props) {
super(props)
this.$root = getPortalRoot()
this.state = {hasRoot: !!this.$root}
}

if (!modalRootElem) {
console.error('Modal root is not present, could not render modal')
return null
// on first launch, $portalRoot isn't in DOM; double check once we're mounted
// TODO(mc, 2018-10-08): prerender UI instead
componentDidMount () {
if (!this.$root) {
this.$root = getPortalRoot()
this.setState({hasRoot: !!this.$root})
}
}

return ReactDom.createPortal(
props.children,
modalRootElem
)
render () {
if (!this.$root) return null
return ReactDom.createPortal(this.props.children, this.$root)
}
}

0 comments on commit 5930a34

Please sign in to comment.