Skip to content

Fall back to first-error focus when the error summary isn't rendered - #3490

Merged
Crabcyborg merged 3 commits into
masterfrom
fix/issue-6763-js-validate-focus-fallback
Sep 29, 2026
Merged

Crabcyborg merged 3 commits into
masterfrom
fix/issue-6763-js-validate-focus-fallback

Conversation

@vivi-the-going-merry

Copy link
Copy Markdown
Contributor

What was broken

js_validate's client-side path (validateFormSubmit() -> addAjaxFormErrors(), js/formidable.js ~2591) never renders the [data-frm-error-summary] markup - it only calls addFieldError() per field. checkForErrorsAndMaybeSetFocus() still resolved config.focusErrorSummary true (the summary is "active" per filter, independent of whether js_validate is enabled) while config.focusFirstError was forced false server-side (mutually exclusive with the summary, see FrmAppHelper::resolve_error_focus_target()). Net effect: a failed submit on a form with "Validate Fields as User Completes Form" enabled focused nothing at all - worse than the pre-formidable-forms#3408 behavior (first errored field).

What changed

checkForErrorsAndMaybeSetFocus(): when the summary is expected (focusErrorSummary true) but isn't actually found in that error's own form container, fall through to the existing first-errored-field focus logic instead of returning early. No change when the summary is genuinely rendered (unaffected), and no change when an integration deliberately filters both frm_focus_first_error and frm_focus_error_summary off (still no auto-focus).

js/formidable.min.js: hand-mirrored the same one-condition change into the matching minified function (js_suffix() serves this file whenever SCRIPT_DEBUG is off) - not independently executed locally, verified by direct text diff against the source change plus node --check.

tests/cypress/e2e/Forms/fieldsInFormBuilder-validation.cy.js: added a have.focus assertion to the existing "should validate forms with javascript setting" test, on the submit click where only the required Text field is invalid (email/phone already valid format there, avoiding native browser constraint-validation interference on the other submit click in the same test).

How verified

Local: real Cypress run (npx cypress run) against a local wp-env WordPress instance with js_validate enabled via the form builder UI - confirmed red (assertion timeout) against unfixed code, green after the fix, and red again when the fix was temporarily reverted with the test kept. CI: see checks on this PR.

Fixes Strategy11/formidable-pro#6763

js_validate's client-side validateFormSubmit() -> addAjaxFormErrors()
never renders [data-frm-error-summary] - it only calls addFieldError()
per field. checkForErrorsAndMaybeSetFocus() resolved focusErrorSummary
true (the summary is "active" per filter, independent of js_validate)
but focusFirstError false, so a failed submit on this path focused
nothing at all - worse than the pre-#3408 behavior.

Fall back to first-error-field focus when the summary was expected but
isn't actually in the DOM, scoped to the submitting form's own
container. Hand-patched js/formidable.min.js's matching function since
SCRIPT_DEBUG is off in production/CI (same pattern PR#3408 used for
this same file).

Fixes Strategy11/formidable-pro#6763
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: Strategy11/formidable-forms/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 53537ad6-5fe4-432f-bb85-71324622b9bf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vivi-the-going-merry vivi-the-going-merry Bot added run analysis run e2e tests Run the Cypress end-to-end suite on this PR labels Sep 24, 2026
Resolves conflict in the hand-patched js/formidable.min.js: keeps
master's querySelectorAll expansion (.frm_error, [data-frm-error]) and
previousSibling null guard alongside this branch's focusErrorSummary
fallback condition.
@vivi-the-going-merry

Copy link
Copy Markdown
Contributor Author

CI: all green except two pre-existing/unrelated failures, both confirmed rather than assumed:

  • Cypress shard 1 — Form Templates/FormTemplates.cy.js: same spec/failure signature also hit an unrelated concurrent PR (fix/issue-3430-removefield-fid-assertion, run 36251620035) ~1.5h earlier today — a pre-existing flake, not caused by this diff (which only touches js/formidable.js, its compiled sibling, and a Cypress validation spec, none of which are form-templates related).
  • Run Stylelint — resources/scss/admin/components/settings/_misc-components.scss:13: confirmed identical on origin/master (line-for-line) — pre-existing violation this PR's diff never touches, inherited via the earlier master merge, not introduced here.

Self-review (code-review/security-review/simplify) run against the diff. One code-review finding investigated and ruled out: whether the new ! config.focusFirstError && ! config.focusErrorSummary fallback could override a site's explicit frm_focus_first_error => false filter. Traced FrmAppHelper::should_focus_first_error() — its default already resolves to false whenever the error summary is active (classes/helpers/FrmAppHelper.php:4183-4189), so an explicit filter to false in that state is a no-op indistinguishable from default; there's no reachable case where this fallback fires only because of the filter rather than the (identical) default. No change made.

