Repository: DevExpress/devextreme-react
Pull request #358 (2019-06-07 11:30:35)

Fix setting widget options when modifying nested components (T742357)


Patch:
diff --git a/example/app.tsx b/example/app.tsx
index 86f828e..7a04b6b 100644
--- a/example/app.tsx
+++ b/example/app.tsx
@@ -22,6 +22,7 @@ import ToolbarExample from "./toolbar-example";
 import ValidationExample from "./validation-example";
 
 import { Button, NumberBox } from "../src";
+import BoxExample from "./box-example";
 
 ReactDOM.render(
   <div>
@@ -73,6 +74,9 @@ ReactDOM.render(
     <Example title="SelectBox example">
       <SelectBoxExample />
     </Example>
+
+    <BoxExample />
+
   </div>,
   document.getElementById("app")
 );
diff --git a/example/box-example.tsx b/example/box-example.tsx
new file mode 100644
index 0000000..393c855
--- /dev/null
+++ b/example/box-example.tsx
@@ -0,0 +1,71 @@
+import * as React from "react";
+
+import Box, { Item } from "../src/box";
+import Button from "../src/button";
+import Example from "./example-block";
+
+const initialState = {
+  items: [
+    {
+      id: 1,
+      name: "Item #1"
+    },
+    {
+      id: 2,
+      name: "Item #2"
+    }
+  ]
+};
+
+class App extends React.Component<any, typeof initialState> {
+
+  constructor(props: any) {
+    super(props);
+
+    this.state = { ...initialState };
+  }
+
+  public render() {
+    return (
+      <Example title="Box example">
+        <Button
+          text="Add"
+          onClick={this.add}
+        />
+        <Button
+          text="Remove"
+          onClick={this.remove}
+        />
+        <Box direction={"row"} width={"100%"} height={75}>
+          {this.renderItems()}
+        </Box>
+      </Example>
+    );
+  }
+
+  private renderItems = () => {
+    return this.state.items.map((item) => (
+      <Item key={item.id} ratio={1}>
+        {item.name}
+      </Item>
+    ));
+  }
+
+  private add = () => {
+    const items = [...this.state.items];
+    const id = items.length ? items[items.length - 1].id + 1 : 1;
+    items.push({
+      id,
+      name: `Item #${id}`
+    });
+
+    this.setState({ items });
+  }
+
+  private remove = () => {
+    const items = this.state.items.slice(0, this.state.items.length - 1);
+    this.setState({ items });
+  }
+}
+
+export default App;
diff --git a/package.json b/package.json
index 6c63139..535320f 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,9 @@
-{
+﻿{
   "author": {
     "name": "Developer Express Inc."
   },
   "name": "devextreme-react",
-  "version": "19.1.3",
+  "version": "19.1.4",
   "description": "DevExtreme React UI and Visualization Components",
   "repository": {
     "type": "git",
@@ -15,7 +15,7 @@
     "prop-types": "^15.6.1"
   },
   "peerDependencies": {
-    "devextreme": "~19.1.3",
+    "devextreme": "~19.1.4",
     "react": "^16.2.0",
     "react-dom": "^16.2.0"
   },
@@ -28,7 +28,7 @@
     "css-loader": "^2.1.0",
     "dasherize": "^2.0.0",
     "del": "^3.0.0",
-    "devextreme": "~19.1.3",
+    "devextreme": "~19.1.4",
     "dot": "^1.1.2",
     "enzyme": "^3.7.0",
     "enzyme-adapter-react-16": "^1.6.0",
diff --git a/src/core/component-base.ts b/src/core/component-base.ts
index 16a1b9e..33b85f8 100644
--- a/src/core/component-base.ts
+++ b/src/core/component-base.ts
@@ -63,11 +63,8 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
     this._updateCssClasses(prevProps, this.props);
 
     const preparedProps = this._prepareProps(this.props);
-    const options: Record<string, any> = {
-      ...preparedProps.options,
-      ...this._templatesManager.options
-    };
-    this._optionsManager.processChangedValues(options, prevProps);
+
+    this._optionsManager.updateOptions(preparedProps.options, prevProps);
   }
 
   public componentWillUnmount() {
@@ -172,7 +169,7 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
   private _prepareProps(rawProps: Record<string, any>): IWidgetConfig {
     const separatedProps = separateProps(rawProps, this._defaults, this._templateProps);
 
-    this._templatesManager.add({
+    const templatesOptions = this._templatesManager.add({
       useChildren: () => false,
       ownerName: "",
       templateProps: this._templateProps,
@@ -181,7 +178,10 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
     });
 
     return {
-        options: separatedProps.options,
+        options: {
+          ...separatedProps.options,
+          ...templatesOptions
+        },
         defaults: separatedProps.defaults
     };
   }
diff --git a/src/core/options-manager.ts b/src/core/options-manager.ts
index 2cad25a..3ceb159 100644
--- a/src/core/options-manager.ts
+++ b/src/core/options-manager.ts
@@ -55,10 +55,11 @@ class OptionsManager {
         this._templatesManager = templateHost;
 
         this._setOption = this._setOption.bind(this);
+        this._syncOptions = this._syncOptions.bind(this);
         this._registerNestedOption = this._registerNestedOption.bind(this);
         this.registerNestedOption = this.registerNestedOption.bind(this);
         this.handleOptionChange = this.handleOptionChange.bind(this);
-        this.processChangedValues = this.processChangedValues.bind(this);
+        this.updateOptions = this.updateOptions.bind(this);
     }
 
     public resetNestedElements() {
@@ -132,37 +133,24 @@ class OptionsManager {
         this._setGuard(optionName, optionValue);
     }
 
-    public processChangedValues(newProps: Record<string, any>, prevProps: Record<string, any>): void {
-        this._updatingProps = false;
-
-        for (const optionName of Object.keys(newProps)) {
-            if (newProps[optionName] === prevProps[optionName]) {
-                continue;
-            }
-
-            if (this._guards[optionName]) {
-                window.clearTimeout(this._guards[optionName]);
-                delete this._guards[optionName];
-            }
-
-            if (!this._updatingProps) {
-                this._instance.beginUpdate();
-                this._updatingProps = true;
-            }
-            this._setOption(optionName, newProps[optionName]);
-        }
-
-        if (this._updatingProps) {
-            this._updatingProps = false;
-            this._instance.endUpdate();
-        }
+    public updateOptions(newProps: Record<string, any>, prevProps: Record<string, any>): void {
+        const nestedOptions: Record<string, any> = {};
 
         Object.keys(this._dirtyOptions).forEach((optionName) => {
             const optionDescr = this._dirtyOptions[optionName];
-            const optionValue = this._getNestedOptionObj(optionDescr, false);
-            this._setOption(optionName, optionValue);
+            const optionValue = this._getNestedOptionObj(optionDescr, true);
+
+            nestedOptions[optionName] = optionValue;
         });
 
+        const newOptions: Record<string, any> = {
+            ...this._templatesManager.options,
+            ...nestedOptions,
+            ...newProps
+        };
+
+        this._syncOptions(newOptions, prevProps);
+
         this._dirtyOptions = {};
     }
 
@@ -184,6 +172,35 @@ class OptionsManager {
         }
     }
 
+    private _syncOptions(
+        newOptions: Record<string, any>,
+        prevOptions: Record<string, any>
+    ): void {
+        this._updatingProps = false;
+
+        for (const optionName of Object.keys(newOptions)) {
+            if (newOptions[optionName] === prevOptions[optionName]) {
+                continue;
+            }
+
+            if (this._guards[optionName]) {
+                window.clearTimeout(this._guards[optionName]);
+                delete this._guards[optionName];
+            }
+
+            if (!this._updatingProps) {
+                this._instance.beginUpdate();
+                this._updatingProps = true;
+            }
+            this._setOption(optionName, newOptions[optionName]);
+        }
+
+        if (this._updatingProps) {
+            this._updatingProps = false;
+            this._instance.endUpdate();
+        }
+    }
+
     private _setOption(name: string, value: any): void {
         let actualValue = value;
         if (isEventHanlder(name, value)) {
@@ -238,8 +255,9 @@ class OptionsManager {
             const hasChildrenForTemplate =
                 ReactChildren.count(e.element.props.children) > nestedObjectsCount;
 
+            let templatesOptions = {};
             if (templateRegistrationRequired) {
-                this._templatesManager.add({
+                templatesOptions = this._templatesManager.add({
                     useChildren: (optionName) => {
                         return optionName === "template" && hasChildrenForTemplate;
                     },
@@ -249,7 +267,14 @@ class OptionsManager {
                         configComponent,
                         configComponent.isCollectionItem ? index : undefined,
                     ),
-                    propsGetter: (prop) => configComponent.elementEntries[index].element.props[prop]
+                    propsGetter: (prop) => {
+                        const nestedElement = configComponent.elementEntries[index];
+                        if (!nestedElement) {
+                            return;
+                        }
+
+                        return nestedElement.element.props[prop];
+                    }
                 });
             }
 
@@ -257,7 +282,8 @@ class OptionsManager {
                 ...e.predefinedProps,
                 ...props.defaults,
                 ...props.options,
-                ...nestedObjects
+                ...nestedObjects,
+                ...templatesOptions
             };
         });
 
@@ -323,7 +349,7 @@ class OptionsManager {
                     nestedOptionClass.type.TemplateProps
                 ).options;
 
-                this.processChangedValues(
+                this._syncOptions(
                     addPrefixToKeys(newOptions, optionItemName + "."),
                     addPrefixToKeys(prevProps, optionItemName + ".")
                 );
diff --git a/src/core/templates-manager.ts b/src/core/templates-manager.ts
index 531daf1..cee3d13 100644
--- a/src/core/templates-manager.ts
+++ b/src/core/templates-manager.ts
@@ -41,7 +41,6 @@ const contentCreators = {
 class TemplatesManager {
     private _templatesStore: TemplatesStore;
     private _templates: Record<string, any> = {};
-    private _stubs: Record<string, any> = {};
     private _nestedTemplateProps: Record<string, {
         render: any;
         component: any;
@@ -52,9 +51,9 @@ class TemplatesManager {
         this._templatesStore = templatesStore;
     }
 
-    public add(meta: IIntegrationDescr) {
+    public add(meta: IIntegrationDescr): Record<string, any> {
         const templates: Record<string, IDxTemplate> = {};
-        const stubs: Record<string, any> = {};
+        const templatesOptions: Record<string, any> = {};
 
         const props = meta.props;
         const templateProps = meta.templateProps || [];
@@ -85,7 +84,7 @@ class TemplatesManager {
             contentCreator = contentCreator.bind(this, propName, meta.propsGetter);
 
             const name = ownerName ? `${ownerName}.${tmpl.tmplOption}` : tmpl.tmplOption;
-            stubs[name] = name;
+            templatesOptions[tmpl.tmplOption] = name;
             templates[name] = createDxTemplate(
                 contentCreator,
                 this._templatesStore,
@@ -98,10 +97,7 @@ class TemplatesManager {
             ...templates
         };
 
-        this._stubs = {
-            ...this._stubs,
-            ...stubs
-        };
+        return templatesOptions;
     }
 
     public addNested(props: ITemplateProps): void {
@@ -127,8 +123,7 @@ class TemplatesManager {
         return {
             integrationOptions: {
                 templates: this._templates
-            },
-            ...this._stubs
+            }
         };
     }
 }
diff --git a/tools/integration-data.json b/tools/integration-data.json
index 5af237d..b02ecf8 100644
--- a/tools/integration-data.json
+++ b/tools/integration-data.json
@@ -1,6 +1,6 @@
 {
   "meta": {
-    "docsRevision": "d5608f4d01bad62026055b451281774c081edeed"
+    "docsRevision": "a73766e2d79539bb3d9d86e3f60128cc7f6d422a"
   },
   "widgets": [
     {
@@ -23742,6 +23742,14 @@
                     }
                   ]
                 },
+                {
+                  "name": "cornerRadius",
+                  "types": [
+                    {
+                      "type": "Number"
+                    }
+                  ]
+                },
                 {
                   "name": "dashStyle",
                   "types": [
@@ -24067,7 +24075,7 @@
           "optionName": "border",
           "owners": [
             "annotation",
-            "tooltip"
+            "legend"
           ],
           "props": [
             {
@@ -24078,6 +24086,14 @@
                 }
               ]
             },
+            {
+              "name": "cornerRadius",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
             {
               "name": "dashStyle",
               "types": [
@@ -26647,7 +26663,7 @@
           "optionName": "border",
           "owners": [
             "annotation",
-            "tooltip",
+            "legend",
             "commonPaneSettings",
             "commonSeriesSettings",
             "commonSeriesSettingsHoverStyle",
@@ -26656,7 +26672,7 @@
             "point",
             "pointHoverStyle",
             "pointSelectionStyle",
-            "legend"
+            "tooltip"
           ],
           "props": [
             {
@@ -26667,6 +26683,14 @@
                 }
               ]
             },
+            {
+              "name": "cornerRadius",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
             {
               "name": "dashStyle",
               "types": [
@@ -26736,14 +26760,6 @@
                   "type": "Boolean"
                 }
               ]
-            },
-            {
-              "name": "cornerRadius",
-              "types": [
-                {
-                  "type": "Number"
-                }
-              ]
             }
           ]
         },
@@ -27166,6 +27182,14 @@
                     }
                   ]
                 },
+                {
+                  "name": "cornerRadius",
+                  "types": [
+                    {
+                      "type": "Number"
+                    }
+                  ]
+                },
                 {
                   "name": "dashStyle",
                   "types": [
@@ -32929,6 +32953,10 @@
             "DxChart"
           ],
           "nesteds": [
+            {
+              "componentName": "annotationBorder",
+              "optionName": "border"
+            },
             {
               "componentName": "border",
               "optionName": "border"
@@ -32937,10 +32965,6 @@
               "componentName": "font",
               "optionName": "font"
             },
-            {
-              "componentName": "legendBorder",
-              "optionName": "border"
-            },
             {
               "componentName": "legendMargin",
               "optionName": "margin"
@@ -33481,69 +33505,6 @@
             }
           ]
         },
-        {
-          "name": "legendBorder",
-          "optionName": "border",
-          "owners": [
-            "legend"
-          ],
-          "props": [
-            {
-              "name": "color",
-              "types": [
-                {
-                  "type": "String"
-                }
-              ]
-            },
-            {
-              "name": "cornerRadius",
-              "types": [
-                {
-                  "type": "Number"
-                }
-              ]
-            },
-            {
-              "name": "dashStyle",
-              "types": [
-                {
-                  "type": "String",
-                  "acceptableValues": [
-                    "\"dash\"",
-                    "\"dot\"",
-                    "\"longDash\"",
-                    "\"solid\""
-                  ]
-                }
-              ]
-            },
-            {
-              "name": "opacity",
-              "types": [
-                {
-                  "type": "Number"
-                }
-              ]
-            },
-            {
-              "name": "visible",
-              "types": [
-                {
-                  "type": "Boolean"
-                }
-              ]
-            },
-            {
-              "name": "width",
-              "types": [
-                {
-                  "type": "Number"
-                }
-              ]
-            }
-          ]
-        },
         {
           "name": "legendMargin",
           "optionName": "margin",
@@ -37483,10 +37444,6 @@
             "DxChart"
           ],
           "nesteds": [
-            {
-              "componentName": "annotationBorder",
-              "optionName": "border"
-            },
             {
               "componentName": "argumentFormat",
               "optionName": "argumentFormat"
@@ -37506,6 +37463,10 @@
             {
               "componentName": "shadow",
               "optionName": "shadow"
+            },
+            {
+              "componentName": "tooltipBorder",
+              "optionName": "border"
             }
           ],
           "props": [
@@ -37752,6 +37713,61 @@
             }
           ]
         },
+        {
+          "name": "tooltipBorder",
+          "optionName": "border",
+          "owners": [
+            "tooltip"
+          ],
+          "props": [
+            {
+              "name": "color",
+              "types": [
+                {
+                  "type": "String"
+                }
+              ]
+            },
+            {
+              "name": "dashStyle",
+              "types": [
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"dash\"",
+                    "\"dot\"",
+                    "\"longDash\"",
+                    "\"solid\""
+                  ]
+                }
+              ]
+            },
+            {
+              "name": "opacity",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
+            {
+              "name": "visible",
+              "types": [
+                {
+                  "type": "Boolean"
+                }
+              ]
+            },
+            {
+              "name": "width",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            }
+          ]
+        },
         {
           "name": "url",
           "optionName": "url",
@@ -79227,6 +79243,14 @@
             }
           ]
         },
+        {
+          "name": "showArrowIcon",
+          "types": [
+            {
+              "type": "Boolean"
+            }
+          ]
+        },
         {
           "name": "splitButton",
           "types": [
@@ -152216,6 +152240,22 @@
                 }
               ]
             },
+            {
+              "name": "maxBubbleSize",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
+            {
+              "name": "minBubbleSize",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
             {
               "name": "negativesAsZeroes",
               "types": [
@@ -155540,6 +155580,22 @@
                 }
               ]
             },
+            {
+              "name": "maxBubbleSize",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
+            {
+              "name": "minBubbleSize",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
             {
               "name": "negativesAsZeroes",
               "types": [
@@ -200086,6 +200142,23 @@
                               "type": "Boolean"
                             }
                           ]
+                        },
+                        {
+                          "name": "node",
+                          "types": [
+                            {
+                              "type": "dxTreeListNode",
+                              "isCustomType": true
+                            }
+                          ]
+                        },
+                        {
+                          "name": "level",
+                          "types": [
+                            {
+                              "type": "Number"
+                            }
+                          ]
                         }
                       ]
                     }
@@ -200749,6 +200822,23 @@
                               "isCustomType": true
                             }
                           ]
+                        },
+                        {
+                          "name": "node",
+                          "types": [
+                            {
+                              "type": "dxTreeListNode",
+                              "isCustomType": true
+                            }
+                          ]
+                        },
+                        {
+                          "name": "level",
+                          "types": [
+                            {
+                              "type": "Number"
+                            }
+                          ]
                         }
                       ]
                     }
@@ -243557,6 +243647,14 @@
                 }
               ]
             },
