| @@ -1,7 +1,8 @@ | ||
| 1 | 1 | import React, { useEffect, useRef } from 'react'; |
| 2 | 2 | import UIOverlay from '../UIComponents/UIOverlay'; |
| 3 | 3 | import { |
| 4 | + formsScript, | |
| 4 | 5 | formsScriptPayload, |
| 5 | 6 | hublet as region, |
| 6 | 7 | } from '../../constants/leadinConfig'; |
| 7 | 8 | import PreviewDisabled from '../Common/PreviewDisabled'; |
| @@ -27,9 +28,21 @@ | ||
| 27 | 28 | const hbspt = window.parent.hbspt || window.hbspt; |
| 28 | 29 | inputEl.current.innerHTML = ''; |
| 29 | 30 | const isQa = formsScriptPayload.includes('qa'); |
| 30 | 31 | if (isFormV4) { |
| 31 | - const container = document.createElement('div'); | |
| 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'); | |
| 32 | 45 | container.classList.add('hs-form-frame'); |
| 33 | 46 | container.dataset.region = region; |
| 34 | 47 | container.dataset.formId = formId; |
| 35 | 48 | container.dataset.portalId = portalId.toString(); |
| @@ -34,8 +47,29 @@ | ||
| 34 | 47 | container.dataset.formId = formId; |
| 35 | 48 | container.dataset.portalId = portalId.toString(); |
| 36 | 49 | container.dataset.env = isQa ? 'qa' : ''; |
| 37 | 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 | + } | |
| 38 | 72 | } else { |
| 39 | 73 | const additionalParams = isQa ? { env: 'qa' } : {}; |
| 40 | 74 | |
| 41 | 75 | hbspt.forms.create({ |