1
1
import { expect , userEvent , waitFor , within } from '@storybook/test' ;
2
+ import {
3
+ IconArrowBack ,
4
+ IconArrowForward ,
5
+ IconClipboard ,
6
+ IconCopy ,
7
+ IconCut ,
8
+ IconDeviceFloppy ,
9
+ IconFile ,
10
+ IconFileText ,
11
+ IconFolder ,
12
+ IconSearch ,
13
+ IconSettings ,
14
+ } from '@tabler/icons-react' ;
2
15
import React , { useState } from 'react' ;
3
16
4
- import { Dialog , DialogTrigger } from '../../overlays/Dialog' ;
17
+ import {
18
+ Dialog ,
19
+ DialogTrigger ,
20
+ useDialogContainer ,
21
+ } from '../../overlays/Dialog' ;
5
22
import { Button } from '../Button' ;
6
23
import { Menu } from '../Menu/Menu' ;
7
24
@@ -557,6 +574,41 @@ MultipleSelection.args = {
557
574
autoFocus : true ,
558
575
} ;
559
576
577
+ export const SingleSelection : StoryFn < CubeCommandMenuProps < any > > = ( args ) => {
578
+ const [ selectedKey , setSelectedKey ] = useState < string | null > ( null ) ;
579
+
580
+ return (
581
+ < div style = { { display : 'flex' , flexDirection : 'column' , gap : '16px' } } >
582
+ < div >
583
+ < strong > Selected:</ strong > { selectedKey || 'None' }
584
+ </ div >
585
+ < CommandMenu
586
+ { ...args }
587
+ selectionMode = "single"
588
+ selectedKeys = { selectedKey ? [ selectedKey ] : [ ] }
589
+ onSelectionChange = { ( keys ) => {
590
+ setSelectedKey ( keys [ 0 ] || null ) ;
591
+ } }
592
+ >
593
+ { basicCommands . map ( ( command ) => (
594
+ < CommandMenu . Item
595
+ key = { command . key }
596
+ description = { command . description }
597
+ hotkeys = { command . hotkeys }
598
+ >
599
+ { command . label }
600
+ </ CommandMenu . Item >
601
+ ) ) }
602
+ </ CommandMenu >
603
+ </ div >
604
+ ) ;
605
+ } ;
606
+
607
+ SingleSelection . args = {
608
+ searchPlaceholder : 'Select a single command...' ,
609
+ autoFocus : true ,
610
+ } ;
611
+
560
612
export const CustomStyling : StoryFn < CubeCommandMenuProps < any > > = ( args ) => (
561
613
< CommandMenu
562
614
{ ...args }
@@ -673,7 +725,7 @@ export const WithDialog: StoryFn<CubeCommandMenuProps<any>> = (args) => (
673
725
< DialogTrigger >
674
726
< Button > Open Command Menu</ Button >
675
727
< Dialog size = "medium" isDismissable = { false } >
676
- < CommandMenu width = "100%" height = "20x " { ...args } size = "medium" >
728
+ < CommandMenu width = "100%" height = "min(40x, 90vh) " { ...args } size = "medium" >
677
729
{ basicCommands . map ( ( command ) => (
678
730
< CommandMenu . Item
679
731
key = { command . key }
@@ -692,3 +744,187 @@ WithDialog.args = {
692
744
searchPlaceholder : 'Search commands...' ,
693
745
autoFocus : true ,
694
746
} ;
747
+
748
+ WithDialog . play = async ( { canvasElement } ) => {
749
+ const canvas = within ( canvasElement ) ;
750
+ const button = canvas . getByText ( 'Open Command Menu' ) ;
751
+ await userEvent . click ( button ) ;
752
+
753
+ // Wait for dialog to open
754
+ await waitFor ( ( ) => {
755
+ canvas . getByPlaceholderText ( 'Search commands...' ) ;
756
+ } ) ;
757
+ } ;
758
+
759
+ function CommandMenuDialogContent ( {
760
+ onClose,
761
+ ...args
762
+ } : CubeCommandMenuProps < any > & { onClose : ( ) => void } ) {
763
+ const commandMenuProps = {
764
+ ...args ,
765
+ onAction : ( key : React . Key ) => {
766
+ console . log ( 'Action selected:' , key ) ;
767
+ onClose ( ) ;
768
+ } ,
769
+ } ;
770
+
771
+ return (
772
+ < Dialog size = "medium" isDismissable = { false } >
773
+ < CommandMenu
774
+ width = "100%"
775
+ height = "min(40x, 90vh)"
776
+ { ...commandMenuProps }
777
+ size = "medium"
778
+ >
779
+ { basicCommands . map ( ( command ) => (
780
+ < CommandMenu . Item
781
+ key = { command . key }
782
+ description = { command . description }
783
+ hotkeys = { command . hotkeys }
784
+ >
785
+ { command . label }
786
+ </ CommandMenu . Item >
787
+ ) ) }
788
+ </ CommandMenu >
789
+ </ Dialog >
790
+ ) ;
791
+ }
792
+
793
+ export const WithDialogContainer : StoryFn < CubeCommandMenuProps < any > > = (
794
+ args ,
795
+ ) => {
796
+ const dialog = useDialogContainer ( CommandMenuDialogContent ) ;
797
+
798
+ const handleOpenDialog = ( ) => {
799
+ dialog . open ( {
800
+ ...args ,
801
+ onClose : dialog . close ,
802
+ } ) ;
803
+ } ;
804
+
805
+ return (
806
+ < div >
807
+ < Button onPress = { handleOpenDialog } > Open Command Menu (Hook)</ Button >
808
+ { dialog . rendered }
809
+ </ div >
810
+ ) ;
811
+ } ;
812
+
813
+ WithDialogContainer . args = {
814
+ searchPlaceholder : 'Search commands...' ,
815
+ autoFocus : true ,
816
+ } ;
817
+
818
+ WithDialogContainer . play = async ( { canvasElement } ) => {
819
+ const canvas = within ( canvasElement ) ;
820
+ const button = canvas . getByText ( 'Open Command Menu (Hook)' ) ;
821
+ await userEvent . click ( button ) ;
822
+
823
+ // Wait for dialog to open
824
+ await waitFor ( ( ) => {
825
+ canvas . getByPlaceholderText ( 'Search commands...' ) ;
826
+ } ) ;
827
+ } ;
828
+
829
+ export const WithIcons : StoryFn < CubeCommandMenuProps < any > > = ( args ) => (
830
+ < CommandMenu { ...args } >
831
+ < Menu . Section title = "File Operations" >
832
+ < CommandMenu . Item
833
+ key = "new-file"
834
+ icon = { < IconFile /> }
835
+ description = "Create a new file"
836
+ hotkeys = "Ctrl+N"
837
+ >
838
+ New File
839
+ </ CommandMenu . Item >
840
+ < CommandMenu . Item
841
+ key = "open-file"
842
+ icon = { < IconFolder /> }
843
+ description = "Open an existing file"
844
+ hotkeys = "Ctrl+O"
845
+ >
846
+ Open File
847
+ </ CommandMenu . Item >
848
+ < CommandMenu . Item
849
+ key = "save-file"
850
+ icon = { < IconDeviceFloppy /> }
851
+ description = "Save current file"
852
+ hotkeys = "Ctrl+S"
853
+ >
854
+ Save File
855
+ </ CommandMenu . Item >
856
+ </ Menu . Section >
857
+
858
+ < Menu . Section title = "Edit Operations" >
859
+ < CommandMenu . Item
860
+ key = "copy"
861
+ icon = { < IconCopy /> }
862
+ description = "Copy selected text"
863
+ hotkeys = "Ctrl+C"
864
+ keywords = { [ 'duplicate' , 'clone' ] }
865
+ >
866
+ Copy
867
+ </ CommandMenu . Item >
868
+ < CommandMenu . Item
869
+ key = "paste"
870
+ icon = { < IconClipboard /> }
871
+ description = "Paste from clipboard"
872
+ hotkeys = "Ctrl+V"
873
+ keywords = { [ 'insert' ] }
874
+ >
875
+ Paste
876
+ </ CommandMenu . Item >
877
+ < CommandMenu . Item
878
+ key = "cut"
879
+ icon = { < IconCut /> }
880
+ description = "Cut selected text"
881
+ hotkeys = "Ctrl+X"
882
+ >
883
+ Cut
884
+ </ CommandMenu . Item >
885
+ < CommandMenu . Item
886
+ key = "undo"
887
+ icon = { < IconArrowBack /> }
888
+ description = "Undo last action"
889
+ hotkeys = "Ctrl+Z"
890
+ >
891
+ Undo
892
+ </ CommandMenu . Item >
893
+ < CommandMenu . Item
894
+ key = "redo"
895
+ icon = { < IconArrowForward /> }
896
+ description = "Redo last action"
897
+ hotkeys = "Ctrl+Y"
898
+ >
899
+ Redo
900
+ </ CommandMenu . Item >
901
+ </ Menu . Section >
902
+
903
+ < Menu . Section title = "Tools" >
904
+ < CommandMenu . Item
905
+ key = "search"
906
+ icon = { < IconSearch /> }
907
+ description = "Search in files"
908
+ hotkeys = "Ctrl+F"
909
+ >
910
+ Search
911
+ </ CommandMenu . Item >
912
+ < CommandMenu . Item
913
+ key = "settings"
914
+ icon = { < IconSettings /> }
915
+ description = "Open settings"
916
+ hotkeys = "Ctrl+."
917
+ >
918
+ Settings
919
+ </ CommandMenu . Item >
920
+ < CommandMenu . Item key = "documents" description = "View all documents" >
921
+ Documents
922
+ </ CommandMenu . Item >
923
+ </ Menu . Section >
924
+ </ CommandMenu >
925
+ ) ;
926
+
927
+ WithIcons . args = {
928
+ searchPlaceholder : 'Search commands with icons...' ,
929
+ autoFocus : true ,
930
+ } ;
0 commit comments