Repository: DevExpress/devextreme-vue
Pull request #358 (2020-03-27 04:12:30)

Update to 19.2.7


Patch:
diff --git a/package.json b/package.json
index ba64762..8492c36 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "devextreme-vue",
-  "version": "19.2.5",
+  "version": "19.2.7",
   "description": "DevExtreme Vue UI and Visualization Components",
   "repository": {
     "type": "git",
@@ -28,7 +28,7 @@
   "author": "Developer Express Inc.",
   "license": "MIT",
   "peerDependencies": {
-    "devextreme": "~19.2.5",
+    "devextreme": "~19.2.7",
     "vue": "^2.5.16"
   },
   "devDependencies": {
@@ -38,7 +38,7 @@
     "css-loader": "^2.1.1",
     "dasherize": "^2.0.0",
     "del": "^3.0.0",
-    "devextreme": "~19.2.5",
+    "devextreme": "~19.2.7",
     "devextreme-internal-tools": "^1.0.0",
     "dot": "^1.1.2",
     "gulp": "^4.0.0",
diff --git a/src/core/component.ts b/src/core/component.ts
index 60bba7f..481f2d2 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -8,7 +8,7 @@ import Configuration, { bindOptionWatchers, setEmitOptionChangedFunc } from "./c
 import { getConfig, getInnerChanges, IConfigurable, initOptionChangedFunc } from "./configuration-component";
 import { DX_REMOVE_EVENT } from "./constants";
 import { IExtension, IExtensionComponentNode } from "./extension-component";
-import { camelize, forEachChildNode, toComparable } from "./helpers";
+import { camelize, forEachChildNode, getOptionValue, toComparable } from "./helpers";
 import {
     IEventBusHolder
 } from "./templates-discovering";
@@ -23,6 +23,7 @@ interface IWidgetComponent extends IConfigurable {
 
 interface IBaseComponent extends IVue, IWidgetComponent, IEventBusHolder {
     $_isExtension: boolean;
+    $_applyConfigurationChanges: () => void;
     $_createWidget: (element: any) => void;
     $_getIntegrationOptions: () => void;
     $_getExtraIntegrationOptions: () => void;
@@ -95,31 +96,8 @@ const BaseComponent: VueConstructor<IBaseComponent> = Vue.extend({
         }
         (this as IBaseComponent).$_pendingOptions = {};
 
-        if (this.$_config.componentsCountChanged) {
-            const options = this.$_config.getNestedOptionValues();
-            const prevOptions = this.$_config.prevNestedOptions;
-            const optionsList = Object.keys(options);
-            const prevOptionsList = Object.keys(prevOptions);
-            if (optionsList.length < prevOptionsList.length) {
-                prevOptionsList.forEach((prevName) => {
-                    const hasOption = optionsList.some((name) => {
-                        return prevName === name;
-                    });
-
-                    if (!hasOption) {
-                        this.$_instance.resetOption(prevName);
-                    }
-                });
-            }
-
-            for (const name in options) {
-                if (options.hasOwnProperty(name)) {
-                    this.$_instance.option(name, options[name]);
-                }
-            }
+        this.$_applyConfigurationChanges();
 
-            this.$_config.componentsCountChanged = false;
-        }
         this.$_instance.endUpdate();
         this.eventBus.$emit("updated");
     },
@@ -145,6 +123,19 @@ const BaseComponent: VueConstructor<IBaseComponent> = Vue.extend({
     },
 
     methods: {
+        $_applyConfigurationChanges(): void {
+            this.$_config.componentsCountChanged.forEach(({ optionPath, isCollection, removed }) => {
+                const options = this.$_config.getNestedOptionValues();
+
+                if (!isCollection && removed) {
+                    this.$_instance.resetOption(optionPath);
+                } else {
+                    this.$_instance.option(optionPath, getOptionValue(options, optionPath));
+                }
+            });
+
+            this.$_config.cleanComponentsCountChanged();
+        },
         $_createWidget(element: any): void {
             const thisComponent = this as IBaseComponent;
 
diff --git a/src/core/configuration-component.ts b/src/core/configuration-component.ts
index 4cec07c..35b8de4 100644
--- a/src/core/configuration-component.ts
+++ b/src/core/configuration-component.ts
@@ -20,6 +20,12 @@ interface IConfigurable extends IConfigurationOwner {
     $_innerChanges: any;
 }
 
+interface IComponentInfo {
+    optionPath: string;
+    isCollection: boolean;
+    removed?: boolean;
+}
+
 function getConfig(vueInstance: Pick<IVue, "$vnode">): Configuration | undefined {
     if (!vueInstance.$vnode) {
         return;
@@ -49,6 +55,17 @@ function initOptionChangedFunc(config, vueInstance: Pick<IVue, "$vnode" | "$prop
     setEmitOptionChangedFunc(config, vueInstance, innerChanges);
 }
 
+function getComponentInfo({name, isCollectionItem, ownerConfig }: Configuration, removed?: boolean): IComponentInfo {
+    const parentPath =  ownerConfig && ownerConfig.fullPath;
+    const optionPath = name && parentPath ? `${parentPath}.${name}` : name || "";
+
+    return {
+        optionPath,
+        isCollection: isCollectionItem,
+        removed
+    };
+}
+
 const DxConfiguration: VueConstructor = Vue.extend({
     beforeMount() {
         const config = getConfig(this) as Configuration;
@@ -59,12 +76,14 @@ const DxConfiguration: VueConstructor = Vue.extend({
 
     mounted() {
         if ((this.$parent as any).$_instance) {
-            (this.$parent as any).$_config.componentsCountChanged = true;
+            (this.$parent as any).$_config.componentsCountChanged
+                .push(getComponentInfo(getConfig(this) as Configuration));
         }
     },
 
     beforeDestroy() {
-        (this.$parent as any).$_config.componentsCountChanged = true;
+        (this.$parent as any).$_config.componentsCountChanged
+            .push(getComponentInfo(getConfig(this) as Configuration, true));
     },
 
     render(createElement: (...args) => VNode): VNode {
@@ -72,4 +91,12 @@ const DxConfiguration: VueConstructor = Vue.extend({
     }
 });
 
-export { DxConfiguration, IConfigurable, IConfigurationComponent, initOptionChangedFunc, getConfig, getInnerChanges };
+export {
+    DxConfiguration,
+    IComponentInfo,
+    IConfigurable,
+    IConfigurationComponent,
+    initOptionChangedFunc,
+    getConfig,
+    getInnerChanges
+};
diff --git a/src/core/configuration.ts b/src/core/configuration.ts
index 726fabe..bb42a2b 100644
--- a/src/core/configuration.ts
+++ b/src/core/configuration.ts
@@ -1,4 +1,5 @@
 import { Vue } from "vue/types/vue";
+import { IComponentInfo } from "./configuration-component";
 import { getOptionInfo, isEqual } from "./helpers";
 
 type UpdateFunc = (name: string, value: any) => void;
@@ -28,7 +29,7 @@ class Configuration {
     private _nestedConfigurations: Configuration[];
     private _prevNestedConfigOptions: any;
     private _emitOptionChanged: EmitOptionChangedFunc;
-    private _componentsCountChanged: boolean;
+    private _componentChanges: IComponentInfo[];
 
     private _options: string[];
 
@@ -49,7 +50,7 @@ class Configuration {
         this._collectionItemIndex = collectionItemIndex;
         this._expectedChildren = expectedChildren || {};
         this._ownerConfig = ownerConfig;
-        this._componentsCountChanged = false;
+        this._componentChanges = [];
 
         this.updateValue = this.updateValue.bind(this);
     }
@@ -64,12 +65,12 @@ class Configuration {
             : this._name;
     }
 
-    public get hasOptionsToUpdate(): boolean {
-        return this._componentsCountChanged;
+    public get componentsCountChanged(): IComponentInfo[] {
+        return this._componentChanges;
     }
 
-    public set hasOptionsToUpdate(value: boolean) {
-        this._componentsCountChanged = value;
+    public cleanComponentsCountChanged() {
+        this._componentChanges = [];
     }
 
     public get fullPath(): string | null {
@@ -78,6 +79,10 @@ class Configuration {
             : this.fullName;
     }
 
+    public get ownerConfig(): Pick<Configuration, "fullPath"> | undefined {
+        return this._ownerConfig;
+    }
+
     public get options(): string[] {
         return this._options;
     }
diff --git a/src/core/helpers.ts b/src/core/helpers.ts
index 51b2edd..a3f1f61 100644
--- a/src/core/helpers.ts
+++ b/src/core/helpers.ts
@@ -49,6 +49,19 @@ export function allKeysAreEqual(obj1: object, obj2: object) {
     return true;
 }
 
+export function getOptionValue(options, optionPath) {
+    let value = options;
+
+    optionPath.split(".").forEach((p) => {
+        const optionInfo = getOptionInfo(p);
+        value = optionInfo.isCollection ?
+            value[optionInfo.name] && value[optionInfo.name][optionInfo.index] :
+            value[optionInfo.name];
+    });
+
+    return value;
+}
+
 export function getOptionInfo(name: string): IOptionInfo | ICollectionOptionInfo {
     const parts = name.split("[");
 
diff --git a/src/core/templates-discovering.ts b/src/core/templates-discovering.ts
index 8d1d18b..f31b916 100644
--- a/src/core/templates-discovering.ts
+++ b/src/core/templates-discovering.ts
@@ -90,6 +90,10 @@ function mountTemplate(
             }
 
             return content[0];
+        },
+        destroyed() {
+            // T857821
+            (this as IEventBusHolder).eventBus.$off("updated");
         }
     });
 }
diff --git a/tools/integration-data.json b/tools/integration-data.json
index 665dc1f..708b435 100644
--- a/tools/integration-data.json
+++ b/tools/integration-data.json
@@ -1,6 +1,6 @@
 {
   "meta": {
-    "docsRevision": "df6745ff68eb9cd9d25fc3860f846061c3663361"
+    "docsRevision": "4e93f0ba90120d71bf8d5a8bf0975a2217544d3a"
   },
   "widgets": [
     {
@@ -47613,6 +47613,14 @@
             }
           ]
         },
+        {
+          "name": "openOnFieldClick",
+          "types": [
+            {
+              "type": "Boolean"
+            }
+          ]
+        },
         {
           "name": "placeholder",
           "types": [
@@ -47645,6 +47653,14 @@
             }
           ]
         },
+        {
+          "name": "showDropDownButton",
+          "types": [
+            {
+              "type": "Boolean"
+            }
+          ]
+        },
         {
           "name": "stylingMode",
           "types": [
@@ -52018,6 +52034,12 @@
           "types": [
             {
               "type": "Boolean"
+            },
+            {
+              "type": "String",
+              "acceptableValues": [
+                "\"auto\""
+              ]
             }
           ]
         },
@@ -52426,6 +52448,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -56290,6 +56318,12 @@
                       "type": "Number"
                     }
                   ]
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -56330,6 +56364,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -56380,6 +56420,12 @@
             },
             {
               "type": "Object"
+            },
+            {
+              "type": "String",
+              "acceptableValues": [
+                "\"auto\""
+              ]
             }
           ],
           "props": [
@@ -57585,6 +57631,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -67829,6 +67881,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -68323,6 +68381,12 @@
                       "type": "Number"
                     }
                   ]
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -68363,6 +68427,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -71283,6 +71353,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -91269,7 +91345,8 @@
           "name": "contextMenuItem",
           "optionName": "items",
           "owners": [
-            "contextMenu"
+            "contextMenu",
+            "item"
           ],
           "props": [
             {
@@ -91319,7 +91396,7 @@
                   "type": "Array",
                   "itemTypes": [
                     {
-                      "type": "dxContextMenuItem",
+                      "type": "dxFileManagerContextMenuItem",
                       "isCustomType": true
                     }
                   ]
@@ -91577,6 +91654,7 @@
           "optionName": "items",
           "owners": [
             "contextMenu",
+            "item",
             "toolbar"
           ],
           "props": [
@@ -91627,7 +91705,7 @@
                   "type": "Array",
                   "itemTypes": [
                     {
-                      "type": "dxContextMenuItem",
+                      "type": "dxFileManagerContextMenuItem",
                       "isCustomType": true
                     }
                   ]
@@ -109442,14 +109520,6 @@
             }
           ]
         },
-        {
-          "name": "rtlEnabled",
-          "types": [
-            {
-              "type": "Boolean"
-            }
-          ]
-        },
         {
           "name": "scaleType",
           "types": [
@@ -127411,6 +127481,14 @@
             }
           ]
         },
+        {
+          "name": "itemCenteringEnabled",
+          "types": [
+            {
+              "type": "Boolean"
+            }
+          ]
+        },
         {
           "name": "items",
           "types": [
@@ -146146,6 +146224,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -147121,6 +147205,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -214424,6 +214514,13 @@
         {
           "name": "height",
           "types": [
+            {
+              "type": "Function",
+              "params": [],
+              "returnValueType": {
+                "type": "Number"
+              }
+            },
             {
               "type": "Number"
             },
@@ -220427,6 +220524,12 @@
           "types": [
             {
               "type": "Boolean"
+            },
+            {
+              "type": "String",
+              "acceptableValues": [
+                "\"auto\""
+              ]
             }
           ]
         },
@@ -220728,6 +220831,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -224383,6 +224492,12 @@
                       "type": "Number"
                     }
                   ]
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -224423,6 +224538,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -224485,6 +224606,12 @@
           "types": [
             {
               "type": "Object"
+            },
+            {
+              "type": "String",
+              "acceptableValues": [
+                "\"auto\""
+              ]
             }
           ],
           "props": [
@@ -225639,6 +225766,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -236155,6 +236288,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -236557,6 +236696,12 @@
                       "type": "Number"
                     }
                   ]
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             },
@@ -236597,6 +236742,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -239492,6 +239643,12 @@
               "types": [
                 {
                   "type": "Boolean"
+                },
+                {
+                  "type": "String",
+                  "acceptableValues": [
+                    "\"auto\""
+                  ]
                 }
               ]
             }
@@ -247905,18 +248062,9 @@
           "types": [
             {
               "type": "Function",
-              "params": [
-                {
-                  "name": "item",
-                  "types": [
-                    {
-                      "type": "Object"
-                    }
-                  ]
-                }
-              ],
+              "params": [],
               "returnValueType": {
-                "type": "String"
+                "type": "void"
               }
             },
             {
@@ -248917,7 +249065,7 @@
           "name": "rootValue",
           "types": [
             {
-              "type": "Object"
+              "type": "Any"
             }
           ]
         },
@@ -273653,6 +273801,14 @@
     {
       "name": "dxDiagramItem",
       "props": [
+        {
+          "name": "dataItem",
+          "types": [
+            {
+              "type": "Object"
+            }
+          ]
+        },
         {
           "name": "id",
           "types": [
@@ -273660,6 +273816,18 @@
               "type": "String"
             }
           ]
+        },
+        {
+          "name": "itemType",
+          "types": [
+            {
+              "type": "String",
+              "acceptableValues": [
+                "\"shape\"",
+                "\"connector\""
+              ]
+            }
+          ]
         }
       ],
       "types": [
@@ -273941,7 +274109,7 @@
               "type": "Array",
               "itemTypes": [
                 {
-                  "type": "dxContextMenuItem",
+                  "type": "dxFileManagerContextMenuItem",
                   "isCustomType": true
                 }
               ]


Test patch:
diff --git a/src/core/component.test.ts b/src/core/component.test.ts
index 0cf9c3d..db3e106 100644
--- a/src/core/component.test.ts
+++ b/src/core/component.test.ts
@@ -841,15 +841,85 @@ describe("nested option", () => {
     });
 
     it("remove nested component by condition", (done) => {
+        const nestedCollectionItem = buildTestConfigCtor();
+        (nestedCollectionItem as any as IConfigurationComponent).$_optionName = "nestedOption";
+        (nestedCollectionItem as any as IConfigurationComponent).$_isCollectionItem = true;
         const vm = new Vue({
             template:
                 `<test-component>` +
-                `  <nested v-if="showNest" :prop1="123" />` +
-                `  <nested :prop1="321" />` +
+                `  <nested-collection-item v-if="showNest" :prop1="123" />` +
+                `  <nested-collection-item :prop1="321" />` +
                 `</test-component>`,
             components: {
                 TestComponent,
-                Nested
+                nestedCollectionItem
+            },
+            data: {
+                showNest: true
+            }
+        }).$mount();
+
+        vm.$data.showNest = false;
+
+        Vue.nextTick(() => {
+            expect(Widget.option).toHaveBeenCalledWith("nestedOption", [{ prop1: 321 }]);
+            done();
+        });
+    });
+
+    it("should update only part of collection components", (done) => {
+        const nestedCollectionItem = buildTestConfigCtor();
+        (nestedCollectionItem as any as IConfigurationComponent).$_optionName = "nestedOption";
+        (nestedCollectionItem as any as IConfigurationComponent).$_isCollectionItem = true;
+        const vm = new Vue({
+            template:
+                `<test-component>` +
+                `  <nested-collection-item>` +
+                `     <nested-collection-item>` +
+                `       <nested-collection-item v-if="showNest" :prop1="123">` +
+                `       </nested-collection-item>` +
+                `       <nested-collection-item :prop1="321">` +
+                `       </nested-collection-item>` +
+                `     </nested-collection-item>` +
+                `  </nested-collection-item>` +
+                `</test-component>`,
+            components: {
+                TestComponent,
+                nestedCollectionItem
+            },
+            data: {
+                showNest: true
+            }
+        }).$mount();
+
+        vm.$data.showNest = false;
+
+        Vue.nextTick(() => {
+            expect(Widget.option)
+                .toHaveBeenCalledWith("nestedOption[0].nestedOption[0].nestedOption", [{ prop1: 321 }]);
+            done();
+        });
+    });
+
+    it("should update only part of collection components (remove all subnested)", (done) => {
+        const nestedCollectionItem = buildTestConfigCtor();
+        (nestedCollectionItem as any as IConfigurationComponent).$_optionName = "nestedOption";
+        (nestedCollectionItem as any as IConfigurationComponent).$_isCollectionItem = true;
+        const vm = new Vue({
+            template:
+                `<test-component>` +
+                `  <nested-collection-item>` +
+                `     <nested-collection-item>` +
+                `       <nested-collection-item v-if="showNest" :prop1="123">` +
+                `       </nested-collection-item>` +
+                `       <nested-collection-item v-if="showNest" :prop1="321">` +
+                `       </nested-collection-item>` +
+                `     </nested-collection-item>` +
+                `  </nested-collection-item>` +
+                `</test-component>`,
+            components: {
+                TestComponent,
+                nestedCollectionItem
             },
             data: {
                 showNest: true
@@ -859,7 +929,7 @@ describe("nested option", () => {
         vm.$data.showNest = false;
 
         Vue.nextTick(() => {
-            expect(Widget.option).toHaveBeenCalledWith("nestedOption", { prop1: 321 });
+            expect(Widget.option).toHaveBeenCalledWith("nestedOption[0].nestedOption[0].nestedOption", undefined);
             done();
         });
     });
@@ -1371,6 +1441,34 @@ describe("template", () => {
         expect(vm.$children[0].$children.length).toEqual(0);
     });
 
+    it("destroyed component should remove subscriptions", (done) => {
+        const vm = new Vue({
+            template: `<test-component :prop1="value">
+                            <template #item="{data}">Template {{data.text}}</template>
+                        </test-component>`,
+            components: {
+                TestComponent
+            },
+            props: ["value"],
+            propsData: {
+                value: 123
+            }
+        }).$mount();
+
+        const container = document.createElement("div");
+        renderItemTemplate({ text: "with data" }, container);
+        events.triggerHandler(container.children[0], "dxremove");
+        renderItemTemplate({ text: "with data" }, container);
+
+        const subscriptions = (vm.$children[0] as any).eventBus._events;
+
+        for (const subscription of subscriptions) {
+            expect(subscription.length).toBe(1);
+        }
+
+        done();
+    });
+
     describe("with DOM", () => {
         let fixture;
 
diff --git a/src/core/helpers.test.ts b/src/core/helpers.test.ts
index a988d25..e9e918d 100644
--- a/src/core/helpers.test.ts
+++ b/src/core/helpers.test.ts
@@ -1,4 +1,4 @@
-import { allKeysAreEqual, getOptionInfo, isEqual, toComparable } from "./helpers";
+import { allKeysAreEqual, getOptionInfo, getOptionValue, isEqual, toComparable } from "./helpers";
 
 describe("toComparable", () => {
 
@@ -70,6 +70,69 @@ describe("allKeysAreEqual", () => {
     });
 });
 
+describe("getOptionValue", () => {
+    it("returns for simple option", () => {
+        const optionValue = getOptionValue({ test: "text" }, "test");
+
+        expect(optionValue).toEqual("text");
+    });
+
+    it("returns for complex option", () => {
+        const optionValue = getOptionValue({ test: { value: "text" } }, "test");
+        const optionValue1 = getOptionValue({ test: { value: "text" } }, "test.value");
+
+        expect(optionValue).toEqual({ value: "text" });
+        expect(optionValue1).toEqual("text");
+    });
+
+    it("returns for complex option", () => {
+        const optionValue = getOptionValue({ test: { value: "text" } }, "test");
+
+        expect(optionValue).toEqual({ value: "text" });
+    });
+
+    it("returns for collection option", () => {
+        const value = [
+            { text: "value1"},
+            { text: "value2"},
+            { text: "value3",
+              test: [{
+               option: {
+                   text: "value1"
+               }
+            }, {
+               text: "value2"
+            }] }
+        ];
+
+        const optionValue1 = getOptionValue({ test: value }, "test[1]");
+        const optionValue2 = getOptionValue({ test: value }, "test");
+        const optionValue3 = getOptionValue({ test: value }, "test[2].test[1]");
+        const optionValue4 = getOptionValue({ test: value }, "test[2].test");
+        const optionValue5 = getOptionValue({ test: value }, "test[2].test[0].option");
+        const optionValue6 = getOptionValue({ test: value }, "test[2].test[0].option.text");
+
+        expect(optionValue1).toEqual({ text: "value2" });
+        expect(optionValue2).toEqual(value);
+        expect(optionValue3).toEqual({ text: "value2" });
+        expect(optionValue4).toEqual([{
+            option: { text: "value1" }
+         }, {
+            text: "value2"
+         }]);
+        expect(optionValue5).toEqual({ text: "value1" });
+        expect(optionValue6).toEqual("value1");
+    });
+
+    it("returns for empty", () => {
+        const optionValue = getOptionValue({}, "test");
+        const optionValue2 = getOptionValue({ test: [{ text: "value1" }] }, "test[1]");
+
+        expect(optionValue).toEqual(undefined);
+        expect(optionValue2).toEqual(undefined);
+    });
+});
+
 describe("getOptionInfo", () => {
     it("returns for simple option", () => {
         const optionInfo = getOptionInfo("test");