diff --git a/README.md b/README.md index 785f21b..23d9224 100644 --- a/README.md +++ b/README.md @@ -406,11 +406,15 @@ shouldRenderSuggestions (size, loading) { ## [Events](#events) +Below are the list of supported events. `@` is short-hand for +[v-on](https://vuejs.org/v2/guide/events.html#Listening-to-Events). + | Prop | Returns | Description | | :-------------------------------- | :-------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `@selected` | suggestionItem, index | suggestion select handler. equivalent to sectionConfigs `on-selected` but for all items | | `@input`, `@focus`, `@blur`, etc. | \* | there is a transparent wrapper on the underlying `` so vue-autosuggest will use any DOM event you pass it for listening. This is implemented using `v-on:`. | -| `@opened`, `@closed` | \* | suggestions visibility handler, indicates when the suggestions are opened and closed. | +| `@opened`, `@closed` | \* | suggestions visibility handler, indicates when the suggestions are opened and closed. This is called alongside [shouldRenderSuggestions](#shouldRenderSuggestions). | +| `@item-changed` | suggestionItem, index | when keying through the results, this event signals which item is highlighted before being selected. | ## Browser support diff --git a/__tests__/autosuggest.test.js b/__tests__/autosuggest.test.js index 091b0ea..66a87a9 100644 --- a/__tests__/autosuggest.test.js +++ b/__tests__/autosuggest.test.js @@ -784,25 +784,62 @@ describe("Autosuggest", () => { expect(str).toMatchSnapshot(); }); }); - it("emits opened and closed events", async () => { - const props = { ...defaultProps }; - props.inputProps = { ...defaultProps.inputProps }; - const wrapper = mount(Autosuggest, { - propsData: props, - }); + it("emits opened and closed events", async () => { + const props = { ...defaultProps }; + props.inputProps = { ...defaultProps.inputProps }; - const input = wrapper.find("input"); - input.setValue("G"); + const wrapper = mount(Autosuggest, { + propsData: props, + }); + + const input = wrapper.find("input"); + input.setValue("G"); + input.trigger("keydown.down"); - // Wait for watchers - await wrapper.vm.$nextTick(() => { - expect(wrapper.emitted().opened).toBeTruthy(); - }); + await wrapper.vm.$nextTick(() => {}) + expect(wrapper.emitted().opened).toBeTruthy(); - input.trigger("keydown.esc"); - await wrapper.vm.$nextTick(() => { + input.trigger("keydown.esc"); + await wrapper.vm.$nextTick(() => {}) expect(wrapper.emitted().closed).toBeTruthy(); }); - }); + + it("emits item-changed event", async () => { + const props = { ...defaultProps }; + props.inputProps = { ...defaultProps.inputProps }; + + const wrapper = mount(Autosuggest, { + propsData: props, + }); + + const input = wrapper.find("input"); + input.setValue("G"); + input.trigger("keydown.down"); + input.trigger("keydown.down"); + + await wrapper.vm.$nextTick(() => {}) + expect(wrapper.emitted()['item-changed']).toHaveLength(2); + const itemChanged1 = wrapper.emitted()['item-changed'][0] + const itemChanged2 = wrapper.emitted()['item-changed'][1] + + // Emits with item and index + expect(itemChanged1[0].item).toBe('clifford kits'); + expect(itemChanged1[1]).toBe(0); + expect(itemChanged2[0].item).toBe('friendly chemistry'); + expect(itemChanged2[1]).toBe(1); + + input.trigger("keydown.up"); + await wrapper.vm.$nextTick(() => {}) + input.trigger("keydown.up"); + await wrapper.vm.$nextTick(() => {}) + await wrapper.vm.$nextTick(() => {}) + + // Ensure empty item-changed is emitted when user keys back + // to the input #177 + expect(wrapper.emitted()['item-changed']).toHaveLength(4) + const itemChangedEmpty = wrapper.emitted()['item-changed'][3] + expect(itemChangedEmpty[0]).toBeNull(); + expect(itemChangedEmpty[1]).toBeNull(); + }); }); diff --git a/src/Autosuggest.vue b/src/Autosuggest.vue index 380583b..ab549fa 100644 --- a/src/Autosuggest.vue +++ b/src/Autosuggest.vue @@ -310,7 +310,8 @@ export default { if (newValue !== oldValue) { this.$emit(newValue ? 'opened' : 'closed'); } - } + }, + immediate: true } }, created() { @@ -399,7 +400,7 @@ export default { this.setChangeItem(this.getItemByIndex(this.currentIndex)); this.didSelectFromOptions = true; } else if (this.currentIndex == -1) { - this.currentIndex = null; + this.setChangeItem(null) this.internalValue = this.searchInputOriginal; e.preventDefault(); } @@ -433,9 +434,10 @@ export default { setChangeItem(item, overrideOriginalInput = false) { if (this.currentIndex === null || !item) { this.currentItem = null; + this.$emit('item-changed', null, null) } else if (item) { this.currentItem = item; - this.$emit('itemChanged', item, this.currentIndex) + this.$emit('item-changed', item, this.currentIndex) const v = this.getSuggestionValue(item) this.internalValue = v; if (overrideOriginalInput) {