Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experimental_customMergeAllOf v2 #4383

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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<S> - See `Form` documentation for the [experimental_customMergeAllOf](./form-props.md#experimental_customMergeAllOf) prop
- [experimental_customMergeAllOf]: Experimental_CustomMergeAllOf<S> - 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