From 5ac606b7b6017a12568e212cfec0c872aed8a494 Mon Sep 17 00:00:00 2001 From: Marek Bodinger Date: Fri, 20 Sep 2024 13:39:05 +0200 Subject: [PATCH 1/5] experimental_customMergeAllOf draft --- packages/core/src/components/Form.tsx | 20 ++- packages/utils/src/createSchemaUtils.ts | 44 ++++-- .../utils/src/schema/getDefaultFormState.ts | 23 +++- packages/utils/src/schema/retrieveSchema.ts | 129 ++++++++++++++---- packages/utils/src/schema/toIdSchema.ts | 37 ++++- packages/utils/src/types.ts | 5 +- 6 files changed, 208 insertions(+), 50 deletions(-) diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx index aa8bc60ab5..46a8dbc73d 100644 --- a/packages/core/src/components/Form.tsx +++ b/packages/core/src/components/Form.tsx @@ -33,6 +33,7 @@ import { validationDataMerge, ValidatorType, Experimental_DefaultFormStateBehavior, + Experimental_CustomMergeAllOf, } from '@rjsf/utils'; import _forEach from 'lodash/forEach'; import _get from 'lodash/get'; @@ -195,6 +196,7 @@ export interface FormProps; // Private /** * _internalFormWrapper is currently used by the semantic-ui theme to provide a custom wrapper around `
` @@ -389,12 +391,26 @@ export default class Form< 'experimental_defaultFormStateBehavior' in props ? props.experimental_defaultFormStateBehavior : this.props.experimental_defaultFormStateBehavior; + const experimental_customMergeAllOf = + 'experimental_customMergeAllOf' in props + ? props.experimental_customMergeAllOf + : this.props.experimental_customMergeAllOf; let schemaUtils: SchemaUtilsType = state.schemaUtils; if ( !schemaUtils || - schemaUtils.doesSchemaUtilsDiffer(props.validator, rootSchema, experimental_defaultFormStateBehavior) + schemaUtils.doesSchemaUtilsDiffer( + props.validator, + rootSchema, + experimental_defaultFormStateBehavior, + experimental_customMergeAllOf + ) ) { - schemaUtils = createSchemaUtils(props.validator, rootSchema, experimental_defaultFormStateBehavior); + schemaUtils = createSchemaUtils( + props.validator, + rootSchema, + experimental_defaultFormStateBehavior, + experimental_customMergeAllOf + ); } const formData: T = schemaUtils.getDefaultFormState(schema, inputFormData) as T; const _retrievedSchema = retrievedSchema ?? schemaUtils.retrieveSchema(schema, formData); diff --git a/packages/utils/src/createSchemaUtils.ts b/packages/utils/src/createSchemaUtils.ts index 39e743d945..746b4d5f75 100644 --- a/packages/utils/src/createSchemaUtils.ts +++ b/packages/utils/src/createSchemaUtils.ts @@ -1,6 +1,7 @@ import deepEquals from './deepEquals'; import { ErrorSchema, + Experimental_CustomMergeAllOf, Experimental_DefaultFormStateBehavior, FormContextType, GlobalUISchemaOptions, @@ -40,6 +41,7 @@ class SchemaUtils; experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior; + experimental_customMergeAllOf?: Experimental_CustomMergeAllOf; /** Constructs the `SchemaUtils` instance with the given `validator` and `rootSchema` stored as instance variables * @@ -50,11 +52,13 @@ class SchemaUtils, rootSchema: S, - experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior + experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior, + experimental_customMergeAllOf?: Experimental_CustomMergeAllOf ) { this.rootSchema = rootSchema; this.validator = validator; this.experimental_defaultFormStateBehavior = experimental_defaultFormStateBehavior; + this.experimental_customMergeAllOf = experimental_customMergeAllOf; } /** Returns the `ValidatorType` in the `SchemaUtilsType` @@ -77,7 +81,8 @@ class SchemaUtils, rootSchema: S, - experimental_defaultFormStateBehavior = {} + experimental_defaultFormStateBehavior = {}, + experimental_customMergeAllOf?: Experimental_CustomMergeAllOf ): boolean { if (!validator || !rootSchema) { return false; @@ -85,7 +90,8 @@ class SchemaUtils(this.validator, schema, this.rootSchema, rawFormData); + return retrieveSchema( + this.validator, + schema, + this.rootSchema, + rawFormData, + this.experimental_customMergeAllOf + ); } /** Sanitize the `data` associated with the `oldSchema` so it is considered appropriate for the `newSchema`. If the @@ -262,7 +275,16 @@ class SchemaUtils { - return toIdSchema(this.validator, schema, id, this.rootSchema, formData, idPrefix, idSeparator); + return toIdSchema( + this.validator, + schema, + id, + this.rootSchema, + formData, + idPrefix, + idSeparator, + this.experimental_customMergeAllOf + ); } /** Generates an `PathSchema` object for the `schema`, recursively @@ -292,7 +314,13 @@ export default function createSchemaUtils< >( validator: ValidatorType, rootSchema: S, - experimental_defaultFormStateBehavior = {} + experimental_defaultFormStateBehavior = {}, + experimental_customMergeAllOf?: Experimental_CustomMergeAllOf ): SchemaUtilsType { - return new SchemaUtils(validator, rootSchema, experimental_defaultFormStateBehavior); + return new SchemaUtils( + validator, + rootSchema, + experimental_defaultFormStateBehavior, + experimental_customMergeAllOf + ); } diff --git a/packages/utils/src/schema/getDefaultFormState.ts b/packages/utils/src/schema/getDefaultFormState.ts index b1f5a096c8..c7de97018e 100644 --- a/packages/utils/src/schema/getDefaultFormState.ts +++ b/packages/utils/src/schema/getDefaultFormState.ts @@ -20,6 +20,7 @@ import mergeDefaultsWithFormData from '../mergeDefaultsWithFormData'; import mergeObjects from '../mergeObjects'; import mergeSchemas from '../mergeSchemas'; import { + Experimental_CustomMergeAllOf, Experimental_DefaultFormStateBehavior, FormContextType, GenericObjectType, @@ -156,6 +157,7 @@ interface ComputeDefaultsProps _recurseList?: string[]; /** Optional configuration object, if provided, allows users to override default form state behavior */ experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior; + experimental_customMergeAllOf?: Experimental_CustomMergeAllOf; /** Optional flag, if true, indicates this schema was required in the parent schema. */ required?: boolean; } @@ -180,6 +182,7 @@ export function computeDefaults(validator, schema, rootSchema, false, [], defaultFormData); + const resolvedSchema = resolveDependencies( + validator, + schema, + rootSchema, + false, + [], + defaultFormData, + experimental_customMergeAllOf + ); schemaToCompute = resolvedSchema[0]; // pick the first element from resolve dependencies } else if (isFixedItems(schema)) { defaults = (schema.items! as S[]).map((itemSchema: S, idx: number) => @@ -298,6 +309,7 @@ export function getObjectDefaults = {}, defaults?: T | T[] | undefined @@ -309,7 +321,7 @@ export function getObjectDefaults(validator, schema, rootSchema, formData) + ? retrieveSchema(validator, schema, rootSchema, formData, experimental_customMergeAllOf) : schema; const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce( (acc: GenericObjectType, key: string) => { @@ -319,6 +331,7 @@ export function getObjectDefaults ) { if (!isObject(theSchema)) { throw new Error('Invalid schema: ' + theSchema); } - const schema = retrieveSchema(validator, theSchema, rootSchema, formData); + const schema = retrieveSchema(validator, theSchema, rootSchema, formData, experimental_customMergeAllOf); const defaults = computeDefaults(validator, schema, { rootSchema, includeUndefinedValues, experimental_defaultFormStateBehavior, + experimental_customMergeAllOf, rawFormData: formData, }); if (formData === undefined || formData === null || (typeof formData === 'number' && isNaN(formData))) { diff --git a/packages/utils/src/schema/retrieveSchema.ts b/packages/utils/src/schema/retrieveSchema.ts index 0f46632f1c..1f98ccb1de 100644 --- a/packages/utils/src/schema/retrieveSchema.ts +++ b/packages/utils/src/schema/retrieveSchema.ts @@ -25,7 +25,14 @@ import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema' import guessType from '../guessType'; import isObject from '../isObject'; import mergeSchemas from '../mergeSchemas'; -import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; +import { + Experimental_CustomMergeAllOf, + FormContextType, + GenericObjectType, + RJSFSchema, + StrictRJSFSchema, + ValidatorType, +} from '../types'; import getFirstMatchingOption from './getFirstMatchingOption'; /** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies @@ -42,8 +49,22 @@ export default function retrieveSchema< T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any ->(validator: ValidatorType, schema: S, rootSchema: S = {} as S, rawFormData?: T): S { - return retrieveSchemaInternal(validator, schema, rootSchema, rawFormData)[0]; +>( + validator: ValidatorType, + schema: S, + rootSchema: S = {} as S, + rawFormData?: T, + experimental_customMergeAllOf?: Experimental_CustomMergeAllOf +): S { + return retrieveSchemaInternal( + validator, + schema, + rootSchema, + rawFormData, + undefined, + undefined, + experimental_customMergeAllOf + )[0]; } /** Resolves a conditional block (if/else/then) by removing the condition and merging the appropriate conditional branch @@ -65,7 +86,8 @@ export function resolveCondition ): S[] { const { if: expression, then, else: otherwise, ...resolvedSchemaLessConditional } = schema; @@ -75,12 +97,28 @@ export function resolveCondition(validator, then as S, rootSchema, formData, expandAllBranches, recurseList) + retrieveSchemaInternal( + validator, + then as S, + rootSchema, + formData, + expandAllBranches, + recurseList, + experimental_customMergeAllOf + ) ); } if (otherwise && typeof otherwise !== 'boolean') { schemas = schemas.concat( - retrieveSchemaInternal(validator, otherwise as S, rootSchema, formData, expandAllBranches, recurseList) + retrieveSchemaInternal( + validator, + otherwise as S, + rootSchema, + formData, + expandAllBranches, + recurseList, + experimental_customMergeAllOf + ) ); } } else { @@ -93,7 +131,8 @@ export function resolveCondition mergeSchemas(resolvedSchemaLessConditional, s) as S); } return resolvedSchemas.flatMap((s) => - retrieveSchemaInternal(validator, s, rootSchema, formData, expandAllBranches, recurseList) + retrieveSchemaInternal( + validator, + s, + rootSchema, + formData, + expandAllBranches, + recurseList, + experimental_customMergeAllOf + ) ); } @@ -156,7 +203,8 @@ export function resolveSchema ): S[] { const updatedSchemas = resolveReference( validator, @@ -181,7 +229,15 @@ export function resolveSchema { - return retrieveSchemaInternal(validator, s, rootSchema, formData, expandAllBranches, recurseList); + return retrieveSchemaInternal( + validator, + s, + rootSchema, + formData, + expandAllBranches, + recurseList, + experimental_customMergeAllOf + ); }); } if (ALL_OF_KEY in schema && Array.isArray(schema.allOf)) { @@ -192,7 +248,8 @@ export function resolveSchema(allOfSchemaElements); @@ -221,7 +278,8 @@ export function resolveReference ): S[] { const updatedSchema = resolveAllReferences(schema, rootSchema, recurseList); if (updatedSchema !== schema) { @@ -232,7 +290,8 @@ export function resolveReference ): S[] { if (!isObject(schema)) { return [{} as S]; @@ -402,7 +462,8 @@ export function retrieveSchemaInternal< rootSchema, expandAllBranches, recurseList, - rawFormData as T + rawFormData as T, + experimental_customMergeAllOf ); } if (ALL_OF_KEY in resolvedSchema) { @@ -412,9 +473,11 @@ export function retrieveSchemaInternal< return [...(allOf as S[]), restOfSchema as S]; } try { - resolvedSchema = mergeAllOf(resolvedSchema, { - deep: false, - } as Options) as S; + resolvedSchema = experimental_customMergeAllOf + ? experimental_customMergeAllOf(resolvedSchema) + : (mergeAllOf(resolvedSchema, { + deep: false, + } as Options) as S); } catch (e) { console.warn('could not merge subschemas in allOf:\n', e); const { allOf, ...resolvedSchemaWithoutAllOf } = resolvedSchema; @@ -492,7 +555,8 @@ export function resolveDependencies ): S[] { // Drop the dependencies from the source schema. const { dependencies, ...remainingSchema } = schema; @@ -511,7 +575,8 @@ export function resolveDependencies ): S[] { let schemas = [resolvedSchema]; // Process dependencies updating the local schema properties as appropriate. @@ -564,7 +630,8 @@ export function processDependencies @@ -575,7 +642,8 @@ export function processDependencies ): S[] { const dependentSchemas = retrieveSchemaInternal( validator, @@ -631,7 +700,8 @@ export function withDependentSchema { const { oneOf, ...dependentSchema } = dependent; @@ -657,7 +727,8 @@ export function withDependentSchema ): S[] { const validSubschemas = oneOf!.filter((subschema) => { if (typeof subschema === 'boolean' || !subschema || !subschema.properties) { @@ -723,7 +795,8 @@ export function withExactlyOneSubschema< rootSchema, formData, expandAllBranches, - recurseList + recurseList, + experimental_customMergeAllOf ); return schemas.map((s) => mergeSchemas(schema, s) as S); }); diff --git a/packages/utils/src/schema/toIdSchema.ts b/packages/utils/src/schema/toIdSchema.ts index 04fb79eaac..5be36a9131 100644 --- a/packages/utils/src/schema/toIdSchema.ts +++ b/packages/utils/src/schema/toIdSchema.ts @@ -3,7 +3,15 @@ import isEqual from 'lodash/isEqual'; import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants'; import isObject from '../isObject'; -import { FormContextType, GenericObjectType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; +import { + Experimental_CustomMergeAllOf, + FormContextType, + GenericObjectType, + IdSchema, + RJSFSchema, + StrictRJSFSchema, + ValidatorType, +} from '../types'; import retrieveSchema from './retrieveSchema'; import getSchemaType from '../getSchemaType'; @@ -28,7 +36,8 @@ function toIdSchemaInternal ): IdSchema { if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { const _schema = retrieveSchema(validator, schema, rootSchema, formData); @@ -42,7 +51,8 @@ function toIdSchemaInternal ): IdSchema { - return toIdSchemaInternal(validator, schema, idPrefix, idSeparator, id, rootSchema, formData); + return toIdSchemaInternal( + validator, + schema, + idPrefix, + idSeparator, + id, + rootSchema, + formData, + undefined, + experimental_customMergeAllOf + ); } diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 6c0fbb0170..e2c2bc8b39 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -88,6 +88,8 @@ export type Experimental_DefaultFormStateBehavior = { allOf?: 'populateDefaults' | 'skipDefaults'; }; +export type Experimental_CustomMergeAllOf = (schema: S) => S; + /** The interface representing a Date object that contains an optional time */ export interface DateObject { /** The year of the Date */ @@ -1035,7 +1037,8 @@ export interface SchemaUtilsType, rootSchema: S, - experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior + experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior, + experimental_customMergeAllOf?: Experimental_CustomMergeAllOf ): boolean; /** Returns the superset of `formData` that includes the given set updated to include any missing fields that have * computed to have defaults provided in the `schema`. From 00dd86e3f0b27eec5b7260283889622438625a37 Mon Sep 17 00:00:00 2001 From: Marek Bodinger Date: Fri, 20 Sep 2024 15:34:08 +0200 Subject: [PATCH 2/5] Add test for experimental_customMergeAllOf --- .../utils/test/schema/retrieveSchemaTest.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/utils/test/schema/retrieveSchemaTest.ts b/packages/utils/test/schema/retrieveSchemaTest.ts index 9f91d199b8..f05a0d5e85 100644 --- a/packages/utils/test/schema/retrieveSchemaTest.ts +++ b/packages/utils/test/schema/retrieveSchemaTest.ts @@ -822,6 +822,37 @@ export default function retrieveSchemaTest(testValidator: TestValidatorType) { default: 'hi', }); }); + + it('should use experimental_customMergeAllOf when provided', () => { + const schema: RJSFSchema = { + allOf: [ + { + type: 'object', + properties: { + string: { type: 'string' }, + }, + }, + { + type: 'object', + properties: { + number: { type: 'number' }, + }, + }, + ], + }; + const rootSchema: RJSFSchema = { definitions: {} }; + const formData = {}; + const customMergeAllOf = jest.fn().mockReturnValue({ + type: 'object', + properties: { string: { type: 'string' }, number: { type: 'number' } }, + }); + + expect(retrieveSchema(testValidator, schema, rootSchema, formData, customMergeAllOf)).toEqual({ + type: 'object', + properties: { string: { type: 'string' }, number: { type: 'number' } }, + }); + expect(customMergeAllOf).toHaveBeenCalledWith(schema); + }); }); describe('Conditional schemas (If, Then, Else)', () => { it('should resolve if, then', () => { From 66febabd10ddb91346c7699526538586c3f786ed Mon Sep 17 00:00:00 2001 From: Marek Bodinger Date: Mon, 23 Sep 2024 21:25:45 +0200 Subject: [PATCH 3/5] Add documentation for experimental_customMergeAllOf --- packages/core/src/components/Form.tsx | 2 ++ .../docs/docs/api-reference/form-props.md | 29 +++++++++++++++++-- packages/utils/src/createSchemaUtils.ts | 10 +++++-- .../utils/src/schema/getDefaultFormState.ts | 2 ++ packages/utils/src/schema/retrieveSchema.ts | 9 ++++++ packages/utils/src/schema/toIdSchema.ts | 2 ++ packages/utils/src/types.ts | 5 ++++ 7 files changed, 53 insertions(+), 6 deletions(-) diff --git a/packages/core/src/components/Form.tsx b/packages/core/src/components/Form.tsx index 46a8dbc73d..0fd28b30c7 100644 --- a/packages/core/src/components/Form.tsx +++ b/packages/core/src/components/Form.tsx @@ -196,6 +196,8 @@ export interface FormProps; // Private /** diff --git a/packages/docs/docs/api-reference/form-props.md b/packages/docs/docs/api-reference/form-props.md index 2f431f406a..a94910d78e 100644 --- a/packages/docs/docs/api-reference/form-props.md +++ b/packages/docs/docs/api-reference/form-props.md @@ -91,8 +91,8 @@ The signature and documentation for this property is as follow: ##### computeSkipPopulate () A function that determines whether to skip populating the array with default values based on the provided validator, schema, and root schema. - If the function returns `true`, the array will not be populated with default values. - If the function returns `false`, the array will be populated with default values according to the `populate` option. +If the function returns `true`, the array will not be populated with default values. +If the function returns `false`, the array will be populated with default values according to the `populate` option. ###### Parameters @@ -104,7 +104,6 @@ A function that determines whether to skip populating the array with default val - boolean: A boolean indicating whether to skip populating the array with default values. - ##### Example ```tsx @@ -252,6 +251,30 @@ render( ); ``` +## experimental_customMergeAllOf + +The `experimental_customMergeAllOf` function allows you to provide a custom implementation for merging `allOf` schemas. This can be particularly useful in scenarios where the default [json-schema-merge-allof](https://github.com/mokkabonna/json-schema-merge-allof) library becomes a performance bottleneck, especially with large and complex schemas or doesn't satisfy your needs. + +By providing your own implementation, you can potentially achieve significant performance improvements. For instance, if your use case only requires a subset of JSON Schema features, you can implement a faster, more tailored merging strategy. + +If you're looking for alternative `allOf` merging implementations, you might consider [allof-merge](https://github.com/udamir/allof-merge). + +**Warning:** This is an experimental feature. Only use this if you fully understand the implications of custom `allOf` merging and are prepared to handle potential edge cases. Incorrect implementations may lead to unexpected behavior or validation errors. + +```tsx +import { Form } from '@rjsf/core'; +import validator from '@rjsf/validator-ajv8'; + +const customMergeAllOf = (schema: RJSFSchema): RJSFSchema => { + // Your custom implementation here +}; + +render( + , + document.getElementById('app') +); +``` + ## disabled It's possible to disable the whole form by setting the `disabled` prop. The `disabled` prop is then forwarded down to each field of the form. diff --git a/packages/utils/src/createSchemaUtils.ts b/packages/utils/src/createSchemaUtils.ts index 746b4d5f75..cfd6883e39 100644 --- a/packages/utils/src/createSchemaUtils.ts +++ b/packages/utils/src/createSchemaUtils.ts @@ -31,9 +31,10 @@ import { } from './schema'; /** The `SchemaUtils` class provides a wrapper around the publicly exported APIs in the `utils/schema` directory such - * that one does not have to explicitly pass the `validator`, `rootSchema`, or `experimental_defaultFormStateBehavior` to each method. - * Since these generally do not change across a `Form`, this allows for providing a simplified set of APIs to the - * `@rjsf/core` components and the various themes as well. This class implements the `SchemaUtilsType` interface. + * that one does not have to explicitly pass the `validator`, `rootSchema`, `experimental_defaultFormStateBehavior` or + * `experimental_customMergeAllOf` to each method. Since these generally do not change across a `Form`, this allows for + * providing a simplified set of APIs to the `@rjsf/core` components and the various themes as well. This class + * implements the `SchemaUtilsType` interface. */ class SchemaUtils implements SchemaUtilsType @@ -48,6 +49,7 @@ class SchemaUtils, @@ -76,6 +78,7 @@ class SchemaUtils _recurseList?: string[]; /** Optional configuration object, if provided, allows users to override default form state behavior */ experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior; + /** Optional function that allows for custom merging of `allOf` schemas */ experimental_customMergeAllOf?: Experimental_CustomMergeAllOf; /** Optional flag, if true, indicates this schema was required in the parent schema. */ required?: boolean; @@ -534,6 +535,7 @@ export function getDefaultBasedOnSchemaType< * If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as * false when computing defaults for any nested object properties. * @param [experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior + * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas * @returns - The resulting `formData` with all the defaults provided */ export default function getDefaultFormState< diff --git a/packages/utils/src/schema/retrieveSchema.ts b/packages/utils/src/schema/retrieveSchema.ts index 1f98ccb1de..c3d6d9413f 100644 --- a/packages/utils/src/schema/retrieveSchema.ts +++ b/packages/utils/src/schema/retrieveSchema.ts @@ -43,6 +43,7 @@ import getFirstMatchingOption from './getFirstMatchingOption'; * @param schema - The schema for which retrieving a schema is desired * @param [rootSchema={}] - The root schema that will be forwarded to all the APIs * @param [rawFormData] - The current formData, if any, to assist retrieving a schema + * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas * @returns - The schema having its conditions, additional properties, references and dependencies resolved */ export default function retrieveSchema< @@ -78,6 +79,7 @@ export default function retrieveSchema< * dependencies as a list of schemas * @param recurseList - The list of recursive references already processed * @param [formData] - The current formData to assist retrieving a schema + * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas * @returns - A list of schemas with the appropriate conditions resolved, possibly with all branches expanded */ export function resolveCondition( @@ -195,6 +197,7 @@ export function getAllPermutationsOfXxxOf( @@ -270,6 +273,7 @@ export function resolveSchema( @@ -426,6 +430,7 @@ export function stubExistingAdditionalProperties< * @param [expandAllBranches=false] - Flag, if true, will return all possible branches of conditions, any/oneOf and * dependencies as a list of schemas * @param [recurseList=[]] - The optional, list of recursive references already processed + * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas * @returns - The schema(s) resulting from having its conditions, additional properties, references and dependencies * resolved. Multiple schemas may be returned if `expandAllBranches` is true. */ @@ -547,6 +552,7 @@ export function resolveAnyOrOneOfSchemas< * as a list of schemas * @param recurseList - The list of recursive references already processed * @param [formData] - The current formData, if any, to assist retrieving a schema + * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas * @returns - The list of schemas with their dependencies resolved */ export function resolveDependencies( @@ -592,6 +598,7 @@ export function resolveDependencies( @@ -681,6 +688,7 @@ export function withDependentProperties * as a list of schemas * @param recurseList - The list of recursive references already processed * @param [formData]- The current formData to assist retrieving a schema + * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas * @returns - The list of schemas with the dependent schema resolved into them */ export function withDependentSchema( @@ -747,6 +755,7 @@ export function withDependentSchema( @@ -102,6 +103,7 @@ function toIdSchemaInternal( diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index e2c2bc8b39..ed1ca7aa69 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -88,6 +88,10 @@ export type Experimental_DefaultFormStateBehavior = { allOf?: 'populateDefaults' | 'skipDefaults'; }; +/** Optional function that allows for custom merging of `allOf` schemas + * @param schema - Schema with `allOf` that needs to be merged + * @returns The merged schema + */ export type Experimental_CustomMergeAllOf = (schema: S) => S; /** The interface representing a Date object that contains an optional time */ @@ -1032,6 +1036,7 @@ export interface SchemaUtilsType Date: Mon, 23 Sep 2024 21:29:59 +0200 Subject: [PATCH 4/5] Changelog update --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 920ccfdeb6..946ca5ddad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ should change the heading of the (upcoming) version to include a major version b - Updated `SchemaField` to pass `required` flag to `_AnyOfField`/`_OneOfField` - Updated `Form` to deal with null objects in `filterErrorsBasedOnSchema()`, fixing [#4306](https://github.com/rjsf-team/react-jsonschema-form/issues/4306) +## @rjsf/utils + +- Added `experimental_customMergeAllOf` option to `retrieveSchema` to allow custom merging of `allOf` schemas + ## Dev / docs / playground - Updated the `custom-widgets-fields.md` to add examples of wrapping a widget/field From 2035941ff4cdf77e40ea2a1a43ed68073de76314 Mon Sep 17 00:00:00 2001 From: Marek Bodinger Date: Fri, 18 Oct 2024 14:56:59 +0200 Subject: [PATCH 5/5] Add changelog v2 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f08a288aee..1597d24fb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,12 @@ should change the heading of the (upcoming) version to include a major version b --> +# 5.21.3 + +## @rjsf/utils + +- Added `experimental_customMergeAllOf` option to `retrieveSchema` to allow custom merging of `allOf` schemas + # 5.21.2 ## @rjsf/core