Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 12 additions & 15 deletions assets/js/hooks/combobox.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,7 @@ export default {
}

const focusedOption = options.find(option => option.dataset.value === this.focusedOptionBeforeUpdate) || options[0]
if (focusedOption) {
this.setFocus(focusedOption)
} else {
this.js().removeAttribute(this.refs.searchInput, 'aria-activedescendant')
}
this.setFocus(focusedOption)
},

sendQuery(query) {
Expand Down Expand Up @@ -263,12 +259,18 @@ export default {
this.notifyFormChange()
},

setFocus(el) {
clearFocus() {
const focused = this.getCurrentFocusedOption()
if (focused) this.js().removeAttribute(focused, 'data-focus')
this.js().removeAttribute(this.refs.searchInput, 'aria-activedescendant')
},

setFocus(el) {
this.clearFocus()
if (!el) return

this.js().setAttribute(el, 'data-focus', 'true')

// Update aria-activedescendant to point to the focused option
if (el.id) {
this.js().setAttribute(this.refs.searchInput, 'aria-activedescendant', el.id)
}
Expand All @@ -278,11 +280,7 @@ export default {

focusFirstOption() {
const firstOption = this.refs.optionsContainer?.querySelector(SELECTORS.VISIBLE_OPTION)
if (firstOption) {
this.setFocus(firstOption)
} else {
this.js().removeAttribute(this.refs.searchInput, 'aria-activedescendant')
}
this.setFocus(firstOption)
},

getCurrentFocusedOption() {
Expand Down Expand Up @@ -324,7 +322,7 @@ export default {
},

selectOption(el) {
if (!el) return
if (!el || el.hasAttribute('data-hidden')) return

let value = el.getAttribute('data-value')
let displayValue = el.getAttribute('data-display')
Expand Down Expand Up @@ -611,11 +609,10 @@ export default {

close() {
clearTimeout(this.searchTimer)
this.js().removeAttribute(this.refs.searchInput, 'aria-activedescendant')
this.clearFocus()
if (this.refs.createOption) {
this.hideOption(this.refs.createOption)
this.refs.createOption.textContent = ''
this.js().removeAttribute(this.refs.createOption, 'data-focus')
}

if (!this.refs.optionsContainer || !this.isOpen) return
Expand Down
20 changes: 20 additions & 0 deletions demo/test/wallaby/demo_web/combobox_keyboard_navigation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ defmodule DemoWeb.ComboboxKeyboardNavigationTest do
|> assert_combobox_selection("#demo-combobox", "demo-combobox[fruit]", "Pear", "Pear")
end

for key <- [:enter, :tab] do
@selection_key key

feature "#{@selection_key} ignores options hidden by filtering", %{session: session} do
session
|> visit_fixture("/fixtures/simple-combobox", "#demo-combobox")
|> click(@search_input)
|> assert_has(Query.css("#demo-combobox [data-value='Apple'][data-focus=true]"))
|> fill_in(@search_input, with: "zzzzzz")
|> assert_has(Query.css("#demo-combobox [role=option]:not([data-hidden])", count: 0))
|> send_keys([@selection_key])
|> assert_combobox_selection("#demo-combobox", "demo-combobox[fruit]", "zzzzzz", nil)
|> assert_has(Query.css("#demo-combobox [data-focus=true]", count: 0, visible: :any))
|> assert_has(Query.css("#demo-combobox input:not([aria-activedescendant])"))
|> fill_in(@search_input, with: "Pear")
|> send_keys([:down_arrow, :enter])
|> assert_combobox_selection("#demo-combobox", "demo-combobox[fruit]", "Pear", "Pear")
end
end

feature "selects focused option with Tab key", %{session: session} do
session
|> visit_fixture("/fixtures/simple-combobox", "#demo-combobox")
Expand Down
Loading