@@ -39,6 +39,7 @@ import { EventType } from '@/events/types'
3939import { ASK_AI_EVENT_GROUP , SEARCH_OVERLAY_EVENT_GROUP } from '@/events/components/event-groups'
4040import type { AIReference } from '../types'
4141import type { AutocompleteSearchHit , GeneralSearchHit } from '@/search/types'
42+ import { focusTrap } from '@primer/behaviors'
4243
4344type Props = {
4445 searchOverlayOpen : boolean
@@ -88,6 +89,14 @@ export function SearchOverlay({
8889
8990 // Group all events between open / close of the overlay together
9091 const searchEventGroupId = useRef < string > ( '' )
92+ const overlayRef = useRef < HTMLDivElement > ( null )
93+
94+ useEffect ( ( ) => {
95+ if ( searchOverlayOpen && overlayRef . current ) {
96+ focusTrap ( overlayRef . current , inputRef . current || undefined )
97+ }
98+ } , [ searchOverlayOpen ] )
99+
91100 useEffect ( ( ) => {
92101 searchEventGroupId . current = uuidv4 ( )
93102 } , [ searchOverlayOpen ] )
@@ -486,6 +495,7 @@ export function SearchOverlay({
486495 < >
487496 < ActionList
488497 aria-label = { t ( 'search.overlay.suggestions_list_aria_label' ) }
498+ id = "search-suggestions-list"
489499 showDividers
490500 className = { styles . suggestionsList }
491501 ref = { suggestionsListHeightRef }
@@ -601,6 +611,7 @@ export function SearchOverlay({
601611 role = "dialog"
602612 aria-modal = "true"
603613 aria-labelledby = { overlayHeadingId }
614+ ref = { overlayRef }
604615 >
605616 < Header className = { styles . header } >
606617 < TextInput
@@ -609,9 +620,17 @@ export function SearchOverlay({
609620 ref = { inputRef }
610621 value = { urlSearchInputQuery }
611622 onChange = { handleSearchQueryChange }
612- onKeyDown = { handleKeyDown }
613623 leadingVisual = { < SearchIcon /> }
614624 aria-labelledby = { overlayHeadingId }
625+ role = "combobox"
626+ aria-controls = "search-suggestions-list"
627+ aria-expanded = { combinedOptions . length > 0 }
628+ aria-activedescendant = {
629+ selectedIndex >= 0
630+ ? `search-option-${ combinedOptions [ selectedIndex ] . group } -${ selectedIndex } `
631+ : undefined
632+ }
633+ onKeyDown = { handleKeyDown }
615634 placeholder = { t ( 'search.input.placeholder_no_icon' ) }
616635 trailingAction = {
617636 < Stack
@@ -793,6 +812,8 @@ function renderSearchGroups(
793812 items . push (
794813 < ActionList . Item
795814 key = { `general-${ index } ` }
815+ id = { `search-option-general-${ index } ` }
816+ role = "option"
796817 className = { styles . noResultsFound }
797818 tabIndex = { - 1 }
798819 aria-label = { t ( 'search.overlay.no_results_found' ) }
@@ -808,6 +829,9 @@ function renderSearchGroups(
808829 items . push (
809830 < ActionList . Item
810831 key = { `general-${ index } ` }
832+ id = { `search-option-general-${ index } ` }
833+ role = "option"
834+ aria-describedby = "search-suggestions-list"
811835 onSelect = { ( ) =>
812836 option . isViewAllResults ? performGeneralSearch ( ) : generalSearchResultOnSelect ( option )
813837 }
@@ -843,11 +867,7 @@ function renderSearchGroups(
843867
844868 groups . push (
845869 < ActionList . Group key = "general" data-testid = "general-autocomplete-suggestions" >
846- < ActionList . GroupHeading
847- as = "h3"
848- tabIndex = { - 1 }
849- aria-label = { t ( 'search.overlay.general_suggestions_list_aria_label' ) }
850- >
870+ < ActionList . GroupHeading as = "h3" tabIndex = { - 1 } id = "search-suggestions-list" >
851871 { t ( 'search.overlay.general_suggestions_list_heading' ) }
852872 </ ActionList . GroupHeading >
853873 { items }
@@ -872,11 +892,7 @@ function renderSearchGroups(
872892 if ( aiOptionsWithUserInput . length && ! isInAskAIState ) {
873893 groups . push (
874894 < ActionList . Group key = "ai-suggestions" data-testid = "ai-autocomplete-suggestions" >
875- < ActionList . GroupHeading
876- as = "h3"
877- tabIndex = { - 1 }
878- aria-label = { t ( 'search.overlay.ai_suggestions_list_aria_label' ) }
879- >
895+ < ActionList . GroupHeading as = "h3" id = "copilot-suggestions" tabIndex = { - 1 } >
880896 < CopilotIcon className = "mr-1" />
881897 { t ( 'search.overlay.ai_autocomplete_list_heading' ) }
882898 </ ActionList . GroupHeading >
@@ -887,6 +903,9 @@ function renderSearchGroups(
887903 const item = (
888904 < ActionList . Item
889905 key = { `ai-${ indexWithOffset } ` }
906+ id = { `search-option-ai-${ indexWithOffset } ` }
907+ role = "option"
908+ aria-describedby = "copilot-suggestions"
890909 onSelect = { ( ) => aiAutocompleteOnSelect ( option ) }
891910 active = { isActive }
892911 tabIndex = { - 1 }
0 commit comments