diff --git a/CHANGELOG.md b/CHANGELOG.md index 6698dfbc56..195c0bf6cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ it according to semantic versioning. For example, if your PR adds a breaking cha should change the heading of the (upcoming) version to include a major version bump. --> +# 6.0.0-beta.12 + +## @rjsf/utils + +- Allow form value overrides with defaults [#4625](https://github.com/rjsf-team/react-jsonschema-form/pull/4625 # 6.0.0-beta.11 diff --git a/packages/docs/docs/api-reference/form-props.md b/packages/docs/docs/api-reference/form-props.md index 7b18d0dc21..bb3733a884 100644 --- a/packages/docs/docs/api-reference/form-props.md +++ b/packages/docs/docs/api-reference/form-props.md @@ -269,6 +269,9 @@ NOTE: If there is a default for a field and the `formData` is unspecified, the d | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `useFormDataIfPresent` | Legacy behavior - Do not merge defaults if there is a value for a field in `formData` even if that value is explicitly set to `undefined` | | `useDefaultIfFormDataUndefined` | If the value of a field within the `formData` is `undefined`, then use the default value instead | +| `useDefaultAlways` | Always use the default value instead of form data | + +| ## experimental_customMergeAllOf diff --git a/packages/docs/docs/api-reference/utility-functions.md b/packages/docs/docs/api-reference/utility-functions.md index db06a5b305..2021fbd4f0 100644 --- a/packages/docs/docs/api-reference/utility-functions.md +++ b/packages/docs/docs/api-reference/utility-functions.md @@ -661,7 +661,7 @@ Merges the `defaults` object of type `T` into the `formData` of type `T` When merging defaults and form data, we want to merge in this specific way: - objects are deeply merged -- arrays are merged in such a way that: +- arrays are either replaced (when `defaultSupercedes` is true) or merged in such a way that: - when the array is set in form data, only array entries set in form data are deeply merged; additional entries from the defaults are ignored unless `mergeExtraArrayDefaults` is true, in which case the extras are appended onto the end of the form data - when the array is not set in form data, the default is copied over - scalars are overwritten/set by form data @@ -672,6 +672,7 @@ When merging defaults and form data, we want to merge in this specific way: - [formData]: T | undefined - The form data into which the defaults will be merged - [mergeExtraArrayDefaults=false]: boolean - If true, any additional default array entries are appended onto the formData - [defaultSupercedesUndefined=false]: boolean - If true, an explicit undefined value will be overwritten by the default value +- [defaultSupercedes=false]: boolean - If true, a value will be overwritten by the default value #### Returns diff --git a/packages/playground/src/components/Header.tsx b/packages/playground/src/components/Header.tsx index 350b5393dc..205be03af7 100644 --- a/packages/playground/src/components/Header.tsx +++ b/packages/playground/src/components/Header.tsx @@ -201,6 +201,11 @@ const liveSettingsSelectSchema: RJSFSchema = { title: 'Use default for undefined field value', enum: ['useDefaultIfFormDataUndefined'], }, + { + type: 'string', + title: 'Always use default for field value', + enum: ['useDefaultAlways'], + }, ], }, }, diff --git a/packages/utils/src/mergeDefaultsWithFormData.ts b/packages/utils/src/mergeDefaultsWithFormData.ts index d208589355..f60a851a9d 100644 --- a/packages/utils/src/mergeDefaultsWithFormData.ts +++ b/packages/utils/src/mergeDefaultsWithFormData.ts @@ -1,7 +1,7 @@ import get from 'lodash/get'; import isObject from './isObject'; -import { GenericObjectType } from '../src'; +import { GenericObjectType, OverrideFormDataStrategy } from '../src'; import isNil from 'lodash/isNil'; /** Merges the `defaults` object of type `T` into the `formData` of type `T` @@ -20,10 +20,7 @@ import isNil from 'lodash/isNil'; * @param [formData] - The form data into which the defaults will be merged * @param [mergeExtraArrayDefaults=false] - If true, any additional default array entries are appended onto the formData * @param [defaultSupercedesUndefined=false] - If true, an explicit undefined value will be overwritten by the default value - * @param [overrideFormDataWithDefaults=false] - If true, the default value will overwrite the form data value. If the value - * doesn't exist in the default, we take it from formData and in the case where the value is set to undefined in formData. - * This is useful when we have already merged formData with defaults and want to add an additional field from formData - * that does not exist in defaults. + * @param [overrideFormDataWithDefaultsStrategy=OverrideFormDataStrategy.noop] - Strategy for merging defaults and form data * @returns - The resulting merged form data with defaults */ export default function mergeDefaultsWithFormData( @@ -31,14 +28,16 @@ export default function mergeDefaultsWithFormData( formData?: T, mergeExtraArrayDefaults = false, defaultSupercedesUndefined = false, - overrideFormDataWithDefaults = false, + overrideFormDataWithDefaultsStrategy: OverrideFormDataStrategy = OverrideFormDataStrategy.noop, ): T | undefined { if (Array.isArray(formData)) { const defaultsArray = Array.isArray(defaults) ? defaults : []; - // If overrideFormDataWithDefaults is true, we want to override the formData with the defaults - const overrideArray = overrideFormDataWithDefaults ? defaultsArray : formData; - const overrideOppositeArray = overrideFormDataWithDefaults ? formData : defaultsArray; + // If overrideFormDataWithDefaultsStrategy is not noop, we want to override the formData with the defaults + const overrideArray = + overrideFormDataWithDefaultsStrategy !== OverrideFormDataStrategy.noop ? defaultsArray : formData; + const overrideOppositeArray = + overrideFormDataWithDefaultsStrategy !== OverrideFormDataStrategy.noop ? formData : defaultsArray; const mapped = overrideArray.map((value, idx) => { // We want to explicitly make sure that the value is NOT undefined since null, 0 and empty space are valid values @@ -48,22 +47,27 @@ export default function mergeDefaultsWithFormData( formData[idx], mergeExtraArrayDefaults, defaultSupercedesUndefined, - overrideFormDataWithDefaults, + overrideFormDataWithDefaultsStrategy, ); } return value; }); // Merge any extra defaults when mergeExtraArrayDefaults is true - // Or when overrideFormDataWithDefaults is true and the default array is shorter than the formData array - if ((mergeExtraArrayDefaults || overrideFormDataWithDefaults) && mapped.length < overrideOppositeArray.length) { + // Or when overrideFormDataWithDefaults is 'merge' and the default array is shorter than the formData array + if ( + (mergeExtraArrayDefaults || overrideFormDataWithDefaultsStrategy === OverrideFormDataStrategy.merge) && + mapped.length < overrideOppositeArray.length + ) { mapped.push(...overrideOppositeArray.slice(mapped.length)); } return mapped as unknown as T; } if (isObject(formData)) { + const iterationSource = + overrideFormDataWithDefaultsStrategy === OverrideFormDataStrategy.replace ? (defaults ?? {}) : formData; const acc: { [key in keyof T]: any } = Object.assign({}, defaults); // Prevent mutation of source object. - return Object.keys(formData as GenericObjectType).reduce((acc, key) => { + return Object.keys(iterationSource as GenericObjectType).reduce((acc, key) => { const keyValue = get(formData, key); const keyExistsInDefaults = isObject(defaults) && key in (defaults as GenericObjectType); const keyExistsInFormData = key in (formData as GenericObjectType); @@ -73,22 +77,31 @@ export default function mergeDefaultsWithFormData( 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, - }; + if ( + keyDefaultIsObject && + keyHasFormDataObject && + !defaultValueIsNestedObject && + overrideFormDataWithDefaultsStrategy !== OverrideFormDataStrategy.replace + ) { + acc[key as keyof T] = { ...keyDefault, ...keyValue }; return acc; } + // overrideFormDataWithDefaultsStrategy can be 'merge' only when the key value exists in defaults + // Or if the key value doesn't exist in formData + const keyOverrideDefaultStrategy = + overrideFormDataWithDefaultsStrategy === OverrideFormDataStrategy.replace + ? OverrideFormDataStrategy.replace + : keyExistsInDefaults || !keyExistsInFormData + ? overrideFormDataWithDefaultsStrategy + : OverrideFormDataStrategy.noop; + acc[key as keyof T] = mergeDefaultsWithFormData( - get(defaults, key) ?? {}, + keyDefault, keyValue, mergeExtraArrayDefaults, defaultSupercedesUndefined, - // overrideFormDataWithDefaults can be true only when the key value exists in defaults - // Or if the key value doesn't exist in formData - overrideFormDataWithDefaults && (keyExistsInDefaults || !keyExistsInFormData), + keyOverrideDefaultStrategy, ); return acc; }, acc); @@ -103,10 +116,10 @@ export default function mergeDefaultsWithFormData( if ( (defaultSupercedesUndefined && ((!isNil(defaults) && isNil(formData)) || (typeof formData === 'number' && isNaN(formData)))) || - (overrideFormDataWithDefaults && !isNil(formData)) + (overrideFormDataWithDefaultsStrategy === OverrideFormDataStrategy.merge && !isNil(formData)) ) { return defaults; } - return formData; + return overrideFormDataWithDefaultsStrategy === OverrideFormDataStrategy.replace ? defaults : formData; } diff --git a/packages/utils/src/schema/getDefaultFormState.ts b/packages/utils/src/schema/getDefaultFormState.ts index 2c0e9e30c2..07c8d474ad 100644 --- a/packages/utils/src/schema/getDefaultFormState.ts +++ b/packages/utils/src/schema/getDefaultFormState.ts @@ -26,6 +26,7 @@ import { Experimental_DefaultFormStateBehavior, FormContextType, GenericObjectType, + OverrideFormDataStrategy, RJSFSchema, StrictRJSFSchema, ValidatorType, @@ -369,6 +370,9 @@ export function computeDefaults; } + +/** Strategy for merging defaults with existing form data */ +export enum OverrideFormDataStrategy { + /** No merge or override applied */ + noop, + /** If the value doesn't exist in the default, we take it from formData and in the case where the value is set to undefined in formData. + * This is useful when we have already merged formData with defaults and want to add an additional field from formData that does not exist in defaults */ + merge, + /** Replace form data with defined default */ + replace, +} diff --git a/packages/utils/test/mergeDefaultsWithFormData.test.ts b/packages/utils/test/mergeDefaultsWithFormData.test.ts index 0e1d6d2ddc..9cc3694169 100644 --- a/packages/utils/test/mergeDefaultsWithFormData.test.ts +++ b/packages/utils/test/mergeDefaultsWithFormData.test.ts @@ -1,4 +1,4 @@ -import { mergeDefaultsWithFormData } from '../src'; +import { mergeDefaultsWithFormData, OverrideFormDataStrategy } from '../src'; describe('mergeDefaultsWithFormData()', () => { it('shouldn`t mutate the provided objects', () => { @@ -141,13 +141,17 @@ describe('mergeDefaultsWithFormData()', () => { expect(mergeDefaultsWithFormData(obj1, obj2)?.a).toBeInstanceOf(File); }); - describe('test with overrideFormDataWithDefaults set to true', () => { + describe('test with overrideFormDataWithDefaults set to `merge`', () => { it('should return data in formData when no defaults', () => { - expect(mergeDefaultsWithFormData(undefined, [2], undefined, undefined, true)).toEqual([2]); + expect(mergeDefaultsWithFormData(undefined, [2], undefined, undefined, OverrideFormDataStrategy.merge)).toEqual([ + 2, + ]); }); it('should return formData when formData is undefined', () => { - expect(mergeDefaultsWithFormData({}, undefined, undefined, undefined, true)).toEqual(undefined); + expect(mergeDefaultsWithFormData({}, undefined, undefined, undefined, OverrideFormDataStrategy.merge)).toEqual( + undefined, + ); }); it('should deeply merge and return formData when formData is undefined and defaultSupercedesUndefined false', () => { @@ -170,7 +174,7 @@ describe('mergeDefaultsWithFormData()', () => { }, undefined, undefined, - true, + OverrideFormDataStrategy.merge, ), ).toEqual({ arrayWithDefaults: [null], @@ -183,45 +187,64 @@ describe('mergeDefaultsWithFormData()', () => { }); it('should return default when formData is undefined and defaultSupercedesUndefined true', () => { - expect(mergeDefaultsWithFormData({}, undefined, undefined, true, true)).toEqual({}); + expect(mergeDefaultsWithFormData({}, undefined, undefined, true, OverrideFormDataStrategy.merge)).toEqual({}); }); it('should return default when formData is null and defaultSupercedesUndefined true', () => { - expect(mergeDefaultsWithFormData({}, null, undefined, true, true)).toEqual({}); + expect(mergeDefaultsWithFormData({}, null, undefined, true, OverrideFormDataStrategy.merge)).toEqual({}); }); it('should merge two one-level deep objects', () => { - expect(mergeDefaultsWithFormData({ a: 1 }, { b: 2 }, undefined, undefined, true)).toEqual({ + expect( + mergeDefaultsWithFormData({ a: 1 }, { b: 2 }, undefined, undefined, OverrideFormDataStrategy.merge), + ).toEqual({ a: 1, b: 2, }); }); it('should override the first object with the values from the second', () => { - expect(mergeDefaultsWithFormData({ a: 1 }, { a: 2 }, undefined, undefined, true)).toEqual({ a: 1 }); + expect( + mergeDefaultsWithFormData({ a: 1 }, { a: 2 }, undefined, undefined, OverrideFormDataStrategy.merge), + ).toEqual({ a: 1 }); }); it('should override non-existing values of the first object with the values from the second', () => { expect( - mergeDefaultsWithFormData({ a: { b: undefined } }, { a: { b: { c: 1 } } }, undefined, undefined, true), + mergeDefaultsWithFormData( + { a: { b: undefined } }, + { a: { b: { c: 1 } } }, + undefined, + undefined, + OverrideFormDataStrategy.merge, + ), ).toEqual({ a: { b: { c: 1 } }, }); }); it('should merge arrays using entries from second', () => { - expect(mergeDefaultsWithFormData([1, 2, 3], [4, 5], undefined, undefined, true)).toEqual([1, 2, 3]); + expect( + mergeDefaultsWithFormData([1, 2, 3], [4, 5], undefined, undefined, OverrideFormDataStrategy.merge), + ).toEqual([1, 2, 3]); }); it('should merge arrays using entries from second and extra from the first', () => { - expect(mergeDefaultsWithFormData([1, 2], [4, 5, 6], undefined, undefined, true)).toEqual([1, 2, 6]); + expect( + mergeDefaultsWithFormData([1, 2], [4, 5, 6], undefined, undefined, OverrideFormDataStrategy.merge), + ).toEqual([1, 2, 6]); }); it('should deeply merge arrays with overlapping entries', () => { - expect(mergeDefaultsWithFormData([{ a: 1 }], [{ b: 2 }, { c: 3 }], undefined, undefined, true)).toEqual([ - { a: 1, b: 2 }, - { c: 3 }, - ]); + expect( + mergeDefaultsWithFormData( + [{ a: 1 }], + [{ b: 2 }, { c: 3 }], + undefined, + undefined, + OverrideFormDataStrategy.merge, + ), + ).toEqual([{ a: 1, b: 2 }, { c: 3 }]); }); it('should recursively merge deeply nested objects', () => { @@ -256,7 +279,9 @@ describe('mergeDefaultsWithFormData()', () => { }, c: 2, }; - expect(mergeDefaultsWithFormData(obj1, obj2, undefined, undefined, true)).toEqual(expected); + expect(mergeDefaultsWithFormData(obj1, obj2, undefined, undefined, OverrideFormDataStrategy.merge)).toEqual( + expected, + ); }); it('should recursively merge deeply nested objects, including extra array data', () => { @@ -293,7 +318,9 @@ describe('mergeDefaultsWithFormData()', () => { c: 2, d: 4, }; - expect(mergeDefaultsWithFormData(obj1, obj2, undefined, undefined, true)).toEqual(expected); + expect(mergeDefaultsWithFormData(obj1, obj2, undefined, undefined, OverrideFormDataStrategy.merge)).toEqual( + expected, + ); }); it('should recursively merge File objects', () => { @@ -307,4 +334,156 @@ describe('mergeDefaultsWithFormData()', () => { expect(mergeDefaultsWithFormData(obj1, obj2)?.a).toBeInstanceOf(File); }); }); + + describe('test with overrideFormDataWithDefaults set to `noop`', () => { + it('should return form data when no defaults', () => { + expect(mergeDefaultsWithFormData(undefined, [2], undefined, undefined, OverrideFormDataStrategy.noop)).toEqual([ + 2, + ]); + }); + + it('should return default when formData is undefined and defaultSupercedesUndefined true', () => { + expect(mergeDefaultsWithFormData({}, undefined, undefined, true, OverrideFormDataStrategy.noop)).toEqual({}); + }); + + it('should return default when formData is null and defaultSupercedesUndefined true', () => { + expect(mergeDefaultsWithFormData({}, null, undefined, true, OverrideFormDataStrategy.noop)).toEqual({}); + }); + + it('should use values from second object', () => { + expect( + mergeDefaultsWithFormData({ a: 1 }, { a: 2 }, undefined, undefined, OverrideFormDataStrategy.noop), + ).toEqual({ a: 2 }); + }); + }); + + describe('test with overrideFormDataWithDefaults set to `replace`', () => { + it('should return empty array even when no defaults', () => { + expect(mergeDefaultsWithFormData(undefined, [2], undefined, undefined, OverrideFormDataStrategy.replace)).toEqual( + [], + ); + }); + + it('should return default when formData is undefined', () => { + expect(mergeDefaultsWithFormData({}, undefined, undefined, undefined, OverrideFormDataStrategy.replace)).toEqual( + {}, + ); + }); + + it('should return default when formData is undefined and defaultSupercedesUndefined true', () => { + expect(mergeDefaultsWithFormData({}, undefined, undefined, true, OverrideFormDataStrategy.replace)).toEqual({}); + }); + + it('should return default when formData is null and defaultSupercedesUndefined true', () => { + expect(mergeDefaultsWithFormData({}, null, undefined, true, OverrideFormDataStrategy.replace)).toEqual({}); + }); + + it('should return empty object when formData is defined and default is not', () => { + expect( + mergeDefaultsWithFormData(undefined, { a: 1 }, undefined, undefined, OverrideFormDataStrategy.replace), + ).toEqual({}); + }); + + it('should not merge two one-level deep objects', () => { + expect( + mergeDefaultsWithFormData({ a: 1 }, { b: 2 }, undefined, undefined, OverrideFormDataStrategy.replace), + ).toEqual({ + a: 1, + }); + }); + + it('should not override the first object with the values from the second', () => { + expect( + mergeDefaultsWithFormData({ a: 1 }, { a: 2 }, undefined, undefined, OverrideFormDataStrategy.replace), + ).toEqual({ a: 1 }); + }); + + it('should not return undefined from defaults', () => { + expect( + mergeDefaultsWithFormData( + { a: { b: undefined } }, + { a: { b: { c: 1 } } }, + undefined, + undefined, + OverrideFormDataStrategy.replace, + ), + ).toEqual({ + a: { b: {} }, + }); + }); + + it('should not merge arrays using entries from second', () => { + expect( + mergeDefaultsWithFormData([1, 2, 3], [4, 5], undefined, undefined, OverrideFormDataStrategy.replace), + ).toEqual([1, 2, 3]); + }); + + it('should not deeply merge arrays with overlapping entries', () => { + expect( + mergeDefaultsWithFormData( + [{ a: 1 }], + [{ b: 2 }, { c: 3 }], + undefined, + undefined, + OverrideFormDataStrategy.replace, + ), + ).toEqual([{ a: 1 }]); + }); + + it('should replace deep objects', () => { + const defaultObj = { + a: 1, + b: { + c: 3, + d: [0, 1, 2], + e: { f: { g: 1 } }, + h: [{ i: 1 }, { i: 2 }], + }, + c: 2, + }; + const formObj = { + a: 1, + b: { + d: [3], + e: { f: { h: 2 } }, + g: 1, + h: [{ i: 3 }, { i: 4 }, { i: 5 }], + }, + c: 3, + d: 4, + }; + expect( + mergeDefaultsWithFormData(defaultObj, formObj, undefined, undefined, OverrideFormDataStrategy.replace), + ).toEqual({ + a: 1, + b: { + c: 3, + d: [0, 1, 2], + e: { f: { g: 1 } }, + h: [{ i: 1 }, { i: 2 }], + }, + c: 2, + }); + }); + + it('should replace arrays', () => { + expect( + mergeDefaultsWithFormData([1, 2], [4, 5, 6], undefined, undefined, OverrideFormDataStrategy.replace), + ).toEqual([1, 2]); + }); + + it('should replace simple object', () => { + expect( + mergeDefaultsWithFormData( + { a: { b: 1 } }, + { a: { b: 2 } }, + undefined, + undefined, + OverrideFormDataStrategy.replace, + ), + ).toEqual({ + a: { b: 1 }, + }); + }); + }); }); diff --git a/packages/utils/test/schema/getDefaultFormStateTest.ts b/packages/utils/test/schema/getDefaultFormStateTest.ts index 9a22142b90..66df0c598b 100644 --- a/packages/utils/test/schema/getDefaultFormStateTest.ts +++ b/packages/utils/test/schema/getDefaultFormStateTest.ts @@ -1340,6 +1340,65 @@ export default function getDefaultFormStateTest(testValidator: TestValidatorType }); }); }); + + describe('mergeDefaultsIntoFormData set to "useDefaultAlways"', () => { + const experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = { + mergeDefaultsIntoFormData: 'useDefaultAlways', + }; + + test('getDefaultFormState', () => { + expect( + getDefaultFormState( + testValidator, + schema, + rawFormData, + schema, + undefined, + experimental_defaultFormStateBehavior, + ), + ).toEqual({ + animal: 'Fish', + food: 'worms', + water: null, + }); + }); + + test('computeDefaults', () => { + expect( + computeDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + experimental_defaultFormStateBehavior, + shouldMergeDefaultsIntoFormData, + }), + ).toEqual({ + food: 'worms', + water: 'sea', + }); + }); + + test('getDefaultBasedOnSchemaType', () => { + expect( + getDefaultBasedOnSchemaType(testValidator, schema, { + rootSchema: schema, + rawFormData, + shouldMergeDefaultsIntoFormData, + experimental_defaultFormStateBehavior, + }), + ).toEqual({}); + }); + + test('getObjectDefaults', () => { + expect( + getObjectDefaults(testValidator, schema, { + rootSchema: schema, + rawFormData, + shouldMergeDefaultsIntoFormData, + experimental_defaultFormStateBehavior, + }), + ).toEqual({}); + }); + }); }); describe('an object with a valid formData and enum property with default value', () => { @@ -4993,5 +5052,122 @@ export default function getDefaultFormStateTest(testValidator: TestValidatorType }), ).toEqual({ stringArray: [undefined], numberArray: [] }); }); + describe('defaults with oneOf and useDefaultAlways', () => { + const schema: RJSFSchema = { + type: 'object', + properties: { + country: { + type: 'string', + enum: ['UK', 'France', 'Spain'], + }, + rating: { + type: 'string', + }, + }, + dependencies: { + country: { + oneOf: [ + { + properties: { + country: { + enum: ['UK'], + }, + city: { + type: 'array', + uniqueItems: true, + items: { + type: 'string', + enum: ['London', 'Birmingham', 'Liverpool'], + }, + default: ['London'], + }, + }, + }, + { + properties: { + country: { + enum: ['France'], + }, + city: { + type: 'array', + uniqueItems: true, + items: { + type: 'string', + enum: ['Paris', 'Marseille', 'Lyon'], + }, + default: ['Paris'], + }, + }, + }, + { + properties: { + country: { + enum: ['Spain'], + }, + city: { + type: 'array', + uniqueItems: true, + items: { + type: 'string', + enum: ['Madrid', 'Barcelona', 'Valencia'], + }, + default: ['Madrid'], + }, + }, + }, + ], + }, + }, + }; + afterEach(() => { + // Reset the testValidator + if (typeof testValidator.reset === 'function') { + testValidator?.reset(); + } + }); + it('should populate empty defaults for oneOf + dependencies', () => { + testValidator.setReturnValues({ + isValid: [ + true, // First oneOf... first === first + false, // Second oneOf... second !== first + false, + ], + }); + expect( + getDefaultFormState(testValidator, schema, { country: 'UK', rating: '6.0' }, undefined, undefined, { + mergeDefaultsIntoFormData: 'useDefaultAlways', + }), + ).toEqual({ + country: 'UK', + rating: '6.0', + city: ['London'], + }); + }); + it('should replace defaults for oneOf + dependencies', () => { + testValidator.setReturnValues({ + isValid: [ + false, // First oneOf... first === first + true, // Second oneOf... second !== first + false, + ], + }); + expect( + getDefaultFormState( + testValidator, + schema, + { country: 'France', city: ['London'], rating: '6.0' }, + undefined, + undefined, + { + mergeDefaultsIntoFormData: 'useDefaultAlways', + }, + ), + ).toEqual({ + country: 'France', + rating: '6.0', + city: ['Paris'], + }); + }); + }); }); }