Skip to content

Commit 2368668

Browse files
committed
fix(VAutocomplete/VCombobox): disallow auto-select-first via pure blur
fixes #19929
1 parent 3c04145 commit 2368668

File tree

4 files changed

+177
-67
lines changed

4 files changed

+177
-67
lines changed

packages/vuetify/src/components/VAutocomplete/VAutocomplete.tsx

+6-9
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,11 @@ export const VAutocomplete = genericComponent<new <
238238
menu.value = false
239239
}
240240

241-
if (highlightFirst.value && e.key === 'Enter') {
241+
if (
242+
highlightFirst.value &&
243+
['Enter', 'Tab'].includes(e.key) &&
244+
!model.value.some(({ value }) => value === displayItems.value[0].value)
245+
) {
242246
select(displayItems.value[0])
243247
}
244248

@@ -369,15 +373,8 @@ export const VAutocomplete = genericComponent<new <
369373
nextTick(() => isSelecting.value = false)
370374
} else {
371375
if (!props.multiple && search.value == null) model.value = []
372-
else if (
373-
highlightFirst.value &&
374-
!listHasFocus.value &&
375-
!model.value.some(({ value }) => value === displayItems.value[0].value)
376-
) {
377-
select(displayItems.value[0])
378-
}
379376
menu.value = false
380-
if (props.multiple || hasSelectionSlot.value) search.value = ''
377+
if (!model.value.some(({ title }) => title === search.value)) search.value = ''
381378
selectionIndex.value = -1
382379
}
383380
})

packages/vuetify/src/components/VAutocomplete/__tests__/VAutocomplete.spec.cy.tsx

+85-26
Original file line numberDiff line numberDiff line change
@@ -464,33 +464,92 @@ describe('VAutocomplete', () => {
464464
.should('not.have.class', 'v-autocomplete--active-menu')
465465
})
466466

