Skip to content

Commit

Permalink
experimental_customMergeAllOf v2 (#4383)
Browse files Browse the repository at this point in the history
* Pass experimental_customMergeAllOf to missing code branches

* Add experimental_customMergeAllOf parameter to documentation, fix link with anchor

* Changelog

---------

Co-authored-by: Marek Bodinger <marek.bodinger@gmail.com>
  • Loading branch information
MarekBodingerBA and MarekBodinger authored Nov 15, 2024
1 parent e71cb8d commit 011659d
Show file tree
Hide file tree
Showing 13 changed files with 235 additions and 56 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ should change the heading of the (upcoming) version to include a major version b

- Updated `Experimental_DefaultFormStateBehavior` to add a new `constAsDefaults` option
- Updated `getDefaultFormState()` to use the new `constAsDefaults` option to control how const is used for defaulting, fixing [#4344](https://github.com/rjsf-team/react-jsonschema-form/issues/4344), [#4361](https://github.com/rjsf-team/react-jsonschema-form/issues/4361) and [#4377](https://github.com/rjsf-team/react-jsonschema-form/issues/4377)
- Use `experimental_customMergeAllOf` option in functions that have previously missed it.

## Dev / docs / playground

Expand Down
11 changes: 10 additions & 1 deletion packages/docs/docs/api-reference/utility-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ Returns the superset of `formData` that includes the given set updated to includ
- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s
- [includeUndefinedValues=false]: boolean | "excludeObjectChildren" - Optional flag, if true, cause undefined values to be added as defaults. If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as false when computing defaults for any nested object properties.
- [experimental_defaultFormStateBehavior]: Experimental_DefaultFormStateBehavior - See `Form` documentation for the [experimental_defaultFormStateBehavior](./form-props.md#experimental_defaultFormStateBehavior) prop
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_customMergeAllOf) prop
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand All @@ -916,6 +916,7 @@ Determines whether the combination of `schema` and `uiSchema` properties indicat
- [uiSchema={}]: UiSchema<T, S, F> - The UI schema from which to derive potentially displayable information
- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s
- [globalOptions={}]: GlobalUISchemaOptions - The optional Global UI Schema from which to get any fallback `xxx` options
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand All @@ -936,6 +937,7 @@ The closest match is determined using the number of matching properties, and mor
- options: S[] - The list of options to find a matching options from
- [selectedOption=-1]: number - The index of the currently selected option, defaulted to -1 if not specified
- [discriminatorField]: string | undefined - The optional name of the field within the options object whose value is used to determine which option is selected
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand Down Expand Up @@ -985,6 +987,7 @@ Checks to see if the `schema` and `uiSchema` combination represents an array of
- schema: S - The schema for which check for array of files flag is desired
- [uiSchema={}]: UiSchema<T, S, F> - The UI schema from which to check the widget
- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand All @@ -999,6 +1002,7 @@ Checks to see if the `schema` combination represents a multi-select
- validator: ValidatorType<T, S, F> - An implementation of the `ValidatorType` interface that will be used when necessary
- schema: S - The schema for which check for a multi-select flag is desired
- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand All @@ -1013,6 +1017,7 @@ Checks to see if the `schema` combination represents a select
- validator: ValidatorType<T, S, F> - An implementation of the `ValidatorType` interface that will be used when necessary
- theSchema: S - The schema for which check for a select flag is desired
- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand Down Expand Up @@ -1048,6 +1053,7 @@ potentially recursive resolution.
- schema: S - The schema for which retrieving a schema is desired
- [rootSchema={}]: S - The root schema that will be forwarded to all the APIs
- [rawFormData]: T | undefined - The current formData, if any, to assist retrieving a schema
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand All @@ -1067,6 +1073,7 @@ Also, any properties in the old schema that are non-existent in the new schema a
- [newSchema]: S | undefined - The new schema for which the data is being sanitized
- [oldSchema]: S | undefined - The old schema from which the data originated
- [data={}]: any - The form data associated with the schema, defaulting to an empty object when undefined
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand All @@ -1085,6 +1092,7 @@ Generates an `IdSchema` object for the `schema`, recursively
- [formData]: T | undefined - The current formData, if any, to assist retrieving a schema
- [idPrefix='root']: string - The prefix to use for the id
- [idSeparator='_']: string - The separator to use for the path segments in the id
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand All @@ -1101,6 +1109,7 @@ Generates an `PathSchema` object for the `schema`, recursively
- [name='']: string - The base name for the schema
- [rootSchema]: S | undefined - The root schema, used to primarily to look up `$ref`s
- [formData]: T | undefined - The current formData, if any, to assist retrieving a schema
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf&lt;S&gt; - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_custommergeallof) prop

#### Returns

Expand Down
36 changes: 29 additions & 7 deletions packages/utils/src/createSchemaUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,14 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
* @returns - True if the label should be displayed or false if it should not
*/
getDisplayLabel(schema: S, uiSchema?: UiSchema<T, S, F>, globalOptions?: GlobalUISchemaOptions) {
return getDisplayLabel<T, S, F>(this.validator, schema, uiSchema, this.rootSchema, globalOptions);
return getDisplayLabel<T, S, F>(
this.validator,
schema,
uiSchema,
this.rootSchema,
globalOptions,
this.experimental_customMergeAllOf
);
}

/** Determines which of the given `options` provided most closely matches the `formData`.
Expand Down Expand Up @@ -161,7 +168,8 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
formData,
options,
selectedOption,
discriminatorField
discriminatorField,
this.experimental_customMergeAllOf
);
}

Expand Down Expand Up @@ -199,7 +207,7 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
* @returns - True if schema/uiSchema contains an array of files, otherwise false
*/
isFilesArray(schema: S, uiSchema?: UiSchema<T, S, F>) {
return isFilesArray<T, S, F>(this.validator, schema, uiSchema, this.rootSchema);
return isFilesArray<T, S, F>(this.validator, schema, uiSchema, this.rootSchema, this.experimental_customMergeAllOf);
}

/** Checks to see if the `schema` combination represents a multi-select
Expand All @@ -208,7 +216,7 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
* @returns - True if schema contains a multi-select, otherwise false
*/
isMultiSelect(schema: S) {
return isMultiSelect<T, S, F>(this.validator, schema, this.rootSchema);
return isMultiSelect<T, S, F>(this.validator, schema, this.rootSchema, this.experimental_customMergeAllOf);
}

/** Checks to see if the `schema` combination represents a select
Expand All @@ -217,7 +225,7 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
* @returns - True if schema contains a select, otherwise false
*/
isSelect(schema: S) {
return isSelect<T, S, F>(this.validator, schema, this.rootSchema);
return isSelect<T, S, F>(this.validator, schema, this.rootSchema, this.experimental_customMergeAllOf);
}

/** Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in
Expand Down Expand Up @@ -265,7 +273,14 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
* to `undefined`. Will return `undefined` if the new schema is not an object containing properties.
*/
sanitizeDataForNewSchema(newSchema?: S, oldSchema?: S, data?: any): T {
return sanitizeDataForNewSchema(this.validator, this.rootSchema, newSchema, oldSchema, data);
return sanitizeDataForNewSchema(
this.validator,
this.rootSchema,
newSchema,
oldSchema,
data,
this.experimental_customMergeAllOf
);
}

/** Generates an `IdSchema` object for the `schema`, recursively
Expand Down Expand Up @@ -298,7 +313,14 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends Fo
* @returns - The `PathSchema` object for the `schema`
*/
toPathSchema(schema: S, name?: string, formData?: T): PathSchema<T> {
return toPathSchema<T, S, F>(this.validator, schema, name, this.rootSchema, formData);
return toPathSchema<T, S, F>(
this.validator,
schema,
name,
this.rootSchema,
formData,
this.experimental_customMergeAllOf
);
}
}

