diff --git a/package.json b/package.json index f41df024..8d457794 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@formio/core", - "version": "2.1.0-dev.tt.13", + "version": "2.3.0-dev.160.cabaa43", "description": "The core Form.io renderering framework.", "main": "lib/index.js", "exports": { diff --git a/src/process/__tests__/process.test.ts b/src/process/__tests__/process.test.ts index 77d02182..4e81b5cc 100644 --- a/src/process/__tests__/process.test.ts +++ b/src/process/__tests__/process.test.ts @@ -16,68 +16,6 @@ import { } from './fixtures'; import { get } from 'lodash'; -/* -describe('Process Tests', () => { - it('Should perform the processes using the processReduced method.', async () => { - const reduced: ReducerScope = process({ - components: form1.components, - data: data1a.data, - scope: { - processes: {} - } - }); - const targets = processReduceTargets(reduced.processes); - expect(targets.length).to.equal(5); - expect(targets[0].target).to.equal('server'); - expect(Object.keys(targets[0].processes).length).to.equal(1); - expect(targets[0].processes.defaultValue.length).to.equal(6); - expect(targets[1].target).to.equal('custom'); - expect(Object.keys(targets[1].processes).length).to.equal(1); - expect(targets[1].processes.customDefaultValue.length).to.equal(1); - expect(targets[2].target).to.equal('server'); - expect(Object.keys(targets[2].processes).length).to.equal(1); - expect(targets[2].processes.fetch.length).to.equal(1); - expect(targets[3].target).to.equal('custom'); - expect(Object.keys(targets[3].processes).length).to.equal(1); - expect(targets[3].processes.calculate.length).to.equal(6); - expect(targets[4].target).to.equal('server'); - expect(Object.keys(targets[4].processes).length).to.equal(2); - expect(targets[4].processes.conditions.length).to.equal(1); - expect(targets[4].processes.validate.length).to.equal(28); - const scope = {errors: []}; - - // Reset all values that will be calculated. - reduced.data.subtotal = 0; - reduced.data.taxes = 0; - reduced.data.total = 0; - reduced.data.cart.forEach((item: any) => { - item.price = 0; - }); - for (let i = 0; i < targets.length; i++) { - await processReduced({ - components: form1.components, - data: reduced.data, - processes: targets[i].processes, - fetch: (url: string, options?: RequestInit | undefined): Promise => { - return Promise.resolve({ - json: () => { - return Promise.resolve(subs); - } - } as Response); - }, - scope - }); - } - expect(reduced.data.subtotal).to.equal(100); - expect(reduced.data.taxes).to.equal(8); - expect(reduced.data.total).to.equal(108); - expect(reduced.data.cart[0].price).to.equal(30); - expect(reduced.data.cart[1].price).to.equal(20); - expect(reduced.data.cart[2].price).to.equal(10); - }); -}); -*/ - describe('Process Tests', function () { it('Should submit data within a nested form.', async function () { const form = { @@ -951,7 +889,6 @@ describe('Process Tests', function () { }, owner: '65ea3601c3792e416cabcb2a', access: [], - _vnote: '', state: 'submitted', form: '65ea368b705068f84a93c87a', @@ -1666,7 +1603,6 @@ describe('Process Tests', function () { }); }); - // TODO: test case naming it('Should not unset submission data of nested forms with identical keys', function () { const parentForm = { display: 'form', @@ -3386,6 +3322,51 @@ describe('Process Tests', function () { }); }); + it('Should include submission data for intentionally hidden fields', async function () { + const form = { + display: 'form', + components: [ + { + type: 'textfield', + key: 'textField', + label: 'Text Field', + input: true, + }, + { + type: 'textarea', + key: 'textArea', + label: 'Text Area', + input: true, + hidden: true, + }, + ], + }; + + const submission = { + data: { + textField: 'not empty', + textArea: 'also not empty', + }, + }; + + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.evaluator, + scope: {}, + config: { + server: true, + }, + }; + processSync(context); + expect(context.data).to.deep.equal({ + textField: 'not empty', + textArea: 'also not empty', + }); + }); + it('Should not filter a simple datamap compoennt', async function () { const form = { display: 'form', @@ -4738,30 +4719,6 @@ describe('Process Tests', function () { }); }); - it('Should not return fields from conditionally hidden containers, clearOnHide = true', async function () { - const { form, submission } = clearOnHideWithCustomCondition; - const context = { - form, - submission, - data: submission.data, - components: form.components, - processors: ProcessTargets.submission, - scope: {}, - config: { - server: true, - }, - }; - - processSync(context); - context.processors = ProcessTargets.evaluator; - processSync(context); - - expect(context.data).to.deep.equal({ - candidates: [{ candidate: { data: {} } }], - submit: true, - }); - }); - it('Should skip child validation with conditional', async function () { const { form, submission } = skipValidForConditionallyHiddenComp; const context = { @@ -4822,83 +4779,6 @@ describe('Process Tests', function () { expect((context.scope as ValidationScope).errors).to.have.length(0); }); - it('Should not return fields from conditionally hidden containers, clearOnHide = false', async function () { - const { form, submission } = clearOnHideWithCustomCondition; - const containerComponent = getComponent(form.components, 'section6') as ContainerComponent; - containerComponent.clearOnHide = false; - const context = { - form, - submission, - data: submission.data, - components: form.components, - processors: ProcessTargets.submission, - scope: {}, - config: { - server: true, - }, - }; - - processSync(context); - context.processors = ProcessTargets.evaluator; - processSync(context); - - expect(context.data).to.deep.equal({ - candidates: [{ candidate: { data: { section6: {} } } }], - submit: true, - }); - }); - - it('Should not validate fields from hidden containers, clearOnHide = false', async function () { - const { form, submission } = clearOnHideWithHiddenParent; - const context = { - form, - submission, - data: submission.data, - components: form.components, - processors: ProcessTargets.submission, - scope: { errors: [] }, - config: { - server: true, - }, - }; - - processSync(context); - context.processors = ProcessTargets.evaluator; - processSync(context); - - expect(context.data).to.deep.equal({ - candidates: [{ candidate: { data: { section6: {} } } }], - submit: true, - }); - expect(context.scope.errors.length).to.equal(0); - }); - - it('Should not return fields from hidden containers, clearOnHide = true', async function () { - const { form, submission } = clearOnHideWithHiddenParent; - const containerComponent = getComponent(form.components, 'section6') as ContainerComponent; - containerComponent.clearOnHide = true; - const context = { - form, - submission, - data: submission.data, - components: form.components, - processors: ProcessTargets.submission, - scope: {}, - config: { - server: true, - }, - }; - - processSync(context); - context.processors = ProcessTargets.evaluator; - processSync(context); - - expect(context.data).to.deep.equal({ - candidates: [{ candidate: { data: {} } }], - submit: true, - }); - }); - it('Should validate when all child components are empty in required Data Grid', async function () { const { form, submission } = forDataGridRequired; const context = { @@ -5109,199 +4989,465 @@ describe('Process Tests', function () { }); }); - /* - it('Should not clearOnHide when set to false', async () => { - var components = [ - { - "input": true, - "tableView": true, - "inputType": "radio", - "label": "Selector", - "key": "selector", - "values": [ - { - "value": "one", - "label": "One" - }, - { - "value": "two", - "label": "Two" - } - ], - "defaultValue": "", - "protected": false, - "persistent": true, - "validate": { - "required": false, - "custom": "", - "customPrivate": false - }, - "type": "radio", - "conditional": { - "show": "", - "when": null, - "eq": "" - } - }, - { - "input": false, - "title": "Panel", - "theme": "default", - "components": [ - { - "input": true, - "tableView": true, - "inputType": "text", - "inputMask": "", - "label": "No Clear Field", - "key": "noClear", - "placeholder": "", - "prefix": "", - "suffix": "", - "multiple": false, - "defaultValue": "", - "protected": false, - "unique": false, - "persistent": true, - "clearOnHide": false, - "validate": { - "required": false, - "minLength": "", - "maxLength": "", - "pattern": "", - "custom": "", - "customPrivate": false - }, - "conditional": { - "show": null, - "when": null, - "eq": "" - }, - "type": "textfield" - } - ], - "type": "panel", - "key": "panel", - "conditional": { - "show": "true", - "when": "selector", - "eq": "two" - } - } - ]; - - helper - .form('test', components) - .submission({ - selector: 'one', - noClear: 'testing' - }) - .execute(function(err) { - if (err) { - return done(err); - } - - var submission = helper.getLastSubmission(); - assert.deepEqual({selector: 'one', noClear: 'testing'}, submission.data); - done(); - }); - }); - - it('Should clearOnHide when set to true', async () => { - var components = [ - { - "input": true, - "tableView": true, - "inputType": "radio", - "label": "Selector", - "key": "selector", - "values": [ - { - "value": "one", - "label": "One" - }, - { - "value": "two", - "label": "Two" - } - ], - "defaultValue": "", - "protected": false, - "persistent": true, - "validate": { - "required": false, - "custom": "", - "customPrivate": false - }, - "type": "radio", - "conditional": { - "show": "", - "when": null, - "eq": "" - } - }, - { - "input": false, - "title": "Panel", - "theme": "default", - "components": [ - { - "input": true, - "tableView": true, - "inputType": "text", - "inputMask": "", - "label": "Clear Me", - "key": "clearMe", - "placeholder": "", - "prefix": "", - "suffix": "", - "multiple": false, - "defaultValue": "", - "protected": false, - "unique": false, - "persistent": true, - "clearOnHide": true, - "validate": { - "required": false, - "minLength": "", - "maxLength": "", - "pattern": "", - "custom": "", - "customPrivate": false - }, - "conditional": { - "show": null, - "when": null, - "eq": "" - }, - "type": "textfield" - } - ], - "type": "panel", - "key": "panel", - "conditional": { - "show": "true", - "when": "selector", - "eq": "two" - } - } - ]; - - helper - .form('test', components) - .submission({ - selector: 'one', - clearMe: 'Clear Me!!!!' - }) - .execute(function(err) { - if (err) { - return done(err); - } - - var submission = helper.getLastSubmission(); - assert.deepEqual({selector: 'one'}, submission.data); - done(); - }); - }); - */ + describe('clearOnHide', function () { + it('Should not include submission data from conditionally hidden containers when clearOnHide ("Omit Data When Conditionally Hidden" is true', async function () { + const { form, submission } = clearOnHideWithCustomCondition; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: {}, + config: { + server: true, + }, + }; + + processSync(context); + context.processors = ProcessTargets.evaluator; + processSync(context); + + expect(context.data).to.deep.equal({ + candidates: [{ candidate: { data: {} } }], + submit: true, + }); + }); + + it('Should not return fields from conditionally hidden containers, clearOnHide = false', async function () { + const { form, submission } = clearOnHideWithCustomCondition; + const containerComponent = getComponent(form.components, 'section6') as ContainerComponent; + containerComponent.clearOnHide = false; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: {}, + config: { + server: true, + }, + }; + + processSync(context); + context.processors = ProcessTargets.evaluator; + processSync(context); + + expect(context.data).to.deep.equal({ + candidates: [{ candidate: { data: { section6: {} } } }], + submit: true, + }); + }); + + it('Should not validate fields from hidden containers, clearOnHide = false', async function () { + const { form, submission } = clearOnHideWithHiddenParent; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: { errors: [] }, + config: { + server: true, + }, + }; + + processSync(context); + context.processors = ProcessTargets.evaluator; + processSync(context); + + expect(context.data).to.deep.equal({ + candidates: [{ candidate: { data: { section6: { c: {}, d: [] } } } }], + submit: true, + }); + expect(context.scope.errors.length).to.equal(0); + }); + + it('Should include submission data from hidden containers even when clearOnHide ("Omit Data When Conditionally Hidden" is true', async function () { + const { form, submission } = clearOnHideWithHiddenParent; + const containerComponent = getComponent(form.components, 'section6') as ContainerComponent; + containerComponent.clearOnHide = true; + const context = { + form, + submission, + data: submission.data, + components: form.components, + processors: ProcessTargets.submission, + scope: {}, + config: { + server: true, + }, + }; + + processSync(context); + context.processors = ProcessTargets.evaluator; + processSync(context); + + expect(context.data).to.deep.equal({ + candidates: [{ candidate: { data: { section6: { c: {}, d: [] } } } }], + submit: true, + }); + }); + + it('Should include submission data for simple fields that are intentionally hidden, even when clearOnHide ("Omit When Conditionally Hidden") is true', async function () { + const components = [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + hidden: true, + clearOnHide: true, + }, + ]; + const submission = { + data: { + textField: 'test', + }, + }; + const context = { + form: { components }, + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ textField: 'test' }); + }); + + it('Should include submission data for simple components that are intentionally hidden when clearOnHide ("Omit When Conditionally Hidden") is false', async function () { + const components = [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + hidden: true, + clearOnHide: false, + }, + ]; + const submission = { + data: { + textField: 'test', + }, + }; + const context = { + form: { components }, + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ textField: 'test' }); + }); + + it('Should include submission data for container components that are intentionally hidden, even when clearOnHide ("Omit When Conditionally Hidden") is true', async function () { + const components = [ + { + key: 'container', + type: 'container', + input: true, + hidden: true, + clearOnHide: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + clearOnHide: true, + }, + ], + }, + ]; + const submission = { + data: { + container: { + textField: 'test', + }, + }, + }; + const context = { + form: { components }, + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ container: { textField: 'test' } }); + }); + + it('Should include submission data for container components that are intentionally hidden when clearOnHide ("Omit When Conditionally Hidden") is false', async function () { + const components = [ + { + key: 'container', + type: 'container', + input: true, + hidden: true, + clearOnHide: false, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + clearOnHide: true, + }, + ], + }, + ]; + const submission = { + data: { + container: { + textField: 'test', + }, + }, + }; + const context = { + form: { components }, + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ container: { textField: 'test' } }); + }); + + it("Should not include submission data for simple fields that are conditionally hidden when clearOnHide ('Omit When Conditionally Hidden') is true", async function () { + const components = [ + { + type: 'checkbox', + key: 'selector', + label: 'Selector', + input: true, + }, + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + conditional: { + show: true, + when: 'selector', + eq: true, + }, + clearOnHide: true, + }, + ]; + const submission = { + data: { + selector: false, + textField: 'test', + }, + }; + const context = { + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ selector: false }); + }); + + it("Should include submission data for simple fields that are conditionally hidden when clearOnHide ('Omit When Conditionally Hidden') is false", async function () { + const components = [ + { + type: 'checkbox', + key: 'selector', + label: 'Selector', + input: true, + }, + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + conditional: { + show: true, + when: 'selector', + eq: true, + }, + clearOnHide: false, + }, + ]; + const submission = { + data: { + selector: false, + textField: 'test', + }, + }; + const context = { + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ selector: false, textField: 'test' }); + }); + + it("Should not include submission data for container components that are conditionally hidden when clearOnHide ('Omit When Conditionally Hidden') is true", async function () { + const components = [ + { + type: 'checkbox', + key: 'selector', + label: 'Selector', + input: true, + }, + { + key: 'container', + type: 'container', + input: true, + conditional: { + show: true, + when: 'selector', + eq: true, + }, + clearOnHide: true, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + ]; + const submission = { + data: { + selector: false, + container: { + textField: 'test', + }, + }, + }; + const context = { + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ selector: false }); + }); + + it("Should include submission data for container components that are conditionally hidden when clearOnHide ('Omit When Conditionally Hidden') is false (but not their children, assuming clearOnHide is true or omitted in the child)", async function () { + const components = [ + { + type: 'checkbox', + key: 'selector', + label: 'Selector', + input: true, + }, + { + key: 'container', + type: 'container', + input: true, + conditional: { + show: true, + when: 'selector', + eq: true, + }, + clearOnHide: false, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + }, + ], + }, + ]; + const submission = { + data: { + selector: false, + container: { + textField: 'test', + }, + }, + }; + const context = { + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ selector: false, container: {} }); + }); + + it("Should include submission data for container components that are conditionally hidden when clearOnHide ('Omit When Conditionally Hidden') is false (and include their children when clearOnHide is false in the child)", async function () { + const components = [ + { + type: 'checkbox', + key: 'selector', + label: 'Selector', + input: true, + }, + { + key: 'container', + type: 'container', + input: true, + conditional: { + show: true, + when: 'selector', + eq: true, + }, + clearOnHide: false, + components: [ + { + label: 'Text Field', + tableView: true, + key: 'textField', + type: 'textfield', + input: true, + clearOnHide: false, + }, + ], + }, + ]; + const submission = { + data: { + selector: false, + container: { + textField: 'test', + }, + }, + }; + const context = { + submission, + data: submission.data, + components, + processors: ProcessTargets.evaluator, + scope: {}, + }; + processSync(context); + expect(context.data).to.deep.equal({ selector: false, container: { textField: 'test' } }); + }); + }); }); diff --git a/src/process/clearHidden/__tests__/clearHidden.test.ts b/src/process/clearHidden/__tests__/clearHidden.test.ts new file mode 100644 index 00000000..1209f76b --- /dev/null +++ b/src/process/clearHidden/__tests__/clearHidden.test.ts @@ -0,0 +1,87 @@ +import { expect } from 'chai'; + +import { clearHiddenProcess } from '../index'; + +describe('clearHidden', function () { + it('Shoud not clear conditionally hidden component data when clearOnHide is false', function () { + // Test case data + const context = { + component: { + type: 'textfield', + key: 'foo', + clearOnHide: false, + input: true, + }, + data: { + foo: 'bar', + }, + value: 'foo', + row: {}, + scope: { + clearHidden: {}, + conditionals: [ + { + path: 'foo', + conditionallyHidden: true, + }, + ], + }, + path: 'foo', + }; + clearHiddenProcess(context); + expect(context.data).to.deep.equal({ foo: 'bar' }); + }); + + it('Should clear conditionally hidden component data when clearOnHide is true', function () { + // Test case data + const context = { + component: { + type: 'textfield', + key: 'foo', + clearOnHide: true, + input: true, + }, + data: { + foo: 'bar', + }, + value: 'foo', + row: {}, + scope: { + clearHidden: {}, + conditionals: [ + { + path: 'foo', + conditionallyHidden: true, + }, + ], + }, + path: 'foo', + }; + clearHiddenProcess(context); + expect(context.data).to.deep.equal({}); + }); + + it('Should not clear component data when the component is intentionally hidden', function () { + // Test case data + const context = { + component: { + type: 'textfield', + key: 'foo', + clearOnHide: true, + input: true, + hidden: true, + }, + data: { + foo: 'bar', + }, + value: 'foo', + row: {}, + scope: { + clearHidden: {}, + }, + path: 'foo', + }; + clearHiddenProcess(context); + expect(context.data).to.deep.equal({ foo: 'bar' }); + }); +}); diff --git a/src/process/clearHidden.ts b/src/process/clearHidden/index.ts similarity index 93% rename from src/process/clearHidden.ts rename to src/process/clearHidden/index.ts index ce55e11d..d97c4f08 100644 --- a/src/process/clearHidden.ts +++ b/src/process/clearHidden/index.ts @@ -40,7 +40,7 @@ export const clearHiddenProcess: ProcessorFnSync = (context) = if ( shouldClearValueWhenHidden && - (isConditionallyHidden || component.hidden || component.ephemeralState?.conditionallyHidden) + (isConditionallyHidden || component.ephemeralState?.conditionallyHidden) ) { unset(data, absolutePath); scope.clearHidden[absolutePath] = true; diff --git a/src/process/hideChildren.ts b/src/process/hideChildren.ts index 5037e8c4..944774ec 100644 --- a/src/process/hideChildren.ts +++ b/src/process/hideChildren.ts @@ -24,9 +24,16 @@ export const hideChildrenProcessor: ProcessorFnSync = (context) scope.conditionals = []; } - if (isConditionallyHidden || component.hidden || parent?.ephemeralState?.conditionallyHidden) { + if (isConditionallyHidden || parent?.ephemeralState?.conditionallyHidden) { registerEphemeralState(component, 'conditionallyHidden', true); } + + if ( + (component.hasOwnProperty('hidden') && !!component.hidden) || + parent?.ephemeralState?.intentionallyHidden + ) { + registerEphemeralState(component, 'intentionallyHidden', true); + } }; export const hideChildrenProcessorAsync: ProcessorFn = async (context) => { diff --git a/src/process/validation/index.ts b/src/process/validation/index.ts index 187e502b..f8be96d5 100644 --- a/src/process/validation/index.ts +++ b/src/process/validation/index.ts @@ -87,7 +87,6 @@ export function isValueHidden(context: ValidationContext): boolean { } return false; } - export function isForcedHidden( context: ValidationContext, isConditionallyHidden: ConditionallyHidden, @@ -96,6 +95,9 @@ export function isForcedHidden( if (isConditionallyHidden(context as ConditionsContext)) { return true; } + if (component.ephemeralState?.intentionallyHidden) { + return true; + } if (component.hasOwnProperty('hidden')) { return !!component.hidden; } @@ -119,7 +121,7 @@ export const _shouldSkipValidation = ( ) { return true; } - const { validateWhenHidden = false } = component || {}; + const { validateWhenHidden = false } = component; const rules = [ // Skip validation if component is readOnly // () => this.options.readOnly, diff --git a/src/process/validation/rules/__tests__/validateRequired.test.ts b/src/process/validation/rules/__tests__/validateRequired.test.ts index 941de965..06f51e0a 100644 --- a/src/process/validation/rules/__tests__/validateRequired.test.ts +++ b/src/process/validation/rules/__tests__/validateRequired.test.ts @@ -81,6 +81,16 @@ describe('validateRequired', function () { expect(context.scope.errors.length).to.equal(0); }); + it('Should validate a hidden component that has the `validateWhenHidden` property set to true.', async function () { + const component = { ...hiddenRequiredField }; + component.validateWhenHidden = true; + const data = {}; + const context = generateProcessorContext(component, data) as ProcessorsContext; + context.processors = [validateProcessInfo]; + await processOne(context); + expect(context.scope.errors.length).to.equal(1); + }); + it('Validating a simple component that is required but conditionally hidden', async function () { const component = { ...simpleTextField }; component.validate = { required: true }; diff --git a/src/process/validation/rules/validateRequired.ts b/src/process/validation/rules/validateRequired.ts index 746ab7e2..07cd8236 100644 --- a/src/process/validation/rules/validateRequired.ts +++ b/src/process/validation/rules/validateRequired.ts @@ -73,10 +73,7 @@ const valueIsPresent = ( export const shouldValidate = (context: ValidationContext) => { const { component } = context; - if ( - component.validate?.required && - !(component.hidden || component.ephemeralState?.conditionallyHidden) - ) { + if (component.validate?.required) { return true; } return false; diff --git a/src/types/BaseComponent.ts b/src/types/BaseComponent.ts index 4b976a44..7d2a04df 100644 --- a/src/types/BaseComponent.ts +++ b/src/types/BaseComponent.ts @@ -33,6 +33,7 @@ export type BaseComponent = { hidden?: boolean; ephemeralState?: { conditionallyHidden?: boolean; + intentionallyHidden?: boolean; }; clearOnHide?: boolean; refreshOn?: string;