467-
it('should auto-select-first item when pressing enter', () => {
468-
const selectedItems = ref(undefined)
467+
describe('auto-select-first', () => {
468+
it('should auto-select-first item when pressing enter', () => {
469+
const selectedItems = ref(undefined)
469470

470-
cy
471-
.mount(() => (
472-
<VAutocomplete
473-
v-model={ selectedItems.value }
474-
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
475-
multiple
476-
autoSelectFirst
477-
/>
478-
))
479-
.get('.v-autocomplete')
480-
.click()
481-
.get('.v-list-item')
482-
.should('have.length', 6)
483-
.get('.v-autocomplete input')
484-
.type('Cal')
485-
.get('.v-list-item').eq(0)
486-
.should('have.class', 'v-list-item--active')
487-
.get('.v-autocomplete input')
488-
.trigger('keydown', { key: keyValues.enter, waitForAnimations: false })
489-
.get('.v-list-item')
490-
.should('have.length', 1)
491-
.then(_ => {
492-
expect(selectedItems.value).to.deep.equal(['California'])
493-
})
471+
cy
472+
.mount(() => (
473+
<VAutocomplete
474+
v-model={ selectedItems.value }
475+
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
476+
multiple
477+
autoSelectFirst
478+
/>
479+
))
480+
.get('.v-autocomplete')
481+
.click()
482+
.get('.v-list-item')
483+
.should('have.length', 6)
484+
.get('.v-autocomplete input')
485+
.type('Cal')
486+
.get('.v-list-item').eq(0)
487+
.should('have.class', 'v-list-item--active')
488+
.get('.v-autocomplete input')
489+
.trigger('keydown', { key: keyValues.enter, waitForAnimations: false })
490+
.get('.v-list-item')
491+
.should('have.length', 1)
492+
.then(_ => {
493+
expect(selectedItems.value).to.deep.equal(['California'])
494+
})
495+
})
496+
497+
it('should auto-select-first item when pressing tab', () => {
498+
const selectedItems = ref([])
499+
500+
cy
501+
.mount(() => (
502+
<VAutocomplete
503+
v-model={ selectedItems.value }
504+
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
505+
multiple
506+
autoSelectFirst
507+
/>
508+
))
509+
.get('.v-autocomplete')
510+
.click()
511+
.get('.v-list-item')
512+
.should('have.length', 6)
513+
.get('.v-autocomplete input')
514+
.type('Cal')
515+
.get('.v-list-item').eq(0)
516+
.should('have.class', 'v-list-item--active')
517+
.realPress('Tab')
518+
.get('.v-list-item')
519+
.should('have.length', 0)
520+
.then(_ => {
521+
expect(selectedItems.value).to.deep.equal(['California'])
522+
})
523+
})
524+
525+
it('should not auto-select-first item when blur', () => {
526+
const selectedItems = ref(undefined)
527+
528+
cy
529+
.mount(() => (
530+
<VAutocomplete
531+
v-model={ selectedItems.value }
532+
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
533+
multiple
534+
autoSelectFirst
535+
/>
536+
))
537+
.get('.v-autocomplete')
538+
.click()
539+
.get('.v-list-item')
540+
.should('have.length', 6)
541+
.get('.v-autocomplete input')
542+
.type('Cal')
543+
.get('.v-list-item').eq(0)
544+
.should('have.class', 'v-list-item--active')
545+
.get('.v-autocomplete input')
546+
.blur()
547+
.get('.v-list-item')
548+
.should('have.length', 0)
549+
.should(_ => {
550+
expect(selectedItems.value).to.deep.equal(undefined)
551+
})
552+
})
494553
})
495554

496555
// https://github.com/vuetifyjs/vuetify/issues/18796

packages/vuetify/src/components/VCombobox/VCombobox.tsx

+6-11
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,12 @@ export const VCombobox = genericComponent<new <
290290
menu.value = false
291291
}
292292

293-
if (['Enter', 'Escape'].includes(e.key)) {
294-
if (highlightFirst.value && e.key === 'Enter') {
293+
if (['Enter', 'Escape', 'Tab'].includes(e.key)) {
294+
if (
295+
highlightFirst.value &&
296+
['Enter', 'Tab'].includes(e.key) &&
297+
!model.value.some(({ value }) => value === displayItems.value[0].value)
298+
) {
295299
select(filteredItems.value[0])
296300
}
297301

@@ -412,15 +416,6 @@ export const VCombobox = genericComponent<new <
412416
selectionIndex.value = -1
413417
menu.value = false
414418

415-
if (
416-
highlightFirst.value &&
417-
!listHasFocus.value &&
418-
!model.value.some(({ value }) => value === displayItems.value[0].value)
419-
) {
420-
select(displayItems.value[0])
421-
return
422-
}
423-
424419
if (search.value) {
425420
if (props.multiple) {
426421
select(transformItem(props, search.value))

packages/vuetify/src/components/VCombobox/__tests__/VCombobox.spec.cy.tsx

+80-21
Original file line numberDiff line numberDiff line change
@@ -593,27 +593,86 @@ describe('VCombobox', () => {
593593
.should('not.have.class', 'v-combobox--active-menu')
594594
})
595595

596-
it('should auto-select-first item when pressing enter', () => {
597-
cy
598-
.mount(() => (
599-
<VCombobox
600-
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
601-
multiple
602-
autoSelectFirst
603-
/>
604-
))
605-
.get('.v-combobox')
606-
.click()
607-
.get('.v-list-item')
608-
.should('have.length', 6)
609-
.get('.v-combobox input')
610-
.type('Cal')
611-
.get('.v-list-item').eq(0)
612-
.should('have.class', 'v-list-item--active')
613-
.get('.v-combobox input')
614-
.trigger('keydown', { key: keyValues.enter, waitForAnimations: false })
615-
.get('.v-list-item')
616-
.should('have.length', 6)
596+
describe('auto-select-first', () => {
597+
it('should auto-select-first item when pressing enter', () => {
598+
cy
599+
.mount(() => (
600+
<VCombobox
601+
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
602+
multiple
603+
autoSelectFirst
604+
/>
605+
))
606+
.get('.v-combobox')
607+
.click()
608+
.get('.v-list-item')
609+
.should('have.length', 6)
610+
.get('.v-combobox input')
611+
.type('Cal')
612+
.get('.v-list-item').eq(0)
613+
.should('have.class', 'v-list-item--active')
614+
.get('.v-combobox input')
615+
.trigger('keydown', { key: keyValues.enter, waitForAnimations: false })
616+
.get('.v-list-item')
617+
.should('have.length', 6)
618+
})
619+
620+
it('should auto-select-first item when pressing tab', () => {
621+
const selectedItems = ref([])
622+
623+
cy
624+
.mount(() => (
625+
<VCombobox
626+
v-model={ selectedItems.value }
627+
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
628+
multiple
629+
autoSelectFirst
630+
/>
631+
))
632+
.get('.v-combobox')
633+
.click()
634+
.get('.v-list-item')
635+
.should('have.length', 6)
636+
.get('.v-combobox input')
637+
.type('Cal')
638+
.get('.v-list-item').eq(0)
639+
.should('have.class', 'v-list-item--active')
640+
.realPress('Tab')
641+
.get('.v-list-item')
642+
.should('have.length', 0)
643+
.then(_ => {
644+
expect(selectedItems.value).to.deep.equal(['California'])
645+
})
646+
})
647+
648+
it('should not auto-select-first item when blur', () => {
649+
const selectedItems = ref(undefined)
650+
651+
cy
652+
.mount(() => (
653+
<VCombobox
654+
v-model={ selectedItems.value }
655+
items={['California', 'Colorado', 'Florida', 'Georgia', 'Texas', 'Wyoming']}
656+
multiple
657+
autoSelectFirst
658+
/>
659+
))
660+
.get('.v-combobox')
661+
.click()
662+
.get('.v-list-item')
663+
.should('have.length', 6)
664+
.get('.v-combobox input')
665+
.type('Cal')
666+
.get('.v-list-item').eq(0)
667+
.should('have.class', 'v-list-item--active')
668+
.get('.v-combobox input')
669+
.blur()
670+
.get('.v-list-item')
671+
.should('have.length', 0)
672+
.should(_ => {
673+
expect(selectedItems.value).to.deep.equal(['Cal'])
674+
})
675+
})
617676
})
618677

619678
it(`doesn't add duplicate values`, () => {

0 commit comments

Comments
 (0)