Repository: DevExpress/devextreme-react
Pull request #538 (2021-04-26 14:58:07)

Rollback complex options by fields (T988609)


Patch:
diff --git a/packages/devextreme-react/src/core/configuration/tree.ts b/packages/devextreme-react/src/core/configuration/tree.ts
index 9342b44..16129a5 100644
--- a/packages/devextreme-react/src/core/configuration/tree.ts
+++ b/packages/devextreme-react/src/core/configuration/tree.ts
@@ -2,13 +2,13 @@ import { IConfigNode, ITemplate } from './config-node';
 import { mergeNameParts, parseOptionName } from './utils';
 
 interface IConfig {
-  options: Record<string, any>;
+  options: Record<string, unknown>;
   templates: Record<string, ITemplate>;
 }
 
 function buildTemplates(
   node: IConfigNode,
-  optionsAccum: Record<string, any>,
+  optionsAccum: Record<string, unknown>,
   templatesAccum: Record<string, ITemplate>,
 ): void {
   node.templates.forEach(
@@ -28,8 +28,8 @@ function buildNode(
   node: IConfigNode,
   templatesAccum: Record<string, ITemplate>,
   ignoreInitialValues: boolean,
-): Record<string, any> {
-  const result: Record<string, any> = {};
+): Record<string, unknown> {
+  const result: Record<string, unknown> = {};
 
   Object.keys(node.predefinedOptions).forEach((key) => {
     result[key] = node.predefinedOptions[key];
@@ -71,7 +71,7 @@ function buildConfig(root: IConfigNode, ignoreInitialValues: boolean): IConfig {
 }
 
 interface IValueDescriptor {
-  value: any;
+  value: unknown;
   type: ValueType;
 }
 
@@ -86,7 +86,7 @@ function findValueInObject(obj: unknown, path: string[]): undefined | IValueDesc
   if (!key) {
     return {
       value: obj,
-      type: ValueType.Simple,
+      type: obj instanceof Object ? ValueType.Complex : ValueType.Simple,
     };
   }
 
diff --git a/packages/devextreme-react/src/core/options-manager.ts b/packages/devextreme-react/src/core/options-manager.ts
index ad49ae5..d2df91b 100644
--- a/packages/devextreme-react/src/core/options-manager.ts
+++ b/packages/devextreme-react/src/core/options-manager.ts
@@ -113,7 +113,7 @@ class OptionsManager {
     }
 
     const { value, type } = valueDescriptor;
-    if (type === ValueType.Complex) {
+    if (type === ValueType.Complex && value instanceof Object) {
       Object.keys(value).forEach((key) => {
         if (value[key] === (e.value as Record<string, unknown>)[key]) {
           return;


Test patch:
diff --git a/packages/devextreme-react/src/core/__tests__/props-updating.test.tsx b/packages/devextreme-react/src/core/__tests__/props-updating.test.tsx
index 09ca859..cc7e056 100644
--- a/packages/devextreme-react/src/core/__tests__/props-updating.test.tsx
+++ b/packages/devextreme-react/src/core/__tests__/props-updating.test.tsx
@@ -34,6 +34,7 @@ class NestedComponent extends ConfigurationComponent<{
   b?: string;
   c?: string;
   defaultC?: string;
+  complexValue?: Record<string, unknown>;
   value?: number;
   onValueChange?: (value: number) => void;
 }> {
@@ -256,8 +257,9 @@ describe('option control', () => {
     fireOptionChange('complexOption', {});
     jest.runAllTimers();
 
-    expect(Widget.option.mock.calls.length).toBe(1);
-    expect(Widget.option.mock.calls[0]).toEqual(['complexOption', { a: 123, b: 234 }]);
+    expect(Widget.option.mock.calls.length).toBe(2);
+    expect(Widget.option.mock.calls[0]).toEqual(['complexOption.a', 123]);
+    expect(Widget.option.mock.calls[1]).toEqual(['complexOption.b', 234]);
   });
 
   it('rolls back complex option controlled field', () => {
@@ -436,6 +438,20 @@ describe('cfg-component option control', () => {
     expect(Widget.option.mock.calls[0]).toEqual(['items[0].a', 1]);
   });
 
+  it('rolls cfg-component option complex value', () => {
+    render(
+      <ControlledComponent>
+        <NestedComponent complexValue={{ a: 123, b: 234 }} />
+      </ControlledComponent>,
+    );
+
+    fireOptionChange('nestedOption.complexValue', {});
+    jest.runAllTimers();
+    expect(Widget.option.mock.calls.length).toBe(2);
+    expect(Widget.option.mock.calls[0]).toEqual(['nestedOption.complexValue.a', 123]);
+    expect(Widget.option.mock.calls[1]).toEqual(['nestedOption.complexValue.b', 234]);
+  });
+
   it('rolls cfg-component option value if parent object changes another field', () => {
     render(
       <ControlledComponent>