Expand Down
39 changes: 31 additions & 8 deletions packages/utils/src/schema/getClosestMatchingOption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import getFirstMatchingOption from './getFirstMatchingOption';
import retrieveSchema, { resolveAllReferences } from './retrieveSchema';
import { ONE_OF_KEY, REF_KEY, JUNK_OPTION_ID, ANY_OF_KEY } from '../constants';
import guessType from '../guessType';
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator';

Expand Down Expand Up @@ -45,13 +45,15 @@ export const JUNK_OPTION: StrictRJSFSchema = {
* @param rootSchema - The root JSON schema of the entire form
* @param schema - The schema for which the score is being calculated
* @param formData - The form data associated with the schema, used to calculate the score
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
* @returns - The score a schema against the formData
*/
export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
validator: ValidatorType<T, S, F>,
rootSchema: S,
schema?: S,
formData?: any
formData?: any,
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
): number {
let totalScore = 0;
if (schema) {
Expand All @@ -64,8 +66,23 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
return score;
}
if (has(value, REF_KEY)) {
const newSchema = retrieveSchema<T, S, F>(validator, value as S, rootSchema, formValue);
return score + calculateIndexScore<T, S, F>(validator, rootSchema, newSchema, formValue || {});
const newSchema = retrieveSchema<T, S, F>(
validator,
value as S,
rootSchema,
formValue,
experimental_customMergeAllOf
);
return (
score +
calculateIndexScore<T, S, F>(
validator,
rootSchema,
newSchema,
formValue || {},
experimental_customMergeAllOf
)
);
}
if ((has(value, ONE_OF_KEY) || has(value, ANY_OF_KEY)) && formValue) {
const key = has(value, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
Expand All @@ -78,7 +95,8 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
formValue,
get(value, key) as S[],
-1,
discriminator
discriminator,
experimental_customMergeAllOf
)
);
}
Expand All @@ -87,7 +105,10 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
// If the structure is matching then give it a little boost in score
score += 1;
}
return score + calculateIndexScore<T, S, F>(validator, rootSchema, value as S, formValue);
return (
score +
calculateIndexScore<T, S, F>(validator, rootSchema, value as S, formValue, experimental_customMergeAllOf)
);
}
if (value.type === guessType(formValue)) {
// If the types match, then we bump the score by one
Expand Down Expand Up @@ -135,6 +156,7 @@ export function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSc
* @param [selectedOption=-1] - The index of the currently selected option, defaulted to -1 if not specified
* @param [discriminatorField] - The optional name of the field within the options object whose value is used to
* determine which option is selected
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
* @returns - The index of the option that is the closest match to the `formData` or the `selectedOption` if no match
*/
export default function getClosestMatchingOption<
Expand All @@ -147,7 +169,8 @@ export default function getClosestMatchingOption<
formData: T | undefined,
options: S[],
selectedOption = -1,
discriminatorField?: string
discriminatorField?: string,
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>
): number {
// First resolve any refs in the options
const resolvedOptions = options.map((option) => {
Expand Down Expand Up @@ -185,7 +208,7 @@ export default function getClosestMatchingOption<
(scoreData: BestType, index: number) => {
const { bestScore } = scoreData;
const option = resolvedOptions[index];
const score = calculateIndexScore(validator, rootSchema, option, formData);
const score = calculateIndexScore(validator, rootSchema, option, formData, experimental_customMergeAllOf);
scoreCount.add(score);
if (score > bestScore) {
return { bestIndex: index, bestScore: score };
Expand Down
15 changes: 12 additions & 3 deletions packages/utils/src/schema/getDefaultFormState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
includeUndefinedValues,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined,
rawFormData: formData as T,
required,
Expand Down Expand Up @@ -268,7 +269,8 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
isEmpty(formData) ? undefined : formData,
oneOf as S[],
0,
discriminator
discriminator,
experimental_customMergeAllOf
)
] as S;
schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
Expand All @@ -285,7 +287,8 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
isEmpty(formData) ? undefined : formData,
anyOf as S[],
0,
discriminator
discriminator,
experimental_customMergeAllOf
)
] as S;
schemaToCompute = mergeSchemas(remaining, schemaToCompute) as S;
Expand All @@ -297,6 +300,7 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
includeUndefinedValues,
_recurseList: updatedRecurseList,
experimental_defaultFormStateBehavior: experimental_dfsb_to_compute,
experimental_customMergeAllOf,
parentDefaults: defaults as T | undefined,
rawFormData: formData as T,
required,
Expand Down Expand Up @@ -404,6 +408,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
includeUndefinedValues: includeUndefinedValues === true,
parentDefaults: get(defaults, [key]),
rawFormData: get(formData, [key]),
Expand Down Expand Up @@ -440,6 +445,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
rootSchema = {} as S,
_recurseList = [],
experimental_defaultFormStateBehavior = undefined,
experimental_customMergeAllOf = undefined,
required,
}: ComputeDefaultsProps<T, S> = {},
defaults?: T | T[] | undefined
Expand All @@ -465,6 +471,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
parentDefaults: item,
required,
});
Expand All @@ -482,6 +489,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
rawFormData: item,
parentDefaults: get(defaults, [idx]),
required,
Expand Down Expand Up @@ -513,7 +521,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
if (
!schema.minItems ||
isMultiSelect<T, S, F>(validator, schema, rootSchema) ||
isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) ||
computeSkipPopulate<T, S, F>(validator, schema, rootSchema) ||
schema.minItems <= defaultsLength
) {
Expand All @@ -531,6 +539,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
required,
})
) as T[];
Expand Down
Loading

0 comments on commit 011659d

Please sign in to comment.