I recently had to add some support for global key shortcuts in a Vue application I am working on. Vue has built-in support for listening to keys when you are in an input element. What is not supported directly are global shortcuts. For instance, if I am viewing an email in Gmail, I can hit 'a' to answer that email.
To accomplish this in Vue with either need to use low-level JavaScript event listeners or a plugin, like vue-shortkey. The approaches are not actually that different since vue-shortkey is, not surprisingly, just wrapping event listeners. It is straight forward to write your own event listener in a component so I didn't see a huge amount of value in using a plugin. There are a few blog posts covering event listeners in Vue already, but I am going to take a stab a showing a more complete example here, including how to test the component.
Implementing
Let's say we wanted to create a component that displays a message whenever the escape key is pressed.
Our Template block:
<div>{{ message }}</div>
Our Script block (Typescript):
import Vue from "vue";
export default Vue.component("Demo", {
created() {
window.addEventListener("keydown", this.escapeListener);
},
// make sure you remove the listener when the component is no longer visible
destroyed() {
window.removeEventListener("keydown", this.escapeListener);
},
data: function() {
return {
message: ""
};
},
methods: {
escapeListener(event: KeyboardEvent) {
if (event.key === "Escape") {
this.message = "Escape has been pressed";
}
}
}
});
If you prefer to use class syntax, change the script block to the following:
export default class Demo extends Vue {
private message = "";
private created() {
window.addEventListener("keydown", this.escapeListener);
}
private destroyed() {
window.removeEventListener("keydown", this.escapeListener);
}
private escapeListener(event: KeyboardEvent) {
if (event.key === "Escape") {
this.message = "Escape has been pressed";
}
}
}
Testing
This is all well and good, but it was not immediately clear how you test this behavior. After a few false starts, I stumbled upon a Github issue thread with the solution.
The magic I was missing was to use the Vue testing utils attachToDocument
option when mounting or shallow mounting the component under test. Once we attach our component to a document, we can use wrapper.trigger
to simulate our keypresses.
describe("Demo.vue", function() {
it("Displays a message when escape is pressed", function() {
const wrapper = shallowMount(Demo, { attachToDocument: true });
// the browser will add 'key' to the event,
// but when testing we need to add it manually
wrapper.trigger("keydown.esc", { key: "Escape" });
expect(wrapper.text()).to.include("Escape has been pressed");
// always make sure to destroy when using attachToDocument
wrapper.destroy();
});
});
And that is it, a straightforward way to test-drive our global shortcuts as we add them to our component.
Top comments (2)
First of all, great article and thanks to quote vue-shortkey.
The main reason to not use your approach is the fact you will attach so much listeners in your application and it is not good. Think about change the page and the effort to control the dismisses.
Vue-Shotkey use only one listener globaly! And dispatch the action only when is needed.
For simple stuff at least, this works well.