diff --git a/src/helpers.js b/src/helpers.js index 8eca889..38a8889 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -244,10 +244,18 @@ function updateField(field, requiredFields, node, formValues, logic, config) { const updateAttributes = (fieldAttrs) => { Object.entries(fieldAttrs).forEach(([key, value]) => { - // some attributes' value (eg "schema") are a function, so we need to call it here - field[key] = typeof value === 'function' ? value() : value; + field[key] = value; + + if (key === 'schema' && typeof value === 'function') { + // key "schema" refers to YupSchema that needs to be processed for validations. + field[key] = value(); + } if (key === 'value') { + /* NOTE/TODO: This section does not have any unit test. Be careful when changing this. + You'll need to check the internal MRs !9266 and !6572 (or other through git blame) + to better understand the reason. Then try to remove this workaround and/or write comprehensive unit tests. */ + // The value of the field should not be driven by the json-schema, // unless it's a read-only field // If the readOnly property has changed, use that updated value, diff --git a/src/tests/createHeadlessForm.test.js b/src/tests/createHeadlessForm.test.js index 061043d..872309d 100644 --- a/src/tests/createHeadlessForm.test.js +++ b/src/tests/createHeadlessForm.test.js @@ -2177,6 +2177,75 @@ describe('createHeadlessForm', () => { }); }); + it('pass custom attributes as function', () => { + function FakeComponent(props) { + const { label, description } = props; + return `A React component with ${label} and ${description}`; + } + // Any custom attributes must be inside "x-jsf-presentation" + const { fields, handleValidation } = createHeadlessForm({ + properties: { + field_a: { + title: 'Field A', + 'x-jsf-presentation': { + inputType: 'text', + MyComponent: FakeComponent, + }, + }, + field_b: { + title: 'Field B', + 'x-jsf-presentation': { + inputType: 'text', + MyComponent: FakeComponent, + }, + }, + }, + allOf: [ + { + if: { + properties: { + field_a: { const: 'yes' }, + }, + required: ['field_a'], + }, + then: { + required: ['field_b'], + }, + }, + ], + }); + + const fieldA = getField(fields, 'field_a'); + expect(fieldA).toMatchObject({ + label: 'Field A', + MyComponent: expect.any(Function), + }); + + const fieldB = getField(fields, 'field_b'); + expect(fieldB).toMatchObject({ + label: 'Field B', + required: false, + MyComponent: expect.any(Function), + }); + + const fakeProps = { label: 'Field B', description: 'fake description' }; + expect(fieldB.MyComponent(fakeProps)).toBe( + 'A React component with Field B and fake description' + ); + + // Ensure "MyComponent" attribute still exsits after a validation cycle. + // This covers the updateField(). Check PR for more context. + handleValidation({ field_a: 'yes' }); + + expect(getField(fields, 'field_a')).toMatchObject({ + MyComponent: expect.any(Function), + }); + expect(getField(fields, 'field_b')).toMatchObject({ + required: true, + MyComponent: expect.any(Function), + }); + }); + it('passes scopedJsonSchema to each field', () => { const { fields } = createHeadlessForm(schemaWithoutInputTypes, { strictInputType: false,