Staff report: fix infinite iframe-grow loop when framed in Civi
The framed report posts its content height to the parent so the Civi tab
can resize the iframe to fit. Two pieces interacted badly:
- The root layout sets html.h-full and body.min-h-full, so documentElement
and body heights track the iframe's viewport height.
- The parent template sets iframe.height = postedHeight + 24 every time
a height message arrives.
The combination produced an unbounded feedback loop: parent grows the
iframe by 24px, viewport grows, document height grows, ResizeObserver
fires, we post the new height, parent grows by another 24px. The outer
CiviCRM page scrollbar visibly shrank each cycle.
Fix on the report side (no extension change needed): when framed, override
html height to auto and body min-height to 0 so the document decouples
from the viewport. Observe body (the actual content), measure
body.scrollHeight, and skip posting when the value is unchanged. Original
styles are restored on unmount so route changes back to the standalone
view still work.
This commit is contained in:
@@ -76,22 +76,39 @@ export function StaffReportView({
|
||||
// When embedded, post our content height to the parent so the Civi tab's
|
||||
// iframe can resize to fit (no nested scrollbars). The receiving script
|
||||
// lives in the WebForm-mw Civi extension's tab template.
|
||||
//
|
||||
// The root layout sets `html.h-full` and `body.min-h-full`, which tie
|
||||
// document height to the iframe's viewport height. Combined with the
|
||||
// parent setting `iframe.height = postedHeight + 24` on every message,
|
||||
// that creates an unbounded feedback loop (viewport grows -> measured
|
||||
// height grows -> parent grows the iframe -> repeat). Inside the iframe
|
||||
// we decouple html/body from the viewport, measure `body.scrollHeight`
|
||||
// (the actual content), observe the body, and skip duplicate posts.
|
||||
useEffect(() => {
|
||||
if (!framed || typeof window === "undefined") return;
|
||||
if (window.parent === window) return;
|
||||
const html = document.documentElement;
|
||||
const body = document.body;
|
||||
const prevHtmlHeight = html.style.height;
|
||||
const prevBodyMinHeight = body.style.minHeight;
|
||||
html.style.height = "auto";
|
||||
body.style.minHeight = "0";
|
||||
let lastHeight = -1;
|
||||
const post = () => {
|
||||
window.parent.postMessage(
|
||||
{ type: "webform-mw-height", height: document.documentElement.scrollHeight },
|
||||
"*",
|
||||
);
|
||||
const h = body.scrollHeight;
|
||||
if (h === lastHeight) return;
|
||||
lastHeight = h;
|
||||
window.parent.postMessage({ type: "webform-mw-height", height: h }, "*");
|
||||
};
|
||||
post();
|
||||
const ro = new ResizeObserver(post);
|
||||
ro.observe(document.documentElement);
|
||||
ro.observe(body);
|
||||
window.addEventListener("load", post);
|
||||
return () => {
|
||||
ro.disconnect();
|
||||
window.removeEventListener("load", post);
|
||||
html.style.height = prevHtmlHeight;
|
||||
body.style.minHeight = prevBodyMinHeight;
|
||||
};
|
||||
}, [framed, load]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user