Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track if we programmatically changed zoom states for the user #66381

Open
wants to merge 19 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions packages/block-editor/src/hooks/use-zoom-out.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import { useEffect, useRef } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -21,23 +21,50 @@ export function useZoomOut( zoomOut = true ) {
);
const { isZoomOut } = unlock( useSelect( blockEditorStore ) );

useEffect( () => {
const isZoomOutOnMount = isZoomOut();
const toggleZoomOnUnmount = useRef( null );
draganescu marked this conversation as resolved.
Show resolved Hide resolved

// Let this hook know if the zoom state was changed manually.
const manualIsZoomOutCheck = isZoomOut();
draganescu marked this conversation as resolved.
Show resolved Hide resolved

useEffect( () => {
return () => {
if ( isZoomOutOnMount ) {
setZoomLevel( 'auto-scaled' );
} else {
if ( ! toggleZoomOnUnmount.current ) {
return;
}

if ( isZoomOut() ) {
resetZoomLevel();
} else {
setZoomLevel( 'auto-scaled' );
}
};
}, [] );

// This hook should only run when zoomOut changes, so we check for isZoomOut() within the
// hook rather than passing in
useEffect( () => {
const isZoomedOut = isZoomOut();

// Requested zoom and current zoom states are different, so toggle the state.
if ( zoomOut !== isZoomedOut ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is so confusing unless you scroll up to see the function signature 😆

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Not sure what should be renamed. I think the passed zoomOut would be best to rename. Maybe change to setZoomState? Or zoomLevel?

toggleZoomOnUnmount.current = true;

if ( isZoomedOut ) {
resetZoomLevel();
} else {
setZoomLevel( 'auto-scaled' );
}
}
}, [ zoomOut, setZoomLevel, isZoomOut, resetZoomLevel ] );

useEffect( () => {
if ( zoomOut ) {
setZoomLevel( 'auto-scaled' );
} else {
resetZoomLevel();
// If the zoom state changed (isZoomOut) and it does not match the requested zoom
// state (zoomOut), then it means the user manually changed the zoom state and we should
// not toggle the zoom level on unmount.
if ( manualIsZoomOutCheck !== zoomOut ) {
toggleZoomOnUnmount.current = false;
}
}, [ zoomOut, setZoomLevel, resetZoomLevel ] );
}, [ manualIsZoomOutCheck ] );
// Intentionally excluding zoomOut from the dependency array. We want to catch instances where
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add a eslint exclude?

// the zoom out state changes due to user interaction and not due to the hook.
}
228 changes: 210 additions & 18 deletions test/e2e/specs/site-editor/site-editor-inserter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,40 @@ test.describe( 'Site Editor Inserter', () => {
await editor.canvas.locator( 'body' ).click();
} );

test.use( {
InserterUtils: async ( { editor, page }, use ) => {
await use( new InserterUtils( { editor, page } ) );
},
} );

test( 'inserter toggle button should toggle global inserter', async ( {
page,
InserterUtils,
} ) => {
await page.click( 'role=button[name="Block Inserter"i]' );
const inserterButton = InserterUtils.getInserterButton();

await inserterButton.click();

const blockLibrary = InserterUtils.getBlockLibrary();

// Visibility check
await expect(
page.locator( 'role=searchbox[name="Search"i]' )
).toBeVisible();
await page.click( 'role=button[name="Block Inserter"i]' );
await expect( blockLibrary ).toBeVisible();
await inserterButton.click();
//Hidden State check
await expect(
page.locator( 'role=searchbox[name="Search"i]' )
).toBeHidden();
await expect( blockLibrary ).toBeHidden();
} );

// A test for https://github.com/WordPress/gutenberg/issues/43090.
test( 'should close the inserter when clicking on the toggle button', async ( {
page,
editor,
InserterUtils,
} ) => {
const inserterButton = page.getByRole( 'button', {
name: 'Block Inserter',
exact: true,
} );
const blockLibrary = page.getByRole( 'region', {
name: 'Block Library',
} );
const inserterButton = InserterUtils.getInserterButton();
const blockLibrary = InserterUtils.getBlockLibrary();

const beforeBlocks = await editor.getBlocks();

await inserterButton.click();
await blockLibrary.getByRole( 'tab', { name: 'Blocks' } ).click();
await InserterUtils.getBlockLibraryTab( 'Blocks' ).click();
await blockLibrary.getByRole( 'option', { name: 'Buttons' } ).click();

await expect
Expand All @@ -64,4 +65,195 @@ test.describe( 'Site Editor Inserter', () => {

await expect( blockLibrary ).toBeHidden();
} );

test( 'should open the inserter to patterns tab if using zoom out', async ( {
InserterUtils,
} ) => {
const zoomOutButton = InserterUtils.getZoomOutButton();
const inserterButton = InserterUtils.getInserterButton();
const blockLibrary = InserterUtils.getBlockLibrary();

await zoomOutButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

await inserterButton.click();
const patternsTab = InserterUtils.getBlockLibraryTab( 'Patterns' );
await expect( patternsTab ).toHaveAttribute(
'data-active-item',
'true'
);
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

await inserterButton.click();

await expect( blockLibrary ).toBeHidden();

// We should still be in Zoom Out
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();
} );

test( 'should enter zoom out from patterns tab and exit zoom out when closing the inserter', async ( {
InserterUtils,
} ) => {
const inserterButton = InserterUtils.getInserterButton();
const blockLibrary = InserterUtils.getBlockLibrary();

await inserterButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();

const blocksTab = InserterUtils.getBlockLibraryTab( 'Blocks' );
await expect( blocksTab ).toHaveAttribute( 'data-active-item', 'true' );

const patternsTab = InserterUtils.getBlockLibraryTab( 'Patterns' );
await patternsTab.click();
await expect( patternsTab ).toHaveAttribute(
'data-active-item',
'true'
);
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

await inserterButton.click();

await expect( blockLibrary ).toBeHidden();

await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();
} );

test( 'should return you to zoom out if starting from zoom out', async ( {
InserterUtils,
} ) => {
const zoomOutButton = InserterUtils.getZoomOutButton();
const inserterButton = InserterUtils.getInserterButton();
const blockLibrary = InserterUtils.getBlockLibrary();

// Manually enter zoom out
await zoomOutButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

// Open inserter
await inserterButton.click();

// Patterns tab should be active
const patternsTab = InserterUtils.getBlockLibraryTab( 'Patterns' );
await expect( patternsTab ).toHaveAttribute(
'data-active-item',
'true'
);
// Canvas should be zoomed
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

// Select blocks tab
const blocksTab = InserterUtils.getBlockLibraryTab( 'Blocks' );
await blocksTab.click();
await expect( blocksTab ).toHaveAttribute( 'data-active-item', 'true' );
// Zoom out should disengage
await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();

// Close the inserter
await inserterButton.click();
await expect( blockLibrary ).toBeHidden();

// We should return to zoom out since the inserter was opened with
// zoom out engaged, and it was automatically disengaged when selecting
// the blocks tab
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();
} );

// Test for https://github.com/WordPress/gutenberg/issues/66328
test( 'should not return you to zoom out if manually disengaged', async ( {
InserterUtils,
} ) => {
const zoomOutButton = InserterUtils.getZoomOutButton();
const inserterButton = InserterUtils.getInserterButton();
const blockLibrary = InserterUtils.getBlockLibrary();

await zoomOutButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

await inserterButton.click();
const patternsTab = InserterUtils.getBlockLibraryTab( 'Patterns' );
await expect( patternsTab ).toHaveAttribute(
'data-active-item',
'true'
);
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

await zoomOutButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();

// Close the inserter
await inserterButton.click();

await expect( blockLibrary ).toBeHidden();

// We should not return to zoom out since it was manually disengaged
await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();
} );

// Similar test to the above but starting from not zoomed in
test( 'should not toggle zoom state when closing the inserter if the user manually changed zoom state', async ( {
InserterUtils,
} ) => {
const zoomOutButton = InserterUtils.getZoomOutButton();
const inserterButton = InserterUtils.getInserterButton();
const blockLibrary = InserterUtils.getBlockLibrary();

await inserterButton.click();
const patternsTab = InserterUtils.getBlockLibraryTab( 'Patterns' );
await patternsTab.click();
await expect( patternsTab ).toHaveAttribute(
'data-active-item',
'true'
);
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

await zoomOutButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();
// Toggle again to return to zoom state
await zoomOutButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

// Close the inserter
await inserterButton.click();

await expect( blockLibrary ).toBeHidden();

// We should stay in zoomed out state since it was manually engaged
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();
} );
} );

class InserterUtils {
constructor( { editor, page } ) {
this.editor = editor;
this.page = page;
}

getInserterButton() {
return this.page.getByRole( 'button', {
name: 'Block Inserter',
exact: true,
} );
}

getBlockLibrary() {
return this.page.getByRole( 'region', {
name: 'Block Library',
} );
}

getBlockLibraryTab( name ) {
return this.page.getByRole( 'tab', { name } );
}

getZoomOutButton() {
return this.page.getByRole( 'button', {
name: 'Zoom Out',
exact: true,
} );
}

getZoomCanvas() {
return this.page.locator( '.is-zoomed-out' );
}
}
Loading