|
1 | 1 | import { Component, ViewChild, ViewChildren, QueryList, DebugElement } from '@angular/core';
|
2 | 2 | import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
|
3 |
| -import { FormsModule, FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; |
| 3 | +import { FormsModule, FormBuilder, ReactiveFormsModule, Validators, FormControl, FormGroup } from '@angular/forms'; |
4 | 4 | import { By } from '@angular/platform-browser';
|
5 | 5 | import { IgxInputGroupComponent, IgxInputGroupModule } from '../../input-group/input-group.component';
|
6 | 6 | import { IgxInputDirective, IgxInputState } from './input.directive';
|
7 | 7 | import { configureTestSuite } from '../../test-utils/configure-suite';
|
8 | 8 |
|
9 | 9 | const INPUT_CSS_CLASS = 'igx-input-group__input';
|
| 10 | +const CSS_CLASS_INPUT_GROUP_LABEL = 'igx-input-group__label'; |
10 | 11 | const TEXTAREA_CSS_CLASS = 'igx-input-group__textarea';
|
11 | 12 |
|
12 | 13 | const INPUT_GROUP_FOCUSED_CSS_CLASS = 'igx-input-group--focused';
|
@@ -35,7 +36,8 @@ describe('IgxInput', () => {
|
35 | 36 | DataBoundDisabledInputWithoutValueComponent,
|
36 | 37 | ReactiveFormComponent,
|
37 | 38 | InputsWithSameNameAttributesComponent,
|
38 |
| - ToggleRequiredWithNgModelInputComponent |
| 39 | + ToggleRequiredWithNgModelInputComponent, |
| 40 | + InputReactiveFormComponent |
39 | 41 | ],
|
40 | 42 | imports: [
|
41 | 43 | IgxInputGroupModule,
|
@@ -521,11 +523,16 @@ describe('IgxInput', () => {
|
521 | 523 | fixture.detectChanges();
|
522 | 524 | const igxInput = fixture.componentInstance.strIgxInput;
|
523 | 525 |
|
| 526 | + expect(igxInput.disabled).toBe(false); |
| 527 | + expect(igxInput.inputGroup.disabled).toBe(false); |
| 528 | + |
524 | 529 | fixture.componentInstance.form.disable();
|
525 | 530 | expect(igxInput.disabled).toBe(true);
|
| 531 | + expect(igxInput.inputGroup.disabled).toBe(true); |
526 | 532 |
|
527 | 533 | fixture.componentInstance.form.get('str').enable();
|
528 | 534 | expect(igxInput.disabled).toBe(false);
|
| 535 | + expect(igxInput.inputGroup.disabled).toBe(false); |
529 | 536 | }));
|
530 | 537 |
|
531 | 538 | it('should style input when required is toggled dynamically.', () => {
|
@@ -663,6 +670,69 @@ describe('IgxInput', () => {
|
663 | 670 | igxInput.value = 'Test';
|
664 | 671 | expect(igxInput.value).toBe('Test');
|
665 | 672 | });
|
| 673 | + |
| 674 | + it('Should properly initialize when used as a reactive form control - toggle validators', fakeAsync(() => { |
| 675 | + const fix = TestBed.createComponent(InputReactiveFormComponent); |
| 676 | + fix.detectChanges(); |
| 677 | + // 1) check if label's --required class and its asterisk are applied |
| 678 | + const dom = fix.debugElement; |
| 679 | + const input = fix.componentInstance.input; |
| 680 | + const inputGroup = fix.componentInstance.igxInputGroup.element.nativeElement; |
| 681 | + const formGroup: FormGroup = fix.componentInstance.reactiveForm; |
| 682 | + |
| 683 | + // interaction test - expect actual asterisk |
| 684 | + // The only way to get a pseudo elements like :before OR :after is to use getComputedStyle(element [, pseudoElt]), |
| 685 | + // as these are not in the actual DOM |
| 686 | + let asterisk = window.getComputedStyle(dom.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').content; |
| 687 | + expect(asterisk).toBe('"*"'); |
| 688 | + expect(inputGroup.classList.contains(INPUT_GROUP_REQUIRED_CSS_CLASS)).toBe(true); |
| 689 | + |
| 690 | + // 2) check that input group's --invalid class is NOT applied |
| 691 | + expect(inputGroup.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false); |
| 692 | + |
| 693 | + // interaction test - focus&blur, so the --invalid and --required classes are applied |
| 694 | + // *Use markAsTouched() instead of user interaction ( calling focus + blur) because: |
| 695 | + // Angular handles blur and marks the component as touched, however: |
| 696 | + // in order to ensure Angular handles blur prior to our blur handler (where we check touched), |
| 697 | + // we have to call blur twice. |
| 698 | + fix.debugElement.componentInstance.markAsTouched(); |
| 699 | + tick(); |
| 700 | + fix.detectChanges(); |
| 701 | + |
| 702 | + expect(inputGroup.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true); |
| 703 | + expect(inputGroup.classList.contains(INPUT_GROUP_REQUIRED_CSS_CLASS)).toBe(true); |
| 704 | + |
| 705 | + // 3) Check if the input group's --invalid and --required classes are removed when validator is dynamically cleared |
| 706 | + fix.componentInstance.removeValidators(formGroup); |
| 707 | + fix.detectChanges(); |
| 708 | + tick(); |
| 709 | + |
| 710 | + const formReference = fix.componentInstance.reactiveForm.controls.fullName; |
| 711 | + // interaction test - expect no asterisk |
| 712 | + asterisk = window.getComputedStyle(dom.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').content; |
| 713 | + expect(formReference).toBeDefined(); |
| 714 | + expect(input).toBeDefined(); |
| 715 | + expect(input.nativeElement.value).toEqual(''); |
| 716 | + expect(inputGroup.classList.contains(INPUT_GROUP_REQUIRED_CSS_CLASS)).toEqual(false); |
| 717 | + expect(asterisk).toBe('none'); |
| 718 | + expect(input.valid).toEqual(IgxInputState.INITIAL); |
| 719 | + |
| 720 | + // interact with the input and expect no changes |
| 721 | + input.nativeElement.dispatchEvent(new Event('focus')); |
| 722 | + input.nativeElement.dispatchEvent(new Event('blur')); |
| 723 | + tick(); |
| 724 | + fix.detectChanges(); |
| 725 | + expect(input.valid).toEqual(IgxInputState.INITIAL); |
| 726 | + |
| 727 | + // Re-add all Validators |
| 728 | + fix.componentInstance.addValidators(formGroup); |
| 729 | + fix.detectChanges(); |
| 730 | + |
| 731 | + expect(inputGroup.classList.contains(INPUT_GROUP_REQUIRED_CSS_CLASS)).toBe(true); |
| 732 | + // interaction test - expect actual asterisk |
| 733 | + asterisk = window.getComputedStyle(dom.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').content; |
| 734 | + expect(asterisk).toBe('"*"'); |
| 735 | + })); |
666 | 736 | });
|
667 | 737 |
|
668 | 738 | @Component({
|
@@ -917,6 +987,67 @@ class ToggleRequiredWithNgModelInputComponent {
|
917 | 987 | public data1 = '';
|
918 | 988 | public isRequired = false;
|
919 | 989 | }
|
| 990 | +@Component({ |
| 991 | + template: ` |
| 992 | + <form [formGroup]="reactiveForm" (ngSubmit)="onSubmitReactive()"> |
| 993 | + <igx-input-group #igxInputGroup> |
| 994 | + <input igxInput #inputReactive name="fullName" type="text" formControlName="fullName" /> |
| 995 | + <label igxLabel for="fullName">Full Name</label> |
| 996 | + <igx-suffix> |
| 997 | + <igx-icon>person</igx-icon> |
| 998 | + </igx-suffix> |
| 999 | + </igx-input-group> |
| 1000 | + </form> |
| 1001 | +` |
| 1002 | +}) |
| 1003 | + |
| 1004 | +class InputReactiveFormComponent { |
| 1005 | + @ViewChild('igxInputGroup', { static: true }) public igxInputGroup: IgxInputGroupComponent; |
| 1006 | + @ViewChild('inputReactive', { read: IgxInputDirective }) public input: IgxInputDirective; |
| 1007 | + public reactiveForm: FormGroup; |
| 1008 | + |
| 1009 | + public validationType = { |
| 1010 | + fullName: [Validators.required] |
| 1011 | + }; |
| 1012 | + |
| 1013 | + constructor(fb: FormBuilder) { |
| 1014 | + this.reactiveForm = fb.group({ |
| 1015 | + fullName: new FormControl('', Validators.required) |
| 1016 | + }); |
| 1017 | + } |
| 1018 | + public onSubmitReactive() { } |
| 1019 | + |
| 1020 | + public removeValidators(form: FormGroup) { |
| 1021 | + for (const key in form.controls) { |
| 1022 | + if (form.controls.hasOwnProperty(key)) { |
| 1023 | + form.get(key).clearValidators(); |
| 1024 | + form.get(key).updateValueAndValidity(); |
| 1025 | + } |
| 1026 | + } |
| 1027 | + } |
| 1028 | + |
| 1029 | + public addValidators(form: FormGroup) { |
| 1030 | + for (const key in form.controls) { |
| 1031 | + if (form.controls.hasOwnProperty(key)) { |
| 1032 | + form.get(key).setValidators(this.validationType[key]); |
| 1033 | + form.get(key).updateValueAndValidity(); |
| 1034 | + } |
| 1035 | + } |
| 1036 | + } |
| 1037 | + |
| 1038 | + public markAsTouched() { |
| 1039 | + if (!this.reactiveForm.valid) { |
| 1040 | + for (const key in this.reactiveForm.controls) { |
| 1041 | + if (this.reactiveForm.controls.hasOwnProperty(key)) { |
| 1042 | + if (this.reactiveForm.controls[key]) { |
| 1043 | + this.reactiveForm.controls[key].markAsTouched(); |
| 1044 | + this.reactiveForm.controls[key].updateValueAndValidity(); |
| 1045 | + } |
| 1046 | + } |
| 1047 | + } |
| 1048 | + } |
| 1049 | + } |
| 1050 | +} |
920 | 1051 |
|
921 | 1052 | const testRequiredValidation = (inputElement, fixture) => {
|
922 | 1053 | dispatchInputEvent('focus', inputElement, fixture);
|
|
0 commit comments