From 96b9baabbbeded972b1dad8da08bc3618d0e2489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Bascop=C3=A9?= Date: Mon, 1 Jun 2026 14:34:29 -0400 Subject: [PATCH 1/3] Added isVisible and insideLoop validation --- src/ValidationsFactory.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ValidationsFactory.js b/src/ValidationsFactory.js index 23e0210ea..03ead2a29 100644 --- a/src/ValidationsFactory.js +++ b/src/ValidationsFactory.js @@ -157,7 +157,7 @@ class FormLoopValidations extends Validations { return; } page.items.filter(item => { - if (item.component === 'FormLoop' && item.config.name === this.element.config.name) { + if (item.component === 'FormLoop' && item.config.name === this.element.config.name && item !== this.element) { siblings.push(item); } }); @@ -240,8 +240,10 @@ class PageNavigateValidations extends Validations { */ class FormElementValidations extends Validations { async addValidations(validations) { - // Disable validations if field is hidden - if (!this.isVisible()) { + // When inside a loop, each row may have different data so the static isVisible() + // check (which uses only the first row's data) cannot reliably determine visibility. + // The runtime closure evaluates conditionalHide per-row with the correct data. + if (!this.insideLoop && !this.isVisible()) { return; } if (this.element.config && this.element.config.readonly) { From bab02c526a034f90d3864df5013abf2ac847cde0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Bascop=C3=A9?= Date: Mon, 10 Aug 2026 17:05:18 -0400 Subject: [PATCH 2/3] Fix the required validation for signature --- src/ValidationsFactory.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/ValidationsFactory.js b/src/ValidationsFactory.js index 03ead2a29..749e7ddf7 100644 --- a/src/ValidationsFactory.js +++ b/src/ValidationsFactory.js @@ -240,10 +240,19 @@ class PageNavigateValidations extends Validations { */ class FormElementValidations extends Validations { async addValidations(validations) { - // When inside a loop, each row may have different data so the static isVisible() - // check (which uses only the first row's data) cannot reliably determine visibility. - // The runtime closure evaluates conditionalHide per-row with the correct data. - if (!this.insideLoop && !this.isVisible()) { + if (this.insideLoop) { + // Inside loops, conditionalHide depends on per-row data and is evaluated + // correctly in the runtime closure. Skip that check here. + // However, visibleInDevice is device-level (not per-row) and is reliably + // set by the VisibilityRule extension, so we still honor it. + const visibleInDevice = + this.element.visibleInDevice === null || this.element.visibleInDevice === undefined + ? true + : this.element.visibleInDevice; + if (!visibleInDevice) { + return; + } + } else if (!this.isVisible()) { return; } if (this.element.config && this.element.config.readonly) { From 72c7e2c0cc81cac1f5cf1b72f70b74be3c7c2ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Bascop=C3=A9?= Date: Mon, 10 Aug 2026 17:40:14 -0400 Subject: [PATCH 3/3] SonarQube fix --- src/ValidationsFactory.js | 155 +++++++++++++++----------------------- 1 file changed, 62 insertions(+), 93 deletions(-) diff --git a/src/ValidationsFactory.js b/src/ValidationsFactory.js index 749e7ddf7..6547176d1 100644 --- a/src/ValidationsFactory.js +++ b/src/ValidationsFactory.js @@ -269,12 +269,13 @@ class FormElementValidations extends Validations { } const fieldName = this.element.config.name; const validationConfig = this.element.config.validation; - const conditionalHide = this.element.config.conditionalHide; - const parentVisibilityRule = this.parentVisibilityRule; - const insideLoop = this.insideLoop || false; - const deviceConfig = this.element.config.deviceVisibility - ? this.element.config.deviceVisibility - : { showForDesktop: true, showForMobile: true }; + const closureOptions = { + fieldName, + conditionalHide: this.element.config.conditionalHide, + parentVisibilityRule: this.parentVisibilityRule, + insideLoop: this.insideLoop || false, + deviceConfig: this.element.config.deviceVisibility || { showForDesktop: true, showForMobile: true }, + }; set(validations, fieldName, get(validations, fieldName, {})); const fieldValidation = get(validations, fieldName); @@ -286,109 +287,77 @@ class FormElementValidations extends Validations { } let validationFn = validators[rule]; if (!validationFn) { - // eslint-disable-next-line no-console return; } if (validation.configs instanceof Array) { - const params = []; - validation.configs.forEach((cnf) => { - params.push(cnf.value); - }); + const params = validation.configs.map((cnf) => cnf.value); params.push(fieldName); validationFn = validationFn(...params); } - fieldValidation[rule] = function(...props) { - const data = props[1]; - const level = fieldName.split('.').length - 1; - const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level); - if (parentVisibilityRule) { - const nextParentLevel = insideLoop ? 1 : 0; - const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel); - let isParentVisible = true; - try { - isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent); - } catch (error) { - isParentVisible = false; - } - - if (!isParentVisible ) { - return true; - } - } - - // Check Device Visibility - let visibleInDevice = true; - try { - const isMobileScreen = this.$root.$children[0].$refs.renderer.definition.isMobile; - visibleInDevice = - (isMobileScreen && deviceConfig.showForMobile) || - (!isMobileScreen && deviceConfig.showForDesktop); - } catch (error) { - visibleInDevice = true; - } - if (!visibleInDevice) { - return true; - } - - // Check Field Visibility - let visible = true; - if (conditionalHide) { - try { - visible = !!Parser.evaluate(conditionalHide, dataWithParent); - } catch (error) { - visible = false; - } - } - if (!visible) { - return true; - } - return validationFn.apply(this,props); - }; + fieldValidation[rule] = this.buildClosure(validationFn, closureOptions); }); } else if (typeof validationConfig === 'string' && validationConfig) { - let validationFn = validators[validationConfig]; + const validationFn = validators[validationConfig]; if (!validationFn) { - // eslint-disable-next-line no-console return; } - fieldValidation[validationConfig] = function(...props) { - const data = props[1]; - const level = fieldName.split('.').length - 1; - const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level); - // Check Parent Visibility - if (parentVisibilityRule) { - const nextParentLevel = insideLoop ? 1 : 0; - const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel); - let isParentVisible = true; - try { - isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent); - } catch (error) { - isParentVisible = false; - } - - if (!isParentVisible) { - return true; - } - } - // Check Field Visibility - let visible = true; - if (conditionalHide) { - try { - visible = !!Parser.evaluate(conditionalHide, dataWithParent); - } catch (error) { - visible = false; - } - } - if (!visible) { - return true; - } - return validationFn.apply(this,props); - }; + fieldValidation[validationConfig] = this.buildClosure(validationFn, closureOptions); } if (this.element.items) { ValidationsFactory(this.element.items, { screen: this.screen, data: this.data }).addValidations(validations); } } + + buildClosure(validationFn, { fieldName, parentVisibilityRule, insideLoop, deviceConfig, conditionalHide }) { + return function (...props) { + const data = props[1]; + const level = fieldName.split('.').length - 1; + const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level); + + if (parentVisibilityRule) { + const nextParentLevel = insideLoop ? 1 : 0; + const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel); + let isParentVisible = true; + try { + isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent); + } catch (error) { + isParentVisible = false; + } + if (!isParentVisible) { + return true; + } + } + + // Check Device Visibility + let visibleInDevice = true; + try { + const isMobileScreen = this.$root.$children[0].$refs.renderer.definition.isMobile; + visibleInDevice = + (isMobileScreen && deviceConfig.showForMobile) || + (!isMobileScreen && deviceConfig.showForDesktop); + } catch (error) { + visibleInDevice = true; + } + if (!visibleInDevice) { + return true; + } + + // Check Field Visibility + let visible = true; + if (conditionalHide) { + try { + visible = !!Parser.evaluate(conditionalHide, dataWithParent); + } catch (error) { + visible = false; + } + } + if (!visible) { + return true; + } + return validationFn.apply(this, props); + }; + } + camelCase(name) { return name.replace(/_\w/g, m => m.substr(1, 1).toUpperCase()); }