Skip to content

Commit

Permalink
Fix/616 vue custom event listeners (#617)
Browse files Browse the repository at this point in the history
* fix(vue): shift event listener logic from directive to onMounted

* fix(vue): remove lower case event naming

* tests(vue): for custom event handler and v-model directive

* test(vue): include coverage for custom event handler for input component
  • Loading branch information
andrewbeng89 authored Feb 21, 2025
1 parent d52c5f7 commit e8f92eb
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 24 deletions.
9 changes: 9 additions & 0 deletions example-project/vue-app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
import HelloWorld from './components/HelloWorld.vue'
// @ts-ignore
import Input from './components/Input.vue'
import { MyComponent } from 'component-library-vue'
import { ref } from 'vue'
const isClicked = ref(false)
const handleCustomEvent = () => {
isClicked.value = true
}
</script>

<template>
Expand All @@ -14,6 +21,8 @@ import Input from './components/Input.vue'
</a>
</div>
<HelloWorld msg="Vite + Vue" />
<MyComponent @myCustomEvent="handleCustomEvent" />
<p data-testid="mycomponent-click" v-show="isClicked">MyComponent was clicked</p>
<Input />
</template>

Expand Down
16 changes: 5 additions & 11 deletions example-project/vue-app/src/components/Input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,18 @@ export default defineComponent({
MyInput,
},
setup() {
const inputEvent = ref('');
const inputValue = ref('');
const changeEvent = ref('');
const handleInput = (ev) => {
console.log('handleInput', ev.target.value);
inputEvent.value = ev.target.value;
};
const handleChange = (ev) => {
console.log('handleChange', ev.target.value);
changeEvent.value = ev.detail.value;
};
return {
inputEvent,
inputValue,
changeEvent,
handleInput,
handleChange,
handleChange
};
},
});
Expand All @@ -40,12 +34,12 @@ export default defineComponent({
<template>
<div>
<MyInput
@myInput="handleInput"
v-model="inputValue"
@myChange="handleChange"
>
</MyInput>
<div class="inputResult">
<p>Input Event: {{ inputEvent }}</p>
<p>Input v-model: {{ inputValue }}</p>
<p>Change Event: {{ changeEvent }}</p>
</div>
</div>
Expand Down
7 changes: 6 additions & 1 deletion example-project/vue-app/tests/test.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ describe('Stencil Vue Integration', () => {
it('should allow to interact with input element', async () => {
await $('my-input').$('input').setValue('Hello World');
await expect(await $$('.inputResult p').map((p) => p.getText())).toEqual([
'Input Event: Hello World',
'Input v-model: Hello World',
'Change Event: Hello World'
]);
});

it('should listen to custom events', async () => {
await $('my-component').$('div').click();
await expect(await $('[data-testid="mycomponent-click"]').getText()).toEqual('MyComponent was clicked');
});
});
26 changes: 14 additions & 12 deletions packages/vue/src/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineComponent, getCurrentInstance, h, inject, ref, Ref, withDirectives } from 'vue';
import { defineComponent, getCurrentInstance, h, inject, onMounted, ref, Ref, withDirectives } from 'vue';

export { defineStencilSSRComponent } from './ssr';
export interface InputProps<T> {
Expand Down Expand Up @@ -81,6 +81,18 @@ export const defineContainer = <Props, VModelType = string | number | boolean>(
const containerRef = ref<HTMLElement>();
const classes = new Set(getComponentClasses(attrs.class));

onMounted(() => {
/**
* we register the event emmiter for @Event definitions
* so we can use @event
*/
emitProps.forEach((eventName: string) => {
containerRef.value!.addEventListener(eventName, (e: Event) => {
emit(eventName, e);
});
});
});

/**
* This directive is responsible for updating any reactive
* reference associated with v-model on the component.
Expand All @@ -95,7 +107,7 @@ export const defineContainer = <Props, VModelType = string | number | boolean>(
created: (el: HTMLElement) => {
const eventsNames = Array.isArray(modelUpdateEvent) ? modelUpdateEvent : [modelUpdateEvent];
eventsNames.forEach((eventName: string) => {
el.addEventListener(eventName.toLowerCase(), (e: Event) => {
el.addEventListener(eventName, (e: Event) => {
/**
* Only update the v-model binding if the event's target is the element we are
* listening on. For example, Component A could emit ionChange, but it could also
Expand All @@ -109,16 +121,6 @@ export const defineContainer = <Props, VModelType = string | number | boolean>(
}
});
});

/**
* we register the event emmiter for @Event definitions
* so we can use @event
*/
emitProps.forEach((eventName: string) => {
el.addEventListener(eventName, (e: Event) => {
emit(eventName, e);
});
});
},
};

Expand Down

0 comments on commit e8f92eb

Please sign in to comment.