Repository navigation
feat: variable column/row spans for posts in Query Loop grid layout - #602
ViewFromTheBox merged 2 commits into
Conversation
Add `gridColumnSpan` / `gridRowSpan` attributes (default 1) to the
`artisanpack/post-variant` block. Both opt into the `artisanpackResponsive`
cascade so authors can set per-breakpoint spans. When the parent Query
Loop's post-template renders in grid layout and a post matches a
variant, the spans propagate to the iteration `<li>` via
`ap-post-span-N-{bp}-{columns,row}` classes.
- Post-template now provides its `layout` / `columns` attributes as
block context (`artisanpack/postTemplateLayout` / `Columns`) so the
variant inspector can gate the new "Grid Spans" panel on the parent
template's grid state and clamp the column slider to its column count.
- Post-template editor switched from upstream `is-flex-container` to
`is-layout-grid` so the editor canvas and Blade frontend share the
same CSS-grid layout — spans now visibly take effect in both.
- New `post-template.css` defines `grid-template-columns: repeat(N, …)`
for `is-layout-grid.columns-N` (the layout baseline only ships
`display: grid`).
- `QueryInliner` stamps a normalised `_resolvedGridSpan = { columns,
rows }` on the synthetic `core/post-template-item` wrapper when (a) a
variant matched and (b) the post-template's `layout === 'grid'`. The
Blade / Vue / React renderers translate that into the class list via
a shared `postTemplateItemSpanClasses()` helper plus a Blade-side
emitter gated on the runtime `BreakpointRegistry`.
- `post-variant.css` carries the span rules with two parallel
selectors per breakpoint: a direct compound match for the frontend
`<li.wp-block-post-template-item>` and a `:has()`-based match for
the editor canvas's `<li.wp-block-post>` (where the span class is
nested on the variant block wrapper, not on the `<li>` itself).
- Tests: Pest covers the inliner (precedence, clamping, grid-gating,
responsive merging), a Blade feature test asserts end-to-end class
emission, and a vitest suite pins renderer parity between Vue and
React span-class helpers.
Closes #592
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds responsive ChangesPost-Variant Responsive Grid Spans
Sequence Diagram(s)sequenceDiagram
rect rgba(100, 149, 237, 0.5)
note over Editor: Block authoring
participant PostTemplateBlock
participant PostVariantEdit
PostTemplateBlock->>PostVariantEdit: provides artisanpack/postTemplateLayout, artisanpack/postTemplateColumns via context
PostVariantEdit->>PostVariantEdit: isGridLayout() → show Grid Spans panel
PostVariantEdit->>PostVariantEdit: RangeControl → sets gridColumnSpan, gridRowSpan attributes
end
rect rgba(144, 238, 144, 0.5)
note over Server: Render-time inlining
participant QueryInliner
participant resolveVariantSpans
participant PostTemplateItem as core/post-template-item
QueryInliner->>resolveVariantSpans: variant block attributes (gridColumnSpan, gridRowSpan, breakpoint overrides)
resolveVariantSpans-->>QueryInliner: _resolvedGridSpan record or null
QueryInliner->>PostTemplateItem: stamp _resolvedGridSpan in attributes
end
rect rgba(255, 165, 0, 0.5)
note over Renderers: Frontend class emission
participant PostTemplateItem
participant postTemplateItemSpanClasses as postTemplateItemSpanClasses (React/Vue)
participant BladeTemplate as post-template-item.blade.php
PostTemplateItem->>postTemplateItemSpanClasses: _resolvedGridSpan
postTemplateItemSpanClasses-->>PostTemplateItem: ap-post-span-* class array
PostTemplateItem->>BladeTemplate: _resolvedGridSpan
BladeTemplate->>BladeTemplate: validate + clamp via BreakpointRegistry → ap-post-span-* classes
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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.
Inline comments:
In `@resources/js/visual-editor/blocks/post-variant/edit.tsx`:
- Around line 57-71: The clampSpan function currently respects the max parameter
passed to it, but the backend renderers and CSS rules enforce a hardcoded
maximum of 12 columns. Modify the clampSpan function to ensure it never returns
a value greater than 12 to maintain parity with the renderer behavior, by
capping the maximum constraint to 12 in addition to the existing max parameter
check. Additionally, apply the same fix to the other span clamping logic at
lines 158-160 and 286-303 to ensure consistent column span handling across all
editor paths.
- Around line 95-101: The isGridLayout function currently falls back to checking
displayLayout.type === 'grid' even when artisanpack/postTemplateLayout is
explicitly set to a non-grid value. To fix this, ensure the post-template layout
is authoritative when present by checking if artisanpack/postTemplateLayout
exists in the context. If it exists, return the result of comparing it directly
to 'grid' without evaluating the displayLayout fallback. Only apply the
displayLayout.type === 'grid' check when artisanpack/postTemplateLayout is
undefined or absent from the context.
In `@src/Resources/QueryInliner.php`:
- Around line 628-630: The grid layout detection in the variable
`$postTemplateIsGrid` only checks if the layout attribute is a plain string
equal to 'grid', but it does not handle cases where the layout is serialized as
an object/array with a type property or as a separate layoutType attribute.
Expand the condition to also check if layout is an array with a 'type' key equal
to 'grid', or if a layoutType attribute exists and equals 'grid', so that all
valid grid layout formats are properly detected before stamping the
_resolvedGridSpan value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 37734f18-3f61-47bd-890b-ee5bf257c90a
📒 Files selected for processing (21)
packages/visual-editor-renderer-blade/resources/assets/frontend/post-template.csspackages/visual-editor-renderer-blade/resources/assets/frontend/post-variant.csspackages/visual-editor-renderer-blade/resources/views/blocks/core/post-template-item.blade.phppackages/visual-editor-renderer-blade/resources/views/components/blocks-styles.blade.phppackages/visual-editor-renderer-blade/src/View/Components/BlocksStylesComponent.phppackages/visual-editor-renderer-blade/tests/Feature/PostVariantGridSpansRenderTest.phppackages/visual-editor-renderer-react/src/blocks/core/query.tsxpackages/visual-editor-renderer-react/src/support/attributes.tspackages/visual-editor-renderer-vue/src/blocks/core/query.tspackages/visual-editor-renderer-vue/src/support/attributes.tsresources/js/visual-editor/blocks/__tests__/post-variant-grid-spans.test.tsresources/js/visual-editor/blocks/post-template/block.jsonresources/js/visual-editor/blocks/post-template/edit.tsxresources/js/visual-editor/blocks/post-template/index.tsresources/js/visual-editor/blocks/post-template/post-template.cssresources/js/visual-editor/blocks/post-variant/block.jsonresources/js/visual-editor/blocks/post-variant/edit.tsxresources/js/visual-editor/blocks/post-variant/index.tsresources/js/visual-editor/blocks/post-variant/post-variant.csssrc/Resources/QueryInliner.phptests/Unit/VisualEditor/Resources/QueryInlinerVariantSpansTest.php
- Cap `resolveNumColumns()` / `clampSpan()` at the renderer's hardcoded ceiling of 12 so the editor slider never offers a value the renderer would silently clamp away (introduced `HARD_MAX_SPAN`). - Make the post-template the authoritative source for grid detection in `isGridLayout()` — even when its `layout` is set to a non-grid value, fall through to the query's `displayLayout.type` only when the post-template hasn't published a layout key at all. - Tolerate the object-form `layout = ['type' => 'grid']` and the sibling `layoutType` attribute in `QueryInliner::postTemplateLayoutIsGrid()` so hosts that wire post-template through Gutenberg's block-supports layout system still get spans stamped. New Pest case asserts both shapes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Description
Adds
gridColumnSpan/gridRowSpanattributes (default 1) toartisanpack/post-variant, both opting into theartisanpackResponsivecascade for per-breakpoint values. When the parent Query Loop's post-template renders in grid layout and a post matches a variant, the spans propagate to the iteration<li>so authors can build magazine-style layouts (e.g. lead story spans 2×2, sticky posts span 2×1) without leaving the post-variant system from #591.Closes: #592
Type of Change
Related Issue
Issue: #592
Motivation and Context
Before this change every post in a Query Loop's grid layout occupied a 1×1 cell — there was no way to say "make the first post 2×2 and the second post 2×1." The standalone
artisanpack/gridblock already supports the same expressiveness viaartisanpack/grid-item. This issue brings parity to the Query Loop, keeping a single mental model: any per-post difference is just a variant attribute, and spans are one more thing a variant can change.Changes Made
artisanpack/post-variantdeclaresgridColumnSpan/gridRowSpan(default 1) and lists both insupports.artisanpackResponsive.attributes.providesContextfor itslayout/columnsattributes underartisanpack/postTemplateLayout/Columnsso the variant inspector can gate the new "Grid Spans" panel on the parent template's grid state and clamp the column slider to its column count.1..numColumns; row span clamps to1..12with a notice above 4.is-flex-containertois-layout-gridso the editor canvas and Blade frontend share the same CSS-grid layout. Newpost-template.cssdefinesgrid-template-columns: repeat(N, …)foris-layout-grid.columns-N(the layout baseline only shipsdisplay: grid).QueryInlinerstamps a normalised_resolvedGridSpan = { columns, rows }on the syntheticcore/post-template-itemwrapper when (a) a variant matched and (b) the post-template'slayout === 'grid'. Defaults of 1×1 with no breakpoint overrides skip the stamp entirely.PostTemplateItemtranslate_resolvedGridSpanintoap-post-span-N-{bp}-{columns,row}classes via a sharedpostTemplateItemSpanClasses()helper (Blade side gates on the runtimeBreakpointRegistry).post-variant.csscarries the span rules with two parallel selectors per breakpoint — a direct compound match for the frontend<li.wp-block-post-template-item>and a:has()-based match for the editor canvas's<li.wp-block-post>(where the span class is nested on the variant wrapper, not on the<li>itself).BlocksStylesComponentshipspost-template.cssandpost-variant.cssas new linked stylesheets, with helpful comments above each<link>explaining the role.How Has This Been Tested?
Testing Environment:
Tests Performed:
~/Herd/artisanpack-ui-dev, switched the post-template to grid, added a position:first post-variant, set column span 2 / row span 2. Verified the variant's inspector showed the new Grid Spans panel, that it disappeared when toggling the post-template back to list, that the column slider capped at the parent's column count, and that both editor canvas and rendered frontend showed the first post spanning 2×2 across a 3-column grid../vendor/bin/pest --compact→ 1315 passed, 1 skipped (pre-existing hooks skip).npx vitest run→ 2223 passed, 258 files.npm run verify:parity→ clean.cr review --base release/1.2 --agent) — 1 minor finding (request to add JSON-schemamin/maxto block attribute definitions); skipped to stay consistent with the existingartisanpack/grid-itemattribute precedent, given that clamping is already enforced in the editor, the inliner, and the renderers. Second pass returned 0 findings.Accessibility Tests Run
Details:
Inspector controls are standard
@wordpress/componentsRangeControls, which are already keyboard-navigable and screen-reader-labelled. No new custom UI was introduced. The visible markup change is purely class-based — the rendered semantics (<ul>+<li>withid="post-N") are unchanged from the existing post-template family.Tests Added
Test details:
tests/Unit/VisualEditor/Resources/QueryInlinerVariantSpansTest.php— 6 cases covering the stamp gating (grid layout only), the omitted-stamp shortcut for 1×1 defaults, base + responsive merging, clamping to 1..12, and the variant precedence cascade for spans.packages/visual-editor-renderer-blade/tests/Feature/PostVariantGridSpansRenderTest.php— 3 end-to-end render tests asserting the right class set on the rendered<li>(or its absence on non-grid layouts), including per-breakpoint overrides.resources/js/visual-editor/blocks/__tests__/post-variant-grid-spans.test.ts— 9 cases pinning the block.json shape and renderer-parity helper output between Vue and React.Documentation
Documentation details:
Each new file carries a header docblock explaining its role (post-template grid container vs. post-variant span overrides; the two-selector pattern for editor / frontend markup). Resource-level PHPDoc on
QueryInliner::resolveVariantSpans()documents the normalisation rules and the 1×1 short-circuit.Screenshots
See acceptance demo on the artisanpack-ui-dev app — a position:first variant set to 2 cols × 2 rows in a 3-column grid renders as the lead cell across both editor canvas and frontend output.
Pre-Submission Checklist
Summary by CodeRabbit