Skip to content
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' => self::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.

25 changes: 24 additions & 1 deletion js/src/admin/admin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/* 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 */

/**
* Internal dependencies
*/
const { validateField } = require( './settings/validateField' );
const { getRangeSettingsDefaults, validateNumberRangeSetting, validateStepSetting } = require( './settings/validateRangeSettings' );

window.FrmFormsConnect = window.FrmFormsConnect || ( function( document, window, $ ) {
/*global jQuery:false, frm_admin_js, frmGlobal, ajaxurl */

Expand Down Expand Up @@ -8449,7 +8455,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 @@ -11156,6 +11167,18 @@ window.frmAdminBuildJS = function() {
}
},

/**
* @since x.x
*/
settings: {
validate: {
validateField,
getRangeSettingsDefaults,
validateNumberRangeSetting,
validateStepSetting,
},
},

applyZebraStriping,
initModal,
infoModal,
Expand Down
30 changes: 30 additions & 0 deletions js/src/admin/settings/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* 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;
}
Comment thread
shervElmi marked this conversation as resolved.
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.
*/
export 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 } );
Comment thread
shervElmi marked this conversation as resolved.
}

/**
* 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' )
: '';
} );
}