Skip to content

Commit adb66b8

Browse files
authored
Merge branch '10.1.x' into astaev/issue7569-master
2 parents 59c12a5 + e7edbae commit adb66b8

File tree

2 files changed

+99
-15
lines changed

2 files changed

+99
-15
lines changed

projects/igniteui-angular/src/lib/select/select.component.spec.ts

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const CSS_CLASS_DISABLED_ITEM = 'igx-drop-down__item--disabled';
2828
const CSS_CLASS_FOCUSED_ITEM = 'igx-drop-down__item--focused';
2929
const CSS_CLASS_INPUT_GROUP_BOX = 'igx-input-group--box';
3030
const 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';
3133
const CSS_CLASS_INPUT_GROUP_BORDER = 'igx-input-group--border';
3234
const CSS_CLASS_INPUT_GROUP_COMFORTABLE = 'igx-input-group--comfortable';
3335
const 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({

projects/igniteui-angular/src/lib/select/select.component.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,11 +402,19 @@ export class IgxSelectComponent extends IgxDropDownComponent implements IgxSelec
402402
}
403403

404404
protected manageRequiredAsterisk(): void {
405+
const hasRequiredHTMLAttribute = this.elementRef.nativeElement.hasAttribute('required');
405406
if (this.ngControl && this.ngControl.control.validator) {
406407
// Run the validation with empty object to check if required is enabled.
407408
const error = this.ngControl.control.validator({} as AbstractControl);
408409
this.inputGroup.isRequired = error && error.required;
409410
this.cdr.markForCheck();
411+
412+
// If validator is dynamically cleared and no required HTML attribute is set,
413+
// reset label's required class(asterisk) and IgxInputState #6896
414+
} else if (this.inputGroup.isRequired && this.ngControl && !this.ngControl.control.validator && !hasRequiredHTMLAttribute) {
415+
this.input.valid = IgxInputState.INITIAL;
416+
this.inputGroup.isRequired = false;
417+
this.cdr.markForCheck();
410418
}
411419
}
412420
private setSelection(item: IgxDropDownItemBaseDirective) {

0 commit comments

Comments
 (0)