Skip to content

Commit de08a80

Browse files
authored
Merge branch '9.1.x' into ddincheva/selectionHGrid-9.1
2 parents eaa6aa9 + d4b865c commit de08a80

File tree

6 files changed

+129
-83
lines changed

6 files changed

+129
-83
lines changed

projects/igniteui-angular/src/lib/combo/combo.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<ng-content select="igx-hint, [igxHint]"></ng-content>
1010
</ng-container>
1111
<input igxInput #comboInput name="comboInput" type="text" [value]="value" readonly [attr.placeholder]="placeholder"
12-
[disabled]="disabled" (blur)="onBlur()" (focus)="onFocus()"/>
12+
[disabled]="disabled" (blur)="onBlur()" />
1313
<ng-container ngProjectAs="igx-suffix">
1414
<ng-content select="igx-suffix"></ng-content>
1515
</ng-container>

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

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { AfterViewInit, ChangeDetectorRef, Component, Injectable, OnInit, ViewChild, OnDestroy, DebugElement } from '@angular/core';
2-
import { async, TestBed, tick, fakeAsync } from '@angular/core/testing';
2+
import { async, TestBed, tick, fakeAsync, ComponentFixture } from '@angular/core/testing';
33
import { By } from '@angular/platform-browser';
44
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
5-
import { FormGroup, FormControl, Validators, FormBuilder, ReactiveFormsModule, FormsModule, NgControl } from '@angular/forms';
5+
import { FormGroup, FormControl, Validators, FormBuilder, ReactiveFormsModule, FormsModule, NgControl, NgModel } from '@angular/forms';
66
import { IgxComboComponent, IgxComboModule, IComboSelectionChangeEventArgs, IgxComboState,
77
IComboSearchInputEventArgs } from './combo.component';
88
import { IgxComboItemComponent } from './combo-item.component';
@@ -60,7 +60,7 @@ const defaultDropdownItemHeight = 40;
6060
const defaultDropdownItemMaxHeight = 400;
6161

