Skip to content

Commit da5493b

Browse files
authored
fix(VCombobox): keep highlighted item on filteredItems change (#14196)
fixes #14194
1 parent 82e0081 commit da5493b

5 files changed

Lines changed: 159 additions & 8 deletions

File tree

packages/vuetify/src/components/VAutocomplete/VAutocomplete.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,16 @@ export default VSelect.extend({
217217
// for duplicate items? no idea
218218
if (val === oldVal) return
219219

220-
this.setMenuIndex(-1)
220+
if (!this.autoSelectFirst) {
221+
const preSelectedItem = oldVal[this.$refs.menu.listIndex]
222+
223+
if (preSelectedItem) {
224+
this.setMenuIndex(val.findIndex(i => i === preSelectedItem))
225+
} else {
226+
this.setMenuIndex(-1)
227+
}
228+
this.$emit('update:list-index', this.$refs.menu.listIndex)
229+
}
221230

222231
this.$nextTick(() => {
223232
if (
@@ -227,7 +236,11 @@ export default VSelect.extend({
227236
) return
228237

229238
this.$refs.menu.getTiles()
230-
this.setMenuIndex(0)
239+
240+
if (this.autoSelectFirst && val.length) {
241+
this.setMenuIndex(0)
242+
this.$emit('update:list-index', this.$refs.menu.listIndex)
243+
}
231244
})
232245
},
233246
onInternalSearchChanged () {

packages/vuetify/src/components/VCombobox/VCombobox.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,6 @@ export default VAutocomplete.extend({
108108

109109
this.$nextTick(this.updateSelf)
110110
},
111-
onFilteredItemsChanged (val: never[], oldVal: never[]) {
112-
if (!this.autoSelectFirst) return
113-
114-
VAutocomplete.options.methods.onFilteredItemsChanged.call(this, val, oldVal)
115-
},
116111
onKeyDown (e: KeyboardEvent) {
117112
const keyCode = e.keyCode
118113

packages/vuetify/src/components/VCombobox/__tests__/VCombobox-multiple.spec.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable max-statements */
12
// Components
23
import VCombobox from '../VCombobox'
34

@@ -578,4 +579,116 @@ describe('VCombobox.ts', () => {
578579

579580
expect(change).toHaveBeenLastCalledWith(['bar', 'foo'])
580581
})
582+
583+
// example 1 in https://github.com/vuetifyjs/vuetify/issues/14194
584+
it('should not point to a result that does not exist as in example 1', async () => {
585+
const { wrapper } = createMultipleCombobox({
586+
items: ['a', 'aa', 'aaa', 'bar'],
587+
})
588+
589+
const input = wrapper.find('input')
590+
const element = input.element as HTMLInputElement
591+
592+
const listIndexUpdate = jest.fn()
593+
wrapper.vm.$on('update:list-index', listIndexUpdate)
594+
595+
input.trigger('focus')
596+
await wrapper.vm.$nextTick()
597+
element.value = 'a'
598+
input.trigger('input')
599+
await wrapper.vm.$nextTick()
600+
601+
input.trigger('keydown.down')
602+
await wrapper.vm.$nextTick()
603+
604+
input.trigger('keydown.down')
605+
await wrapper.vm.$nextTick()
606+
607+
input.trigger('keydown.down')
608+
await wrapper.vm.$nextTick()
609+
610+
input.trigger('keydown.down')
611+
await wrapper.vm.$nextTick()
612+
613+
element.value = 'aa'
614+
input.trigger('input')
615+
await wrapper.vm.$nextTick()
616+
expect(listIndexUpdate.mock.calls.length === 6).toBe(true)
617+
expect(listIndexUpdate.mock.calls[0][0]).toBe(-1)
618+
expect(listIndexUpdate.mock.calls[1][0]).toBe(0)
619+
expect(listIndexUpdate.mock.calls[2][0]).toBe(1)
620+
expect(listIndexUpdate.mock.calls[3][0]).toBe(2)
621+
expect(listIndexUpdate.mock.calls[4][0]).toBe(3)
622+
expect(listIndexUpdate.mock.calls[5][0]).toBe(-1)
623+
})
624+
625+
// example 2 in https://github.com/vuetifyjs/vuetify/issues/14194
626+
it('should not change selection on search input as in example 2', async () => {
627+
const { wrapper } = createMultipleCombobox({
628+
items: ['a', 'aa', 'aaa', 'bar'],
629+
})
630+
631+
const input = wrapper.find('input')
632+
const element = input.element as HTMLInputElement
633+
634+
const listIndexUpdate = jest.fn()
635+
wrapper.vm.$on('update:list-index', listIndexUpdate)
636+
637+
input.trigger('focus')
638+
await wrapper.vm.$nextTick()
639+
element.value = 'a'
640+
input.trigger('input')
641+
await wrapper.vm.$nextTick()
642+
643+
input.trigger('keydown.down')
644+
await wrapper.vm.$nextTick()
645+
646+
input.trigger('keydown.down')
647+
await wrapper.vm.$nextTick()
648+
649+
input.trigger('keydown.down')
650+
await wrapper.vm.$nextTick()
651+
652+
element.value = 'aa'
653+
input.trigger('input')
654+
await wrapper.vm.$nextTick()
655+
656+
expect(listIndexUpdate.mock.calls.length === 5).toBe(true)
657+
expect(listIndexUpdate.mock.calls[0][0]).toBe(-1)
658+
expect(listIndexUpdate.mock.calls[1][0]).toBe(0)
659+
expect(listIndexUpdate.mock.calls[2][0]).toBe(1)
660+
expect(listIndexUpdate.mock.calls[3][0]).toBe(2)
661+
expect(listIndexUpdate.mock.calls[4][0]).toBe(1)
662+
})
663+
664+
// example 3 in https://github.com/vuetifyjs/vuetify/issues/14194
665+
it('should not point to a result that does not exist as in example 3', async () => {
666+
const { wrapper } = createMultipleCombobox({
667+
items: ['a', 'aa', 'aaa', 'bar'],
668+
})
669+
670+
const input = wrapper.find('input')
671+
const element = input.element as HTMLInputElement
672+
673+
const listIndexUpdate = jest.fn()
674+
wrapper.vm.$on('update:list-index', listIndexUpdate)
675+
676+
input.trigger('focus')
677+
await wrapper.vm.$nextTick()
678+
element.value = 'a'
679+
input.trigger('input')
680+
await wrapper.vm.$nextTick()
681+
682+
input.trigger('keydown.down')
683+
await wrapper.vm.$nextTick()
684+
685+
element.value = 'aaaa'
686+
input.trigger('input')
687+
await wrapper.vm.$nextTick()
688+
689+
expect(listIndexUpdate.mock.calls.length === 3).toBe(true)
690+
expect(listIndexUpdate.mock.calls[0][0]).toBe(-1)
691+
expect(listIndexUpdate.mock.calls[1][0]).toBe(0)
692+
expect(listIndexUpdate.mock.calls[2][0]).toBe(-1)
693+
})
581694
})

packages/vuetify/src/components/VCombobox/__tests__/VCombobox.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,34 @@ describe('VCombobox.ts', () => {
321321

322322
expect(wrapper.vm.$attrs.autocomplete).toBe('on')
323323
})
324+
325+
// https://github.com/vuetifyjs/vuetify/issues/6607
326+
it('should select first row when autoSelectFirst true is applied', async () => {
327+
const wrapper = mountFunction({
328+
propsData: {
329+
autoSelectFirst: true,
330+
items: [
331+
{ text: 'Learn JavaScript', done: false },
332+
{ text: 'Learn Vue', done: false },
333+
{ text: 'Play around in JSFiddle', done: true },
334+
{ text: 'Build something awesome', done: true },
335+
],
336+
},
337+
})
338+
339+
const input = wrapper.find('input')
340+
const element = input.element as HTMLInputElement
341+
342+
const listIndexUpdate = jest.fn()
343+
wrapper.vm.$on('update:list-index', listIndexUpdate)
344+
345+
input.trigger('focus')
346+
await wrapper.vm.$nextTick()
347+
element.value = 'L'
348+
input.trigger('input')
349+
await wrapper.vm.$nextTick()
350+
351+
expect(listIndexUpdate.mock.calls.length === 1).toBe(true)
352+
expect(listIndexUpdate.mock.calls[0][0]).toBe(0)
353+
})
324354
})

packages/vuetify/src/components/VSelect/VSelect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ export default baseMixins.extend<options>().extend({
664664

665665
// If menu is active, allow default
666666
// listIndex change from menu
667-
if (this.isMenuActive && keyCode !== keyCodes.tab) {
667+
if (this.isMenuActive && [keyCodes.up, keyCodes.down, keyCodes.home, keyCodes.end, keyCodes.enter].includes(keyCode)) {
668668
this.$nextTick(() => {
669669
menu.changeListIndex(e)
670670
this.$emit('update:list-index', menu.listIndex)

0 commit comments

Comments
 (0)