From 7afc6e2fdbf12089e6401a65ba46ecdabc90ff52 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 5 Jun 2025 10:21:17 +0000 Subject: [PATCH] Get mutation observer working --- frontend/src/routes/_account.plan.index.tsx | 73 +++++++++++++-------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/frontend/src/routes/_account.plan.index.tsx b/frontend/src/routes/_account.plan.index.tsx index 5d2977dba..b7ab93351 100644 --- a/frontend/src/routes/_account.plan.index.tsx +++ b/frontend/src/routes/_account.plan.index.tsx @@ -5,7 +5,7 @@ import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import { Navigate, createFileRoute, redirect } from "@tanstack/react-router"; -import { type Ref, useCallback } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { preload } from "react-dom"; import { graphql } from "../gql"; import { graphqlRequest } from "../graphql"; @@ -46,9 +46,15 @@ function Plan(): React.ReactElement { return ; } + const ref = useRef(null); + // Query the size of the iframe content and set the height // This will only work where the iframe is served from the same origin - const calculateHeight = useCallback((iframe: HTMLIFrameElement) => { + const calculateHeight = () => { + const iframe = ref.current; + if (!iframe) { + return; + } const height = iframe.contentWindow?.document.body.parentElement?.scrollHeight; @@ -57,37 +63,52 @@ function Plan(): React.ReactElement { } else { iframe.height = "500px"; } + }; + + const observer = useMemo( + () => + new MutationObserver((_mutationsList) => { + // we calculate the height immediately when the observer is triggered + calculateHeight(); + // then we recalculate the height after a short timeout + // to ensure that any layout changes have settled + setTimeout(() => { + calculateHeight(); + }, 1000); + // n.b. we don't worry about the timeout happening after the component is unmounted + }), + [], + ); + + useEffect(() => { + const iframe = ref.current; + if (iframe) { + attachObserver(iframe); + } + // Cleanup observer when the component unmounts + return () => observer.disconnect(); }, []); - const ref: Ref = useCallback( - (iframe: HTMLIFrameElement | null) => { - if (!iframe) return; - calculateHeight(iframe); - - if (iframe.contentWindow) { - const iframeDocument = iframe.contentWindow.document; - - const observer = new MutationObserver((_mutationsList) => { - calculateHeight(iframe); - }); - - observer.observe(iframeDocument.body, { - childList: true, - subtree: true, - attributes: true, - }); - - return () => observer.disconnect(); - } - }, - [calculateHeight], - ); + const attachObserver = (iframe: HTMLIFrameElement) => { + const iframeBody = iframe.contentWindow?.document.body; + if (!iframeBody) { + return; + } + // calculate the height immediately + calculateHeight(); + // observe future changes to the body of the iframe + observer.observe(iframeBody, { + childList: true, + subtree: true, + attributes: true, + }); + }; return (