@@ -348,3 +348,96 @@ export const FormSubmission: Story = {
348348 } ) ;
349349 } ,
350350} ;
351+
352+ export const KeyboardNavigation : Story = {
353+ parameters : {
354+ docs : {
355+ description : {
356+ story : 'Test keyboard navigation with arrow keys and Enter selection.' ,
357+ } ,
358+ } ,
359+ } ,
360+ decorators : [ selectRouterDecorator ] ,
361+ play : async ( { canvasElement, step } ) => {
362+ const canvas = within ( canvasElement ) ;
363+
364+ await step ( 'Test keyboard navigation on Custom Region select' , async ( ) => {
365+ // Open the Custom Region select
366+ const regionSelect = canvas . getByLabelText ( 'Custom Region' ) ;
367+ await userEvent . click ( regionSelect ) ;
368+
369+ // Verify the dropdown is open and input is focused
370+ const listbox = await within ( document . body ) . findByRole ( 'listbox' ) ;
371+ expect ( listbox ) . toBeInTheDocument ( ) ;
372+
373+ const searchInput = within ( listbox ) . getByPlaceholderText ( 'Search...' ) ;
374+ expect ( searchInput ) . toHaveFocus ( ) ;
375+
376+ // Verify first item is active by default (should have aria-activedescendant)
377+ const firstOptionId = searchInput . getAttribute ( 'aria-activedescendant' ) ;
378+ expect ( firstOptionId ) . toBeTruthy ( ) ;
379+
380+ // Verify the first option exists and has the correct ID
381+ const firstOption = within ( listbox ) . getByRole ( 'option' , { name : 'Alabama' } ) ;
382+ expect ( firstOption ) . toHaveAttribute ( 'id' , firstOptionId ) ;
383+ expect ( firstOption ) . toHaveAttribute ( 'data-active' , 'true' ) ;
384+ } ) ;
385+
386+ await step ( 'Navigate with arrow keys' , async ( ) => {
387+ const listbox = within ( document . body ) . getByRole ( 'listbox' ) ;
388+ const searchInput = within ( listbox ) . getByPlaceholderText ( 'Search...' ) ;
389+
390+ // Press ArrowDown twice to move to the third item
391+ await userEvent . keyboard ( '{ArrowDown}' ) ;
392+ await userEvent . keyboard ( '{ArrowDown}' ) ;
393+
394+ // Verify the active item has changed
395+ const activeOptionId = searchInput . getAttribute ( 'aria-activedescendant' ) ;
396+ const activeOption = document . getElementById ( activeOptionId ! ) ;
397+ expect ( activeOption ) . toHaveAttribute ( 'data-active' , 'true' ) ;
398+
399+ // Should be the third option (index 2)
400+ expect ( activeOption ) . toHaveAttribute ( 'data-index' , '2' ) ;
401+ } ) ;
402+
403+ await step ( 'Select with Enter key' , async ( ) => {
404+ const listbox = within ( document . body ) . getByRole ( 'listbox' ) ;
405+ const searchInput = within ( listbox ) . getByPlaceholderText ( 'Search...' ) ;
406+
407+ // Press Enter to select the active item
408+ await userEvent . keyboard ( '{Enter}' ) ;
409+
410+ // Verify the dropdown closed and the trigger shows the selected value
411+ await expect ( ( ) => within ( document . body ) . getByRole ( 'listbox' ) ) . rejects . toThrow ( ) ;
412+
413+ const regionSelect = canvas . getByLabelText ( 'Custom Region' ) ;
414+ // The third item should be "Arizona" (AL, AK, AZ...)
415+ expect ( regionSelect ) . toHaveTextContent ( 'Arizona' ) ;
416+ } ) ;
417+
418+ await step ( 'Test filtering and active item reset' , async ( ) => {
419+ // Open the dropdown again
420+ const regionSelect = canvas . getByLabelText ( 'Custom Region' ) ;
421+ await userEvent . click ( regionSelect ) ;
422+
423+ const listbox = await within ( document . body ) . findByRole ( 'listbox' ) ;
424+ const searchInput = within ( listbox ) . getByPlaceholderText ( 'Search...' ) ;
425+
426+ // Type to filter
427+ await userEvent . type ( searchInput , 'cal' ) ;
428+
429+ // Verify the active item reset to the first filtered item
430+ const activeOptionId = searchInput . getAttribute ( 'aria-activedescendant' ) ;
431+ const activeOption = document . getElementById ( activeOptionId ! ) ;
432+ expect ( activeOption ) . toHaveAttribute ( 'data-index' , '0' ) ;
433+ expect ( activeOption ) . toHaveTextContent ( 'California' ) ;
434+
435+ // Press Enter to select the filtered item
436+ await userEvent . keyboard ( '{Enter}' ) ;
437+
438+ // Verify selection
439+ await expect ( ( ) => within ( document . body ) . getByRole ( 'listbox' ) ) . rejects . toThrow ( ) ;
440+ expect ( regionSelect ) . toHaveTextContent ( 'California' ) ;
441+ } ) ;
442+ } ,
443+ } ;
0 commit comments