| @@ -1,29 +1,91 @@ | ||
| 1 | 1 | import React, { useEffect, useRef } from 'react'; |
| 2 | 2 | import UIOverlay from '../UIComponents/UIOverlay'; |
| 3 | -import { formsScriptPayload, hublet } from '../../constants/leadinConfig'; | |
| 4 | -import useFormScript from './hooks/useFormsScript'; | |
| 3 | +import { | |
| 4 | + formsScript, | |
| 5 | + formsScriptPayload, | |
| 6 | + hublet as region, | |
| 7 | +} from '../../constants/leadinConfig'; | |
| 8 | +import PreviewDisabled from '../Common/PreviewDisabled'; | |
| 5 | 9 | |
| 6 | 10 | export default function PreviewForm({ |
| 7 | 11 | portalId, |
| 8 | 12 | formId, |
| 13 | + fullSiteEditor, | |
| 14 | + embedVersion, | |
| 9 | 15 | }: { |
| 10 | 16 | portalId: number; |
| 11 | 17 | formId: string; |
| 18 | + fullSiteEditor?: boolean; | |
| 19 | + embedVersion?: string; | |
| 12 | 20 | }) { |
| 21 | + const isFormV4 = embedVersion === 'v4'; | |
| 22 | + | |
| 13 | 23 | const inputEl = useRef<HTMLDivElement>(null); |
| 14 | - const ready = useFormScript(); | |
| 15 | 24 | |
| 16 | 25 | useEffect(() => { |
| 17 | - if (!ready) { | |
| 18 | - return; | |
| 19 | - } | |
| 20 | 26 | if (inputEl.current) { |
| 27 | + //@ts-expect-error Hubspot global | |
| 28 | + const hbspt = window.parent.hbspt || window.hbspt; | |
| 21 | 29 | inputEl.current.innerHTML = ''; |
| 22 | - const embedScript = document.createElement('script'); | |
| 23 | - embedScript.innerHTML = `hbspt.forms.create({ portalId: '${portalId}', formId: '${formId}', region: '${hublet}', ${formsScriptPayload} });`; | |
| 24 | - inputEl.current.appendChild(embedScript); | |
| 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 | + } | |
| 25 | 83 | } |
| 26 | - }, [formId, portalId, ready, inputEl]); | |
| 84 | + }, [formId, portalId, inputEl, isFormV4]); | |
| 27 | 85 | |
| 28 | - return <UIOverlay ref={inputEl} />; | |
| 86 | + if (fullSiteEditor) { | |
| 87 | + return <PreviewDisabled />; | |
| 88 | + } | |
| 89 | + | |
| 90 | + return <UIOverlay ref={inputEl} id={`hbspt-previewform-${formId}`} />; | |
| 29 | 91 | } |