feat(forms): shared FormGuard submit guard (PMS-517) #351

Merged
nrupard merged 1 commit from feat/PMS-517-form-submit-guard into main 2026-06-25 19:47:10 +02:00
Owner

What

Second foundation piece for the PMS-515 epic (PMS-516 is now merged on main). Adds FormGuard (src/utils/form_guard.rs), the submit-time counterpart to the per-field blur validation from PMS-516.

How

A submit handler runs each required field through the guard, then bails if blocked:

let mut guard = FormGuard::new();
title_error.set(guard.field("title", &title_v, "Title", &[Rule::Required]));
company_error.set(guard.field("company", &company_id_v, "Company", &[Rule::Uuid]));
if guard.blocked() { is_submitting.set(false); return; }
  • field(id, value, label, rules) -> String returns that field's message (empty when valid, clearing a stale message) and records the first invalid field. The caller sets its own error signal with the return value.
  • note_invalid(focus_id) covers cross-field / non-field failures (XOR pickers, server-only errors) with no inline slot.
  • blocked() focuses the first invalid field and returns whether to abort the submit.

Generalizes the per-field PMS-514 fix into one reusable helper: every failing field surfaces at once (each into its own inline slot), submit is blocked while any field is invalid, first invalid field is focused.

Design notes

  • No Dioxus context / no component coupling - a plain helper called inside the handler, so it works identically for <form onsubmit> page forms and onclick-submit modals. Avoids the re-render / hook-ordering pitfalls of an auto-registration context.
  • Signal-free core, so the accumulation logic is pure and unit-tested (7 cases).
  • focus_field is gated on target_arch = "wasm32" (repo web_sys convention, lib.rs:80): web_sys imports panic off-wasm and the web feature is on even for host test builds, so on host/test it is a no-op. (Real bug caught by the unit tests on first run.)

Scope

Delivers the guard helper + tests. Adopting it across the ~30 forms and deleting the bespoke per-handler checks is PMS-518.

Verification

just check parity (rust-builder image): clippy --all-targets -- -D warnings, fmt --check, cargo test --lib form_guard (7 passed), cargo check --target wasm32-unknown-unknown - all green.

Acceptance criteria (PMS-517)

  • Shared submit guard validates all fields and blocks the submit (no POST) while any field is invalid.
  • All failing fields surfaced at once, each in its own inline slot.
  • First invalid field focused on a blocked submit.
  • Cross-field / non-field errors have a path (note_invalid + the handler's form-level banner).
  • Works for onsubmit page forms and onclick-submit modals.
  • just check green.
## What Second foundation piece for the PMS-515 epic (PMS-516 is now merged on `main`). Adds `FormGuard` (`src/utils/form_guard.rs`), the submit-time counterpart to the per-field blur validation from PMS-516. ## How A submit handler runs each required field through the guard, then bails if blocked: ```rust let mut guard = FormGuard::new(); title_error.set(guard.field("title", &title_v, "Title", &[Rule::Required])); company_error.set(guard.field("company", &company_id_v, "Company", &[Rule::Uuid])); if guard.blocked() { is_submitting.set(false); return; } ``` - `field(id, value, label, rules) -> String` returns that field's message (empty when valid, clearing a stale message) and records the first invalid field. The caller sets its own error signal with the return value. - `note_invalid(focus_id)` covers cross-field / non-field failures (XOR pickers, server-only errors) with no inline slot. - `blocked()` focuses the first invalid field and returns whether to abort the submit. Generalizes the per-field PMS-514 fix into one reusable helper: every failing field surfaces at once (each into its own inline slot), submit is blocked while any field is invalid, first invalid field is focused. ## Design notes - No Dioxus context / no component coupling - a plain helper called inside the handler, so it works identically for `<form onsubmit>` page forms and `onclick`-submit modals. Avoids the re-render / hook-ordering pitfalls of an auto-registration context. - Signal-free core, so the accumulation logic is pure and unit-tested (7 cases). - `focus_field` is gated on `target_arch = "wasm32"` (repo `web_sys` convention, `lib.rs:80`): `web_sys` imports panic off-wasm and the `web` feature is on even for host test builds, so on host/test it is a no-op. (Real bug caught by the unit tests on first run.) ## Scope Delivers the guard helper + tests. Adopting it across the ~30 forms and deleting the bespoke per-handler checks is PMS-518. ## Verification `just check` parity (rust-builder image): `clippy --all-targets -- -D warnings`, `fmt --check`, `cargo test --lib form_guard` (7 passed), `cargo check --target wasm32-unknown-unknown` - all green. ## Acceptance criteria (PMS-517) - [x] Shared submit guard validates all fields and blocks the submit (no POST) while any field is invalid. - [x] All failing fields surfaced at once, each in its own inline slot. - [x] First invalid field focused on a blocked submit. - [x] Cross-field / non-field errors have a path (`note_invalid` + the handler's form-level banner). - [x] Works for `onsubmit` page forms and `onclick`-submit modals. - [x] `just check` green.
feat(forms): shared FormGuard submit guard (PMS-517)
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 8m53s
Create release / Create release from merged PR (pull_request) Has been skipped
39c1210d58
Second foundation piece for the PMS-515 epic, stacked on PMS-516. Adds src/utils/form_guard.rs: FormGuard, the submit-time counterpart to the per-field blur validation from PMS-516. A submit handler runs every required field through guard.field(id, value, label, rules) - which returns that field's message (empty when valid, so a stale message is cleared) and records the first invalid field - then bails on guard.blocked(), which focuses the first invalid field and returns whether to abort. note_invalid covers cross-field / non-field failures (XOR pickers, server-only errors) that have no inline slot.

This generalizes the per-field PMS-514 fix into one reusable helper: every failing field surfaces at once (each into its own inline error slot via the caller's error signal), the submit is blocked while any field is invalid, and the first invalid field is focused. It is a plain helper with no Dioxus context or component coupling, so it works identically for <form onsubmit> page forms and the onclick-submit modals, and it is deliberately Signal-free so the accumulation logic is pure and unit-tested (7 cases: all-valid, report-all-at-once, first-invalid ordering, stale-clear, note_invalid focus/no-focus, field-before-note priority).

focus_field is gated on target_arch = "wasm32" (the repo's web_sys convention, see lib.rs), because web_sys imports panic when called off-wasm and the `web` feature is on even for host test builds; on the host/test target it is a no-op.

Adopting the guard across the ~30 forms (and deleting the bespoke per-handler checks) is PMS-518.

just check parity (rust-builder image): clippy --all-targets -D warnings, fmt --check, cargo test --lib form_guard (7 passed), cargo check --target wasm32-unknown-unknown all green.

#PMS-517
nrupard deleted branch feat/PMS-517-form-submit-guard 2026-06-25 19:47:10 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-apps!351
No description provided.