Repository: DevExpress/devextreme-react
Pull request #256 (2018-12-04 16:27:12)

Introduce Template Host


Patch:
diff --git a/package-lock.json b/package-lock.json
index 72c2787..5785d78 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8571,7 +8571,7 @@
     },
     "parchment": {
       "version": "1.1.4",
-      "resolved": "http://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
+      "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
       "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
       "dev": true
     },
@@ -9649,7 +9649,7 @@
     },
     "quill": {
       "version": "1.3.6",
-      "resolved": "http://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
+      "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
       "integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==",
       "dev": true,
       "requires": {
diff --git a/src/core/component-base.ts b/src/core/component-base.ts
index 5617b92..3a852fc 100644
--- a/src/core/component-base.ts
+++ b/src/core/component-base.ts
@@ -5,21 +5,17 @@ import OptionsManager, { INestedOption } from "./options-manager";
 import { findProps as findNestedTemplateProps, ITemplateMeta } from "./template";
 import { elementPropNames, getClassName, separateProps } from "./widget-config";
 
-import {
-  getTemplateOptions,
-  TemplateGetter
-} from "./template-helper";
+import TemplateHost from "./template-host";
 
 const DX_REMOVE_EVENT = "dxremove";
 
 interface IWidgetConfig {
     defaults: Record<string, any>;
     options: Record<string, any>;
-    integrationOptions: Record<string, any>;
 }
 
 interface IState {
-  templates: Record<string, TemplateGetter>;
+  templates: Record<string, () => void>;
 }
 
 interface IHtmlOptions {
@@ -37,6 +33,7 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
   protected readonly _templateProps: ITemplateMeta[] = [];
   protected readonly _expectedChildren: Record<string, INestedOption>;
 
+  private readonly _templateHost: TemplateHost;
   private readonly _optionsManager: OptionsManager;
 
   constructor(props: P) {
@@ -48,31 +45,12 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
       templates: {}
     };
 
-    this._optionsManager = new OptionsManager((name) => this.props[name]);
-  }
-
-  public componentWillUpdate(nextProps: P) {
-    const preparedProps = this._prepareProps(nextProps);
-    const options: Record<string, any> = {
-      ...preparedProps.options,
-      ...preparedProps.integrationOptions
-    };
-
-    this._optionsManager.processChangedValues(options, this.props);
+    this._templateHost = new TemplateHost(this._updateTemplatesState);
+    this._optionsManager = new OptionsManager((name) => this.props[name], this._templateHost);
   }
 
   public render() {
-    const elementProps: Record<string, any> = {
-      ref: (element: HTMLDivElement) => this._element = element
-    };
-
-    elementPropNames.forEach((name) => {
-      if (name in this.props) {
-        elementProps[name] = this.props[name];
-      }
-    });
-
-    return React.createElement("div", elementProps, ...this._prepareChildren());
+    return React.createElement("div", this._getElementProps(), ...this._prepareChildren());
   }
 
   public componentDidMount() {
@@ -81,6 +59,14 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
 
   public componentDidUpdate(prevProps: P) {
     this._updateCssClasses(prevProps, this.props);
+
+    const preparedProps = this._prepareProps(this.props);
+    const options: Record<string, any> = {
+      ...preparedProps.options,
+      ...this._templateHost.options
+    };
+
+    this._optionsManager.processChangedValues(options, prevProps);
   }
 
   public componentWillUnmount() {
@@ -92,31 +78,31 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
 
   protected _prepareChildren(args: any[] = []): any[] {
     this._optionsManager.resetNestedElements();
-    let nestedTemplates: Record<string, any> = {};
+
     React.Children.forEach(this.props.children, (child: React.ReactElement<any>) => {
-      nestedTemplates = {
-        ...nestedTemplates,
-        ...findNestedTemplateProps(child)
-      };
       args.push(this._preprocessChild(child) || child);
     });
 
     const templates = Object.getOwnPropertyNames(this.state.templates) || [];
 
     templates.forEach((t) => {
-      args.push(this.state.templates[t](nestedTemplates));
+      args.push(this.state.templates[t]());
     });
 
     return args;
   }
 
   protected _preprocessChild(component: React.ReactElement<any>): React.ReactElement<any> {
+    const nestedTemplate = findNestedTemplateProps(component);
+    if (nestedTemplate) {
+      this._templateHost.addNested(nestedTemplate);
+    }
     return this._optionsManager.registerNestedOption(component, this._expectedChildren) || component;
   }
 
   protected _createWidget(element?: Element) {
     element = element || this._element;
-    const nestedProps = this._optionsManager.getNestedOptionsObjects(this._updateTemplatesState);
+    const nestedProps = this._optionsManager.getNestedOptionsObjects();
     const props = {
         ...(this.props as any),
         ...nestedProps
@@ -128,7 +114,7 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
       templatesRenderAsynchronously: true,
       ...preparedProps.defaults,
       ...preparedProps.options,
-      ...preparedProps.integrationOptions
+      ...this._templateHost.options
     };
 
     this._optionsManager.wrapEventHandlers(options);
@@ -148,6 +134,19 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
     });
   }
 
+  private _getElementProps(): Record<string, any> {
+    const elementProps: Record<string, any> = {
+      ref: (element: HTMLDivElement) => this._element = element
+    };
+
+    elementPropNames.forEach((name) => {
+      if (name in this.props) {
+        elementProps[name] = this.props[name];
+      }
+    });
+    return elementProps;
+  }
+
   private _updateCssClasses(prevProps: P | null, newProps: P) {
     const prevClassName = prevProps ? getClassName(prevProps) : undefined;
     const newClassName = getClassName(newProps);
@@ -172,46 +171,20 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
   private _prepareProps(rawProps: Record<string, any>): IWidgetConfig {
     const separatedProps = separateProps(rawProps, this._defaults, this._templateProps);
 
-    const templateOptions = getTemplateOptions({
+    this._templateHost.add({
+      ownerName: "",
       templateProps: this._templateProps,
-      options: separatedProps.templates,
-      nestedOptions: getNestedTemplates(rawProps),
-      stateUpdater: this._updateTemplatesState,
+      props: separatedProps.templates,
       propsGetter: (prop) => this.props[prop]
     });
 
-    const templates = {
-      ...rawProps.integrationOptions && rawProps.integrationOptions.templates,
-      ...templateOptions.templates
-    };
-
-    const integrationOptions = Object.keys(templates).length ? {
-      integrationOptions: {
-        templates
-      },
-      ...templateOptions.templateStubs
-    } : undefined;
-
     return {
         options: separatedProps.options,
-        defaults: separatedProps.defaults,
-        integrationOptions
+        defaults: separatedProps.defaults
     };
   }
 }
 
-function getNestedTemplates(rawProps: Record<string, any>): Record<string, any> {
-  let nestedTemplates: Record<string, any> = {};
-  React.Children.forEach(rawProps.children, (child: React.ReactElement<any>) => {
-      nestedTemplates = {
-        ...nestedTemplates,
-        ...findNestedTemplateProps(child)
-      };
-  });
-
-  return nestedTemplates;
-}
-
 export {
   IState,
   IHtmlOptions,
diff --git a/src/core/component.ts b/src/core/component.ts
index a19c4ad..56e719f 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -1,15 +1,9 @@
 import * as React from "react";
 
-import { ComponentBase } from "./component-base";
+import { ComponentBase, IHtmlOptions } from "./component-base";
 import { ExtensionComponent } from "./extension-component";
 
-interface IHtmlOptions {
-  id?: string;
-  className?: string;
-  style?: any;
-}
-
-class Component<P extends IHtmlOptions> extends ComponentBase<P> {
+class Component<P> extends ComponentBase<P> {
   private readonly _extensions: Array<(element: Element) => void> = [];
 
   public componentDidMount() {
@@ -20,9 +14,7 @@ class Component<P extends IHtmlOptions> extends ComponentBase<P> {
 
   protected _prepareChildren(): any[] {
     const args: any[] = [];
-    const children = React.Children.toArray(this.props.children);
-    const hasOnlyTextNode = children.length === 1 && typeof children[0] === "string";
-    if (hasOnlyTextNode) {
+    if (isSingleTextNode(this.props.children)) {
       args.push(React.Fragment);
     }
 
@@ -46,6 +38,11 @@ class Component<P extends IHtmlOptions> extends ComponentBase<P> {
   }
 }
 
+function isSingleTextNode(input: React.ReactNode) {
+  const children = React.Children.toArray(input);
+  return children.length === 1 && typeof children[0] === "string";
+}
+
 export {
   Component,
   IHtmlOptions
diff --git a/src/core/nested-option.ts b/src/core/nested-option.ts
index 5b28c90..b81813a 100644
--- a/src/core/nested-option.ts
+++ b/src/core/nested-option.ts
@@ -43,9 +43,9 @@ class NestedOption<P> extends React.PureComponent<P, any> {
             : React.createElement(React.Fragment, {}, ...children);
     }
 
-    public componentWillUpdate(nextProps: P) {
+    public componentDidUpdate(prevProps: P) {
         if (this._isAttached) {
-            this._updateFunc(clearProps(nextProps), this.props);
+            this._updateFunc(clearProps(this.props), prevProps);
         }
     }
 }
diff --git a/src/core/options-manager.ts b/src/core/options-manager.ts
index 0f7733e..ac2dd56 100644
--- a/src/core/options-manager.ts
+++ b/src/core/options-manager.ts
@@ -2,7 +2,7 @@ import { ITemplateMeta } from "./template";
 
 import { addPrefixToKeys, getNestedValue } from "./helpers";
 import { createOptionComponent, INestedOptionMeta } from "./nested-option";
-import { getTemplateOptions } from "./template-helper";
+import TemplateHost from "./template-host";
 import { separateProps } from "./widget-config";
 
 interface INestedOption {
@@ -41,13 +41,15 @@ class OptionsManager {
     private readonly _guards: Record<string, number> = {};
     private readonly _nestedOptions: Record<string, INestedConfigDescr> = {};
     private readonly _optionValueGetter: (name: string) => any;
+    private readonly _templateHost: TemplateHost;
 
     private _instance: any;
 
     private _updatingProps: boolean;
 
-    constructor(optionValueGetter: (name: string) => any) {
+    constructor(optionValueGetter: (name: string) => any, templateHost: TemplateHost) {
         this._optionValueGetter = optionValueGetter;
+        this._templateHost = templateHost;
         this._setOption = this._setOption.bind(this);
         this._registerNestedOption = this._registerNestedOption.bind(this);
 
@@ -140,8 +142,8 @@ class OptionsManager {
         }
     }
 
-    public getNestedOptionsObjects(stateUpdater: any): Record<string, any> {
-        return this._getNestedOptionsObjects(this._nestedOptions, stateUpdater);
+    public getNestedOptionsObjects(): Record<string, any> {
+        return this._getNestedOptionsObjects(this._nestedOptions);
     }
 
     public registerNestedOption(
@@ -168,38 +170,29 @@ class OptionsManager {
     }
 
     private _getNestedOptionsObjects(
-        optionsCollection: Record<string, INestedConfigDescr>,
-        stateUpdater: any
+        optionsCollection: Record<string, INestedConfigDescr>
     ): Record<string, any> {
         const configComponents: Record<string, any> = {};
 
-        let templates = {};
         Object.keys(optionsCollection).forEach((key) => {
             const configComponent = optionsCollection[key];
             const options = configComponent.elementEntries.map((e, index) => {
                 const props = separateProps(e.element.props,
                     configComponent.defaults,
                     configComponent.templates);
-                const templateOptions = getTemplateOptions({
-                    options: props.templates,
-                    nestedOptions: {},
+
+                this._templateHost.add({
+                    props: props.templates,
                     templateProps: configComponent.templates,
                     ownerName: `${configComponent.optionName}${configComponent.isCollectionItem ? `[${index}]` : ""}`,
-                    stateUpdater,
                     propsGetter: (prop) => configComponent.elementEntries[index].element.props[prop]
                 });
 
-                templates = {
-                    ...templates,
-                    ...templateOptions.templates
-                };
-
                 return {
                     ...e.predefinedProps,
                     ...props.defaults,
                     ...props.options,
-                    ...templateOptions.templateStubs,
-                    ...this._getNestedOptionsObjects(e.children, stateUpdater)
+                    ...this._getNestedOptionsObjects(e.children)
                 };
             });
             configComponents[configComponent.optionName] = configComponent.isCollectionItem
@@ -207,12 +200,6 @@ class OptionsManager {
                 : options[options.length - 1];
         });
 
-        if (Object.keys(templates).length) {
-            configComponents.integrationOptions = {
-                templates
-            };
-        }
-
         return configComponents;
     }
 
diff --git a/src/core/template-helper.ts b/src/core/template-helper.ts
deleted file mode 100644
index 3a78c16..0000000
--- a/src/core/template-helper.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import * as React from "react";
-
-import { ITemplateMeta } from "./template";
-
-import { generateID } from "./helpers";
-import { ITemplateWrapperProps, TemplateWrapper } from "./template-wrapper";
-
-type TemplateGetter = (nestedTemplates: Record<string, any>) => React.ReactElement<ITemplateWrapperProps>;
-type StateUpdater = (callback: (templates: TemplateGetter) => void) => void;
-type PropsGetter = (propName: string) => any;
-
-interface IDxTemplateData {
-    container: any;
-    model?: any;
-    index?: any;
-    onRendered?: () => void;
-}
-
-interface IDxTemplate {
-    render: (data: IDxTemplateData) => any;
-}
-
-interface ITemplateDescr {
-    name: string;
-    propName: string;
-    isNested: boolean;
-    isComponent: boolean;
-    propsGetter: PropsGetter;
-}
-
-interface IIntegrationDescr {
-    options: Record<string, any>;
-    nestedOptions: Record<string, any>;
-    templateProps: ITemplateMeta[];
-    ownerName?: string;
-    propsGetter: PropsGetter;
-    stateUpdater: StateUpdater;
-}
-
-function getTemplateOptions(meta: IIntegrationDescr): {
-    templates: any;
-    templateStubs: any;
-} {
-    const templates: Record<string, IDxTemplate> = {};
-    const templateStubs: Record<string, any> = {};
-    const options = meta.options;
-    const stateUpdater = meta.stateUpdater;
-    const templateProps = meta.templateProps || [];
-
-    const prefix = meta.ownerName || "";
-    templateProps.forEach((m) => {
-        if (!options[m.component] && !options[m.render]) {
-            return;
-        }
-        const name = `${prefix}${m.tmplOption}`;
-
-        const templateDescr: ITemplateDescr = {
-            name,
-            propName: options[m.component] ? m.component : m.render,
-            isComponent: !!options[m.component],
-            isNested: false,
-            propsGetter: meta.propsGetter
-        };
-        templateStubs[m.tmplOption] = name;
-        templates[name] = wrapTemplate(templateDescr, stateUpdater);
-    });
-
-    const nestedOptions = meta.nestedOptions;
-    Object.keys(nestedOptions).forEach((name) => {
-        const templateDescr: ITemplateDescr = {
-            name,
-            propName: !!nestedOptions[name].component ? "component" : "render",
-            isComponent: !!nestedOptions[name].component,
-            isNested: true,
-            propsGetter: meta.propsGetter
-        };
-        templates[name] = wrapTemplate(templateDescr, stateUpdater);
-    });
-
-    return {
-        templates,
-        templateStubs
-    };
-}
-
-function wrapTemplate(templateDescr: ITemplateDescr, stateUpdater: StateUpdater): IDxTemplate {
-    return {
-        render: (data: IDxTemplateData) => {
-            const templateId = "__template_" + generateID();
-            const container = unwrapElement(data.container);
-            const createWrapper = (nestedTemplates) => {
-                const propsGetter = templateDescr.isNested
-                    ? (prop) => nestedTemplates[templateDescr.name][prop]
-                    : templateDescr.propsGetter;
-
-                const contentProvider = templateDescr.isComponent
-                    ? React.createElement.bind(null, propsGetter(templateDescr.propName))
-                    : propsGetter(templateDescr.propName);
-
-                const model = data.model;
-                if (model && model.hasOwnProperty("key")) {
-                    model.dxkey = model.key;
-                }
-                return React.createElement<ITemplateWrapperProps>(TemplateWrapper, {
-                    content: contentProvider(model),
-                    container,
-                    onRemoved: () => stateUpdater((t) => delete t[templateId]),
-                    onRendered: data.onRendered,
-                    key: templateId
-                });
-            };
-            stateUpdater((t) => t[templateId] = createWrapper);
-            return container;
-        }
-    };
-}
-
-function unwrapElement(element: any) {
-    return element.get ? element.get(0) : element;
-}
-
-export {
-    PropsGetter,
-    StateUpdater,
-    TemplateGetter,
-    IIntegrationDescr,
-    getTemplateOptions
-};
diff --git a/src/core/template-host.ts b/src/core/template-host.ts
new file mode 100644
index 0000000..db9183e
--- /dev/null
+++ b/src/core/template-host.ts
@@ -0,0 +1,156 @@
+import { ITemplateMeta } from "./template";
+
+import * as React from "react";
+
+import { generateID } from "./helpers";
+import { ITemplateWrapperProps, TemplateWrapper } from "./template-wrapper";
+
+type TemplateGetter = (nestedTemplates: Record<string, any>) => React.ReactElement<ITemplateWrapperProps>;
+type StateUpdater = (callback: (templates: TemplateGetter) => void) => void;
+type PropsGetter = (propName: string) => any;
+
+interface IDxTemplateData {
+    container: any;
+    model?: any;
+    index?: any;
+    onRendered?: () => void;
+}
+
+interface IDxTemplate {
+    render: (data: IDxTemplateData) => any;
+}
+
+interface ITemplateDescr {
+    name: string;
+    propName: string;
+    isComponent: boolean;
+    propsGetter: PropsGetter;
+}
+
+interface IIntegrationDescr {
+    props: Record<string, any>;
+    templateProps: ITemplateMeta[];
+    ownerName: string;
+    propsGetter: PropsGetter;
+}
+
+class TemplateHost {
+    private readonly _stateUpdater: StateUpdater;
+
+    private _templates: Record<string, any> = {};
+    private _stubs: Record<string, any> = {};
+    private _nestedTemplateProps: Record<string, {
+        render: any;
+        component: any;
+    }> = {};
+
+    constructor(stateUpdater: StateUpdater) {
+        this._stateUpdater = stateUpdater;
+    }
+
+    public add(meta: IIntegrationDescr) {
+        const templates: Record<string, IDxTemplate> = {};
+        const stubs: Record<string, any> = {};
+
+        const props = meta.props;
+        const templateProps = meta.templateProps || [];
+        const ownerName = meta.ownerName;
+
+        for (const tmpl of templateProps) {
+            if (!props[tmpl.component] && !props[tmpl.render]) {
+                continue;
+            }
+            const name = ownerName ? `${ownerName}.${tmpl.tmplOption}` : tmpl.tmplOption;
+
+            const templateDescr: ITemplateDescr = {
+                name,
+                propName: props[tmpl.component] ? tmpl.component : tmpl.render,
+                isComponent: !!props[tmpl.component],
+                propsGetter: meta.propsGetter
+            };
+            stubs[name] = name;
+            templates[name] = wrapTemplate(templateDescr, this._stateUpdater);
+        }
+
+        this._templates = {
+            ...this._templates,
+            ...templates
+        };
+
+        this._stubs = {
+            ...this._stubs,
+            ...stubs
+        };
+    }
+
+    public addNested(props: {
+        name: string;
+        render: any;
+        component: any;
+    }): void {
+        const name: string = props.name;
+        this._nestedTemplateProps[name] = {
+            component: props.component,
+            render: props.render
+        };
+        const propsGetter = (prop) => this._nestedTemplateProps[name][prop];
+
+        const templateDescr: ITemplateDescr = {
+            name,
+            propName: !!props.component ? "component" : "render",
+            isComponent: !!props.component,
+            propsGetter
+        };
+
+        this._templates[name] = wrapTemplate(templateDescr, this._stateUpdater);
+    }
+
+    public get options(): Record<string, any> | undefined {
+        if (!Object.keys(this._templates).length) {
+            return;
+        }
+
+        return {
+            integrationOptions: {
+                templates: this._templates
+            },
+            ...this._stubs
+        };
+    }
+}
+
+function wrapTemplate(templateDescr: ITemplateDescr, stateUpdater: StateUpdater): IDxTemplate {
+    return {
+        render: (data: IDxTemplateData) => {
+            const templateId = "__template_" + generateID();
+            const container = unwrapElement(data.container);
+            const createWrapper = () => {
+                const propsGetter = templateDescr.propsGetter;
+
+                const contentProvider = templateDescr.isComponent
+                    ? React.createElement.bind(null, propsGetter(templateDescr.propName))
+                    : propsGetter(templateDescr.propName);
+
+                const model = data.model;
+                if (model && model.hasOwnProperty("key")) {
+                    model.dxkey = model.key;
+                }
+                return React.createElement<ITemplateWrapperProps>(TemplateWrapper, {
+                    content: contentProvider(model),
+                    container,
+                    onRemoved: () => stateUpdater((t) => delete t[templateId]),
+                    onRendered: data.onRendered,
+                    key: templateId
+                });
+            };
+            stateUpdater((t) => t[templateId] = createWrapper);
+            return container;
+        }
+    };
+}
+
+function unwrapElement(element: any) {
+    return element.get ? element.get(0) : element;
+}
+
+export default TemplateHost;
diff --git a/src/core/template.ts b/src/core/template.ts
index 0fae3c0..18e3e64 100644
--- a/src/core/template.ts
+++ b/src/core/template.ts
@@ -32,18 +32,15 @@ const requiredPropsCheck = (props: Record<string, any>) => {
     render: requiredPropsCheck
 };
 
-function findProps(child: React.ReactElement<any>): Record<string, { render: any, component: any }> {
+function findProps(child: React.ReactElement<any>): { name: string, render: any, component: any } | undefined {
     if (child.type !== Template) {
-        return {};
+        return;
     }
-    const result: Record<string, { render: any, component: any }> = {};
-
-    result[child.props.name] = {
+    return {
+        name: child.props.name,
         render: child.props.render,
         component: child.props.component
     };
-
-    return result;
 }
 
 export {


Test patch:
diff --git a/src/core/__tests__/template.test.tsx b/src/core/__tests__/template.test.tsx
index f678d95..1641de9 100644
--- a/src/core/__tests__/template.test.tsx
+++ b/src/core/__tests__/template.test.tsx
@@ -336,15 +336,14 @@ describe("component/render in nested options", () => {
         );
 
         const options = WidgetClass.mock.calls[0][1];
-
-        expect(options.option.item).toBe("optionitem");
+        expect(options["option.item"]).toBe("option.item");
 
         const integrationOptions = options.integrationOptions;
 
         expect(integrationOptions).toBeDefined();
         expect(integrationOptions.templates).toBeDefined();
-        expect(integrationOptions.templates.optionitem).toBeDefined();
-        expect(typeof integrationOptions.templates.optionitem.render).toBe("function");
+        expect(integrationOptions.templates["option.item"]).toBeDefined();
+        expect(typeof integrationOptions.templates["option.item"].render).toBe("function");
     });
 
     it("pass integrationOptions to widget with Template component", () => {
@@ -366,8 +365,8 @@ describe("component/render in nested options", () => {
         expect(integrationOptions.templates.item).toBeDefined();
         expect(typeof integrationOptions.templates.item.render).toBe("function");
 
-        expect(integrationOptions.templates.optionitem).toBeDefined();
-        expect(typeof integrationOptions.templates.optionitem.render).toBe("function");
+        expect(integrationOptions.templates["option.item"]).toBeDefined();
+        expect(typeof integrationOptions.templates["option.item"].render).toBe("function");
     });
 
     it("pass integrationOptions options to widget with several templates", () => {
@@ -381,12 +380,12 @@ describe("component/render in nested options", () => {
 
         const options = WidgetClass.mock.calls[0][1];
 
-        expect(options.option.item).toBe("optionitem");
-        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;
 
-        expect(Object.keys(integrationOptions.templates)).toEqual(["optionitem", "collection[0]template"]);
+        expect(Object.keys(integrationOptions.templates)).toEqual(["option.item", "collection[0].template"]);
     });
 
     it("pass integrationOptions options for collection nested components", () => {
@@ -400,12 +399,12 @@ 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[0].template"]).toBe("collection[0].template");
+        expect(options["collection[1].template"]).toBe("collection[1].template");
 
         const integrationOptions = options.integrationOptions;
 
-        expect(Object.keys(integrationOptions.templates)).toEqual(["collection[0]template", "collection[1]template"]);
+        expect(Object.keys(integrationOptions.templates)).toEqual(["collection[0].template", "collection[1].template"]);
     });
 
     it("renders templates", () => {
@@ -415,7 +414,7 @@ describe("component/render in nested options", () => {
                 <NestedComponent itemComponent={FirstTemplate} />
             </TestComponent >
         );
-        renderTemplate("optionitem");
+        renderTemplate("option.item");
         component.update();
         expect(component.find(".template").html()).toBe('<div class="template">First Template</div>');