Skip to content
Snippets Groups Projects
Commit 58fc7cb3 authored by Jesse Baker's avatar Jesse Baker
Browse files

Issue #3492862 by jessebaker, bnjmnm, lauriii, inwerpsel: Overlay and iFrame...

Issue #3492862 by jessebaker, bnjmnm, lauriii, inwerpsel: Overlay and iFrame flicker on updating props values
parent 328b8a3c
No related branches found
No related tags found
1 merge request!936Fix for flicker seemingly caused by swapping display block/none
Pipeline #485979 passed
...@@ -49,9 +49,11 @@ const IFrameSwapper = forwardRef<HTMLIFrameElement, IFrameSwapperProps>( ...@@ -49,9 +49,11 @@ const IFrameSwapper = forwardRef<HTMLIFrameElement, IFrameSwapperProps>(
const swapIFrames = useCallback(() => { const swapIFrames = useCallback(() => {
setWhichActive((current) => (current ? 0 : 1)); setWhichActive((current) => (current ? 0 : 1));
setTimeout(() => { setTimeout(() => {
const { activeIFrame } = getIFrames();
activeIFrame.style.display = '';
setIsReloading(false); setIsReloading(false);
}, 0); }, 0);
}, [setIsReloading]); }, [getIFrames, setIsReloading]);
useEffect(() => { useEffect(() => {
whichActiveRef.current = whichActive; whichActiveRef.current = whichActive;
...@@ -64,6 +66,7 @@ const IFrameSwapper = forwardRef<HTMLIFrameElement, IFrameSwapperProps>( ...@@ -64,6 +66,7 @@ const IFrameSwapper = forwardRef<HTMLIFrameElement, IFrameSwapperProps>(
// Initialize active iframe if not already initialized // Initialize active iframe if not already initialized
if (activeIFrame && !activeIFrame.srcdoc) { if (activeIFrame && !activeIFrame.srcdoc) {
activeIFrame.style.display = 'block';
activeIFrame.srcdoc = srcDocument; activeIFrame.srcdoc = srcDocument;
} }
...@@ -75,6 +78,13 @@ const IFrameSwapper = forwardRef<HTMLIFrameElement, IFrameSwapperProps>( ...@@ -75,6 +78,13 @@ const IFrameSwapper = forwardRef<HTMLIFrameElement, IFrameSwapperProps>(
// Set up load event listener and update content for inactive iframe. Once loaded, it will be swapped in. // Set up load event listener and update content for inactive iframe. Once loaded, it will be swapped in.
if (inactiveIFrame) { if (inactiveIFrame) {
inactiveIFrame.removeEventListener('load', swapIFrames); inactiveIFrame.removeEventListener('load', swapIFrames);
// There is a flicker in Chrome when swapping in an iframe by changing the css display from none to block but the
// flicker does not occur when swapping opacity from 0 to 1.
// Here, we set the inactive iframe's display to block before updating its srcdoc (but the stylesheet still
// maintains opacity: 0, so the iframe remains hidden until it has finished loading)
// Once the iframe loads, its opacity changes to 1, making it visible while the newly inactive iframe becomes display: none.
// This means that when the swap occurs, both iframes are display: block; and we are just swapping the opacity from 0/1
inactiveIFrame.style.display = 'block';
inactiveIFrame.addEventListener('load', swapIFrames); inactiveIFrame.addEventListener('load', swapIFrames);
inactiveIFrame.srcdoc = srcDocument; inactiveIFrame.srcdoc = srcDocument;
} }
......
...@@ -10,12 +10,13 @@ ...@@ -10,12 +10,13 @@
border: 1px solid #ccc; border: 1px solid #ccc;
&[data-xb-swap-active="true"] { &[data-xb-swap-active="true"] {
display: block; opacity: 1;
} }
&[data-xb-swap-active="false"] { &[data-xb-swap-active="false"] {
display: none; display: none;
pointer-events: none !important; pointer-events: none !important;
opacity: 0;
} }
&.interactable { &.interactable {
pointer-events: all; pointer-events: all;
......
import { useCallback, useRef } from 'react'; import { useCallback, useLayoutEffect, useRef } from 'react';
import { useEffect } from 'react';
/** /**
* This hook takes preview iFrame and ensures that the height of the iFrame html element matches the height of the * This hook takes preview iFrame and ensures that the height of the iFrame html element matches the height of the
...@@ -21,9 +20,9 @@ function useSyncIframeHeightToContent( ...@@ -21,9 +20,9 @@ function useSyncIframeHeightToContent(
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
if (previewContainer?.style) { if (previewContainer?.style) {
// set the iFrame container height to the height of the content inside the iFrame. // set the iFrame container height to the height of the content inside the iFrame.
previewContainer.style.height = iframeHTML?.offsetHeight if (iframeHTML?.offsetHeight) {
? `${iframeHTML.offsetHeight}px` previewContainer.style.height = `${iframeHTML.offsetHeight}px`;
: 'auto'; }
previewContainer.style.width = width + 'px'; previewContainer.style.width = width + 'px';
previewContainer.style.minHeight = height + 'px'; previewContainer.style.minHeight = height + 'px';
} }
...@@ -37,7 +36,7 @@ function useSyncIframeHeightToContent( ...@@ -37,7 +36,7 @@ function useSyncIframeHeightToContent(
} }
}, [iframe, height, width, previewContainer]); }, [iframe, height, width, previewContainer]);
useEffect(() => { useLayoutEffect(() => {
if (iframe) { if (iframe) {
const handleLoad = () => { const handleLoad = () => {
const iframeContentDoc = iframe.contentDocument; const iframeContentDoc = iframe.contentDocument;
......
import { useEffect, useState, useCallback, useRef, useMemo } from 'react'; import { useState, useCallback, useRef, useMemo, useLayoutEffect } from 'react';
import { calculateBoundingRect, elemIsVisible } from '@/utils/function-utils'; import { calculateBoundingRect, elemIsVisible } from '@/utils/function-utils';
/** /**
...@@ -67,11 +67,15 @@ function useSyncPreviewElementSize(input: HTMLElement[] | HTMLElement | null) { ...@@ -67,11 +67,15 @@ function useSyncPreviewElementSize(input: HTMLElement[] | HTMLElement | null) {
if (newRect && elements) { if (newRect && elements) {
requestAnimationFrame(() => { requestAnimationFrame(() => {
setElementRect((prevRect) => { setElementRect((prevRect) => {
// Only update if the values have changed so the hook returns the same object preventing components that use
// it from re-rendering. Don't update if the height/width is 0 to stop border flickering
if ( if (
prevRect.top !== newRect.top || (prevRect.top !== newRect.top ||
prevRect.left !== newRect.left || prevRect.left !== newRect.left ||
prevRect.width !== newRect.width || prevRect.width !== newRect.width ||
prevRect.height !== newRect.height prevRect.height !== newRect.height) &&
newRect.width !== 0 &&
newRect.height !== 0
) { ) {
return newRect; return newRect;
} }
...@@ -81,7 +85,7 @@ function useSyncPreviewElementSize(input: HTMLElement[] | HTMLElement | null) { ...@@ -81,7 +85,7 @@ function useSyncPreviewElementSize(input: HTMLElement[] | HTMLElement | null) {
} }
}, []); }, []);
useEffect(() => { useLayoutEffect(() => {
elementsRef.current = elements; elementsRef.current = elements;
recalculateBorder(); recalculateBorder();
}, [elements, recalculateBorder]); }, [elements, recalculateBorder]);
...@@ -150,7 +154,7 @@ function useSyncPreviewElementSize(input: HTMLElement[] | HTMLElement | null) { ...@@ -150,7 +154,7 @@ function useSyncPreviewElementSize(input: HTMLElement[] | HTMLElement | null) {
}); });
}, [recalculateBorder]); }, [recalculateBorder]);
useEffect(() => { useLayoutEffect(() => {
if (elements?.length) { if (elements?.length) {
init(); init();
} }
......
...@@ -271,10 +271,16 @@ describe('Perform CRUD operations on components', () => { ...@@ -271,10 +271,16 @@ describe('Perform CRUD operations on components', () => {
// Add the section that was created earlier in this test. // Add the section that was created earlier in this test.
cy.openLibraryPanel(); cy.openLibraryPanel();
cy.findByText('Sections').click(); cy.findByText('Sections').click();
// This and the other screenshot a few lines below are for debugging, but
// for some reason their presence also causes the test to pass on CI.
cy.screenshot('Add section - see available sections');
cy.get('.primaryPanelContent').within(() => { cy.get('.primaryPanelContent').within(() => {
cy.findByText(sectionName).should('exist'); cy.findByText(sectionName).should('exist');
cy.findByText(sectionName).click(clickDefault); cy.findByText(sectionName).click(clickDefault);
}); });
cy.screenshot('Add section - clicked section to add');
// After adding the section, there should be four Hero components. // After adding the section, there should be four Hero components.
cy.get( cy.get(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment