Skip to content

Commit

Permalink
fix: make <VisualEditing> render in portals consistently (#2345)
Browse files Browse the repository at this point in the history
  • Loading branch information
stipsan authored Dec 21, 2024
1 parent 40baa85 commit c25d0af
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 17 deletions.
2 changes: 0 additions & 2 deletions packages/visual-editing/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export {VERCEL_STEGA_REGEX} from '@vercel/stega'

export const OVERLAY_ID = 'sanity-visual-editing'

/**
* How long to wait after the last subscriber has unsubscribed before resetting the observable and disconnecting the listener
* We want to keep the listener alive for a short while after the last subscriber has unsubscribed to avoid unnecessary reconnects
Expand Down
28 changes: 24 additions & 4 deletions packages/visual-editing/src/ui/VisualEditing.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {useEffect, useState, type FunctionComponent} from 'react'
import {useEffect, useState} from 'react'
import {createPortal} from 'react-dom'
import type {VisualEditingOptions} from '../types'
import {History} from './History'
import {Meta} from './Meta'
Expand All @@ -10,15 +11,30 @@ import {useDatasetMutator} from './useDatasetMutator'
/**
* @public
*/
export const VisualEditing: FunctionComponent<VisualEditingOptions> = (props) => {
const {components, history, refresh, zIndex} = props
export const VisualEditing = (props: VisualEditingOptions & {portal: boolean}): React.ReactNode => {
const {components, history, portal = true, refresh, zIndex} = props

const [inFrame, setInFrame] = useState<boolean | null>(null)
useEffect(() => setInFrame(window.self !== window.top || Boolean(window.opener)), [])

const [portalElement, setPortalElement] = useState<HTMLElement | null>(null)
useEffect(() => {
if (portal === false) return undefined
const node = document.createElement('sanity-visual-editing')
document.documentElement.appendChild(node)
setPortalElement(node)
return () => {
setPortalElement(null)
if (document.documentElement.contains(node)) {
document.documentElement.removeChild(node)
}
}
}, [portal])

const comlink = useComlink(inFrame === true)
useDatasetMutator(comlink)

return (
const children = (
<>
{inFrame !== null && (
<Overlays
Expand All @@ -37,5 +53,9 @@ export const VisualEditing: FunctionComponent<VisualEditingOptions> = (props) =>
)}
</>
)

if (portal === false || !portalElement) return children

return createPortal(children, portalElement)
}
VisualEditing.displayName = 'VisualEditing'
18 changes: 9 additions & 9 deletions packages/visual-editing/src/ui/renderVisualEditing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import {StrictMode} from 'react'
import {createRoot, type Root} from 'react-dom/client'
import {OVERLAY_ID} from '../constants'
import type {VisualEditingOptions} from '../types'
import {VisualEditing} from './VisualEditing'

Expand Down Expand Up @@ -36,13 +35,7 @@ export function renderVisualEditing(
})

if (!node) {
// eslint-disable-next-line no-warning-comments
// @TODO use 'sanity-visual-editing' instead of 'div'
node = document.createElement('div')
// eslint-disable-next-line no-warning-comments
// @TODO after the element is `sanity-visual-editing` instead of `div`, stop setting this ID
node.id = OVERLAY_ID

node = document.createElement('sanity-visual-editing')
// render sanity-visual-editing after closing </body> tag
document.body.parentNode!.insertBefore(node, document.body.nextSibling)
}
Expand All @@ -53,7 +46,14 @@ export function renderVisualEditing(

root.render(
<StrictMode>
<VisualEditing components={components} history={history} refresh={refresh} zIndex={zIndex} />
<VisualEditing
components={components}
history={history}
refresh={refresh}
zIndex={zIndex}
// Disabling the portal, as this function is already making sure the overlays render in the right spot
portal={false}
/>
</StrictMode>,
)
}
3 changes: 1 addition & 2 deletions packages/visual-editing/src/util/findSanityNodes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {decodeSanityNodeData} from '@repo/visual-editing-helpers/csm'
import {OVERLAY_ID} from '../constants'
import type {
ElementNode,
OverlayElement,
Expand Down Expand Up @@ -148,7 +147,7 @@ export function findSanityNodes(
else if (isElementNode(node)) {
// Do not traverse script tags
// Do not traverse the visual editing overlay
if (node.tagName === 'SCRIPT' || node.id === OVERLAY_ID) {
if (node.tagName === 'SCRIPT' || node.tagName === 'SANITY-VISUAL-EDITING') {
continue
}

Expand Down

0 comments on commit c25d0af

Please sign in to comment.