| 1 |
import React, { useEffect, useRef } from 'react'; |
| 2 |
import UIOverlay from '../UIComponents/UIOverlay'; |
| 3 |
import { |
| 4 |
formsScript, |
| 5 |
formsScriptPayload, |
| 6 |
hublet as region, |
| 7 |
} from '../../constants/leadinConfig'; |
| 8 |
import PreviewDisabled from '../Common/PreviewDisabled'; |
| 9 |
|
| 10 |
export default function PreviewForm({ |
| 11 |
portalId, |
| 12 |
formId, |
| 13 |
fullSiteEditor, |
| 14 |
embedVersion, |
| 15 |
}: { |
| 16 |
portalId: number; |
| 17 |
formId: string; |
| 18 |
fullSiteEditor?: boolean; |
| 19 |
embedVersion?: string; |
| 20 |
}) { |
| 21 |
const isFormV4 = embedVersion === 'v4'; |
| 22 |
|
| 23 |
const inputEl = useRef<HTMLDivElement>(null); |
| 24 |
|
| 25 |
useEffect(() => { |
| 26 |
if (inputEl.current) { |
| 27 |
//@ts-expect-error Hubspot global |
| 28 |
const hbspt = window.parent.hbspt || window.hbspt; |
| 29 |
inputEl.current.innerHTML = ''; |
| 30 |
const isQa = formsScriptPayload.includes('qa'); |
| 31 |
if (isFormV4) { |
| 32 |
// WP 6.3+ renders the block-editor canvas inside an iframe |
| 33 |
// (iframe[name="editor-canvas"]). The block's DOM lives in that iframe, |
| 34 |
// but our React runs in the top window's JS realm, so the global |
| 35 |
// `document` here is the TOP frame's document, not the iframe's. The v4 |
| 36 |
// forms embed script scans its executing context's local `document` |
| 37 |
// (getElementsByClassName('hs-form-frame') + MutationObserver on |
| 38 |
// document.body) and has no programmatic create() API. So both the |
| 39 |
// placeholder and the embed script must live in the IFRAME's document |
| 40 |
// for hydration to happen. Route everything through the placeholder's |
| 41 |
// ownerDocument to target the correct realm. |
| 42 |
const doc = inputEl.current.ownerDocument; |
| 43 |
|
| 44 |
const container = doc.createElement('div'); |
| 45 |
container.classList.add('hs-form-frame'); |
| 46 |
container.dataset.region = region; |
| 47 |
container.dataset.formId = formId; |
| 48 |
container.dataset.portalId = portalId.toString(); |
| 49 |
container.dataset.env = isQa ? 'qa' : ''; |
| 50 |
inputEl.current.appendChild(container); |
| 51 |
|
| 52 |
// Derive the portal-specific embed URL from the v2 script URL: take its |
| 53 |
// host (origin) so we follow whatever host a PHP filter applies to the |
| 54 |
// v2 URL, and append the v4 embed path. Inject it into the iframe's |
| 55 |
// document so it executes in the iframe window and scans the iframe |
| 56 |
// document where the placeholder is. Running post-mount means doc.body |
| 57 |
// exists, so the script's MutationObserver initializes without the |
| 58 |
// "not a Node" crash seen when it's loaded too early in the iframe |
| 59 |
// head. One copy per iframe window is enough — its MutationObserver |
| 60 |
// hydrates any later placeholders too. |
| 61 |
const v4EmbedUrl = formsScript |
| 62 |
? `${new URL(formsScript).origin}/forms/embed/${portalId}.js` |
| 63 |
: ''; |
| 64 |
const v4AlreadyPresent = |
| 65 |
!v4EmbedUrl || !!doc.querySelector(`script[src="${v4EmbedUrl}"]`); |
| 66 |
if (v4EmbedUrl && !v4AlreadyPresent) { |
| 67 |
const embedScript = doc.createElement('script'); |
| 68 |
embedScript.src = v4EmbedUrl; |
| 69 |
embedScript.defer = true; |
| 70 |
doc.body.appendChild(embedScript); |
| 71 |
} |
| 72 |
} else { |
| 73 |
const additionalParams = isQa ? { env: 'qa' } : {}; |
| 74 |
|
| 75 |
hbspt.forms.create({ |
| 76 |
portalId, |
| 77 |
formId, |
| 78 |
region, |
| 79 |
target: `#${inputEl.current.id}`, |
| 80 |
...additionalParams, |
| 81 |
}); |
| 82 |
} |
| 83 |
} |
| 84 |
}, [formId, portalId, inputEl, isFormV4]); |
| 85 |
|
| 86 |
if (fullSiteEditor) { |
| 87 |
return <PreviewDisabled />; |
| 88 |
} |
| 89 |
|
| 90 |
return <UIOverlay ref={inputEl} id={`hbspt-previewform-${formId}`} />; |
| 91 |
} |
| 92 |
|