@rjsf/core 6.4.2 → 6.5.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/dist/core.umd.js +98 -36
- package/dist/index.cjs +98 -36
- package/dist/index.cjs.map +3 -3
- package/dist/index.esm.js +110 -42
- package/dist/index.esm.js.map +3 -3
- package/lib/components/Form.d.ts +6 -0
- package/lib/components/Form.d.ts.map +1 -1
- package/lib/components/Form.js +29 -13
- package/lib/components/fields/ObjectField.d.ts.map +1 -1
- package/lib/components/fields/ObjectField.js +29 -5
- package/lib/components/templates/WrapIfAdditionalTemplate.d.ts.map +1 -1
- package/lib/components/templates/WrapIfAdditionalTemplate.js +1 -1
- package/lib/components/widgets/CheckboxesWidget.d.ts +1 -1
- package/lib/components/widgets/CheckboxesWidget.d.ts.map +1 -1
- package/lib/components/widgets/CheckboxesWidget.js +7 -5
- package/lib/components/widgets/RadioWidget.d.ts.map +1 -1
- package/lib/components/widgets/RadioWidget.js +5 -4
- package/lib/components/widgets/SelectWidget.d.ts.map +1 -1
- package/lib/components/widgets/SelectWidget.js +11 -10
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/components/Form.tsx +58 -14
- package/src/components/fields/ObjectField.tsx +30 -5
- package/src/components/templates/WrapIfAdditionalTemplate.tsx +1 -0
- package/src/components/widgets/CheckboxesWidget.tsx +12 -7
- package/src/components/widgets/RadioWidget.tsx +9 -6
- package/src/components/widgets/SelectWidget.tsx +14 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rjsf/core",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"description": "A simple React component capable of building HTML forms out of a JSON schema.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"compileReplacer": "tsc -p tsconfig.replacer.json && move-file lodashReplacer.js lodashReplacer.cjs",
|
|
@@ -70,20 +70,20 @@
|
|
|
70
70
|
"react": ">=18"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"lodash": "^4.
|
|
74
|
-
"lodash-es": "^4.
|
|
73
|
+
"lodash": "^4.18.1",
|
|
74
|
+
"lodash-es": "^4.18.1",
|
|
75
75
|
"markdown-to-jsx": "^8.0.0",
|
|
76
76
|
"prop-types": "^15.8.1"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@rjsf/snapshot-tests": "6.
|
|
80
|
-
"@rjsf/utils": "6.
|
|
81
|
-
"@rjsf/validator-ajv8": "6.
|
|
79
|
+
"@rjsf/snapshot-tests": "6.5.0",
|
|
80
|
+
"@rjsf/utils": "6.5.0",
|
|
81
|
+
"@rjsf/validator-ajv8": "6.5.0",
|
|
82
82
|
"@testing-library/jest-dom": "^6.9.1",
|
|
83
83
|
"@testing-library/react": "^16.3.2",
|
|
84
84
|
"@testing-library/user-event": "^14.6.1",
|
|
85
85
|
"@types/react-portal": "^4.0.7",
|
|
86
|
-
"ajv": "^8.
|
|
86
|
+
"ajv": "^8.18.0",
|
|
87
87
|
"atob": "^2.1.2",
|
|
88
88
|
"eslint": "^8.57.1",
|
|
89
89
|
"jsdom": "^27.0.1",
|
package/src/components/Form.tsx
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
ErrorSchema,
|
|
7
7
|
ErrorSchemaBuilder,
|
|
8
8
|
ErrorTransformer,
|
|
9
|
-
expandUiSchemaDefinitions,
|
|
10
9
|
FieldPathId,
|
|
11
10
|
FieldPathList,
|
|
12
11
|
FormContextType,
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
RegistryWidgetsType,
|
|
23
22
|
RJSFSchema,
|
|
24
23
|
RJSFValidationError,
|
|
24
|
+
removeOptionalEmptyObjects,
|
|
25
25
|
SchemaUtilsType,
|
|
26
26
|
shouldRender,
|
|
27
27
|
SUBMIT_BTN_OPTIONS_KEY,
|
|
@@ -208,6 +208,12 @@ export interface FormProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F e
|
|
|
208
208
|
* called. Set to `false` by default.
|
|
209
209
|
*/
|
|
210
210
|
omitExtraData?: boolean;
|
|
211
|
+
/** If set to true, optional object properties whose fields are all empty (undefined, null, or empty string)
|
|
212
|
+
* will be automatically removed from formData. This prevents the scenario where interacting with fields inside
|
|
213
|
+
* an optional object "activates" it permanently, making the form unsubmittable when the optional object has
|
|
214
|
+
* required inner fields. This works independently of `omitExtraData`. Set to `false` by default.
|
|
215
|
+
*/
|
|
216
|
+
removeEmptyOptionalObjects?: boolean;
|
|
211
217
|
/** When this prop is set to `top` or 'bottom', a list of errors (or the custom error list defined in the `ErrorList`) will also
|
|
212
218
|
* show. When set to false, only inline input validation errors will be shown. Set to `top` by default
|
|
213
219
|
*/
|
|
@@ -663,11 +669,6 @@ export default class Form<
|
|
|
663
669
|
const newRegistry = this.getRegistry(props, rootSchema, schemaUtils);
|
|
664
670
|
const registry = deepEquals(state.registry, newRegistry) ? state.registry : newRegistry;
|
|
665
671
|
|
|
666
|
-
// Pre-expand ui:definitions into the uiSchema structure (must happen after registry is created)
|
|
667
|
-
const expandedUiSchema: UiSchema<T, S, F> = registry.uiSchemaDefinitions
|
|
668
|
-
? expandUiSchemaDefinitions<T, S, F>(rootSchema, uiSchema, registry)
|
|
669
|
-
: uiSchema;
|
|
670
|
-
|
|
671
672
|
// Only compute a new `fieldPathId` when the `idPrefix` is different than the existing fieldPathId's ID_KEY
|
|
672
673
|
const fieldPathId =
|
|
673
674
|
state.fieldPathId && state.fieldPathId?.[ID_KEY] === registry.globalFormOptions.idPrefix
|
|
@@ -676,7 +677,7 @@ export default class Form<
|
|
|
676
677
|
const nextState: FormState<T, S, F> = {
|
|
677
678
|
schemaUtils,
|
|
678
679
|
schema: rootSchema,
|
|
679
|
-
uiSchema
|
|
680
|
+
uiSchema,
|
|
680
681
|
fieldPathId,
|
|
681
682
|
formData,
|
|
682
683
|
edit,
|
|
@@ -892,7 +893,8 @@ export default class Form<
|
|
|
892
893
|
this._isProcessingUserChange = true;
|
|
893
894
|
const { newValue, path, id } = this.pendingChanges[0];
|
|
894
895
|
const { newErrorSchema } = this.pendingChanges[0];
|
|
895
|
-
const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange } =
|
|
896
|
+
const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange, removeEmptyOptionalObjects } =
|
|
897
|
+
this.props;
|
|
896
898
|
const { formData: oldFormData, schemaUtils, schema, fieldPathId, schemaValidationErrorSchema, errors } = this.state;
|
|
897
899
|
let { customErrors, errorSchema: originalErrorSchema } = this.state;
|
|
898
900
|
const rootPathId = fieldPathId.path[0] || '';
|
|
@@ -937,6 +939,19 @@ export default class Form<
|
|
|
937
939
|
};
|
|
938
940
|
}
|
|
939
941
|
|
|
942
|
+
if (removeEmptyOptionalObjects) {
|
|
943
|
+
newFormData = removeOptionalEmptyObjects(
|
|
944
|
+
schemaUtils.getValidator(),
|
|
945
|
+
schema,
|
|
946
|
+
schemaUtils.getRootSchema(),
|
|
947
|
+
newFormData,
|
|
948
|
+
) as T;
|
|
949
|
+
state = {
|
|
950
|
+
...state,
|
|
951
|
+
formData: newFormData,
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
|
|
940
955
|
if (newErrorSchema) {
|
|
941
956
|
// First check to see if there is an existing validation error on this path...
|
|
942
957
|
// @ts-expect-error TS2590, because getting from the error schema is confusing TS
|
|
@@ -1051,19 +1066,28 @@ export default class Form<
|
|
|
1051
1066
|
* @param data - The data associated with the field that was blurred
|
|
1052
1067
|
*/
|
|
1053
1068
|
onBlur = (id: string, data: any) => {
|
|
1054
|
-
const { onBlur, omitExtraData, liveOmit, liveValidate } = this.props;
|
|
1069
|
+
const { onBlur, omitExtraData, liveOmit, liveValidate, removeEmptyOptionalObjects } = this.props;
|
|
1055
1070
|
if (onBlur) {
|
|
1056
1071
|
onBlur(id, data);
|
|
1057
1072
|
}
|
|
1058
1073
|
if ((omitExtraData === true && liveOmit === 'onBlur') || liveValidate === 'onBlur') {
|
|
1059
1074
|
const { onChange, extraErrors } = this.props;
|
|
1060
|
-
const { formData } = this.state;
|
|
1075
|
+
const { formData, schemaUtils, schema } = this.state;
|
|
1061
1076
|
let newFormData: T | undefined = formData;
|
|
1062
1077
|
let state: Partial<FormState<T, S, F>> = { formData: newFormData };
|
|
1063
1078
|
if (omitExtraData === true && liveOmit === 'onBlur') {
|
|
1064
1079
|
newFormData = this.omitExtraData(formData);
|
|
1065
1080
|
state = { formData: newFormData };
|
|
1066
1081
|
}
|
|
1082
|
+
if (removeEmptyOptionalObjects) {
|
|
1083
|
+
newFormData = removeOptionalEmptyObjects(
|
|
1084
|
+
schemaUtils.getValidator(),
|
|
1085
|
+
schema,
|
|
1086
|
+
schemaUtils.getRootSchema(),
|
|
1087
|
+
newFormData,
|
|
1088
|
+
) as T;
|
|
1089
|
+
state = { ...state, formData: newFormData };
|
|
1090
|
+
}
|
|
1067
1091
|
if (liveValidate === 'onBlur') {
|
|
1068
1092
|
const { schema, schemaUtils, errorSchema, customErrors, retrievedSchema } = this.state;
|
|
1069
1093
|
const liveValidation = this.liveValidate(
|
|
@@ -1121,13 +1145,23 @@ export default class Form<
|
|
|
1121
1145
|
}
|
|
1122
1146
|
|
|
1123
1147
|
event.persist();
|
|
1124
|
-
const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props;
|
|
1148
|
+
const { omitExtraData, extraErrors, noValidate, onSubmit, removeEmptyOptionalObjects } = this.props;
|
|
1125
1149
|
let { formData: newFormData } = this.state;
|
|
1126
1150
|
|
|
1127
1151
|
if (omitExtraData === true) {
|
|
1128
1152
|
newFormData = this.omitExtraData(newFormData);
|
|
1129
1153
|
}
|
|
1130
1154
|
|
|
1155
|
+
if (removeEmptyOptionalObjects) {
|
|
1156
|
+
const { schemaUtils, schema } = this.state;
|
|
1157
|
+
newFormData = removeOptionalEmptyObjects(
|
|
1158
|
+
schemaUtils.getValidator(),
|
|
1159
|
+
schema,
|
|
1160
|
+
schemaUtils.getRootSchema(),
|
|
1161
|
+
newFormData,
|
|
1162
|
+
) as T;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1131
1165
|
if (noValidate || this.validateFormWithFormData(newFormData)) {
|
|
1132
1166
|
// There are no errors generated through schema validation.
|
|
1133
1167
|
// Check for user provided errors and update state accordingly.
|
|
@@ -1234,8 +1268,9 @@ export default class Form<
|
|
|
1234
1268
|
const elementId = path.join(idSeparator);
|
|
1235
1269
|
let field = this.formElement.current.elements[elementId];
|
|
1236
1270
|
if (!field) {
|
|
1237
|
-
// if not an exact match, try finding
|
|
1238
|
-
|
|
1271
|
+
// if not an exact match, try finding a focusable element starting with the element id (like radio buttons or checkboxes)
|
|
1272
|
+
// some themes (e.g. shadcn) use button elements instead of native inputs for radio groups
|
|
1273
|
+
field = this.formElement.current.querySelector(`input[id^="${elementId}"], button[id^="${elementId}"]`);
|
|
1239
1274
|
}
|
|
1240
1275
|
if (field && field.length) {
|
|
1241
1276
|
// If we got a list with length > 0
|
|
@@ -1310,11 +1345,20 @@ export default class Form<
|
|
|
1310
1345
|
* @returns - True if the form is valid, false otherwise.
|
|
1311
1346
|
*/
|
|
1312
1347
|
validateForm() {
|
|
1313
|
-
const { omitExtraData } = this.props;
|
|
1348
|
+
const { omitExtraData, removeEmptyOptionalObjects } = this.props;
|
|
1314
1349
|
let { formData: newFormData } = this.state;
|
|
1315
1350
|
if (omitExtraData === true) {
|
|
1316
1351
|
newFormData = this.omitExtraData(newFormData);
|
|
1317
1352
|
}
|
|
1353
|
+
if (removeEmptyOptionalObjects) {
|
|
1354
|
+
const { schemaUtils, schema } = this.state;
|
|
1355
|
+
newFormData = removeOptionalEmptyObjects(
|
|
1356
|
+
schemaUtils.getValidator(),
|
|
1357
|
+
schema,
|
|
1358
|
+
schemaUtils.getRootSchema(),
|
|
1359
|
+
newFormData,
|
|
1360
|
+
) as T;
|
|
1361
|
+
}
|
|
1318
1362
|
return this.validateFormWithFormData(newFormData);
|
|
1319
1363
|
}
|
|
1320
1364
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FocusEvent, useCallback, useState } from 'react';
|
|
1
|
+
import { FocusEvent, useCallback, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ADDITIONAL_PROPERTY_FLAG,
|
|
4
4
|
ANY_OF_KEY,
|
|
@@ -220,11 +220,14 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
220
220
|
} = props;
|
|
221
221
|
const { fields, schemaUtils, translateString, globalUiOptions } = registry;
|
|
222
222
|
const { OptionalDataControlsField } = fields;
|
|
223
|
+
const formDataRef = useRef(formData);
|
|
224
|
+
formDataRef.current = formData;
|
|
223
225
|
const schema: S = schemaUtils.retrieveSchema(rawSchema, formData, true);
|
|
224
226
|
const uiOptions = getUiOptions<T, S, F>(uiSchema, globalUiOptions);
|
|
225
227
|
const { properties: schemaProperties = {} } = schema;
|
|
226
228
|
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
227
229
|
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
230
|
+
const lastRenamedProperty = useRef({ previousKey: '', currentKey: undefined as string | undefined });
|
|
228
231
|
|
|
229
232
|
const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
|
|
230
233
|
const description = uiOptions.description ?? schema.description;
|
|
@@ -292,6 +295,10 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
292
295
|
set(newFormData as GenericObjectType, newKey, newValue);
|
|
293
296
|
}
|
|
294
297
|
|
|
298
|
+
if (lastRenamedProperty.current.previousKey === newKey) {
|
|
299
|
+
lastRenamedProperty.current.currentKey = newKey;
|
|
300
|
+
lastRenamedProperty.current.previousKey = getAvailableKey(newKey, newFormData);
|
|
301
|
+
}
|
|
295
302
|
onChange(newFormData, childFieldPathId.path);
|
|
296
303
|
}, [formData, onChange, registry, childFieldPathId, getAvailableKey, schema]);
|
|
297
304
|
|
|
@@ -305,9 +312,10 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
305
312
|
const handleKeyRename = useCallback(
|
|
306
313
|
(oldKey: string, newKey: string) => {
|
|
307
314
|
if (oldKey !== newKey) {
|
|
308
|
-
const
|
|
315
|
+
const currentFormData = formDataRef.current;
|
|
316
|
+
const actualNewKey = getAvailableKey(newKey, currentFormData);
|
|
309
317
|
const newFormData: GenericObjectType = {
|
|
310
|
-
...(
|
|
318
|
+
...(currentFormData as GenericObjectType),
|
|
311
319
|
};
|
|
312
320
|
const newKeys: GenericObjectType = { [oldKey]: actualNewKey };
|
|
313
321
|
const keyValues = Object.keys(newFormData).map((key) => {
|
|
@@ -316,10 +324,15 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
316
324
|
});
|
|
317
325
|
const renamedObj = Object.assign({}, ...keyValues);
|
|
318
326
|
|
|
327
|
+
formDataRef.current = renamedObj as T;
|
|
328
|
+
if (oldKey !== lastRenamedProperty.current.currentKey) {
|
|
329
|
+
lastRenamedProperty.current.previousKey = oldKey;
|
|
330
|
+
}
|
|
331
|
+
lastRenamedProperty.current.currentKey = actualNewKey;
|
|
319
332
|
onChange(renamedObj, childFieldPathId.path);
|
|
320
333
|
}
|
|
321
334
|
},
|
|
322
|
-
[
|
|
335
|
+
[onChange, childFieldPathId, getAvailableKey],
|
|
323
336
|
);
|
|
324
337
|
|
|
325
338
|
/** Handles the remove click which calls the `onChange` callback with the special ADDITIONAL_PROPERTY_FIELD_REMOVE
|
|
@@ -332,6 +345,18 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
332
345
|
[onChange, childFieldPathId],
|
|
333
346
|
);
|
|
334
347
|
|
|
348
|
+
/** Returns the stable React key for a property. For the most recently renamed
|
|
349
|
+
* additional property, returns the previous key so that React reuses the
|
|
350
|
+
* existing component instance instead of unmounting/remounting it. This
|
|
351
|
+
* preserves DOM focus naturally without manual focus management.
|
|
352
|
+
*/
|
|
353
|
+
const getStableKey = useCallback((property: string) => {
|
|
354
|
+
if (lastRenamedProperty.current.currentKey === property) {
|
|
355
|
+
return lastRenamedProperty.current.previousKey;
|
|
356
|
+
}
|
|
357
|
+
return property;
|
|
358
|
+
}, []);
|
|
359
|
+
|
|
335
360
|
if (!renderOptionalField || hasFormData) {
|
|
336
361
|
try {
|
|
337
362
|
const properties = Object.keys(schemaProperties);
|
|
@@ -365,7 +390,7 @@ export default function ObjectField<T = any, S extends StrictRJSFSchema = RJSFSc
|
|
|
365
390
|
const hidden = getUiOptions<T, S, F>(fieldUiSchema).widget === 'hidden';
|
|
366
391
|
const content = (
|
|
367
392
|
<ObjectFieldProperty<T, S, F>
|
|
368
|
-
key={name}
|
|
393
|
+
key={getStableKey(name)}
|
|
369
394
|
propertyName={name}
|
|
370
395
|
required={isRequired<S>(schema, name)}
|
|
371
396
|
schema={get(schema, [PROPERTIES_KEY, name], {}) as S}
|
|
@@ -68,6 +68,7 @@ export default function WrapIfAdditionalTemplate<
|
|
|
68
68
|
{displayLabel && <Label label={keyLabel} required={required} id={`${id}-key`} />}
|
|
69
69
|
{displayLabel && rawDescription && <div> </div>}
|
|
70
70
|
<input
|
|
71
|
+
key={label}
|
|
71
72
|
className='form-control'
|
|
72
73
|
type='text'
|
|
73
74
|
id={`${id}-key`}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ChangeEvent, FocusEvent, useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ariaDescribedByIds,
|
|
4
|
+
enumOptionValueDecoder,
|
|
5
|
+
enumOptionValueEncoder,
|
|
4
6
|
enumOptionsDeselectValue,
|
|
5
7
|
enumOptionsIsSelected,
|
|
6
8
|
enumOptionsSelectValue,
|
|
7
|
-
|
|
9
|
+
getOptionValueFormat,
|
|
8
10
|
optionId,
|
|
9
11
|
FormContextType,
|
|
10
12
|
WidgetProps,
|
|
@@ -20,7 +22,7 @@ import {
|
|
|
20
22
|
function CheckboxesWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>({
|
|
21
23
|
id,
|
|
22
24
|
disabled,
|
|
23
|
-
options
|
|
25
|
+
options,
|
|
24
26
|
value,
|
|
25
27
|
autofocus = false,
|
|
26
28
|
readonly,
|
|
@@ -29,19 +31,22 @@ function CheckboxesWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
29
31
|
onFocus,
|
|
30
32
|
htmlName,
|
|
31
33
|
}: WidgetProps<T, S, F>) {
|
|
34
|
+
const { inline = false, enumOptions, enumDisabled, emptyValue } = options;
|
|
35
|
+
const optionValueFormat = getOptionValueFormat(options);
|
|
32
36
|
const checkboxesValues = Array.isArray(value) ? value : [value];
|
|
33
37
|
|
|
34
38
|
const handleBlur = useCallback(
|
|
35
39
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
36
|
-
onBlur(id,
|
|
37
|
-
[onBlur, id, enumOptions, emptyValue],
|
|
40
|
+
onBlur(id, enumOptionValueDecoder<S>(target && target.value, enumOptions, optionValueFormat, emptyValue)),
|
|
41
|
+
[onBlur, id, enumOptions, emptyValue, optionValueFormat],
|
|
38
42
|
);
|
|
39
43
|
|
|
40
44
|
const handleFocus = useCallback(
|
|
41
45
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
42
|
-
onFocus(id,
|
|
43
|
-
[onFocus, id, enumOptions, emptyValue],
|
|
46
|
+
onFocus(id, enumOptionValueDecoder<S>(target && target.value, enumOptions, optionValueFormat, emptyValue)),
|
|
47
|
+
[onFocus, id, enumOptions, emptyValue, optionValueFormat],
|
|
44
48
|
);
|
|
49
|
+
|
|
45
50
|
return (
|
|
46
51
|
<div className='checkboxes' id={id}>
|
|
47
52
|
{Array.isArray(enumOptions) &&
|
|
@@ -65,7 +70,7 @@ function CheckboxesWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
|
|
|
65
70
|
id={optionId(id, index)}
|
|
66
71
|
name={htmlName || id}
|
|
67
72
|
checked={checked}
|
|
68
|
-
value={
|
|
73
|
+
value={enumOptionValueEncoder(option.value, index, optionValueFormat)}
|
|
69
74
|
disabled={disabled || itemDisabled || readonly}
|
|
70
75
|
autoFocus={autofocus && index === 0}
|
|
71
76
|
onChange={handleChange}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { FocusEvent, useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ariaDescribedByIds,
|
|
4
|
+
enumOptionValueDecoder,
|
|
5
|
+
enumOptionValueEncoder,
|
|
4
6
|
enumOptionsIsSelected,
|
|
5
|
-
|
|
7
|
+
getOptionValueFormat,
|
|
6
8
|
optionId,
|
|
7
9
|
FormContextType,
|
|
8
10
|
RJSFSchema,
|
|
@@ -29,17 +31,18 @@ function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
|
|
|
29
31
|
htmlName,
|
|
30
32
|
}: WidgetProps<T, S, F>) {
|
|
31
33
|
const { enumOptions, enumDisabled, inline, emptyValue } = options;
|
|
34
|
+
const optionValueFormat = getOptionValueFormat(options);
|
|
32
35
|
|
|
33
36
|
const handleBlur = useCallback(
|
|
34
37
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
35
|
-
onBlur(id,
|
|
36
|
-
[onBlur, enumOptions, emptyValue, id],
|
|
38
|
+
onBlur(id, enumOptionValueDecoder<S>(target && target.value, enumOptions, optionValueFormat, emptyValue)),
|
|
39
|
+
[onBlur, enumOptions, emptyValue, id, optionValueFormat],
|
|
37
40
|
);
|
|
38
41
|
|
|
39
42
|
const handleFocus = useCallback(
|
|
40
43
|
({ target }: FocusEvent<HTMLInputElement>) =>
|
|
41
|
-
onFocus(id,
|
|
42
|
-
[onFocus, enumOptions, emptyValue, id],
|
|
44
|
+
onFocus(id, enumOptionValueDecoder<S>(target && target.value, enumOptions, optionValueFormat, emptyValue)),
|
|
45
|
+
[onFocus, enumOptions, emptyValue, id, optionValueFormat],
|
|
43
46
|
);
|
|
44
47
|
|
|
45
48
|
return (
|
|
@@ -60,7 +63,7 @@ function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends
|
|
|
60
63
|
checked={checked}
|
|
61
64
|
name={htmlName || id}
|
|
62
65
|
required={required}
|
|
63
|
-
value={
|
|
66
|
+
value={enumOptionValueEncoder(option.value, i, optionValueFormat)}
|
|
64
67
|
disabled={disabled || itemDisabled || readonly}
|
|
65
68
|
autoFocus={autofocus && i === 0}
|
|
66
69
|
onChange={handleChange}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ChangeEvent, FocusEvent, SyntheticEvent, useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ariaDescribedByIds,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
enumOptionSelectedValue,
|
|
5
|
+
enumOptionValueDecoder,
|
|
6
|
+
enumOptionValueEncoder,
|
|
7
|
+
getOptionValueFormat,
|
|
6
8
|
FormContextType,
|
|
7
9
|
RJSFSchema,
|
|
8
10
|
StrictRJSFSchema,
|
|
@@ -42,32 +44,33 @@ function SelectWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
|
|
|
42
44
|
}: WidgetProps<T, S, F>) {
|
|
43
45
|
const { enumOptions, enumDisabled, emptyValue: optEmptyVal } = options;
|
|
44
46
|
const emptyValue = multiple ? [] : '';
|
|
47
|
+
const optionValueFormat = getOptionValueFormat(options);
|
|
45
48
|
|
|
46
49
|
const handleFocus = useCallback(
|
|
47
50
|
(event: FocusEvent<HTMLSelectElement>) => {
|
|
48
51
|
const newValue = getValue(event, multiple);
|
|
49
|
-
return onFocus(id,
|
|
52
|
+
return onFocus(id, enumOptionValueDecoder<S>(newValue, enumOptions, optionValueFormat, optEmptyVal));
|
|
50
53
|
},
|
|
51
|
-
[onFocus, id, multiple, enumOptions, optEmptyVal],
|
|
54
|
+
[onFocus, id, multiple, enumOptions, optEmptyVal, optionValueFormat],
|
|
52
55
|
);
|
|
53
56
|
|
|
54
57
|
const handleBlur = useCallback(
|
|
55
58
|
(event: FocusEvent<HTMLSelectElement>) => {
|
|
56
59
|
const newValue = getValue(event, multiple);
|
|
57
|
-
return onBlur(id,
|
|
60
|
+
return onBlur(id, enumOptionValueDecoder<S>(newValue, enumOptions, optionValueFormat, optEmptyVal));
|
|
58
61
|
},
|
|
59
|
-
[onBlur, id, multiple, enumOptions, optEmptyVal],
|
|
62
|
+
[onBlur, id, multiple, enumOptions, optEmptyVal, optionValueFormat],
|
|
60
63
|
);
|
|
61
64
|
|
|
62
65
|
const handleChange = useCallback(
|
|
63
66
|
(event: ChangeEvent<HTMLSelectElement>) => {
|
|
64
67
|
const newValue = getValue(event, multiple);
|
|
65
|
-
return onChange(
|
|
68
|
+
return onChange(enumOptionValueDecoder<S>(newValue, enumOptions, optionValueFormat, optEmptyVal));
|
|
66
69
|
},
|
|
67
|
-
[onChange, multiple, enumOptions, optEmptyVal],
|
|
70
|
+
[onChange, multiple, enumOptions, optEmptyVal, optionValueFormat],
|
|
68
71
|
);
|
|
69
72
|
|
|
70
|
-
const
|
|
73
|
+
const selectValue = enumOptionSelectedValue<S>(value, enumOptions, multiple, optionValueFormat, emptyValue);
|
|
71
74
|
const showPlaceholderOption = !multiple && schema.default === undefined;
|
|
72
75
|
|
|
73
76
|
return (
|
|
@@ -77,7 +80,7 @@ function SelectWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
|
|
|
77
80
|
multiple={multiple}
|
|
78
81
|
role='combobox'
|
|
79
82
|
className='form-control'
|
|
80
|
-
value={
|
|
83
|
+
value={selectValue}
|
|
81
84
|
required={required}
|
|
82
85
|
disabled={disabled || readonly}
|
|
83
86
|
autoFocus={autofocus}
|
|
@@ -91,7 +94,7 @@ function SelectWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extend
|
|
|
91
94
|
enumOptions.map(({ value, label }, i) => {
|
|
92
95
|
const disabled = enumDisabled && enumDisabled.indexOf(value) !== -1;
|
|
93
96
|
return (
|
|
94
|
-
<option key={i} value={
|
|
97
|
+
<option key={i} value={enumOptionValueEncoder(value, i, optionValueFormat)} disabled={disabled}>
|
|
95
98
|
{label}
|
|
96
99
|
</option>
|
|
97
100
|
);
|