@@ -555,3 +555,223 @@ test("textarea with local value prop changes independently from root", async ()
555555
556556 await expect . element ( Textarea ) . toHaveValue ( "value changed" ) ;
557557} ) ;
558+
559+ test ( "field root has data-qds-root attribute" , async ( ) => {
560+ render ( < BasicInput /> ) ;
561+
562+ await expect . element ( Root ) . toBeVisible ( ) ;
563+
564+ const rootElement = await Root . element ( ) ;
565+ expect ( rootElement ?. hasAttribute ( "data-qds-root" ) ) . toBe ( true ) ;
566+ } ) ;
567+
568+ test ( "field root has data-disabled when disabled" , async ( ) => {
569+ render ( < BasicInput disabled /> ) ;
570+
571+ await expect . element ( Root ) . toBeVisible ( ) ;
572+
573+ const rootElement = await Root . element ( ) ;
574+ expect ( rootElement ?. hasAttribute ( "data-disabled" ) ) . toBe ( true ) ;
575+ } ) ;
576+
577+ test ( "field root does not have data-disabled when not disabled" , async ( ) => {
578+ render ( < BasicInput /> ) ;
579+
580+ await expect . element ( Root ) . toBeVisible ( ) ;
581+
582+ const rootElement = await Root . element ( ) ;
583+ expect ( rootElement ?. hasAttribute ( "data-disabled" ) ) . toBe ( false ) ;
584+ } ) ;
585+
586+ test ( "field root has data-required when required" , async ( ) => {
587+ render ( < BasicInput required /> ) ;
588+
589+ await expect . element ( Root ) . toBeVisible ( ) ;
590+
591+ const rootElement = await Root . element ( ) ;
592+ expect ( rootElement ?. hasAttribute ( "data-required" ) ) . toBe ( true ) ;
593+ } ) ;
594+
595+ test ( "field root does not have data-required when not required" , async ( ) => {
596+ render ( < BasicInput /> ) ;
597+
598+ await expect . element ( Root ) . toBeVisible ( ) ;
599+
600+ const rootElement = await Root . element ( ) ;
601+ expect ( rootElement ?. hasAttribute ( "data-required" ) ) . toBe ( false ) ;
602+ } ) ;
603+
604+ test ( "field root has data-readonly when readonly" , async ( ) => {
605+ render ( < BasicInput readOnly /> ) ;
606+
607+ await expect . element ( Root ) . toBeVisible ( ) ;
608+
609+ const rootElement = await Root . element ( ) ;
610+ expect ( rootElement ?. hasAttribute ( "data-readonly" ) ) . toBe ( true ) ;
611+ } ) ;
612+
613+ test ( "field root does not have data-readonly when not readonly" , async ( ) => {
614+ render ( < BasicInput /> ) ;
615+
616+ await expect . element ( Root ) . toBeVisible ( ) ;
617+
618+ const rootElement = await Root . element ( ) ;
619+ expect ( rootElement ?. hasAttribute ( "data-readonly" ) ) . toBe ( false ) ;
620+ } ) ;
621+
622+ test ( "field root has data-empty when value is empty" , async ( ) => {
623+ render ( < BasicInput /> ) ;
624+
625+ await expect . element ( Root ) . toBeVisible ( ) ;
626+
627+ const rootElement = await Root . element ( ) ;
628+ expect ( rootElement ?. hasAttribute ( "data-empty" ) ) . toBe ( true ) ;
629+ } ) ;
630+
631+ const FilledValueInput = component$ ( ( ) => {
632+ const initialValue = useSignal ( "test value" ) ;
633+ return (
634+ < Field . Root data-testid = "root" bind :value = { initialValue } >
635+ < Field . Label data-testid = "label" > Username</ Field . Label >
636+ < Field . Input data-testid = "input" />
637+ </ Field . Root >
638+ ) ;
639+ } ) ;
640+
641+ test ( "field root does not have data-empty when value exists" , async ( ) => {
642+ render ( < FilledValueInput /> ) ;
643+
644+ await expect . element ( Root ) . toBeVisible ( ) ;
645+
646+ const rootElement = await Root . element ( ) ;
647+ expect ( rootElement ?. hasAttribute ( "data-empty" ) ) . toBe ( false ) ;
648+ } ) ;
649+
650+ test ( "field root data-empty updates when input changes from empty to filled" , async ( ) => {
651+ render ( < BasicInput /> ) ;
652+
653+ await expect . element ( Root ) . toBeVisible ( ) ;
654+
655+ const rootElement = await Root . element ( ) ;
656+ expect ( rootElement ?. hasAttribute ( "data-empty" ) ) . toBe ( true ) ;
657+
658+ await userEvent . fill ( Input , "test value" ) ;
659+
660+ expect ( rootElement ?. hasAttribute ( "data-empty" ) ) . toBe ( false ) ;
661+ } ) ;
662+
663+ test ( "field root data-empty updates when input changes from filled to empty" , async ( ) => {
664+ render ( < FilledValueInput /> ) ;
665+
666+ await expect . element ( Root ) . toBeVisible ( ) ;
667+
668+ const rootElement = await Root . element ( ) ;
669+ expect ( rootElement ?. hasAttribute ( "data-empty" ) ) . toBe ( false ) ;
670+
671+ await userEvent . clear ( Input ) ;
672+
673+ expect ( rootElement ?. hasAttribute ( "data-empty" ) ) . toBe ( true ) ;
674+ } ) ;
675+
676+ const DynamicStateInput = component$ ( ( ) => {
677+ const disabled = useSignal ( false ) ;
678+ const required = useSignal ( false ) ;
679+ const readOnly = useSignal ( false ) ;
680+
681+ return (
682+ < div >
683+ < Field . Root
684+ data-testid = "root"
685+ bind :disabled = { disabled }
686+ bind :required = { required }
687+ bind :readOnly = { readOnly }
688+ >
689+ < Field . Label data-testid = "label" > Username</ Field . Label >
690+ < Field . Input data-testid = "input" />
691+ </ Field . Root >
692+ < button
693+ type = "button"
694+ data-testid = "toggle-disabled"
695+ onClick$ = { ( ) => ( disabled . value = ! disabled . value ) }
696+ >
697+ Toggle Disabled
698+ </ button >
699+ < button
700+ type = "button"
701+ data-testid = "toggle-required"
702+ onClick$ = { ( ) => ( required . value = ! required . value ) }
703+ >
704+ Toggle Required
705+ </ button >
706+ < button
707+ type = "button"
708+ data-testid = "toggle-readonly"
709+ onClick$ = { ( ) => ( readOnly . value = ! readOnly . value ) }
710+ >
711+ Toggle Readonly
712+ </ button >
713+ </ div >
714+ ) ;
715+ } ) ;
716+
717+ test ( "field root data-disabled updates dynamically" , async ( ) => {
718+ render ( < DynamicStateInput /> ) ;
719+
720+ await expect . element ( Root ) . toBeVisible ( ) ;
721+ await expect . element ( Root ) . not . toHaveAttribute ( "data-disabled" ) ;
722+
723+ await userEvent . click ( page . getByTestId ( "toggle-disabled" ) ) ;
724+ await expect . element ( Root ) . toHaveAttribute ( "data-disabled" ) ;
725+
726+ await userEvent . click ( page . getByTestId ( "toggle-disabled" ) ) ;
727+ await expect . element ( Root ) . not . toHaveAttribute ( "data-disabled" ) ;
728+ } ) ;
729+
730+ test ( "field root data-required updates dynamically" , async ( ) => {
731+ render ( < DynamicStateInput /> ) ;
732+
733+ await expect . element ( Root ) . toBeVisible ( ) ;
734+ await expect . element ( Root ) . not . toHaveAttribute ( "data-required" ) ;
735+
736+ await userEvent . click ( page . getByTestId ( "toggle-required" ) ) ;
737+ await expect . element ( Root ) . toHaveAttribute ( "data-required" ) ;
738+
739+ await userEvent . click ( page . getByTestId ( "toggle-required" ) ) ;
740+ await expect . element ( Root ) . not . toHaveAttribute ( "data-required" ) ;
741+ } ) ;
742+
743+ test ( "field root data-readonly updates dynamically" , async ( ) => {
744+ render ( < DynamicStateInput /> ) ;
745+
746+ await expect . element ( Root ) . toBeVisible ( ) ;
747+ await expect . element ( Root ) . not . toHaveAttribute ( "data-readonly" ) ;
748+
749+ await userEvent . click ( page . getByTestId ( "toggle-readonly" ) ) ;
750+ await expect . element ( Root ) . toHaveAttribute ( "data-readonly" ) ;
751+
752+ await userEvent . click ( page . getByTestId ( "toggle-readonly" ) ) ;
753+ await expect . element ( Root ) . not . toHaveAttribute ( "data-readonly" ) ;
754+ } ) ;
755+
756+ const AllAttributesInput = component$ ( ( ) => {
757+ const fieldValue = useSignal ( "" ) ;
758+ return (
759+ < Field . Root data-testid = "root" bind :value = { fieldValue } disabled required readOnly >
760+ < Field . Label data-testid = "label" > Username</ Field . Label >
761+ < Field . Input data-testid = "input" />
762+ </ Field . Root >
763+ ) ;
764+ } ) ;
765+
766+ test ( "field root has all data attributes simultaneously" , async ( ) => {
767+ render ( < AllAttributesInput /> ) ;
768+
769+ await expect . element ( Root ) . toBeVisible ( ) ;
770+
771+ const rootElement = await Root . element ( ) ;
772+ expect ( rootElement ?. hasAttribute ( "data-qds-root" ) ) . toBe ( true ) ;
773+ expect ( rootElement ?. hasAttribute ( "data-disabled" ) ) . toBe ( true ) ;
774+ expect ( rootElement ?. hasAttribute ( "data-required" ) ) . toBe ( true ) ;
775+ expect ( rootElement ?. hasAttribute ( "data-readonly" ) ) . toBe ( true ) ;
776+ expect ( rootElement ?. hasAttribute ( "data-empty" ) ) . toBe ( true ) ;
777+ } ) ;
0 commit comments