@@ -28,6 +28,8 @@ const CSS_CLASS_DISABLED_ITEM = 'igx-drop-down__item--disabled';
2828const CSS_CLASS_FOCUSED_ITEM = 'igx-drop-down__item--focused' ;
2929const CSS_CLASS_INPUT_GROUP_BOX = 'igx-input-group--box' ;
3030const CSS_CLASS_INPUT_GROUP_REQUIRED = 'igx-input-group--required' ;
31+ const CSS_CLASS_INPUT_GROUP_INVALID = 'igx-input-group--invalid ' ;
32+ const CSS_CLASS_INPUT_GROUP_LABEL = 'igx-input-group__label' ;
3133const CSS_CLASS_INPUT_GROUP_BORDER = 'igx-input-group--border' ;
3234const CSS_CLASS_INPUT_GROUP_COMFORTABLE = 'igx-input-group--comfortable' ;
3335const CSS_CLASS_INPUT_GROUP_COSY = 'igx-input-group--cosy' ;
@@ -455,15 +457,15 @@ describe('igxSelect', () => {
455457 describe ( 'Form tests: ' , ( ) => {
456458 it ( 'Should properly initialize when used as a reactive form control - with validators' , fakeAsync ( ( ) => {
457459 const fix = TestBed . createComponent ( IgxSelectReactiveFormComponent ) ;
458- const inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
460+ const inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
459461 fix . detectChanges ( ) ;
460462 const selectComp = fix . componentInstance . select ;
461463 const selectFormReference = fix . componentInstance . reactiveForm . controls . optionsSelect ;
462464 expect ( selectFormReference ) . toBeDefined ( ) ;
463465 expect ( selectComp ) . toBeDefined ( ) ;
464466 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
465467 expect ( selectComp . value ) . toEqual ( '' ) ;
466- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
468+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
467469 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
468470
469471 selectComp . toggle ( ) ;
@@ -491,18 +493,57 @@ describe('igxSelect', () => {
491493
492494 it ( 'Should properly initialize when used as a reactive form control - without initial validators' , fakeAsync ( ( ) => {
493495 const fix = TestBed . createComponent ( IgxSelectReactiveFormComponent ) ;
494-
495- let inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
496496 fix . detectChanges ( ) ;
497+ // 1) check if label's --required class and its asterisk are applied
498+ const dom = fix . debugElement ;
497499 const selectComp = fix . componentInstance . select ;
498500 const formGroup : FormGroup = fix . componentInstance . reactiveForm ;
501+ let inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
502+ let inputGroupInvalidClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_INVALID ) ) ;
503+ // interaction test - expect actual asterisk
504+ // The only way to get a pseudo elements like :before OR :after is to use getComputedStyle(element [, pseudoElt]),
505+ // as these are not in the actual DOM
506+ let asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
507+ expect ( asterisk ) . toBe ( '"*"' ) ;
508+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
509+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
510+
511+ // 2) check that input group's --invalid class is NOT applied
512+ expect ( inputGroupInvalidClass ) . toBeNull ( ) ;
513+
514+ // interaction test - markAsTouched + open&close so the --invalid and --required classes are applied
515+ fix . debugElement . componentInstance . markAsTouched ( ) ;
516+ const inputGroup = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP ) ) ;
517+ inputGroup . nativeElement . click ( ) ;
518+ const toggleBtn = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_TOGGLE_BUTTON ) ) ;
519+ toggleBtn . nativeElement . click ( ) ;
520+ tick ( ) ;
521+ fix . detectChanges ( ) ;
522+ expect ( selectComp . collapsed ) . toEqual ( true ) ;
523+
524+ inputGroupInvalidClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_INVALID ) ) ;
525+ expect ( inputGroupInvalidClass ) . not . toBeNull ( ) ;
526+ expect ( inputGroupInvalidClass ) . not . toBeUndefined ( ) ;
527+
528+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
529+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
530+ expect ( inputGroupIsRequiredClass ) . not . toBeUndefined ( ) ;
531+
532+ // 3) Check if the input group's --invalid and --required classes are removed when validator is dynamically cleared
499533 fix . componentInstance . removeValidators ( formGroup ) ;
534+ fix . detectChanges ( ) ;
535+ tick ( ) ;
536+
537+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
500538 const selectFormReference = fix . componentInstance . reactiveForm . controls . optionsSelect ;
539+ // interaction test - expect no asterisk
540+ asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
501541 expect ( selectFormReference ) . toBeDefined ( ) ;
502542 expect ( selectComp ) . toBeDefined ( ) ;
503543 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
504544 expect ( selectComp . value ) . toEqual ( '' ) ;
505- expect ( inputGroupWithRequiredAsterisk ) . toBeNull ( ) ;
545+ expect ( inputGroupIsRequiredClass ) . toBeNull ( ) ;
546+ expect ( asterisk ) . toBe ( 'none' ) ;
506547 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
507548
508549 selectComp . onBlur ( ) ;
@@ -522,18 +563,42 @@ describe('igxSelect', () => {
522563
523564 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
524565
566+ // Re-add all Validators
525567 fix . componentInstance . addValidators ( formGroup ) ;
526568 fix . detectChanges ( ) ;
527- inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
528- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
529569
570+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
571+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
572+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
573+ expect ( inputGroupIsRequiredClass ) . not . toBeUndefined ( ) ;
574+ // interaction test - expect actual asterisk
575+ asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
576+ expect ( asterisk ) . toBe ( '"*"' ) ;
577+
578+ // 4) Should NOT remove asterisk, when remove validators on igxSelect with required HTML attribute set(edge case)
579+ // set required HTML attribute
580+ inputGroup . parent . nativeElement . setAttribute ( 'required' , '' ) ;
581+ // Re-add all Validators
582+ fix . componentInstance . addValidators ( formGroup ) ;
583+ fix . detectChanges ( ) ;
584+ // update and clear validators
585+ fix . componentInstance . removeValidators ( formGroup ) ;
586+ fix . detectChanges ( ) ;
587+ tick ( ) ;
588+ // expect asterisk
589+ asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
590+ expect ( asterisk ) . toBe ( '"*"' ) ;
591+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
592+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
593+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
594+ expect ( inputGroupIsRequiredClass ) . not . toBeUndefined ( ) ;
530595 } ) ) ;
531596
532597
533598 it ( 'Should properly initialize when used as a form control - with initial validators' , fakeAsync ( ( ) => {
534599 const fix = TestBed . createComponent ( IgxSelectTemplateFormComponent ) ;
535600
536- let inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
601+ let inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
537602 fix . detectChanges ( ) ;
538603 const selectComp = fix . componentInstance . select ;
539604 const selectFormReference = fix . componentInstance . ngForm . form ;
@@ -543,7 +608,7 @@ describe('igxSelect', () => {
543608 fix . detectChanges ( ) ;
544609 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
545610 expect ( selectComp . value ) . toBeNull ( ) ;
546- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
611+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
547612 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
548613
549614 selectComp . toggle ( ) ;
@@ -569,14 +634,14 @@ describe('igxSelect', () => {
569634
570635 fix . componentInstance . isRequired = false ;
571636 fix . detectChanges ( ) ;
572- inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
573- expect ( inputGroupWithRequiredAsterisk ) . toBeNull ( ) ;
637+ inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
638+ expect ( inputGroupIsRequiredClass ) . toBeNull ( ) ;
574639 } ) ) ;
575640
576641 it ( 'Should properly initialize when used as a form control - without initial validators' , fakeAsync ( ( ) => {
577642 const fix = TestBed . createComponent ( IgxSelectTemplateFormComponent ) ;
578643
579- let inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
644+ let inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
580645 fix . detectChanges ( ) ;
581646 const selectComp = fix . componentInstance . select ;
582647 const selectFormReference = fix . componentInstance . ngForm . form ;
@@ -585,7 +650,7 @@ describe('igxSelect', () => {
585650 expect ( selectComp ) . toBeDefined ( ) ;
586651 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
587652 expect ( selectComp . value ) . toBeUndefined ( ) ;
588- expect ( inputGroupWithRequiredAsterisk ) . toBeNull ( ) ;
653+ expect ( inputGroupIsRequiredClass ) . toBeNull ( ) ;
589654 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
590655
591656 selectComp . onBlur ( ) ;
@@ -607,8 +672,8 @@ describe('igxSelect', () => {
607672
608673 fix . componentInstance . isRequired = true ;
609674 fix . detectChanges ( ) ;
610- inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
611- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
675+ inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
676+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
612677 } ) ) ;
613678
614679 it ( 'Should have correctly bound focus and blur handlers' , ( ) => {
@@ -2821,6 +2886,17 @@ class IgxSelectReactiveFormComponent {
28212886 form . get ( key ) . updateValueAndValidity ( ) ;
28222887 }
28232888 }
2889+
2890+ public markAsTouched ( ) {
2891+ if ( ! this . reactiveForm . valid ) {
2892+ for ( const key in this . reactiveForm . controls ) {
2893+ if ( this . reactiveForm . controls [ key ] ) {
2894+ this . reactiveForm . controls [ key ] . markAsTouched ( ) ;
2895+ this . reactiveForm . controls [ key ] . updateValueAndValidity ( ) ;
2896+ }
2897+ }
2898+ }
2899+ }
28242900}
28252901
28262902@Component ( {
0 commit comments