Skip to content

feat(recipes): fold progressive-enhancement (subsume ws-disabled)#14

Merged
adnaan merged 2 commits into
mainfrom
recipes-progressive-enhancement-fold
May 10, 2026
Merged

feat(recipes): fold progressive-enhancement (subsume ws-disabled)#14
adnaan merged 2 commits into
mainfrom
recipes-progressive-enhancement-fold

Conversation

@adnaan
Copy link
Copy Markdown
Contributor

@adnaan adnaan commented May 10, 2026

Summary

  • Folds examples/progressive-enhancement/ into docs/content/recipes/progressive-enhancement/_app/ and subsumes examples/ws-disabled/ — the only non-trivial difference between the two is one option flag.
  • Recipe tells a unified three-tier graceful-degradation story from a single controller + template:
    • Tier A — JS on, WS on (default).
    • Tier B — JS on, WS off (WithWebSocketDisabled() appended).
    • Tier C — JS off entirely (browser POST → server 303 See Other with flash cookie).
  • cmd/site mounts the same handler twice: /apps/progressive-enhancement/ and /apps/progressive-enhancement/no-ws/. Bookmarks domain from examples/ws-disabled/ is dropped — it added nothing pedagogical over a todo list, and unifying around one domain lets the recipe focus the lesson on the option flag rather than two unrelated CRUD apps.
  • New e2e suite docs/e2e/progressive-enhancement/ merges the two source examples' tests into Tier A (browser+WS), Tier B (browser+no-WS + WebSocket-rejected sentinel), and Tier C (raw HTTP POST + 303 + JSON validation) groups. Sibling test-docs-progressive-enhancement cross-repo CI job comes in a follow-up livetemplate PR.

Architecture notes

  • No handlerOnce singleton — needed two mounts with different option sets, so Handler builds a fresh livetemplate.New(...) + mux per call. The embedded template is still extracted once via tmplOnce.
  • examples/progressive-enhancement/ and examples/ws-disabled/ stay in place per the leave-in-place rule for migrations.
  • Tier C is demonstrated via prose ("disable JS in DevTools, refresh"), not a third sandbox iframe — embed-lvt doesn't expose sandbox and the e2e tests assert the PRG path directly.

Test plan

  • go build ./... clean (root and docs/e2e/)
  • curl smoke: Tier A 200, Tier B 200 with X-Livetemplate-Websocket: disabled, Tier B WS upgrade rejected with 400, Tier C POST → 303 + lvt-flash cookie
  • cd docs/e2e/progressive-enhancement && go test -timeout 15m -count=1 ./... — 16 subtests across all three tiers green (~18s)
  • iPhone manual over Tailscale: recipe page with both embed-lvt iframes interactive; tightened the comparison tables to bullet lists for narrow viewports
  • gofmt -l and go vet clean
  • Production deploy: confirm catalog routes return 200 after merge

🤖 Generated with Claude Code

The same controller and template degrade through three transports:
Tier A (JS+WS) is the default mount, Tier B is the same handler with
WithWebSocketDisabled() at /no-ws/, Tier C is Tier A viewed without
JS — the framework's POST-Redirect-GET path falls out of native
browser form behavior.

Subsuming the ws-disabled example here drops the optional
auth-and-fallback composite recipe by one item; the no-WS angle is
just one option flag on the same PE app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 10, 2026 11:10
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new “Progressive enhancement: graceful degradation” recipe to the docs site, folding the previous progressive-enhancement / ws-disabled examples into a single app + narrative that demonstrates three transport tiers (WS, fetch fallback, and raw form POST/PRG).

Changes:

  • Introduces the new recipe page plus its embedded app package (controller/handler/template) under content/recipes/progressive-enhancement/.
  • Mounts the app twice in cmd/site (/apps/progressive-enhancement/ and /apps/progressive-enhancement/no-ws/) and adds the recipe to the docs navigation.
  • Adds a dedicated E2E suite + local test server under e2e/progressive-enhancement/ covering Tier A/B/C behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
