Skip to content

Commit 8eafe97

Browse files
antoniocapelolukad
andauthored
chore(next): RMT-1584: mutate field properties based on conditional schemas (#155)
* chore: add getField util and improve json-schema-form schema type * chore: remove test code * chore: remove unnecessary Array.isArray checks * chore: remove unnecessary type check * feat: update field properties * chore: rename file and functions * add mutation tests * fix type check and linter warnings --------- Co-authored-by: Luka Dornhecker <[email protected]>
1 parent fc1ef3e commit 8eafe97

File tree

4 files changed

+409
-29
lines changed

4 files changed

+409
-29
lines changed

next/src/form.ts

+33-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import type { JsfObjectSchema, JsfSchema, NonBooleanJsfSchema, SchemaValue } fro
44
import type { ValidationOptions } from './validation/schema'
55
import { getErrorMessage } from './errors/messages'
66
import { buildFieldObject } from './field/object'
7+
import { mutateFields } from './mutations'
78
import { validateSchema } from './validation/schema'
89
import { isObjectValue } from './validation/util'
9-
import { updateFieldVisibility } from './visibility'
1010

1111
export { ValidationOptions } from './validation/schema'
1212

@@ -295,17 +295,20 @@ export function createHeadlessForm(
295295
const initialValues = options.initialValues || {}
296296
const fields = buildFields({ schema })
297297

298-
// Making sure visibility is set correctly upon form creation, for initial values
299-
updateFieldVisibility(fields, initialValues, schema)
298+
// Making sure field properties are correct for the initial values
299+
mutateFields(fields, initialValues, schema)
300300

301301
// TODO: check if we need this isError variable exposed
302302
const isError = false
303303

304304
const handleValidation = (value: SchemaValue) => {
305305
const result = validate(value, schema, options.validationOptions)
306306

307-
// Updating field visibility based on the new value
308-
updateFieldVisibility(fields, value, schema, options.validationOptions)
307+
// Fields properties might have changed, so we need to reset the fields by updating them in place
308+
buildFieldsInPlace(fields, schema)
309+
310+
// Updating field properties based on the new form value
311+
mutateFields(fields, value, schema, options.validationOptions)
309312

310313
return result
311314
}
@@ -317,3 +320,28 @@ export function createHeadlessForm(
317320
handleValidation,
318321
}
319322
}
323+
324+
/**
325+
* Updates fields in place based on a schema, recursively if needed
326+
* @param fields - The fields array to mutate
327+
* @param schema - The schema to use for updating fields
328+
*/
329+
function buildFieldsInPlace(fields: Field[], schema: JsfObjectSchema): void {
330+
// Clear existing fields array
331+
fields.length = 0
332+
333+
// Get new fields from schema
334+
const newFields = buildFieldObject(schema, 'root', true).fields || []
335+
336+
// Push all new fields into existing array
337+
fields.push(...newFields)
338+
339+
// Recursively update any nested fields
340+
for (const field of fields) {
341+
// eslint-disable-next-line ts/ban-ts-comment
342+
// @ts-expect-error
343+
if (field.fields && schema.properties?.[field.name]?.type === 'object') {
344+
buildFieldsInPlace(field.fields, schema.properties[field.name] as JsfObjectSchema)
345+
}
346+
}
347+
}

next/src/visibility.ts renamed to next/src/mutations.ts

+20-21
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,18 @@
11
import type { Field } from './field/type'
22
import type { JsfObjectSchema, JsfSchema, NonBooleanJsfSchema, ObjectValue, SchemaValue } from './types'
33
import type { ValidationOptions } from './validation/schema'
4+
import { buildFieldSchema } from './field/schema'
45
import { validateSchema } from './validation/schema'
56
import { isObjectValue } from './validation/util'
67

7-
/**
8-
* Resets the visibility of all fields to true
9-
* @param fields - The fields to reset
10-
*/
11-
function resetVisibility(fields: Field[]) {
12-
for (const field of fields) {
13-
field.isVisible = true
14-
if (field.fields) {
15-
resetVisibility(field.fields)
16-
}
17-
}
18-
}
198
/**
209
* Updates field visibility based on JSON schema conditional rules
2110
* @param fields - The fields to update
2211
* @param values - The current form values
2312
* @param schema - The JSON schema definition
2413
* @param options - Validation options
2514
*/
26-
export function updateFieldVisibility(
15+
export function mutateFields(
2716
fields: Field[],
2817
values: SchemaValue,
2918
schema: JsfObjectSchema,
@@ -33,9 +22,6 @@ export function updateFieldVisibility(
3322
return
3423
}
3524

36-
// Reseting fields visibility to the default before re-calculating it
37-
resetVisibility(fields)
38-
3925
// Apply rules to current level of fields
4026
applySchemaRules(fields, values, schema, options)
4127

@@ -125,21 +111,23 @@ function applySchemaRules(
125111
for (const { rule, matches } of conditionalRules) {
126112
// If the rule matches, process the then branch
127113
if (matches && rule.then) {
128-
processBranch(fields, rule.then)
114+
processBranch(fields, values, rule.then, options)
129115
}
130116
// If the rule doesn't match, process the else branch
131117
else if (!matches && rule.else) {
132-
processBranch(fields, rule.else)
118+
processBranch(fields, values, rule.else, options)
133119
}
134120
}
135121
}
136122

137123
/**
138124
* Processes a branch of a conditional rule, updating the visibility of fields based on the branch's schema
139125
* @param fields - The fields to process
126+
* @param values - The current form values
140127
* @param branch - The branch (schema representing and then/else) to process
128+
* @param options - Validation options
141129
*/
142-
function processBranch(fields: Field[], branch: JsfSchema) {
130+
function processBranch(fields: Field[], values: SchemaValue, branch: JsfSchema, options: ValidationOptions = {}) {
143131
if (branch.properties) {
144132
// Cycle through each property in the schema and search for any (possibly nested)
145133
// fields that have a false boolean schema. If found, set the field's visibility to false
@@ -148,15 +136,26 @@ function processBranch(fields: Field[], branch: JsfSchema) {
148136
const field = fields.find(e => e.name === fieldName)
149137
if (field) {
150138
if (field?.fields) {
151-
processBranch(field.fields, fieldSchema)
139+
processBranch(field.fields, values, fieldSchema)
152140
}
153141
else if (fieldSchema === false) {
154142
field.isVisible = false
155143
}
156144
else {
157-
field.isVisible = true
145+
// If the field has properties being declared on this branch, we need to update the field
146+
// with the new properties
147+
const newField = buildFieldSchema(fieldSchema as JsfObjectSchema, fieldName, true)
148+
for (const key in newField) {
149+
// We don't want to override the type property
150+
if (!['type'].includes(key)) {
151+
field[key] = newField[key]
152+
}
153+
}
158154
}
159155
}
160156
}
161157
}
158+
159+
// Apply rules to the branch
160+
applySchemaRules(fields, values, branch as JsfObjectSchema, options)
162161
}

0 commit comments

Comments
 (0)