Repository: DevExpress/devextreme-react
Pull request #111 (2018-05-29 18:05:42)

Implement generating default props for nested options


Patch:
diff --git a/example/data-grid-example.tsx b/example/data-grid-example.tsx
index 4733981..88ac327 100644
--- a/example/data-grid-example.tsx
+++ b/example/data-grid-example.tsx
@@ -7,13 +7,13 @@ import { NumberBox } from "../src/ui/number-box";
 
 import { sales } from "./data";
 
-export default class extends React.Component<any, { expandAll: boolean, pageIndex: number }> {
+export default class extends React.Component<any, { expandAll: boolean, pageSize: number }> {
 
     constructor(props: any) {
         super(props);
         this.state = {
             expandAll: true,
-            pageIndex: 1
+            pageSize: 5
         };
 
         this.handleExpandAllChange = this.handleExpandAllChange.bind(this);
@@ -42,8 +42,8 @@ export default class extends React.Component<any, { expandAll: boolean, pageInde
                     filterRow={{ visible: true }}
                 >
                     <Paging
-                        pageSize={5}
-                        pageIndex={this.state.pageIndex}
+                        pageSize={this.state.pageSize}
+                        defaultPageIndex={2}
                     />
 
                     <Column dataField="orderId" caption="Order ID" width={90} />
@@ -64,7 +64,7 @@ export default class extends React.Component<any, { expandAll: boolean, pageInde
                 <br/>
                 <NumberBox
                     showSpinButtons={true}
-                    value={this.state.pageIndex}
+                    value={this.state.pageSize}
                     onValueChanged={this.handlePageIndexChange}
                 />
             </Example>
@@ -78,7 +78,7 @@ export default class extends React.Component<any, { expandAll: boolean, pageInde
     }
     private handlePageIndexChange(e: any) {
         this.setState({
-            pageIndex: e.value
+            pageSize: e.value
         });
     }
 }
diff --git a/tools/src/component-generator.ts b/tools/src/component-generator.ts
index 69e8dd6..eab45bb 100644
--- a/tools/src/component-generator.ts
+++ b/tools/src/component-generator.ts
@@ -16,7 +16,7 @@ interface INestedComponent {
     className: string;
     optionName: string;
     owner: string;
-    nested: IOption[];
+    options: IOption[];
     isCollectionItem?: boolean;
 }
 
@@ -24,6 +24,7 @@ interface IOption {
     name: string;
     type?: string;
     nested?: IOption[];
+    isSubscribable?: boolean;
 }
 
 interface IPropTyping {
@@ -55,14 +56,34 @@ function generate(component: IComponent): string {
     const nestedComponents = component.nestedComponents
         ? component.nestedComponents
             .sort(createKeyComparator<INestedComponent>((o) => o.optionName))
-            .map((c) => ({
-                className: c.className,
-                optionName: c.optionName,
-                ownerName: c.owner,
-                interfaceName: `I${uppercaseFirst(c.optionName)}Options`,
-                rendered: renderObject(c.nested, 0),
-                isCollectionItem: c.isCollectionItem
-            }))
+            .map((c) => {
+                const options = [...c.options];
+                const nestedSubscribableOptions = options.filter((o) => o.isSubscribable);
+                let renderedSubscribableOptions = null;
+                if (isNotEmptyArray(nestedSubscribableOptions)) {
+
+                    options.push(...nestedSubscribableOptions.map((o) => ({
+                        ...o,
+                        name: `default${uppercaseFirst(o.name)}`
+                    })));
+
+                    renderedSubscribableOptions = nestedSubscribableOptions.map((o) =>
+                        renderObjectEntry({
+                            key: `default${uppercaseFirst(o.name)}`,
+                            value: o.name
+                        })
+                    );
+                }
+                return {
+                    className: c.className,
+                    optionName: c.optionName,
+                    ownerName: c.owner,
+                    interfaceName: `I${uppercaseFirst(c.optionName)}Options`,
+                    rendered: renderObject(options, 0),
+                    renderedSubscribableOptions,
+                    isCollectionItem: c.isCollectionItem
+                };
+            })
         : null;
 
     const optionsName = `I${component.name}Options`;
@@ -78,7 +99,6 @@ function generate(component: IComponent): string {
         baseComponentPath: component.baseComponentPath,
         configComponentPath: component.configComponentPath,
         dxExportPath: component.dxExportPath,
-
         widgetName: `dx${uppercaseFirst(component.name)}`,
         optionsName,
         hasExtraOptions: !!templates || !!subscribableOptions,
@@ -126,6 +146,7 @@ function createPropTypingModel(typing: IPropTyping): IRenderedPropTyping {
     };
 }
 
+// tslint:disable:max-line-length
 const renderComponent: (model: {
     className: string;
     widgetName: string;
@@ -140,9 +161,9 @@ const renderComponent: (model: {
         renderedProp: string;
     }>;
     subscribableOptions: Array<{
-        name: string,
-        type: string,
-        renderedProp: string
+        name: string;
+        type: string;
+        renderedProp: string;
     }>;
     nestedComponents: Array<{
         optionName: string;
@@ -150,6 +171,7 @@ const renderComponent: (model: {
         ownerName: string;
         interfaceName: string;
         rendered: string;
+        renderedSubscribableOptions: string;
         isCollectionItem: boolean;
     }>;
     renderedPropTypings: string[],
@@ -190,7 +212,9 @@ class <#= it.className #> extends BaseComponent<<#= it.optionsName #>> {
 class <#= nested.className #> extends NestedOption<<#= nested.rendered #>> {<#? nested.isCollectionItem #>
   public static IsCollectionItem = true;<#?#>
   public static OwnerType = <#= nested.ownerName #>;
-  public static OptionName = "<#= nested.optionName #>";
+  public static OptionName = "<#= nested.optionName #>";<#? nested.renderedSubscribableOptions #>
+  public static DefaultsProps = {<#= nested.renderedSubscribableOptions.join(',') #>
+  };<#?#>
 }<#~#>
 <#?#>
 export {
diff --git a/tools/src/generator.ts b/tools/src/generator.ts
index 9687de7..c75b656 100644
--- a/tools/src/generator.ts
+++ b/tools/src/generator.ts
@@ -107,7 +107,7 @@ function extractNestedComponents(
       owner,
       className,
       optionName: p.name,
-      nested: nestedProps.map(mapOption),
+      options: nestedProps.map(mapOption),
       isCollectionItem
     });
 
@@ -152,7 +152,8 @@ function createPropTyping(option: IProp): IPropTyping {
 function mapOption(prop: IProp): IOption {
   const result: IOption = {
     name: prop.name,
-    type: "any"
+    type: "any",
+    isSubscribable: prop.isSubscribable
   };
 
   if (isNotEmptyArray(prop.props)) {


Test patch:
diff --git a/tools/src/component-generator.test.ts b/tools/src/component-generator.test.ts
index 145e76f..38fca8a 100644
--- a/tools/src/component-generator.test.ts
+++ b/tools/src/component-generator.test.ts
@@ -336,7 +336,7 @@ export {
                         className: "Opt_1_Component",
                         owner: "CLASS_NAME",
                         optionName: "opt_1",
-                        nested: [
+                        options: [
                             {
                                 name: "sub_opt_2",
                                 type: "TYPE_1"
@@ -361,12 +361,11 @@ export {
                             }
                         ]
                     },
-
                     {
                         className: "Opt_6_SubComponent",
                         owner: "Opt_1_Component",
                         optionName: "sub_sub_opt_7",
-                        nested: [
+                        options: [
                             {
                                 name: "sub_sub_sub_opt_8",
                                 type: "TYPE_4"
@@ -434,19 +433,18 @@ export {
                         owner: "CLASS_NAME",
                         optionName: "opt_1",
                         isCollectionItem: true,
-                        nested: [
+                        options: [
                             {
                                 name: "sub_opt_2",
                                 type: "TYPE_1"
                             }
                         ]
                     },
-
                     {
                         className: "Opt_6_SubComponent",
                         owner: "Opt_1_Component",
                         optionName: "sub_sub_opt_7",
-                        nested: [
+                        options: [
                             {
                                 name: "sub_sub_sub_opt_8",
                                 type: "TYPE_4"
@@ -457,6 +455,83 @@ export {
             })
         ).toBe(EXPECTED);
     });
+
+    it("gerates default props for nested options", () => {
+        //#region EXPECTED
+        const EXPECTED = `
+import dxCLASS_NAME, {
+    IOptions as ICLASS_NAMEOptions
+} from "devextreme/DX/WIDGET/PATH";
+
+import BaseComponent from "BASE_COMPONENT_PATH";
+import NestedOption from "CONFIG_COMPONENT_PATH";
+
+class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
+
+  public get instance(): dxCLASS_NAME {
+    return this._instance;
+  }
+
+  protected _WidgetClass = dxCLASS_NAME;
+}
+// tslint:disable:max-classes-per-file
+
+class Opt_1_Component extends NestedOption<{
+  sub_opt_2?: TYPE_1;
+  sub_opt_3?: {
+    sub_sub_opt_4?: TYPE_2;
+  };
+  defaultSub_opt_2?: TYPE_1;
+}> {
+  public static OwnerType = CLASS_NAME;
+  public static OptionName = "opt_1";
+  public static DefaultsProps = {
+    defaultSub_opt_2: "sub_opt_2"
+  };
+}
+
+export {
+  CLASS_NAME,
+  ICLASS_NAMEOptions,
+  Opt_1_Component
+};
+`.trimLeft();
+        //#endregion
+
+        expect(
+            generate({
+                name: "CLASS_NAME",
+                baseComponentPath: "BASE_COMPONENT_PATH",
+                configComponentPath: "CONFIG_COMPONENT_PATH",
+                dxExportPath: "DX/WIDGET/PATH",
+                nestedComponents: [
+                    {
+                        className: "Opt_1_Component",
+                        owner: "CLASS_NAME",
+                        optionName: "opt_1",
+                        options: [
+                            {
+                                name: "sub_opt_2",
+                                type: "TYPE_1",
+                                isSubscribable: true
+                            },
+                            {
+                                name: "sub_opt_3",
+                                nested: [
+                                    {
+                                        name: "sub_sub_opt_4",
+                                        type: "TYPE_2",
+                                        isSubscribable: true // should not be rendered
+                                    }
+                                ]
+                            }
+                        ]
+                    }
+                ]
+            })
+        ).toBe(EXPECTED);
+    });
+
 });
 
 describe("prop typings", () => {