From 83c8d1c16477ae01cf10008d9471c19e181cb508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Mon, 7 Dec 2020 13:25:28 +0200 Subject: [PATCH] FSE: load content in iframe --- .github/workflows/performance.yml | 7 + lib/client-assets.php | 37 +++ .../components/block-list/block-popover.js | 4 +- .../block-list/block-selection-button.js | 6 + .../src/components/iframe/index.js | 226 ++++++++++++++++++ packages/block-editor/src/components/index.js | 1 + .../src/components/use-resize-canvas/index.js | 17 +- packages/block-editor/src/style.scss | 2 + packages/components/src/popover/index.js | 107 +++++++-- packages/e2e-test-utils/README.md | 4 + packages/e2e-test-utils/src/canvas.js | 6 + packages/e2e-test-utils/src/index.js | 1 + packages/e2e-test-utils/src/inserter.js | 12 +- .../e2e-test-utils/src/show-block-toolbar.js | 6 +- packages/e2e-tests/jest.performance.config.js | 1 + .../experiments/multi-entity-editing.test.js | 17 +- .../experiments/multi-entity-saving.test.js | 3 +- .../specs/experiments/template-part.test.js | 30 +-- .../specs/performance/site-editor.test.js | 7 +- .../src/components/block-editor/index.js | 53 ++-- .../edit-site/src/components/editor/index.js | 24 +- .../src/components/editor/style.scss | 5 +- 22 files changed, 478 insertions(+), 98 deletions(-) create mode 100644 packages/block-editor/src/components/iframe/index.js create mode 100644 packages/e2e-test-utils/src/canvas.js diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 1c6be548c7a51..748ad31e2038e 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -38,3 +38,10 @@ jobs: - name: Run the performance tests run: ./bin/plugin/cli.js perf --ci $GITHUB_SHA master --tests-branch $GITHUB_SHA + + - name: Archive debug artifacts (screenshots, HTML snapshots) + uses: actions/upload-artifact@v2 + if: always() + with: + name: failures-artifacts + path: artifacts diff --git a/lib/client-assets.php b/lib/client-assets.php index fb3b528a56642..e37dabc2cbd7c 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -662,3 +662,40 @@ function gutenberg_extend_block_editor_settings_with_fse_theme_flag( $settings ) return $settings; } add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_fse_theme_flag' ); + +/** + * Sets the editor styles to be consumed by JS. + */ +function gutenberg_extend_block_editor_styles_html() { + $handles = array( + 'wp-block-editor', + 'wp-block-library', + 'wp-edit-blocks', + ); + + $block_registry = WP_Block_Type_Registry::get_instance(); + + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { + if ( ! empty( $block_type->style ) ) { + $handles[] = $block_type->style; + } + + if ( ! empty( $block_type->editor_style ) ) { + $handles[] = $block_type->editor_style; + } + } + + $handles = array_unique( $handles ); + $done = wp_styles()->done; + + ob_start(); + + wp_styles()->done = array(); + wp_styles()->do_items( $handles ); + wp_styles()->done = $done; + + $editor_styles = wp_json_encode( array( 'html' => ob_get_clean() ) ); + + echo ""; +} +add_action( 'admin_footer-toplevel_page_gutenberg-edit-site', 'gutenberg_extend_block_editor_styles_html' ); diff --git a/packages/block-editor/src/components/block-list/block-popover.js b/packages/block-editor/src/components/block-list/block-popover.js index 5bf1e5a12164d..97367b3735f95 100644 --- a/packages/block-editor/src/components/block-list/block-popover.js +++ b/packages/block-editor/src/components/block-list/block-popover.js @@ -167,7 +167,9 @@ function BlockPopover( { : 'top right left'; const stickyBoundaryElement = showEmptyBlockSideInserter ? undefined - : getScrollContainer( node ) || ownerDocument.body; + : getScrollContainer( ownerDocument.defaultView.frameElement ) || + getScrollContainer( node ) || + ownerDocument.body; return ( { + // Search the document for stylesheets targetting the editor canvas. + Array.from( document.styleSheets ).forEach( ( styleSheet ) => { + try { + // May fail for external styles. + // eslint-disable-next-line no-unused-expressions + styleSheet.cssRules; + } catch ( e ) { + return; + } + + const { ownerNode, cssRules } = styleSheet; + + if ( ! cssRules ) { + return; + } + + const isMatch = Array.from( cssRules ).find( + ( { selectorText } ) => + selectorText && + ( selectorText.includes( `.${ BODY_CLASS_NAME }` ) || + selectorText.includes( `.${ BLOCK_PREFIX }` ) ) + ); + + if ( isMatch && ! doc.getElementById( ownerNode.id ) ) { + doc.head.appendChild( ownerNode.cloneNode( true ) ); + } + } ); + }, [] ); +} + +/** + * Bubbles some event types (keydown, keypress, and dragover) to parent document + * document to ensure that the keyboard shortcuts and drag and drop work. + * + * Ideally, we should remove event bubbling in the future. Keyboard shortcuts + * should be context dependent, e.g. actions on blocks like Cmd+A should not + * work globally outside the block editor. + * + * @param {Document} doc Document to attach listeners to. + */ +function useBubbleEvents( doc ) { + useEffect( () => { + const { defaultView } = doc; + const { frameElement } = defaultView; + + function bubbleEvent( event ) { + const prototype = Object.getPrototypeOf( event ); + const constructorName = prototype.constructor.name; + const Constructor = window[ constructorName ]; + + const init = {}; + + for ( const key in event ) { + init[ key ] = event[ key ]; + } + + if ( event instanceof defaultView.MouseEvent ) { + const rect = frameElement.getBoundingClientRect(); + init.clientX += rect.left; + init.clientY += rect.top; + } + + const newEvent = new Constructor( event.type, init ); + const cancelled = ! frameElement.dispatchEvent( newEvent ); + + if ( cancelled ) { + event.preventDefault(); + } + } + + const eventTypes = [ 'keydown', 'keypress', 'dragover' ]; + + for ( const name of eventTypes ) { + doc.addEventListener( name, bubbleEvent ); + } + + return () => { + for ( const name of eventTypes ) { + doc.removeEventListener( name, bubbleEvent ); + } + }; + }, [] ); +} + +/** + * Sets the document direction. + * + * Sets the `editor-styles-wrapper` class name on the body. + * + * Copies the `admin-color-*` class name to the body so that the admin color + * scheme applies to components in the iframe. + * + * @param {Document} doc Document to add class name to. + */ +function useBodyClassName( doc ) { + useEffect( () => { + doc.dir = document.dir; + doc.body.className = BODY_CLASS_NAME; + + for ( const name of document.body.classList ) { + if ( name.startsWith( 'admin-color-' ) ) { + doc.body.classList.add( name ); + } + } + }, [] ); +} + +/** + * Positions the body element so that the resize listener works correctly. We're + * using an absolute position here because the resize listener doesn't seem to + * report shrinking when the position is relative, causing the iframe not to + * shrink when content is removed. + * + * @see https://github.com/FezVrasta/react-resize-aware#usage + * + * @param {Document} doc Document to set styles to. + */ +function useResizeListenerCompat( doc ) { + useEffect( () => { + // Necessary for the resize listener to work correctly. + doc.body.style.position = 'absolute'; + doc.body.style.right = '0'; + doc.body.style.left = '0'; + }, [] ); +} + +/** + * Sets the document head and default styles. + * + * @param {Document} doc Document to set the head for. + * @param {string} head HTML to set as the head. + */ +function useHead( doc, head ) { + useEffect( () => { + doc.head.innerHTML = + // Body margin must be overridable by themes. + '' + + '' + + head; + }, [] ); +} + +function IframeContent( { doc, head, children } ) { + useHead( doc, head ); + useStyleSheetsCompat( doc ); + useBubbleEvents( doc ); + useBodyClassName( doc ); + useResizeListenerCompat( doc ); + return createPortal( children, doc.body ); +} + +export default function Iframe( { children, head, style = {}, ...props } ) { + const [ resizeListener, sizes ] = useResizeObserver(); + const [ contentDocument, setContentDocument ] = useState(); + const ref = useRef(); + + function setDocumentIfReady( doc ) { + const { readyState } = doc; + + if ( readyState === 'interactive' || readyState === 'complete' ) { + setContentDocument( doc ); + } + } + + useEffect( () => { + setDocumentIfReady( ref.current.contentDocument ); + }, [] ); + + function setRef( newRef ) { + ref.current = newRef; + + if ( newRef ) { + setDocumentIfReady( newRef.contentDocument ); + } + } + + return ( + + ); +} diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 3e845204bed82..b9378e2c76f32 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -118,6 +118,7 @@ export { export { default as Warning } from './warning'; export { default as WritingFlow } from './writing-flow'; export { useCanvasClickRedirect as __unstableUseCanvasClickRedirect } from './use-canvas-click-redirect'; +export { default as __unstableIframe } from './iframe'; /* * State Related Components diff --git a/packages/block-editor/src/components/use-resize-canvas/index.js b/packages/block-editor/src/components/use-resize-canvas/index.js index d5134d634aabd..d1ac582f90096 100644 --- a/packages/block-editor/src/components/use-resize-canvas/index.js +++ b/packages/block-editor/src/components/use-resize-canvas/index.js @@ -11,11 +11,15 @@ import { default as useSimulatedMediaQuery } from '../../components/use-simulate /** * Function to resize the editor window. * - * @param {string} deviceType Used for determining the size of the container (e.g. Desktop, Tablet, Mobile) + * @param {string} deviceType Used for determining the size of the container (e.g. Desktop, Tablet, Mobile) + * @param {boolean} __unstableDisableSimulate * * @return {Object} Inline styles to be added to resizable container. */ -export default function useResizeCanvas( deviceType ) { +export default function useResizeCanvas( + deviceType, + __unstableDisableSimulate +) { const [ actualWidth, updateActualWidth ] = useState( window.innerWidth ); useEffect( () => { @@ -69,10 +73,11 @@ export default function useResizeCanvas( deviceType ) { } }; - useSimulatedMediaQuery( - 'resizable-editor-section', - getCanvasWidth( deviceType ) - ); + const width = __unstableDisableSimulate + ? null + : getCanvasWidth( deviceType ); + + useSimulatedMediaQuery( 'resizable-editor-section', width ); return contentInlineStyles( deviceType ); } diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 1ea21ed457452..e4201596a11f1 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -64,3 +64,5 @@ @import "./components/block-toolbar/style.scss"; @import "./components/inserter/style.scss"; @import "./components/preview-options/style.scss"; + +@include wordpress-admin-schemes(); diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js index 40b080f320d61..783a9266f0aa0 100644 --- a/packages/components/src/popover/index.js +++ b/packages/components/src/popover/index.js @@ -42,6 +42,24 @@ const FocusManaged = withConstrainedTabbing( */ const SLOT_NAME = 'Popover'; +function offsetIframe( rect, ownerDocument ) { + const { defaultView } = ownerDocument; + const { frameElement } = defaultView; + + if ( ! frameElement ) { + return rect; + } + + const iframeRect = frameElement.getBoundingClientRect(); + + return new defaultView.DOMRect( + rect.left + iframeRect.left, + rect.top + iframeRect.top, + rect.width, + rect.height + ); +} + function computeAnchorRect( anchorRefFallback, anchorRect, @@ -75,14 +93,20 @@ function computeAnchorRect( // `anchorRef instanceof window.Range` checks will break across document boundaries // such as in an iframe if ( typeof anchorRef?.cloneRange === 'function' ) { - return getRectangleFromRange( anchorRef ); + return offsetIframe( + getRectangleFromRange( anchorRef ), + anchorRef.endContainer.ownerDocument + ); } // Duck-type to check if `anchorRef` is an instance of Element // `anchorRef instanceof window.Element` checks will break across document boundaries // such as in an iframe if ( typeof anchorRef?.getBoundingClientRect === 'function' ) { - const rect = anchorRef.getBoundingClientRect(); + const rect = offsetIframe( + anchorRef.getBoundingClientRect(), + anchorRef.ownerDocument + ); if ( shouldAnchorIncludePadding ) { return rect; @@ -94,11 +118,14 @@ function computeAnchorRect( const { top, bottom } = anchorRef; const topRect = top.getBoundingClientRect(); const bottomRect = bottom.getBoundingClientRect(); - const rect = new window.DOMRect( - topRect.left, - topRect.top, - topRect.width, - bottomRect.bottom - topRect.top + const rect = offsetIframe( + new window.DOMRect( + topRect.left, + topRect.top, + topRect.width, + bottomRect.bottom - topRect.top + ), + top.ownerDocument ); if ( shouldAnchorIncludePadding ) { @@ -199,6 +226,22 @@ function setClass( element, name, toggle ) { } } +function getAnchorDocument( anchor ) { + if ( ! anchor ) { + return; + } + + if ( anchor.endContainer ) { + return anchor.endContainer.ownerDocument; + } + + if ( anchor.top ) { + return anchor.top.ownerDocument; + } + + return anchor.ownerDocument; +} + const Popover = ( { headerTitle, onClose, @@ -363,6 +406,9 @@ const Popover = ( { refresh(); + const { ownerDocument } = containerRef.current; + const { defaultView } = ownerDocument; + /* * There are sometimes we need to reposition or resize the popover that * are not handled by the resize/scroll window events (i.e. CSS changes @@ -370,35 +416,58 @@ const Popover = ( { * * For these situations, we refresh the popover every 0.5s */ - const intervalHandle = window.setInterval( refresh, 500 ); + const intervalHandle = defaultView.setInterval( refresh, 500 ); let rafId; const refreshOnAnimationFrame = () => { - window.cancelAnimationFrame( rafId ); - rafId = window.requestAnimationFrame( refresh ); + defaultView.cancelAnimationFrame( rafId ); + rafId = defaultView.requestAnimationFrame( refresh ); }; // Sometimes a click trigger a layout change that affects the popover // position. This is an opportunity to immediately refresh rather than // at the interval. - window.addEventListener( 'click', refreshOnAnimationFrame ); - window.addEventListener( 'resize', refresh ); - window.addEventListener( 'scroll', refresh, true ); + defaultView.addEventListener( 'click', refreshOnAnimationFrame ); + defaultView.addEventListener( 'resize', refresh ); + defaultView.addEventListener( 'scroll', refresh, true ); + + const anchorDocument = getAnchorDocument( anchorRef ); + + if ( anchorDocument && anchorDocument !== ownerDocument ) { + anchorDocument.defaultView.addEventListener( 'resize', refresh ); + anchorDocument.defaultView.addEventListener( + 'scroll', + refresh, + true + ); + } let observer; if ( __unstableObserveElement ) { - observer = new window.MutationObserver( refresh ); + observer = new defaultView.MutationObserver( refresh ); observer.observe( __unstableObserveElement, { attributes: true } ); } return () => { - window.clearInterval( intervalHandle ); - window.removeEventListener( 'resize', refresh ); - window.removeEventListener( 'scroll', refresh, true ); - window.removeEventListener( 'click', refreshOnAnimationFrame ); - window.cancelAnimationFrame( rafId ); + defaultView.clearInterval( intervalHandle ); + defaultView.removeEventListener( 'resize', refresh ); + defaultView.removeEventListener( 'scroll', refresh, true ); + defaultView.removeEventListener( 'click', refreshOnAnimationFrame ); + defaultView.cancelAnimationFrame( rafId ); + + if ( anchorDocument && anchorDocument !== ownerDocument ) { + anchorDocument.defaultView.removeEventListener( + 'resize', + refresh + ); + anchorDocument.defaultView.removeEventListener( + 'scroll', + refresh, + true + ); + } if ( observer ) { observer.disconnect(); diff --git a/packages/e2e-test-utils/README.md b/packages/e2e-test-utils/README.md index 29e2074224857..6b909bd03b38e 100644 --- a/packages/e2e-test-utils/README.md +++ b/packages/e2e-test-utils/README.md @@ -40,6 +40,10 @@ _Returns_ - `Promise`: Boolean which represents the state of prepublish checks. +# **canvas** + +Gets the editor canvas frame. + # **changeSiteTimezone** Visits general settings page and changes the timezone to the given value. diff --git a/packages/e2e-test-utils/src/canvas.js b/packages/e2e-test-utils/src/canvas.js new file mode 100644 index 0000000000000..965007d432d0e --- /dev/null +++ b/packages/e2e-test-utils/src/canvas.js @@ -0,0 +1,6 @@ +/** + * Gets the editor canvas frame. + */ +export function canvas() { + return page.frames().find( ( f ) => f.name() === 'editor-canvas' ) || page; +} diff --git a/packages/e2e-test-utils/src/index.js b/packages/e2e-test-utils/src/index.js index c70f7fd15f69c..06899b154c983 100644 --- a/packages/e2e-test-utils/src/index.js +++ b/packages/e2e-test-utils/src/index.js @@ -2,6 +2,7 @@ export { activatePlugin } from './activate-plugin'; export { activateTheme } from './activate-theme'; export { arePrePublishChecksEnabled } from './are-pre-publish-checks-enabled'; export { changeSiteTimezone } from './change-site-timezone'; +export { canvas } from './canvas'; export { clearLocalStorage } from './clear-local-storage'; export { clickBlockAppender } from './click-block-appender'; export { clickBlockToolbarButton } from './click-block-toolbar-button'; diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js index c3d31299f3e42..bb4c0b8cce7e2 100644 --- a/packages/e2e-test-utils/src/inserter.js +++ b/packages/e2e-test-utils/src/inserter.js @@ -2,6 +2,7 @@ * Internal dependencies */ import { pressKeyWithModifier } from './press-key-with-modifier'; +import { canvas } from './canvas'; // This selector is written to support the current and old inserter markup // because the performance tests need to be able to run across versions. @@ -57,12 +58,11 @@ export async function toggleGlobalBlockInserter() { * Retrieves the document container by css class and checks to make sure the document's active element is within it */ async function waitForInserterCloseAndContentFocus() { - await page.waitForFunction( () => - document.body - .querySelector( - '.interface-interface-skeleton__content .block-editor-block-list__layout' - ) - .contains( document.activeElement ) + await canvas().waitForFunction( + () => + document.activeElement.closest( + '.block-editor-block-list__layout' + ) !== null ); } diff --git a/packages/e2e-test-utils/src/show-block-toolbar.js b/packages/e2e-test-utils/src/show-block-toolbar.js index f1ac720e028d4..cda28f80d3c66 100644 --- a/packages/e2e-test-utils/src/show-block-toolbar.js +++ b/packages/e2e-test-utils/src/show-block-toolbar.js @@ -3,7 +3,9 @@ * Call this function to reveal it. */ export async function showBlockToolbar() { + const content = await page.$( '.interface-interface-skeleton__content' ); + const { x, y } = await content.boundingBox(); // Move the mouse to disable the isTyping mode - await page.mouse.move( 50, 50 ); - await page.mouse.move( 100, 100 ); + await page.mouse.move( x + 50, y + 50 ); + await page.mouse.move( x + 100, y + 100 ); } diff --git a/packages/e2e-tests/jest.performance.config.js b/packages/e2e-tests/jest.performance.config.js index a13d06b943c95..780aab42d51fb 100644 --- a/packages/e2e-tests/jest.performance.config.js +++ b/packages/e2e-tests/jest.performance.config.js @@ -3,6 +3,7 @@ module.exports = { testMatch: [ '**/performance/*.test.js' ], setupFiles: [ '/config/gutenberg-phase.js' ], setupFilesAfterEnv: [ + '/config/setup-debug-artifacts.js', '/config/setup-performance-test.js', '@wordpress/jest-console', '@wordpress/jest-puppeteer-axe', diff --git a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js index 3a8e75b31158c..914602847f027 100644 --- a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js +++ b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js @@ -8,6 +8,7 @@ import { publishPost, trashAllPosts, activateTheme, + canvas, } from '@wordpress/e2e-test-utils'; import { addQueryArgs } from '@wordpress/url'; @@ -21,7 +22,7 @@ const visitSiteEditor = async () => { page: 'gutenberg-edit-site', } ).slice( 1 ); await visitAdminPage( 'admin.php', query ); - await page.waitForSelector( '.edit-site-visual-editor' ); + await page.waitForSelector( '.edit-site-visual-editor iframe' ); }; const clickTemplateItem = async ( menus, itemName ) => { @@ -50,8 +51,8 @@ const createTemplatePart = async ( await page.keyboard.type( templatePartName ); }; -const editTemplatePart = async ( textToAdd, isNested = false ) => { - await page.click( +const editTemplatePart = async ( textToAdd, isNested = false, p = page ) => { + await p.click( `${ isNested ? '.wp-block-template-part .wp-block-template-part' @@ -146,13 +147,13 @@ describe( 'Multi-entity editor states', () => { await clickTemplateItem( 'Template Parts', 'header' ); // Wait for blocks to load. - await page.waitForSelector( '.wp-block' ); + await canvas().waitForSelector( '.wp-block' ); expect( await isEntityDirty( 'header' ) ).toBe( false ); expect( await isEntityDirty( 'front-page' ) ).toBe( false ); // Switch back and make sure it is still clean. await clickTemplateItem( 'Templates', 'Front Page' ); - await page.waitForSelector( '.wp-block' ); + await canvas().waitForSelector( '.wp-block' ); expect( await isEntityDirty( 'header' ) ).toBe( false ); expect( await isEntityDirty( 'front-page' ) ).toBe( false ); @@ -186,7 +187,7 @@ describe( 'Multi-entity editor states', () => { await visitSiteEditor(); // Wait for site editor to load. - await page.waitForSelector( + await canvas().waitForSelector( '.wp-block-template-part .block-editor-block-list__layout' ); @@ -217,7 +218,7 @@ describe( 'Multi-entity editor states', () => { } ); it( 'should only dirty the child when editing the child', async () => { - await page.click( + await canvas().click( '.wp-block-template-part .wp-block[data-type="core/paragraph"]' ); await page.keyboard.type( 'Some more test words!' ); @@ -228,7 +229,7 @@ describe( 'Multi-entity editor states', () => { } ); it( 'should only dirty the nested entity when editing the nested entity', async () => { - await page.click( + await canvas().click( '.wp-block-template-part .wp-block-template-part .wp-block[data-type="core/paragraph"]' ); await page.keyboard.type( 'Nested test words!' ); diff --git a/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js b/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js index 1cb3f8aaf773b..4682ec9e7755c 100644 --- a/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js +++ b/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js @@ -8,6 +8,7 @@ import { visitAdminPage, trashAllPosts, activateTheme, + canvas, } from '@wordpress/e2e-test-utils'; import { addQueryArgs } from '@wordpress/url'; @@ -200,7 +201,7 @@ describe( 'Multi-entity save flow', () => { await navigationPanel.close(); // Click the first block so that the template part inserts in the right place. - const firstBlock = await page.$( '.wp-block' ); + const firstBlock = await canvas().$( '.wp-block' ); await firstBlock.click(); // Insert something to dirty the editor. diff --git a/packages/e2e-tests/specs/experiments/template-part.test.js b/packages/e2e-tests/specs/experiments/template-part.test.js index e531067f277aa..7a25548ac18e5 100644 --- a/packages/e2e-tests/specs/experiments/template-part.test.js +++ b/packages/e2e-tests/specs/experiments/template-part.test.js @@ -11,6 +11,7 @@ import { getAllBlocks, selectBlockByClientId, clickBlockToolbarButton, + canvas, } from '@wordpress/e2e-test-utils'; import { addQueryArgs } from '@wordpress/url'; @@ -39,7 +40,7 @@ describe( 'Template Part', () => { page: 'gutenberg-edit-site', } ).slice( 1 ) ); - await page.waitForSelector( '.edit-site-visual-editor' ); + await page.waitForSelector( '.edit-site-visual-editor iframe' ); } ); async function updateHeader( content ) { @@ -89,7 +90,7 @@ describe( 'Template Part', () => { } async function assertParagraphInTemplatePart( content ) { - const paragraphInTemplatePart = await page.waitForXPath( + const paragraphInTemplatePart = await canvas().waitForXPath( `//*[@data-type="core/template-part"][//p[text()="${ content }"]]` ); expect( paragraphInTemplatePart ).not.toBeNull(); @@ -105,7 +106,7 @@ describe( 'Template Part', () => { it( 'Should detach blocks from template part', async () => { await updateHeader( 'Header Template Part 456' ); - const initialTemplateParts = await page.$$( + const initialTemplateParts = await canvas().$$( '.wp-block-template-part' ); @@ -115,16 +116,17 @@ describe( 'Template Part', () => { ( block ) => block.name === 'core/template-part' ); await selectBlockByClientId( headerBlock.clientId ); + + // Detach blocks from template part using ellipsis menu. + await triggerEllipsisMenuItem( 'Detach blocks from template part' ); + // TODO: Remove when toolbar supports text fields expect( console ).toHaveWarnedWith( 'Using custom components as toolbar controls is deprecated. Please use ToolbarItem or ToolbarButton components instead. See: https://developer.wordpress.org/block-editor/components/toolbar-button/#inside-blockcontrols' ); - // Detach blocks from template part using ellipsis menu. - await triggerEllipsisMenuItem( 'Detach blocks from template part' ); - // Verify there is one less template part on the page. - const finalTemplateParts = await page.$$( + const finalTemplateParts = await canvas().$$( '.wp-block-template-part' ); expect( @@ -132,15 +134,15 @@ describe( 'Template Part', () => { ).toBe( 1 ); // Verify content of the template part is still present. - const [ expectedContent ] = await page.$x( + const [ expectedContent ] = await canvas().$x( '//p[contains(text(), "Header Template Part 456")]' ); expect( expectedContent ).not.toBeUndefined(); } ); it( 'Should convert selected block to template part', async () => { - await page.waitForSelector( '.wp-block-template-part' ); - const initialTemplateParts = await page.$$( + await canvas().waitForSelector( '.wp-block-template-part' ); + const initialTemplateParts = await canvas().$$( '.wp-block-template-part' ); @@ -162,7 +164,7 @@ describe( 'Template Part', () => { ); // Verify there is 1 more template part on the page than previously. - const finalTemplateParts = await page.$$( + const finalTemplateParts = await canvas().$$( '.wp-block-template-part' ); expect( @@ -171,8 +173,8 @@ describe( 'Template Part', () => { } ); it( 'Should convert multiple selected blocks to template part', async () => { - await page.waitForSelector( '.wp-block-template-part' ); - const initialTemplateParts = await page.$$( + await canvas().waitForSelector( '.wp-block-template-part' ); + const initialTemplateParts = await canvas().$$( '.wp-block-template-part' ); @@ -206,7 +208,7 @@ describe( 'Template Part', () => { ); // Verify there is 1 more template part on the page than previously. - const finalTemplateParts = await page.$$( + const finalTemplateParts = await canvas().$$( '.wp-block-template-part' ); expect( diff --git a/packages/e2e-tests/specs/performance/site-editor.test.js b/packages/e2e-tests/specs/performance/site-editor.test.js index c0c076eafe16a..8bd6c25d700bc 100644 --- a/packages/e2e-tests/specs/performance/site-editor.test.js +++ b/packages/e2e-tests/specs/performance/site-editor.test.js @@ -11,6 +11,7 @@ import { trashAllPosts, visitAdminPage, activateTheme, + canvas, } from '@wordpress/e2e-test-utils'; import { addQueryArgs } from '@wordpress/url'; @@ -49,7 +50,11 @@ describe( 'Site Editor Performance', () => { while ( i-- ) { const startTime = new Date(); await page.reload(); - await page.waitForSelector( '.wp-block', { timeout: 120000 } ); + await page.waitForSelector( '.edit-site-visual-editor iframe', { + timeout: 120000, + } ); + await canvas().waitForSelector( '.wp-block', { timeout: 120000 } ); + results.load.push( new Date() - startTime ); } diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index 61313977e3336..45e15e23101f1 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; -import { useCallback, useRef } from '@wordpress/element'; +import { useCallback } from '@wordpress/element'; import { useEntityBlockEditor } from '@wordpress/core-data'; import { BlockEditorProvider, @@ -10,10 +10,14 @@ import { __experimentalLinkControl, BlockInspector, WritingFlow, - ObserveTyping, BlockList, + __experimentalUseResizeCanvas as useResizeCanvas, __unstableUseBlockSelectionClearer as useBlockSelectionClearer, + __unstableUseTypingObserver as useTypingObserver, + __unstableUseEditorStyles as useEditorStyles, + __unstableIframe as Iframe, } from '@wordpress/block-editor'; +import { DropZoneProvider } from '@wordpress/components'; /** * Internal dependencies @@ -22,16 +26,34 @@ import TemplatePartConverter from '../template-part-converter'; import NavigateToLink from '../navigate-to-link'; import { SidebarInspectorFill } from '../sidebar'; +function Canvas( { body, styles } ) { + useBlockSelectionClearer( body ); + useTypingObserver( body ); + useEditorStyles( body, styles ); + + return ( + + + + + + ); +} + export default function BlockEditor( { setIsInserterOpen } ) { - const { settings, templateType, page } = useSelect( + const { settings, templateType, page, deviceType } = useSelect( ( select ) => { - const { getSettings, getTemplateType, getPage } = select( - 'core/edit-site' - ); + const { + getSettings, + getTemplateType, + getPage, + __experimentalGetPreviewDeviceType, + } = select( 'core/edit-site' ); return { settings: getSettings( setIsInserterOpen ), templateType: getTemplateType(), page: getPage(), + deviceType: __experimentalGetPreviewDeviceType(), }; }, [ setIsInserterOpen ] @@ -41,9 +63,8 @@ export default function BlockEditor( { setIsInserterOpen } ) { templateType ); const { setPage } = useDispatch( 'core/edit-site' ); - const ref = useRef(); - useBlockSelectionClearer( ref ); + const resizedCanvasStyles = useResizeCanvas( deviceType, true ); return ( -
- - - - - -
+ { ( body ) => ( + + ) } +
); } diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 380a1f3d1dae9..0bee13e82359c 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -1,13 +1,7 @@ /** * WordPress dependencies */ -import { - useEffect, - useState, - useMemo, - useCallback, - useRef, -} from '@wordpress/element'; +import { useEffect, useState, useMemo, useCallback } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { SlotFillProvider, @@ -19,8 +13,6 @@ import { EntityProvider } from '@wordpress/core-data'; import { BlockContextProvider, BlockBreadcrumb, - __unstableUseEditorStyles as useEditorStyles, - __experimentalUseResizeCanvas as useResizeCanvas, __experimentalLibrary as Library, } from '@wordpress/block-editor'; import { @@ -56,7 +48,6 @@ function Editor() { const { isFullscreenActive, isInserterOpen, - deviceType, sidebarIsOpened, settings, entityId, @@ -68,7 +59,6 @@ function Editor() { const { isFeatureActive, isInserterOpened, - __experimentalGetPreviewDeviceType, getSettings, getTemplateId, getTemplatePartId, @@ -90,7 +80,6 @@ function Editor() { return { isInserterOpen: isInserterOpened(), isFullscreenActive: isFeatureActive( 'fullscreenMode' ), - deviceType: __experimentalGetPreviewDeviceType(), sidebarIsOpened: !! select( 'core/interface' ).getActiveComplementaryArea( 'core/edit-site' ), @@ -121,8 +110,6 @@ function Editor() { updateEditorSettings( { defaultTemplateTypes } ); }, [ defaultTemplateTypes ] ); - const inlineStyles = useResizeCanvas( deviceType ); - const [ isEntitiesSavedStatesOpen, setIsEntitiesSavedStatesOpen, @@ -169,9 +156,6 @@ function Editor() { }, [ isNavigationOpen ] ); const isMobile = useViewportMatch( 'medium', '<' ); - const ref = useRef(); - - useEditorStyles( ref, settings.styles ); return ( <> @@ -202,7 +186,6 @@ function Editor() { } secondarySidebar={ @@ -259,10 +242,7 @@ function Editor() { /> } content={ -
+
{ template && ( diff --git a/packages/edit-site/src/components/editor/style.scss b/packages/edit-site/src/components/editor/style.scss index 52960903d4143..9c3dd1e0a0d5f 100644 --- a/packages/edit-site/src/components/editor/style.scss +++ b/packages/edit-site/src/components/editor/style.scss @@ -22,7 +22,10 @@ .edit-site-visual-editor { position: relative; - background-color: $white; + + iframe { + background-color: $white; + } } // Ideally we don't need all these nested divs.