From 3885913064dd2da68d62219c7e836a2483b6d2d1 Mon Sep 17 00:00:00 2001 From: Boris Date: Tue, 9 Mar 2021 12:19:31 +0200 Subject: [PATCH] fix(igx-input): add support for disabled attr without value --- .../directives/input/input.directive.spec.ts | 71 +++++++++++++++---- .../lib/directives/input/input.directive.ts | 24 ++++--- .../input-group/input-group.component.spec.ts | 35 ++++++++- 3 files changed, 106 insertions(+), 24 deletions(-) diff --git a/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts b/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts index 137c70fa7bc..8b368e1bb72 100644 --- a/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts @@ -1,6 +1,6 @@ import { Component, ViewChild, ViewChildren, QueryList, DebugElement } from '@angular/core'; import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; -import { FormsModule, FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { FormsModule, FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { IgxInputGroupComponent, IgxInputGroupModule } from '../../input-group/input-group.component'; import { IgxInputDirective, IgxInputState } from './input.directive'; @@ -32,6 +32,7 @@ describe('IgxInput', () => { RequiredInputComponent, RequiredTwoWayDataBoundInputComponent, DataBoundDisabledInputComponent, + DataBoundDisabledInputWithoutValueComponent, ReactiveFormComponent, InputsWithSameNameAttributesComponent, ToggleRequiredWithNgModelInputComponent @@ -42,7 +43,7 @@ describe('IgxInput', () => { ReactiveFormsModule ] }) - .compileComponents(); + .compileComponents(); })); it('Initializes an input.', () => { @@ -186,6 +187,25 @@ describe('IgxInput', () => { expect(igxInput.disabled).toBe(true); }); + it('should have disabled style if disabled attr is set without value', () => { + const fixture = TestBed.createComponent(DataBoundDisabledInputWithoutValueComponent); + fixture.detectChanges(); + + const igxInput = fixture.componentInstance.igxInput; + const inputElement = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement; + const inputGroupElement = fixture.debugElement.query(By.css('igx-input-group')).nativeElement; + expect(inputGroupElement.classList.contains(INPUT_GROUP_DISABLED_CSS_CLASS)).toBe(true); + expect(inputElement.disabled).toBe(true); + expect(igxInput.disabled).toBe(true); + + fixture.componentInstance.changeDisabledState(); + fixture.detectChanges(); + + expect(inputGroupElement.classList.contains(INPUT_GROUP_DISABLED_CSS_CLASS)).toBe(false); + expect(inputElement.disabled).toBe(false); + expect(igxInput.disabled).toBe(false); + }); + it('should style required input correctly.', () => { const fixture = TestBed.createComponent(RequiredInputComponent); fixture.detectChanges(); @@ -645,7 +665,8 @@ describe('IgxInput', () => { }); }); -@Component({ template: ` +@Component({ + template: `
@@ -666,7 +687,8 @@ class InputsWithSameNameAttributesComponent { } -@Component({ template: ` +@Component({ + template: ` ` }) @@ -675,14 +697,16 @@ class InputComponent { @ViewChild(IgxInputDirective, { static: true }) public igxInput: IgxInputDirective; } -@Component({ template: ` +@Component({ + template: ` ` }) class TextareaComponent { } -@Component({ template: ` +@Component({ + template: ` ` }) @@ -690,7 +714,8 @@ class InputWithPlaceholderComponent { @ViewChild(IgxInputDirective, { static: true }) public igxInput: IgxInputDirective; } -@Component({ template: ` +@Component({ + template: ` ` }) @@ -699,7 +724,8 @@ class FilledInputComponent { @ViewChild(IgxInputDirective, { static: true }) public igxInput: IgxInputDirective; } -@Component({ template: ` +@Component({ + template: ` ` }) @@ -708,7 +734,8 @@ class DisabledInputComponent { @ViewChild(IgxInputDirective, { static: true }) public igxInput: IgxInputDirective; } -@Component({ template: ` +@Component({ + template: ` ` }) @@ -717,7 +744,8 @@ class RequiredInputComponent { @ViewChild(IgxInputDirective, { static: true }) public igxInput: IgxInputDirective; } -@Component({ template: ` +@Component({ + template: ` ` }) @@ -730,7 +758,8 @@ class RequiredTwoWayDataBoundInputComponent { }; } -@Component({ template: ` +@Component({ + template: ` @@ -793,7 +822,8 @@ class InitiallyFilledInputComponent { }; } -@Component({ template: ` +@Component({ + template: ` ` }) @@ -804,9 +834,21 @@ class DataBoundDisabledInputComponent { public disabled = false; } +@Component({ + template: ` + + + ` +}) +class DataBoundDisabledInputWithoutValueComponent extends DataBoundDisabledInputComponent { + public changeDisabledState() { + this.igxInput.disabled = !this.igxInput.disabled; + } +} + @Component({ template: - ` + `
@@ -855,7 +897,8 @@ class ReactiveFormComponent { } } -@Component({ template: ` +@Component({ + template: ` diff --git a/projects/igniteui-angular/src/lib/directives/input/input.directive.ts b/projects/igniteui-angular/src/lib/directives/input/input.directive.ts index 3fb27dbc6c1..205bb05d88b 100644 --- a/projects/igniteui-angular/src/lib/directives/input/input.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/input/input.directive.ts @@ -9,7 +9,7 @@ import { Input, OnDestroy, Optional, - Self, + Self } from '@angular/core'; import { AbstractControl, @@ -98,6 +98,7 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy { private _valid = IgxInputState.INITIAL; private _statusChanges$: Subscription; private _fileNames: string; + private _disabled = false; constructor( public inputGroup: IgxInputGroupBase, @@ -108,7 +109,7 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy { protected formControl: FormControlName, protected element: ElementRef, protected cdr: ChangeDetectorRef - ) {} + ) { } private get ngControl(): NgControl { return this.ngModel ? this.ngModel : this.formControl; @@ -153,8 +154,10 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy { */ @Input() public set disabled(value: boolean) { - this.nativeElement.disabled = value; - this.inputGroup.disabled = value; + // handle case when disabled attr is set with no value + this._disabled = value != null && `${value}` !== 'false'; + this.nativeElement.disabled = this._disabled; + this.inputGroup.disabled = this._disabled; } /** * Gets the `disabled` property @@ -167,7 +170,10 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy { * ``` */ public get disabled() { - return this.nativeElement.hasAttribute('disabled'); + if (this.ngControl && this.ngControl.disabled !== null) { + return this.ngControl.disabled; + } + return this._disabled = this.nativeElement.disabled; } /** @@ -342,8 +348,8 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy { if (!this.disabled && (this.ngControl.control.touched || this.ngControl.control.dirty)) { // the control is not disabled and is touched or dirty this._valid = this.ngControl.invalid ? - IgxInputState.INVALID : this.focused ? IgxInputState.VALID : - IgxInputState.INITIAL; + IgxInputState.INVALID : this.focused ? IgxInputState.VALID : + IgxInputState.INITIAL; } else { // if control is untouched, pristine, or disabled its state is initial. This is when user did not interact // with the input or when form/control is reset @@ -449,8 +455,8 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy { private checkNativeValidity() { if (!this.disabled && this._hasValidators()) { this._valid = this.nativeElement.checkValidity() ? - this.focused ? IgxInputState.VALID : IgxInputState.INITIAL : - IgxInputState.INVALID; + this.focused ? IgxInputState.VALID : IgxInputState.INITIAL : + IgxInputState.INVALID; } } diff --git a/projects/igniteui-angular/src/lib/input-group/input-group.component.spec.ts b/projects/igniteui-angular/src/lib/input-group/input-group.component.spec.ts index a51114dab50..4e3002df900 100644 --- a/projects/igniteui-angular/src/lib/input-group/input-group.component.spec.ts +++ b/projects/igniteui-angular/src/lib/input-group/input-group.component.spec.ts @@ -30,6 +30,7 @@ describe('IgxInputGroup', () => { InputGroupDisabledComponent, InputGroupDisabledByDefaultComponent, InputGroupCosyDisplayDensityComponent, + InputGroupDisabledWithoutValueComponent, InputGroupCompactDisplayDensityComponent, InputGroupInputDisplayDensityComponent ], @@ -37,7 +38,7 @@ describe('IgxInputGroup', () => { IgxInputGroupModule, IgxIconModule ] }) - .compileComponents(); + .compileComponents(); })); it('Initializes an input group.', () => { @@ -147,6 +148,24 @@ describe('IgxInputGroup', () => { expect(igxInputGroup.disabled).toBeTruthy(); }); + it('should handle disabled attribute without value', () => { + pending(); + const fixture = TestBed.createComponent(InputGroupDisabledWithoutValueComponent); + fixture.detectChanges(); + + const component = fixture.componentInstance; + const igxInputGroup = component.igxInputGroup; + expect(igxInputGroup.disabled).toBeTruthy(); + + component.changeDisableState(); + fixture.detectChanges(); + expect(igxInputGroup.disabled).toBeFalsy(); + + component.changeDisableState(); + fixture.detectChanges(); + expect(igxInputGroup.disabled).toBeTruthy(); + }); + it('default Display Density applied', () => { const fixture = TestBed.createComponent(InputGroupDisabledByDefaultComponent); fixture.detectChanges(); @@ -348,6 +367,20 @@ class InputGroupDisabledComponent { } } +@Component({ + template: ` + + ` +}) +class InputGroupDisabledWithoutValueComponent { + @ViewChild('igxInputGroup') + public igxInputGroup: IgxInputGroupComponent; + + public changeDisableState() { + this.igxInputGroup.disabled = !this.igxInputGroup.disabled; + } +} + @Component({ template: `