@@ -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' ;
@@ -456,15 +458,15 @@ describe('igxSelect', () => {
456458 describe ( 'Form tests: ' , ( ) => {
457459 it ( 'Should properly initialize when used as a reactive form control - with validators' , fakeAsync ( ( ) => {
458460 const fix = TestBed . createComponent ( IgxSelectReactiveFormComponent ) ;
459- const inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
461+ const inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
460462 fix . detectChanges ( ) ;
461463 const selectComp = fix . componentInstance . select ;
462464 const selectFormReference = fix . componentInstance . reactiveForm . controls . optionsSelect ;
463465 expect ( selectFormReference ) . toBeDefined ( ) ;
464466 expect ( selectComp ) . toBeDefined ( ) ;
465467 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
466468 expect ( selectComp . value ) . toEqual ( '' ) ;
467- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
469+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
468470 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
469471
470472 selectComp . toggle ( ) ;
@@ -492,18 +494,57 @@ describe('igxSelect', () => {
492494
493495 it ( 'Should properly initialize when used as a reactive form control - without initial validators' , fakeAsync ( ( ) => {
494496 const fix = TestBed . createComponent ( IgxSelectReactiveFormComponent ) ;
495-
496- let inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
497497 fix . detectChanges ( ) ;
498+ // 1) check if label's --required class and its asterisk are applied
499+ const dom = fix . debugElement ;
498500 const selectComp = fix . componentInstance . select ;
499501 const formGroup : FormGroup = fix . componentInstance . reactiveForm ;
502+ let inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
503+ let inputGroupInvalidClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_INVALID ) ) ;
504+ // interaction test - expect actual asterisk
505+ // The only way to get a pseudo elements like :before OR :after is to use getComputedStyle(element [, pseudoElt]),
506+ // as these are not in the actual DOM
507+ let asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
508+ expect ( asterisk ) . toBe ( '"*"' ) ;
509+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
510+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
511+
512+ // 2) check that input group's --invalid class is NOT applied
513+ expect ( inputGroupInvalidClass ) . toBeNull ( ) ;
514+
515+ // interaction test - markAsTouched + open&close so the --invalid and --required classes are applied
516+ fix . debugElement . componentInstance . markAsTouched ( ) ;
517+ const inputGroup = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP ) ) ;
518+ inputGroup . nativeElement . click ( ) ;
519+ const toggleBtn = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_TOGGLE_BUTTON ) ) ;
520+ toggleBtn . nativeElement . click ( ) ;
521+ tick ( ) ;
522+ fix . detectChanges ( ) ;
523+ expect ( selectComp . collapsed ) . toEqual ( true ) ;
524+
525+ inputGroupInvalidClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_INVALID ) ) ;
526+ expect ( inputGroupInvalidClass ) . not . toBeNull ( ) ;
527+ expect ( inputGroupInvalidClass ) . not . toBeUndefined ( ) ;
528+
529+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
530+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
531+ expect ( inputGroupIsRequiredClass ) . not . toBeUndefined ( ) ;
532+
533+ // 3) Check if the input group's --invalid and --required classes are removed when validator is dynamically cleared
500534 fix . componentInstance . removeValidators ( formGroup ) ;
535+ fix . detectChanges ( ) ;
536+ tick ( ) ;
537+
538+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
501539 const selectFormReference = fix . componentInstance . reactiveForm . controls . optionsSelect ;
540+ // interaction test - expect no asterisk
541+ asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
502542 expect ( selectFormReference ) . toBeDefined ( ) ;
503543 expect ( selectComp ) . toBeDefined ( ) ;
504544 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
505545 expect ( selectComp . value ) . toEqual ( '' ) ;
506- expect ( inputGroupWithRequiredAsterisk ) . toBeNull ( ) ;
546+ expect ( inputGroupIsRequiredClass ) . toBeNull ( ) ;
547+ expect ( asterisk ) . toBe ( 'none' ) ;
507548 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
508549
509550 selectComp . onBlur ( ) ;
@@ -523,18 +564,42 @@ describe('igxSelect', () => {
523564
524565 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
525566
567+ // Re-add all Validators
526568 fix . componentInstance . addValidators ( formGroup ) ;
527569 fix . detectChanges ( ) ;
528- inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
529- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
530570
571+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
572+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
573+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
574+ expect ( inputGroupIsRequiredClass ) . not . toBeUndefined ( ) ;
575+ // interaction test - expect actual asterisk
576+ asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
577+ expect ( asterisk ) . toBe ( '"*"' ) ;
578+
579+ // 4) Should NOT remove asterisk, when remove validators on igxSelect with required HTML attribute set(edge case)
580+ // set required HTML attribute
581+ inputGroup . parent . nativeElement . setAttribute ( 'required' , '' ) ;
582+ // Re-add all Validators
583+ fix . componentInstance . addValidators ( formGroup ) ;
584+ fix . detectChanges ( ) ;
585+ // update and clear validators
586+ fix . componentInstance . removeValidators ( formGroup ) ;
587+ fix . detectChanges ( ) ;
588+ tick ( ) ;
589+ // expect asterisk
590+ asterisk = window . getComputedStyle ( dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_LABEL ) ) . nativeElement , ':after' ) . content ;
591+ expect ( asterisk ) . toBe ( '"*"' ) ;
592+ inputGroupIsRequiredClass = dom . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
593+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
594+ expect ( inputGroupIsRequiredClass ) . not . toBeNull ( ) ;
595+ expect ( inputGroupIsRequiredClass ) . not . toBeUndefined ( ) ;
531596 } ) ) ;
532597
533598
534599 it ( 'Should properly initialize when used as a form control - with initial validators' , fakeAsync ( ( ) => {
535600 const fix = TestBed . createComponent ( IgxSelectTemplateFormComponent ) ;
536601
537- let inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
602+ let inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
538603 fix . detectChanges ( ) ;
539604 const selectComp = fix . componentInstance . select ;
540605 const selectFormReference = fix . componentInstance . ngForm . form ;
@@ -544,7 +609,7 @@ describe('igxSelect', () => {
544609 fix . detectChanges ( ) ;
545610 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
546611 expect ( selectComp . value ) . toBeNull ( ) ;
547- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
612+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
548613 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
549614
550615 selectComp . toggle ( ) ;
@@ -570,14 +635,14 @@ describe('igxSelect', () => {
570635
571636 fix . componentInstance . isRequired = false ;
572637 fix . detectChanges ( ) ;
573- inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
574- expect ( inputGroupWithRequiredAsterisk ) . toBeNull ( ) ;
638+ inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
639+ expect ( inputGroupIsRequiredClass ) . toBeNull ( ) ;
575640 } ) ) ;
576641
577642 it ( 'Should properly initialize when used as a form control - without initial validators' , fakeAsync ( ( ) => {
578643 const fix = TestBed . createComponent ( IgxSelectTemplateFormComponent ) ;
579644
580- let inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
645+ let inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
581646 fix . detectChanges ( ) ;
582647 const selectComp = fix . componentInstance . select ;
583648 const selectFormReference = fix . componentInstance . ngForm . form ;
@@ -586,7 +651,7 @@ describe('igxSelect', () => {
586651 expect ( selectComp ) . toBeDefined ( ) ;
587652 expect ( selectComp . selectedItem ) . toBeUndefined ( ) ;
588653 expect ( selectComp . value ) . toBeUndefined ( ) ;
589- expect ( inputGroupWithRequiredAsterisk ) . toBeNull ( ) ;
654+ expect ( inputGroupIsRequiredClass ) . toBeNull ( ) ;
590655 expect ( selectComp . input . valid ) . toEqual ( IgxInputState . INITIAL ) ;
591656
592657 selectComp . onBlur ( ) ;
@@ -608,8 +673,8 @@ describe('igxSelect', () => {
608673
609674 fix . componentInstance . isRequired = true ;
610675 fix . detectChanges ( ) ;
611- inputGroupWithRequiredAsterisk = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
612- expect ( inputGroupWithRequiredAsterisk ) . toBeDefined ( ) ;
676+ inputGroupIsRequiredClass = fix . debugElement . query ( By . css ( '.' + CSS_CLASS_INPUT_GROUP_REQUIRED ) ) ;
677+ expect ( inputGroupIsRequiredClass ) . toBeDefined ( ) ;
613678 } ) ) ;
614679
615680 it ( 'Should have correctly bound focus and blur handlers' , ( ) => {
@@ -2796,6 +2861,17 @@ class IgxSelectReactiveFormComponent {
27962861 form . get ( key ) . updateValueAndValidity ( ) ;
27972862 }
27982863 }
2864+
2865+ public markAsTouched ( ) {
2866+ if ( ! this . reactiveForm . valid ) {
2867+ for ( const key in this . reactiveForm . controls ) {
2868+ if ( this . reactiveForm . controls [ key ] ) {
2869+ this . reactiveForm . controls [ key ] . markAsTouched ( ) ;
2870+ this . reactiveForm . controls [ key ] . updateValueAndValidity ( ) ;
2871+ }
2872+ }
2873+ }
2874+ }
27992875}
28002876
28012877@Component ( {
0 commit comments