Compare commits

..
2 Commits
Author SHA1 Message Date
Joel Brock 38f4738eca 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.
2026-06-09 17:14:54 -07:00
Joel Brock 57bd5be4ab Civi extension: register templates/ dir via hook_civicrm_config
The Engagement Report tab page renders templates/CRM/WebformMw/Page/Tab.tpl
via Smarty. Without an explicit template-dir registration, Smarty cannot
locate the file and the tab fails to render. Use CRM_Core_Smarty's
prependTemplateDir() (cleaner than splicing template_dir by hand).

Mirrors the same fix the CiviCRM admin applied on the server so the repo
source no longer drifts from the working deployed state.
2026-06-09 17:14:54 -07:00
2 changed files with 35 additions and 5 deletions
+13
View File
@@ -37,6 +37,19 @@ function _webform_mw_staff_key(): string {
return is_string($env) ? $env : '';
}
/**
* Implements hook_civicrm_config().
*
* Registers this extension's `templates/` directory with Smarty so the
* Engagement Report tab page can locate `CRM/WebformMw/Page/Tab.tpl`.
* Without this hook the page callback runs but Smarty has no idea where
* the template lives.
*/
function webform_mw_civicrm_config(&$config) {
$template = CRM_Core_Smarty::singleton();
$template->prependTemplateDir(__DIR__ . '/templates');
}
/**
* Implements hook_civicrm_xmlMenu().
*
+22 -5
View File
@@ -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]);