@@ -651,4 +651,97 @@ describe("MultiSelect", () => {
651651 expect ( options [ 2 ] ) . toHaveAttribute ( "aria-selected" , "true" ) ;
652652 expect ( input ) . toHaveFocus ( ) ;
653653 } ) ;
654+
655+ // Regression test for https://github.com/carbon-design-system/carbon-components-svelte/issues/2313
656+ describe ( "keyboard navigation (issue #2313)" , ( ) => {
657+ it ( "filterable: menu opens when starting to type after Tab focus" , async ( ) => {
658+ render ( MultiSelect , {
659+ props : {
660+ items,
661+ filterable : true ,
662+ placeholder : "Filter..." ,
663+ } ,
664+ } ) ;
665+
666+ const input = screen . getByPlaceholderText ( "Filter..." ) ;
667+
668+ // Simulate tabbing into the field
669+ input . focus ( ) ;
670+
671+ // Menu doesn't need to open immediately on focus for filterable variant
672+ // but should open when user starts typing
673+ await user . type ( input , "s" ) ;
674+
675+ // Menu should now be open
676+ expect ( input ) . toHaveAttribute ( "aria-expanded" , "true" ) ;
677+ expect ( screen . getByRole ( "listbox" ) ) . toBeInTheDocument ( ) ;
678+ } ) ;
679+
680+ it ( "filterable: accepts keyboard input after tabbing into field" , async ( ) => {
681+ render ( MultiSelect , {
682+ props : {
683+ items,
684+ filterable : true ,
685+ placeholder : "Filter..." ,
686+ } ,
687+ } ) ;
688+
689+ const input = screen . getByPlaceholderText ( "Filter..." ) ;
690+
691+ // Simulate tabbing into the field
692+ input . focus ( ) ;
693+
694+ // Should be able to type immediately
695+ await user . type ( input , "slack" ) ;
696+ expect ( input ) . toHaveValue ( "slack" ) ;
697+
698+ // Filtered results should be shown
699+ expect ( screen . getByText ( "Slack" ) ) . toBeInTheDocument ( ) ;
700+ expect ( screen . queryByText ( "Email" ) ) . not . toBeInTheDocument ( ) ;
701+ } ) ;
702+
703+ it ( "filterable: Tab key does not close menu when navigating" , async ( ) => {
704+ render ( MultiSelect , {
705+ props : {
706+ items,
707+ filterable : true ,
708+ placeholder : "Filter..." ,
709+ } ,
710+ } ) ;
711+
712+ const input = screen . getByPlaceholderText ( "Filter..." ) ;
713+ await user . click ( input ) ;
714+
715+ // Menu should be open
716+ expect ( input ) . toHaveAttribute ( "aria-expanded" , "true" ) ;
717+
718+ // Press Tab - menu should close to allow natural tab navigation
719+ await user . keyboard ( "{Tab}" ) ;
720+
721+ // Menu should close when Tab is pressed to move focus away
722+ expect ( input ) . toHaveAttribute ( "aria-expanded" , "false" ) ;
723+ } ) ;
724+
725+ it ( "filterable: focus should go to input, not clear button when items selected" , async ( ) => {
726+ render ( MultiSelect , {
727+ props : {
728+ items,
729+ filterable : true ,
730+ placeholder : "Filter..." ,
731+ selectedIds : [ "0" ] ,
732+ } ,
733+ } ) ;
734+
735+ const input = screen . getByPlaceholderText ( "Filter..." ) ;
736+
737+ // Simulate tabbing into the field
738+ input . focus ( ) ;
739+
740+ // Input should have focus, not the clear button
741+ expect ( input ) . toHaveFocus ( ) ;
742+
743+ const clearButton = screen . getAllByRole ( "button" , { name : / c l e a r / i } ) [ 0 ] ;
744+ expect ( clearButton ) . not . toHaveFocus ( ) ;
745+ } ) ;
746+ } ) ;
654747} ) ;
0 commit comments