Repository navigation
Composed view: missing/invalid template fallback + toast - #694
Conversation
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
|
Warning Review limit reached
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 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
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. Comment |
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:
missing/unknown-slugmissing/emptyerror(network / 5xx)All three land on the same canvas, so the toggle stays on and the content stays editable.
Closes: #624
Type of Change
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
composed-view/default-template.ts— the fallback block tree as a client-side constant (artisanpack/post-title→post-featured-image→post-content) plusdefaultTemplateName(). 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 findsartisanpack/post-contentas the content slot exactly as it would in a real template.composed-view/fallback.ts—selectComposedTemplate()routes the three miss states to the default tree withslug: null;composedFallbackNotice()owns the copy. Pure functions, so the routing is testable without mounting the editor.composed-view/use-fallback-toast.ts— fires the toast, mirroringuseSaveNotifications. 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 returningnull; the two old miss/errorAlertbranches collapse intocomposedFallbackNotice(). 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) +isFallbackadded 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:
feature/templates-preview)Tests Performed:
*/applied-templatein devtools — error-toned toast, same fallback canvas.Accessibility Tests Run
Details: No new interactive controls are introduced. The toast is announced through
ToastProvider's existing live region and the standing alert keeps therole="status"it already carried; the toggle remains keyboard-operable and is not disabled by the fallback. Tone is carried by the existingAlert/Toastcolorvariants, so contrast is inherited from the theme rather than newly specified.Tests Added
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 aclientId);idle/loadingcompose nothing rather than flashing the default; and each copy variant, including the contradictoryunknown-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.tsxfails 18 tests withwindow.localStorageundefined — confirmed failing identically onfeature/templates-previewbefore 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
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
Note for reviewers: one AC is not fully closed here
#623 is still open and the ribbon does not exist yet. This PR plumbs the data it needs —
templateSlug: nullandisFallback: trueon 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.