Repository: DevExpress/devextreme-vue (ts)

Issue:
TextBox - The valueChanged event does not fire inside DOM templates
Can be reproduced [here](https://codesandbox.io/s/y314q0k4yx). 

For those who encounter the same issue, use the following workaround:

`<dx-text-box :value="myValue" :on-value-changed="onValueChanged">
</dx-text-box>`

Pull request:
Fix events emitting for the kebab-case
Fixes #77.

Patch:
diff --git a/example/components/text-box-example.vue b/example/components/text-box-example.vue
index 974dcb3..9e8c73d 100644
--- a/example/components/text-box-example.vue
+++ b/example/components/text-box-example.vue
@@ -13,12 +13,18 @@
         valueChangeEvent="input"
       />
 
-      <h5 class="text-box-label">valueChanged</h5>
+      <h5 class="text-box-label">@valueChanged</h5>
       <dx-text-box
         @valueChanged="text = $event.value"
         valueChangeEvent="input"
       />
 
+      <h5 class="text-box-label">@value-changed</h5>
+      <dx-text-box
+        @value-changed="text = $event.value"
+        valueChangeEvent="input"
+      />
+
       <h5 class="text-box-label">:value.sync</h5>
       <dx-text-box
         :value.sync="text"
diff --git a/src/core/component.ts b/src/core/component.ts
index cc9822c..4ed9b2b 100644
--- a/src/core/component.ts
+++ b/src/core/component.ts
@@ -3,6 +3,8 @@ import { VNode, VueConstructor } from "vue";
 
 import * as events from "devextreme/events";
 
+import { camelize } from "./helpers";
+
 const DX_TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper";
 const DX_REMOVE_EVENT = "dxremove";
 
@@ -90,9 +92,10 @@ const DxComponent: VueConstructor = Vue.extend({
         },
 
         $_createEmitters(instance: any): void {
-            Object.keys(this.$listeners).forEach((eventName: string) => {
+            Object.keys(this.$listeners).forEach((listenerName: string) => {
+                const eventName = camelize(listenerName);
                 instance.on(eventName, (e: any) => {
-                    this.$emit(eventName, e);
+                    this.$emit(listenerName, e);
                 });
             });
         }
diff --git a/src/core/helpers.ts b/src/core/helpers.ts
new file mode 100644
index 0000000..2d35b66
--- /dev/null
+++ b/src/core/helpers.ts
@@ -0,0 +1,11 @@
+export function uppercaseFirst(value: string): string {
+    return value[0].toUpperCase() + value.substr(1);
+}
+
+export function lowercaseFirst(value: string): string {
+    return value[0].toLowerCase() + value.substr(1);
+}
+
+export function camelize(value: string): string {
+    return lowercaseFirst(value.split("-").map((v) => uppercaseFirst(v)).join(""));
+}


Test patch:
diff --git a/src/core/component.test.ts b/src/core/component.test.ts
index e61d4ef..9619a19 100644
--- a/src/core/component.test.ts
+++ b/src/core/component.test.ts
@@ -199,7 +199,7 @@ describe("template", () => {
 
 describe("events emitting", () => {
 
-    it("forwards DevExtreme events", () => {
+    it("forwards DevExtreme events in camelCase", () => {
         const expectedArgs = {};
         const parent = new Vue({
             template: "<TestComponent v-on:testEventName=''></TestComponent>",
@@ -213,6 +213,21 @@ describe("events emitting", () => {
         expect($emitSpy.mock.calls[0][0]).toBe("testEventName");
         expect($emitSpy.mock.calls[0][1]).toBe(expectedArgs);
     });
+
+    it("forwards DevExtreme events in kebab-case", () => {
+        const expectedArgs = {};
+        const parent = new Vue({
+            template: "<TestComponent v-on:test-event-name=''></TestComponent>",
+            components: { TestComponent }
+        }).$mount();
+        const $emitSpy = jest.spyOn(parent.$children[0], "$emit");
+
+        Widget.fire("testEventName", expectedArgs);
+
+        expect($emitSpy).toHaveBeenCalledTimes(1);
+        expect($emitSpy.mock.calls[0][0]).toBe("test-event-name");
+        expect($emitSpy.mock.calls[0][1]).toBe(expectedArgs);
+    });
 });
 
 describe("disposing", () => {