Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
82dcde3
Add number field settings validation with min/max/step checks
shervElmi Dec 11, 2025
b1f6f16
Add handleNumberFieldSettingsChange function to validate on input change
shervElmi Dec 11, 2025
dded20c
Add number field settings validation on builder change events
shervElmi Dec 11, 2025
41f9ff4
Move number field settings validation import to top of file
shervElmi Dec 12, 2025
7ba9e27
Refactor number field settings validation to use vanilla JS and impro…
shervElmi Dec 12, 2025
2242d4e
Add field validation utilities with number range and step validation …
shervElmi Dec 12, 2025
e9ef672
Add utility function to get field ID from settings container element
shervElmi Dec 12, 2025
c39a3a2
Refactor number field settings validation to use new field validation…
shervElmi Dec 12, 2025
7ed6f95
Remove number field settings validation module after refactoring into…
shervElmi Dec 12, 2025
4c2f249
Refactor field validation utilities to improve code organization and …
shervElmi Dec 15, 2025
cfc75e2
Add filter hook for range settings defaults and improve field validat…
shervElmi Dec 15, 2025
c343f55
Refactor field validation module to range settings validation and add…
shervElmi Dec 15, 2025
03ae96b
Refactor getFieldId utility to accept singleSettings parameter and ad…
shervElmi Dec 16, 2025
3064356
Rename rangeSettingsValidation.js to validateRangeSettings.js and ref…
shervElmi Dec 16, 2025
b276ade
Add validateField utility function for field validation with UI feedb…
shervElmi Dec 16, 2025
86dea2a
Refactor range settings validation imports and add pro check to build…
shervElmi Dec 16, 2025
82c2b95
Refactor range settings validation to move input element queries outs…
shervElmi Dec 16, 2025
5fa17d5
Add proIsConnected flag to admin global JavaScript localization for p…
shervElmi Dec 16, 2025
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
1 change: 1 addition & 0 deletions classes/helpers/FrmAppHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3865,6 +3865,7 @@ public static function load_admin_wide_js( $load = true ) {
'nonce' => wp_create_nonce( 'frm_ajax' ),
'proIncludesSliderJs' => is_callable( 'FrmProFormsHelper::prepare_custom_currency' ),
'inboxSlideIn' => FrmInbox::get_inbox_slide_in_value_for_js(),
'proIsConnected' => FrmAppHelper::pro_is_connected(),
);
wp_localize_script( 'formidable_admin_global', 'frmGlobal', $global_strings );

Expand Down
2 changes: 1 addition & 1 deletion js/formidable_admin.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/formidable_admin.js.map

Large diffs are not rendered by default.

19 changes: 18 additions & 1 deletion js/src/admin/admin.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Internal dependencies
*/
const { validateField } = require( './settings/validateField' );
const { validateNumberRangeSetting, validateStepSetting } = require( './settings/validateRangeSettings' );
const utils = require( './settings/utils' );

/* exported frm_add_logic_row, frm_remove_tag, frm_show_div, frmCheckAll, frmCheckAllLevel */
/* eslint-disable jsdoc/require-param, prefer-const, no-redeclare, @wordpress/no-unused-vars-before-return, jsdoc/check-types, jsdoc/check-tag-names, @wordpress/i18n-translator-comments, @wordpress/valid-sprintf, jsdoc/require-returns-description, jsdoc/require-param-type, no-unused-expressions, compat/compat */

