Skip to content

feat(ui): form-level onChange with per-input debounce (#2151)#2265

Merged
viniciusdacal merged 8 commits into
mainfrom
viniciusdacal/form-onchange-debounce
Apr 4, 2026
Merged

feat(ui): form-level onChange with per-input debounce (#2151)#2265
viniciusdacal merged 8 commits into
mainfrom
viniciusdacal/form-onchange-debounce

Conversation

@viniciusdacal
Copy link
Copy Markdown
Contributor

Summary

  • Adds <form onChange={handler}> that fires with all form values (FormValues object) when any child input changes
  • Adds debounce={ms} prop on <input>, <textarea>, <select> to control per-input callback timing
  • Compiler transforms onChange on <form> to __formOnChange(el, handler) and debounce={N} to data-vertz-debounce="N"

Closes #2151

Public API Changes

Breaking

  • onChange on <form> now receives FormValues instead of a DOM Event. Use ref + addEventListener for the raw DOM event.

Additions

Compiler

Phases

  1. Runtime helper__formOnChange with microtask batching, per-input debounce via data-vertz-debounce, disposal scope cleanup (18 tests)
  2. Compiler transforms — Rust transforms for debounce prop rename and form onChange routing (12 new compiler tests, 81 total)
  3. JSX types + theme — Element-specific attribute interfaces, theme component prop forwarding (type-level tests + 4 component tests)
  4. Integration + docs — E2E acceptance test (6 scenarios), forms guide update, changeset

Review Findings

Adversarial review found 1 blocker (flush-after-disposal race condition) — fixed by adding disposed flag in flush(). 6 should-fix items addressed (NaN guard, test coverage, comments).

Test plan

  • 18 unit tests for __formOnChange runtime helper pass
  • 6 E2E acceptance tests (search/filter, rapid typing, mixed interaction, form.reset, cleanup, type safety)
  • 81 compiler JSX transform tests pass (12 new, 69 existing)
  • Type-level tests verify FormValues handler and debounce: number
  • Rust quality gates: cargo test, clippy clean, fmt clean
  • TypeScript typecheck clean for packages/ui

🤖 Generated with Claude Code

@github-actions github-actions Bot force-pushed the viniciusdacal/form-onchange-debounce branch 2 times, most recently from c07c6ea to 176b2bb Compare April 4, 2026 21:23
viniciusdacal and others added 8 commits April 4, 2026 21:23
…2151

Runtime helper for form-level onChange with per-input debounce support.
Listens to input events via delegation, reads data-vertz-debounce from
targets, coalesces via microtask batching, cancels pending timers on
immediate flush. Includes full test coverage (17 tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…2151

- Transform debounce={N} to data-vertz-debounce on input/textarea/select
- Transform <form onChange={handler}> to __formOnChange(el, handler)
- Register __formOnChange in import injection system
- 12 new JS-side compiler tests covering all transform cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add FormHTMLAttributes with onChange?: (values: FormValues) => void
- Add InputHTMLAttributes, TextareaHTMLAttributes, SelectHTMLAttributes with debounce?: number
- Update IntrinsicElements with specific types for form/input/textarea/select
- Forward debounce prop as data-vertz-debounce in ComposedInput and ComposedTextarea
- Type-level tests verify correct and incorrect usage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- E2E acceptance test covering search/filter pattern, rapid typing,
  mixed interactions, form.reset(), cleanup, and type safety
- Document form-level onChange, per-input debounce, limitations,
  escape hatch, and form() interaction in forms guide
- Add changeset for @vertz/ui, @vertz/ui-primitives, @vertz/native-compiler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add disposed flag to prevent handler fire after cleanup (B1)
- Guard parseInt against NaN with || 0 fallback (S4)
- Add test for cleanup-during-pending-flush race condition (S2)
- Improve comments for FormValues cast and reset timing (S1, S3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ross-test leak

The CSS injection tests relied on resetInjectedStyles() to clean up, but
orphaned sheets from other test files remained in document.adoptedStyleSheets.
Clear adoptedStyleSheets directly in beforeEach/afterEach for reliable isolation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rm onChange #2151

Document the debounce prop on Input and Textarea in the component library
guide, and add a cross-reference to form-level onChange in the form API
reference page.

Co-Authored-By: Claude Opus 4.6 <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.

feat(ui): form-level onChange with per-input debounce

1 participant