6262
describe('igxCombo', () => {
63-
let fixture;
63+
let fixture: ComponentFixture<any>;
6464
let combo: IgxComboComponent;
6565
let input: DebugElement;
6666

@@ -94,12 +94,10 @@ describe('igxCombo', () => {
9494

9595
// writeValue
9696
expect(combo.value).toBe('');
97-
mockSelection.add_items.and.returnValue(new Set(['test']));
97+
mockSelection.get.and.returnValue(new Set(['test']));
9898
spyOnProperty(combo, 'isRemote').and.returnValue(false);
9999
combo.writeValue(['test']);
100-
// TODO: Uncomment after fix for write value going through entire selection process
101-
// expect(mockNgControl.registerOnChangeCb).not.toHaveBeenCalled();
102-
expect(mockSelection.add_items).toHaveBeenCalledWith(combo.id, ['test'], true);
100+
expect(mockNgControl.registerOnChangeCb).not.toHaveBeenCalled();
103101
expect(mockSelection.select_items).toHaveBeenCalledWith(combo.id, ['test'], true);
104102
expect(combo.value).toBe('test');
105103

@@ -120,11 +118,8 @@ describe('igxCombo', () => {
120118
spyOnProperty(combo, 'collapsed').and.returnValue(true);
121119
spyOnProperty(combo, 'valid', 'set');
122120

123-
combo.onFocus();
124-
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(1);
125-
126121
combo.onBlur();
127-
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(2);
122+
expect(mockNgControl.registerOnTouchedCb).toHaveBeenCalledTimes(1);
128123
});
129124
it('should correctly handle ngControl validity', () => {
130125
pending('Convert existing form test here');
@@ -199,17 +194,18 @@ describe('igxCombo', () => {
199194
combo = new IgxComboComponent({ nativeElement: null }, mockCdr, mockSelection as any, mockComboService, null, mockInjector);
200195
combo.ngOnInit();
201196
combo.data = data;
202-
spyOn(combo, 'selectItems');
197+
mockSelection.select_items.calls.reset();
198+
spyOnProperty(combo, 'isRemote').and.returnValue(false);
203199
combo.writeValue(['EXAMPLE']);
204-
expect(combo.selectItems).toHaveBeenCalledTimes(1);
200+
expect(mockSelection.select_items).toHaveBeenCalledTimes(1);
205201

206-
// Calling "SelectItems" through the writeValue accessor should clear the previous values;
202+
// Calling "select_items" through the writeValue accessor should clear the previous values;
207203
// Select items is called with the invalid value and it is written in selection, though no item is selected
208204
// Controlling the selection is up to the user
209-
expect(combo.selectItems).toHaveBeenCalledWith(['EXAMPLE'], true);
205+
expect(mockSelection.select_items).toHaveBeenCalledWith(combo.id, ['EXAMPLE'], true);
210206
combo.writeValue(combo.data[0]);
211207
// When value key is specified, the item's value key is stored in the selection
212-
expect(combo.selectItems).toHaveBeenCalledWith(combo.data[0], true);
208+
expect(mockSelection.select_items).toHaveBeenCalledWith(combo.id, [], true);
213209
});
214210
it('should select items through setSelctedItem method', () => {
215211
const selectionService = new IgxSelectionAPIService();
@@ -2578,6 +2574,7 @@ describe('igxCombo', () => {
25782574
expect(combo.valid).toEqual(IgxComboState.INVALID);
25792575
expect(combo.comboInput.valid).toEqual(IgxInputState.INVALID);
25802576

2577+
input.triggerEventHandler('focus', {});
25812578
combo.selectAllItems();
25822579
fixture.detectChanges();
25832580
expect(combo.valid).toEqual(IgxComboState.VALID);
@@ -2589,17 +2586,72 @@ describe('igxCombo', () => {
25892586
expect(combo.valid).toEqual(IgxComboState.INVALID);
25902587
expect(combo.comboInput.valid).toEqual(IgxInputState.INVALID);
25912588
});
2592-
it('should have correctly bound focus and blur handlers', () => {
2593-
spyOn(combo, 'onFocus');
2594-
spyOn(combo, 'onBlur');
2589+
it('should properly init with empty array and handle consecutive model changes', fakeAsync(() => {
2590+
const model = fixture.debugElement.query(By.directive(NgModel)).injector.get(NgModel);
2591+
fixture.componentInstance.values = [];
2592+
fixture.detectChanges();
2593+
tick();
2594+
expect(combo.valid).toEqual(IgxComboState.INITIAL);
2595+
expect(combo.comboInput.valid).toEqual(IgxInputState.INITIAL);
2596+
expect(model.valid).toBeFalse();
2597+
expect(model.dirty).toBeFalse();
2598+
expect(model.touched).toBeFalse();
25952599

2596-
input.triggerEventHandler('focus', {});
2597-
expect(combo.onFocus).toHaveBeenCalled();
2598-
expect(combo.onFocus).toHaveBeenCalledWith();
2600+
fixture.componentInstance.values = ['Missouri'];
2601+
fixture.detectChanges();
2602+
tick();
2603+
expect(combo.valid).toEqual(IgxComboState.INITIAL);
2604+
expect(combo.comboInput.valid).toEqual(IgxInputState.INITIAL);
2605+
expect(combo.selectedItems()).toEqual(['Missouri']);
2606+
expect(combo.value).toEqual('Missouri');
2607+
expect(model.valid).toBeTrue();
2608+
expect(model.touched).toBeFalse();
2609+
2610+
fixture.componentInstance.values = ['Missouri', 'Missouri'];
2611+
fixture.detectChanges();
2612+
expect(combo.valid).toEqual(IgxComboState.INITIAL);
2613+
expect(combo.comboInput.valid).toEqual(IgxInputState.INITIAL);
2614+
expect(combo.selectedItems()).toEqual(['Missouri']);
2615+
expect(combo.value).toEqual('Missouri');
2616+
expect(model.valid).toBeTrue();
2617+
expect(model.touched).toBeFalse();
2618+
2619+
fixture.componentInstance.values = null;
2620+
fixture.detectChanges();
2621+
tick();
2622+
expect(combo.valid).toEqual(IgxComboState.INITIAL);
2623+
expect(combo.comboInput.valid).toEqual(IgxInputState.INITIAL);
2624+
expect(combo.selectedItems()).toEqual([]);
2625+
expect(combo.value).toEqual('');
2626+
expect(model.valid).toBeFalse();
2627+
expect(model.touched).toBeFalse();
2628+
expect(model.dirty).toBeFalse();
2629+
2630+
combo.onBlur();
2631+
fixture.detectChanges();
2632+
expect(combo.valid).toEqual(IgxComboState.INVALID);
2633+
expect(combo.comboInput.valid).toEqual(IgxInputState.INVALID);
2634+
expect(model.valid).toBeFalse();
2635+
expect(model.touched).toBeTrue();
2636+
expect(model.dirty).toBeFalse();
2637+
2638+
fixture.componentInstance.values = ['New Jersey'];
2639+
fixture.detectChanges();
2640+
tick();
2641+
expect(combo.valid).toEqual(IgxComboState.INITIAL);
2642+
expect(combo.comboInput.valid).toEqual(IgxInputState.INITIAL);
2643+
expect(combo.selectedItems()).toEqual(['New Jersey']);
2644+
expect(combo.value).toEqual('New Jersey');
2645+
expect(model.valid).toBeTrue();
2646+
expect(model.touched).toBeTrue();
2647+
expect(model.dirty).toBeFalse();
2648+
}));
2649+
it('should have correctly bound blur handler', () => {
2650+
spyOn(combo, 'onBlur');
25992651

26002652
input.triggerEventHandler('blur', {});
26012653
expect(combo.onBlur).toHaveBeenCalled();
2602-
expect(combo.onFocus).toHaveBeenCalledWith();
2654+
expect(combo.onBlur).toHaveBeenCalledWith();
26032655
});
26042656
});
26052657
});

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,11 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
11451145
protected onStatusChanged = () => {
11461146
if ((this.ngControl.control.touched || this.ngControl.control.dirty) &&
11471147
(this.ngControl.control.validator || this.ngControl.control.asyncValidator)) {
1148-
this.valid = this.ngControl.valid ? IgxComboState.VALID : IgxComboState.INVALID;
1148+
if (!this.collapsed || this.inputGroup.isFocused) {
1149+
this.valid = this.ngControl.valid ? IgxComboState.VALID : IgxComboState.INVALID;
1150+
} else {
1151+
this.valid = this.ngControl.valid ? IgxComboState.INITIAL : IgxComboState.INVALID;
1152+
}
11491153
}
11501154
this.manageRequiredAsterisk();
11511155
}
@@ -1172,13 +1176,6 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
11721176
}
11731177
}
11741178

1175-
/** @hidden @internal */
1176-
public onFocus() {
1177-
if (this.collapsed) {
1178-
this._onTouchedCallback();
1179-
}
1180-
}
1181-
11821179
/**
11831180
* @hidden @internal
11841181
*/
@@ -1218,8 +1215,10 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
12181215
* @hidden @internal
12191216
*/
12201217
public writeValue(value: any[]): void {
1221-
this.selectItems(value, true);
1222-
this.cdr.markForCheck();
1218+
const selection = Array.isArray(value) ? value : [];
1219+
const oldSelection = this.selectedItems();
1220+
this.selection.select_items(this.id, selection, true);
1221+
this._value = this.createDisplayText(this.selectedItems(), oldSelection);
12231222
}
12241223

12251224
/**

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3772,6 +3772,7 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
37723772
}
37733773

37743774
/**
3775+
* Reorder columns in the main columnList and _columns collections.
37753776
* @hidden
37763777
*/
37773778
protected _moveColumns(from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
@@ -3801,44 +3802,44 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
38013802
}
38023803

38033804
/**
3805+
* Reorders columns inside the passed column collection.
3806+
* When reordering column group collection, the collection is not flattened.
3807+
* In all other cases, the columns collection is flattened, this is why adittional calculations on the dropIndex are done.
38043808
* @hidden
38053809
*/
3806-
protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[]) {
3810+
protected _reorderColumns(from: IgxColumnComponent, to: IgxColumnComponent, position: DropPosition, columnCollection: any[],
3811+
inGroup = false) {
38073812
const fromIndex = columnCollection.indexOf(from);
3808-
const childColumnsCount = from.allChildren.length;
3809-
// remove item(s) to be moved.
3810-
const fromCollection = columnCollection.splice(fromIndex, childColumnsCount + 1);
3811-
3813+
const childColumnsCount = inGroup ? 1 : from.allChildren.length + 1;
3814+
columnCollection.splice(fromIndex, childColumnsCount);
38123815
let dropIndex = columnCollection.indexOf(to);
3813-
38143816
if (position === DropPosition.AfterDropTarget) {
38153817
dropIndex++;
3816-
if (to.columnGroup) {
3818+
if (!inGroup && to.columnGroup) {
38173819
dropIndex += to.allChildren.length;
38183820
}
38193821
}
3820-
columnCollection.splice(dropIndex, 0, ...fromCollection);
3822+
columnCollection.splice(dropIndex, 0, from);
38213823
}
3824+
38223825
/**
3826+
* Reorder column group collection.
38233827
* @hidden
38243828
*/
38253829
protected _moveChildColumns(parent: IgxColumnComponent, from: IgxColumnComponent, to: IgxColumnComponent, pos: DropPosition) {
38263830
const buffer = parent.children.toArray();
3827-
this._reorderColumns(from, to, pos, buffer);
3831+
this._reorderColumns(from, to, pos, buffer, true);
38283832
parent.children.reset(buffer);
38293833
}
38303834
/**
3831-
* Moves a column to the specified drop target.
3835+
* Places a column before or after the specified target column.
38323836
* @example
38333837
* ```typescript
3834-
* grid.moveColumn(compName, persDetails);
3838+
* grid.moveColumn(column, target);
38353839
* ```
38363840
*/
3837-
public moveColumn(column: IgxColumnComponent, dropTarget: IgxColumnComponent, pos: DropPosition = DropPosition.None) {
3841+
public moveColumn(column: IgxColumnComponent, target: IgxColumnComponent, pos: DropPosition = DropPosition.None) {
38383842

3839-
if (column === dropTarget) {
3840-
return;
3841-
}
38423843
let position = pos;
38433844
if (position === DropPosition.None) {
38443845
warningShown = showMessage(
@@ -3847,21 +3848,18 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
38473848
warningShown);
38483849
position = DropPosition.AfterDropTarget;
38493850
}
3850-
if ((column.level !== dropTarget.level) ||
3851-
(column.topLevelParent !== dropTarget.topLevelParent)) {
3851+
3852+
if (column === target || (column.level !== target.level) ||
3853+
(column.topLevelParent !== target.topLevelParent)) {
38523854
return;
38533855
}
38543856

38553857
this.endEdit(true);
38563858
if (column.level) {
3857-
this._moveChildColumns(column.parent, column, dropTarget, position);
3858-
}
3859-
3860-
if (dropTarget.pinned && column.pinned) {
3861-
this._reorderColumns(column, dropTarget, position, this._pinnedColumns);
3859+
this._moveChildColumns(column.parent, column, target, position);
38623860
}
38633861

3864-
if (dropTarget.pinned && !column.pinned) {
3862+
if (target.pinned && !column.pinned) {
38653863
column.pin();
38663864
if (!this.isPinningToStart) {
38673865
if (pos === DropPosition.AfterDropTarget) {
@@ -3870,36 +3868,30 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
38703868
position = DropPosition.None;
38713869
}
38723870
}
3873-
this._reorderColumns(column, dropTarget, position, this._pinnedColumns);
3871+
this._reorderColumns(column, target, position, this._pinnedColumns);
38743872
}
38753873

3876-
if (!dropTarget.pinned && column.pinned) {
3874+
if (!target.pinned && column.pinned) {
38773875
column.unpin();
3878-
let list = [];
3879-
3880-
if (this.pinnedColumns.indexOf(column) === -1 && this.pinnedColumns.indexOf(dropTarget) === -1) {
3881-
list = this._unpinnedColumns;
3882-
} else {
3883-
list = this._pinnedColumns;
3884-
}
3885-
3886-
const fi = list.indexOf(column);
3887-
const ti = list.indexOf(dropTarget);
3876+
}
38883877

3889-
if (pos === DropPosition.BeforeDropTarget && fi < ti) {
3890-
position = DropPosition.BeforeDropTarget;
3891-
} else if (pos === DropPosition.AfterDropTarget && fi > ti) {
3892-
position = DropPosition.AfterDropTarget;
3893-
} else {
3894-
position = DropPosition.None;
3895-
}
3878+
if (target.pinned && column.pinned) {
3879+
this._reorderColumns(column, target, position, this._pinnedColumns);
38963880
}
38973881

3898-
if (!dropTarget.pinned) {
3899-
this._reorderColumns(column, dropTarget, position, this._unpinnedColumns);
3882+
if (!target.pinned && !column.pinned) {
3883+
this._reorderColumns(column, target, position, this._unpinnedColumns);
39003884
}
39013885

3902-
this._moveColumns(column, dropTarget, position);
3886+
this._moveColumns(column, target, position);
3887+
this._columnsReordered(column, target);
3888+
}
3889+
3890+
/**
3891+
* Notiy changes, reset cache and populateVisibleIndexes.
3892+
* @hidden
3893+
*/
3894+
private _columnsReordered(column: IgxColumnComponent, target) {
39033895
this.notifyChanges();
39043896
if (this.hasColumnLayouts) {
39053897
this.columns.filter(x => x.columnLayout).forEach(x => x.populateVisibleIndexes());
@@ -3910,7 +3902,7 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
39103902

39113903
const args = {
39123904
source: column,
3913-
target: dropTarget
3905+
target: target
39143906
};
39153907

39163908
this.onColumnMovingEnd.emit(args);

src/app/combo/combo.sample.html

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,17 @@
7070
<div>
7171
<h5 class="sample-title">Template Form</h5>
7272
<form>
73-
<igx-combo class="input-container" [placeholder]="'Locations'"
74-
name="anyName" required [(ngModel)]="values1" [ngModelOptions]="{ updateOn: 'blur' }" #comboModel="ngModel"
73+
<igx-combo class="input-container" [placeholder]="'Locations'" #comboModel="ngModel"
74+
name="anyName" required [(ngModel)]="values1" [ngModelOptions]="{ updateOn: 'blur' }" required minlength="2"
7575
[data]="items" [filterable]="filterableFlag"
7676
[displayKey]="valueKeyVar" [valueKey]="valueKeyVar"
7777
[groupKey]="valueKeyVar ? 'region' : ''" [width]="'100%'">
78+
<label igxLabel>States</label>
7879
<igx-hint>Please select the states you've visited</igx-hint>
7980
</igx-combo>
8081
</form>
82+
<button igxButton (click)="values1 = values1.concat(['Missouri'])">Add Missouri</button>
83+
<button igxButton (click)="values1 = []">Clear values </button>
8184
</div>
8285
<div>
8386
<h5 class="sample-title">Reactive Form</h5>

src/app/combo/combo.sample.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class ComboSampleComponent implements OnInit, AfterViewInit {
4545
public customValuesFlag = true;
4646
public autoFocusSearch = true;
4747
public items: any[] = [];
48-
public values1: Array<any>;
48+
public values1: Array<any> = ['Arizona'];
4949
public values2: Array<any>;
5050

5151
public valueKeyVar = 'field';

0 commit comments

Comments
 (0)