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

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
62 changes: 53 additions & 9 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,67 @@ export function useZoomOut( zoomOut = true ) {
);
const { isZoomOut } = unlock( useSelect( blockEditorStore ) );

const toggleZoomOnUnmount = useRef( false );
const userZoomState = useRef( null );
const isZoomedOut = isZoomOut();

useEffect( () => {
const isZoomOutOnMount = isZoomOut();
// Store the user's manually set zoom state on mount.
userZoomState.current = isZoomOut();

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

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

/**
* This hook should only run when the requested zoomOut changes. We don't want to
* update it when isZoomedOut changes.
*/
useEffect( () => {
if ( zoomOut ) {
setZoomLevel( 'auto-scaled' );
} else {
resetZoomLevel();
// 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?

// If the requested zoomOut matches the user's manually set zoom state,
// do not toggle the zoom level on unmount.
if ( userZoomState.current === zoomOut ) {
toggleZoomOnUnmount.current = false;
} else {
toggleZoomOnUnmount.current = true;
}

if ( isZoomedOut ) {
resetZoomLevel();
} else {
setZoomLevel( 'auto-scaled' );
}
}
// Intentionally excluding isZoomedOut so this hook will not recursively udpate.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ zoomOut, setZoomLevel, resetZoomLevel ] );

/**
* This hook tracks if the zoom state was changed manually by the user via clicking
* the zoom out button.
*/
useEffect( () => {
// 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 ( isZoomedOut !== zoomOut ) {
toggleZoomOnUnmount.current = false;
userZoomState.current = zoomOut;
}

// Intentionally excluding zoomOut from the dependency array. We want to catch instances where
// the zoom out state changes due to user interaction and not due to the hook.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ isZoomedOut ] );
}
274 changes: 256 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 @@
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,241 @@

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();

Check failure on line 76 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:69:2 › Site Editor Inserter › should open the inserter to patterns tab if using zoom out

1) [chromium] › site-editor/site-editor-inserter.spec.js:69:2 › Site Editor Inserter › should open the inserter to patterns tab if using zoom out TimeoutError: locator.click: Timeout 10000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Zoom Out', exact: true }) 74 | const blockLibrary = InserterUtils.getBlockLibrary(); 75 | > 76 | await zoomOutButton.click(); | ^ 77 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 78 | 79 | await inserterButton.click(); at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:76:23

Check failure on line 76 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:69:2 › Site Editor Inserter › should open the inserter to patterns tab if using zoom out

1) [chromium] › site-editor/site-editor-inserter.spec.js:69:2 › Site Editor Inserter › should open the inserter to patterns tab if using zoom out Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── TimeoutError: locator.click: Timeout 10000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Zoom Out', exact: true }) 74 | const blockLibrary = InserterUtils.getBlockLibrary(); 75 | > 76 | await zoomOutButton.click(); | ^ 77 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 78 | 79 | await inserterButton.click(); at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:76:23

Check failure on line 76 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:69:2 › Site Editor Inserter › should open the inserter to patterns tab if using zoom out

1) [chromium] › site-editor/site-editor-inserter.spec.js:69:2 › Site Editor Inserter › should open the inserter to patterns tab if using zoom out Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── TimeoutError: locator.click: Timeout 10000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Zoom Out', exact: true }) 74 | const blockLibrary = InserterUtils.getBlockLibrary(); 75 | > 76 | await zoomOutButton.click(); | ^ 77 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 78 | 79 | await inserterButton.click(); at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:76:23
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();

Check failure on line 113 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:95:2 › Site Editor Inserter › should enter zoom out from patterns tab and exit zoom out when closing the inserter

2) [chromium] › site-editor/site-editor-inserter.spec.js:95:2 › Site Editor Inserter › should enter zoom out from patterns tab and exit zoom out when closing the inserter Error: Timed out 5000ms waiting for expect(locator).toBeVisible() Locator: locator('.is-zoomed-out') Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 5000ms - waiting for locator('.is-zoomed-out') 111 | 'true' 112 | ); > 113 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); | ^ 114 | 115 | await inserterButton.click(); 116 | at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:113:55

Check failure on line 113 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:95:2 › Site Editor Inserter › should enter zoom out from patterns tab and exit zoom out when closing the inserter

