diff --git a/packages/widgets/src/blocks/legacy-widget/edit/preview.js b/packages/widgets/src/blocks/legacy-widget/edit/preview.js index 39e4d65a51cb45..66109d62558ae9 100644 --- a/packages/widgets/src/blocks/legacy-widget/edit/preview.js +++ b/packages/widgets/src/blocks/legacy-widget/edit/preview.js @@ -13,36 +13,54 @@ import { Placeholder, Spinner, Disabled } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; export default function Preview( { idBase, instance, isVisible } ) { - const [ iframeHeight, setIframeHeight ] = useState(); + const [ isLoaded, setIsLoaded ] = useState( false ); // 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 ref = useRefEffect( + ( iframe ) => { + // Only set height if the iframe is loaded, + // or it will grow to an unexpected large height in Safari if it's hidden initially. + if ( isLoaded ) { + // If the preview frame has another origin then this won't work. + // One possible solution is to add custom script to call `postMessage` in the preview frame. + // Or, better yet, we migrate away from iframe. + function setHeight() { + // Pick the maximum of these two values to account for margin collapsing. + const height = Math.max( + iframe.contentDocument.documentElement.offsetHeight, + iframe.contentDocument.body.offsetHeight + ); + iframe.style.height = `${ height }px`; + } - const { IntersectionObserver } = iframe.ownerDocument.defaultView; + 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 ); + // 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( + ( [ entry ] ) => { + if ( entry.isIntersecting ) { + setHeight(); + } + }, + { + threshold: 1, + } + ); + intersectionObserver.observe( iframe ); - iframe.addEventListener( 'load', onChange ); + iframe.addEventListener( 'load', setHeight ); - return () => { - iframe.removeEventListener( 'load', onChange ); - }; - }, [] ); + return () => { + intersectionObserver.disconnect(); + iframe.removeEventListener( 'load', setHeight ); + }; + } + }, + [ isLoaded ] + ); return ( <> @@ -53,7 +71,7 @@ export default function Preview( { idBase, instance, isVisible } ) { move the iframe off-screen instead of hiding it because web browsers will not trigger onLoad if the iframe is hidden. */ } - { isVisible && iframeHeight === null && ( + { isVisible && ! isLoaded && ( @@ -62,7 +80,7 @@ export default function Preview( { idBase, instance, isVisible } ) { className={ classnames( 'wp-block-legacy-widget__edit-preview', { - 'is-offscreen': ! isVisible || iframeHeight === null, + 'is-offscreen': ! isVisible || ! isLoaded, } ) } > @@ -84,7 +102,17 @@ export default function Preview( { idBase, instance, isVisible } ) { instance, }, } ) } - height={ iframeHeight || 100 } + onLoad={ ( event ) => { + // To hide the scrollbars of the preview frame for some edge cases, + // such as negative margins in the Gallery Legacy Widget. + // It can't be scrolled anyway. + // TODO: Ideally, this should be fixed in core. + event.target.contentDocument.body.style.overflow = + 'hidden'; + + setIsLoaded( true ); + } } + height={ 100 } />