Repository: DevExpress/devextreme-react
Pull request #161 (2018-07-19 15:53:19)

Support setting several collection item templates


Patch:
diff --git a/example/data-grid-example.tsx b/example/data-grid-example.tsx
index 6284a5c..9568c53 100644
--- a/example/data-grid-example.tsx
+++ b/example/data-grid-example.tsx
@@ -29,6 +29,10 @@ const CityComponent = (props: any) => {
     return <i>{props.displayValue}</i>;
 };
 
+const RegionComponent = (props: any) => {
+    return <b>{props.displayValue}</b>;
+};
+
 export default class extends React.Component<any, { expandAll: boolean, pageSize: number }> {
 
     constructor(props: any) {
@@ -74,7 +78,7 @@ export default class extends React.Component<any, { expandAll: boolean, pageSize
                     <Column dataField="orderId" caption="Order ID" width={90} />
                     <Column dataField="city" cellComponent={CityComponent}/>
                     <Column dataField="country" groupIndex={0} width={180} />
-                    <Column dataField="region" />
+                    <Column dataField="region" cellComponent={RegionComponent}/>
                     <Column dataField="date" dataType="date" />
                     <Column dataField="amount" dataType="currency" width={90} />
 
diff --git a/src/core/options-manager.ts b/src/core/options-manager.ts
index 8d97d29..b087f2d 100644
--- a/src/core/options-manager.ts
+++ b/src/core/options-manager.ts
@@ -154,11 +154,11 @@ class OptionsManager {
                 const props = separateProps(e.element.props,
                     nestedOption.defaults,
                     nestedOption.templates);
-
                 const templateOptions = getTemplateOptions({
                     options: props.templates,
                     nestedOptions: {},
                     templateProps: nestedOption.templates,
+                    ownerName: `${nestedOption.name}${nestedOption.isCollectionItem ? `[${index}]` : ""}`,
                     stateUpdater,
                     propsGetter: (prop) => nestedOption.elementEntries[index].element.props[prop]
                 });
diff --git a/src/core/template-helper.ts b/src/core/template-helper.ts
index 1809ab8..dac753b 100644
--- a/src/core/template-helper.ts
+++ b/src/core/template-helper.ts
@@ -31,6 +31,7 @@ interface IIntegrationDescr {
     options: Record<string, any>;
     nestedOptions: Record<string, any>;
     templateProps: ITemplateMeta[];
+    ownerName?: string;
     propsGetter: PropsGetter;
     stateUpdater: StateUpdater;
 }
@@ -45,20 +46,22 @@ function getTemplateOptions(meta: IIntegrationDescr): {
     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: m.tmplOption,
+            name,
             propName: options[m.component] ? m.component : m.render,
             isComponent: !!options[m.component],
             isNested: false,
             propsGetter: meta.propsGetter
         };
-        templateStubs[m.tmplOption] = m.tmplOption;
-        templates[m.tmplOption] = wrapTemplate(templateDescr, stateUpdater);
+        templateStubs[m.tmplOption] = name;
+        templates[name] = wrapTemplate(templateDescr, stateUpdater);
     });
 
     const nestedOptions = meta.nestedOptions;


Test patch:
diff --git a/src/core/__tests__/template.test.tsx b/src/core/__tests__/template.test.tsx
index ff6a77e..593a744 100644
--- a/src/core/__tests__/template.test.tsx
+++ b/src/core/__tests__/template.test.tsx
@@ -251,17 +251,18 @@ describe("component/render in nested options", () => {
     }
 
     // tslint:disable-next-line:max-classes-per-file
-    class AnotherNestedComponent extends ConfigurationComponent<{
-        content?: any;
-        contentRender?: any;
-        contentComponent?: any;
+    class CollectionNestedComponent extends ConfigurationComponent<{
+        template?: any;
+        render?: any;
+        component?: any;
     }> {
+        public static IsCollectionItem = true;
         public static OwnerType = TestComponent;
-        public static OptionName = "contentOption";
+        public static OptionName = "collection";
         public static TemplateProps = [{
-            tmplOption: "content",
-            render: "contentRender",
-            component: "contentComponent"
+            tmplOption: "template",
+            render: "render",
+            component: "component"
         }];
     }
 
@@ -275,14 +276,14 @@ describe("component/render in nested options", () => {
 
         const options = WidgetClass.mock.calls[0][1];
 
-        expect(options.option.item).toBe("item");
+        expect(options.option.item).toBe("optionitem");
 
         const integrationOptions = options.integrationOptions;
 
         expect(integrationOptions).toBeDefined();
         expect(integrationOptions.templates).toBeDefined();
-        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");
     });
 
     it("pass integrationOptions options to widget with several templates", () => {
@@ -290,18 +291,37 @@ describe("component/render in nested options", () => {
         mount(
             <TestComponent>
                 <NestedComponent itemComponent={UserTemplate} />
-                <AnotherNestedComponent contentRender={UserTemplate} />
+                <CollectionNestedComponent render={UserTemplate} />
+            </TestComponent>
+        );
+
+        const options = WidgetClass.mock.calls[0][1];
+
+        expect(options.option.item).toBe("optionitem");
+        expect(options.collection[0].template).toBe("collection[0]template");
+
+        const integrationOptions = options.integrationOptions;
+
+        expect(Object.keys(integrationOptions.templates)).toEqual(["optionitem", "collection[0]template"]);
+    });
+
+    it("pass integrationOptions options for collection nested components", () => {
+        const UserTemplate = () => <div>Template</div>;
+        mount(
+            <TestComponent>
+                <CollectionNestedComponent render={UserTemplate} />
+                <CollectionNestedComponent render={UserTemplate} />
             </TestComponent>
         );
 
         const options = WidgetClass.mock.calls[0][1];
 
-        expect(options.option.item).toBe("item");
-        expect(options.contentOption.content).toBe("content");
+        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(["item", "content"]);
+        expect(Object.keys(integrationOptions.templates)).toEqual(["collection[0]template", "collection[1]template"]);
     });
 
     it("renders templates", () => {
@@ -311,7 +331,7 @@ describe("component/render in nested options", () => {
                 <NestedComponent itemComponent={FirstTemplate} />
             </TestComponent >
         );
-        renderItemTemplate();
+        renderTemplate("optionitem");
         component.update();
         expect(component.find(".template").html()).toBe('<div class="template">First Template</div>');