2) [chromium] › site-editor/site-editor-inserter.spec.js:95:2 › Site Editor Inserter › should enter zoom out from patterns tab and exit zoom out when closing the inserter Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toBeVisible() Locator: locator('.is-zoomed-out') Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 5000ms - waiting for locator('.is-zoomed-out') 111 | 'true' 112 | ); > 113 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); | ^ 114 | 115 | await inserterButton.click(); 116 | at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:113:55

Check failure on line 113 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:95:2 › Site Editor Inserter › should enter zoom out from patterns tab and exit zoom out when closing the inserter

2) [chromium] › site-editor/site-editor-inserter.spec.js:95:2 › Site Editor Inserter › should enter zoom out from patterns tab and exit zoom out when closing the inserter Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toBeVisible() Locator: locator('.is-zoomed-out') Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 5000ms - waiting for locator('.is-zoomed-out') 111 | 'true' 112 | ); > 113 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); | ^ 114 | 115 | await inserterButton.click(); 116 | at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:113:55

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();

Check failure on line 130 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should return you to zoom out if starting from zoom out

3) [chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should return you to zoom out if starting from zoom out TimeoutError: locator.click: Timeout 10000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Zoom Out', exact: true }) 128 | 129 | // Manually enter zoom out > 130 | await zoomOutButton.click(); | ^ 131 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 132 | 133 | // Open inserter at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:130:23

Check failure on line 130 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should return you to zoom out if starting from zoom out

3) [chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should return you to zoom out if starting from zoom out Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── TimeoutError: locator.click: Timeout 10000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Zoom Out', exact: true }) 128 | 129 | // Manually enter zoom out > 130 | await zoomOutButton.click(); | ^ 131 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 132 | 133 | // Open inserter at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:130:23

Check failure on line 130 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should return you to zoom out if starting from zoom out

3) [chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should return you to zoom out if starting from zoom out Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── TimeoutError: locator.click: Timeout 10000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Zoom Out', exact: true }) 128 | 129 | // Manually enter zoom out > 130 | await zoomOutButton.click(); | ^ 131 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 132 | 133 | // Open inserter at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:130:23
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();

Check failure on line 171 in test/e2e/specs/site-editor/site-editor-inserter.spec.js

View workflow job for this annotation

GitHub Actions / Playwright - 6

[chromium] › site-editor/site-editor-inserter.spec.js:164:2 › Site Editor Inserter › should not return you to zoom out if manually disengaged

4) [chromium] › site-editor/site-editor-inserter.spec.js:164:2 › Site Editor Inserter › should not return you to zoom out if manually disengaged TimeoutError: locator.click: Timeout 10000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Zoom Out', exact: true }) 169 | const blockLibrary = InserterUtils.getBlockLibrary(); 170 | > 171 | await zoomOutButton.click(); | ^ 172 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 173 | 174 | await inserterButton.click(); at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:171:23
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();

// Go to patterns tab which should enter zoom out
const patternsTab = InserterUtils.getBlockLibraryTab( 'Patterns' );
await patternsTab.click();
await expect( patternsTab ).toHaveAttribute(
'data-active-item',
'true'
);
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

// Manually toggle zoom out off
await zoomOutButton.click();
await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();

// Manually toggle zoom out again to return to zoomed-in state set by the patterns tab.
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();
} );

// Covers bug found in https://github.com/WordPress/gutenberg/pull/66381#issuecomment-2441540851
test( 'should return to initial zoom out state after switching between multiple tabs', 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' );
const blocksTab = InserterUtils.getBlockLibraryTab( 'Blocks' );
const mediaTab = InserterUtils.getBlockLibraryTab( 'Media' );

// Should start with pattern tab selected in zoom out state
await expect( patternsTab ).toHaveAttribute(
'data-active-item',
'true'
);
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

// Go to blocks tab which should exit zoom out
await blocksTab.click();
await expect( blocksTab ).toHaveAttribute( 'data-active-item', 'true' );
await expect( await InserterUtils.getZoomCanvas() ).toBeHidden();

// Go to media tab which should enter zoom out again since that's the starting state
await mediaTab.click();
await expect( mediaTab ).toHaveAttribute( 'data-active-item', 'true' );
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

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

// We should re-enter zoomed out state since it was the starting point
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