diff --git a/CHANGELOG.md b/CHANGELOG.md index 3804fc6b0c..679e75eb20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ should change the heading of the (upcoming) version to include a major version b ## @rjsf/utils - Fixed issue where oneOf radio button could not be modified when defaults were set, fixing [#4634](https://github.com/rjsf-team/react-jsonschema-form/issues/4634) +- Fix default value for object properties such as enums that are rendered in select, radio inputs, etc. # 6.0.0-beta.10 diff --git a/packages/utils/src/mergeDefaultsWithFormData.ts b/packages/utils/src/mergeDefaultsWithFormData.ts index 11664db5e7..d208589355 100644 --- a/packages/utils/src/mergeDefaultsWithFormData.ts +++ b/packages/utils/src/mergeDefaultsWithFormData.ts @@ -67,8 +67,22 @@ export default function mergeDefaultsWithFormData( const keyValue = get(formData, key); const keyExistsInDefaults = isObject(defaults) && key in (defaults as GenericObjectType); const keyExistsInFormData = key in (formData as GenericObjectType); + const keyDefault = get(defaults, key) ?? {}; + const defaultValueIsNestedObject = keyExistsInDefaults && Object.entries(keyDefault).some(([, v]) => isObject(v)); + + const keyDefaultIsObject = keyExistsInDefaults && isObject(get(defaults, key)); + const keyHasFormDataObject = keyExistsInFormData && isObject(keyValue); + + if (keyDefaultIsObject && keyHasFormDataObject && !defaultValueIsNestedObject) { + acc[key as keyof T] = { + ...get(defaults, key), + ...keyValue, + }; + return acc; + } + acc[key as keyof T] = mergeDefaultsWithFormData( - defaults ? get(defaults, key) : {}, + get(defaults, key) ?? {}, keyValue, mergeExtraArrayDefaults, defaultSupercedesUndefined, diff --git a/packages/utils/src/schema/getDefaultFormState.ts b/packages/utils/src/schema/getDefaultFormState.ts index 649463be6e..2c0e9e30c2 100644 --- a/packages/utils/src/schema/getDefaultFormState.ts +++ b/packages/utils/src/schema/getDefaultFormState.ts @@ -728,6 +728,13 @@ export default function getDefaultFormState< shouldMergeDefaultsIntoFormData: true, }); + if (schema.type !== 'object' && isObject(schema.default)) { + return { + ...defaults, + ...formData, + } as T; + } + // If the formData is an object or an array, add additional properties from formData and override formData with // defaults since the defaults are already merged with formData. if (isObject(formData) || Array.isArray(formData)) { diff --git a/packages/utils/test/schema/getDefaultFormStateTest.ts b/packages/utils/test/schema/getDefaultFormStateTest.ts index 1c5b4c5694..9a22142b90 100644 --- a/packages/utils/test/schema/getDefaultFormStateTest.ts +++ b/packages/utils/test/schema/getDefaultFormStateTest.ts @@ -1342,6 +1342,28 @@ export default function getDefaultFormStateTest(testValidator: TestValidatorType }); }); + describe('an object with a valid formData and enum property with default value', () => { + test('getDefaultFormState', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + test: { + type: 'string', + enum: [ + { label: 'a', value: 'a' }, + { label: 'b', value: 'b' }, + ], + default: { label: 'a', value: 'a' }, + }, + }, + }; + + expect(getDefaultFormState(testValidator, schema, { test: { label: 'b', value: 'b' } })).toEqual({ + test: { label: 'b', value: 'b' }, + }); + }); + }); + describe('oneOf with const values', () => { const schema: RJSFSchema = { type: 'object', @@ -1970,6 +1992,22 @@ export default function getDefaultFormStateTest(testValidator: TestValidatorType expect(ensureFormDataMatchingSchema(testValidator, schema, schema, 'a')).toEqual('a'); }); + + it('Test schema with valid formData with an enum and its default value', () => { + schema = { + type: 'string', + enum: [ + { label: 'a', value: 'a' }, + { label: 'b', value: 'b' }, + ], + default: { label: 'a', value: 'a' }, + }; + + expect(getDefaultFormState(testValidator, schema, { label: 'b', value: 'b' })).toEqual({ + label: 'b', + value: 'b', + }); + }); }); describe('AJV $data reference in const property in schema should not be treated as default/const value', () => { let schema: RJSFSchema;