Skip to content

Conversation

@Infinite-Null
Copy link
Contributor

@Infinite-Null Infinite-Null commented Feb 7, 2025

What?

Closes #69086

Reset zoom level when editor component unmounts to prevent stale zoom states.

Why?

The zoom state and button remained active after responsive changes, causing inconsistency between the visual zoom effect and the button state. This led to a confusing user experience in which the zoom button appeared pressed, but no zoom effect was visible.

How?

Added a cleanup effect in the ZoomOutToggle component that calls resetZoomLevel when the component unmounts. This ensures the zoom state is properly reset between editor sessions and responsive changes.

Testing Instructions

  1. Open the WordPress site editor
  2. Click the zoom-out button
  3. Resize browser window to a smaller width
  4. Resize browser window back to larger width
  5. Verify the zoom state and button are in sync

Screencast

Screen.Recording.2025-02-07.at.12.27.40.PM.mov

@github-actions
Copy link

github-actions bot commented Feb 7, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: Infinite-Null <[email protected]>
Co-authored-by: Mamaduka <[email protected]>
Co-authored-by: t-hamano <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@Mamaduka Mamaduka added [Type] Bug An existing feature does not function as intended [Feature] Zoom Out labels Feb 7, 2025
@Mamaduka
Copy link
Member

Mamaduka commented Feb 7, 2025

Do you know what part of the code disables zoomout mode for smaller screens?

Controlling a global state-based component's mounted state is tricky and usually causes more bugs. This bug probably has a different root cause; I suggest finding it and fixing the issue there.

@Infinite-Null
Copy link
Contributor Author

Sure @Mamaduka, I’ll check what disables zoom-out on smaller screens and find the root cause.

@Infinite-Null
Copy link
Contributor Author

Infinite-Null commented Feb 12, 2025

Hi @Mamaduka,
I was able to find the root cause and updated my PR. While doing research I found an issue:

The useLayoutEffect hook is triggered when the viewport width is at or above 782px. However, the condition:

const isMediumOrBigger = window.matchMedia('(min-width: 782px)').matches;

does not return false when the viewport width gradually decreases to exactly 782px. This is because the (min-width: 782px) media query does not include 782px itself it applies only to widths greater than 782px.

As a result, when resizing the viewport slowly, the condition remains true even at 782px. However, when the viewport is resized quickly, bypassing the 782px threshold, the condition updates correctly and evaluates to false as expected.

A fix to this would be to change code like:

const isMediumOrBigger = window.matchMedia('(min-width: 783px)').matches;

Or

const isMediumOrBigger = window.width>=782;

Screencast:

Screen.Recording.2025-02-12.at.10.53.59.AM.mov

Should I open an issue about this also I am quite not sure which solution would be better.
I would love to hear your thoughts.

Copy link
Member

@Mamaduka Mamaduka left a comment

Choose a reason for hiding this comment

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

Thanks for investigating this, @Infinite-Null!

I left a a couple of of notes, but unfortunately, I don't have time to test the proposed solution properly.

Pinging @WordPress/gutenberg-core in case someone has time to have a look.

} = useDispatch( editorStore );
const { get: getPreference } = useSelect( preferencesStore );
const registry = useRegistry();
const { resetZoomLevel } = unlock( useDispatch( blockEditorStore ) );
Copy link
Member

Choose a reason for hiding this comment

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

The useDispatch( blockEditorStore ) is also defined above. Let's move and combine them in single call.

const { get: getPreference } = useSelect( preferencesStore );
const registry = useRegistry();
const { resetZoomLevel } = unlock( useDispatch( blockEditorStore ) );
const { isZoomOut } = unlock( select( blockEditorStore ) );
Copy link
Member

Choose a reason for hiding this comment

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

The components shouldn't use global select it doesn't react to store changes.

Suggested change
const { isZoomOut } = unlock( select( blockEditorStore ) );
const { isZoomOut } = unlock( useSelect( blockEditorStore ) );

@Infinite-Null
Copy link
Contributor Author

Hi @Mamaduka,
I have made the necessary changes please take a look at it at your convenience. Thank You!

@t-hamano
Copy link
Contributor

  1. Open the WordPress block editor
  2. Click the zoom out button to activate zoom
  3. Resize the browser window to a smaller width
  4. Resize the browser window back to larger width
  5. Observe that the zoom visual effect is lost but the button remains in a "pressed" state

