Skip to content

Composed view: missing/invalid template fallback + toast - #694

Merged
ViewFromTheBox merged 1 commit into
feature/templates-previewfrom
feature/624-composed-view-missing-template-fallback
Aug 3, 2026
Merged

ViewFromTheBox merged 1 commit into
feature/templates-previewfrom
feature/624-composed-view-missing-template-fallback

Conversation

@ViewFromTheBox

Copy link
Copy Markdown
Member

Description

When the applied-template endpoint cannot resolve a template for the current content, the composed view previously rendered bare content behind a "no template chrome is shown" banner — the With Template toggle read as having silently done nothing.

All three unresolvable states now compose against a minimal built-in default template (title → featured image → content slot) and say why:

State Copy Tone
missing / unknown-slug The template “{slug}” is unavailable — previewing on the default template. warning
missing / empty No template is set for this content — previewing on the default template. warning
error (network / 5xx) The template could not be loaded — previewing on the default template. error

All three land on the same canvas, so the toggle stays on and the content stays editable.

Closes: #624

Type of Change

  • Bug fix (fixes an issue)
  • New feature (adds new functionality)
  • Enhancement (improves existing functionality)
  • Refactoring (code improvement, no behavior change)
  • Documentation update
  • Performance improvement
  • Security fix
  • Breaking change (breaks backward compatibility)

Related Issue

Issue: #624 (parent: #618)

Motivation and Context

#619–#622 landed the composed view for the happy path. The miss path shipped with a placeholder treatment — an explanatory banner and no chrome — explicitly marked in the code as "review-fix only; the designed fallback is #624". This is that fallback.

The author-facing problem: an unresolvable template left the canvas identical to bare-content mode while the toggle still read "With template". Nothing distinguished "your template has no header" from "your template could not be found".

Changes Made

  • New composed-view/default-template.ts — the fallback block tree as a client-side constant (artisanpack/post-title → post-featured-image → post-content) plus defaultTemplateName(). Deliberately not persisted: writing it to the database would put a phantom template in the site editor's template list. Emitted in the server block shape so it flows through the existing split → hydrate pipeline unchanged, and uses forked block names so the split finds artisanpack/post-content as the content slot exactly as it would in a real template.
  • New composed-view/fallback.ts — selectComposedTemplate() routes the three miss states to the default tree with slug: null; composedFallbackNotice() owns the copy. Pure functions, so the routing is testable without mounting the editor.
  • New composed-view/use-fallback-toast.ts — fires the toast, mirroring useSaveNotifications. The latch keys on the message rather than a boolean, so picking a second broken template without leaving composed mode still announces the new failure; a plain boolean would swallow it and the author would see the template change do nothing.
  • editor-app.tsx — composed chrome falls back instead of returning null; the two old miss/error Alert branches collapse into composedFallbackNotice(). The alert is kept alongside the toast because it is what an author still sees a minute later, once the toast has auto-dismissed.
  • templateSlug (null on fallback) + isFallback added to the composed chrome, so Composed view: locked-canvas ribbon with Edit template deep-link CTA #623's ribbon can show the fallback name and hide Edit template ↗. See the note below.

How Has This Been Tested?

Testing Environment:

  • Operating System: macOS (Darwin 27.0.0)
  • Browser (if applicable): Chrome, via the artisanpack-ui-dev Herd app
  • PHP Version: 8.4.3
  • Project Version: 1.x (feature/templates-preview)

Tests Performed:

  1. Cleared the template in the document panel, flipped With Template — warning toast + default-template canvas (title, featured image, editable content), matching standing alert.
  2. Pointed the content at an unresolvable slug — toast names the slug.
  3. Blocked */applied-template in devtools — error-toned toast, same fallback canvas.
  4. Toggled off and back on in each case — content stayed editable, toast re-announced on each toggle-on but not on idle re-renders.
  5. Happy path unchanged — a content item with a real template still renders real chrome with the "Previewing the “X” template" info notice and no toast.

Accessibility Tests Run

  • Keyboard navigation tested
  • Screen reader tested
  • Color contrast verified
  • ARIA labels checked

Details: No new interactive controls are introduced. The toast is announced through ToastProvider's existing live region and the standing alert keeps the role="status" it already carried; the toggle remains keyboard-operable and is not disabled by the fallback. Tone is carried by the existing Alert/Toast color variants, so contrast is inherited from the theme rather than newly specified.

Tests Added

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

