Add server-side rendered preview for unselected query loop blocks#11
Add server-side rendered preview for unselected query loop blocks#11roborourke wants to merge 11 commits into
Conversation
Pages with many query loops (e.g. magazine homepages) trigger a large number of API requests as each block loads its content previews. This replaces the full editor rendering with a server-side rendered preview when the query loop block is not selected, switching to the full editor when the user clicks on it. Registers a hidden hm-query-loop/preview block whose render_callback runs do_blocks() on the serialized block markup. The editor uses ServerSideRender with httpMethod="POST" to send the full inner blocks content to this endpoint, wrapped in the Disabled component to prevent interaction until the block is selected. https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
Playwright test resultsDetails
|
The ServerSideRender component uses __experimentalSanitizeBlockAttributes internally which requires the block to be registered in the JS block registry. Add a minimal registerBlockType call for hm-query-loop/preview with inserter: false so it resolves during attribute sanitization. Remove the Disabled wrapper since ServerSideRender already renders static HTML that doesn't need interaction prevention. https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
Adds two GitHub Actions workflows adapted from hm-url-tabs: - pr-playground-preview.yml: On PR open/sync, builds the plugin, pushes built assets to a pr-N-built branch, and appends a WordPress Playground preview button to the PR description using the official action. - pr-cleanup.yml: Deletes the pr-N-built branch when the PR is closed. https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
In the site editor, getCurrentPostId() can return a non-numeric template identifier. Sending post_id=0 or a string also fails the block-renderer endpoint's integer validation. Only include the parameter when we have a real numeric post ID. https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
The server-rendered HTML contains real anchor tags and other interactive elements that would navigate the editor iframe when clicked. Wrapping in the Disabled component prevents this. https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
Two problems with the Disabled component wrapper: - It changes the DOM structure so the query loop block can't be selected via click (the Disabled wrapper intercepts the event) - Reverting to SSR preview on deselection triggers unnecessary re-fetches Fix both by: - Adding the inert attribute to top-level elements in the rendered HTML server-side using WP_HTML_Tag_Processor, preserving the original DOM structure so block selection works naturally - Using useState to latch blocks as "activated" once first selected, keeping them in full edit mode permanently for the session https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
…toggle The previous approach auto-activated on first click and never reverted, which caused a refetch storm: getBlock(clientId) is reference-unstable so every editor keystroke triggered a new POST to the block renderer. The new approach adds an explicit Preview/Edit toggle to the Query Loop toolbar. Snapshot serialization happens once on entering preview mode (gated in a useEffect keyed only on mode transitions), so unrelated editor activity never fires additional renderer requests. A per-session hm-query-loop/preview-mode Redux store holds per-clientId mode state — intentionally not persisted to post content, matching the collapsed-panel convention. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…essor get_current_depth() only exists on WP_HTML_Processor. Calling it on WP_HTML_Tag_Processor caused a PHP fatal, which produced an empty REST API response and a blank preview. create_fragment() wraps content in an implicit body context, so top-level elements are at depth 1 rather than 0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moving snapshot capture from a useEffect into the toggleMode handler ensures React batches setSnapshot and setMode into a single render. The first ServerSideRender POST then has real content rather than an empty string, eliminating the spurious empty initial render. Also removes leftover echo/die() debug code and fixes a blank line.
mattheu
left a comment
There was a problem hiding this comment.
I couldn't get this working and had to make a few fixes. However... I'm still not really sure what value this brings. Yes the preview is rendered server side, but the value needs to be enabled and doesn't persist. Am I misunderstanding something?
|
The thinking here is that inside the editor each query loop, and each post template block triggers a ton of API requests, for instance fetching featured images, fetching post meta, fetching terms etc... Firing one API request and generating the resulting markup server side I thought might reduce how much the editor needed to process, and how much data needed to be fetched in order to render a query loop, especially given it's unlikely an editor will need to or want to modify every loop on the page each time. It's a bit too brittle still though, I haven't got it working well across several iterations, so perhaps need to go back to the drawing board on this idea. The best workaround for now is to recommend using synced patterns and editing those directly for speed. |
|
I actually do like this idea a lot and has potential. For a lot of publishing sites - the layout of the actual query loop post template will remain fixed and they'll just tweak the query to include different posts. So locking that preview, rendering server side, and possibly preloading that query, will be a notable UX improvement IMO. |
|
Another thing that this solves is... some blocks used within post template make their own REST requests - e.g. to display post terms. So locking the preview like this also solves that issue. |
Pages with many query loops (e.g. magazine homepages) trigger a large
number of API requests as each block loads its content previews. This
replaces the full editor rendering with a server-side rendered preview
when the query loop block is not selected, switching to the full editor
when the user clicks on it.
Registers a hidden hm-query-loop/preview block whose render_callback
runs do_blocks() on the serialized block markup. The editor uses
ServerSideRender with httpMethod="POST" to send the full inner blocks
content to this endpoint, wrapped in the Disabled component to prevent
interaction until the block is selected.
https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj