Skip to content

Commit

Permalink
feat(VMenu): add jump to first/last item keyboard navigation (#12348)
Browse files Browse the repository at this point in the history
Co-authored-by: Kael <kaelwd@gmail.com>
  • Loading branch information
janKollars and KaelWD committed Nov 11, 2020
1 parent 41fd082 commit 181d337
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
22 changes: 22 additions & 0 deletions packages/vuetify/src/components/VMenu/VMenu.ts
Expand Up @@ -258,6 +258,10 @@ export default baseMixins.extend({
this.nextTile()
} else if (e.keyCode === keyCodes.up) {
this.prevTile()
} else if (e.keyCode === keyCodes.end) {
this.lastTile()
} else if (e.keyCode === keyCodes.home) {
this.firstTile()
} else if (e.keyCode === keyCodes.enter && this.listIndex !== -1) {
this.tiles[this.listIndex].click()
} else { return }
Expand Down Expand Up @@ -424,6 +428,24 @@ export default baseMixins.extend({
this.listIndex--
if (tile.tabIndex === -1) this.prevTile()
},
lastTile () {
const tile = this.tiles[this.tiles.length - 1]

if (!tile) return

this.listIndex = this.tiles.length - 1

if (tile.tabIndex === -1) this.prevTile()
},
firstTile () {
const tile = this.tiles[0]

if (!tile) return

this.listIndex = 0

if (tile.tabIndex === -1) this.nextTile()
},
onKeyDown (e: KeyboardEvent) {
if (e.keyCode === keyCodes.esc) {
// Wait for dependent elements to close first
Expand Down
33 changes: 33 additions & 0 deletions packages/vuetify/src/components/VMenu/__tests__/VMenu.spec.ts
Expand Up @@ -258,4 +258,37 @@ describe('VMenu.ts', () => {

expect('Unable to locate target [data-app]').toHaveBeenTipped()
})

it('should select first or last item when pressing home or end on active menu', async () => {
const event = (keyCode: number) => new KeyboardEvent('keydown', { keyCode })
const wrapper = mountFunction({
propsData: { eager: true },
scopedSlots: {
default () {
return this.$createElement('div', [
this.$createElement(VListItem),
this.$createElement(VListItem, { props: { link: true } }),
this.$createElement(VListItem, { props: { link: true } }),
this.$createElement(VListItem, { props: { link: true } }),
])
},
},
})

wrapper.setData({ isActive: true })

wrapper.vm.onKeyDown(event(keyCodes.end))

await wrapper.vm.$nextTick()

expect(wrapper.vm.listIndex).toBe(3)

wrapper.vm.onKeyDown(event(keyCodes.home))

await wrapper.vm.$nextTick()

expect(wrapper.vm.listIndex).toBe(1)

expect('Unable to locate target [data-app]').toHaveBeenTipped()
})
})
19 changes: 16 additions & 3 deletions packages/vuetify/src/components/VSelect/VSelect.ts
Expand Up @@ -661,13 +661,13 @@ export default baseMixins.extend<options>().extend({
})
}

// If menu is not active, up and down can do
// If menu is not active, up/down/home/end can do
// one of 2 things. If multiple, opens the
// menu, if not, will cycle through all
// available options
if (
!this.isMenuActive &&
[keyCodes.up, keyCodes.down].includes(keyCode)
[keyCodes.up, keyCodes.down, keyCodes.home, keyCodes.end].includes(keyCode)
) return this.onUpDown(e)

// If escape deactivate the menu
Expand Down Expand Up @@ -782,7 +782,20 @@ export default baseMixins.extend<options>().extend({

window.requestAnimationFrame(() => {
menu.getTiles()
keyCodes.up === keyCode ? menu.prevTile() : menu.nextTile()
switch (keyCode) {
case keyCodes.up:
menu.prevTile()
break
case keyCodes.down:
menu.nextTile()
break
case keyCodes.home:
menu.firstTile()
break
case keyCodes.end:
menu.lastTile()
break
}
menu.activeTile && menu.activeTile.click()
})
},
Expand Down
16 changes: 16 additions & 0 deletions packages/vuetify/src/components/VSelect/__tests__/VSelect2.spec.ts
Expand Up @@ -476,5 +476,21 @@ describe('VSelect.ts', () => {
await waitAnimationFrame()

expect(wrapper.vm.internalValue).toBe(1)

// End key
event.keyCode = keyCodes.end
wrapper.vm.onKeyDown(event)

await waitAnimationFrame()

expect(wrapper.vm.internalValue).toBe(4)

// Home key
event.keyCode = keyCodes.home
wrapper.vm.onKeyDown(event)

await waitAnimationFrame()

expect(wrapper.vm.internalValue).toBe(1)
})
})

0 comments on commit 181d337

Please sign in to comment.