diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 8dc2f64063c8e..ef260beb85906 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -58,6 +58,11 @@ function InserterMenu( ( select ) => unlock( select( blockEditorStore ) ).isZoomOut(), [] ); + const hasSectionRootClientId = useSelect( + ( select ) => + !! unlock( select( blockEditorStore ) ).getSectionRootClientId(), + [] + ); const [ filterValue, setFilterValue, delayedFilterValue ] = useDebouncedInput( __experimentalFilterValue ); const [ hoveredItem, setHoveredItem ] = useState( null ); @@ -81,7 +86,9 @@ function InserterMenu( const [ selectedTab, setSelectedTab ] = useState( getInitialTab() ); const shouldUseZoomOut = - selectedTab === 'patterns' || selectedTab === 'media'; + hasSectionRootClientId && + ( selectedTab === 'patterns' || selectedTab === 'media' ); + useZoomOut( shouldUseZoomOut && isLargeViewport ); const [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ] = diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js index 51c341f2c1bd1..79199b15b1ad1 100644 --- a/packages/editor/src/components/header/index.js +++ b/packages/editor/src/components/header/index.js @@ -30,6 +30,7 @@ import { PATTERN_POST_TYPE, NAVIGATION_POST_TYPE, } from '../../store/constants'; +import { unlock } from '../../lock-unlock'; const toolbarVariations = { distractionFreeDisabled: { y: '-50px' }, @@ -102,6 +103,13 @@ function Header( { ( hasFixedToolbar && ( ! hasBlockSelection || isBlockToolsCollapsed ) ) ); const hasBackButton = useHasBackButton(); + + const hasSectionRootClientId = useSelect( + ( select ) => + !! unlock( select( blockEditorStore ) ).getSectionRootClientId(), + [] + ); + /* * The edit-post-header classname is only kept for backward compatability * as some plugins might be relying on its presence. @@ -169,9 +177,11 @@ function Header( { forceIsAutosaveable={ forceIsDirty } /> - { canBeZoomedOut && isWideViewport && ( - - ) } + { canBeZoomedOut && + isWideViewport && + hasSectionRootClientId && ( + + ) } { ( isWideViewport || ! showIconLabels ) && ( diff --git a/test/e2e/specs/editor/various/parsing-patterns.spec.js b/test/e2e/specs/editor/various/parsing-patterns.spec.js index d8edc544ffa03..98261804acb58 100644 --- a/test/e2e/specs/editor/various/parsing-patterns.spec.js +++ b/test/e2e/specs/editor/various/parsing-patterns.spec.js @@ -37,9 +37,8 @@ test.describe( 'Parsing patterns', () => { } ); } ); - // Exit zoom out mode and select the inner buttons block to ensure + // Select the inner buttons block to ensure // the correct insertion point is selected. - await page.getByRole( 'button', { name: 'Zoom Out' } ).click(); await editor.selectBlocks( editor.canvas.locator( 'role=document[name="Block: Button"i]' ) ); diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js index 7069b4cec258a..7d2be84187ef6 100644 --- a/test/e2e/specs/editor/various/pattern-overrides.spec.js +++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js @@ -268,12 +268,25 @@ test.describe( 'Pattern Overrides', () => { } ); await editor.setContent( '' ); + await editor.switchEditorTool( 'Design' ); + // Insert a `
` group block. + // In zoomed out and write mode it acts as the section root. + // Inside is a pattern that acts as a section. await editor.insertBlock( { - name: 'core/block', - attributes: { ref: id }, + name: 'core/group', + attributes: { tagName: 'main' }, + innerBlocks: [ + { + name: 'core/block', + attributes: { ref: id }, + }, + ], } ); + const groupBlock = editor.canvas.getByRole( 'document', { + name: 'Block: Group', + } ); const patternBlock = editor.canvas.getByRole( 'document', { name: 'Block: Pattern', } ); @@ -290,14 +303,35 @@ test.describe( 'Pattern Overrides', () => { hasText: 'No Overrides or Binding', } ); - await test.step( 'Zoomed in / Design mode', async () => { - await editor.switchEditorTool( 'Design' ); - // In zoomed in and design mode the pattern block and child blocks - // with bindings are editable. + await test.step( 'Click-through behavior', async () => { + // With the group block selected, all the inner blocks of the pattern + // are inert due to the 'click-through' behavior, that requires the + // pattern block be selected first before its inner blocks are selectable. + await editor.selectBlocks( groupBlock ); await expect( patternBlock ).not.toHaveAttribute( 'inert', 'true' ); + await expect( blockWithOverrides ).toHaveAttribute( + 'inert', + 'true' + ); + await expect( blockWithBindings ).toHaveAttribute( + 'inert', + 'true' + ); + await expect( blockWithoutOverridesOrBindings ).toHaveAttribute( + 'inert', + 'true' + ); + } ); + + await test.step( 'Zoomed in / Design mode', async () => { + await editor.selectBlocks( patternBlock ); + + // Once selected and in zoomed in/design mode the child blocks + // of the pattern with bindings are editable, but unbound + // blocks are inert. await expect( blockWithOverrides ).not.toHaveAttribute( 'inert', 'true' @@ -314,11 +348,16 @@ test.describe( 'Pattern Overrides', () => { await test.step( 'Zoomed in / Write mode - pattern as a section', async () => { await editor.switchEditorTool( 'Write' ); + // The pattern block is still editable as a section. await expect( patternBlock ).not.toHaveAttribute( 'inert', 'true' ); + + // Ensure the pattern block is selected. + await editor.selectBlocks( patternBlock ); + // Child blocks of the pattern with bindings are editable. await expect( blockWithOverrides ).not.toHaveAttribute( 'inert', @@ -336,11 +375,18 @@ test.describe( 'Pattern Overrides', () => { await test.step( 'Zoomed out / Write mode - pattern as a section', async () => { await page.getByLabel( 'Zoom Out' ).click(); - // In zoomed out only the pattern block is editable, as in this scenario it's a section. + // In zoomed out only the pattern block is editable, + // as in this scenario it's a section. await expect( patternBlock ).not.toHaveAttribute( 'inert', 'true' ); + + // Ensure the pattern block is selected before checking the child blocks + // to ensure the click-through behavior isn't interfering. + await editor.selectBlocks( patternBlock ); + + // None of the child blocks are editable in zoomed out mode. await expect( blockWithOverrides ).toHaveAttribute( 'inert', 'true' @@ -357,11 +403,17 @@ test.describe( 'Pattern Overrides', () => { await test.step( 'Zoomed out / Design mode - pattern as a section', async () => { await editor.switchEditorTool( 'Design' ); - // In zoomed out only the pattern block is editable, as in this scenario it's a section. + // In zoomed out only the pattern block is editable, + // as in this scenario it's a section. await expect( patternBlock ).not.toHaveAttribute( 'inert', 'true' ); + + // Ensure the pattern block is selected before checking the child blocks + // to ensure the click-through behavior isn't interfering. + await editor.selectBlocks( patternBlock ); + await expect( blockWithOverrides ).toHaveAttribute( 'inert', 'true' @@ -376,7 +428,7 @@ test.describe( 'Pattern Overrides', () => { ); } ); - // Zoom out and group the pattern. + // Zoom out and group the pattern so that it's no longer a section. await page.getByLabel( 'Zoom Out' ).click(); await editor.selectBlocks( patternBlock ); await editor.clickBlockOptionsMenuItem( 'Group' ); diff --git a/test/e2e/specs/site-editor/zoom-out.spec.js b/test/e2e/specs/site-editor/zoom-out.spec.js index e698a94b7cf0d..77d121e199939 100644 --- a/test/e2e/specs/site-editor/zoom-out.spec.js +++ b/test/e2e/specs/site-editor/zoom-out.spec.js @@ -4,7 +4,8 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); const EDITOR_ZOOM_OUT_CONTENT = ` - + +

First Section Start

@@ -58,6 +59,21 @@ const EDITOR_ZOOM_OUT_CONTENT = `

Fourth Section End

+
+`; + +const EDITOR_ZOOM_OUT_CONTENT_NO_SECTION_ROOT = ` +
+

First Section Start

+ + + +

First Section Center

+ + + +

First Section End

+
`; test.describe( 'Zoom Out', () => { @@ -67,6 +83,8 @@ test.describe( 'Zoom Out', () => { test.afterAll( async ( { requestUtils } ) => { await requestUtils.activateTheme( 'twentytwentyone' ); + await requestUtils.deleteAllTemplates( 'wp_template' ); + await requestUtils.deleteAllTemplates( 'wp_template_part' ); } ); test.beforeEach( async ( { admin } ) => { @@ -215,4 +233,29 @@ test.describe( 'Zoom Out', () => { await expect( thirdSectionEnd ).toBeInViewport(); await expect( fourthSectionStart ).not.toBeInViewport(); } ); + + test( 'Zoom Out cannot be activated when the section root is missing', async ( { + page, + editor, + } ) => { + await editor.setContent( EDITOR_ZOOM_OUT_CONTENT_NO_SECTION_ROOT ); + + // Check that the Zoom Out toggle button is not visible. + await expect( + page.getByRole( 'button', { name: 'Zoom Out' } ) + ).toBeHidden(); + + // Check that activating the Patterns tab in the Inserter does not activate + // Zoom Out. + await page + .getByRole( 'button', { + name: 'Block Inserter', + exact: true, + } ) + .click(); + + await page.getByRole( 'tab', { name: 'Patterns' } ).click(); + + await expect( page.locator( '.is-zoomed-out' ) ).toBeHidden(); + } ); } );