e2e/progressive-enhancement/progressive_enhancement_test.go New E2E coverage for Tier A (WS), Tier B (no-WS + sentinel), and Tier C (PRG + JSON validation).
e2e/progressive-enhancement/main.go Test-server binary mounting the recipe twice for Tier A/B, configured for dev-mode and permissive origin checks.
content/tinkerdown.yaml Adds the new recipe to the site navigation.
content/recipes/progressive-enhancement/index.md New recipe documentation explaining the 3-tier progressive enhancement story with embeds and code excerpts.
content/recipes/progressive-enhancement/_app/progressive-enhancement.tmpl New template implementing a single form-based UI intended to work across WS/fetch/PRG.
content/recipes/progressive-enhancement/_app/handler.go New handler package exporting Handler(opts...) for mounting under different option sets.
content/recipes/progressive-enhancement/_app/controller.go New controller/state implementing add/toggle/delete + validation and InputTitle persistence.
cmd/site/main.go Mounts the recipe at two routes and factors a shared allowedOrigins list for some recipes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


cleanup := func() {
if cmd.Process != nil {
cmd.Process.Kill()
<td>
<form method="POST" name="delete" class="inline">
<input type="hidden" name="id" value="{{.ID}}">
<button type="submit" name="delete" class="compact contrast outline">&#10005;</button>
Comment on lines +56 to +60
if success := ctx.GetString("success"); success != "" {
ctx.SetFlash("success", success)
}
if errorMsg := ctx.GetString("error"); errorMsg != "" {
ctx.SetFlash("error", errorMsg)
Comment on lines +508 to +512
req.Header.Set("Upgrade", "websocket")
req.Header.Set("Connection", "Upgrade")

client := &http.Client{}
resp, err := client.Do(req)
Comment on lines +365 to +367
chromedp.WaitReady("body"),
waitForWSClient(10*time.Second),
chromedp.OuterHTML("html", &html),
Comment thread cmd/site/main.go Outdated
Comment on lines +66 to +70
allowedOrigins := []string{
"https://livetemplate.fly.dev",
"https://livetemplate-docs-staging.fly.dev",
"http://localhost:8080",
"http://localhost:8084",
<!-- region:toggle-form -->
<form method="POST" name="toggle" class="inline">
<input type="hidden" name="id" value="{{.ID}}">
<button type="submit" name="toggle" class="compact secondary outline">
Comment on lines +76 to +85
newID := fmt.Sprintf("%d", time.Now().UnixNano())
state.Items = append(state.Items, Todo{
ID: newID,
Title: strings.TrimSpace(input.Title),
Completed: false,
CreatedAt: formatTime(),
})

state.InputTitle = ""
ctx.SetFlash("success", fmt.Sprintf("Added: %s", input.Title))
- controller: drop URL query-param → flash bridge in Mount (banner spoof + Tier C uses lvt-flash cookie); use trimmed title in success flash so the list and the banner agree
- template: aria-label on toggle (Mark complete/incomplete) and delete buttons for assistive tech
- cmd/site: hoist allowedOrigins to one shared list across patterns/todos/PE so the allowlist can't drift
- e2e: real gorilla/websocket dial for the no-WS sentinel (a hand-rolled GET with bare Upgrade headers false-passes when WS is enabled); cmd.WaitDelay+Wait to bound the reap (go run's child holds stdout open after SIGKILL); chromedp.ByQuery on Tier B WaitReady/OuterHTML for selector consistency

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@adnaan adnaan merged commit 0f32806 into main May 10, 2026
2 checks passed
adnaan added a commit to livetemplate/livetemplate that referenced this pull request May 10, 2026
Sibling job to test-docs-todos (#402), running the
docs/e2e/progressive-enhancement suite against this branch's core
changes. Mirrors the todos job verbatim with the working-directory
swapped — adds Tier A/B/C coverage (WS, no-WS, raw POST PRG) for the
progressive-enhancement recipe folded in livetemplate/docs#14.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants