@rjsf/utils 6.6.2 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +201 -0
- package/dist/index.cjs +229 -108
- package/dist/index.cjs.map +4 -4
- package/dist/utils.esm.js +229 -108
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +260 -136
- package/lib/SelectedOptionDescription.d.ts +4 -0
- package/lib/SelectedOptionDescription.js +24 -0
- package/lib/SelectedOptionDescription.js.map +1 -0
- package/lib/constants.d.ts +8 -5
- package/lib/constants.js +10 -7
- package/lib/constants.js.map +1 -1
- package/lib/createSchemaUtils.js +1 -1
- package/lib/createSchemaUtils.js.map +1 -1
- package/lib/enums.d.ts +4 -0
- package/lib/enums.js +4 -0
- package/lib/enums.js.map +1 -1
- package/lib/getSubmitButtonOptions.js +1 -1
- package/lib/getSubmitButtonOptions.js.map +1 -1
- package/lib/getWidget.js +2 -1
- package/lib/getWidget.js.map +1 -1
- package/lib/hashForSchema.d.ts +2 -0
- package/lib/hashForSchema.js +3 -1
- package/lib/hashForSchema.js.map +1 -1
- package/lib/index.d.ts +5 -2
- package/lib/index.js +4 -2
- package/lib/index.js.map +1 -1
- package/lib/isFixedItems.d.ts +2 -2
- package/lib/isFixedItems.js +3 -3
- package/lib/isFixedItems.js.map +1 -1
- package/lib/isRootSchema.js +2 -3
- package/lib/isRootSchema.js.map +1 -1
- package/lib/logUnsupportedDefaultForEnum.d.ts +9 -0
- package/lib/logUnsupportedDefaultForEnum.js +18 -0
- package/lib/logUnsupportedDefaultForEnum.js.map +1 -0
- package/lib/mergeSchemas.js +8 -1
- package/lib/mergeSchemas.js.map +1 -1
- package/lib/optionsList.js +22 -21
- package/lib/optionsList.js.map +1 -1
- package/lib/resolveUiSchema.js.map +1 -1
- package/lib/schema/getDefaultFormState.d.ts +1 -1
- package/lib/schema/getDefaultFormState.js +54 -17
- package/lib/schema/getDefaultFormState.js.map +1 -1
- package/lib/schema/getDisplayLabel.js +2 -3
- package/lib/schema/getDisplayLabel.js.map +1 -1
- package/lib/schema/omitExtraData.js +11 -0
- package/lib/schema/omitExtraData.js.map +1 -1
- package/lib/schema/retrieveSchema.d.ts +5 -1
- package/lib/schema/retrieveSchema.js +49 -14
- package/lib/schema/retrieveSchema.js.map +1 -1
- package/lib/schemaRequiresTrueValue.js +4 -3
- package/lib/schemaRequiresTrueValue.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +24 -0
- package/lib/useAltDateWidgetProps.d.ts +1 -1
- package/lib/useFileWidgetProps.js +2 -2
- package/lib/useFileWidgetProps.js.map +1 -1
- package/package.json +19 -19
- package/src/SelectedOptionDescription.tsx +48 -0
- package/src/constants.ts +10 -7
- package/src/createSchemaUtils.ts +1 -1
- package/src/enums.ts +4 -0
- package/src/getSubmitButtonOptions.ts +1 -1
- package/src/getWidget.tsx +1 -1
- package/src/hashForSchema.ts +3 -1
- package/src/index.ts +6 -0
- package/src/isFixedItems.ts +3 -3
- package/src/isRootSchema.ts +2 -4
- package/src/logUnsupportedDefaultForEnum.ts +28 -0
- package/src/mergeSchemas.ts +9 -2
- package/src/optionsList.ts +20 -22
- package/src/resolveUiSchema.ts +12 -4
- package/src/schema/getDefaultFormState.ts +80 -22
- package/src/schema/getDisplayLabel.ts +3 -4
- package/src/schema/omitExtraData.ts +11 -0
- package/src/schema/retrieveSchema.ts +59 -13
- package/src/schemaRequiresTrueValue.ts +3 -3
- package/src/types.ts +31 -0
- package/src/useFileWidgetProps.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rjsf/utils",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
@@ -42,20 +42,6 @@
|
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=20"
|
|
44
44
|
},
|
|
45
|
-
"scripts": {
|
|
46
|
-
"compileReplacer": "tsc -p tsconfig.replacer.json && move-file lodashReplacer.js lodashReplacer.cjs",
|
|
47
|
-
"build:ts": "npm run compileReplacer && rimraf ./lib && tsc -b tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
48
|
-
"build:cjs": "esbuild ./src/index.ts --bundle --outfile=dist/index.cjs --sourcemap --packages=external --format=cjs",
|
|
49
|
-
"build:esm": "esbuild ./src/index.ts --bundle --outfile=dist/utils.esm.js --sourcemap --packages=external --format=esm",
|
|
50
|
-
"build:umd": "rollup dist/utils.esm.js --format=umd --file=dist/utils.umd.js --name=@rjsf/utils",
|
|
51
|
-
"build": "npm run build:ts && npm run build:cjs && npm run build:esm && npm run build:umd",
|
|
52
|
-
"cs-check": "oxfmt --check \"{src,test}/**/*.{ts,tsx}\"",
|
|
53
|
-
"cs-format": "oxfmt \"{src,test}/**/*.{ts,tsx}\"",
|
|
54
|
-
"lint": "oxlint src test",
|
|
55
|
-
"precommit": "lint-staged",
|
|
56
|
-
"test": "vitest run",
|
|
57
|
-
"test:debug": "node --inspect-brk ../../node_modules/.bin/vitest --pool=forks"
|
|
58
|
-
},
|
|
59
45
|
"lint-staged": {
|
|
60
46
|
"{src,test}/**/*.{ts,tsx}": [
|
|
61
47
|
"oxlint --fix",
|
|
@@ -68,7 +54,7 @@
|
|
|
68
54
|
"dependencies": {
|
|
69
55
|
"@x0k/json-schema-merge": "^1.0.3",
|
|
70
56
|
"fast-equals": "^6.0.0",
|
|
71
|
-
"fast-uri": "^
|
|
57
|
+
"fast-uri": "^4.0.0",
|
|
72
58
|
"jsonpointer": "^5.0.1",
|
|
73
59
|
"lodash": "^4.18.1",
|
|
74
60
|
"lodash-es": "^4.18.1",
|
|
@@ -77,7 +63,7 @@
|
|
|
77
63
|
"devDependencies": {
|
|
78
64
|
"@types/json-schema": "^7.0.15",
|
|
79
65
|
"@types/react-is": "^18.3.1",
|
|
80
|
-
"deep-freeze-es6": "^
|
|
66
|
+
"deep-freeze-es6": "^5.0.0"
|
|
81
67
|
},
|
|
82
68
|
"publishConfig": {
|
|
83
69
|
"access": "public"
|
|
@@ -96,5 +82,19 @@
|
|
|
96
82
|
"type": "git",
|
|
97
83
|
"url": "git+https://github.com/rjsf-team/react-jsonschema-form.git"
|
|
98
84
|
},
|
|
99
|
-
"license": "Apache-2.0"
|
|
100
|
-
|
|
85
|
+
"license": "Apache-2.0",
|
|
86
|
+
"scripts": {
|
|
87
|
+
"compileReplacer": "tsc -p tsconfig.replacer.json && move-file lodashReplacer.js lodashReplacer.cjs",
|
|
88
|
+
"build:ts": "pnpm run compileReplacer && rimraf ./lib && tsc -b tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
89
|
+
"build:cjs": "esbuild ./src/index.ts --bundle --outfile=dist/index.cjs --sourcemap --packages=external --format=cjs",
|
|
90
|
+
"build:esm": "esbuild ./src/index.ts --bundle --outfile=dist/utils.esm.js --sourcemap --packages=external --format=esm",
|
|
91
|
+
"build:umd": "rollup dist/utils.esm.js --format=umd --file=dist/utils.umd.js --name=@rjsf/utils",
|
|
92
|
+
"build": "pnpm run build:ts && pnpm run build:cjs && pnpm run build:esm && pnpm run build:umd",
|
|
93
|
+
"cs-check": "oxfmt --check \"{src,test}/**/*.{ts,tsx}\"",
|
|
94
|
+
"cs-format": "oxfmt \"{src,test}/**/*.{ts,tsx}\"",
|
|
95
|
+
"lint": "oxlint src test",
|
|
96
|
+
"precommit": "lint-staged",
|
|
97
|
+
"test": "vitest run",
|
|
98
|
+
"test:debug": "node --inspect-brk ../../node_modules/.bin/vitest --pool=forks"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import enumOptionsIsSelected from './enumOptionsIsSelected';
|
|
2
|
+
import getTemplate from './getTemplate';
|
|
3
|
+
import getUiOptions from './getUiOptions';
|
|
4
|
+
import { descriptionId } from './idGenerators';
|
|
5
|
+
import type { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from './types';
|
|
6
|
+
|
|
7
|
+
export type SelectedOptionDescriptionProps<
|
|
8
|
+
T = any,
|
|
9
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
10
|
+
F extends FormContextType = any,
|
|
11
|
+
> = Pick<WidgetProps<T, S, F>, 'id' | 'multiple' | 'options' | 'registry' | 'uiSchema' | 'value' | 'hideLabel'>;
|
|
12
|
+
|
|
13
|
+
/** Renders the description associated with the selected oneOf or anyOf option in a single-select widget. */
|
|
14
|
+
export default function SelectedOptionDescription<
|
|
15
|
+
T = any,
|
|
16
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
17
|
+
F extends FormContextType = any,
|
|
18
|
+
>({ hideLabel, id, multiple, options, registry, uiSchema, value }: SelectedOptionDescriptionProps<T, S, F>) {
|
|
19
|
+
if (multiple || hideLabel) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { label = true } = getUiOptions<T, S, F>(uiSchema, registry.globalUiOptions);
|
|
24
|
+
if (!label) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const option = options.enumOptions?.find(({ value: enumValue }) => enumOptionsIsSelected(enumValue, value));
|
|
29
|
+
const description = option?.schema?.description;
|
|
30
|
+
if (!description) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate', T, S, F>(
|
|
35
|
+
'DescriptionFieldTemplate',
|
|
36
|
+
registry,
|
|
37
|
+
options,
|
|
38
|
+
);
|
|
39
|
+
return (
|
|
40
|
+
<DescriptionFieldTemplate
|
|
41
|
+
id={descriptionId(id)}
|
|
42
|
+
description={description}
|
|
43
|
+
schema={option.schema!}
|
|
44
|
+
uiSchema={uiSchema}
|
|
45
|
+
registry={registry}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** Below are the list of all the keys into various elements of a RJSFSchema or UiSchema that are used by the various
|
|
2
|
-
* utility functions. In addition to those keys, there are the special `ADDITIONAL_PROPERTY_FLAG
|
|
3
|
-
* `
|
|
4
|
-
* utility.
|
|
2
|
+
* utility functions. In addition to those keys, there are the special `ADDITIONAL_PROPERTY_FLAG`,
|
|
3
|
+
* `RJSF_REF_KEY`, and `RJSF_REF_CYCLE_KEY` Symbols that are added to a schema under certain conditions by the
|
|
4
|
+
* `retrieveSchema()` utility. Using Symbols keeps these internal markers invisible to JSON serialisation and
|
|
5
|
+
* JSON Schema validators (AJV, ATA, etc.), which only enumerate string-keyed properties.
|
|
5
6
|
*/
|
|
6
|
-
export const ADDITIONAL_PROPERTY_FLAG = '__additional_property';
|
|
7
|
+
export const ADDITIONAL_PROPERTY_FLAG = Symbol('__additional_property');
|
|
7
8
|
export const ADDITIONAL_PROPERTIES_KEY = 'additionalProperties';
|
|
8
9
|
export const ALL_OF_KEY = 'allOf';
|
|
9
10
|
export const ANY_OF_KEY = 'anyOf';
|
|
@@ -25,7 +26,8 @@ export const READONLY_KEY = 'readonly';
|
|
|
25
26
|
export const REQUIRED_KEY = 'required';
|
|
26
27
|
export const SUBMIT_BTN_OPTIONS_KEY = 'submitButtonOptions';
|
|
27
28
|
export const REF_KEY = '$ref';
|
|
28
|
-
export const RJSF_REF_KEY = '__rjsf_ref';
|
|
29
|
+
export const RJSF_REF_KEY = Symbol('__rjsf_ref');
|
|
30
|
+
export const RJSF_REF_CYCLE_KEY = Symbol('__rjsf_ref_cycle');
|
|
29
31
|
export const SCHEMA_KEY = '$schema';
|
|
30
32
|
export const DEFAULT_ID_PREFIX = 'root';
|
|
31
33
|
export const DEFAULT_ID_SEPARATOR = '_';
|
|
@@ -40,8 +42,9 @@ export const FORM_CONTEXT_NAME = 'formContext';
|
|
|
40
42
|
/** The name of the `layoutGridLookupMap` attribute in the form context
|
|
41
43
|
*/
|
|
42
44
|
export const LOOKUP_MAP_NAME = 'layoutGridLookupMap';
|
|
43
|
-
export const
|
|
44
|
-
export const
|
|
45
|
+
export const RJSF_PREFIX_KEY = '__rjsf';
|
|
46
|
+
export const RJSF_ADDITIONAL_PROPERTIES_FLAG = `${RJSF_PREFIX_KEY}_additionalProperties`;
|
|
47
|
+
export const ROOT_SCHEMA_PREFIX = `${RJSF_PREFIX_KEY}_rootSchema`;
|
|
45
48
|
export const UI_FIELD_KEY = 'ui:field';
|
|
46
49
|
export const UI_WIDGET_KEY = 'ui:widget';
|
|
47
50
|
export const UI_OPTIONS_KEY = 'ui:options';
|
package/src/createSchemaUtils.ts
CHANGED
|
@@ -63,7 +63,7 @@ class SchemaUtils<
|
|
|
63
63
|
experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior,
|
|
64
64
|
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
65
65
|
) {
|
|
66
|
-
if (rootSchema
|
|
66
|
+
if (rootSchema?.[SCHEMA_KEY] === JSON_SCHEMA_DRAFT_2020_12) {
|
|
67
67
|
this.rootSchema = makeAllReferencesAbsolute(rootSchema, get(rootSchema, ID_KEY, '#'));
|
|
68
68
|
} else {
|
|
69
69
|
this.rootSchema = rootSchema;
|
package/src/enums.ts
CHANGED
|
@@ -27,6 +27,8 @@ export enum TranslatableString {
|
|
|
27
27
|
AddItemButton = 'Add Item',
|
|
28
28
|
/** Copy button title, used by IconButton */
|
|
29
29
|
CopyButton = 'Copy',
|
|
30
|
+
/** Expand cycle button title */
|
|
31
|
+
ExpandButton = 'Expand Cycle',
|
|
30
32
|
/** Move down button title, used by IconButton */
|
|
31
33
|
MoveDownButton = 'Move down',
|
|
32
34
|
/** Move up button title, used by IconButton */
|
|
@@ -70,6 +72,8 @@ export enum TranslatableString {
|
|
|
70
72
|
KeyLabel = '%1 Key',
|
|
71
73
|
/** Deprecated label, where %1 will be replaced by the label as provided by SchemaField */
|
|
72
74
|
DeprecatedLabel = '%1 (deprecated)',
|
|
75
|
+
/** Cyclic schema message */
|
|
76
|
+
CycleDetected = 'Circular reference ($ref cycle) detected for field "%1". You may choose to expand to the next cycle break',
|
|
73
77
|
// Strings with replaceable parameters AND/OR that support markdown and html
|
|
74
78
|
/** Invalid object field configuration as provided by the ObjectField.
|
|
75
79
|
* NOTE: Use markdown notation rather than html tags.
|
|
@@ -23,7 +23,7 @@ export default function getSubmitButtonOptions<
|
|
|
23
23
|
F extends FormContextType = any,
|
|
24
24
|
>(uiSchema: UiSchema<T, S, F> = {}): UISchemaSubmitButtonOptions {
|
|
25
25
|
const uiOptions = getUiOptions<T, S, F>(uiSchema);
|
|
26
|
-
if (uiOptions
|
|
26
|
+
if (uiOptions?.[SUBMIT_BTN_OPTIONS_KEY]) {
|
|
27
27
|
const options = uiOptions[SUBMIT_BTN_OPTIONS_KEY];
|
|
28
28
|
return { ...DEFAULT_OPTIONS, ...options };
|
|
29
29
|
}
|
package/src/getWidget.tsx
CHANGED
|
@@ -75,7 +75,7 @@ function mergeWidgetOptions<T = any, S extends StrictRJSFSchema = RJSFSchema, F
|
|
|
75
75
|
// cache return value as property of widget for proper react reconciliation
|
|
76
76
|
if (!MergedWidget) {
|
|
77
77
|
// oxlint-disable-next-line typescript/no-deprecated
|
|
78
|
-
const defaultOptions =
|
|
78
|
+
const defaultOptions = AWidget.defaultProps?.options || {};
|
|
79
79
|
MergedWidget = ({ options, ...props }) => <AWidget options={{ ...defaultOptions, ...options }} {...props} />;
|
|
80
80
|
set(AWidget, 'MergedWidget', MergedWidget);
|
|
81
81
|
}
|
package/src/hashForSchema.ts
CHANGED
|
@@ -47,10 +47,12 @@ export function hashObject(object: unknown): string {
|
|
|
47
47
|
|
|
48
48
|
/** Stringifies the schema and returns the hash of the resulting string. Sorts schema fields
|
|
49
49
|
* in consistent order before stringify to prevent different hash ids for the same schema.
|
|
50
|
+
* Symbol-keyed properties (RJSF_REF_KEY, RJSF_REF_CYCLE_KEY, ADDITIONAL_PROPERTY_FLAG) are
|
|
51
|
+
* automatically excluded by JSON.stringify, so no special filtering is needed.
|
|
50
52
|
*
|
|
51
53
|
* @param schema - The schema for which the hash is desired
|
|
52
54
|
* @returns - The string obtained from the hash of the stringified schema
|
|
53
55
|
*/
|
|
54
56
|
export default function hashForSchema<S extends StrictRJSFSchema = RJSFSchema>(schema: S) {
|
|
55
|
-
return
|
|
57
|
+
return hashString(sortedJSONStringify(schema));
|
|
56
58
|
}
|
package/src/index.ts
CHANGED
|
@@ -52,6 +52,7 @@ import isObject from './isObject';
|
|
|
52
52
|
import isRootSchema from './isRootSchema';
|
|
53
53
|
import labelValue from './labelValue';
|
|
54
54
|
import localToUTC from './localToUTC';
|
|
55
|
+
import logUnsupportedDefaultForEnum from './logUnsupportedDefaultForEnum';
|
|
55
56
|
import lookupFromFormContext from './lookupFromFormContext';
|
|
56
57
|
import mergeDefaultsWithFormData from './mergeDefaultsWithFormData';
|
|
57
58
|
import mergeObjects from './mergeObjects';
|
|
@@ -66,6 +67,8 @@ import removeOptionalEmptyObjects from './removeOptionalEmptyObjects';
|
|
|
66
67
|
import replaceStringParameters from './replaceStringParameters';
|
|
67
68
|
import resolveUiSchema from './resolveUiSchema';
|
|
68
69
|
import schemaRequiresTrueValue from './schemaRequiresTrueValue';
|
|
70
|
+
import SelectedOptionDescription from './SelectedOptionDescription';
|
|
71
|
+
import type { SelectedOptionDescriptionProps } from './SelectedOptionDescription';
|
|
69
72
|
import shallowEquals from './shallowEquals';
|
|
70
73
|
import type { ComponentUpdateStrategy } from './shouldRender';
|
|
71
74
|
import shouldRender from './shouldRender';
|
|
@@ -98,6 +101,7 @@ export type {
|
|
|
98
101
|
DateElementProp,
|
|
99
102
|
DateElementProps,
|
|
100
103
|
FileInfoType,
|
|
104
|
+
SelectedOptionDescriptionProps,
|
|
101
105
|
UseAltDateWidgetResult,
|
|
102
106
|
UseFileWidgetPropsResult,
|
|
103
107
|
};
|
|
@@ -154,6 +158,7 @@ export {
|
|
|
154
158
|
isRootSchema,
|
|
155
159
|
labelValue,
|
|
156
160
|
localToUTC,
|
|
161
|
+
logUnsupportedDefaultForEnum,
|
|
157
162
|
lookupFromFormContext,
|
|
158
163
|
mergeDefaultsWithFormData,
|
|
159
164
|
mergeObjects,
|
|
@@ -170,6 +175,7 @@ export {
|
|
|
170
175
|
replaceStringParameters,
|
|
171
176
|
resolveUiSchema,
|
|
172
177
|
schemaRequiresTrueValue,
|
|
178
|
+
SelectedOptionDescription,
|
|
173
179
|
shallowEquals,
|
|
174
180
|
shouldRender,
|
|
175
181
|
shouldRenderOptionalField,
|
package/src/isFixedItems.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import isObject from './isObject';
|
|
2
2
|
import type { RJSFSchema, StrictRJSFSchema } from './types';
|
|
3
3
|
|
|
4
|
-
/** Detects whether the given `schema` contains fixed items. This is the case when `schema.items` is
|
|
5
|
-
*
|
|
4
|
+
/** Detects whether the given `schema` contains fixed items. This is the case when `schema.items` is an array that only
|
|
5
|
+
* contains objects.
|
|
6
6
|
*
|
|
7
7
|
* @param schema - The schema in which to check for fixed items
|
|
8
8
|
* @returns - True if there are fixed items in the schema, false otherwise
|
|
9
9
|
*/
|
|
10
10
|
export default function isFixedItems<S extends StrictRJSFSchema = RJSFSchema>(schema: S) {
|
|
11
|
-
return Array.isArray(schema.items) && schema.items.
|
|
11
|
+
return Array.isArray(schema.items) && schema.items.every((item) => isObject(item));
|
|
12
12
|
}
|
package/src/isRootSchema.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { REF_KEY, RJSF_REF_KEY } from './constants';
|
|
1
|
+
import { REF_KEY } from './constants';
|
|
4
2
|
import deepEquals from './deepEquals';
|
|
5
3
|
import type { FormContextType, Registry, RJSFSchema, StrictRJSFSchema } from './types';
|
|
6
4
|
|
|
@@ -25,7 +23,7 @@ export default function isRootSchema<T = any, S extends StrictRJSFSchema = RJSFS
|
|
|
25
23
|
}
|
|
26
24
|
if (REF_KEY in rootSchema) {
|
|
27
25
|
const resolvedSchema = schemaUtils.retrieveSchema(rootSchema);
|
|
28
|
-
return deepEquals(schemaToCompare,
|
|
26
|
+
return deepEquals(schemaToCompare, resolvedSchema);
|
|
29
27
|
}
|
|
30
28
|
return false;
|
|
31
29
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import enumOptionsIndexForValue from './enumOptionsIndexForValue';
|
|
2
|
+
import type { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types';
|
|
3
|
+
|
|
4
|
+
/** Logs a warning when a single-select enum widget has a schema default that is not one of its enum options.
|
|
5
|
+
*
|
|
6
|
+
* @param id - The field id used in the warning message
|
|
7
|
+
* @param schema - The schema whose default value is checked
|
|
8
|
+
* @param enumOptions - The enum options available to the widget
|
|
9
|
+
* @param multiple - Whether the widget allows multiple selections
|
|
10
|
+
*/
|
|
11
|
+
export default function logUnsupportedDefaultForEnum<S extends StrictRJSFSchema = RJSFSchema>(
|
|
12
|
+
id: string,
|
|
13
|
+
schema: S,
|
|
14
|
+
enumOptions?: EnumOptionsType<S>[],
|
|
15
|
+
multiple = false,
|
|
16
|
+
) {
|
|
17
|
+
if (
|
|
18
|
+
!multiple &&
|
|
19
|
+
Array.isArray(enumOptions) &&
|
|
20
|
+
schema.default !== undefined &&
|
|
21
|
+
enumOptionsIndexForValue<S>(schema.default, enumOptions, multiple) === undefined
|
|
22
|
+
) {
|
|
23
|
+
// oxlint-disable-next-line no-console
|
|
24
|
+
console.error(
|
|
25
|
+
`The schema default value "${schema.default}" is not one of the values in the enum options for "${id}"`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/mergeSchemas.ts
CHANGED
|
@@ -3,7 +3,7 @@ import union from 'lodash/union';
|
|
|
3
3
|
import { REQUIRED_KEY } from './constants';
|
|
4
4
|
import getSchemaType from './getSchemaType';
|
|
5
5
|
import isObject from './isObject';
|
|
6
|
-
import type { GenericObjectType } from './types';
|
|
6
|
+
import type { GenericObjectType, GenericSymbolObjectType } from './types';
|
|
7
7
|
|
|
8
8
|
/** Recursively merge deeply nested schemas. The difference between `mergeSchemas` and `mergeObjects` is that
|
|
9
9
|
* `mergeSchemas` only concats arrays for values under the 'required' keyword, and when it does, it doesn't include
|
|
@@ -15,7 +15,7 @@ import type { GenericObjectType } from './types';
|
|
|
15
15
|
*/
|
|
16
16
|
export default function mergeSchemas(obj1: GenericObjectType, obj2: GenericObjectType) {
|
|
17
17
|
const acc = { ...obj1 }; // Prevent mutation of source object.
|
|
18
|
-
|
|
18
|
+
const result = Object.keys(obj2).reduce((accumulator, key) => {
|
|
19
19
|
const left = obj1 ? obj1[key] : {},
|
|
20
20
|
right = obj2[key];
|
|
21
21
|
if (obj1 && key in obj1 && isObject(right)) {
|
|
@@ -35,4 +35,11 @@ export default function mergeSchemas(obj1: GenericObjectType, obj2: GenericObjec
|
|
|
35
35
|
}
|
|
36
36
|
return accumulator;
|
|
37
37
|
}, acc);
|
|
38
|
+
// Copy own Symbol-keyed properties from obj2 (Object.keys skips them).
|
|
39
|
+
for (const sym of Object.getOwnPropertySymbols(obj2)) {
|
|
40
|
+
// To avoid issues with symbols, we cast them to GenericSymbolObjectType. If we make the mergeSchema function take
|
|
41
|
+
// in GenericSymbolObjectType rather than GenericObjectType, it causes a bunch of type errors downstream
|
|
42
|
+
(result as GenericSymbolObjectType)[sym] = (obj2 as GenericSymbolObjectType)[sym];
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
38
45
|
}
|
package/src/optionsList.ts
CHANGED
|
@@ -79,26 +79,24 @@ export default function optionsList<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
79
79
|
const { optionsSchemaSelector = selectorField } = getUiOptions<T, S, F>(uiSchema);
|
|
80
80
|
selectorField = optionsSchemaSelector;
|
|
81
81
|
}
|
|
82
|
-
return (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
})
|
|
103
|
-
);
|
|
82
|
+
return altSchemas?.map((aSchemaDef, index) => {
|
|
83
|
+
const { title } = getUiOptions<T, S, F>(altUiSchemas?.[index]);
|
|
84
|
+
const aSchema = aSchemaDef as S;
|
|
85
|
+
let value: EnumOptionsType<S>['value'];
|
|
86
|
+
let label = title;
|
|
87
|
+
if (selectorField) {
|
|
88
|
+
const innerSchema: S = get(aSchema, [PROPERTIES_KEY, selectorField], {}) as S;
|
|
89
|
+
value = get(innerSchema, DEFAULT_KEY, get(innerSchema, CONST_KEY));
|
|
90
|
+
// Use nullish coalescing so that an explicitly empty string title is preserved
|
|
91
|
+
label = label ?? innerSchema?.title ?? aSchema.title ?? String(value);
|
|
92
|
+
} else {
|
|
93
|
+
value = toConstant(aSchema);
|
|
94
|
+
label = label ?? aSchema.title ?? String(value);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
schema: aSchema,
|
|
98
|
+
label,
|
|
99
|
+
value,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
104
102
|
}
|
package/src/resolveUiSchema.ts
CHANGED
|
@@ -3,7 +3,15 @@ import isEmpty from 'lodash/isEmpty';
|
|
|
3
3
|
import { ANY_OF_KEY, ONE_OF_KEY, REF_KEY, RJSF_REF_KEY } from './constants';
|
|
4
4
|
import findSchemaDefinition from './findSchemaDefinition';
|
|
5
5
|
import mergeObjects from './mergeObjects';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
FormContextType,
|
|
8
|
+
GenericObjectType,
|
|
9
|
+
Registry,
|
|
10
|
+
RJSFMarkedSchema,
|
|
11
|
+
RJSFSchema,
|
|
12
|
+
StrictRJSFSchema,
|
|
13
|
+
UiSchema,
|
|
14
|
+
} from './types';
|
|
7
15
|
|
|
8
16
|
/** Resolves the uiSchema for a given schema, considering `ui:definitions` stored in the registry.
|
|
9
17
|
*
|
|
@@ -26,7 +34,7 @@ export default function resolveUiSchema<
|
|
|
26
34
|
S extends StrictRJSFSchema = RJSFSchema,
|
|
27
35
|
F extends FormContextType = any,
|
|
28
36
|
>(schema: S, localUiSchema: UiSchema<T, S, F> | undefined, registry: Registry<T, S, F>): UiSchema<T, S, F> {
|
|
29
|
-
const ref = ((schema as
|
|
37
|
+
const ref = ((schema as RJSFMarkedSchema)[RJSF_REF_KEY] ?? schema[REF_KEY]) as string | undefined;
|
|
30
38
|
const definitions = registry.uiSchemaDefinitions;
|
|
31
39
|
const definitionUiSchema = ref && definitions ? definitions[ref] : undefined;
|
|
32
40
|
|
|
@@ -47,7 +55,7 @@ export default function resolveUiSchema<
|
|
|
47
55
|
// can read dropdown option titles at the parent level.
|
|
48
56
|
if (definitions) {
|
|
49
57
|
let resolvedSchema: S = schema;
|
|
50
|
-
if (ref && schema[REF_KEY] && !(schema as
|
|
58
|
+
if (ref && schema[REF_KEY] && !(schema as RJSFMarkedSchema)[RJSF_REF_KEY]) {
|
|
51
59
|
try {
|
|
52
60
|
resolvedSchema = findSchemaDefinition<S>(ref, registry.rootSchema);
|
|
53
61
|
} catch (e) {
|
|
@@ -66,7 +74,7 @@ export default function resolveUiSchema<
|
|
|
66
74
|
let hasExpanded = false;
|
|
67
75
|
for (let i = 0; i < schemaOptions.length; i++) {
|
|
68
76
|
const option = schemaOptions[i] as GenericObjectType | undefined;
|
|
69
|
-
const optionRef = (option?.[RJSF_REF_KEY] ?? option?.[REF_KEY]) as string | undefined;
|
|
77
|
+
const optionRef = ((option as RJSFMarkedSchema)?.[RJSF_REF_KEY] ?? option?.[REF_KEY]) as string | undefined;
|
|
70
78
|
if (optionRef && optionRef in definitions) {
|
|
71
79
|
const optionUiSchema = (uiSchemaArray[i] || {}) as GenericObjectType;
|
|
72
80
|
uiSchemaArray[i] = mergeObjects(definitions[optionRef] as GenericObjectType, optionUiSchema) as UiSchema<
|
|
@@ -232,16 +232,18 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
232
232
|
rootSchema = {} as S,
|
|
233
233
|
includeUndefinedValues = false,
|
|
234
234
|
_recurseList = [],
|
|
235
|
-
experimental_defaultFormStateBehavior
|
|
236
|
-
experimental_customMergeAllOf
|
|
235
|
+
experimental_defaultFormStateBehavior,
|
|
236
|
+
experimental_customMergeAllOf,
|
|
237
237
|
required,
|
|
238
238
|
shouldMergeDefaultsIntoFormData = false,
|
|
239
239
|
initialDefaultsGenerated,
|
|
240
240
|
} = computeDefaultsProps;
|
|
241
241
|
let formData: T = (isObject(rawFormData) ? rawFormData : {}) as T;
|
|
242
242
|
const schema: S = isObject(rawSchema) ? rawSchema : ({} as S);
|
|
243
|
-
// Compute the defaults recursively: give highest priority to deepest nodes.
|
|
243
|
+
// Compute the defaults recursively: give highest priority to deepest nodes unless nestedDefaultsPrecedence is ancestorWins.
|
|
244
244
|
let defaults: T | T[] | undefined = parentDefaults;
|
|
245
|
+
const preferParentDefaults =
|
|
246
|
+
defaults && experimental_defaultFormStateBehavior?.nestedDefaultsPrecedence === 'ancestorWins';
|
|
245
247
|
// If we get a new schema, then we need to recompute defaults again for the new schema found.
|
|
246
248
|
let schemaToCompute: S | null = null;
|
|
247
249
|
let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior;
|
|
@@ -259,12 +261,25 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
259
261
|
!schema[ONE_OF_KEY] &&
|
|
260
262
|
!schema[REF_KEY]
|
|
261
263
|
) {
|
|
262
|
-
// For object defaults,
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
264
|
+
// For object defaults, merge defaults by precedence setting.
|
|
265
|
+
// Skip this for anyOf/oneOf/$ref schemas - they need special handling.
|
|
266
|
+
if (preferParentDefaults) {
|
|
267
|
+
// Use schema.default as the base and only override values that are defined in parent defaults.
|
|
268
|
+
defaults = mergeObjects(schema.default as GenericObjectType, defaults) as T;
|
|
269
|
+
} else {
|
|
270
|
+
// Only override parent defaults that are defined in schema.default.
|
|
271
|
+
defaults = mergeObjects(defaults, schema.default as GenericObjectType) as T;
|
|
272
|
+
}
|
|
273
|
+
} else if (
|
|
274
|
+
DEFAULT_KEY in schema &&
|
|
275
|
+
!preferParentDefaults &&
|
|
276
|
+
!schema[ANY_OF_KEY] &&
|
|
277
|
+
!schema[ONE_OF_KEY] &&
|
|
278
|
+
!schema[REF_KEY]
|
|
279
|
+
) {
|
|
280
|
+
// If the schema has a default value and parentDefaults does not have precedence
|
|
281
|
+
// And if the schema does not have anyOf or oneOf (since we need to merge the defaults with the formData)
|
|
282
|
+
// Then we should use it as the default.
|
|
268
283
|
defaults = schema.default as unknown as T;
|
|
269
284
|
} else if (REF_KEY in schema) {
|
|
270
285
|
const refName = schema[REF_KEY];
|
|
@@ -305,7 +320,9 @@ export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema
|
|
|
305
320
|
experimental_customMergeAllOf,
|
|
306
321
|
);
|
|
307
322
|
[schemaToCompute] = resolvedSchema; // pick the first element from resolve dependencies
|
|
308
|
-
} else if (isFixedItems(schema)) {
|
|
323
|
+
} else if (isFixedItems(schema) && !preferParentDefaults) {
|
|
324
|
+
// If the schema contains fixed items and parentDefaults does not have precedence
|
|
325
|
+
// Then construct defaults from defaults of array items.
|
|
309
326
|
defaults = (schema.items! as S[]).map((itemSchema: S, idx: number) =>
|
|
310
327
|
computeDefaults<T, S>(validator, itemSchema, {
|
|
311
328
|
rootSchema,
|
|
@@ -443,19 +460,44 @@ export function ensureFormDataMatchingSchema<
|
|
|
443
460
|
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior,
|
|
444
461
|
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
445
462
|
): T | T[] | undefined {
|
|
463
|
+
const shouldRetrieveAllOf =
|
|
464
|
+
experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema;
|
|
465
|
+
const schemaToMatch = shouldRetrieveAllOf
|
|
466
|
+
? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf)
|
|
467
|
+
: schema;
|
|
446
468
|
const isSelectField =
|
|
447
|
-
!isConstant<S>(
|
|
469
|
+
!isConstant<S>(schemaToMatch) &&
|
|
470
|
+
isSelect<T, S, F>(validator, schemaToMatch, rootSchema, experimental_customMergeAllOf);
|
|
448
471
|
let validFormData: T | T[] | undefined = formData;
|
|
449
472
|
if (isSelectField) {
|
|
450
|
-
const getOptionsList = optionsList<T, S, F>(
|
|
473
|
+
const getOptionsList = optionsList<T, S, F>(schemaToMatch);
|
|
451
474
|
const isValid = getOptionsList?.some((option) => deepEquals(option.value, formData));
|
|
452
475
|
validFormData = isValid ? formData : undefined;
|
|
453
476
|
}
|
|
454
477
|
|
|
455
478
|
// Override the formData with the const if the constAsDefaults is set to always
|
|
456
|
-
const constTakesPrecedence =
|
|
479
|
+
const constTakesPrecedence =
|
|
480
|
+
schemaToMatch[CONST_KEY] && experimental_defaultFormStateBehavior?.constAsDefaults === 'always';
|
|
457
481
|
if (constTakesPrecedence) {
|
|
458
|
-
validFormData =
|
|
482
|
+
validFormData = schemaToMatch.const as T;
|
|
483
|
+
} else if (isObject(validFormData) && isObject(schemaToMatch.properties)) {
|
|
484
|
+
validFormData = Object.keys(schemaToMatch.properties).reduce(
|
|
485
|
+
(acc: GenericObjectType, key: string) => {
|
|
486
|
+
const propertySchema: S = get(schemaToMatch, [PROPERTIES_KEY, key], {}) as S;
|
|
487
|
+
if (key in acc && (shouldRetrieveAllOf || (isObject(propertySchema) && ALL_OF_KEY in propertySchema))) {
|
|
488
|
+
acc[key] = ensureFormDataMatchingSchema<T, S, F>(
|
|
489
|
+
validator,
|
|
490
|
+
propertySchema,
|
|
491
|
+
rootSchema,
|
|
492
|
+
get(acc, key),
|
|
493
|
+
experimental_defaultFormStateBehavior,
|
|
494
|
+
experimental_customMergeAllOf,
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
return acc;
|
|
498
|
+
},
|
|
499
|
+
{ ...(validFormData as GenericObjectType) },
|
|
500
|
+
) as T;
|
|
459
501
|
}
|
|
460
502
|
|
|
461
503
|
return validFormData;
|
|
@@ -477,8 +519,8 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
477
519
|
rootSchema = {} as S,
|
|
478
520
|
includeUndefinedValues = false,
|
|
479
521
|
_recurseList = [],
|
|
480
|
-
experimental_defaultFormStateBehavior
|
|
481
|
-
experimental_customMergeAllOf
|
|
522
|
+
experimental_defaultFormStateBehavior,
|
|
523
|
+
experimental_customMergeAllOf,
|
|
482
524
|
required,
|
|
483
525
|
shouldMergeDefaultsIntoFormData,
|
|
484
526
|
initialDefaultsGenerated,
|
|
@@ -605,8 +647,8 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
605
647
|
rawFormData,
|
|
606
648
|
rootSchema = {} as S,
|
|
607
649
|
_recurseList = [],
|
|
608
|
-
experimental_defaultFormStateBehavior
|
|
609
|
-
experimental_customMergeAllOf
|
|
650
|
+
experimental_defaultFormStateBehavior,
|
|
651
|
+
experimental_customMergeAllOf,
|
|
610
652
|
required,
|
|
611
653
|
requiredAsRoot = false,
|
|
612
654
|
shouldMergeDefaultsIntoFormData,
|
|
@@ -674,7 +716,16 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
674
716
|
}
|
|
675
717
|
}
|
|
676
718
|
|
|
719
|
+
const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
|
|
720
|
+
|
|
677
721
|
if (neverPopulate) {
|
|
722
|
+
if (shouldMergeDefaultsIntoFormData) {
|
|
723
|
+
if (schema.minItems && schema.minItems > defaultsLength) {
|
|
724
|
+
const fillerEntries = Array.from({ length: schema.minItems - defaultsLength }, () => null) as T[];
|
|
725
|
+
return (defaults ?? []).concat(fillerEntries);
|
|
726
|
+
}
|
|
727
|
+
return defaults;
|
|
728
|
+
}
|
|
678
729
|
return defaults ?? emptyDefault;
|
|
679
730
|
}
|
|
680
731
|
if (ignoreMinItemsFlagSet && !required) {
|
|
@@ -684,7 +735,6 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
684
735
|
}
|
|
685
736
|
|
|
686
737
|
let arrayDefault: T[] | undefined;
|
|
687
|
-
const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
|
|
688
738
|
if (
|
|
689
739
|
!schema.minItems ||
|
|
690
740
|
isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) ||
|
|
@@ -733,8 +783,8 @@ export function getDefaultBasedOnSchemaType<
|
|
|
733
783
|
validator: ValidatorType<T, S, F>,
|
|
734
784
|
rawSchema: S,
|
|
735
785
|
computeDefaultsProps: ComputeDefaultsProps<T, S> = {},
|
|
736
|
-
defaults?: T | T[]
|
|
737
|
-
): T | T[] |
|
|
786
|
+
defaults?: T | T[],
|
|
787
|
+
): T | T[] | undefined {
|
|
738
788
|
switch (getSchemaType<S>(rawSchema)) {
|
|
739
789
|
// We need to recurse for object schema inner default values.
|
|
740
790
|
case 'object': {
|
|
@@ -808,9 +858,17 @@ export default function getDefaultFormState<
|
|
|
808
858
|
if (isObject(formData) || Array.isArray(formData)) {
|
|
809
859
|
const { mergeDefaultsIntoFormData } = experimental_defaultFormStateBehavior || {};
|
|
810
860
|
const defaultSupercedesUndefined = mergeDefaultsIntoFormData === 'useDefaultIfFormDataUndefined';
|
|
861
|
+
const matchingFormData = ensureFormDataMatchingSchema<T, S, F>(
|
|
862
|
+
validator,
|
|
863
|
+
schema,
|
|
864
|
+
rootSchema ?? schema,
|
|
865
|
+
formData,
|
|
866
|
+
experimental_defaultFormStateBehavior,
|
|
867
|
+
experimental_customMergeAllOf,
|
|
868
|
+
);
|
|
811
869
|
const result = mergeDefaultsWithFormData<T | T[]>(
|
|
812
870
|
defaults,
|
|
813
|
-
|
|
871
|
+
matchingFormData,
|
|
814
872
|
true, // set to true to add any additional default array entries.
|
|
815
873
|
defaultSupercedesUndefined,
|
|
816
874
|
true, // set to true to override formData with defaults if they exist.
|