+            {
+              "name": "cornerRadius",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
             {
               "name": "dashStyle",
               "types": [
@@ -243938,6 +244036,14 @@
                 }
               ]
             },
+            {
+              "name": "cornerRadius",
+              "types": [
+                {
+                  "type": "Number"
+                }
+              ]
+            },
             {
               "name": "dashStyle",
               "types": [


Test patch:
diff --git a/src/core/__tests__/template.test.tsx b/src/core/__tests__/template.test.tsx
index d01853f..ffb8fbb 100644
--- a/src/core/__tests__/template.test.tsx
+++ b/src/core/__tests__/template.test.tsx
@@ -1,3 +1,4 @@
+/* tslint:disable:no-string-literal */
 import * as events from "devextreme/events";
 
 import ConfigurationComponent from "../../core/nested-option";
@@ -459,7 +460,7 @@ describe("component/render in nested options", () => {
         );
 
         const options = WidgetClass.mock.calls[0][1];
-        expect(options["option.item"]).toBe("option.item");
+        expect(options["option"]["item"]).toBe("option.item");
 
         const integrationOptions = options.integrationOptions;
 
@@ -503,8 +504,8 @@ describe("component/render in nested options", () => {
 
         const options = WidgetClass.mock.calls[0][1];
 
-        expect(options["option.item"]).toBe("option.item");
-        expect(options["collection[0].template"]).toBe("collection[0].template");
+        expect(options["option"]["item"]).toBe("option.item");
+        expect(options["collection"][0]["template"]).toBe("collection[0].template");
 
         const integrationOptions = options.integrationOptions;
 
@@ -532,10 +533,10 @@ describe("component/render in nested options", () => {
 
         const options = WidgetClass.mock.calls[0][1];
 
-        expect(options["collection[0].template"]).toBe("collection[0].template");
-        expect(options["collection[1].template"]).toBe("collection[1].template");
-        expect(options["collection[2].option.item"]).toBe("collection[2].option.item");
-        expect(options["option.collection[0].template"]).toBe("option.collection[0].template");
+        expect(options["collection"][0]["template"]).toBe("collection[0].template");
+        expect(options["collection"][1]["template"]).toBe("collection[1].template");
+        expect(options["collection"][2]["option"]["item"]).toBe("collection[2].option.item");
+        expect(options["option"]["collection"][0]["template"]).toBe("option.collection[0].template");
 
         const integrationOptions = options.integrationOptions;
 
@@ -589,15 +590,15 @@ describe("component/render in nested options", () => {
 
         const options = WidgetClass.mock.calls[0][1];
 
-        expect(options["collection[0].template"]).toBe("collection[0].template");
-        expect(options["collection[1].template"]).toBe("collection[1].template");
-        expect(options["collection[2].template"]).toBe("collection[2].template");
-        expect(options["collection[3].template"]).toBe("collection[3].template");
-        expect(options["collection[4].template"]).toBe(undefined);
-        expect(options["collection[5].template"]).toBe(undefined);
-        expect(options["collection[6].template"]).toBe(undefined);
-        expect(options["option.item"]).toBe(undefined);
-        expect(options["option.template"]).toBe(undefined);
+        expect(options["collection"][0]["template"]).toBe("collection[0].template");
+        expect(options["collection"][1]["template"]).toBe("collection[1].template");
+        expect(options["collection"][2]["template"]).toBe("collection[2].template");
+        expect(options["collection"][3]["template"]).toBe("collection[3].template");
+        expect(options["collection"][4]["template"]).toBe(undefined);
+        expect(options["collection"][5]["template"]).toBe(undefined);
+        expect(options["collection"][6]["template"]).toBe(undefined);
+        expect(options["option"]["item"]).toBe(undefined);
+        expect(options["option"]["template"]).toBe(undefined);
 
         const integrationOptions = options.integrationOptions;
 
@@ -652,4 +653,82 @@ describe("component/render in nested options", () => {
         component.update();
         expect(component.find(".template").html()).toBe('<div class="template">Second Template</div>');
     });
+
+    it("adds nested components dynamically", () => {
+        const renderItem = () => <div>Template</div>;
+        const items = [{id: 1, render: renderItem}];
+
+        const TestContainer = (props: any) => (
+            <TestComponent>
+                {props.items.map((item) => <CollectionNestedComponent key={item.id} render={item.render} />)}
+            </TestComponent>
+        );
+
+        const component = mount(<TestContainer items={items} />);
+        component.setProps({
+            items: [
+                ...items,
+                {id: 2, render: renderItem}
+            ]
+        });
+
+        const updatedOptions = Widget.option.mock.calls;
+
+        expect(updatedOptions[0][0]).toBe("integrationOptions");
+        expect(Object.keys(updatedOptions[0][1].templates)).toEqual([
+            "collection[0].template",
+            "collection[1].template"
+        ]);
+
+        expect(updatedOptions[1][0]).toBe("collection");
+        expect(updatedOptions[1][1].length).toBe(2);
+        expect(updatedOptions[1][1][0].template).toBe("collection[0].template");
+        expect(updatedOptions[1][1][1].template).toBe("collection[1].template");
+    });
+
+    it("removes nested components dynamically", () => {
+        const renderItem = () => <div>Template</div>;
+        const items = [{id: 1, render: renderItem}, {id: 2, render: renderItem}];
+
+        const TestContainer = (props: any) => (
+            <TestComponent>
+                {props.items.map((item) => <CollectionNestedComponent key={item.id} render={item.render} />)}
+            </TestComponent>
+        );
+
+        const component = mount(<TestContainer items={items} />);
+        component.setProps({
+            items: items.slice(0, 1)
+        });
+
+        const updatedOptions = Widget.option.mock.calls;
+
+        expect(updatedOptions[1][0]).toBe("collection");
+        expect(updatedOptions[1][1].length).toBe(1);
+        expect(updatedOptions[1][1][0].template).toBe("collection[0].template");
+    });
+
+    xit("removes deleted tempalates from integrationOptions", () => {
+        const ItemTemplate = () => <div>Template</div>;
+        const items = [{id: 1, render: ItemTemplate}, {id: 2, render: ItemTemplate}];
+
+        const TestContainer = (props: any) => (
+            <TestComponent>
+                {props.items.map((item) => <CollectionNestedComponent key={item.id} render={item.render} />)}
+            </TestComponent>
+        );
+
+        const component = mount(<TestContainer items={items} />);
+        component.setProps({
+            items: items.slice(0, 1)
+        });
+
+        const updatedOptions = Widget.option.mock.calls;
+
+        expect(updatedOptions[0][0]).toBe("integrationOptions");
+        expect(Object.keys(updatedOptions[0][1].templates).length).toBe(1);
+        expect(Object.keys(updatedOptions[0][1].templates)[0]).toBe(
+            "collection[0].template"
+        );
+    });
 });