Repository: DevExpress/devextreme-react
Pull request #211 (2018-10-04 10:06:01)

Remove strict owner check


Patch:
diff --git a/src/core/component.ts b/src/core/component.ts
index ec4c13b..3490b1a 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -110,7 +110,7 @@ abstract class ComponentBase<P extends IHtmlOptions> extends React.PureComponent
   }
 
   protected _preprocessChild(component: React.ReactElement<any>): React.ReactElement<any> {
-    return this._optionsManager.registerNestedOption(component, this) || component;
+    return this._optionsManager.registerNestedOption(component) || component;
   }
 
   protected _createWidget(element?: Element) {
diff --git a/src/core/nested-option.ts b/src/core/nested-option.ts
index 6dafce6..5b28c90 100644
--- a/src/core/nested-option.ts
+++ b/src/core/nested-option.ts
@@ -1,6 +1,6 @@
 import * as React from "react";
 
-type RegisterNestedOptionFunc = (component: React.ReactElement<any>, owner: any) => any;
+type RegisterNestedOptionFunc = (component: React.ReactElement<any>) => any;
 type UpdateFunc = (newProps, prevProps) => void;
 
 interface INestedOptionMeta {
@@ -32,7 +32,7 @@ class NestedOption<P> extends React.PureComponent<P, any> {
 
         const children: any[] = [];
         React.Children.forEach(this.props.children, (c: React.ReactElement<any>) => {
-            const processedChild = this._registerNestedOption(c, this);
+            const processedChild = this._registerNestedOption(c);
             if (processedChild) {
                 children.push(processedChild);
             }
diff --git a/src/core/options-manager.ts b/src/core/options-manager.ts
index 086dda3..ecc26a2 100644
--- a/src/core/options-manager.ts
+++ b/src/core/options-manager.ts
@@ -1,7 +1,7 @@
 import { ITemplateMeta } from "./template";
 
 import { addPrefixToKeys, getNestedValue } from "./helpers";
-import { createOptionComponent } from "./nested-option";
+import { createOptionComponent, INestedOptionMeta } from "./nested-option";
 import { getTemplateOptions } from "./template-helper";
 import { separateProps } from "./widget-config";
 
@@ -20,7 +20,6 @@ interface INestedOptionDescr {
 interface INestedOptionClass {
     type: {
         IsCollectionItem: boolean;
-        OwnerType: any;
         OptionName: string;
         DefaultsProps: Record<string, string>;
         TemplateProps: ITemplateMeta[];
@@ -139,8 +138,8 @@ class OptionsManager {
         return this._getNestedOptionsObjects(this._nestedOptions, stateUpdater);
     }
 
-    public registerNestedOption(component: React.ReactElement<any>, owner: any): any {
-        return this._registerNestedOption(component, owner, this._nestedOptions);
+    public registerNestedOption(component: React.ReactElement<any>): any {
+        return this._registerNestedOption(component, this._nestedOptions);
     }
 
     private _getNestedOptionsObjects(
@@ -194,16 +193,11 @@ class OptionsManager {
 
     private _registerNestedOption(
         element: React.ReactElement<any>,
-        owner: any,
         owningCollection: Record<string, INestedOptionDescr>,
         ownerFullName?: string
     ): any {
         const nestedOptionClass = element as any as INestedOptionClass;
-        if (!(
-            nestedOptionClass && nestedOptionClass.type &&
-            nestedOptionClass.type.OptionName &&
-            nestedOptionClass.type.OwnerType && owner instanceof nestedOptionClass.type.OwnerType
-        )) {
+        if (!(nestedOptionClass && nestedOptionClass.type && nestedOptionClass.type.OptionName)) {
             return null;
         }
 
@@ -224,24 +218,23 @@ class OptionsManager {
             optionFullName = `${ownerFullName}.${optionFullName}`;
         }
 
-        const optionComponent = createOptionComponent(
-            element,
-            {
-                optionName,
-                registerNestedOption: (c: React.ReactElement<any>, o: any) => {
-                    return this._registerNestedOption(c, o, nestedOptionsCollection, optionName);
-                },
-                updateFunc: (newProps, prevProps) => {
-                    const newOptions = separateProps(newProps,
-                        nestedOptionClass.type.DefaultsProps,
-                        nestedOptionClass.type.TemplateProps).options;
-                    this.processChangedValues(
-                        addPrefixToKeys(newOptions, optionFullName + "."),
-                        addPrefixToKeys(prevProps, optionFullName + ".")
-                    );
-                }
+        const nestedOptionMeta: INestedOptionMeta = {
+            optionName,
+            registerNestedOption: (c: React.ReactElement<any>) => {
+                return this._registerNestedOption(c, nestedOptionsCollection, optionName);
+            },
+            updateFunc: (newProps, prevProps) => {
+                const newOptions = separateProps(newProps,
+                    nestedOptionClass.type.DefaultsProps,
+                    nestedOptionClass.type.TemplateProps).options;
+                this.processChangedValues(
+                    addPrefixToKeys(newOptions, optionFullName + "."),
+                    addPrefixToKeys(prevProps, optionFullName + ".")
+                );
             }
-        );
+        };
+
+        const optionComponent = createOptionComponent(element, nestedOptionMeta);
 
         entry.elementEntries.push({
             element,
diff --git a/tools/src/component-generator.ts b/tools/src/component-generator.ts
index d0bce65..f374cd9 100644
--- a/tools/src/component-generator.ts
+++ b/tools/src/component-generator.ts
@@ -101,7 +101,8 @@ function generate(component: IComponent): string {
                     renderedSubscribableOptions,
                     renderedTemplateProps: nestedTemplates && nestedTemplates.map(renderTemplateOption),
                     isCollectionItem: c.isCollectionItem,
-                    predefinedProps
+                    predefinedProps,
+                    ownerClassName: c.ownerClassName
                 };
             })
         : null;
@@ -171,10 +172,6 @@ function generate(component: IComponent): string {
         }),
 
         renderedNestedComponents: nestedComponents && nestedComponents.map(renderNestedComponent),
-        nestedComponentLinks: nestedComponents && nestedComponents.map((c) => ({
-            className: c.className,
-            ownerClassName: c.ownerName
-        })),
 
         defaultExport: component.name,
         renderedExports: renderExports(exportNames)
@@ -213,10 +210,6 @@ const renderModule: (model: {
     renderedOptionsInterface: string;
     renderedComponent: string;
     renderedNestedComponents: string[];
-    nestedComponentLinks: Array<{
-        className: string;
-        ownerClassName: string;
-    }>;
     defaultExport: string;
     renderedExports: string;
 }) => string = createTempate(
@@ -230,12 +223,9 @@ const renderModule: (model: {
 
 `<#? it.renderedNestedComponents #>` +
     `// tslint:disable:max-classes-per-file` +
-    `<#~ it.renderedNestedComponents :nestedComponent #>` + `\n` + `\n` +
+    `<#~ it.renderedNestedComponents :nestedComponent #>` + `\n\n` +
         `<#= nestedComponent #>` +
-    `<#~#>` + `\n` + `\n` +
-    `<#~ it.nestedComponentLinks :link #>` +
-    `(<#= link.className #> as any).OwnerType = <#= link.ownerClassName #>;` + `\n` +
-    `<#~#>` + `\n` +
+    `<#~#>` + `\n\n` +
 `<#?#>` +
 
 `export default <#= it.defaultExport #>;` + `\n` +
@@ -340,7 +330,9 @@ const renderNestedComponent: (model: {
     renderedType: string;
     renderedSubscribableOptions: string[];
     renderedTemplateProps: string[];
+    ownerClassName: string;
 }) => string = createTempate(
+`// owner class name: <#= it.ownerClassName #>\n` +
 `class <#= it.className #> extends NestedOption<<#= it.renderedType #>> {` +
 L1 + `public static OptionName = "<#= it.optionName #>";` +
 


Test patch:
diff --git a/src/core/__tests__/extension-component.test.tsx b/src/core/__tests__/extension-component.test.tsx
index 2f6534f..10cf76d 100644
--- a/src/core/__tests__/extension-component.test.tsx
+++ b/src/core/__tests__/extension-component.test.tsx
@@ -16,7 +16,6 @@ class TestExtensionComponent<P = any> extends ExtensionComponent<P> {
 }
 
 class NestedComponent extends ConfigurationComponent<{ a: number }> {
-    public static OwnerType = TestExtensionComponent;
     public static OptionName = "option1";
 }
 
diff --git a/src/core/__tests__/nested-option.test.tsx b/src/core/__tests__/nested-option.test.tsx
index 2238b2d..0a23577 100644
--- a/src/core/__tests__/nested-option.test.tsx
+++ b/src/core/__tests__/nested-option.test.tsx
@@ -4,12 +4,10 @@ import { TestComponent, Widget, WidgetClass } from "./test-component";
 
 // tslint:disable:max-classes-per-file
 class NestedComponent extends ConfigurationComponent<{ a: number }> {
-    public static OwnerType = TestComponent;
     public static OptionName = "option";
 }
 
 class NestedComponentWithPredfeinedProps extends ConfigurationComponent<{ a: number }> {
-    public static OwnerType = TestComponent;
     public static OptionName = "option";
     public static PredefinedProps = {
         predefinedProp: "predefined-value"
@@ -18,7 +16,6 @@ class NestedComponentWithPredfeinedProps extends ConfigurationComponent<{ a: num
 
 class CollectionNestedWithPredfeinedProps1 extends ConfigurationComponent<{ a: number }> {
     public static IsCollectionItem = true;
-    public static OwnerType = TestComponent;
     public static OptionName = "option";
     public static PredefinedProps = {
         predefinedProp: "predefined-value-1"
@@ -27,7 +24,6 @@ class CollectionNestedWithPredfeinedProps1 extends ConfigurationComponent<{ a: n
 
 class CollectionNestedWithPredfeinedProps2 extends ConfigurationComponent<{ a: number }> {
     public static IsCollectionItem = true;
-    public static OwnerType = TestComponent;
     public static OptionName = "option";
     public static PredefinedProps = {
         predefinedProp: "predefined-value-2"
@@ -35,36 +31,27 @@ class CollectionNestedWithPredfeinedProps2 extends ConfigurationComponent<{ a: n
 }
 
 class SubNestedComponent extends ConfigurationComponent<{ d: string }> {
-    public static OwnerType = NestedComponent;
     public static OptionName = "subOption";
 }
 
 class AnotherSubNestedComponent extends ConfigurationComponent<{ e: string }> {
-    public static OwnerType = NestedComponent;
     public static OptionName = "anotherSubOption";
 }
 
 class AnotherNestedComponent extends ConfigurationComponent<{ b: string }> {
-    public static OwnerType = TestComponent;
     public static OptionName = "anotherOption";
 }
 
 class CollectionNestedComponent extends ConfigurationComponent<{ c?: number, d?: string }> {
     public static IsCollectionItem = true;
-    public static OwnerType = TestComponent;
     public static OptionName = "itemOptions";
 }
 
 class CollectionSubNestedComponent extends ConfigurationComponent<{ c?: number, d?: string }> {
     public static IsCollectionItem = true;
-    public static OwnerType = NestedComponent;
     public static OptionName = "subItemsOptions";
 }
 
-class WrongNestedComponent extends ConfigurationComponent<{ x: number }> {
-    public static OwnerType = WrongNestedComponent;
-    public static OptionName = "optionW";
-}
 // tslint:enable:max-classes-per-file
 
 describe("nested option", () => {
@@ -88,7 +75,6 @@ describe("nested option", () => {
         mount(
             <TestComponent>
                 <NestedComponent a={123} />
-                <WrongNestedComponent x={456} />
                 <AnotherNestedComponent b="abc" />
             </TestComponent>
         );
@@ -104,22 +90,6 @@ describe("nested option", () => {
         });
     });
 
-    it("isn't pulled by wrong parent", () => {
-        mount(
-            <TestComponent>
-                <NestedComponent a={123} />
-                <WrongNestedComponent x={456} />
-            </TestComponent>
-        );
-
-        expect(WidgetClass.mock.calls[0][1]).toEqual({
-            templatesRenderAsynchronously: true,
-            option: {
-                a: 123
-            }
-        });
-    });
-
     it("is pulled overriden if not a collection item", () => {
         mount(
             <TestComponent>
@@ -275,7 +245,6 @@ describe("nested sub-option", () => {
             <TestComponent>
                 <NestedComponent a={123} >
                     <SubNestedComponent d={"abc"} />
-                    <WrongNestedComponent x={456} />
                     <AnotherSubNestedComponent e={"def"} />
                 </NestedComponent>
             </TestComponent>
@@ -295,27 +264,6 @@ describe("nested sub-option", () => {
         });
     });
 
-    it("isn't pulled by wrong parent", () => {
-        mount(
-            <TestComponent>
-                <NestedComponent a={123} >
-                    <SubNestedComponent d={"abc"} />
-                    <WrongNestedComponent x={456} />
-                </NestedComponent>
-            </TestComponent>
-        );
-
-        expect(WidgetClass.mock.calls[0][1]).toEqual({
-            templatesRenderAsynchronously: true,
-            option: {
-                a: 123,
-                subOption: {
-                    d: "abc"
-                }
-            }
-        });
-    });
-
     it("is pulled overriden if not a collection item", () => {
         mount(
             <TestComponent>
diff --git a/src/core/__tests__/props-updating.test.tsx b/src/core/__tests__/props-updating.test.tsx
index 77f4a90..9e2fe1e 100644
--- a/src/core/__tests__/props-updating.test.tsx
+++ b/src/core/__tests__/props-updating.test.tsx
@@ -35,7 +35,6 @@ class NestedComponent extends ConfigurationComponent<{
     };
 } // tslint:disable-line:max-classes-per-file
 
-(NestedComponent as any).OwnerType = ControlledComponent;
 (NestedComponent as any).OptionName = "nestedOption";
 
 it("calls option method on props update", () => {
diff --git a/src/core/__tests__/template.test.tsx b/src/core/__tests__/template.test.tsx
index 59e9da2..d462da9 100644
--- a/src/core/__tests__/template.test.tsx
+++ b/src/core/__tests__/template.test.tsx
@@ -287,7 +287,6 @@ describe("component/render in nested options", () => {
         itemRender?: any;
         itemComponent?: any;
     }> {
-        public static OwnerType = TestComponent;
         public static OptionName = "option";
         public static TemplateProps = [{
             tmplOption: "item",
@@ -303,7 +302,6 @@ describe("component/render in nested options", () => {
         component?: any;
     }> {
         public static IsCollectionItem = true;
-        public static OwnerType = TestComponent;
         public static OptionName = "collection";
         public static TemplateProps = [{
             tmplOption: "template",
@@ -441,9 +439,11 @@ it("removes deleted nodes from state", () => {
     expect(Object.getOwnPropertyNames(component.state("templates")).length).toBe(1);
     component.update();
     const templateContent = component.find(".template").getDOMNode();
-    const parentNode = templateContent.parentNode;
-    parentNode!.removeChild(templateContent);
-    events.triggerHandler(parentNode, "dxremove");
+    const parentElement = templateContent.parentElement;
+    if (!parentElement) { throw new Error(); }
+
+    parentElement.removeChild(templateContent);
+    events.triggerHandler(parentElement, "dxremove");
     component.update();
     expect(Object.getOwnPropertyNames(component.state("templates")).length).toBe(0);
 });
diff --git a/tools/src/component-generator.test.ts b/tools/src/component-generator.test.ts
index 71c8d52..6f4bce1 100644
--- a/tools/src/component-generator.test.ts
+++ b/tools/src/component-generator.test.ts
@@ -347,6 +347,7 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 }
 // tslint:disable:max-classes-per-file
 
+// owner class name: CLASS_NAME
 class Opt_1_Component extends NestedOption<{
   sub_opt_2?: TYPE_1;
   sub_opt_3?: {
@@ -359,15 +360,13 @@ class Opt_1_Component extends NestedOption<{
   public static OptionName = "opt_1";
 }
 
+// owner class name: Opt_1_Component
 class Opt_6_SubComponent extends NestedOption<{
   sub_sub_sub_opt_8?: TYPE_4;
 }> {
   public static OptionName = "sub_sub_opt_7";
 }
 
-(Opt_1_Component as any).OwnerType = CLASS_NAME;
-(Opt_6_SubComponent as any).OwnerType = Opt_1_Component;
-
 export default CLASS_NAME;
 export {
   CLASS_NAME,
@@ -455,6 +454,7 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 }
 // tslint:disable:max-classes-per-file
 
+// owner class name: CLASS_NAME
 class Opt_1_Component extends NestedOption<{
   sub_opt_2?: TYPE_1;
 }> {
@@ -462,8 +462,6 @@ class Opt_1_Component extends NestedOption<{
   public static IsCollectionItem = true;
 }
 
-(Opt_1_Component as any).OwnerType = CLASS_NAME;
-
 export default CLASS_NAME;
 export {
   CLASS_NAME,
@@ -521,6 +519,7 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 }
 // tslint:disable:max-classes-per-file
 
+// owner class name: CLASS_NAME
 class Opt_1_Component extends NestedOption<{
   sub_opt_2?: TYPE_1;
   sub_opt_3?: {
@@ -534,8 +533,6 @@ class Opt_1_Component extends NestedOption<{
   };
 }
 
-(Opt_1_Component as any).OwnerType = CLASS_NAME;
-
 export default CLASS_NAME;
 export {
   CLASS_NAME,
@@ -604,6 +601,7 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 }
 // tslint:disable:max-classes-per-file
 
+// owner class name: CLASS_NAME
 class Opt_1_Component extends NestedOption<{
   optionTemplate?: TYPE_1;
   optionRender?: (props: any) => React.ReactNode;
@@ -617,8 +615,6 @@ class Opt_1_Component extends NestedOption<{
   }];
 }
 
-(Opt_1_Component as any).OwnerType = CLASS_NAME;
-
 export default CLASS_NAME;
 export {
   CLASS_NAME,
@@ -676,6 +672,7 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 }
 // tslint:disable:max-classes-per-file
 
+// owner class name: CLASS_NAME
 class Opt_1_Component extends NestedOption<{
   sub_opt_2?: TYPE_1;
 }> {
@@ -686,8 +683,6 @@ class Opt_1_Component extends NestedOption<{
   };
 }
 
-(Opt_1_Component as any).OwnerType = CLASS_NAME;
-
 export default CLASS_NAME;
 export {
   CLASS_NAME,
diff --git a/tools/src/converter.test.ts b/tools/src/converter.test.ts
index 3ef958a..6d192f3 100644
--- a/tools/src/converter.test.ts
+++ b/tools/src/converter.test.ts
@@ -52,16 +52,19 @@ it("returns undefined if array is null", () => {
 });
 
 it("expands custom types", () => {
-    expect(convertTypes([
-        { type: "CustomType", isCustomType: true, acceptableValues: [] },
-    ], {
-        CustomType: {
-            name: "CustomType",
-            types: [
-                { type: "String", isCustomType: false, acceptableValues: []},
-                { type: "Number", isCustomType: false, acceptableValues: []}
-            ],
-            props: []
+    expect(convertTypes(
+        [
+            { type: "CustomType", isCustomType: true, acceptableValues: [] },
+        ], {
+            CustomType: {
+                name: "CustomType",
+                types: [
+                    { type: "String", isCustomType: false, acceptableValues: []},
+                    { type: "Number", isCustomType: false, acceptableValues: []}
+                ],
+                props: [],
+                templates: []
+            }
         }
-    })).toEqual(["object", "string", "number"]);
+    )).toEqual(["object", "string", "number"]);
 });