Skip to content

Commit

Permalink
Merge pull request #6476 from IgniteUI/dpetev/select-control-touched-8.1
Browse files Browse the repository at this point in the history
Select - properly update FormControl touched
  • Loading branch information
rkaraivanov committed Jan 22, 2020
2 parents a4745d1 + 4ab0467 commit 1a425aa
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
[attr.aria-owns]="this.listId"
[attr.aria-activedescendant]="!this.collapsed ? this.focusedItem?.id : null"
(blur)="onBlur()"
(focus)="onFocus()"
/>
<ng-container ngProjectAs="igx-suffix">
<ng-content select="igx-suffix,[igxSuffix]"></ng-content>
Expand Down
73 changes: 71 additions & 2 deletions projects/igniteui-angular/src/lib/select/select.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { IgxInputState } from './../directives/input/input.directive';
import { Component, ViewChild, DebugElement, OnInit } from '@angular/core';
import { async, TestBed, tick, fakeAsync } from '@angular/core/testing';
import { FormsModule, FormGroup, FormBuilder, FormControl, Validators, ReactiveFormsModule, NgForm } from '@angular/forms';
import { FormsModule, FormGroup, FormBuilder, FormControl, Validators, ReactiveFormsModule, NgForm, NgControl } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { IgxDropDownModule } from '../drop-down/index';
import { IgxDropDownModule, IgxDropDownItemComponent } from '../drop-down/index';
import { IgxIconModule } from '../icon/index';
import { IgxInputGroupModule } from '../input-group/index';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
Expand Down Expand Up @@ -604,6 +604,24 @@ describe('igxSelect', () => {
inputGroupWithRequiredAsterisk = fix.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED));
expect(inputGroupWithRequiredAsterisk).toBeDefined();
}));

