From 8df992366cfb7977f987d314673e6b1314409b9a Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Wed, 19 Nov 2025 17:05:14 -0400 Subject: [PATCH 1/6] Improve support for conditional square --- square/js/frontend.js | 180 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 2 deletions(-) diff --git a/square/js/frontend.js b/square/js/frontend.js index 51ab61489b..0dc8b5ea16 100644 --- a/square/js/frontend.js +++ b/square/js/frontend.js @@ -49,6 +49,12 @@ if ( squareCardElementIsComplete ) { enableSubmit(); } else { + if ( squareIsConditionallyDisabled( thisForm ) ) { + running = 0; + enableSubmit(); + return; + } + disableSubmit( thisForm ); } } @@ -57,6 +63,50 @@ return card; } + /** + * Check if a Square card element is conditionally hidden. + * If it is, we should not be disabling the submit button. + * + * @since x.x + * + * @param {HTMLElement} form + * @returns {boolean} + */ + function squareIsConditionallyDisabled( form ) { + const fieldContainer = getPaymentElementFieldContainer( form ); + if ( ! fieldContainer ) { + return false; + } + + // Field is conditionally hidden. + if ( 'none' === fieldContainer.style.display ) { + return true; + } + + // Section parent is conditionally hidden. + const parentSection = fieldContainer.closest( '.frm_section_heading' ); + if ( parentSection && 'none' === parentSection.style.display ) { + return true; + } + + return false; + } + + /** + * Try to get the field container for a Square card payment element. + * The field container is checked to determine if the field is conditionally hidden or not. + * + * @param {HTMLElement} form + * @returns {HTMLElement|null} + */ + function getPaymentElementFieldContainer( form ) { + const paymentElement = form.querySelector( '.frm-card-element' ); + if ( ! paymentElement ) { + return null; + } + return paymentElement.parentElement.closest( '.frm_form_field' ); + } + /** * Enable the submit button for the form. */ @@ -192,11 +242,19 @@ if ( cardContainer ) { thisForm = cardContainer.closest( 'form' ); if ( thisForm ) { - // Initially disable the submit button until card is valid - disableSubmit( thisForm ); + listenForFieldMutations( thisForm ); + + if ( ! squareIsConditionallyDisabled( thisForm ) ) { + // Initially disable the submit button until card is valid + disableSubmit( thisForm ); + } // Add event listener for form submission thisForm.addEventListener( 'submit', function( event ) { + if ( squareIsConditionallyDisabled( thisForm ) ) { + return; + } + event.preventDefault(); event.stopPropagation(); @@ -286,6 +344,124 @@ } } + /** + * Possibly toggle on and off the submit button when a Stripe Link payment field is conditionally shown or hidden. + * + * @since 3.1.5 + * + * @param {HTMLElement} form + * @returns {void} + */ + function listenForFieldMutations( form ) { + const fieldContainer = getPaymentElementFieldContainer( form ); + if ( ! fieldContainer ) { + return; + } + + observeAttributeMutations( fieldContainer, handleMutation ); + + const section = fieldContainer.closest( '.frm_section_heading' ); + if ( section ) { + observeAttributeMutations( section, handleMutation ); + } + + const formId = getFormIdForForm( form ); + + /** + * Handle a style attribute change for either a payment field container + * or the field container of its parent section. + * + * @param {MutationRecord} mutation + * @returns {void} + */ + function handleMutation( mutation ) { + if ( mutation.attributeName !== 'style' ) { + return; + } + + if ( submitButtonIsConditionallyDisabled( formId ) ) { + return; + } + + const shouldEnable = 'none' === mutation.target.display || squareCardElementIsComplete || squareIsConditionallyDisabled( form ); + if ( ! shouldEnable ) { + disableSubmit( form ); + return; + } + + thisForm = form; + running = 0; + enableSubmit(); + } + } + + /** + * @param {HTMLElement} element + * @returns {void} + */ + function observeAttributeMutations( element, mutationHandler ) { + const observer = new MutationObserver( + ( mutations ) => each( mutations, mutationHandler ) + ); + observer.observe( + element, + { attributes: true } + ); + } + + /** + * Check if the submit button is conditionally disabled. + * This is required for Stripe link so the button does not get enabled at the wrong time after completing the Stripe elements. + * + * @since 3.0 + * + * @param {String} formId + * @returns {bool} + */ + function submitButtonIsConditionallyDisabled( formId ) { + return submitButtonIsConditionallyNotAvailable( formId ) && 'disable' === __FRMRULES[ 'submit_' + formId ].hideDisable; + } + + /** + * Check submit button is conditionally "hidden". This is also used for the enabled check and is used in submitButtonIsConditionallyDisabled. + * + * @since 3.0 + * + * @param {String} formId + * @returns bool + */ + function submitButtonIsConditionallyNotAvailable( formId ) { + var hideFields = document.getElementById( 'frm_hide_fields_' + formId ); + return hideFields && -1 !== hideFields.value.indexOf( '["frm_form_' + formId + '_container .frm_final_submit"]' ); + } + + /** + * @since 3.0 + * + * @param {@array|NodeList} items + * @param {function} callback + */ + function each( items, callback ) { + var index, length; + + length = items.length; + for ( index = 0; index < length; index++ ) { + if ( false === callback( items[ index ], index ) ) { + break; + } + } + } + + /** + * Check a form's form_id input for a form ID value. + * + * @param {HTMLElement} form + * @returns {number} + */ + function getFormIdForForm( form ) { + return parseInt( form.querySelector( '[name="form_id"]' ).value ); + } + document.addEventListener( 'DOMContentLoaded', async function() { if ( ! window.Square ) { console.error( 'Square.js failed to load properly' ); From abd1b991b69086182604267d3f2edfee1006a0ca Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Wed, 19 Nov 2025 17:12:20 -0400 Subject: [PATCH 2/6] Update since versions, remove each function (not necessary) --- square/js/frontend.js | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/square/js/frontend.js b/square/js/frontend.js index 0dc8b5ea16..e3cc00e925 100644 --- a/square/js/frontend.js +++ b/square/js/frontend.js @@ -347,7 +347,7 @@ /** * Possibly toggle on and off the submit button when a Stripe Link payment field is conditionally shown or hidden. * - * @since 3.1.5 + * @since x.x * * @param {HTMLElement} form * @returns {void} @@ -401,7 +401,9 @@ */ function observeAttributeMutations( element, mutationHandler ) { const observer = new MutationObserver( - ( mutations ) => each( mutations, mutationHandler ) + ( mutations ) => { + mutations.forEach( mutationHandler ); + } ); observer.observe( element, @@ -413,7 +415,7 @@ * Check if the submit button is conditionally disabled. * This is required for Stripe link so the button does not get enabled at the wrong time after completing the Stripe elements. * - * @since 3.0 + * @since x.x * * @param {String} formId * @returns {bool} @@ -425,7 +427,7 @@ /** * Check submit button is conditionally "hidden". This is also used for the enabled check and is used in submitButtonIsConditionallyDisabled. * - * @since 3.0 + * @since x.x * * @param {String} formId * @returns bool @@ -435,23 +437,6 @@ return hideFields && -1 !== hideFields.value.indexOf( '["frm_form_' + formId + '_container .frm_final_submit"]' ); } - /** - * @since 3.0 - * - * @param {@array|NodeList} items - * @param {function} callback - */ - function each( items, callback ) { - var index, length; - - length = items.length; - for ( index = 0; index < length; index++ ) { - if ( false === callback( items[ index ], index ) ) { - break; - } - } - } - /** * Check a form's form_id input for a form ID value. * From ffe463fb22636da927dd97beac6ec9cb7459acb3 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Wed, 19 Nov 2025 17:25:31 -0400 Subject: [PATCH 3/6] Call recalculate size after conditionally showing the payment field --- square/js/frontend.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/square/js/frontend.js b/square/js/frontend.js index e3cc00e925..15fc318f67 100644 --- a/square/js/frontend.js +++ b/square/js/frontend.js @@ -383,7 +383,7 @@ return; } - const shouldEnable = 'none' === mutation.target.display || squareCardElementIsComplete || squareIsConditionallyDisabled( form ); + const shouldEnable = 'none' === mutation.target.display || ! squareCardElementIsComplete || squareIsConditionallyDisabled( form ); if ( ! shouldEnable ) { disableSubmit( form ); return; @@ -392,6 +392,8 @@ thisForm = form; running = 0; enableSubmit(); + + cardGlobal.recalculateSize(); } } From 880260d0b028d7da513cb2dcf81ec00fd83e0f67 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 27 Apr 2026 15:28:17 -0300 Subject: [PATCH 4/6] Run eslint fixer --- square/js/frontend.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/square/js/frontend.js b/square/js/frontend.js index cb16e3ef29..39bf1d28d2 100644 --- a/square/js/frontend.js +++ b/square/js/frontend.js @@ -87,7 +87,7 @@ * @since x.x * * @param {HTMLElement} form - * @returns {boolean} + * @return {boolean} */ function squareIsConditionallyDisabled( form ) { const fieldContainer = getPaymentElementFieldContainer( form ); @@ -114,7 +114,7 @@ * The field container is checked to determine if the field is conditionally hidden or not. * * @param {HTMLElement} form - * @returns {HTMLElement|null} + * @return {HTMLElement|null} */ function getPaymentElementFieldContainer( form ) { const paymentElement = form.querySelector( '.frm-card-element' ); @@ -367,7 +367,7 @@ * @since x.x * * @param {HTMLElement} form - * @returns {void} + * @return {void} */ function listenForFieldMutations( form ) { const fieldContainer = getPaymentElementFieldContainer( form ); @@ -389,7 +389,7 @@ * or the field container of its parent section. * * @param {MutationRecord} mutation - * @returns {void} + * @return {void} */ function handleMutation( mutation ) { if ( mutation.attributeName !== 'style' ) { @@ -407,7 +407,7 @@ } thisForm = form; - running = 0; + running = 0; enableSubmit(); cardGlobal.recalculateSize(); @@ -416,11 +416,12 @@ /** * @param {HTMLElement} element - * @returns {void} + * @param mutationHandler + * @return {void} */ function observeAttributeMutations( element, mutationHandler ) { const observer = new MutationObserver( - ( mutations ) => { + mutations => { mutations.forEach( mutationHandler ); } ); @@ -436,11 +437,11 @@ * * @since x.x * - * @param {String} formId - * @returns {bool} + * @param {string} formId + * @return {bool} */ function submitButtonIsConditionallyDisabled( formId ) { - return submitButtonIsConditionallyNotAvailable( formId ) && 'disable' === __FRMRULES[ 'submit_' + formId ].hideDisable; + return submitButtonIsConditionallyNotAvailable( formId ) && 'disable' === __FRMRULES[ `submit_${ formId }` ].hideDisable; } /** @@ -448,19 +449,19 @@ * * @since x.x * - * @param {String} formId - * @returns bool + * @param {string} formId + * @return bool */ function submitButtonIsConditionallyNotAvailable( formId ) { - var hideFields = document.getElementById( 'frm_hide_fields_' + formId ); - return hideFields && -1 !== hideFields.value.indexOf( '["frm_form_' + formId + '_container .frm_final_submit"]' ); + const hideFields = document.getElementById( `frm_hide_fields_${ formId }` ); + return hideFields && hideFields.value.includes( `["frm_form_${ formId }_container .frm_final_submit"]` ); } /** * Check a form's form_id input for a form ID value. * * @param {HTMLElement} form - * @returns {number} + * @return {number} */ function getFormIdForForm( form ) { return parseInt( form.querySelector( '[name="form_id"]' ).value ); From 47f75747edeb5e7ba969df2bf6e4b00efb2c34f7 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 27 Apr 2026 15:29:49 -0300 Subject: [PATCH 5/6] Simplify --- square/js/frontend.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/square/js/frontend.js b/square/js/frontend.js index 39bf1d28d2..0e022c33aa 100644 --- a/square/js/frontend.js +++ b/square/js/frontend.js @@ -102,11 +102,7 @@ // Section parent is conditionally hidden. const parentSection = fieldContainer.closest( '.frm_section_heading' ); - if ( parentSection && 'none' === parentSection.style.display ) { - return true; - } - - return false; + return parentSection && 'none' === parentSection.style.display; } /** From f94f0efdbc2788ecb47808dfec4a2a179c842657 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 27 Apr 2026 16:01:52 -0300 Subject: [PATCH 6/6] Fix more eslint issues --- square/js/frontend.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/square/js/frontend.js b/square/js/frontend.js index 0e022c33aa..1eaf80b993 100644 --- a/square/js/frontend.js +++ b/square/js/frontend.js @@ -87,7 +87,8 @@ * @since x.x * * @param {HTMLElement} form - * @return {boolean} + * + * @return {boolean} True if the field is conditionally hidden, false otherwise. */ function squareIsConditionallyDisabled( form ) { const fieldContainer = getPaymentElementFieldContainer( form ); @@ -110,7 +111,8 @@ * The field container is checked to determine if the field is conditionally hidden or not. * * @param {HTMLElement} form - * @return {HTMLElement|null} + * + * @return {HTMLElement|null} The field container element or null if not found. */ function getPaymentElementFieldContainer( form ) { const paymentElement = form.querySelector( '.frm-card-element' ); @@ -412,7 +414,8 @@ /** * @param {HTMLElement} element - * @param mutationHandler + * @param {function} mutationHandler + * * @return {void} */ function observeAttributeMutations( element, mutationHandler ) { @@ -434,7 +437,8 @@ * @since x.x * * @param {string} formId - * @return {bool} + * + * @return {boolean} True if the submit button is conditionally disabled, false otherwise. */ function submitButtonIsConditionallyDisabled( formId ) { return submitButtonIsConditionallyNotAvailable( formId ) && 'disable' === __FRMRULES[ `submit_${ formId }` ].hideDisable; @@ -446,7 +450,8 @@ * @since x.x * * @param {string} formId - * @return bool + * + * @return {boolean} True if the submit button is conditionally not available, false otherwise. */ function submitButtonIsConditionallyNotAvailable( formId ) { const hideFields = document.getElementById( `frm_hide_fields_${ formId }` ); @@ -457,7 +462,8 @@ * Check a form's form_id input for a form ID value. * * @param {HTMLElement} form - * @return {number} + * + * @return {number} The form ID. */ function getFormIdForForm( form ) { return parseInt( form.querySelector( '[name="form_id"]' ).value );