Skip to content

Commit eb696e2

Browse files
Improve node processing logic
1 parent 13879c4 commit eb696e2

File tree

3 files changed

+142
-58
lines changed

3 files changed

+142
-58
lines changed

v0/src/helpers.js

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import set from 'lodash/set';
77
import { lazy } from 'yup';
88

99
import { checkIfConditionMatchesProperties } from './internals/checkIfConditionMatches';
10-
import { supportedTypes, getInputType } from './internals/fields';
10+
import { supportedTypes } from './internals/fields';
1111
import { pickXKey } from './internals/helpers';
1212
import { processJSONLogicNode } from './jsonLogic';
1313
import { hasProperty } from './utils';
@@ -293,7 +293,7 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
293293
// If the field is a fieldset, restore the visibility of the fields within it.
294294
// If this is not in place, calling updateField for multiple conditionals touching
295295
// the same fieldset will unset previously calculated visibility for the nested fields.
296-
// This is because rebuildFieldset is called via a calculateConditionalProperties closure
296+
// This is because rebuildFieldset is called via the calculateConditionalProperties closure
297297
// created at the time of building the fields, and it returns a new fieldset.fields array
298298
if (key === 'fields' && !isNil(nestedFieldsVisibility)) {
299299
restoreNestedFieldsVisibility(field, nestedFieldsVisibility);
@@ -383,25 +383,19 @@ export function processNode({
383383
accRequired = new Set(),
384384
parentID = 'root',
385385
logic,
386-
processingConditional = false,
387386
}) {
388387
// Set initial required fields
389388
const requiredFields = new Set(accRequired);
390389

391390
// Go through the node properties definition and update each field accordingly
392-
Object.keys(node.properties ?? []).forEach((fieldName) => {
391+
Object.entries(node.properties ?? []).forEach(([fieldName, nestedNode]) => {
393392
const field = getField(fieldName, formFields);
394393
updateField(field, requiredFields, node, formValues, logic, { parentID });
395394

396-
// If we're processing a conditional field node and it's respective to a fieldset field,
395+
// If we're processing a fieldset field node
397396
// update the nested fields going through the node recursively.
398-
// As an example, the node here can be:
399-
// 1. { properties: { perks: { properties: { retirement: { const: 'basic' } } } } } }
400-
// 2. { properties: { perks: { required: ['retirement'] } } } }
401-
// where 'perks' is a fieldset field.
402-
const nestedNode = node.properties[fieldName];
403397
const isFieldset = field?.inputType === supportedTypes.FIELDSET;
404-
if (isFieldset && processingConditional) {
398+
if (isFieldset) {
405399
processNode({
406400
node: nestedNode,
407401
formValues: formValues[fieldName] || {},
@@ -432,7 +426,6 @@ export function processNode({
432426
accRequired: requiredFields,
433427
parentID,
434428
logic,
435-
processingConditional: true,
436429
});
437430

438431
branchRequired.forEach((field) => requiredFields.add(field));
@@ -444,7 +437,6 @@ export function processNode({
444437
accRequired: requiredFields,
445438
parentID,
446439
logic,
447-
processingConditional: true,
448440
});
449441
branchRequired.forEach((field) => requiredFields.add(field));
450442
}
@@ -464,22 +456,6 @@ export function processNode({
464456
});
465457
}
466458

467-
if (node.properties) {
468-
Object.entries(node.properties).forEach(([name, nestedNode]) => {
469-
const inputType = getInputType(nestedNode);
470-
if (inputType === supportedTypes.FIELDSET) {
471-
// It's a fieldset, which might contain scoped conditions
472-
processNode({
473-
node: nestedNode,
474-
formValues: formValues[name] || {},
475-
formFields: getField(name, formFields).fields,
476-
parentID: name,
477-
logic,
478-
});
479-
}
480-
});
481-
}
482-
483459
if (node.allOf) {
484460
node.allOf
485461
.map((allOfNode) =>

v0/src/tests/createHeadlessForm.test.js

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2202,48 +2202,53 @@ describe('createHeadlessForm', () => {
22022202
});
22032203
});
22042204

2205-
describe('supports fieldsets conditionals over nested fieldsets', () => {
2206-
it('Given a basic retirement, the perks.has_pension is hidden', async () => {
2205+
describe('supports conditionals over nested fieldsets', () => {
2206+
it('retirement_plan fieldset is hidden when no values are provided', () => {
22072207
const { fields, handleValidation } = createHeadlessForm(
22082208
schemaWithNestedFieldsetsConditionals,
22092209
{}
22102210
);
22112211
const validateForm = (vals) => friendlyError(handleValidation(vals));
22122212

2213+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(false);
2214+
22132215
expect(validateForm({ perks: {} })).toEqual({
22142216
perks: {
22152217
benefits_package: 'Required field',
2218+
has_retirement_plan: 'Required field',
22162219
},
22172220
});
22182221

2219-
// retirement_plan is not visible
22202222
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(false);
2223+
});
22212224

2222-
// can submit without retirement_plan
2223-
expect(validateForm({ perks: { benefits_package: 'basic' } })).toBeUndefined();
2225+
it("submits without retirement_plan when user selects 'no' for has_retirement_plan", () => {
2226+
const { fields, handleValidation } = createHeadlessForm(
2227+
schemaWithNestedFieldsetsConditionals,
2228+
{}
2229+
);
2230+
const validateForm = (vals) => friendlyError(handleValidation(vals));
22242231

22252232
expect(
2226-
validateForm({
2227-
perks: { benefits_package: 'plus', has_retirement_plan: 'yes' },
2228-
})
2229-
).toEqual({
2230-
perks: {
2231-
retirement_plan: {
2232-
plan_name: 'Required field',
2233-
year: 'Required field',
2234-
amount: 'Required field',
2235-
},
2236-
},
2237-
});
2233+
validateForm({ perks: { benefits_package: 'basic', has_retirement_plan: 'no' } })
2234+
).toBeUndefined();
22382235

2239-
// retirement_plan becomes visible
2240-
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(true);
2236+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(false);
2237+
});
2238+
2239+
it("retirement_plan fieldset is visible when user selects 'yes' for has_retirement_plan", () => {
2240+
const { fields, handleValidation } = createHeadlessForm(
2241+
schemaWithNestedFieldsetsConditionals,
2242+
{}
2243+
);
2244+
const validateForm = (vals) => friendlyError(handleValidation(vals));
22412245

22422246
expect(
22432247
validateForm({
22442248
perks: {
2245-
benefits_package: 'plus',
2249+
benefits_package: 'basic',
22462250
has_retirement_plan: 'yes',
2251+
declare_amount: 'yes',
22472252
retirement_plan: { plan_name: 'test', year: 2025 },
22482253
},
22492254
})
@@ -2255,25 +2260,51 @@ describe('createHeadlessForm', () => {
22552260
},
22562261
});
22572262

2258-
// submit with valid retirement_plan
2263+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(true);
2264+
expect(getField(fields, 'perks', 'declare_amount').isVisible).toBe(true);
2265+
expect(getField(fields, 'perks', 'declare_amount').default).toBe('yes');
2266+
expect(getField(fields, 'perks', 'retirement_plan', 'amount').isVisible).toBe(true);
2267+
});
2268+
2269+
it("retirement_plan's amount field is hidden when user selects 'no' for declare_amount", () => {
2270+
const { fields, handleValidation } = createHeadlessForm(
2271+
schemaWithNestedFieldsetsConditionals,
2272+
{}
2273+
);
2274+
const validateForm = (vals) => friendlyError(handleValidation(vals));
2275+
22592276
expect(
22602277
validateForm({
22612278
perks: {
2262-
benefits_package: 'plus',
2279+
benefits_package: 'basic',
22632280
has_retirement_plan: 'yes',
2264-
retirement_plan: { plan_name: 'test', year: 2025, amount: 1000 },
2281+
declare_amount: 'no',
2282+
retirement_plan: { plan_name: 'test', year: 2025 },
22652283
},
22662284
})
22672285
).toBeUndefined();
22682286

2287+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(true);
2288+
expect(getField(fields, 'perks', 'declare_amount').isVisible).toBe(true);
2289+
expect(getField(fields, 'perks', 'retirement_plan', 'amount').isVisible).toBe(false);
2290+
});
2291+
2292+
it('submits with valid retirement_plan', async () => {
2293+
const { handleValidation } = createHeadlessForm(
2294+
schemaWithNestedFieldsetsConditionals,
2295+
{}
2296+
);
2297+
const validateForm = (vals) => friendlyError(handleValidation(vals));
2298+
22692299
expect(
22702300
validateForm({
2271-
perks: { benefits_package: 'plus', has_retirement_plan: 'no' },
2301+
perks: {
2302+
benefits_package: 'plus',
2303+
has_retirement_plan: 'yes',
2304+
retirement_plan: { plan_name: 'test', year: 2025, amount: 1000 },
2305+
},
22722306
})
22732307
).toBeUndefined();
2274-
2275-
// retirement_plan becomes invisible
2276-
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(false);
22772308
});
22782309
});
22792310
});

v0/src/tests/helpers.js

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,24 @@ export const schemaWithNestedFieldsetsConditionals = {
20122012
inputType: 'radio',
20132013
},
20142014
},
2015+
declare_amount: {
2016+
oneOf: [
2017+
{
2018+
const: 'yes',
2019+
title: 'Yes',
2020+
},
2021+
{
2022+
const: 'no',
2023+
title: 'No',
2024+
},
2025+
],
2026+
title: 'Declare retirement amount?',
2027+
type: 'string',
2028+
default: 'yes',
2029+
'x-jsf-presentation': {
2030+
inputType: 'radio',
2031+
},
2032+
},
20152033
retirement_plan: {
20162034
type: 'object',
20172035
title: 'Retirement plan',
@@ -2029,10 +2047,13 @@ export const schemaWithNestedFieldsetsConditionals = {
20292047
title: 'Amount',
20302048
},
20312049
},
2032-
required: ['plan_name', 'year', 'amount'],
2050+
required: ['plan_name', 'year'],
2051+
'x-jsf-presentation': {
2052+
inputType: 'fieldset',
2053+
},
20332054
},
20342055
},
2035-
required: ['benefits_package'],
2056+
required: ['benefits_package', 'has_retirement_plan'],
20362057
title: 'Perks',
20372058
type: 'object',
20382059
'x-jsf-presentation': {
@@ -2058,6 +2079,62 @@ export const schemaWithNestedFieldsetsConditionals = {
20582079
perks: {
20592080
properties: {
20602081
retirement_plan: false,
2082+
declare_amount: false,
2083+
},
2084+
},
2085+
},
2086+
},
2087+
},
2088+
{
2089+
if: {
2090+
properties: {
2091+
perks: {
2092+
properties: {
2093+
declare_amount: {
2094+
const: 'no',
2095+
},
2096+
},
2097+
required: ['declare_amount'],
2098+
},
2099+
},
2100+
},
2101+
then: {
2102+
properties: {
2103+
perks: {
2104+
properties: {
2105+
retirement_plan: {
2106+
properties: {
2107+
amount: false,
2108+
},
2109+
},
2110+
},
2111+
},
2112+
},
2113+
},
2114+
},
2115+
{
2116+
if: {
2117+
properties: {
2118+
perks: {
2119+
properties: {
2120+
has_retirement_plan: {
2121+
const: 'yes',
2122+
},
2123+
declare_amount: {
2124+
const: 'yes',
2125+
},
2126+
},
2127+
required: ['has_retirement_plan', 'declare_amount'],
2128+
},
2129+
},
2130+
},
2131+
then: {
2132+
properties: {
2133+
perks: {
2134+
properties: {
2135+
retirement_plan: {
2136+
required: ['amount'],
2137+
},
20612138
},
20622139
},
20632140
},

0 commit comments

Comments
 (0)