it('Should have correctly bound focus and blur handlers', () => {
const fix = TestBed.createComponent(IgxSelectTemplateFormComponent);
fix.detectChanges();
select = fix.componentInstance.select;
const input = fix.debugElement.query(By.css(`.${CSS_CLASS_INPUT}`));

spyOn(select, 'onFocus');
spyOn(select, 'onBlur');

input.triggerEventHandler('focus', {});
expect(select.onFocus).toHaveBeenCalled();
expect(select.onFocus).toHaveBeenCalledWith();

input.triggerEventHandler('blur', {});
expect(select.onBlur).toHaveBeenCalled();
expect(select.onFocus).toHaveBeenCalledWith();
});
});
describe('Selection tests: ', () => {
beforeEach(async(() => {
Expand Down Expand Up @@ -2248,6 +2266,57 @@ describe('igxSelect', () => {
});
});

describe('igxSelect ControlValueAccessor Unit', () => {
let select: IgxSelectComponent;
it('Should correctly implement interface methods', () => {
const mockSelection = jasmine.createSpyObj('IgxSelectionAPIService', ['get', 'set', 'clear', 'first_item']);
const mockCdr = jasmine.createSpyObj('ChangeDetectorRef', ['detectChanges']);
const mockNgControl = jasmine.createSpyObj('NgControl', ['registerOnChangeCb', 'registerOnTouchedCb']);
const mockInjector = jasmine.createSpyObj('Injector', {
'get': mockNgControl
});

// init
select = new IgxSelectComponent(null, mockCdr, mockSelection, null, mockInjector);
select.ngOnInit();
select.registerOnChange(mockNgControl.registerOnChangeCb);
select.registerOnTouched(mockNgControl.registerOnTouchedCb);
expect(mockInjector.get).toHaveBeenCalledWith(NgControl, null);

// writeValue
expect(select.value).toBeUndefined();
select.writeValue('test');
expect(mockSelection.clear).toHaveBeenCalled();
expect(select.value).toBe('test');

// setDisabledState
select.setDisabledState(true);
expect(select.disabled).toBe(true);
select.setDisabledState(false);
expect(select.disabled).toBe(false);

// OnChange callback
const item = new IgxDropDownItemComponent(select, null, null, mockSelection);
item.value = 'itemValue';
select.selectItem(item);
expect(mockSelection.set).toHaveBeenCalledWith(select.id, new Set([item]));
expect(mockNgControl.registerOnChangeCb).toHaveBeenCalledWith('itemValue');

// OnTouched callback
select.onFocus();
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(1);

select.input = {} as any;
spyOnProperty(select, 'collapsed').and.returnValue(true);
select.onBlur();
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(2);
});

it('Should correctly handle ngControl validity', () => {
pending('Convert existing form test here');
});
});

@Component({
template: `
<igx-select #select [width]="'300px'" [height]="'200px'" [placeholder]="'Choose a city'" [(ngModel)]="value">
Expand Down
20 changes: 19 additions & 1 deletion projects/igniteui-angular/src/lib/select/select.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,12 @@ export class IgxSelectComponent extends IgxDropDownComponent implements IgxSelec
super(elementRef, cdr, selection, _displayDensityOptions);
}

//#region ControlValueAccessor

/** @hidden @internal */
private _onChangeCallback: (_: any) => void = noop;
/** @hidden @internal */
private _onTouchedCallback: () => void = noop;

/** @hidden @internal */
public writeValue = (value: any) => {
Expand All @@ -216,7 +220,15 @@ export class IgxSelectComponent extends IgxDropDownComponent implements IgxSelec
}

/** @hidden @internal */
public registerOnTouched(fn: any): void { }
public registerOnTouched(fn: any): void {
this._onTouchedCallback = fn;
}

/** @hidden @internal */
public setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
//#endregion

/** @hidden @internal */
public getEditElement(): HTMLElement {
Expand Down Expand Up @@ -320,6 +332,7 @@ export class IgxSelectComponent extends IgxDropDownComponent implements IgxSelec

/** @hidden @internal */
public onBlur(): void {
this._onTouchedCallback();
if (this.ngControl && !this.ngControl.valid) {
this.input.valid = IgxInputState.INVALID;
} else {
Expand All @@ -330,6 +343,11 @@ export class IgxSelectComponent extends IgxDropDownComponent implements IgxSelec
}
}

/** @hidden @internal */
public onFocus(): void {
this._onTouchedCallback();
}

protected onStatusChanged() {
if ((this.ngControl.control.touched || this.ngControl.control.dirty) &&
(this.ngControl.control.validator || this.ngControl.control.asyncValidator)) {
Expand Down
11 changes: 8 additions & 3 deletions src/app/select/select.sample.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ <h4 class="sample-title">Select with ngModel, set items OnInit</h4>
[required]="true"
[placeholder]="'Pick One'"
[(ngModel)]="value"
[ngModelOptions]="{updateOn: 'blur'}" #selectModel="ngModel"
required
(onOpening)="testOnOpening($event)"
(onOpened)="testOnOpened()"
(onClosing)="testOnClosing($event)"
Expand All @@ -24,12 +26,15 @@ <h4 class="sample-title">Select with ngModel, set items OnInit</h4>
{{ item.field }}
</igx-select-item>
</igx-select>
<div>Model: {{selectModel.value}}</div>

<div>
<h4>Display Density</h4>
<button igxButton="raised" [disabled]="selectDisplayDensity.displayDensity === compact" (click)="setDensity(compact)">Compact</button>
<button igxButton="raised" [disabled]="selectDisplayDensity.displayDensity === cosy" (click)="setDensity(cosy)">Cosy</button>
<button igxButton="raised" [disabled]="selectDisplayDensity.displayDensity === comfortable" (click)="setDensity(comfortable)">Comfortable</button>
<igx-buttongroup (onSelect)="setDensity($event)">
<button igxButton value="compact">Compact</button>
<button igxButton value="cosy">Cosy</button>
<button igxButton value="comfortable">Comfortable</button>
</igx-buttongroup>
</div>

<h4 class="sample-title">Select - declare items in html template</h4>
Expand Down
13 changes: 5 additions & 8 deletions src/app/select/select.sample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
HorizontalAlignment, VerticalAlignment, scaleInTop, scaleOutBottom, ConnectedPositioningStrategy,
AbsoluteScrollStrategy,
IgxSelectComponent,
DisplayDensity
DisplayDensity,
IButtonGroupEventArgs
} from 'igniteui-angular';

@Component({
Expand All @@ -23,10 +24,6 @@ export class SelectSampleComponent implements OnInit {
@ViewChild('displayDensitySelect', { read: IgxSelectComponent, static: true })
public selectDisplayDensity: IgxSelectComponent;

public comfortable = DisplayDensity.comfortable;
public cosy = DisplayDensity.cosy;
public compact = DisplayDensity.compact;

constructor(fb: FormBuilder) {
this.reactiveForm = fb.group({
'citiesSelect': ['', Validators.required]
Expand Down Expand Up @@ -64,7 +61,7 @@ export class SelectSampleComponent implements OnInit {
public onSubmitReactive() { }

public selectBanana() {
this.selectFruits.selectItem(this.selectFruits.items[3]);
this.selectFruits.setSelectedItem(3);
}

public setToNull() {
Expand Down Expand Up @@ -144,7 +141,7 @@ export class SelectSampleComponent implements OnInit {
}
}

setDensity(density: DisplayDensity) {
this.selectDisplayDensity.displayDensity = density;
setDensity(event: IButtonGroupEventArgs) {
this.selectDisplayDensity.displayDensity = event.button.nativeElement.value;
}
}

0 comments on commit 1a425aa

Please sign in to comment.