diff --git a/.changeset/curly-phones-burn.md b/.changeset/curly-phones-burn.md
new file mode 100644
index 0000000000..8e273bce36
--- /dev/null
+++ b/.changeset/curly-phones-burn.md
@@ -0,0 +1,5 @@
+---
+'@primer/view-components': patch
+---
+
+[SelectPanel] Fix tab index issue in multi-select mode
diff --git a/app/components/primer/alpha/select_panel_element.ts b/app/components/primer/alpha/select_panel_element.ts
index f450a1e86c..af1396bf70 100644
--- a/app/components/primer/alpha/select_panel_element.ts
+++ b/app/components/primer/alpha/select_panel_element.ts
@@ -314,11 +314,7 @@ export class SelectPanelElement extends HTMLElement {
const itemContent = this.#getItemContent(item)
if (!itemContent) continue
- if (!this.isItemHidden(item) && !setZeroTabIndex) {
- setZeroTabIndex = true
- } else {
- itemContent.setAttribute('tabindex', '-1')
- }
+ itemContent.setAttribute('tabindex', '-1')
//
elements should not themselves be tabbable
item.removeAttribute('tabindex')
@@ -742,6 +738,8 @@ export class SelectPanelElement extends HTMLElement {
return true
}
+ if (!this.bannerErrorElement) return false
+
return !this.bannerErrorElement.hasAttribute('hidden')
}
diff --git a/demo/package-lock.json b/demo/package-lock.json
index 29fc90d23a..9cc5449d2d 100644
--- a/demo/package-lock.json
+++ b/demo/package-lock.json
@@ -9,7 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@primer/css": "^21.3.6",
- "@primer/primitives": "^9.0.3",
+ "@primer/primitives": "^9.1.0",
"@rails/actioncable": "^7.2.100",
"@rails/ujs": "^7.1.400",
"turbolinks": "^5.2.0",
@@ -543,12 +543,9 @@
"license": "MIT"
},
"node_modules/@primer/primitives": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-9.0.3.tgz",
- "integrity": "sha512-iHl1oI566v3NMAkK2tmKsTkcNmen5mE3nCIdKmaSJZnxLTUT1NWwv77J7FfeRDAQl8VdU6qGfA2/D4sV9JjB/g==",
- "engines": {
- "node": ">=18.18.0 <18.19.0"
- }
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-9.1.0.tgz",
+ "integrity": "sha512-8FvRr4c17q0oOZee03JdSlVBzr6z7GrI2WTGnVUxxUlHe5eG9t5UXGBcuai2WvEbmKaTQWGB5V684tkNvKMK2A=="
},
"node_modules/@primer/view-components": {
"version": "0.27.0",
@@ -4023,9 +4020,9 @@
}
},
"@primer/primitives": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-9.0.3.tgz",
- "integrity": "sha512-iHl1oI566v3NMAkK2tmKsTkcNmen5mE3nCIdKmaSJZnxLTUT1NWwv77J7FfeRDAQl8VdU6qGfA2/D4sV9JjB/g=="
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/@primer/primitives/-/primitives-9.1.0.tgz",
+ "integrity": "sha512-8FvRr4c17q0oOZee03JdSlVBzr6z7GrI2WTGnVUxxUlHe5eG9t5UXGBcuai2WvEbmKaTQWGB5V684tkNvKMK2A=="
},
"@primer/view-components": {
"version": "0.27.0",
diff --git a/demo/package.json b/demo/package.json
index b6f15e71a9..f58ada399e 100644
--- a/demo/package.json
+++ b/demo/package.json
@@ -4,7 +4,7 @@
"version": "0.1.0",
"dependencies": {
"@primer/css": "^21.3.6",
- "@primer/primitives": "^9.0.3",
+ "@primer/primitives": "^9.1.0",
"@rails/actioncable": "^7.2.100",
"@rails/ujs": "^7.1.400",
"turbolinks": "^5.2.0",
diff --git a/test/system/alpha/select_panel_test.rb b/test/system/alpha/select_panel_test.rb
index 9ce82caffc..1eb60d4644 100644
--- a/test/system/alpha/select_panel_test.rb
+++ b/test/system/alpha/select_panel_test.rb
@@ -82,6 +82,21 @@ def wait_for_dialog_ready
end
def filter_results(query:)
+ input = find("input")
+
+ # If the query is an empty string or nil, using fill_in does not
+ # trigger the right type of input event, which in turn prevents
+ # the remote-input element from firing its remote-input-success
+ # event. Instead we have to focus on the input field, select all
+ # the text, and press backspace. Doing so fires the right type of
+ # event and clears the input.
+ if query.blank?
+ old_active_element = active_element
+ input.evaluate_script("this.focus()")
+ keyboard.type([:control, "a"], :backspace)
+ old_active_element.evaluate_script("this.focus()")
+ end
+
find("input").fill_in(with: query)
end
@@ -1052,6 +1067,21 @@ def test_arrowing_skips_filtered_items
end
end
+ def test_can_tab_to_first_item_after_filtering
+ visit_preview(:local_fetch)
+
+ click_on_invoker_button
+
+ filter_results(query: "2")
+ assert_selector "select-panel ul li", count: 1
+
+ filter_results(query: "")
+ keyboard.type(:tab)
+ assert_equal active_element.text, "Item 1"
+ end
+
+ ########## FORM TESTS ############
+
def test_single_select_form
visit_preview(:single_select_form, route_format: :json)