From 5cfb98022ba2c5a3ed5d283fd9f51a6500ea8d22 Mon Sep 17 00:00:00 2001 From: Yerin Park <109822768+yeriiiii@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:02:55 +0900 Subject: [PATCH] fix(VsSelect): fix autocomplete options closing on space key press (#212) Co-authored-by: Suyeon Woo <134579071+seaneez@users.noreply.github.com> Co-authored-by: smithoo --- .../src/components/vs-select/VsSelect.vue | 50 ++++--- .../vs-select/__tests__/vs-select.test.ts | 11 +- .../composables/focus-control-composable.ts | 135 ++++++++---------- 3 files changed, 97 insertions(+), 99 deletions(-) diff --git a/packages/vlossom/src/components/vs-select/VsSelect.vue b/packages/vlossom/src/components/vs-select/VsSelect.vue index 060a8a07a..7fe273941 100644 --- a/packages/vlossom/src/components/vs-select/VsSelect.vue +++ b/packages/vlossom/src/components/vs-select/VsSelect.vue @@ -19,7 +19,7 @@ ref="triggerRef" :class="['vs-select', `vs-${computedColorScheme}`, { ...classObj }, stateClasses]" :style="computedStyleSet" - @click.stop="toggleOptions()" + @click.stop="onClickTrigger()" >
@@ -152,7 +152,6 @@ :aria-multi-selectable="multiple" :aria-activedescendant="focusedOptionId" tabindex="-1" - @keydown.stop="onKeyDown" >
  • { }); describe('keyboard interaction', () => { - it('combobox가 focus를 받은 상태에서 Enter 키, Space 바를 누르면 옵션 리스트를 열고 닫을 수 있다', async () => { + it('combobox가 focus를 받은 상태에서 Enter 키를 누르면 옵션 리스트를 열고 닫을 수 있다', async () => { // when await wrapper.find('.vs-select-input').trigger('keydown', { code: 'Enter' }); // then @@ -670,21 +670,18 @@ describe('vs-select', () => { await vi.advanceTimersByTime(500); // then expect(wrapper.find('ul.vs-select-options').exists()).toBe(false); + }); + it('combobox가 focus를 받은 상태에서 Space 키를 누르면 옵션 리스트를 열 수 있다', async () => { // when await wrapper.find('.vs-select-input').trigger('keydown', { code: 'Space' }); // then expect(wrapper.find('ul.vs-select-options').exists()).toBe(true); - - // when - await wrapper.find('.vs-select-input').trigger('keydown', { code: 'Space' }); - await vi.advanceTimersByTime(500); - // then - expect(wrapper.find('ul.vs-select-options').exists()).toBe(false); }); it('combobox가 focus를 받은 상태에서 Arrow Down 키를 누르면 옵션 리스트가 열리고 listbox의 첫번째 옵션으로 focus가 이동한다', async () => { // when + await wrapper.find('.vs-select-input').trigger('keydown', { code: 'Space' }); await wrapper.find('.vs-select-input').trigger('keydown', { code: 'ArrowDown' }); // then diff --git a/packages/vlossom/src/components/vs-select/composables/focus-control-composable.ts b/packages/vlossom/src/components/vs-select/composables/focus-control-composable.ts index 573805e11..c4548064a 100644 --- a/packages/vlossom/src/components/vs-select/composables/focus-control-composable.ts +++ b/packages/vlossom/src/components/vs-select/composables/focus-control-composable.ts @@ -33,58 +33,7 @@ export function useFocusControl( } } - function onArrowDownKey(event: KeyboardEvent) { - if (!isOpen.value && focusedIndex.value === -1) { - isOpen.value = true; - } - - if (focusedIndex.value < (selectAll.value ? 1 : 0) + loadedOptions.value.length - 1) { - focusedIndex.value += 1; - } - - event.preventDefault(); - } - - function onArrowUpKey(event: KeyboardEvent) { - if (focusedIndex.value > 0) { - focusedIndex.value -= 1; - } - - event.preventDefault(); - } - - function onEnterKey(event: KeyboardEvent) { - if (isOpen.value) { - if (focusedIndex.value !== -1) { - selectFocusedOption(); - } else { - closeOptions(); - } - } else { - isOpen.value = true; - } - - event.preventDefault(); - } - - function onEscapeKey(event: KeyboardEvent) { - closeOptions(); - comboboxFocus(); - - event.preventDefault(); - } - - function onTabKey() { - if (isOpen.value) { - if (focusedIndex.value !== -1) { - selectFocusedOption(); - } - - closeOptions(); - } - } - - function onKeyDown(event: KeyboardEvent) { + function onComboboxKeydown(event: KeyboardEvent) { if (disabled.value || readonly.value) { return; } @@ -94,25 +43,65 @@ export function useFocusControl( chasingMouse.value = false; } - switch (event.code) { - case 'ArrowDown': - onArrowDownKey(event); - break; - case 'ArrowUp': - onArrowUpKey(event); - break; - case 'Enter': - case 'Space': - onEnterKey(event); - break; - case 'Escape': - onEscapeKey(event); - break; - case 'Tab': - onTabKey(); - break; - default: - break; + if (!isOpen.value) { + switch (event.code) { + case 'Enter': + case 'Space': + case 'ArrowDown': + case 'ArrowUp': + isOpen.value = true; + event.preventDefault(); + break; + default: + break; + } + } else { + const lastIndex = (selectAll.value ? 1 : 0) + loadedOptions.value.length - 1; + switch (event.code) { + case 'Enter': + if (focusedIndex.value !== -1) { + selectFocusedOption(); + } else { + closeOptions(); + } + event.preventDefault(); + break; + case 'Escape': + closeOptions(); + comboboxFocus(); + event.preventDefault(); + break; + case 'Space': + if (focusedIndex.value !== -1) { + selectFocusedOption(); + event.preventDefault(); + } + break; + case 'ArrowDown': + if (focusedIndex.value >= lastIndex) { + focusedIndex.value = -1; + } else { + focusedIndex.value += 1; + } + event.preventDefault(); + break; + case 'ArrowUp': + if (focusedIndex.value < 0) { + focusedIndex.value = lastIndex; + } else { + focusedIndex.value -= 1; + } + event.preventDefault(); + break; + case 'Tab': + if (focusedIndex.value !== -1) { + selectFocusedOption(); + } + closeOptions(); + break; + default: + break; + } } } @@ -181,11 +170,11 @@ export function useFocusControl( return { focusedIndex, + focusedOptionId, hoveredIndex, chasingMouse, - onKeyDown, + onComboboxKeydown, onMouseMove, isChasedOption, - focusedOptionId, }; }