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

Controlled useZoomOut #66574

Closed
wants to merge 20 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
70 changes: 61 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,75 @@ export function useZoomOut( zoomOut = true ) {
);
const { isZoomOut } = unlock( useSelect( blockEditorStore ) );

const toggleZoomOnUnmount = useRef( false );
const zoomStateOnMount = useRef( null );
const isZoomedOut = isZoomOut();
const controlZoomLevel = useRef( null );

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

// If they start in a zoomed out state, then do not control the zoom state.
// This user knows about the zoom levels and can toggle it when they want,
// or they have manually toggled it on and want to stay there.
controlZoomLevel.current = ! isZoomOut();

return () => {
if ( isZoomOutOnMount ) {
setZoomLevel( 'auto-scaled' );
} else {
if ( ! controlZoomLevel.current || ! 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();
// If we are in controlled mode and the requested zoom and current zoom states
// are different, toggle the zoom state.
if ( controlZoomLevel.current && zoomOut !== isZoomedOut ) {
// If the requested zoomOut matches the user's manually set zoom state,
// do not toggle the zoom level on unmount.
if ( zoomStateOnMount.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 while
// this hook was mounted, and we should no longer control the zoom state.
if ( isZoomedOut !== zoomOut ) {
// Turn off all automatic zooming control.
controlZoomLevel.current = false;
}

// 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 ] );
}
231 changes: 213 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,198 @@

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 not toggle zoom state between tabs 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 not toggle zoom state between tabs if starting from zoom out

3) [chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should not toggle zoom state between tabs 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 not toggle zoom state between tabs if starting from zoom out

3) [chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should not toggle zoom state between tabs 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 not toggle zoom state between tabs if starting from zoom out

3) [chromium] › site-editor/site-editor-inserter.spec.js:122:2 › Site Editor Inserter › should not toggle zoom state between tabs 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 still be egnaged
await expect( await InserterUtils.getZoomCanvas() ).toBeVisible();

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

// We should stay in zoom out state since it was manually engaged from the start
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 169 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:162:2 › Site Editor Inserter › should not return you to zoom out if manually disengaged

4) [chromium] › site-editor/site-editor-inserter.spec.js:162: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 }) 167 | const blockLibrary = InserterUtils.getBlockLibrary(); 168 | > 169 | await zoomOutButton.click(); | ^ 170 | await expect( await InserterUtils.getZoomCanvas() ).toBeVisible(); 171 | 172 | await inserterButton.click(); at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/site-editor-inserter.spec.js:169: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();
} );
} );

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