55
66import { queryOptions , useSuspenseQuery } from "@tanstack/react-query" ;
77import { Navigate , createFileRoute , redirect } from "@tanstack/react-router" ;
8- import { type Ref , useCallback } from "react" ;
8+ import { useEffect , useMemo , useRef } from "react" ;
99import { preload } from "react-dom" ;
1010import { graphql } from "../gql" ;
1111import { graphqlRequest } from "../graphql" ;
@@ -46,9 +46,15 @@ function Plan(): React.ReactElement {
4646 return < Navigate to = "/" replace /> ;
4747 }
4848
49+ const ref = useRef < HTMLIFrameElement > ( null ) ;
50+
4951 // Query the size of the iframe content and set the height
5052 // This will only work where the iframe is served from the same origin
51- const calculateHeight = useCallback ( ( iframe : HTMLIFrameElement ) => {
53+ const calculateHeight = ( ) => {
54+ const iframe = ref . current ;
55+ if ( ! iframe ) {
56+ return ;
57+ }
5258 const height =
5359 iframe . contentWindow ?. document . body . parentElement ?. scrollHeight ;
5460
@@ -57,37 +63,52 @@ function Plan(): React.ReactElement {
5763 } else {
5864 iframe . height = "500px" ;
5965 }
60- } , [ ] ) ;
61-
62- const ref : Ref < HTMLIFrameElement > = useCallback (
63- ( iframe : HTMLIFrameElement | null ) => {
64- if ( ! iframe ) return ;
65- calculateHeight ( iframe ) ;
66-
67- if ( iframe . contentWindow ) {
68- const iframeDocument = iframe . contentWindow . document ;
66+ } ;
6967
70- const observer = new MutationObserver ( ( _mutationsList ) => {
71- calculateHeight ( iframe ) ;
72- } ) ;
68+ const observer = useMemo (
69+ ( ) =>
70+ new MutationObserver ( ( _mutationsList ) => {
71+ // we calculate the height immediately when the observer is triggered
72+ calculateHeight ( ) ;
73+ // then we recalculate the height after a short timeout
74+ // to ensure that any layout changes have settled
75+ setTimeout ( ( ) => {
76+ calculateHeight ( ) ;
77+ } , 1000 ) ;
78+ // n.b. we don't worry about the timeout happening after the component is unmounted
79+ } ) ,
80+ [ ] ,
81+ ) ;
7382
74- observer . observe ( iframeDocument . body , {
75- childList : true ,
76- subtree : true ,
77- attributes : true ,
78- } ) ;
83+ useEffect ( ( ) => {
84+ const iframe = ref . current ;
85+ if ( iframe ) {
86+ attachObserver ( iframe ) ;
87+ }
88+ // Cleanup observer when the component unmounts
89+ return ( ) => observer . disconnect ( ) ;
90+ } , [ ] ) ;
7991
80- return ( ) => observer . disconnect ( ) ;
81- }
82- } ,
83- [ calculateHeight ] ,
84- ) ;
92+ const attachObserver = ( iframe : HTMLIFrameElement ) => {
93+ const iframeBody = iframe . contentWindow ?. document . body ;
94+ if ( ! iframeBody ) {
95+ return ;
96+ }
97+ // calculate the height immediately
98+ calculateHeight ( ) ;
99+ // observe future changes to the body of the iframe
100+ observer . observe ( iframeBody , {
101+ childList : true ,
102+ subtree : true ,
103+ attributes : true ,
104+ } ) ;
105+ } ;
85106
86107 return (
87108 < iframe
88109 title = "iframe" // no proper title as this is experimental feature
89110 ref = { ref }
90- onLoad = { ( e ) => calculateHeight ( e . target as HTMLIFrameElement ) }
111+ onLoad = { ( e ) => attachObserver ( e . target as HTMLIFrameElement ) }
91112 src = { planManagementIframeUri }
92113 scrolling = "no"
93114 />
0 commit comments