refactor: migrate all examples to progressive complexity (Tier 1)#36
refactor: migrate all examples to progressive complexity (Tier 1)#36
Conversation
…r 1) Replace lvt-* attributes with standard HTML patterns across all examples: - lvt-click → <form method="POST"> + <button name="action"> - lvt-submit → <form method="POST"> + named submit buttons - lvt-data-* → <input type="hidden"> - lvt-action hidden inputs → button name routing Tier 2 attributes retained where no HTML equivalent exists: - lvt-scroll (chat), lvt-upload (avatar), lvt-disable-with (todos) - lvt-change on select/checkbox, lvt-input for search Add CLAUDE.md with progressive complexity conventions. Add tier tracking table to README.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add SetupUpdateEventListener() before "NonExistent" search dispatch to fix race where empty state text was checked before search completed - Use JS-based outerHTML read in Add_First_Todo to avoid stale node IDs after DOM patch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates the examples repo toward the “progressive complexity” Tier 1 pattern by replacing LiveTemplate-specific action attributes with standard HTML forms and named submit buttons, while retaining Tier 2 lvt-* attributes where HTML has no equivalent. It also adds documentation to keep future examples consistent.
Changes:
- Refactors templates to use
<form method="POST">+ named<button>actions + hidden inputs instead oflvt-click/lvt-submit/lvt-data-*in many examples. - Updates E2E + HTTP tests to target the new DOM structure and action triggers.
- Adds/updates repository docs (
CLAUDE.md,README.md) to describe and track tier usage across examples.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ws-disabled/ws_disabled_test.go | Updates browser + HTTP tests to reflect form/button-based actions. |
| ws-disabled/ws-disabled.tmpl | Replaces lvt-submit + lvt-action hidden fields with named submit buttons. |
| todos/todos_test.go | Updates E2E selectors and timing to click named buttons and re-read DOM after patches. |
| todos/todos.tmpl | Refactors row actions (toggle/delete), pagination, and clear-completed to use standard forms/buttons. |
| todos-components/todos_test.go | Updates selectors for confirm/clear actions after moving to form/button-based routing. |
| todos-components/todos-components.tmpl | Refactors add/delete/clear actions to use POST forms with named buttons (keeps Tier 2 checkbox toggle). |
| progressive-enhancement/progressive_enhancement_test.go | Updates assertions/selectors and HTTP payloads for new button-name routing. |
| progressive-enhancement/progressive-enhancement.tmpl | Replaces lvt-submit/lvt-action with POST forms and named action buttons. |
| production/single-host/app.tmpl | Switches counter controls from lvt-click buttons to a POST form with named submit buttons. |
| observability/counter.tmpl | Switches counter controls from lvt-click to a POST form with named submit buttons. |
| graceful-shutdown/counter.tmpl | Switches counter controls from lvt-click to a POST form with named submit buttons. |
| flash-messages/flash.tmpl | Migrates add/clear/simulate/remove item actions to standard POST forms/buttons. |
| counter/counter.tmpl | Switches counter controls from lvt-click to a POST form with named submit buttons. |
| chat/chat_e2e_test.go | Updates E2E selectors from lvt-submit forms to named join/send buttons. |
| chat/chat.tmpl | Replaces lvt-submit join/send with POST forms and named submit buttons (keeps lvt-scroll). |
| avatar-upload/avatar-upload.tmpl | Migrates profile save action from lvt-submit to POST form with named submit button. |
| README.md | Adds a tier-tracking table classifying examples by Tier 1 vs Tier 1+2 usage. |
| CLAUDE.md | Adds documented conventions for progressive complexity tiers and example patterns. |
Comments suppressed due to low confidence (2)
ws-disabled/ws_disabled_test.go:248
- These HTTP "no browser" tests post "action=add" etc, but the updated template submits via named buttons (e.g. ) without an "action" field. A real non-JS form submission will include the clicked button’s name/value (e.g. add=...) rather than action=add, so these tests may no longer reflect actual browser behavior. Either update the request bodies to include the submit button field(s) matching the template, or update the template to submit an explicit action field consistently.
form := strings.NewReader("action=add&label=&url=")
req, err := http.NewRequest("POST", server.URL, form)
if err != nil {
t.Fatalf("Failed to create request: %v", err)
}
progressive-enhancement/progressive_enhancement_test.go:279
- This test claims to simulate a no-JS browser, but it posts "action=add" while the updated HTML uses a named submit button () and no "action" field. To accurately simulate non-JS form submission, the request body should include the clicked submit button’s field (e.g. add=...) consistent with the template, or the template should be changed to submit an explicit action parameter.
// POST a new todo (simulating form submission without JS)
form := strings.NewReader("action=add&title=HTTP+test+todo")
req, err := http.NewRequest("POST", server.URL, form)
if err != nil {
t.Fatalf("Failed to create request: %v", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "text/html") // Indicate we want HTML, not JSON
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
todos/todos.tmpl
Outdated
| <form method="POST" style="display:contents;"> | ||
| <input type="hidden" name="id" value="{{ .ID }}"> | ||
| <td> | ||
| <button type="submit" name="toggle" class="toggle-btn" aria-label="Toggle completion"> | ||
| {{ if .Completed }}✓{{ else }}○{{ end }} | ||
| </button> | ||
| </td> | ||
| <td style="{{ if .Completed }}text-decoration: line-through; opacity: 0.6;{{ end }}"> | ||
| {{ .Text }} | ||
| </td> | ||
| <td> | ||
| <button type="submit" name="delete" class="secondary" aria-label="Delete this todo"> | ||
| Delete | ||
| </button> | ||
| </td> | ||
| </form> |
There was a problem hiding this comment.
In a table row, a element must only contain / (and related table content). Wrapping the row cells in a
(even with display:contents) produces invalid HTML and can lead to inconsistent DOM structure across browsers, which is especially risky for LiveTemplate’s DOM diff/patching. Consider moving the inside a , using separate per-action forms inside each cell, or using the HTML "form" attribute on controls so the can live outside the .| <form method="POST" style="display:contents;"> | |
| <input type="hidden" name="id" value="{{ .ID }}"> | |
| <td> | |
| <button type="submit" name="toggle" class="toggle-btn" aria-label="Toggle completion"> | |
| {{ if .Completed }}✓{{ else }}○{{ end }} | |
| </button> | |
| </td> | |
| <td style="{{ if .Completed }}text-decoration: line-through; opacity: 0.6;{{ end }}"> | |
| {{ .Text }} | |
| </td> | |
| <td> | |
| <button type="submit" name="delete" class="secondary" aria-label="Delete this todo"> | |
| Delete | |
| </button> | |
| </td> | |
| </form> | |
| <td> | |
| <form method="POST"> | |
| <input type="hidden" name="id" value="{{ .ID }}"> | |
| <button type="submit" name="toggle" class="toggle-btn" aria-label="Toggle completion"> | |
| {{ if .Completed }}✓{{ else }}○{{ end }} | |
| </button> | |
| </form> | |
| </td> | |
| <td style="{{ if .Completed }}text-decoration: line-through; opacity: 0.6;{{ end }}"> | |
| {{ .Text }} | |
| </td> | |
| <td> | |
| <form method="POST"> | |
| <input type="hidden" name="id" value="{{ .ID }}"> | |
| <button type="submit" name="delete" class="secondary" aria-label="Delete this todo"> | |
| Delete | |
| </button> | |
| </form> | |
| </td> |
CLAUDE.md
Outdated
| - **Form submission:** `<form method="POST">` with `<button type="submit" name="action">` | ||
| - **Multiple actions:** Use button `name` attribute for routing (e.g., `name="toggle"` routes to `Toggle()` method) |
There was a problem hiding this comment.
Tier 1 guidance is internally inconsistent: it first suggests using a generic submit button name="action", but later states that routing is based on the button name itself (name="X" routes to X()). Since the examples in this PR use name="add"/"delete"/etc, please clarify which pattern is the intended convention (and update this doc accordingly) to avoid future examples/tests diverging.
| - **Form submission:** `<form method="POST">` with `<button type="submit" name="action">` | |
| - **Multiple actions:** Use button `name` attribute for routing (e.g., `name="toggle"` routes to `Toggle()` method) | |
| - **Form submission:** `<form method="POST">` with at least one `<button type="submit" name="ActionName">` (e.g., `name="toggle"`) | |
| - **Multiple actions:** Use distinct semantic button `name` attributes for routing (e.g., `name="toggle"` routes to `Toggle()` method, `name="delete"` routes to `Delete()` method) |
- Move forms back inside <td> elements in todo-item partial to fix invalid HTML (<form> is not allowed as child of <tr>) - Clarify CLAUDE.md Tier 1 guidance: use semantic button names like name="add", name="delete" (not generic name="action") Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
lvt-click,lvt-submit,lvt-data-*attributes with<form method="POST">+ named<button>elements +<input type="hidden">across 11 exampleslvt-scroll(chat),lvt-upload(avatar),lvt-input/lvt-debounce(search),lvt-change(sort/toggle),lvt-disable-with(button states)CLAUDE.mddocumenting progressive complexity conventions for future examplesREADME.mdclassifying all 14 examplesTest plan
go build)🤖 Generated with Claude Code