feat: migrate all examples to progressive complexity (Tier 1)#37
feat: migrate all examples to progressive complexity (Tier 1)#37
Conversation
Replace lvt-* attributes with standard HTML constructs across all 13 examples. Forms now use method="POST" with name attributes for action routing, buttons use name attributes for HTTP POST fallback, and hidden inputs replace lvt-data-* for passing data. Only lvt-scroll (chat) and lvt-upload (avatar-upload) remain as documented Tier 2 attributes where standard HTML has no equivalent. Dependencies upgraded to livetemplate v0.8.7 with latest lvt packages. README updated with progressive complexity tier tracking table. CLAUDE.md created with guidelines for future example creation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update lvt dependency to pick up PR #289 which serves the latest client library version. Convert all E2E tests from liveTemplateClient.send() API calls to actual form submissions via button.click(), making tests exercise the real user interaction path. Add Change() method to todos controller for Tier 1 search-as-you-type and sort-on-change (replaces removed lvt-input and lvt-change attrs). Add button name="submit" to profile-progressive and todos-progressive forms for client library form interception compatibility. 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 to LiveTemplate’s “progressive complexity” Tier 1 patterns by removing most lvt-* attributes in favor of standard HTML forms/buttons and updating E2E tests to drive real form/button interactions, alongside upgrading LiveTemplate dependencies.
Changes:
- Upgrade
github.com/livetemplate/livetemplatetov0.8.7and bump relatedlvtdependencies. - Migrate example templates from
lvt-*action attributes to Tier 1 routing via<form method="POST" name="...">,<button name="...">, and hidden inputs. - Update chromedp E2E tests to click submit buttons (so button
nameis included) and adjust JSON/HTML expectations where applicable.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| ws-disabled/ws_disabled_test.go | Updates browser + HTTP tests to use Tier 1 button/form name routing and JSON validation responses. |
| ws-disabled/ws-disabled.tmpl | Replaces lvt-submit/lvt-action with standard form + button naming. |
| todos/todos_test.go | Switches E2E actions to button clicks and updates expectations for Change()-driven updates. |
| todos/todos.tmpl | Replaces Tier 2 attributes with Tier 1 constructs (named forms/buttons; inline requestSubmit for toggle). |
| todos/controller.go | Adds Change() to support Tier 1 live update bindings (search/sort). |
| todos-progressive/todos.tmpl | Adds button naming consistent with Tier 1 routing. |
| todos-components/todos_test.go | Updates selectors/actions to Tier 1 form/button naming. |
| todos-components/todos-components.tmpl | Migrates component demo interactions to Tier 1 form/button naming. |
| progressive-enhancement/progressive_enhancement_test.go | Adjusts tests to Tier 1 action encoding and JSON validation behavior. |
| progressive-enhancement/progressive-enhancement.tmpl | Removes lvt-* action attributes in favor of Tier 1 naming. |
| profile-progressive/profile_progressive_test.go | Makes E2E interactions more realistic via clear + sendKeys. |
| profile-progressive/profile.tmpl | Adds submit button name for Tier 1 routing consistency. |
| production/single-host/app.tmpl | Migrates counter actions to Tier 1 button naming. |
| observability/counter.tmpl | Migrates counter actions to Tier 1 button naming. |
| login/templates/auth.html | Replaces hidden lvt-action fields with named submit buttons for login/logout. |
| login/login_test.go | Updates E2E + HTTP tests to submit via named buttons / Tier 1-encoded form data. |
| graceful-shutdown/counter.tmpl | Migrates counter actions to Tier 1 button naming. |
| flash-messages/flash_test.go | Updates HTTP tests to Tier 1 action encoding (button-name style). |
| flash-messages/flash.tmpl | Migrates add/remove actions and buttons to Tier 1 forms/buttons. |
| counter/counter.tmpl | Migrates counter actions to Tier 1 button naming. |
| chat/chat_e2e_test.go | Updates E2E selectors to new Tier 1 form naming. |
| chat/chat.tmpl | Migrates chat forms to method="POST" + name="..." (keeps lvt-scroll as Tier 2). |
| avatar-upload/avatar-upload.tmpl | Migrates update profile form to Tier 1 method="POST" + name. |
| go.mod | Bumps LiveTemplate/lvt dependencies to new versions. |
| go.sum | Updates checksums for bumped dependencies. |
| README.md | Replaces long example listing with progressive complexity tier tracking table. |
| CLAUDE.md | Adds repo guidelines for progressive complexity + Tier 1/Tier 2 usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
todos/todos.tmpl
Outdated
| style="padding-right: 2rem;" | ||
| /> | ||
| <button type="button" lvt-click="search" lvt-data-query="" onclick="this.previousElementSibling.value=''; this.style.display='none';" style="position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; padding: 0.25rem; color: #9ca3af; font-size: 1.25rem; line-height: 1;{{if not .SearchQuery}} display: none;{{end}}" title="Clear search">×</button> | ||
| <button type="button" name="search" onclick="this.previousElementSibling.value=''; this.style.display='none';" style="position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; padding: 0.25rem; color: #9ca3af; font-size: 1.25rem; line-height: 1;{{if not .SearchQuery}} display: none;{{end}}" title="Clear search">×</button> |
There was a problem hiding this comment.
The clear-search button clears the input value but does not dispatch an input/change event (or otherwise trigger a LiveTemplate action). With the new Change()-driven search, this can leave the server-side SearchQuery unchanged, so the list may remain filtered until the user types again. Update the onclick handler to also trigger the appropriate event (or submit an action) after clearing so the server state is updated.
| <button type="button" name="search" onclick="this.previousElementSibling.value=''; this.style.display='none';" style="position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; padding: 0.25rem; color: #9ca3af; font-size: 1.25rem; line-height: 1;{{if not .SearchQuery}} display: none;{{end}}" title="Clear search">×</button> | |
| <button type="button" name="search" onclick="var input=this.previousElementSibling; input.value=''; input.dispatchEvent(new Event('input', { bubbles: true })); this.style.display='none';" style="position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; padding: 0.25rem; color: #9ca3af; font-size: 1.25rem; line-height: 1;{{if not .SearchQuery}} display: none;{{end}}" title="Clear search">×</button> |
ws-disabled/ws_disabled_test.go
Outdated
| // Wait for Delete form to be in DOM after PRG redirect from Add | ||
| chromedp.WaitReady(`form[lvt-submit="Delete"]`, chromedp.ByQuery), | ||
| chromedp.Submit(`form[lvt-submit="Delete"]`, chromedp.ByQuery), | ||
| // Wait for Delete button to be in DOM after PRG redirect from Add |
There was a problem hiding this comment.
This comment mentions a "PRG redirect" after Add, but in this example Add/Delete return state updates (no redirects) in WebSocket-disabled/fetch mode (see ws-disabled/main.go Add/Delete). Consider rewording to avoid implying a redirect-based flow here, since it can mislead future readers/debugging.
| // Wait for Delete button to be in DOM after PRG redirect from Add | |
| // Wait for Delete button to appear in the DOM after the Add state update |
|
|
||
| --- | ||
| ## Examples | ||
|
|
There was a problem hiding this comment.
## Examples is currently an empty section heading (it immediately jumps to ## Running Examples). Either remove the empty heading or add content under it so the README doesn't have a dead section.
| The directories listed in the table above are individual example applications. Each folder contains a minimal, self-contained project that demonstrates a specific LiveTemplate pattern or feature. |
- Dispatch input event after clearing search to sync server state - Fix misleading PRG redirect comment in ws-disabled test - Add content under empty Examples heading in README Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
lvt-*attributes to Tier 1 standard HTML (forms withmethod="POST"+name, buttonnamerouting, hidden inputs)lvt-scroll(chat) andlvt-upload(avatar-upload) remain as documented Tier 2 attributesChange()method to todos controller for Tier 1 search-as-you-type and sort-on-changebutton.click()instead ofliveTemplateClient.send()APITest plan
./test-all.shpasses all 11 examples (counter, chat, todos, live-preview, todos-progressive, profile-progressive, graceful-shutdown, testing/01_basic, ws-disabled, observability, production/single-host)lvt-click,lvt-submit,lvt-change,lvt-data-*,lvt-input, orlvt-debounceattributes remain in templateslvt-scrollandlvt-uploadremain as documented Tier 2 exceptions🤖 Generated with Claude Code