From 26f082713e5be7dffe790f3e1966547053abb536 Mon Sep 17 00:00:00 2001 From: Pytal <24800714+Pytal@users.noreply.github.com> Date: Fri, 16 Dec 2022 12:47:19 -0800 Subject: [PATCH] fix: Keep focus on input after select to improve accessibility (#1727) Co-authored-by: Jeff Sagal Huge thanks to @Pytal for the great work! --- src/components/Select.vue | 26 ++++++++++++++++++++++---- src/mixins/typeAheadPointer.js | 6 +++++- tests/unit/Dropdown.spec.js | 3 +-- tests/unit/Filtering.spec.js | 22 ++++++++++++++++++++++ tests/unit/Selectable.spec.js | 2 ++ tests/unit/TypeAhead.spec.js | 22 ++++++++++++++++++++++ 6 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/components/Select.vue b/src/components/Select.vue index 8e304a19d..272a41fc9 100644 --- a/src/components/Select.vue +++ b/src/components/Select.vue @@ -571,7 +571,12 @@ export default { */ selectOnKeyCodes: { type: Array, - default: () => [13], + default: () => [ + // enter + 13, + // space + 32, + ], }, /** @@ -953,6 +958,12 @@ export default { open(isOpen) { this.$emit(isOpen ? 'open' : 'close') }, + + search(search) { + if (search.length) { + this.open = true + } + }, }, created() { @@ -1035,7 +1046,6 @@ export default { onAfterSelect(option) { if (this.closeOnSelect) { this.open = !this.open - this.searchEl.blur() } if (this.clearSearchOnSelect) { @@ -1240,7 +1250,7 @@ export default { */ onEscape() { if (!this.search.length) { - this.searchEl.blur() + this.open = false } else { this.search = '' } @@ -1302,7 +1312,7 @@ export default { /** * Search KeyBoardEvent handler. - * @param e {KeyboardEvent} + * @param {KeyboardEvent} e * @return {Function} */ onSearchKeyDown(e) { @@ -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() }, } diff --git a/src/mixins/typeAheadPointer.js b/src/mixins/typeAheadPointer.js index 4409c0a91..c38758e9f 100644 --- a/src/mixins/typeAheadPointer.js +++ b/src/mixins/typeAheadPointer.js @@ -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 + } }, }, } diff --git a/tests/unit/Dropdown.spec.js b/tests/unit/Dropdown.spec.js index d7e95beaf..614d103af 100755 --- a/tests/unit/Dropdown.spec.js +++ b/tests/unit/Dropdown.spec.js @@ -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', () => { diff --git a/tests/unit/Filtering.spec.js b/tests/unit/Filtering.spec.js index 0b19ca269..48bad26fd 100755 --- a/tests/unit/Filtering.spec.js +++ b/tests/unit/Filtering.spec.js @@ -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) + }) }) diff --git a/tests/unit/Selectable.spec.js b/tests/unit/Selectable.spec.js index 5c01ea7e0..c8dd17aa3 100644 --- a/tests/unit/Selectable.spec.js +++ b/tests/unit/Selectable.spec.js @@ -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') @@ -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') diff --git a/tests/unit/TypeAhead.spec.js b/tests/unit/TypeAhead.spec.js index 3a4f832a5..b031cc1a2 100755 --- a/tests/unit/TypeAhead.spec.js +++ b/tests/unit/TypeAhead.spec.js @@ -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') @@ -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') @@ -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) +})