Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion js/formidable.js
Original file line number Diff line number Diff line change
Expand Up @@ -1284,11 +1284,19 @@ function frmFrontFormJS() {
removeAllErrors();

let $fieldCont = null;
const formErrors = {};

for ( const key in response.errors ) {
const fieldContEl = object.querySelector( `#frm_field_${ key }_container` );
$fieldCont = fieldContEl ? jQuery( fieldContEl ) : jQuery();

if ( ! fieldContEl && isFormLevelErrorKey( key ) ) {
// An error like 'spam' has no field to show on, and a page submit would only lose the message.
formErrors[ key ] = response.errors[ key ];
contSubmit = false;
continue;
}

if ( $fieldCont.length ) {
if ( ! $fieldCont.is( ':visible' ) ) { // eslint-disable-line no-jquery/no-is
const inCollapsedSection = $fieldCont.closest( '.frm_toggle_container' ); // eslint-disable-line no-jquery/no-closest, formidable/no-jquery-variable-methods
Expand Down Expand Up @@ -1340,6 +1348,16 @@ function frmFrontFormJS() {
object.submit();
} else {
object.insertAdjacentHTML( 'afterbegin', response.error_message );
addFormLevelErrors( object, formErrors );

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.

After inserting the summary, nothing scrolls to it or focuses it when the only errors are form-level. On a tall form the summary lands above the viewport. Live: scroll position 1112, summary not in view, document.activeElement is <body>. scrollToFirstField( object ) (called earlier) only finds .frm_blank_field, and checkForErrorsAndMaybeSetFocus() exits at its first line when there is no .frm_form_field .frm_error.

Suggested fix (untested; formidable.min.js needs the same change):

Suggested change
addFormLevelErrors( object, formErrors );
addFormLevelErrors( object, formErrors );
if ( Object.keys( formErrors ).length ) {
const errorSummary = object.querySelector( '[data-frm-error-summary]' );
if ( errorSummary ) {
errorSummary.scrollIntoView( { behavior: 'smooth', block: 'center' } );
errorSummary.focus();
}
}

Keep checkForErrorsAndMaybeSetFocus(); on the next line so the field-error case behaves as before. To confirm: the Cypress spec could also assert cy.get( '[data-frm-error-summary]' ).should( 'be.visible' ) on a form with a tall spacer before the submit button.

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 19b7578. After the summary is filled, it is scrolled to the center and focused when there are form-level errors; checkForErrorsAndMaybeSetFocus() still runs after it. Same change in formidable.min.js. The spec now pushes the submit button below the fold with a 2000px spacer and asserts the summary is visible and focused. Live check in a Playground site (honeypot filled): no reload, summary focused and in view.


if ( Object.keys( formErrors ).length ) {
const errorSummary = object.querySelector( '[data-frm-error-summary]' );

if ( errorSummary ) {
frmFrontForm.scrollMsg( jQuery( errorSummary ), object, true );
}
}

checkForErrorsAndMaybeSetFocus();
}
} else if ( ! willRedirect ) { // Avoid double submission if redirecting to a page.
Expand All @@ -1361,6 +1379,35 @@ function frmFrontFormJS() {
postToAjaxUrl( object, data, success, error );
}

/**
* Check if an AJAX error key is for the whole form, like 'spam' or 'form', rather than a field.
*
* @param {string} key
* @return {boolean} True when the key is not a field ID.
*/
function isFormLevelErrorKey( key ) {
return ! /^\d/.test( key );
}

/**
* Show errors that belong to the whole form in the error summary at the top of the form.
*
* @param {HTMLElement} form
* @param {Object<string,string>} formErrors
* @return {void}
*/
function addFormLevelErrors( form, formErrors ) {
const summary = form.querySelector( '[data-frm-error-summary]' );

if ( ! summary ) {
return;
}

Object.keys( formErrors ).forEach( key => {
summary.insertAdjacentHTML( 'beforeend', `<div id="${ key }_error">${ formErrors[ key ] }</div>` );
} );
}

function postToAjaxUrl( form, data, success, error ) {
let ajaxUrl = frm_js.ajax_url;
const action = form.getAttribute( 'action' );
Expand Down Expand Up @@ -3009,7 +3056,13 @@ function frmFrontFormJS() {
scrollObj = id;
}

jQuery( scrollObj ).trigger( 'focus' );
const scrollElement = scrollObj instanceof jQuery ? scrollObj.get( 0 ) : scrollObj;
if (
! scrollElement?.hasAttribute( 'data-frm-error-summary' ) ||
getErrorConfigForForm( scrollElement.closest( '.frm-show-form' ) ).focusErrorSummary
) {
jQuery( scrollObj ).trigger( 'focus' );
}
newPos = scrollObj.offset().top;
if ( ! newPos || frm_js.offset === '-1' ) {
return;
Expand Down
Loading
Loading