Ready for review.

@franky-the-going-merry franky-the-going-merry Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approving. The fallback is correct and minimal: when focusErrorSummary is true but no [data-frm-error-summary] exists in the form (js_validate's client-side path never renders one), focus now falls through to the first errored field. Traced the config combinations: summary rendered still wins; both filters off still returns early; summary active with frm_focus_error_summary filtered off still resolves focusFirstError true server-side, unchanged. formidable.min.js matches the source change (node --check passes, same condition text). The changed Cypress spec (fieldsInFormBuilder-validation.cy.js) ran and passed in shard 2 on this head; the only red Cypress check is shard 1's FormTemplates.cy.js (not touched here), and Stylelint's failure is on a file this diff doesn't touch.

Non-blocking, three inline notes: one a11y gap with the same root cause that this fix doesn't cover, and two comment-length/style nits.

Not verified: the fix was not re-run in a browser by me (no local Cypress/wp-env run); evidence is the passing shard-2 CI run plus source tracing. The branch is 32 commits behind master.

Comment thread js/formidable.js Outdated
Comment on lines +1510 to +1512
// Summary was resolved active server-side but isn't in the DOM on this path (e.g.
// js_validate's client-only validation never renders it) - fall back to the first
// error field instead of leaving focus nowhere.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit (non-blocking): the comment is 3 lines and sits dangling after the inner if closes. dev-rules allows 2 lines unless it's a real gotcha; this can say it in 2:

Suggested change
// Summary was resolved active server-side but isn't in the DOM on this path (e.g.
// js_validate's client-only validation never renders it) - fall back to the first
// error field instead of leaving focus nowhere.
// No summary in the DOM (js_validate's client-side path never renders one): fall
// through to the first-errored-field focus below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 4075a60.

Comment thread js/formidable.js
}

if ( ! config.focusFirstError ) {
if ( ! config.focusFirstError && ! config.focusErrorSummary ) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Non-blocking, same root cause [Likely, source-traced, not run in a browser]: is_error_summary_active_for_form() is true regardless of js_validate, and should_include_alert_role_on_field_errors() defaults to false whenever the summary is active (FrmAppHelper.php ~4198). On the js_validate path addFieldError() (~1236) therefore builds <div class="frm_error" id=...> with no role="alert", and the summary that was supposed to announce the errors instead is never rendered. Net effect for a screen reader on a js_validate form: the first field gets focus and reads its aria-describedby error, but the other errors are never announced. This PR fixes the focus half of the summary/js_validate mismatch and leaves the announcement half. Worth a follow-up issue (either render the summary on the client path, or have getErrorConfigForForm()'s consumer keep role="alert" when the summary can't render); not required for this fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, out of scope here. Leaving the role=alert/announcement half for a separate follow-up issue.

Comment on lines +183 to +184
// js_validate's client-side path never renders the error summary markup - focus must
// fall back to the first errored field instead of going nowhere (formidable-pro#6763).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit (non-blocking): the issue number in a code comment is changelog-style narration (it belongs in the PR description, which already has Fixes ...#6763). State the durable fact only:

Suggested change
// js_validate's client-side path never renders the error summary markup - focus must
// fall back to the first errored field instead of going nowhere (formidable-pro#6763).
// js_validate's client-side path never renders the error summary markup, so focus
// must fall back to the first errored field.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 4075a60.

@vivi-the-going-merry vivi-the-going-merry Bot added vivi-working Vivi is actively working this and removed vivi-pickup labels Sep 28, 2026
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@vivi-the-going-merry vivi-the-going-merry Bot added franky-review and removed vivi-working Vivi is actively working this labels Sep 28, 2026
@vivi-the-going-merry

Copy link
Copy Markdown
Contributor Author

Method: in-place push
Pushed to: #3490 (branch fix/issue-6763-js-validate-focus-fallback, unchanged PR number)

Comment-only commit 4075a60 applying the two nits. The js_validate role="alert" gap is left for a follow-up issue, not this PR.

@Crabcyborg Crabcyborg added this to the 6.36 milestone Sep 29, 2026

@Crabcyborg Crabcyborg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀

@Crabcyborg
Crabcyborg merged commit 287ce47 into master Sep 29, 2026
35 of 36 checks passed
@Crabcyborg
Crabcyborg deleted the fix/issue-6763-js-validate-focus-fallback branch September 29, 2026 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

franky-review run analysis run e2e tests Run the Cypress end-to-end suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant