Skip to content

feat: add patterns example app — Session 1 (scaffold + forms #1-7)#59

Merged
adnaan merged 4 commits intomainfrom
patterns-session-1
Apr 12, 2026
Merged

feat: add patterns example app — Session 1 (scaffold + forms #1-7)#59
adnaan merged 4 commits intomainfrom
patterns-session-1

Conversation

@adnaan
Copy link
Copy Markdown
Contributor

@adnaan adnaan commented Apr 11, 2026

Summary

  • Implements the patterns example app scaffold and all 7 Forms & Editing patterns from the patterns proposal
  • Each pattern is a focused, isolated demo with its own handler, controller, template, and E2E tests
  • Shared layout template with consolidated dev/prod CSS+JS loading and category nav
  • Index page catalogs all 31 patterns (7 implemented, 24 marked as coming soon)

Patterns implemented

  1. Click To Edit — toggle view/edit with {{if}} conditional rendering
  2. Edit Row — inline table row editing with data-key identity
  3. Inline ValidationChange() with ValidateForm() and .lvt.HasError/.lvt.Error
  4. Bulk Update — batch checkbox operations with ctx.GetBool()
  5. Reset User Input — form auto-clear after successful submission
  6. File Upload — Tier 1 multipart + Tier 2 lvt-upload chunked upload
  7. Preserving File Inputslvt-form:preserve retains values across re-renders

Architecture

  • One controller per pattern (avoids method name conflicts)
  • WithParseFiles("templates/layout.tmpl", "templates/forms/<pattern>.tmpl") for template composition
  • livetemplate.New("layout", ...) with layout as main template entry point

Test plan

  • All 8 test functions pass (30 subtests total)
  • UI_Standards checks on every pattern (CSP compliance, meta tags, container width)
  • Visual_Check subtests (skipped without LVT_VISUAL_CHECK=true)
  • go build and go vet clean
  • "patterns" added to WORKING_EXAMPLES in test-all.sh
  • Manual browser review of all 7 patterns

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 11, 2026 12:54
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 patterns/ working example app that scaffolds a “Patterns” catalog and implements the first 7 Forms & Editing patterns (with shared layout + E2E coverage) as described in the patterns proposal.

Changes:

  • Introduces a shared layout + index catalog page listing all pattern categories (7 implemented, others marked coming soon).
  • Implements 7 Forms & Editing pattern handlers/controllers, state structs, templates, and sample data.
  • Adds chromedp E2E test suite for the index and each implemented pattern; registers patterns in test-all.sh.

Reviewed changes

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

Show a summary per file
File Description
test-all.sh Adds patterns to the working examples list so it’s exercised by the full test script.
patterns/templates/layout.tmpl New shared layout with dev/prod LiveTemplate client + CSS loading.
patterns/templates/index.tmpl New index catalog template listing categories + pattern links.
patterns/templates/forms/click-to-edit.tmpl Pattern #1 template (view/edit toggle).
patterns/templates/forms/edit-row.tmpl Pattern #2 template (inline table row editing).
patterns/templates/forms/inline-validation.tmpl Pattern #3 template (server-side validation feedback).
patterns/templates/forms/bulk-update.tmpl Pattern #4 template (batch checkbox updates).
patterns/templates/forms/reset-input.tmpl Pattern #5 template (auto-reset after submit).
patterns/templates/forms/file-upload.tmpl Pattern #6 template (multipart + chunked upload UI).
patterns/templates/forms/preserve-inputs.tmpl Pattern #7 template (preserve inputs across re-renders).
patterns/state_forms.go Adds state structs for the 7 patterns.
patterns/patterns_test.go Adds E2E suite covering index + 7 patterns with UI standards checks.
patterns/main.go App entrypoint wiring routes + layout composition.
patterns/handlers_forms.go Controllers + handlers for patterns #1#7.
patterns/data.go Sample data helpers + pattern catalog structure.

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

Comment thread patterns/main.go
)
tmpl := livetemplate.Must(livetemplate.New("layout", opts...))
return tmpl.Handle(&IndexController{}, livetemplate.AsState(&IndexState{
Title: "LiveTemplate Patterns",
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

The index page sets Title to "LiveTemplate Patterns", but the shared layout appends " — LiveTemplate Patterns" to every <title>, resulting in "LiveTemplate Patterns — LiveTemplate Patterns" on the index. Consider setting the index Title to something shorter (e.g., "Patterns"), or make the layout conditional so the suffix isn't duplicated.

Suggested change
Title: "LiveTemplate Patterns",
Title: "Patterns",

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +15
<input name="name" value="{{.Name}}">
<input name="email" value="{{.Email}}">
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

These inputs have no associated (or aria-label/aria-labelledby), which makes the inline row editor difficult to use with screen readers. Add labels (they can be visually hidden) or add explicit aria-label attributes for the name/email fields.

Suggested change
<input name="name" value="{{.Name}}">
<input name="email" value="{{.Email}}">
<input name="name" value="{{.Name}}" aria-label="Name">
<input name="email" value="{{.Email}}" aria-label="Email">

Copilot uses AI. Check for mistakes.
<p><small>Form clears automatically after successful submission — no extra attributes needed.</small></p>
<form method="POST">
<fieldset role="group">
<input name="message" placeholder="Type a message..." required>
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

This text input relies on placeholder text only and has no label/aria-label, which is an accessibility issue for screen readers. Add an explicit (visually-hidden is fine) or an aria-label so the field has an accessible name.

Suggested change
<input name="message" placeholder="Type a message..." required>
<input name="message" aria-label="Message" placeholder="Type a message..." required>

Copilot uses AI. Check for mistakes.
<tr data-key="{{.ID}}">
<td>{{.Name}}</td>
<td>{{.Email}}</td>
<td><input type="checkbox" name="active-{{.ID}}" {{if .Active}}checked{{end}}></td>
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

The per-row "Active" checkbox inputs have no accessible name. Consider adding an aria-label (e.g., including the user's name) or associating each checkbox with a so screen readers can identify what each checkbox controls.

Suggested change
<td><input type="checkbox" name="active-{{.ID}}" {{if .Active}}checked{{end}}></td>
<td><input type="checkbox" name="active-{{.ID}}" aria-label="Active for {{.Name}}" {{if .Active}}checked{{end}}></td>

Copilot uses AI. Check for mistakes.
<dt>Last Name</dt><dd>{{.LastName}}</dd>
<dt>Email</dt><dd>{{.Email}}</dd>
</dl>
<button name="edit">Edit</button>
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

In view mode the "Edit" button is not inside a

, so Tier 1 (no-JS) fallback can't submit an action to reach the Edit() handler. Per the repo’s Tier 1 guidance (CLAUDE.md:7-21, 49-55), actions should be routed via standard form submissions + button names. Wrap the button in a POST form (or use a button with a form="..." attribute pointing at a POST form).

Suggested change
<button name="edit">Edit</button>
<form method="POST">
<button name="edit">Edit</button>
</form>

Copilot uses AI. Check for mistakes.
Comment thread patterns/patterns_test.go Outdated
Comment on lines +496 to +498
// Click Update
chromedp.Click(`button[name="bulkUpdate"]`, chromedp.ByQuery),
e2etest.WaitFor(`true`, 2*time.Second), // Wait for re-render
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

This uses e2etest.WaitFor(true, ...) after clicking Update, which doesn’t wait on a meaningful condition and can be flaky (it’s effectively a time-based delay). The repo’s E2E checklist explicitly recommends condition-based waits (CLAUDE.md:78-80). Wait for a concrete DOM condition that reflects the update (e.g., checked state of active-1/active-3) before asserting.

Suggested change
// Click Update
chromedp.Click(`button[name="bulkUpdate"]`, chromedp.ByQuery),
e2etest.WaitFor(`true`, 2*time.Second), // Wait for re-render
// Click Update and wait for the updated checkbox state to be reflected in the DOM
chromedp.Click(`button[name="bulkUpdate"]`, chromedp.ByQuery),
e2etest.WaitFor(`!document.querySelector('input[name="active-1"]').checked && document.querySelector('input[name="active-3"]').checked`, 2*time.Second),

Copilot uses AI. Check for mistakes.
Comment thread patterns/patterns_test.go Outdated
chromedp.SendKeys(`input[name="name"]`, "Test Name", chromedp.ByQuery),
chromedp.SendKeys(`textarea[name="description"]`, "Test Description", chromedp.ByQuery),
chromedp.Click(`button[type="submit"]`, chromedp.ByQuery),
e2etest.WaitFor(`true`, 3*time.Second),
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

This uses e2etest.WaitFor(true, ...) after form submission, which doesn’t wait on any specific post-submit condition and can be flaky. The repo’s E2E checklist recommends condition-based waits (CLAUDE.md:78-80). Prefer waiting for a concrete DOM change (e.g., a success indicator, or the expected input value being present) before reading/asserting field values.

Suggested change
e2etest.WaitFor(`true`, 3*time.Second),
e2etest.WaitFor(`document.querySelector('input[name="name"]') !== null && document.querySelector('input[name="name"]').value === "Test Name"`, 3*time.Second),

Copilot uses AI. Check for mistakes.
Implements the patterns example app scaffold and all 7 Forms & Editing
patterns from the patterns proposal (#330). Each pattern is a focused,
isolated demo with its own handler, controller, template, and E2E tests.

Patterns implemented:
1. Click To Edit — toggle view/edit with conditional rendering
2. Edit Row — inline table row editing with data-key identity
3. Inline Validation — Change() with ValidateForm and error display
4. Bulk Update — batch checkbox operations
5. Reset User Input — auto-clear forms after submission
6. File Upload — Tier 1 multipart + Tier 2 chunked upload
7. Preserving File Inputs — lvt-form:preserve across re-renders

Also includes: shared layout template, categorized index page with all
31 patterns listed (7 implemented, 24 coming soon), and comprehensive
chromedp E2E tests with UI_Standards and Visual_Check subtests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@adnaan adnaan force-pushed the patterns-session-1 branch from 97091fa to 12b5682 Compare April 12, 2026 05:19
adnaan and others added 3 commits April 12, 2026 10:58
Replace <dl> with a Pico-styled table for cleaner key-value display.
Add outline class to Edit button to avoid full-width block styling.
Update tests to match new HTML structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each pattern is a separate LiveTemplate handler. SPA navigation between
handlers shows stale content from the previous handler. Adding
lvt-nav:no-intercept forces full page loads when navigating between
the index page and individual pattern pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Patterns app improvements after Session 1:

- Cross-handler SPA navigation: add dedicated E2E test suite
  (cross_handler_nav_test.go) covering index↔pattern nav, back/forward
  buttons, title updates, and WebSocket reconnection. Removed the
  lvt-nav:no-intercept workaround from index.tmpl/layout.tmpl since
  client v0.8.22 now handles cross-handler navigation correctly.

- File Upload (#6):
  - Button name="upload" routes to Upload() handler method
  - WithUpload("document", ...) for Tier 1 multipart uploads
  - Tier 2 ChunkSize=1024 so upload progress is visible for demo files
  - Flash messages via FlashTag for success/error
  - Tier 2 form renders in-progress uploads via {{range .lvt.Uploads}}
  - Form-lvt:preserve to prevent reset after submit
  - E2E tests: Submit_Without_File, Tier1_Upload_With_File, Form_Structure
  - attachFileViaDataTransfer test helper (Docker-Chrome compatible)

- Preserving Form Inputs (#7):
  - ctx.SetFlash("success", "Saved: "+Name) for visual feedback
  - WithUpload("attachment", ...) so multipart submissions are parsed
  - Use .lvt.ErrorTag helper instead of manual HasError/Error markup
  - New E2E tests: Submit_Shows_Flash, Form_Values_Preserved_After_Submit,
    Values_Survive_Rerender, Submit_With_File_Attached (regression for
    the fieldset-disabled FormData bug fixed in livetemplate/client#58)

- Bulk Update (#4):
  - Add success flash "Updated N user(s)" via FlashTag
  - Test verifies flash text via output[data-flash] selector

- Inline Validation (#3):
  - Use .lvt.AriaInvalid and .lvt.ErrorTag helpers instead of manual
    HasError/Error patterns

- LVT_LOCAL_CLIENT env var in main.go lets developers serve a local
  client build (useful during client development/testing).

All 9 E2E test functions pass against livetemplate/client v0.8.22 from
CDN (no local override required).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@adnaan adnaan merged commit 471ac9f into main Apr 12, 2026
18 of 19 checks passed
adnaan added a commit to livetemplate/livetemplate that referenced this pull request Apr 13, 2026
…n notes

Session 1 shipped as livetemplate/examples#59 (Forms & Editing patterns
#1-7 + scaffold + cross-handler nav tests, merged as 471ac9f). This
updates the Session 1 tracker and adds an "Implementation Notes"
subsection capturing non-obvious learnings so Session 2 runs with fewer
iterations.

Notes captured: FlashTag render output (output[data-flash], not ins/del),
State struct Title+Category shape, data.go :: allPatterns() data-driven
index, setupTest/attachFileViaDataTransfer/runUIStandards helpers,
WaitFor("true") anti-pattern, WithUpload server registration for Tier 1
multipart, and local dev/visual-check flags.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
adnaan added a commit to livetemplate/livetemplate that referenced this pull request Apr 13, 2026
)

* docs(proposals): mark patterns session 1 complete + add implementation notes

Session 1 shipped as livetemplate/examples#59 (Forms & Editing patterns
#1-7 + scaffold + cross-handler nav tests, merged as 471ac9f). This
updates the Session 1 tracker and adds an "Implementation Notes"
subsection capturing non-obvious learnings so Session 2 runs with fewer
iterations.

Notes captured: FlashTag render output (output[data-flash], not ins/del),
State struct Title+Category shape, data.go :: allPatterns() data-driven
index, setupTest/attachFileViaDataTransfer/runUIStandards helpers,
WaitFor("true") anti-pattern, WithUpload server registration for Tier 1
multipart, and local dev/visual-check flags.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(proposals): address PR #334 bot review feedback

- Fix UploadConfig example to use named fields (Copilot): the struct
  has Accept []string as its first field, so UploadConfig{MaxFileSize,
  MaxEntries} positional init won't compile. Match the pattern used in
  the actual Session 1 handlers_forms.go.
- Clarify @latest CDN tradeoff (Claude): document that we accept the
  risk of a client release breaking a demo, rather than pin a version.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (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