@@ -653,6 +653,96 @@ def update_output(search_value):
653653 assert dash_duo .get_logs () == []
654654
655655
656+ def test_a11y012b_tab_from_search_focuses_first_option (dash_duo ):
657+ def send_keys (key ):
658+ actions = ActionChains (dash_duo .driver )
659+ actions .send_keys (key )
660+ actions .perform ()
661+
662+ app = Dash (__name__ )
663+ app .layout = Div (
664+ [
665+ Dropdown (
666+ id = "dropdown" ,
667+ options = ["Apple" , "Banana" , "Cherry" ],
668+ searchable = True ,
669+ ),
670+ Div (id = "output" ),
671+ ]
672+ )
673+
674+ @app .callback (Output ("output" , "children" ), Input ("dropdown" , "value" ))
675+ def update_output (value ):
676+ return f"Selected: { value } "
677+
678+ dash_duo .start_server (app )
679+
680+ dropdown = dash_duo .find_element ("#dropdown" )
681+ dropdown .send_keys ("b" )
682+
683+ dash_duo .wait_for_element (".dash-dropdown-search" )
684+
685+ # Tab from search input should focus the first option
686+ send_keys (Keys .TAB )
687+ sleep (0.1 )
688+
689+ # The dropdown should still be open
690+ dash_duo .wait_for_element (".dash-dropdown-options" )
691+
692+ # Enter selects the focused option
693+ send_keys (Keys .ENTER )
694+ dash_duo .wait_for_text_to_equal ("#output" , "Selected: Banana" )
695+
696+ assert dash_duo .get_logs () == []
697+
698+
699+ def test_a11y012c_shift_tab_between_dropdowns (dash_duo ):
700+ """Shift+Tab should move between dropdowns in a single press,
701+ just like forward Tab does."""
702+ from dash .html import Button as HtmlButton
703+
704+ def shift_tab ():
705+ actions = ActionChains (dash_duo .driver )
706+ actions .key_down (Keys .SHIFT ).send_keys (Keys .TAB ).key_up (Keys .SHIFT )
707+ actions .perform ()
708+
709+ app = Dash (__name__ )
710+ app .layout = Div (
711+ [
712+ Dropdown (
713+ id = "dd1" ,
714+ options = ["Apple" , "Banana" ],
715+ ),
716+ Dropdown (
717+ id = "dd2" ,
718+ options = ["Cherry" , "Date" ],
719+ ),
720+ HtmlButton ("after" , id = "after" ),
721+ ]
722+ )
723+
724+ dash_duo .start_server (app )
725+ dash_duo .wait_for_element ("#dd1" )
726+
727+ # Focus the button at the end
728+ dash_duo .find_element ("#after" ).click ()
729+ sleep (0.1 )
730+
731+ # Shift+Tab once should reach dd2
732+ shift_tab ()
733+ sleep (0.1 )
734+ active = dash_duo .driver .execute_script ("return document.activeElement.id;" )
735+ assert active == "dd2" , f"Expected dd2 but got { active } "
736+
737+ # Shift+Tab once more should reach dd1
738+ shift_tab ()
739+ sleep (0.1 )
740+ active = dash_duo .driver .execute_script ("return document.activeElement.id;" )
741+ assert active == "dd1" , f"Expected dd1 but got { active } "
742+
743+ assert dash_duo .get_logs () == []
744+
745+
656746def test_a11y013_enter_on_search_after_reopen_selects_correctly (dash_duo ):
657747 def send_keys (key ):
658748 actions = ActionChains (dash_duo .driver )
0 commit comments