Skip to content

Commit

Permalink
fix: Keep focus on input after select to improve accessibility (#1727)
Browse files Browse the repository at this point in the history
Co-authored-by: Jeff Sagal <[email protected]>

Huge thanks to @Pytal for the great work!
  • Loading branch information
Pytal authored Dec 16, 2022
1 parent 8d2ac2d commit 26f0827
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 7 deletions.
26 changes: 22 additions & 4 deletions src/components/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,12 @@ export default {
*/
selectOnKeyCodes: {
type: Array,
default: () => [13],
default: () => [
// enter
13,
// space
32,
],
},
/**
Expand Down Expand Up @@ -953,6 +958,12 @@ export default {
open(isOpen) {
this.$emit(isOpen ? 'open' : 'close')
},
search(search) {
if (search.length) {
this.open = true
}
},
},
created() {
Expand Down Expand Up @@ -1035,7 +1046,6 @@ export default {
onAfterSelect(option) {
if (this.closeOnSelect) {
this.open = !this.open
this.searchEl.blur()
}
if (this.clearSearchOnSelect) {
Expand Down Expand Up @@ -1240,7 +1250,7 @@ export default {
*/
onEscape() {
if (!this.search.length) {
this.searchEl.blur()
this.open = false
} else {
this.search = ''
}
Expand Down Expand Up @@ -1302,7 +1312,7 @@ export default {
/**
* Search <input> KeyBoardEvent handler.
* @param e {KeyboardEvent}
* @param {KeyboardEvent} e
* @return {Function}
*/
onSearchKeyDown(e) {
Expand All @@ -1321,11 +1331,19 @@ export default {
// up.prevent
38: (e) => {
e.preventDefault()
if (!this.open) {
this.open = true
return
}
return this.typeAheadUp()
},
// down.prevent
40: (e) => {
e.preventDefault()
if (!this.open) {
this.open = true
return
}
return this.typeAheadDown()
},
}
Expand Down
6 changes: 5 additions & 1 deletion src/mixins/typeAheadPointer.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,16 @@ export default {
* Moves the pointer to the last selected option.
*/
typeAheadToLastSelected() {
this.typeAheadPointer =
const indexOfLastSelected =
this.selectedValue.length !== 0
? this.filteredOptions.indexOf(
this.selectedValue[this.selectedValue.length - 1]
)
: -1

if (indexOfLastSelected !== -1) {
this.typeAheadPointer = indexOfLastSelected
}
},
},
}
3 changes: 1 addition & 2 deletions tests/unit/Dropdown.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,11 @@ describe('Toggling Dropdown', () => {

it('will close the dropdown on escape, if search is empty', () => {
const Select = selectWithProps()
const spy = jest.spyOn(Select.vm.$refs.search, 'blur')

Select.vm.open = true
Select.vm.onEscape()

expect(spy).toHaveBeenCalled()
expect(Select.vm.open).toEqual(false)
})

it('should remove existing search text on escape keydown', () => {
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/Filtering.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,26 @@ describe('Filtering Options', () => {
Select.vm.search = '1'
expect(Select.vm.filteredOptions).toEqual([1, 10])
})

it('should open dropdown on alphabetic input', async () => {
const Select = shallowMount(VueSelect)

const input = Select.find('.vs__search')
input.element.value = 'a'
input.trigger('input')
await Select.vm.$nextTick()

expect(Select.vm.open).toEqual(true)
})

it('should open dropdown on numeric input', async () => {
const Select = shallowMount(VueSelect)

const input = Select.find('.vs__search')
input.element.value = 1
input.trigger('input')
await Select.vm.$nextTick()

expect(Select.vm.open).toEqual(true)
})
})
2 changes: 2 additions & 0 deletions tests/unit/Selectable.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe('Selectable prop', () => {
selectable: (option) => option !== 'two',
})

Select.vm.open = true
Select.vm.typeAheadPointer = 1

Select.findComponent({ ref: 'search' }).trigger('keydown.down')
Expand All @@ -50,6 +51,7 @@ describe('Selectable prop', () => {
selectable: (option) => option !== 'two',
})

Select.vm.open = true
Select.vm.typeAheadPointer = 2

Select.findComponent({ ref: 'search' }).trigger('keydown.up')
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/TypeAhead.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe('Moving the Typeahead Pointer', () => {
it('should move the pointer visually up the list on up arrow keyUp', () => {
const Select = mountDefault()

Select.vm.open = true
Select.vm.typeAheadPointer = 1

Select.findComponent({ ref: 'search' }).trigger('keydown.up')
Expand All @@ -30,6 +31,7 @@ describe('Moving the Typeahead Pointer', () => {
it('should move the pointer visually down the list on down arrow keyUp', () => {
const Select = mountDefault()

Select.vm.open = true
Select.vm.typeAheadPointer = 1

Select.findComponent({ ref: 'search' }).trigger('keydown.down')
Expand Down Expand Up @@ -78,3 +80,23 @@ describe('Moving the Typeahead Pointer', () => {
expect(Select.vm.typeAheadPointer).toEqual(2)
})
})

it('should not move the pointer visually up the list on up arrow keyUp when dropdown is not open', () => {
const Select = mountDefault()

Select.vm.typeAheadPointer = 1

Select.findComponent({ ref: 'search' }).trigger('keydown.up')

expect(Select.vm.typeAheadPointer).toEqual(1)
})

it('should not move the pointer visually down the list on down arrow keyUp when dropdown is not open', () => {
const Select = mountDefault()

Select.vm.typeAheadPointer = 1

Select.findComponent({ ref: 'search' }).trigger('keydown.down')

expect(Select.vm.typeAheadPointer).toEqual(1)
})

0 comments on commit 26f0827

Please sign in to comment.