Test details: 20 new Vitest cases across two files.

  • __tests__/fallback.test.ts — all three miss states route to the default template with a null slug; the default tree splits cleanly around its content slot; a fresh tree is handed out per call (so two hydrations never share a clientId); idle/loading compose nothing rather than flashing the default; and each copy variant, including the contradictory unknown-slug-with-no-slug payload falling through to the no-template wording.
  • __tests__/use-fallback-toast.test.tsx — each copy variant fires; exactly once per toggle-on; a different failure re-announces without leaving composed mode; a toggle off/on re-announces; a resolved template stays silent.

Full editor suite: 342 passing, 36 files. site-editor/__tests__/site-editor-app.test.tsx fails 18 tests with window.localStorage undefined — confirmed failing identically on feature/templates-preview before this branch, unrelated and untouched here.

Security-reviewed: no findings. The only server-influenced value reaching the UI is the template slug, rendered as a React text child (no raw-HTML sink in the new files or in @artisanpack-ui/react's feedback bundle). No fetches, storage writes, or URL construction are added. The change slightly narrows exposure — the previous error branch rendered the raw server error message into the editor UI; it now renders fixed generic copy.

Documentation

  • Inline code documentation added
  • README updated
  • Wiki updated
  • API documentation updated

Documentation details: Each new module carries a file-level docblock covering the fallback contract and the reasoning behind the not-persisted default tree and the message-keyed latch. The author-facing docs/post-editor/ page for the composed view is owned by #626 and is not written here.

Screenshots

Pre-Submission Checklist

  • Followed contributing guidelines
  • Checked for other open PRs for same update
  • Code passes all tests
  • Code has been linted
  • Accessibility tests completed
  • Code follows project style guide
  • Self-review completed
  • Comments added for complex code
  • No new warnings generated

Note for reviewers: one AC is not fully closed here

The ribbon (#623) reflects the fallback template name and hides the Edit template ↗ CTA.

#623 is still open and the ribbon does not exist yet. This PR plumbs the data it needs — templateSlug: null and isFallback: true on the composed chrome, making "no slug ⇒ no CTA" a trivial read — but the two fields are currently unconsumed. Flagging rather than hiding it: if you'd prefer the ribbon built here instead of in #623, say so and I'll fold it in.

When the applied-template endpoint cannot resolve a template for the
current content, the composed view previously rendered bare content
behind a "no template chrome is shown" banner — the toggle read as
having silently done nothing.

All three unresolvable states now compose against a minimal built-in
default template (title, featured image, content slot) and announce
why:

  - `missing` / `empty`        — content has no template selected
  - `missing` / `unknown-slug` — the selected slug resolves to nothing
  - `error`                    — the request failed outright

The first two read as warnings, the third as an error, but all land on
the same canvas so the toggle stays on and functional. A toast fires
once per toggle-on event; the latch keys on the message rather than a
boolean so picking a second broken template without leaving composed
mode still announces the new failure. The standing alert repeats the
copy for after the toast auto-dismisses.

The composed chrome now carries `templateSlug` (null on the fallback)
and `isFallback` so the #623 ribbon can show the fallback name and hide
its Edit template CTA — there is nothing to deep-link to.

Closes #624
@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@ViewFromTheBox, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a0e526e-a78a-4865-87aa-dd3c37a1be8d

📥 Commits

Reviewing files that changed from the base of the PR and between b40e120 and c5cef9c.

📒 Files selected for processing (7)
  • resources/js/visual-editor/editor/composed-view/__tests__/fallback.test.ts
  • resources/js/visual-editor/editor/composed-view/__tests__/use-fallback-toast.test.tsx
  • resources/js/visual-editor/editor/composed-view/default-template.ts
  • resources/js/visual-editor/editor/composed-view/fallback.ts
  • resources/js/visual-editor/editor/composed-view/index.ts
  • resources/js/visual-editor/editor/composed-view/use-fallback-toast.ts
  • resources/js/visual-editor/editor/editor-app.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@ViewFromTheBox ViewFromTheBox moved this from Backlog to In Review in ArtisanPack UI Overview Aug 3, 2026
@ViewFromTheBox
ViewFromTheBox marked this pull request as ready for review August 3, 2026 21:12
@ViewFromTheBox
ViewFromTheBox merged commit 73f0a30 into feature/templates-preview Aug 3, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In Review to Done in ArtisanPack UI Overview Aug 3, 2026
@ViewFromTheBox
ViewFromTheBox deleted the feature/624-composed-view-missing-template-fallback branch August 3, 2026 21:12
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.

1 participant