@@ -682,6 +682,125 @@ describe('StringField', () => {
682682 expect ( options [ 0 ] ) . toHaveTextContent ( '' ) ;
683683 expect ( options ) . toHaveLength ( 1 ) ;
684684 } ) ;
685+
686+ it ( 'should render optgroups when ui:options.optgroups is provided' , ( ) => {
687+ const { node } = createFormComponent ( {
688+ schema : {
689+ type : 'string' ,
690+ enum : [ 'foo' , 'bar' , 'baz' , 'qux' ] ,
691+ } ,
692+ uiSchema : {
693+ 'ui:options' : {
694+ optgroups : {
695+ 'Group A' : [ 'foo' , 'bar' ] ,
696+ 'Group B' : [ 'baz' , 'qux' ] ,
697+ } ,
698+ } ,
699+ } ,
700+ } ) ;
701+
702+ const optgroups = node . querySelectorAll ( 'optgroup' ) ;
703+ expect ( optgroups ) . toHaveLength ( 2 ) ;
704+ expect ( optgroups [ 0 ] ) . toHaveAttribute ( 'label' , 'Group A' ) ;
705+ expect ( optgroups [ 1 ] ) . toHaveAttribute ( 'label' , 'Group B' ) ;
706+ expect ( optgroups [ 0 ] . querySelectorAll ( 'option' ) ) . toHaveLength ( 2 ) ;
707+ expect ( optgroups [ 1 ] . querySelectorAll ( 'option' ) ) . toHaveLength ( 2 ) ;
708+ } ) ;
709+
710+ it ( 'should render ungrouped options after optgroups' , ( ) => {
711+ const { node } = createFormComponent ( {
712+ schema : {
713+ type : 'string' ,
714+ enum : [ 'foo' , 'bar' , 'baz' , 'qux' ] ,
715+ } ,
716+ uiSchema : {
717+ 'ui:options' : {
718+ optgroups : {
719+ 'Group A' : [ 'foo' , 'bar' ] ,
720+ } ,
721+ } ,
722+ } ,
723+ } ) ;
724+
725+ const select = node . querySelector ( 'select' ) ! ;
726+ const optgroups = select . querySelectorAll ( 'optgroup' ) ;
727+ expect ( optgroups ) . toHaveLength ( 1 ) ;
728+ expect ( optgroups [ 0 ] ) . toHaveAttribute ( 'label' , 'Group A' ) ;
729+
730+ // Ungrouped options (baz, qux) should be direct children of select, not inside optgroup
731+ const directOptions = Array . from ( select . children ) . filter ( ( child ) => child . tagName === 'OPTION' ) ;
732+ // placeholder + baz + qux = 3 direct option children
733+ expect ( directOptions ) . toHaveLength ( 3 ) ;
734+ } ) ;
735+
736+ it ( 'should handle enumDisabled within optgroups' , ( ) => {
737+ const { node } = createFormComponent ( {
738+ schema : {
739+ type : 'string' ,
740+ enum : [ 'foo' , 'bar' , 'baz' ] ,
741+ } ,
742+ uiSchema : {
743+ 'ui:options' : {
744+ enumDisabled : [ 'bar' ] ,
745+ optgroups : {
746+ 'Group A' : [ 'foo' , 'bar' ] ,
747+ 'Group B' : [ 'baz' ] ,
748+ } ,
749+ } ,
750+ } ,
751+ } ) ;
752+
753+ const optgroups = node . querySelectorAll ( 'optgroup' ) ;
754+ const groupAOptions = optgroups [ 0 ] . querySelectorAll ( 'option' ) ;
755+ expect ( groupAOptions [ 1 ] ) . toBeDisabled ( ) ;
756+ } ) ;
757+
758+ it ( 'should reflect the change event with optgroups' , ( ) => {
759+ const { node, onChange } = createFormComponent ( {
760+ schema : {
761+ type : 'string' ,
762+ enum : [ 'foo' , 'bar' , 'baz' ] ,
763+ } ,
764+ uiSchema : {
765+ 'ui:options' : {
766+ optgroups : {
767+ 'Group A' : [ 'foo' , 'bar' ] ,
768+ 'Group B' : [ 'baz' ] ,
769+ } ,
770+ } ,
771+ } ,
772+ } ) ;
773+
774+ act ( ( ) => {
775+ fireEvent . change ( node . querySelector ( 'select' ) ! , {
776+ target : { value : '2' } , // index of 'baz'
777+ } ) ;
778+ } ) ;
779+
780+ expectToHaveBeenCalledWithFormData ( onChange , 'baz' , 'root' ) ;
781+ } ) ;
782+
783+ it ( 'should render placeholder with optgroups' , ( ) => {
784+ const { node } = createFormComponent ( {
785+ schema : {
786+ type : 'string' ,
787+ enum : [ 'foo' , 'bar' ] ,
788+ } ,
789+ uiSchema : {
790+ 'ui:options' : {
791+ placeholder : 'Select one' ,
792+ optgroups : {
793+ 'Group A' : [ 'foo' , 'bar' ] ,
794+ } ,
795+ } ,
796+ } ,
797+ } ) ;
798+
799+ const select = node . querySelector ( 'select' ) ! ;
800+ const firstOption = select . querySelector ( 'option' ) ;
801+ expect ( firstOption ) . toHaveTextContent ( 'Select one' ) ;
802+ expect ( firstOption ) . toHaveValue ( '' ) ;
803+ } ) ;
685804 } ) ;
686805
687806 describe ( 'TextareaWidget' , ( ) => {
0 commit comments