Whole-form htmx + Alpine morph for transaction edit

Re-render the entire #wizard-form on each field edit and swap with
hx-swap="morph" so the focused input keeps focus/caret/value while typing.

- Field-level routes return the full form and target #wizard-form
- Key state-owning wrappers (account rows, simple-mode wrapper, vendor
  typeahead) so server-driven value changes re-init across the morph
- Guard tippy/$refs access in typeahead against stale post-morph state
- Round-trip simple/advanced mode via step-params[mode]
- Add e2e/transaction-edit-morph.spec.ts covering focus/caret preservation,
  vendor->account population, and repeated vendor changes
- Seed a second vendor/account for test isolation

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 07:40:30 -07:00
parent b6649a3d1d
commit cdb6bb6fe3
8 changed files with 681 additions and 185 deletions

View File

@@ -18,7 +18,15 @@ async function openEditModal(page: any, transactionIndex: number = 0) {
// The modal is now single-page (Edit Transaction). Click "Manual" tab to ensure
// the manual account coding form is active.
await page.click('button:has-text("Manual")');
// Transactions with 0-1 accounts open in "simple" mode, which has no account
// grid. Switch to "advanced" mode (a whole-form morph swap) so the grid the
// rest of these helpers manipulate is present.
const advancedLink = page.locator('a:has-text("Switch to advanced mode")');
if (await advancedLink.count()) {
await advancedLink.first().click();
}
// Wait for the manual form to appear
await page.waitForSelector('#account-grid-body');
}