From e8fb930da577ba4f4b369ef005f9c15ce78ea4bf Mon Sep 17 00:00:00 2001 From: Capelo Date: Tue, 15 Jul 2025 17:12:19 +0100 Subject: [PATCH 1/2] fix: properly handle function as custom field properties --- src/field/schema.ts | 7 ++++++- src/form.ts | 4 ++-- src/validation/util.ts | 1 + test/fields.test.ts | 7 ++++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/field/schema.ts b/src/field/schema.ts index a46c5070e..907fb488e 100644 --- a/src/field/schema.ts +++ b/src/field/schema.ts @@ -373,7 +373,12 @@ export function buildFieldSchema({ return null } - const presentation = schema['x-jsf-presentation'] || {} + const presentation = { + // We need to use the original schema's presentation as a basis as there are some properties that can't be + // serialized (so they were not cloned) + ...(originalSchema['x-jsf-presentation'] || {}), + ...(schema['x-jsf-presentation'] || {}), + } const errorMessage = schema['x-jsf-errorMessage'] // Get input type from presentation or fallback to schema type diff --git a/src/form.ts b/src/form.ts index 2a5308511..113344199 100644 --- a/src/form.ts +++ b/src/form.ts @@ -245,11 +245,11 @@ function buildFields(params: { schema: JsfObjectSchema, originalSchema: JsfObjec /** * Ensures that no forbidden options are given * @param options - The options to validate - * @throws An error if any forbidden options are found + * Alerts to the console that the option is deprecated and not being considered */ function validateOptions(options: CreateHeadlessFormOptions) { if (Object.prototype.hasOwnProperty.call(options, 'customProperties')) { - throw new Error('`customProperties` is a deprecated option and it\'s not supported on json-schema-form v1') + console.error('[json-schema-form] `customProperties` is a deprecated option and it\'s not supported on json-schema-form v1') } } diff --git a/src/validation/util.ts b/src/validation/util.ts index 40774128b..c0b386e4f 100644 --- a/src/validation/util.ts +++ b/src/validation/util.ts @@ -71,6 +71,7 @@ export function deepEqual(a: SchemaValue, b: SchemaValue): boolean { /** * Deep clones an object using structuredClone if available, otherwise falls back to JSON.parse/stringify approach. + * Please note: some properties might not be serializable (e.g. functions), so they will be lost in the clone. * * @param obj - The object to clone * @returns deep clone of the original object diff --git a/test/fields.test.ts b/test/fields.test.ts index 6f721592c..88ae85846 100644 --- a/test/fields.test.ts +++ b/test/fields.test.ts @@ -163,7 +163,10 @@ describe('fields', () => { }) }) - it('should handle custom x-jsf-presentation properties', () => { + it('should handle custom x-jsf-presentation properties, including functions', () => { + const customComponent = () => { + return null + } const schema: JsfSchema = { type: 'object', properties: { @@ -174,6 +177,7 @@ describe('fields', () => { inputType: 'file', accept: '.pdf,.doc', maxFileSize: 5000000, + Component: customComponent, }, }, }, @@ -192,6 +196,7 @@ describe('fields', () => { required: false, accept: '.pdf,.doc', maxFileSize: 5000000, + Component: customComponent, }, ]) }) From aeb92a84930730acd8b62744746433f5b8fb29e1 Mon Sep 17 00:00:00 2001 From: Capelo Date: Wed, 16 Jul 2025 09:34:58 +0100 Subject: [PATCH 2/2] fix tests --- test/fields/array.test.ts | 2 ++ test/form.test.ts | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/fields/array.test.ts b/test/fields/array.test.ts index e647530d5..3ea59a0b3 100644 --- a/test/fields/array.test.ts +++ b/test/fields/array.test.ts @@ -217,6 +217,8 @@ describe('buildFieldArray', () => { isVisible: true, nameKey: 'title', required: false, + foo: 'bar', + bar: 'baz', }, ], items: expect.any(Object), diff --git a/test/form.test.ts b/test/form.test.ts index b0885e270..4240dc3ba 100644 --- a/test/form.test.ts +++ b/test/form.test.ts @@ -1,5 +1,5 @@ import type { JsfObjectSchema } from '../src/types' -import { describe, expect, it } from '@jest/globals' +import { describe, expect, it, jest } from '@jest/globals' import { createHeadlessForm } from '../src' describe('createHeadlessForm', () => { @@ -16,9 +16,10 @@ describe('createHeadlessForm', () => { } it('should throw error when customProperties option is provided', () => { - expect(() => { - createHeadlessForm(basicSchema, { customProperties: {} } as any) - }).toThrow('`customProperties` is a deprecated option and it\'s not supported on json-schema-form v1') + // spy on console.error + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) + createHeadlessForm(basicSchema, { customProperties: {} } as any) + expect(consoleErrorSpy).toHaveBeenCalledWith('[json-schema-form] `customProperties` is a deprecated option and it\'s not supported on json-schema-form v1') }) it('should not throw error when modifyConfig option is not provided', () => {