I'm trying to reproduce this issue on the trunk branch (ce2a651), but for some reason I can't reproduce it.

Observe that the zoom visual effect is lost

Returning it to a wider width will restore the zoom out canvas correctly 🤔 Can you still reproduce this issue in your environment?

8d267d3f50f3ea45b90a09fe75b904b3.mp4

@Infinite-Null
Copy link
Contributor Author

Infinite-Null commented Feb 17, 2025

@t-hamano, Thank you for looking into the issue:
The issue is happening in the site editor. Here is a screencast:

Site Editor:

Screen.Recording.2025-02-17.at.6.08.07.PM.mov

Block Editor:

Screen.Recording.2025-02-17.at.6.08.41.PM.mov

Sorry for adding the following to the testing instructions:

1. Open the WordPress block editor

I have correctly updated the testing instructions in the PR description now!

@t-hamano
Copy link
Contributor

After looking into this issue in detail, I found that this issue first occurred in #67481. This means that I believe that the useScaleCanvas hook is directly related to this issue.

Try changing the browser width with the zoom out mode enabled.

You should see that the value indicated by trigger here is different between the post editor and the site editor.

I haven't looked into it in detail yet, but I think that the fundamental solution would be to fix the useScaleCanvas() hook so that it works consistently and in the same way regardless of the editor.

@Infinite-Null
Copy link
Contributor Author

Infinite-Null commented Feb 18, 2025

Hi @t-hamano,
I further investigated the issue and found that the problem lies in how the Site Editor behaves differently from the Block Editor when crossing the 782px threshold.

As far as I understood, The trigger variable in this useEffect hook determines whether the effect should take effect or not. It helps ensure that the logic only runs when the isZoomedOut state actually changes.

In the Site Editor, when we resize the browser window to 782px breakpoint, the component unmounts and remounts. This doesn't happen in the Block Editor.

When looking at the useScaleCanvas() hook:

useEffect( () => {
		const trigger =
			iframeDocument && previousIsZoomedOut.current !== isZoomedOut;

		previousIsZoomedOut.current = isZoomedOut;

		if ( ! trigger ) {
			return;
		}
		

The logic in this hook is actually correct, but here's what happens:

  1. When the Site Editor unmounts, all refs are reset

  2. When it remounts:

    • previousIsZoomedOut.current initializes to false
    • Initially, trigger evaluates to undefined because iframeDocument isn't loaded yet
    • previousIsZoomedOut.current is updated to match the zoom-out state here (which is true in this case) even though the trigger check fails
  3. On subsequent renders:

    • Both previousIsZoomedOut.current and isZoomedOut are true
    • This makes previousIsZoomedOut.current !== isZoomedOut evaluate to false
    • trigger becomes false because the values are the same
  4. The effect has an early return when trigger is falsy (both when undefined initially and false later)

  5. This prevents the animation and state synchronization logic from running

I am unable to find what is causing this mount and unmount in case of Site Editor.

Screencast:

If trigger is true in case of Site Editor which is correct:

Screen.Recording.2025-02-18.at.1.30.47.PM.mov

Mount and Un-Mount behaviour in block and site editor:

Screen.Recording.2025-02-18.at.1.36.05.PM.mov

Note: I have explicitly set trigger to true for debugging that's why issue seems to be fixed in the video.

Potential solution:

const trigger = iframeDocument &&
			( previousIsZoomedOut.current !== isZoomedOut ||
				( previousIsZoomedOut.current === true &&
					isZoomedOut === true ) );

We can add a check if both previousIsZoomedOut.current and isZoomedOut is true then also trigger the effect. But I am unsure of this solution.

@Infinite-Null
Copy link
Contributor Author

I found the reason for the mounting and unmounting of the site editor. It is because if this particular condition . Rather than making changes for mounting and unmounting of site editor, I think adding condition would be the ideal choice here:

const trigger = iframeDocument &&
			( previousIsZoomedOut.current !== isZoomedOut ||
				( previousIsZoomedOut.current === true &&
					isZoomedOut === true ) );

I would really appreciate your opinion and help on this @t-hamano.

@t-hamano
Copy link
Contributor

@Infinite-Null sorry for the late reply.

In my testing, the problem seems to be resolved in the latest Gutenberg. After resizing the browser width from a smaller width to a larger width, the zoom effect persists.

Can you also check on your side?

352506f103b41aa90253041d14bb7d24.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Zoom Out [Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zoom state and button remain active after responsive changes

3 participants