@rjsf/utils 6.6.2 → 6.7.1
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/README.md +0 -4
- package/dist/index.cjs +246 -112
- package/dist/index.cjs.map +4 -4
- package/dist/utils.esm.js +246 -112
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +277 -140
- package/lib/SelectedOptionDescription.d.ts +4 -0
- package/lib/SelectedOptionDescription.js +24 -0
- package/lib/SelectedOptionDescription.js.map +1 -0
- package/lib/asNumber.d.ts +6 -5
- package/lib/asNumber.js +14 -9
- package/lib/asNumber.js.map +1 -1
- 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/getDecimalSeparator.d.ts +8 -0
- package/lib/getDecimalSeparator.js +23 -0
- package/lib/getDecimalSeparator.js.map +1 -0
- 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 +6 -2
- package/lib/index.js +5 -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 +53 -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/asNumber.ts +18 -9
- package/src/constants.ts +10 -7
- package/src/createSchemaUtils.ts +1 -1
- package/src/enums.ts +4 -0
- package/src/getDecimalSeparator.ts +20 -0
- package/src/getSubmitButtonOptions.ts +1 -1
- package/src/getWidget.tsx +1 -1
- package/src/hashForSchema.ts +3 -1
- package/src/index.ts +8 -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 +79 -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/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.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines the locale-specific decimal separator.
|
|
3
|
+
* It uses the provided locale or the first locale in navigator.languages (if available) and falls back to "en".
|
|
4
|
+
*
|
|
5
|
+
* @param languages - Optional array of locales or a single locale string
|
|
6
|
+
* @returns The decimal separator character (typically '.' or ',').
|
|
7
|
+
*/
|
|
8
|
+
export default function getDecimalSeparator(languages?: string | string[]): string {
|
|
9
|
+
const locales = languages || (typeof navigator !== 'undefined' ? navigator.languages : undefined);
|
|
10
|
+
if (locales) {
|
|
11
|
+
try {
|
|
12
|
+
const formatter = new Intl.NumberFormat(locales);
|
|
13
|
+
const parts = formatter.formatToParts(1.1);
|
|
14
|
+
return parts.find((part) => part.type === 'decimal')?.value || '.';
|
|
15
|
+
} catch (e) {
|
|
16
|
+
// Fallback
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return '.';
|
|
20
|
+
}
|
|
@@ -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
|
@@ -20,6 +20,7 @@ import findSchemaDefinition from './findSchemaDefinition';
|
|
|
20
20
|
import getChangedFields from './getChangedFields';
|
|
21
21
|
import type { DateElementFormat, DateElementProp } from './getDateElementProps';
|
|
22
22
|
import getDateElementProps from './getDateElementProps';
|
|
23
|
+
import getDecimalSeparator from './getDecimalSeparator';
|
|
23
24
|
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema';
|
|
24
25
|
import getInputProps from './getInputProps';
|
|
25
26
|
import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator';
|
|
@@ -52,6 +53,7 @@ import isObject from './isObject';
|
|
|
52
53
|
import isRootSchema from './isRootSchema';
|
|
53
54
|
import labelValue from './labelValue';
|
|
54
55
|
import localToUTC from './localToUTC';
|
|
56
|
+
import logUnsupportedDefaultForEnum from './logUnsupportedDefaultForEnum';
|
|
55
57
|
import lookupFromFormContext from './lookupFromFormContext';
|
|
56
58
|
import mergeDefaultsWithFormData from './mergeDefaultsWithFormData';
|
|
57
59
|
import mergeObjects from './mergeObjects';
|
|
@@ -66,6 +68,8 @@ import removeOptionalEmptyObjects from './removeOptionalEmptyObjects';
|
|
|
66
68
|
import replaceStringParameters from './replaceStringParameters';
|
|
67
69
|
import resolveUiSchema from './resolveUiSchema';
|
|
68
70
|
import schemaRequiresTrueValue from './schemaRequiresTrueValue';
|
|
71
|
+
import SelectedOptionDescription from './SelectedOptionDescription';
|
|
72
|
+
import type { SelectedOptionDescriptionProps } from './SelectedOptionDescription';
|
|
69
73
|
import shallowEquals from './shallowEquals';
|
|
70
74
|
import type { ComponentUpdateStrategy } from './shouldRender';
|
|
71
75
|
import shouldRender from './shouldRender';
|
|
@@ -98,6 +102,7 @@ export type {
|
|
|
98
102
|
DateElementProp,
|
|
99
103
|
DateElementProps,
|
|
100
104
|
FileInfoType,
|
|
105
|
+
SelectedOptionDescriptionProps,
|
|
101
106
|
UseAltDateWidgetResult,
|
|
102
107
|
UseFileWidgetPropsResult,
|
|
103
108
|
};
|
|
@@ -130,6 +135,7 @@ export {
|
|
|
130
135
|
findSchemaDefinition,
|
|
131
136
|
getChangedFields,
|
|
132
137
|
getDateElementProps,
|
|
138
|
+
getDecimalSeparator,
|
|
133
139
|
getDiscriminatorFieldFromSchema,
|
|
134
140
|
getInputProps,
|
|
135
141
|
getOptionMatchingSimpleDiscriminator,
|
|
@@ -154,6 +160,7 @@ export {
|
|
|
154
160
|
isRootSchema,
|
|
155
161
|
labelValue,
|
|
156
162
|
localToUTC,
|
|
163
|
+
logUnsupportedDefaultForEnum,
|
|
157
164
|
lookupFromFormContext,
|
|
158
165
|
mergeDefaultsWithFormData,
|
|
159
166
|
mergeObjects,
|
|
@@ -170,6 +177,7 @@ export {
|
|
|
170
177
|
replaceStringParameters,
|
|
171
178
|
resolveUiSchema,
|
|
172
179
|
schemaRequiresTrueValue,
|
|
180
|
+
SelectedOptionDescription,
|
|
173
181
|
shallowEquals,
|
|
174
182
|
shouldRender,
|
|
175
183
|
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,15 @@ 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 && !required) {
|
|
723
|
+
// Optional arrays with no existing data should be omitted entirely rather than defaulted to `[]`.
|
|
724
|
+
// Required arrays still fall through to `defaults ?? emptyDefault` below so that `[]` is surfaced,
|
|
725
|
+
// letting the validator report `minItems` violations instead of a missing-required-property error.
|
|
726
|
+
return defaults;
|
|
727
|
+
}
|
|
678
728
|
return defaults ?? emptyDefault;
|
|
679
729
|
}
|
|
680
730
|
if (ignoreMinItemsFlagSet && !required) {
|
|
@@ -684,7 +734,6 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
684
734
|
}
|
|
685
735
|
|
|
686
736
|
let arrayDefault: T[] | undefined;
|
|
687
|
-
const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
|
|
688
737
|
if (
|
|
689
738
|
!schema.minItems ||
|
|
690
739
|
isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) ||
|
|
@@ -733,8 +782,8 @@ export function getDefaultBasedOnSchemaType<
|
|
|
733
782
|
validator: ValidatorType<T, S, F>,
|
|
734
783
|
rawSchema: S,
|
|
735
784
|
computeDefaultsProps: ComputeDefaultsProps<T, S> = {},
|
|
736
|
-
defaults?: T | T[]
|
|
737
|
-
): T | T[] |
|
|
785
|
+
defaults?: T | T[],
|
|
786
|
+
): T | T[] | undefined {
|
|
738
787
|
switch (getSchemaType<S>(rawSchema)) {
|
|
739
788
|
// We need to recurse for object schema inner default values.
|
|
740
789
|
case 'object': {
|
|
@@ -808,9 +857,17 @@ export default function getDefaultFormState<
|
|
|
808
857
|
if (isObject(formData) || Array.isArray(formData)) {
|
|
809
858
|
const { mergeDefaultsIntoFormData } = experimental_defaultFormStateBehavior || {};
|
|
810
859
|
const defaultSupercedesUndefined = mergeDefaultsIntoFormData === 'useDefaultIfFormDataUndefined';
|
|
860
|
+
const matchingFormData = ensureFormDataMatchingSchema<T, S, F>(
|
|
861
|
+
validator,
|
|
862
|
+
schema,
|
|
863
|
+
rootSchema ?? schema,
|
|
864
|
+
formData,
|
|
865
|
+
experimental_defaultFormStateBehavior,
|
|
866
|
+
experimental_customMergeAllOf,
|
|
867
|
+
);
|
|
811
868
|
const result = mergeDefaultsWithFormData<T | T[]>(
|
|
812
869
|
defaults,
|
|
813
|
-
|
|
870
|
+
matchingFormData,
|
|
814
871
|
true, // set to true to add any additional default array entries.
|
|
815
872
|
defaultSupercedesUndefined,
|
|
816
873
|
true, // set to true to override formData with defaults if they exist.
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import get from 'lodash/get';
|
|
2
|
-
|
|
3
1
|
import { ADDITIONAL_PROPERTY_FLAG, UI_FIELD_KEY, UI_WIDGET_KEY } from '../constants';
|
|
4
2
|
import getSchemaType from '../getSchemaType';
|
|
5
3
|
import getUiOptions from '../getUiOptions';
|
|
@@ -7,6 +5,7 @@ import isCustomWidget from '../isCustomWidget';
|
|
|
7
5
|
import type {
|
|
8
6
|
FormContextType,
|
|
9
7
|
GlobalUISchemaOptions,
|
|
8
|
+
RJSFMarkedSchema,
|
|
10
9
|
RJSFSchema,
|
|
11
10
|
StrictRJSFSchema,
|
|
12
11
|
UiSchema,
|
|
@@ -44,7 +43,7 @@ export default function getDisplayLabel<
|
|
|
44
43
|
let displayLabel = Boolean(label);
|
|
45
44
|
if (displayLabel) {
|
|
46
45
|
const schemaType = getSchemaType<S>(schema);
|
|
47
|
-
const addedByAdditionalProperty =
|
|
46
|
+
const addedByAdditionalProperty = Boolean((schema as RJSFMarkedSchema)[ADDITIONAL_PROPERTY_FLAG]);
|
|
48
47
|
|
|
49
48
|
if (schemaType === 'array') {
|
|
50
49
|
displayLabel =
|
|
@@ -60,7 +59,7 @@ export default function getDisplayLabel<
|
|
|
60
59
|
if (schemaType === 'boolean' && uiSchema && !uiSchema[UI_WIDGET_KEY]) {
|
|
61
60
|
displayLabel = false;
|
|
62
61
|
}
|
|
63
|
-
if (uiSchema
|
|
62
|
+
if (uiSchema?.[UI_FIELD_KEY]) {
|
|
64
63
|
displayLabel = false;
|
|
65
64
|
}
|
|
66
65
|
}
|
|
@@ -448,6 +448,17 @@ export default function omitExtraData<
|
|
|
448
448
|
}
|
|
449
449
|
if (allOf) {
|
|
450
450
|
localSchema = doMergeAllOf<S>(localSchema, experimental_customMergeAllOf);
|
|
451
|
+
// Schemas whose allOf entries contain if/then/else keywords may not fully merge: the merger
|
|
452
|
+
// can only hoist one if/then/else triple to the parent level, so additional entries stay in
|
|
453
|
+
// allOf. Process any that remain so their conditional properties are not silently dropped.
|
|
454
|
+
// See: https://github.com/rjsf-team/react-jsonschema-form/issues/5142
|
|
455
|
+
const remainingAllOf = localSchema.allOf;
|
|
456
|
+
if (remainingAllOf) {
|
|
457
|
+
for (let i = 0; i < remainingAllOf.length; i++) {
|
|
458
|
+
// oxlint-disable-next-line no-param-reassign
|
|
459
|
+
target = omit(remainingAllOf[i] as S | boolean, source, target);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
451
462
|
}
|
|
452
463
|
|
|
453
464
|
let filtered = handleAnyOf(localSchema, source, handleOneOf(localSchema.oneOf, localSchema, source, target));
|