@@ -22,7 +22,6 @@ import {ActionListContainerContext} from '../ActionList/ActionListContainerConte
2222import { isValidElementType } from 'react-is'
2323import { useAnnouncements } from './useAnnouncements'
2424import { clsx } from 'clsx'
25- import { useFeatureFlag } from '../FeatureFlags'
2625
2726const menuScrollMargins : ScrollIntoViewOptions = { startMargin : 0 , endMargin : 8 }
2827
@@ -45,6 +44,26 @@ export interface FilteredActionListProps extends Partial<Omit<GroupedListProps,
4544 announcementsEnabled ?: boolean
4645 fullScreenOnNarrow ?: boolean
4746 onSelectAllChange ?: ( checked : boolean ) => void
47+ /**
48+ * Private API for use internally only. Adds the ability to switch between
49+ * `active-descendant` and roving tabindex.
50+ *
51+ * By default, FilteredActionList uses `aria-activedescendant` to manage focus.
52+ *
53+ * Roving tabindex is an alternative focus management method that moves
54+ * focus to the list items themselves instead of keeping focus on the input.
55+ *
56+ * Improper usage can lead to inaccessible experiences, so this prop should be used with caution.
57+ *
58+ * For usage, refer to the documentation:
59+ *
60+ * WAI-ARIA `aria-activedescendant`: https://www.w3.org/TR/wai-aria-1.2/#aria-activedescendant
61+ *
62+ * Roving Tabindex: https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
63+ *
64+ * @default 'active-descendant'
65+ */
66+ _PrivateFocusManagement ?: 'roving-tabindex' | 'active-descendant'
4867}
4968
5069export function FilteredActionList ( {
@@ -67,6 +86,7 @@ export function FilteredActionList({
6786 announcementsEnabled = true ,
6887 fullScreenOnNarrow,
6988 onSelectAllChange,
89+ _PrivateFocusManagement = 'active-descendant' ,
7090 ...listProps
7191} : FilteredActionListProps ) : JSX . Element {
7292 const [ filterValue , setInternalFilterValue ] = useProvidedStateOrCreate ( externalFilterValue , undefined , '' )
@@ -85,7 +105,7 @@ export function FilteredActionList({
85105 const scrollContainerRef = useRef < HTMLDivElement > ( null )
86106 const inputRef = useProvidedRefOrCreate < HTMLInputElement > ( providedInputRef )
87107
88- const usingRemoveActiveDescendant = useFeatureFlag ( 'primer_react_select_panel_remove_active_descendant' )
108+ const usingRovingTabindex = _PrivateFocusManagement === 'roving-tabindex'
89109 const [ listContainerElement , setListContainerElement ] = useState < HTMLUListElement | null > ( null )
90110 const activeDescendantRef = useRef < HTMLElement > ( )
91111
@@ -164,7 +184,6 @@ export function FilteredActionList({
164184 [ activeDescendantRef ] ,
165185 )
166186
167- // BEGIN: Todo remove when we remove usingRemoveActiveDescendant
168187 const listContainerRefCallback = useCallback (
169188 ( node : HTMLUListElement | null ) => {
170189 setListContainerElement ( node )
@@ -175,10 +194,9 @@ export function FilteredActionList({
175194 useEffect ( ( ) => {
176195 onInputRefChanged ?.( inputRef )
177196 } , [ inputRef , onInputRefChanged ] )
178- //END: Todo remove when we remove usingRemoveActiveDescendant
179197
180198 useFocusZone (
181- ! usingRemoveActiveDescendant
199+ ! usingRovingTabindex
182200 ? {
183201 containerRef : { current : listContainerElement } ,
184202 bindKeys : FocusKeys . ArrowVertical | FocusKeys . PageUpDown ,
@@ -196,7 +214,7 @@ export function FilteredActionList({
196214 } ,
197215 }
198216 : undefined ,
199- [ listContainerElement , usingRemoveActiveDescendant ] ,
217+ [ listContainerElement , usingRovingTabindex ] ,
200218 )
201219
202220 useEffect ( ( ) => {
@@ -209,7 +227,7 @@ export function FilteredActionList({
209227 } , [ items , inputRef ] )
210228
211229 useEffect ( ( ) => {
212- if ( usingRemoveActiveDescendant ) {
230+ if ( usingRovingTabindex ) {
213231 const inputAndListContainerElement = inputAndListContainerRef . current
214232 if ( ! inputAndListContainerElement ) return
215233 const list = listRef . current
@@ -228,21 +246,22 @@ export function FilteredActionList({
228246 inputAndListContainerElement . removeEventListener ( 'focusin' , handleFocusIn )
229247 }
230248 }
231- } , [ items , inputRef , listContainerElement , usingRemoveActiveDescendant ] ) // Re-run when items change to update active indicators
249+ } , [ items , inputRef , listContainerElement , usingRovingTabindex ] ) // Re-run when items change to update active indicators
232250
233251 useEffect ( ( ) => {
234- if ( usingRemoveActiveDescendant && ! loading ) {
252+ if ( usingRovingTabindex && ! loading ) {
235253 setIsInputFocused ( inputRef . current && inputRef . current === document . activeElement ? true : false )
236254 }
237- } , [ loading , inputRef , usingRemoveActiveDescendant ] )
255+ } , [ loading , inputRef , usingRovingTabindex ] )
238256
239257 useAnnouncements (
240258 items ,
241- usingRemoveActiveDescendant ? listRef : { current : listContainerElement } ,
259+ usingRovingTabindex ? listRef : { current : listContainerElement } ,
242260 inputRef ,
243261 announcementsEnabled ,
244262 loading ,
245263 messageText ,
264+ _PrivateFocusManagement ,
246265 )
247266 useScrollFlash ( scrollContainerRef )
248267
@@ -265,7 +284,7 @@ export function FilteredActionList({
265284 let firstGroupIndex = 0
266285 const actionListContent = (
267286 < ActionList
268- ref = { usingRemoveActiveDescendant ? listRef : listContainerRefCallback }
287+ ref = { usingRovingTabindex ? listRef : listContainerRefCallback }
269288 showDividers = { showItemDividers }
270289 selectionVariant = { selectionVariant }
271290 { ...listProps }
@@ -316,7 +335,7 @@ export function FilteredActionList({
316335 )
317336
318337 // Use ActionListContainerContext.Provider only for the old behavior (when feature flag is disabled)
319- if ( usingRemoveActiveDescendant ) {
338+ if ( usingRovingTabindex ) {
320339 return (
321340 < ActionListContainerContext . Provider
322341 value = { {
@@ -348,7 +367,7 @@ export function FilteredActionList({
348367 value = { filterValue }
349368 onChange = { onInputChange }
350369 onKeyPress = { onInputKeyPress }
351- onKeyDown = { usingRemoveActiveDescendant ? onInputKeyDown : ( ) => { } }
370+ onKeyDown = { usingRovingTabindex ? onInputKeyDown : ( ) => { } }
352371 placeholder = { placeholderText }
353372 role = "combobox"
354373 aria-expanded = "true"
0 commit comments