diff --git a/src/form.ts b/src/form.ts index 7af93a90..314bf5bc 100644 --- a/src/form.ts +++ b/src/form.ts @@ -284,7 +284,7 @@ export function createHeadlessForm( const updatedSchema = calculateFinalSchema({ schema, values: initialValues, - options: options.legacyOptions, + options, }) const fields = buildFields({ schema: updatedSchema, originalSchema: schema, strictInputType }) @@ -301,7 +301,7 @@ export function createHeadlessForm( const updatedSchema = calculateFinalSchema({ schema, values: value, - options: options.legacyOptions, + options, }) const result = validate(value, updatedSchema, options.legacyOptions) diff --git a/src/mutations.ts b/src/mutations.ts index 5c66ed2a..d0925e55 100644 --- a/src/mutations.ts +++ b/src/mutations.ts @@ -1,4 +1,5 @@ import type { Field } from './field/type' +import type { CreateHeadlessFormOptions } from './form' import type { JsfObjectSchema, JsfSchema, JsonLogicContext, NonBooleanJsfSchema, ObjectValue, SchemaValue } from './types' import type { LegacyOptions } from './validation/schema' import { buildFieldSchema } from './field/schema' @@ -24,18 +25,19 @@ export function calculateFinalSchema({ }: { schema: JsfObjectSchema values: SchemaValue - options?: LegacyOptions + options?: CreateHeadlessFormOptions }): JsfObjectSchema { const jsonLogicContext = schema['x-jsf-logic'] ? getJsonLogicContextFromSchema(schema['x-jsf-logic'], values) : undefined const schemaCopy = safeDeepClone(schema) + const { legacyOptions, customJsonLogicOps } = options - applySchemaRules(schemaCopy, values, options, jsonLogicContext) + applySchemaRules(schemaCopy, values, legacyOptions, jsonLogicContext) if (jsonLogicContext?.schema.computedValues) { - applyComputedAttrsToSchema(schemaCopy, jsonLogicContext.schema.computedValues, values) + applyComputedAttrsToSchema(schemaCopy, jsonLogicContext.schema.computedValues, values, customJsonLogicOps) // If we had computed values applied to the schema, // we need to re-apply the schema rules to update the fields - applySchemaRules(schemaCopy, values, options, jsonLogicContext) + applySchemaRules(schemaCopy, values, legacyOptions, jsonLogicContext) } return schemaCopy diff --git a/src/validation/json-logic.ts b/src/validation/json-logic.ts index 4d5da80f..db4ba6fd 100644 --- a/src/validation/json-logic.ts +++ b/src/validation/json-logic.ts @@ -1,5 +1,6 @@ import type { RulesLogic } from 'json-logic-js' import type { ValidationError, ValidationErrorPath } from '../errors' +import type { CreateHeadlessFormOptions } from '../form' import type { JsfObjectSchema, JsfSchema, JsonLogicContext, JsonLogicRules, JsonLogicSchema, NonBooleanJsfSchema, ObjectValue, SchemaValue } from '../types' import jsonLogic from 'json-logic-js' @@ -128,18 +129,23 @@ export function computePropertyValues( * @param schema - The schema to apply computed attributes to * @param computedValuesDefinition - The computed values to apply * @param values - The current form values + * @param customJsonLogicOps - The custom JSON Logic operations to apply * @returns The schema with computed attributes applied */ -export function applyComputedAttrsToSchema(schema: JsfObjectSchema, computedValuesDefinition: JsonLogicRules['computedValues'], values: SchemaValue): JsfObjectSchema { +export function applyComputedAttrsToSchema(schema: JsfObjectSchema, computedValuesDefinition: JsonLogicRules['computedValues'], values: SchemaValue, customJsonLogicOps: CreateHeadlessFormOptions['customJsonLogicOps'] = {}): JsfObjectSchema { if (computedValuesDefinition) { const computedValues: Record = {} + addCustomJsonLogicOperations(customJsonLogicOps) + Object.entries(computedValuesDefinition).forEach(([name, definition]) => { const computedValue = computePropertyValues(name, definition.rule, values) computedValues[name] = computedValue }) cycleThroughPropertiesAndApplyValues(schema, computedValues) + + removeCustomJsonLogicOperations(customJsonLogicOps) } return schema diff --git a/test/validation/json-logic-v0.test.js b/test/validation/json-logic-v0.test.js index 727e0916..2c7f6bc8 100644 --- a/test/validation/json-logic-v0.test.js +++ b/test/validation/json-logic-v0.test.js @@ -15,6 +15,7 @@ import { schemaWithComputedAttributeThatDoesntExist, schemaWithComputedAttributeThatDoesntExistDescription, schemaWithComputedAttributeThatDoesntExistTitle, + schemaWithCustomComputedValueFunction, schemaWithCustomValidationFunction, schemaWithDeepVarThatDoesNotExist, schemaWithDeepVarThatDoesNotExistOnFieldset, @@ -475,10 +476,18 @@ describe('jsonLogic: cross-values validations', () => { it('validation on custom functions', () => { const actionThatWillThrow = () => { - createHeadlessForm(schemaWithCustomValidationFunction, { strictInputType: false, customJsonLogicOps: { is_hello: 'not a funcion' } }) + createHeadlessForm(schemaWithCustomValidationFunction, { strictInputType: false, customJsonLogicOps: { is_hello: 'not a function' } }) } expect(actionThatWillThrow).toThrow('Custom JSON Logic operator \'is_hello\' must be a function, but received type \'string\'.') }) + + it('applies custom functions when initial values require them', () => { + const actionThatWillThrow = () => { + createHeadlessForm(schemaWithCustomComputedValueFunction, { strictInputType: false, customJsonLogicOps: { is_hello: a => a === 'hello world!' } }) + } + + expect(actionThatWillThrow).not.toThrow() + }) }) }) diff --git a/test/validation/json-logic.fixtures.js b/test/validation/json-logic.fixtures.js index 15a70616..2ccd5c89 100644 --- a/test/validation/json-logic.fixtures.js +++ b/test/validation/json-logic.fixtures.js @@ -763,3 +763,33 @@ export const schemaWithCustomValidationFunction = { }, }, } + +export const schemaWithCustomComputedValueFunction = { + 'properties': { + field_a: { + type: 'string', + }, + field_b: { + 'type': 'string', + 'title': 'Field with computed description', + 'x-jsf-logic-computedAttrs': { + description: 'Computed value is {{custom_function_result}}', + }, + }, + }, + 'x-jsf-logic': { + computedValues: { + custom_function_result: { + rule: { + if: [ + { + is_hello: { var: 'field_a' }, + }, + 'value1', + 'value2', + ], + }, + }, + }, + }, +}