useMediaQuery: support in-iframe queries by accepting Window instance#76446
useMediaQuery: support in-iframe queries by accepting Window instance#76446tellthemachines merged 9 commits intotrunkfrom
Conversation
|
Size Change: +85 B (0%) Total Size: 8.75 MB
ℹ️ View Unchanged
|
|
Flaky tests detected in 883d5d9. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/23005328955
|
packages/compose/README.md
Outdated
| - _object_ `object`: Object which changes to compare. | ||
| - _prefix_ `string`: Just a prefix to show when console logging. | ||
|
|
||
| ### WindowContext |
There was a problem hiding this comment.
Maybe this can just replace __experimentalWidthProvider?
There was a problem hiding this comment.
That'd be nice. Would leave it for follow-up, though.
| @@ -41,8 +44,14 @@ function getMediaQueryList( query ) { | |||
| * @return {boolean} return value of the media query. | |||
| */ | |||
| export default function useMediaQuery( query ) { | |||
| const win = useContext( WindowContext ); | |||
There was a problem hiding this comment.
To consider: whether it's useMediaQuery's job to consider this context, or expect the component that calls it to fetch the right window and pass it to useMediaQuery
There was a problem hiding this comment.
The problem is that not only do several components use useMediaQuery, but several hooks also depend on it. So, if we wanted to get rid of this idea of implicit context and require components themselves to pass a Window instance, we'd have to touch a lot of pieces across Gutenberg.
So which is worse:
- Making all those edits and risk breaking something in the process?
- Adopting this implicit Window context and risk forgetting an edge case where it was actually/accidentally intended that
useMediaQueryuse the global instance?
There was a problem hiding this comment.
I think #76424 is an instance of that type of edge case. The button appender is being opened from within the canvas but it expects its responsive behaviour to go by the width of the viewport.
There was a problem hiding this comment.
We should also take into account that useMediaQuery is public so there are likely third party plugins using it. For that reason, it's best not to change its default behaviour.
What if instead we defaulted to the current behaviour and optionally allowed passing a window instance? Then we could do so for anything in the canvas that calls this function and wants it to respect canvas sizing.
There was a problem hiding this comment.
Passing the window is exactly what I suggested to @mcsf before, I think I prefer the explicitness of it. If this hook didn't exist at all and people were using the native event handler themselves, they're have to use the right window as well. I think it's good for whoever is using the hook to be aware of what window they use instead of a magic context.
There was a problem hiding this comment.
For #76424 though, ideally that modal/popover should be mounted outside the iframe instead of inside the iframe.
There was a problem hiding this comment.
What if instead we defaulted to the current behaviour and optionally allowed passing a window instance?
Yes, this is what I was trying to articulate here and saying that, though reasonable, it entails touching the consumers.
It's true though that, if we simply make the window argument optional and let it default to the global window, that might satisfy both concerns of back-compat and soundness. Thoughts?
packages/compose/src/private-apis.ts
Outdated
| */ | ||
| import { lock } from './lock-unlock'; | ||
|
|
||
| export const WindowContext = createContext< Window >( window ); |
There was a problem hiding this comment.
Where should this live? I only put it in compose because the dependency chain between producers and consumers is:
block-editor (consumer) -> compose (producer)
b1a0c2e to
40d09d2
Compare
WindowContext
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
40d09d2 to
a76689d
Compare
a76689d to
703ea46
Compare
| deviceType, | ||
| } = useContext( PrivateBlockContext ); | ||
|
|
||
| const defaultViewRef = useRefEffect( ( element ) => { |
There was a problem hiding this comment.
I like the readability of this ref 😄 and can't think of any advantage to useBlockElementRef here.
There are no other places that I know of; the block visibility was the one that was obviously broken (and prompted my initial attempt in #75156.)
There don't seem to be many places where we rely on viewport matching to determine behaviour in the editor canvas; where we do this PR is testing well. For instance, site logo allows resizing only on large viewports, but the size at which the resize handles disappear in this branch seems sensible to me.
| } = useContext( PrivateBlockContext ); | ||
|
|
||
| const defaultViewRef = useRefEffect( ( element ) => { | ||
| if ( element ) { |
There was a problem hiding this comment.
With useRefEffect, element is guaranteed. Maybe you just wanted to change it to a regular ref callback?
| }, | ||
| }; | ||
| }, [ query ] ); | ||
| }, [ view, query ] ); |
There was a problem hiding this comment.
Why is this even memo'ed in the first place?
There was a problem hiding this comment.
I assume it might need to be memo'ed so that subscribe and getValue have stable references when passed in to useSyncExternalStore?
| if ( element ) { | ||
| const { ownerDocument } = element; | ||
| const { defaultView } = ownerDocument; | ||
| defaultViewRef.current = defaultView; |
There was a problem hiding this comment.
I'm not sure I'm following what's happening here. Why are we setting the current property on a function? Also, isn't this the same as doing view: ref.ownerDocument.defaultView below? Either way it doesn't re-render the component, and it looks like re-rendering seems necessary here? In which case we should probably set some state? If we don't set state, It's better practice to pass down the whole ref object so it's always fresh.
There was a problem hiding this comment.
I logged out defaultViewRef.current to check what's going on (this is the post editor):
[useBlockProps] defaultViewRef.current at render time: undefined
[useBlockProps] defaultViewRef.current at render time: Window {window: Window, self: Window, document: document, name: 'editor-canvas', location: Location, …}
Those first two renders are undefined, but it looks like the editor triggers a re-render shortly after mount, which then assigns the iframe's Window.
I didn't observe any issues during the initial render, so it works in practice 😄 But possibly incidentally.
I tried setting state, and there are quite a few more renders:
const [ view, setView ] = useState();
const defaultViewRef = useRefEffect( ( element ) => {
setView( element.ownerDocument.defaultView );
return () => setView( undefined );
}, [] );
But the re-render happening on purpose coz setView is saying "something changed, re-render."
|
I came across this PR while investigating the cause of the incorrect behavior of For most core blocks, the position of the popover is determined by the ❌ trunkThe popover's position changes even though it's not a mobile viewport. 546a88c2e84824c3d51f40b385b04f20.mp4✅ This PRThe popover's position remains constant. 67741cbfc25b99f299387e64c34f391f.mp4 |
andrewserong
left a comment
There was a problem hiding this comment.
This is testing very nicely for me, it feels good and performant in the editor, and I can happily resize the preview and see blocks becoming hidden or visible as expected based on block visibility settings.
Some good questions re: using useRefEffect vs useState. In practice I didn't encounter any issues with the absence of setting state while manually testing (i.e. a lack of re-renders didn't seem to cause any issues for me).
If time is of the essence for this bug fix, would it be worth merging it now and tidying up the useRefEffect later?
|
Came here from #76424. This PR also fixes the issue reported there (at least for me), where
|
Agreed. The window adjusts when resizing the editor, so it shouldn't need any further state updates in order to function. I'm going to go ahead and merge this because it works well and we need the fix in RC1. We can always make quality improvements later if needed. |
|
There was a conflict while trying to cherry-pick the commit to the wp/7.0 branch. Please resolve the conflict manually and create a PR to the wp/7.0 branch. PRs to wp/7.0 are similar to PRs to trunk, but you should base your PR on the wp/7.0 branch instead of trunk. |
|
To resolve the cherry-pick conflict, it might be better to backport #75826 first. This should allow for a clean backport of this PR. However, the conflict in the CHANGELOG file will still need to be resolved manually.
|
Do we know whether that PR is intended to be backported? I don't see any indication of it (no labels at least) |
It's just a reordering of code and doesn't look like backporting it will cause issues so I'll do it anyway. |
) Co-authored-by: tellthemachines <isabel_brison@git.wordpress.org> Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org> Co-authored-by: andrewserong <andrewserong@git.wordpress.org> Co-authored-by: t-hamano <wildworks@git.wordpress.org> Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
) (#76660) Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org> Co-authored-by: andrewserong <andrewserong@git.wordpress.org> Co-authored-by: t-hamano <wildworks@git.wordpress.org> Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>
WindowContextThis updates the pinned hash from the `gutenberg` from `8c78d87453509661a9f28f978ba2c242d515563b` to `487a096a9782ba6110a7686d7b4b2d0c55ed1b06`. The following changes are included: - Disables anchor support for the Page Break block. (WordPress/gutenberg#76434) - WP Admin: Update Connectors screen footer text for consistency. (WordPress/gutenberg#76382) - E2E Tests: Add coverage for AI plugin callout banner on Connectors page (WordPress/gutenberg#76432) - Update sync docs (WordPress/gutenberg#75972) - RTC: Add preference for collaborator notifications (WordPress/gutenberg#76460) - Fix "should undo bold" flaky test (WordPress/gutenberg#76464) - TimePicker: Clamp month day to valid day for month (WordPress/gutenberg#76400) - RTC: Fix error when entity record doesn't have 'meta' property (WordPress/gutenberg#76311) - Navigation: Update close button size. (WordPress/gutenberg#76482) - TemplateContentPanel: fix useSelect warning (WordPress/gutenberg#76421) - DataViews: Add spinner in `DataViewsLayout` in initial load of data (WordPress/gutenberg#76486) (WordPress/gutenberg#76490) - RTC: Fix TypeError in areEditorStatesEqual when selection is undefined (WordPress/gutenberg#76163) - Page/Post Content Focus Mode: Fix insertion into Post Content block (WordPress/gutenberg#76477) - Revisions: use useSubRegistry={false} to fix global store selectors (WordPress/gutenberg#76152) (WordPress/gutenberg#76522) - Fix RTL styling on Connectors, Font Library, and boot-based admin pages (WordPress/gutenberg#76496) - RTC: Auto-register custom taxonomy rest_base values for CRDT sync (WordPress/gutenberg#75983) - RTC: Add a limit for the default provider (WordPress/gutenberg#76437) - Fix RTL styling on AI plugin callout banner (WordPress/gutenberg#76497) - Add command palette trigger button to admin bar (WordPress/gutenberg#75757) - Block Bindings: Remove source items constrained by enums (WordPress/gutenberg#76200) - HTML Block: Remove "unsaved changes" check (WordPress/gutenberg#76086) - CI: Don't build release notes during plugin build workflow for WP Core sync (WordPress/gutenberg#76398) (WordPress/gutenberg#76578) - CI: Simplify strategy matrix in Build Gutenberg Plugin Zip workflow (WordPress/gutenberg#76435) (WordPress/gutenberg#76538) - Media: Add hooks and extension points for client-side media processing (WordPress/gutenberg#74913) - RTC: Fix list sidebar reset during real-time collaboration (WordPress/gutenberg#76025) - RTC: Fix CRDT serialization of nested RichText attributes (WordPress/gutenberg#76597) - RTC: Remove post list lock icon and replace user-specific lock text (WordPress/gutenberg#76322) - Fix HEIC upload error handling and sub-size format (WordPress/gutenberg#76514) - RTC: Fix cursor index sync with rich text formatting (WordPress/gutenberg#76418) - RTC: Allow filtering of `SyncConnectionModal` (WordPress/gutenberg#76554) - RTC: Implement front-end peer limits (WordPress/gutenberg#76565) - Navigation overlay close button may be displayed twice (WordPress/gutenberg#76585) - Site Editor > Templates: fix author filter (WordPress/gutenberg#76625) - Revisions: Show changed block attributes in inspector sidebar (WordPress/gutenberg#76550) - Fix IS_GUTENBERG_PLUGIN env var override in build config (WordPress/gutenberg#76605) - Real Time Collaboration: Introduce filters for the polling intervals. (WordPress/gutenberg#76518) - RTC: Fix RichTextData deserialization (WordPress/gutenberg#76607) - Cross Origin Isolation: Remove `img` from the list of elements that get mutated (WordPress/gutenberg#76618) - RTC: Scroll to collaborator on click (WordPress/gutenberg#76561) - Update changelog link for pull request 11276 (WordPress/gutenberg#76638) - Fix backport changelog filename (WordPress/gutenberg#76651) - Build: Skip non-minified build for WASM-inlined workers (WordPress/gutenberg#76615) - RTC: Change RTC option name (WordPress/gutenberg#76643) - BlockListBlock: fix crash when selectedProps are null (WordPress/gutenberg#75826) - Build: Fix vips worker 404 when SCRIPT_DEBUG is true (WordPress/gutenberg#76657) - useMediaQuery: support in-iframe queries via new `WindowContext` (WordPress/gutenberg#76446) (WordPress/gutenberg#76660) - Refactor admin-ui Page component to use @wordpress/theme tokens and @wordpress/ui layout primitive (WordPress/gutenberg#75963) - Connectors: Improve accessibility (WordPress/gutenberg#76456) - Build: Remove unused JXL WASM module from vips worker (WordPress/gutenberg#76639) - Connectors: fix button size (WordPress/gutenberg#76582) - Compose: Implement useCopyToClipboard and useCopyOnClick with native clipboard API (WordPress/gutenberg#75723) (WordPress/gutenberg#76663) - Connectors: Fetch specific plugin instead of all plugins (WordPress/gutenberg#76594) - Revisions: Add Meta fields diff panel to document sidebar (WordPress/gutenberg#76341) - Loosen client-side media processing requirements (WordPress/gutenberg#76616) - Reduce the added halo for selected block. (WordPress/gutenberg#76619) - Connectors: Add unregisterConnector and upsert support (WordPress/gutenberg#76541) A full list of changes can be found on GitHub: https://github.com/WordPress/gutenberg/compare/8c78d87453509661a9f28f978ba2c242d515563b…487a096a9782ba6110a7686d7b4b2d0c55ed1b06. Log created with: git log --reverse --format="- %s" 8c78d87453509661a9f28f978ba2c242d515563b..487a096a9782ba6110a7686d7b4b2d0c55ed1b06 | sed 's|#\([0-9][0-9]*\)|https://github.com/WordPress/gutenberg/pull/\1|g; /github\.com\/WordPress\/gutenberg\/pull/!d' | pbcopy See #64595. git-svn-id: https://develop.svn.wordpress.org/trunk@62063 602fd350-edb4-49c9-b593-d223f7449a82
This updates the pinned hash from the `gutenberg` from `8c78d87453509661a9f28f978ba2c242d515563b` to `487a096a9782ba6110a7686d7b4b2d0c55ed1b06`. The following changes are included: - Disables anchor support for the Page Break block. (WordPress/gutenberg#76434) - WP Admin: Update Connectors screen footer text for consistency. (WordPress/gutenberg#76382) - E2E Tests: Add coverage for AI plugin callout banner on Connectors page (WordPress/gutenberg#76432) - Update sync docs (WordPress/gutenberg#75972) - RTC: Add preference for collaborator notifications (WordPress/gutenberg#76460) - Fix "should undo bold" flaky test (WordPress/gutenberg#76464) - TimePicker: Clamp month day to valid day for month (WordPress/gutenberg#76400) - RTC: Fix error when entity record doesn't have 'meta' property (WordPress/gutenberg#76311) - Navigation: Update close button size. (WordPress/gutenberg#76482) - TemplateContentPanel: fix useSelect warning (WordPress/gutenberg#76421) - DataViews: Add spinner in `DataViewsLayout` in initial load of data (WordPress/gutenberg#76486) (WordPress/gutenberg#76490) - RTC: Fix TypeError in areEditorStatesEqual when selection is undefined (WordPress/gutenberg#76163) - Page/Post Content Focus Mode: Fix insertion into Post Content block (WordPress/gutenberg#76477) - Revisions: use useSubRegistry={false} to fix global store selectors (WordPress/gutenberg#76152) (WordPress/gutenberg#76522) - Fix RTL styling on Connectors, Font Library, and boot-based admin pages (WordPress/gutenberg#76496) - RTC: Auto-register custom taxonomy rest_base values for CRDT sync (WordPress/gutenberg#75983) - RTC: Add a limit for the default provider (WordPress/gutenberg#76437) - Fix RTL styling on AI plugin callout banner (WordPress/gutenberg#76497) - Add command palette trigger button to admin bar (WordPress/gutenberg#75757) - Block Bindings: Remove source items constrained by enums (WordPress/gutenberg#76200) - HTML Block: Remove "unsaved changes" check (WordPress/gutenberg#76086) - CI: Don't build release notes during plugin build workflow for WP Core sync (WordPress/gutenberg#76398) (WordPress/gutenberg#76578) - CI: Simplify strategy matrix in Build Gutenberg Plugin Zip workflow (WordPress/gutenberg#76435) (WordPress/gutenberg#76538) - Media: Add hooks and extension points for client-side media processing (WordPress/gutenberg#74913) - RTC: Fix list sidebar reset during real-time collaboration (WordPress/gutenberg#76025) - RTC: Fix CRDT serialization of nested RichText attributes (WordPress/gutenberg#76597) - RTC: Remove post list lock icon and replace user-specific lock text (WordPress/gutenberg#76322) - Fix HEIC upload error handling and sub-size format (WordPress/gutenberg#76514) - RTC: Fix cursor index sync with rich text formatting (WordPress/gutenberg#76418) - RTC: Allow filtering of `SyncConnectionModal` (WordPress/gutenberg#76554) - RTC: Implement front-end peer limits (WordPress/gutenberg#76565) - Navigation overlay close button may be displayed twice (WordPress/gutenberg#76585) - Site Editor > Templates: fix author filter (WordPress/gutenberg#76625) - Revisions: Show changed block attributes in inspector sidebar (WordPress/gutenberg#76550) - Fix IS_GUTENBERG_PLUGIN env var override in build config (WordPress/gutenberg#76605) - Real Time Collaboration: Introduce filters for the polling intervals. (WordPress/gutenberg#76518) - RTC: Fix RichTextData deserialization (WordPress/gutenberg#76607) - Cross Origin Isolation: Remove `img` from the list of elements that get mutated (WordPress/gutenberg#76618) - RTC: Scroll to collaborator on click (WordPress/gutenberg#76561) - Update changelog link for pull request 11276 (WordPress/gutenberg#76638) - Fix backport changelog filename (WordPress/gutenberg#76651) - Build: Skip non-minified build for WASM-inlined workers (WordPress/gutenberg#76615) - RTC: Change RTC option name (WordPress/gutenberg#76643) - BlockListBlock: fix crash when selectedProps are null (WordPress/gutenberg#75826) - Build: Fix vips worker 404 when SCRIPT_DEBUG is true (WordPress/gutenberg#76657) - useMediaQuery: support in-iframe queries via new `WindowContext` (WordPress/gutenberg#76446) (WordPress/gutenberg#76660) - Refactor admin-ui Page component to use @wordpress/theme tokens and @wordpress/ui layout primitive (WordPress/gutenberg#75963) - Connectors: Improve accessibility (WordPress/gutenberg#76456) - Build: Remove unused JXL WASM module from vips worker (WordPress/gutenberg#76639) - Connectors: fix button size (WordPress/gutenberg#76582) - Compose: Implement useCopyToClipboard and useCopyOnClick with native clipboard API (WordPress/gutenberg#75723) (WordPress/gutenberg#76663) - Connectors: Fetch specific plugin instead of all plugins (WordPress/gutenberg#76594) - Revisions: Add Meta fields diff panel to document sidebar (WordPress/gutenberg#76341) - Loosen client-side media processing requirements (WordPress/gutenberg#76616) - Reduce the added halo for selected block. (WordPress/gutenberg#76619) - Connectors: Add unregisterConnector and upsert support (WordPress/gutenberg#76541) A full list of changes can be found on GitHub: https://github.com/WordPress/gutenberg/compare/8c78d87453509661a9f28f978ba2c242d515563b…487a096a9782ba6110a7686d7b4b2d0c55ed1b06. Log created with: git log --reverse --format="- %s" 8c78d87453509661a9f28f978ba2c242d515563b..487a096a9782ba6110a7686d7b4b2d0c55ed1b06 | sed 's|#\([0-9][0-9]*\)|https://github.com/WordPress/gutenberg/pull/\1|g; /github\.com\/WordPress\/gutenberg\/pull/!d' | pbcopy See #64595. Built from https://develop.svn.wordpress.org/trunk@62063 git-svn-id: http://core.svn.wordpress.org/trunk@61345 1a063a9b-81f0-0310-95a4-ce76da25c4cd
) Co-authored-by: tellthemachines <isabel_brison@git.wordpress.org> Co-authored-by: mcsf <mcsf@git.wordpress.org> Co-authored-by: ellatrix <ellatrix@git.wordpress.org> Co-authored-by: andrewserong <andrewserong@git.wordpress.org> Co-authored-by: t-hamano <wildworks@git.wordpress.org> Co-authored-by: ramonjd <ramonopoly@git.wordpress.org>




What
Alternative to #76420 and #75156
Until now,
useMediaQueryalways queried the global Window object:window.matchMedia( … ). If the hook is called in a component nested in anIframe, queries such as "is the viewport larger than x" will not reflect the the viewport of the iframe, which is most likely unexpected behaviour.One such instance was identified in #75156: when resizing the canvas, the block visibility rules (e.g. "this block is only visible on a tablet") were not applied correctly.
In this PR, we:create a new React context,WindowContextset the context whenever we use an iframe:<WindowContext.Provider value={iframeDocument.defaultView}>consume it inuseMediaQueryto retrieve in order to callwin.matchMediaon the relevant WindowIn this PR, we:
view: Windowargument touseMediaQuery, defaulting to the globalwindowWindow#matchMedia, adapting the module's cache to accout for multiple instancesuseViewportMatch,useBlockVisibility)useBlockVisibility(useBlockProps,BlockListBlockProvider) to:useRefEffectto access their element's corresponding Window (defaultView)useBlockVisibilityWe therefore avoid exposing and passing a fast-updating value like
useScaleCanvas'scontainerWidth, thereby avoiding performance issues like forced re-renders of iframe bodies.In the process, I also converted
use-media-queryto TypeScript, though unfortunately the changes are too many for Git to produce a "rename" diff, making the diff harder to read.Caveats
This new behaviour is implicit. Is there any risk that some consumer ofuseMediaQuery(or its descendantuseViewportMatch) is actually relying on always accessing the globalwindow? Scrutiny needed.Testing Instructions
Per-device block visibility
Follow the steps in #75156 to check the original issue is still fixed.
Rendering performance
The editor should now resize smoothly:
resizing-editor.mp4
Elsewhere
Ensure there are no unintended changes in behaviour wherever
useMediaQueryis involved and especiallyuseViewportMatch.