Repository: DevExpress/devextreme-react
Pull request #289 (2019-01-22 17:06:39)

 Fix pulling templates from nested config-components


Patch:
diff --git a/src/core/options-manager.ts b/src/core/options-manager.ts
index 8885a12..89f156a 100644
--- a/src/core/options-manager.ts
+++ b/src/core/options-manager.ts
@@ -11,6 +11,7 @@ interface INestedOption {
 }
 
 interface INestedConfigDescr extends INestedOption {
+    optionFullName: string;
     defaults: Record<string, any>;
     templates: ITemplateMeta[];
     elementEntries: Array<{
@@ -223,9 +224,9 @@ class OptionsManager {
                     },
                     props: props.templates,
                     templateProps: configComponent.templates,
-                    ownerName: this.buildFullOptionName(
-                        configComponent.optionName,
-                        configComponent.isCollectionItem ? index : undefined
+                    ownerName: this.buildOptionItemName(
+                        configComponent,
+                        configComponent.isCollectionItem ? index : undefined,
                     ),
                     propsGetter: (prop) => configComponent.elementEntries[index].element.props[prop]
                 });
@@ -265,18 +266,23 @@ class OptionsManager {
         const optionName = resolvedNested.optionName;
         const isCollectionItem = resolvedNested.isCollectionItem;
 
+        let optionFullName = optionName;
+        if (ownerFullName) {
+            optionFullName = `${ownerFullName}.${optionFullName}`;
+        }
+
         const option = ensureNestedOption(
             optionName,
+            optionFullName,
             owningCollection,
             nestedOptionClass.type.DefaultsProps,
             nestedOptionClass.type.TemplateProps,
             isCollectionItem
         );
 
-        const optionFullName = this.buildFullOptionName(
-            optionName,
-            isCollectionItem ? option.elementEntries.length : undefined,
-            ownerFullName
+        const optionItemName = this.buildOptionItemName(
+            option,
+            isCollectionItem ? option.elementEntries.length : undefined
         );
 
         const nestedOptionMeta: INestedOptionMeta = {
@@ -286,7 +292,7 @@ class OptionsManager {
                     c,
                     nestedOptionClass.type.ExpectedChildren,
                     nestedOptionsCollection,
-                    optionFullName
+                    optionItemName
                 );
             },
             updateFunc: (newProps, prevProps) => {
@@ -294,8 +300,8 @@ class OptionsManager {
                     nestedOptionClass.type.DefaultsProps,
                     nestedOptionClass.type.TemplateProps).options;
                 this.processChangedValues(
-                    addPrefixToKeys(newOptions, optionFullName + "."),
-                    addPrefixToKeys(prevProps, optionFullName + ".")
+                    addPrefixToKeys(newOptions, optionItemName + "."),
+                    addPrefixToKeys(prevProps, optionItemName + ".")
                 );
             },
             makeDirty: () => {
@@ -316,18 +322,14 @@ class OptionsManager {
         return optionComponent;
     }
 
-    private buildFullOptionName(optionName: string, index?: number, ownerName?: string, ): string {
-        let optionFullName = optionName;
+    private buildOptionItemName(descr: INestedConfigDescr, index?: number): string {
+        let optionItemName = descr.optionFullName;
 
         if (index !== undefined) {
-            optionFullName += `[${index}]`;
-        }
-
-        if (ownerName) {
-            optionFullName = `${ownerName}.${optionFullName}`;
+            optionItemName += `[${index}]`;
         }
 
-        return optionFullName;
+        return optionItemName;
     }
 
     private _setGuard(optionName: string, optionValue: any): void {
@@ -347,6 +349,7 @@ class OptionsManager {
 
 function ensureNestedOption(
     optionName: string,
+    optionFullName: string,
     optionsCollection: Record<string, INestedConfigDescr>,
     defaults: Record<string, any>,
     templates: ITemplateMeta[],
@@ -358,6 +361,7 @@ function ensureNestedOption(
     ) {
         optionsCollection[optionName] = {
             optionName,
+            optionFullName,
             defaults,
             templates,
             elementEntries: [],


Test patch:
diff --git a/src/core/__tests__/template.test.tsx b/src/core/__tests__/template.test.tsx
index 2bb37fd..6435ab4 100644
--- a/src/core/__tests__/template.test.tsx
+++ b/src/core/__tests__/template.test.tsx
@@ -469,6 +469,12 @@ describe("component/render in nested options", () => {
             <TestComponent>
                 <CollectionNestedComponent render={UserTemplate} />
                 <CollectionNestedComponent render={UserTemplate} />
+                <CollectionNestedComponent>
+                    <NestedComponent itemRender={UserTemplate}/>
+                </CollectionNestedComponent>
+                <NestedComponent>
+                    <CollectionNestedComponent render={UserTemplate} />
+                </NestedComponent>
             </TestComponent>
         );
 
@@ -476,10 +482,17 @@ describe("component/render in nested options", () => {
 
         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;
 
-        expect(Object.keys(integrationOptions.templates)).toEqual(["collection[0].template", "collection[1].template"]);
+        expect(Object.keys(integrationOptions.templates)).toEqual([
+            "collection[0].template",
+            "collection[1].template",
+            "collection[2].option.item",
+            "option.collection[0].template"
+        ]);
     });
 
     it("pass integrationOptions for collection nested component with 'template' option if a child defined", () => {