Skip to content

feat(blocks): preview real post data in post-* edits inside a query loop - #507

Merged
ViewFromTheBox merged 2 commits into
release/1.0from
enhancement/483-editor-preview-post-blocks-real-data
May 30, 2026
Merged

ViewFromTheBox merged 2 commits into
release/1.0from
enhancement/483-editor-preview-post-blocks-real-data

Conversation

@ViewFromTheBox

@ViewFromTheBox ViewFromTheBox commented May 30, 2026 •

Copy link
Copy Markdown
Member

Description

When an artisanpack/query block is in the editor and useQueryPreview has resolved at least one matching post, this PR pipes that first post through the existing BlockContextProvider so inner post-title / post-date / post-excerpt / post-author / post-featured-image block edits render the actual post's data in the canvas instead of the static placeholder labels ("Post Title", "Post Date", …) they used to show.

Closes: #483

Type of Change

  • Enhancement (improves existing functionality)

Related Issue

Issue: #483

Motivation and Context

The Phase I5 entity cluster (#413) shipped these post-* blocks with a deliberate-shortcut edit that rendered a static label, because the post editor's @wordpress/core-data shim does not expose entity data. The front end resolves correctly (the QueryInliner stamps _resolved* attributes), but inside an artisanpack/query loop the canvas just showed placeholders, making it look like the query was empty or broken when it was actually working.

The fix follows the constraint in the issue: no new core-data shim requirements — the data flows through the BlockContextProvider → postId path the query block already establishes, extended with an artisanpack/postPreview key carrying the resolved first post.

Changes Made

Server (PHP):

  • WpEntityResource adds a _preview envelope alongside the existing WP REST shape: dateFormatted, author (name / bio / url / avatarUrl), featuredImage (url / alt / width / height). Mirrors the _resolved* keys PostResolver stamps for the front-end renderers so editor canvas and front end agree.

Client (TS/TSX):

  • QueryPreviewPost gains dateFormatted; mapWpEntityToPost extracts the _preview envelope.
  • query/edit.tsx's BlockContextProvider now carries { postType, postId, 'artisanpack/postPreview': firstPost | null }.
  • The 5 post-* block.json files declare artisanpack/postPreview in usesContext.
  • createEntityPlaceholderEdit accepts a fromQueryPreview(post) extractor. Priority: stamped _resolvedX attribute → context post → labelled placeholder.
  • Each of the 5 post-* edits supplies its field extractor (title, formatted/ISO date, excerpt, author name, featured image url+alt).
  • post-author/block.json drops supports.inserter: false so the block can be inserted alongside the others — upstream hides it because core/post-author is deprecated in favor of post-author-name + post-author-biography + avatar, none of which the fork ships yet. Tracked as a follow-up in Fork remaining WP theme-category blocks (post-author-name, post-comments-*, query-pagination-*, etc.) #506. The divergence is documented in upstream-state.json.

How Has This Been Tested?

Testing Environment:

  • Operating System: macOS
  • Browser (if applicable): Chrome (latest)
  • PHP Version: 8.4.3
  • Project Version: 1.0.0-spike (release/1.0)

Tests Performed:

  1. Ran the full Vitest suite — 1487 tests passed (205 files), including 28 new/extended tests for the context-preview behavior and the _preview envelope mapping.
  2. Ran the full Pest suite — 680 tests passed (1978 assertions), including 5 new unit tests for the WpEntityResource _preview envelope and 1 new feature test asserting the query/resolve endpoint surfaces it.
  3. Ran npm run verify:parity — all 89 blocks registered across Blade / React / Vue renderers.
  4. Built the package (npm run build), deployed dist/editor/* into the jmwd-keystone-cms host app's public/visual-editor/, and manually verified in the browser:
    • Inserted an artisanpack/query block with two matching posts → inner post-title, post-date, post-excerpt, post-author, and post-featured-image blocks all preview the first matched post's real data.
    • Dropped a post-title outside a query loop → falls back to the labelled placeholder.
    • Acceptance criterion 1 in Editor preview: show real post data in post-* block edits instead of placeholder labels #483 specifies "the first matched post's actual data" — multi-iteration in the canvas is by design (the existing PreviewBanner already announces this) and is handled on the front end by QueryInliner.

Accessibility Tests Run

  • Keyboard navigation tested (insert / focus / move blocks via keyboard inside the query loop)
  • Screen reader tested (block labels still announce correctly; alt on featured-image previews is read)
  • Color contrast verified (placeholder uses currentColor dashed border + 0.55 opacity, unchanged from existing helper)
  • ARIA labels checked

Details: The change is read-only preview content — no new interactive surfaces. The placeholder-vs-resolved path is purely about which string/image the existing block wrapper renders.

Tests Added

  • Unit tests added/updated
  • Integration tests added/updated
  • All tests passing

Test details:

  • New: resources/js/visual-editor/blocks/_shared/__tests__/entity-placeholder-query-preview.test.tsx — 17 tests covering attribute-vs-context priority, placeholder fallback, image-kind rendering, non-object context guard, per-block extractors, the block.json usesContext declaration, and the post-author insertability flag.
  • New: tests/Unit/VisualEditor/Resources/WpEntityResourcePreviewTest.php — 5 tests covering the null envelope, the author envelope with bio/url and description/website fallbacks, the dateFormatted field, and the featured-image envelope short-circuiting when media-library helpers aren't bound.
  • Extended: resources/js/visual-editor/editor/__tests__/use-query-preview.test.tsx — 3 new cases for _preview extraction, omitted-extras nulling, and url-less featured-image dropping.
  • Extended: tests/Feature/VisualEditor/QueryResolve/QueryResolveControllerTest.php — 1 new case asserting the _preview envelope structure on resolved posts.

Documentation

  • Inline code documentation added

Documentation details:

  • WpEntityResource::previewEnvelope / authorEnvelope / featuredImageEnvelope / formattedDate carry full PHPDoc explaining the mapWpEntityToPost() consumer contract.
  • createEntityPlaceholderEdit's file-level docblock + EntityPreviewValue / EntityPlaceholderConfig interface comments document the attribute → context → placeholder priority.
  • Each post-* edit.tsx carries an updated header comment noting the new fallback step.
  • post-author/upstream-state.json documents the deliberate supports.inserter divergence.

Screenshots

Pre-#483: every post-* block in the query loop showed "Post Title" / "Post Date" / etc. placeholder labels.
Post-#483: the same blocks show the first matched post's actual title, date, excerpt, author name, and featured image.

Pre-Submission Checklist

  • Followed contributing guidelines
  • Checked for other open PRs for same update
  • Code passes all tests (1487 JS + 680 PHP)
  • Code has been linted (composer scripts don't define a lint task in this package; tests + renderer-parity pass)
  • Accessibility tests completed
  • Code follows project style guide
  • Self-review completed
  • Comments added for complex code
  • No new warnings generated
  • CodeRabbit CLI review run against release/1.0 — addressed the one minor finding (defensive image url guard); pass 2 clean

Summary by CodeRabbit

  • New Features

    • Post blocks now show live query-loop previews (title, date, excerpt, author, featured image) in the editor; featured image alt/text and formatted dates are surfaced.
    • Post Author block is insertable alongside other post display blocks during rollout.
  • Tests

    • Added extensive tests validating query-preview mapping, placeholder fallbacks, and post-* block preview behavior in the editor.

Review Change Stack

…uery loop

Pipes the resolved first post from `useQueryPreview` through the
`artisanpack/query` block's `BlockContextProvider` so inner
`post-title` / `post-date` / `post-excerpt` / `post-author` /
`post-featured-image` block edits render the actual post's data
in the editor canvas instead of placeholder labels.

The data flow:
- `WpEntityResource` adds a `_preview` envelope (formatted date,
  author name/avatar/url/bio, featured-image url/alt/dimensions)
  alongside the existing WP REST shape. Mirrors the `_resolved*`
  keys the server-side `PostResolver` stamps for the front end.
- `mapWpEntityToPost` extracts `_preview` into `QueryPreviewPost`.
- `query/edit.tsx` adds `artisanpack/postPreview` to its
  `BlockContextProvider` value carrying the first matched post.
- Each post-* `block.json` declares `artisanpack/postPreview` in
  `usesContext`.
- `createEntityPlaceholderEdit` accepts a `fromQueryPreview(post)`
  extractor with priority: stamped `_resolvedX` → context post →
  placeholder.

Also drops `supports.inserter: false` on `artisanpack/post-author`
so it can be inserted into a query loop alongside the other
post-* blocks. Upstream hides it because `core/post-author` is
deprecated in favor of `post-author-name` + `post-author-biography`
+ `avatar`, none of which the fork ships yet — see #506 for the
follow-up to fork the rest of the WP theme catalog.

Closes #483
@coderabbitai

coderabbitai Bot commented May 30, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: be5973fd-4316-4052-a0ac-2a905b70da13

📥 Commits

Reviewing files that changed from the base of the PR and between 22b24a6 and 76faa7e.

📒 Files selected for processing (1)
  • src/Http/Resources/Adapters/CmsFramework/WpEntityResource.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Http/Resources/Adapters/CmsFramework/WpEntityResource.php

📝 Walkthrough

Walkthrough

Backend responses include an editor _preview envelope; frontend maps that into QueryPreviewPost, QueryEdit injects artisanpack/postPreview into block context, createEntityPlaceholderEdit can extract preview values via fromQueryPreview, and post-* blocks use that to render live previews or fall back to placeholders.

Changes

Query Preview Support

Layer / File(s) Summary
API preview envelope generation
src/Http/Resources/Adapters/CmsFramework/WpEntityResource.php, tests/Feature/VisualEditor/QueryResolve/QueryResolveControllerTest.php
WpEntityResource now includes a _preview envelope with formatted date, author envelope, and featured image metadata; feature test asserts _preview present for resolved posts.
Frontend preview hook and type mapping
resources/js/visual-editor/editor/use-query-preview.ts, resources/js/visual-editor/editor/__tests__/use-query-preview.test.tsx
useQueryPreview maps entity._preview into QueryPreviewPost (adds dateFormatted, author, featuredImage) with defensive mapping helpers and tests for missing/invalid preview fields.
Core placeholder infrastructure for preview rendering
resources/js/visual-editor/blocks/_shared/entity-placeholder-edit.tsx, resources/js/visual-editor/blocks/_shared/__tests__/entity-placeholder-query-preview.test.tsx
Entity placeholder helper adds EntityPreviewValue and optional fromQueryPreview extractor; createEntityPlaceholderEdit prefers stamped _resolved* attributes, then query-preview-derived values, then placeholder label; tests validate precedence and rendering (text/html/image).
Query block context provider
resources/js/visual-editor/blocks/query/edit.tsx
QueryEdit populates BlockContextProvider with artisanpack/postPreview set to the first matched post object when ready or null otherwise.
Post- block editor implementations*
resources/js/visual-editor/blocks/post-title/*, resources/js/visual-editor/blocks/post-date/*, resources/js/visual-editor/blocks/post-excerpt/*, resources/js/visual-editor/blocks/post-author/*, resources/js/visual-editor/blocks/post-featured-image/*
Each post-* block adds artisanpack/postPreview to usesContext and supplies a fromQueryPreview callback that extracts the block-specific preview value (title, formatted/sliced date, excerpt, author.name, featuredImage url/alt) or returns null to trigger placeholder fallback.
Post- blocks integration testing*
resources/js/visual-editor/blocks/_shared/__tests__/entity-placeholder-query-preview.test.tsx
Vitest integration suite verifies post-* edit components render context-derived preview values, fall back to placeholders when missing, confirms post-author inserter support, and checks usesContext declarations.
Backend preview envelope testing
tests/Unit/VisualEditor/Resources/WpEntityResourcePreviewTest.php
Unit tests validate _preview envelope behaviors: nulls when missing, author mapping and fallbacks, date formatting, and null featuredImage when media helpers unavailable.

Sequence Diagram(s)

sequenceDiagram
  participant Post as Post Model
  participant API as WpEntityResource
  participant Hook as useQueryPreview
  participant Context as BlockContextProvider
  participant Block as PostTitleEdit
  participant Renderer as EntityPlaceholderRenderer

  Post->>API: model with author, published_at, featured media
  API->>API: build _preview (dateFormatted, author, featuredImage)
  API->>Hook: response { _preview }
  Hook->>Hook: mapPreviewAuthor / mapPreviewFeaturedImage
  Hook->>Context: artisanpack/postPreview = QueryPreviewPost
  Context->>Block: context.artisanpack/postPreview
  Block->>Block: fromQueryPreview(post)
  Block->>Renderer: EntityPreviewValue (text/image)
  Renderer->>Renderer: renderResolved or fallback placeholder
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I nibble through code and plant a seed,

Previews sprout where placeholders used to be,
Dates formatted, authors softly named,
Images and excerpts now rightly framed,
The editor hops alive with glee.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main change: enabling post-* blocks to render real post data from a query loop context, which is the core feature across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enhancement/483-editor-preview-post-blocks-real-data

Comment @coderabbitai help to get the list of available commands and usage tips.

@ViewFromTheBox ViewFromTheBox moved this from Backlog to In Review in ArtisanPack UI Overview May 30, 2026
@ViewFromTheBox
ViewFromTheBox marked this pull request as ready for review May 30, 2026 22:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/Http/Resources/Adapters/CmsFramework/WpEntityResource.php (1)

190-203: ⚡ Quick win

Add defensive try/catch around apGetMedia() / apGetMediaUrl() in featuredImageEnvelope()

function_exists() only prevents “helper missing” fatals; if either helper throws, the exception will bubble out of toArray() (via previewEnvelope() → featuredImageEnvelope()) and fail the whole response for that resource. This mirrors the defensive try/catch already used in relationIds(). Media-library helpers are documented to return empty/null for missing/not-found media, but a catch keeps the editor resilient if a host helper implementation throws unexpectedly.

🛡️ Proposed fix to isolate helper failures
 		if ( function_exists( 'apGetMediaUrl' ) ) {
-			$resolved = apGetMediaUrl( $mediaId, 'full' );
-			$url      = is_string( $resolved ) ? $resolved : '';
+			try {
+				$resolved = apGetMediaUrl( $mediaId, 'full' );
+				$url      = is_string( $resolved ) ? $resolved : '';
+			} catch ( Throwable ) {
+				$url = '';
+			}
 		}

 		if ( function_exists( 'apGetMedia' ) ) {
-			$media = apGetMedia( $mediaId );
-
-			if ( is_object( $media ) ) {
-				$alt    = isset( $media->alt_text ) && is_scalar( $media->alt_text ) ? (string) $media->alt_text : '';
-				$width  = isset( $media->width ) && is_numeric( $media->width ) ? (int) $media->width : 0;
-				$height = isset( $media->height ) && is_numeric( $media->height ) ? (int) $media->height : 0;
-			}
+			try {
+				$media = apGetMedia( $mediaId );
+
+				if ( is_object( $media ) ) {
+					$alt    = isset( $media->alt_text ) && is_scalar( $media->alt_text ) ? (string) $media->alt_text : '';
+					$width  = isset( $media->width ) && is_numeric( $media->width ) ? (int) $media->width : 0;
+					$height = isset( $media->height ) && is_numeric( $media->height ) ? (int) $media->height : 0;
+				}
+			} catch ( Throwable ) {
+				$alt = '';
+				$width = 0;
+				$height = 0;
+			}
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Http/Resources/Adapters/CmsFramework/WpEntityResource.php` around lines
190 - 203, Wrap the calls to apGetMediaUrl() and apGetMedia() inside a defensive
try/catch within featuredImageEnvelope() so any exception from those helpers
does not bubble out of toArray()/previewEnvelope(); specifically, enclose the
existing function_exists(...) blocks that call apGetMediaUrl($mediaId, 'full')
and apGetMedia($mediaId) in a try block, catch Throwable, and on catch leave
$url/$media/$alt/$width/$height set to their safe defaults (and optionally log
the error), mirroring the defensive pattern used in relationIds().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/Http/Resources/Adapters/CmsFramework/WpEntityResource.php`:
- Around line 190-203: Wrap the calls to apGetMediaUrl() and apGetMedia() inside
a defensive try/catch within featuredImageEnvelope() so any exception from those
helpers does not bubble out of toArray()/previewEnvelope(); specifically,
enclose the existing function_exists(...) blocks that call
apGetMediaUrl($mediaId, 'full') and apGetMedia($mediaId) in a try block, catch
Throwable, and on catch leave $url/$media/$alt/$width/$height set to their safe
defaults (and optionally log the error), mirroring the defensive pattern used in
relationIds().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 93232791-1b19-42ad-8c08-19a3d222dd7f

📥 Commits

Reviewing files that changed from the base of the PR and between 4a1f65a and 22b24a6.

📒 Files selected for processing (19)
  • resources/js/visual-editor/blocks/_shared/__tests__/entity-placeholder-query-preview.test.tsx
  • resources/js/visual-editor/blocks/_shared/entity-placeholder-edit.tsx
  • resources/js/visual-editor/blocks/post-author/block.json
  • resources/js/visual-editor/blocks/post-author/edit.tsx
  • resources/js/visual-editor/blocks/post-author/upstream-state.json
  • resources/js/visual-editor/blocks/post-date/block.json
  • resources/js/visual-editor/blocks/post-date/edit.tsx
  • resources/js/visual-editor/blocks/post-excerpt/block.json
  • resources/js/visual-editor/blocks/post-excerpt/edit.tsx
  • resources/js/visual-editor/blocks/post-featured-image/block.json
  • resources/js/visual-editor/blocks/post-featured-image/edit.tsx
  • resources/js/visual-editor/blocks/post-title/block.json
  • resources/js/visual-editor/blocks/post-title/edit.tsx
  • resources/js/visual-editor/blocks/query/edit.tsx
  • resources/js/visual-editor/editor/__tests__/use-query-preview.test.tsx
  • resources/js/visual-editor/editor/use-query-preview.ts
  • src/Http/Resources/Adapters/CmsFramework/WpEntityResource.php
  • tests/Feature/VisualEditor/QueryResolve/QueryResolveControllerTest.php
  • tests/Unit/VisualEditor/Resources/WpEntityResourcePreviewTest.php

Wrap the `apGetMediaUrl()` / `apGetMedia()` calls in
`featuredImageEnvelope()` in defensive `try/catch` blocks so a host
helper implementation that throws cannot bubble out of `toArray()`
and break the entire response. Mirrors the defensive pattern
`relationIds()` already uses in the same file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

Editor preview: show real post data in post-* block edits instead of placeholder labels

1 participant