-
Notifications
You must be signed in to change notification settings - Fork 41
Lite: Fix number field validation appearing when min/max values are empty #2664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
97c02b5
f3ce3a8
18de437
f92f1a5
0df7555
d0944d6
bf245ed
43eed37
382a5bc
6573fc5
b3ae6ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| 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 ]; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /** | ||
| * 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' ); | ||
| focusFieldOnModalDismiss( field ); | ||
| } else { | ||
| field.classList.remove( 'frm_invalid_field' ); | ||
| } | ||
|
|
||
| return errorMessage; | ||
| } | ||
|
|
||
| /** | ||
| * Returns focus to the invalid field once the info modal is dismissed. | ||
| * | ||
| * @since x.x | ||
| * | ||
| * @param {HTMLElement} field The invalid field element. | ||
| * | ||
| * @return {void} | ||
| */ | ||
| function focusFieldOnModalDismiss( field ) { | ||
| const dismissers = document.querySelectorAll( | ||
| '#frm_info_modal .dismiss, #frm_info_modal #frm-info-click, .ui-widget-overlay.ui-front' | ||
| ); | ||
|
|
||
| function onModalClose() { | ||
| setTimeout( () => field.focus(), 0 ); | ||
| dismissers.forEach( el => el.removeEventListener( 'click', onModalClose ) ); | ||
| } | ||
|
|
||
| dismissers.forEach( el => el.addEventListener( 'click', onModalClose ) ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| /** | ||
| * 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 } ); | ||
|
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' ) | ||
| : ''; | ||
| } ); | ||
|
Comment on lines
+72
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| /** | ||
| * 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, minNum, 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 ''; | ||
| } | ||
|
|
||
| const minValueInput = document.querySelector( `[name="field_options[minnum_${ fieldId }]"]` ); | ||
| const minValue = parseFloat( minValueInput?.value || minNum ); | ||
| const maxValue = parseFloat( maxValueInput.value || maxNum ); | ||
|
|
||
| return stepInputValue > maxValue - minValue | ||
| ? __( 'Step value cannot be greater than the difference between the minimum and maximum values.', 'formidable' ) | ||
| : ''; | ||
| } ); | ||
|
Comment on lines
+104
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| /** | ||
| * Validates all range related settings for a field and lets add-ons extend the validation. | ||
| * | ||
| * This is the single entry point used by the builder change handler. It runs | ||
| * the number range and step checks, then exposes the `frm_validate_range_settings` | ||
| * filter so add-ons (like Pro) can contribute additional checks (e.g. gap range) | ||
| * without the core needing to know anything about them. | ||
| * | ||
| * @since x.x | ||
| * | ||
| * @param {HTMLElement} field The field element being validated. | ||
| * | ||
| * @return {string} The error message, or empty string when valid. | ||
| */ | ||
| export function validateRangeSettings( field ) { | ||
| let errorMessage = validateNumberRangeSetting( field ); | ||
| if ( ! errorMessage ) { | ||
| errorMessage = validateStepSetting( field ); | ||
| } | ||
|
|
||
| /** | ||
| * Filters the range settings validation result so add-ons can add their own checks. | ||
| * | ||
| * @since x.x | ||
| * | ||
| * @param {string} errorMessage The current error message, or empty string when valid. | ||
| * @param {Object} context Additional context. | ||
| * @param {HTMLElement} context.field The field element being validated. | ||
| * | ||
| * @return {string} The (possibly updated) error message. | ||
| */ | ||
| return wp.hooks.applyFilters( 'frm_validate_range_settings', errorMessage || '', { field } ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.