diff --git a/packages/block-library/src/legacy-widget/edit/preview.js b/packages/block-library/src/legacy-widget/edit/preview.js index 5f884aa117fb34..639d7a6d0874e2 100644 --- a/packages/block-library/src/legacy-widget/edit/preview.js +++ b/packages/block-library/src/legacy-widget/edit/preview.js @@ -6,13 +6,44 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +import { useRefEffect } from '@wordpress/compose'; import { addQueryArgs } from '@wordpress/url'; import { useState } from '@wordpress/element'; import { Placeholder, Spinner, Disabled } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; export default function Preview( { idBase, instance, isVisible } ) { - const [ iframeHeight, setIframeHeight ] = useState( null ); + const [ iframeHeight, setIframeHeight ] = useState(); + + // Resize the iframe on either the load event, or when the iframe becomes visible. + const ref = useRefEffect( ( iframe ) => { + function onChange() { + const boundingRect = iframe?.contentDocument?.body?.getBoundingClientRect(); + if ( boundingRect ) { + // Include `top` in the height calculation to avoid the bottom + // of widget previews being cut-off. Most widgets have a + // heading at the top that has top margin, and the `height` + // alone doesn't take that margin into account. + setIframeHeight( boundingRect.top + boundingRect.height ); + } + } + + const { IntersectionObserver } = iframe.ownerDocument.defaultView; + + // Observe for intersections that might cause a change in the height of + // the iframe, e.g. a Widget Area becoming expanded. + const intersectionObserver = new IntersectionObserver( onChange, { + threshold: 1, + } ); + intersectionObserver.observe( iframe ); + + iframe.addEventListener( 'load', onChange ); + + return () => { + iframe.removeEventListener( 'load', onChange ); + }; + }, [] ); + return ( <> { /* @@ -41,6 +72,7 @@ export default function Preview( { idBase, instance, isVisible } ) { load scripts and styles that it needs to run. */ }