Expand Down Expand Up @@ -8442,7 +8449,12 @@ window.frmAdminBuildJS = function() {
* @return {void}
*/
function handleBuilderChangeEvent( event ) {
maybeShowSaveAndReloadModal( event.target );
const target = event.target;
maybeShowSaveAndReloadModal( target );
if ( ! frmGlobal.proIsConnected ) {
validateNumberRangeSetting( target );
validateStepSetting( target );
}
}

/**
Expand Down Expand Up @@ -11149,6 +11161,11 @@ window.frmAdminBuildJS = function() {
}
},

utils,
validateField,
validateNumberRangeSetting,
validateStepSetting,

applyZebraStriping,
initModal,
infoModal,
Expand Down
29 changes: 29 additions & 0 deletions js/src/admin/settings/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Gets the field ID from the single settings element or the closest single settings element to the field.
*
* @since x.x
*
* @param {HTMLElement} singleSettings The single settings element.
* @param {HTMLElement} field The field element to get field ID from.
*
* @return {string|undefined} The field ID or undefined if not found.
*/
export const getFieldId = ( singleSettings = null, field = null ) =>
singleSettings ? singleSettings.dataset.fid : field?.closest( '.frm-single-settings' )?.dataset.fid;

/**
* Gets the field type from the single settings element.
*
* @since x.x
*
* @param {HTMLElement} singleSettings The single settings element.
* @param {HTMLElement} field The field element.
*
* @return {string|undefined} The field type or undefined if not found.
*/
export const getFieldType = ( singleSettings = null, field = null ) => {
if ( ! singleSettings ) {
singleSettings = field?.closest( '.frm-single-settings' );
}
return singleSettings?.className.match( /frm-type-(\w+)/ )?.[ 1 ];
};
21 changes: 21 additions & 0 deletions js/src/admin/settings/validateField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Runs validation and handles UI feedback.
*
* @since x.x
*
* @param {HTMLElement} field The field element being validated.
* @param {Function} getError Function that returns error message or empty string.
*
* @return {string} The error message or empty string.
*/
export function validateField( field, getError ) {
const errorMessage = getError();
if ( errorMessage ) {
frmAdminBuild.infoModal( errorMessage );
field.classList.add( 'frm_invalid_field' );
} else {
field.classList.remove( 'frm_invalid_field' );
}

return errorMessage;
}
120 changes: 120 additions & 0 deletions js/src/admin/settings/validateRangeSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { validateField } from './validateField';
import { getFieldId, getFieldType } from './utils';

/**
* Gets the default values for range settings validation.
*
* @since x.x
*
* @param {HTMLElement} singleSettings The single settings element.
*
* @return {Object} The defaults object with maxNum, minNum, and step.
*/
function getRangeSettingsDefaults( singleSettings ) {
const fieldType = getFieldType( singleSettings ) || 'number';
const defaultSettings = {
maxNum: 9999999,
minNum: 0,
step: 1
};

/**
* Filters the default values for range settings validation.
*
* @since x.x
*
* @param {Object} defaultSettings The default settings.
* @param {Object} context Additional context.
* @param {HTMLElement} context.singleSettings The single settings element.
* @param {string} context.fieldType The field type.
*
* @return {Object} The filtered default settings.
*/
return wp.hooks.applyFilters( 'frm_range_settings_defaults', defaultSettings, { singleSettings, fieldType } );
}

/**
* Validates number range setting.
*
* @since x.x
*
* @param {HTMLElement} field The field element being validated.
*/
export function validateNumberRangeSetting( field ) {
if ( ! field.closest( '.frm-number-range' ) ) {
return;
}

const singleSettings = field.closest( '.frm-single-settings' );
const fieldId = getFieldId( singleSettings );
if ( ! fieldId ) {
return;
}

const minValueInput = document.querySelector( `[name="field_options[minnum_${ fieldId }]"]` );
if ( ! minValueInput ) {
return;
}

const maxValueInput = document.querySelector( `[name="field_options[maxnum_${ fieldId }]"]` );
if ( ! maxValueInput ) {
return;
}

return validateField( field, () => {
const { minNum, maxNum } = getRangeSettingsDefaults( singleSettings );

return parseFloat( minValueInput.value || minNum ) >= parseFloat( maxValueInput.value || maxNum )
? __( 'Minimum value cannot be greater than or equal to maximum value.', 'formidable' )
: '';
} );
}

/**
* Validates step setting.
*
* @since x.x
*
* @param {HTMLElement} field The field element being validated.
*/
export function validateStepSetting( field ) {
if ( ! field.closest( '.frm-step' ) ) {
return;
}

const singleSettings = field.closest( '.frm-single-settings' );
const fieldId = getFieldId( singleSettings );
if ( ! fieldId ) {
return;
}

const stepInput = document.querySelector( `[name="field_options[step_${ fieldId }]"]` );
if ( ! stepInput ) {
return;
}

return validateField( field, () => {
const { step, maxNum } = getRangeSettingsDefaults( singleSettings );
const stepInputValue = parseFloat( stepInput.value || step );
if ( stepInputValue <= 0 ) {
return __( 'Step value must be greater than 0.', 'formidable' );
}

const maxValueInput = document.querySelector( `[name="field_options[maxnum_${ fieldId }]"]` );
if ( ! maxValueInput ) {
return '';
}

return stepInputValue > parseFloat( maxValueInput.value || maxNum )
? __( 'Step value must be less than maximum value.', 'formidable' )
: '';
} );
}