Skip to content

Commit 04f654c

Browse files
authored
Merge branch 'master' into VDyulgerov_fix-boolean-column-master
2 parents 4bb7d29 + d0cadec commit 04f654c

File tree

7 files changed

+174
-8
lines changed

7 files changed

+174
-8
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes for each version of this project will be documented in this
44

55
## 12.2.0
66

7+
### New Features
8+
- `igxTreeGrid`
9+
- Added `TreeGridMatchingRecordsOnlyFilteringStrategy`, which allows you to display only the records matching particular filtering condition without any trace for their parents.
10+
11+
## 12.1.6
12+
713
### General
814
- `igxGrid`, `igxHierarchicalGrid`, `igxTreeGrid`
915
- 'oddRowCSS' and 'evenRowCSS' properties has been deprecated

projects/igniteui-angular/src/lib/core/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,8 @@ export const yieldingLoop = (count: number, chunkSize: number, callback: (index:
502502
chunk();
503503
};
504504

505+
export const isConstructor = (ref: any) => typeof ref === 'function' && Boolean(ref.prototype) && Boolean(ref.prototype.constructor);
506+
505507
export const reverseAnimationResolver = (animation: AnimationReferenceMetadata): AnimationReferenceMetadata =>
506508
oppositeAnimation.get(animation) ?? animation;
507509

projects/igniteui-angular/src/lib/date-picker/date-picker.component.spec.ts

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
2-
import { FormsModule, NgForm, ReactiveFormsModule } from '@angular/forms';
2+
import { FormControl, FormGroup, FormsModule, NgForm, ReactiveFormsModule, Validators } from '@angular/forms';
33
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
44
import { UIInteractions } from '../test-utils/ui-interactions.spec';
55
import {
@@ -33,6 +33,9 @@ const CSS_CLASS_DATE_PICKER = 'igx-date-picker';
3333
const DATE_PICKER_TOGGLE_ICON = 'today';
3434
const DATE_PICKER_CLEAR_ICON = 'clear';
3535

36+
const CSS_CLASS_INPUT_GROUP_REQUIRED = 'igx-input-group--required';
37+
const CSS_CLASS_INPUT_GROUP_INVALID = 'igx-input-group--invalid ';
38+
const CSS_CLASS_INPUT_GROUP_LABEL = 'igx-input-group__label';
3639

3740
describe('IgxDatePicker', () => {
3841
describe('Integration tests', () => {
@@ -44,7 +47,8 @@ describe('IgxDatePicker', () => {
4447
IgxDatePickerTestComponent,
4548
IgxDatePickerNgModelComponent,
4649
IgxDatePickerWithProjectionsComponent,
47-
IgxDatePickerInFormComponent
50+
IgxDatePickerInFormComponent,
51+
IgxDatePickerReactiveFormComponent
4852
],
4953
imports: [IgxDatePickerModule, FormsModule, ReactiveFormsModule,
5054
NoopAnimationsModule, IgxInputGroupModule, IgxCalendarModule,
@@ -193,7 +197,9 @@ describe('IgxDatePicker', () => {
193197
});
194198

195199
describe('NgControl integration', () => {
196-
let fixture: ComponentFixture<IgxDatePickerNgModelComponent | IgxDatePickerInFormComponent>;
200+
let fixture: ComponentFixture<IgxDatePickerNgModelComponent |
201+
IgxDatePickerInFormComponent |
202+
IgxDatePickerReactiveFormComponent>;
197203
let datePicker: IgxDatePickerComponent;
198204

199205
beforeEach(fakeAsync(() => {
@@ -238,6 +244,53 @@ describe('IgxDatePicker', () => {
238244
tick();
239245
expect((datePicker as any).inputDirective.valid).toEqual(IgxInputState.INITIAL);
240246
}));
247+
248+
it('should apply asterix properly when required validator is set dynamically', () => {
249+
fixture = TestBed.createComponent(IgxDatePickerReactiveFormComponent);
250+
fixture.detectChanges();
251+
datePicker = fixture.componentInstance.datePicker;
252+
253+
let inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED));
254+
let inputGroupInvalidClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_INVALID));
255+
let asterisk = window.
256+
getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').
257+
content;
258+
expect(asterisk).toBe('"*"');
259+
expect(inputGroupRequiredClass).toBeDefined();
260+
expect(inputGroupRequiredClass).not.toBeNull();
261+
262+
datePicker.clear();
263+
fixture.detectChanges();
264+
265+
inputGroupInvalidClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_INVALID));
266+
expect(inputGroupInvalidClass).not.toBeNull();
267+
expect(inputGroupInvalidClass).not.toBeUndefined();
268+
269+
inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED));
270+
expect(inputGroupRequiredClass).not.toBeNull();
271+
expect(inputGroupRequiredClass).not.toBeUndefined();
272+
273+
(fixture.componentInstance as IgxDatePickerReactiveFormComponent).removeValidators();
274+
fixture.detectChanges();
275+
276+
inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED));
277+
asterisk = window.
278+
getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').
279+
content;
280+
expect(inputGroupRequiredClass).toBeNull();
281+
expect(asterisk).toBe('none');
282+
283+
(fixture.componentInstance as IgxDatePickerReactiveFormComponent).addValidators();
284+
fixture.detectChanges();
285+
286+
inputGroupRequiredClass = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_REQUIRED));
287+
asterisk = window.
288+
getComputedStyle(fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUT_GROUP_LABEL)).nativeElement, ':after').
289+
content;
290+
expect(inputGroupRequiredClass).toBeDefined();
291+
expect(inputGroupRequiredClass).not.toBeNull();
292+
expect(asterisk).toBe('"*"');
293+
});
241294
});
242295

243296
describe('Projected elements', () => {
@@ -339,6 +392,7 @@ describe('IgxDatePicker', () => {
339392
let overlay: IgxOverlayService;
340393
let mockOverlayEventArgs: OverlayEventArgs & OverlayCancelableEventArgs;
341394
let mockInjector;
395+
let mockCdr;
342396
let mockInputGroup: Partial<IgxInputGroupComponent>;
343397
let datePicker: IgxDatePickerComponent;
344398
let mockDateEditor: any;
@@ -403,6 +457,8 @@ describe('IgxDatePicker', () => {
403457
get: mockNgControl
404458
});
405459

460+
mockCdr = jasmine.createSpyObj('ChangeDetectorRef', ['detectChanges']);
461+
406462
mockCalendar = { selected: new EventEmitter<any>() };
407463
const mockComponentInstance = {
408464
calendar: mockCalendar,
@@ -512,7 +568,7 @@ describe('IgxDatePicker', () => {
512568
},
513569
focus: () => { }
514570
};
515-
datePicker = new IgxDatePickerComponent(elementRef, null, overlay, mockModuleRef, mockInjector, renderer2, null);
571+
datePicker = new IgxDatePickerComponent(elementRef, null, overlay, mockModuleRef, mockInjector, renderer2, null, mockCdr);
516572
(datePicker as any).inputGroup = mockInputGroup;
517573
(datePicker as any).inputDirective = mockInputDirective;
518574
(datePicker as any).dateTimeEditor = mockDateEditor;
@@ -1251,3 +1307,35 @@ export class IgxDatePickerInFormComponent {
12511307

12521308
public date: Date = new Date(2012, 5, 3);
12531309
}
1310+
1311+
@Component({
1312+
template: `
1313+
<form [formGroup]="form">
1314+
<div class="date-picker-wrapper">
1315+
<igx-date-picker formControlName="date" [value]="date">
1316+
<label igxLabel>Date </label>
1317+
</igx-date-picker>
1318+
</div>
1319+
</form>
1320+
`
1321+
})
1322+
export class IgxDatePickerReactiveFormComponent {
1323+
@ViewChild(IgxDatePickerComponent)
1324+
public datePicker: IgxDatePickerComponent;
1325+
1326+
public date: Date = new Date(2012, 5, 3);
1327+
1328+
public form: FormGroup = new FormGroup({
1329+
date: new FormControl(null, Validators.required)
1330+
});
1331+
1332+
public removeValidators() {
1333+
this.form.get('date').clearValidators();
1334+
this.form.get('date').updateValueAndValidity();
1335+
}
1336+
1337+
public addValidators() {
1338+
this.form.get('date').setValidators(Validators.required);
1339+
this.form.get('date').updateValueAndValidity();
1340+
}
1341+
}

projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
Component, ContentChild, EventEmitter, HostBinding, Input,
33
OnDestroy, Output, ViewChild, ElementRef, Inject, HostListener,
44
NgModuleRef, OnInit, AfterViewInit, Injector, AfterViewChecked, ContentChildren,
5-
QueryList, LOCALE_ID, Renderer2, Optional, PipeTransform
5+
QueryList, LOCALE_ID, Renderer2, Optional, PipeTransform, ChangeDetectorRef
66
} from '@angular/core';
77
import {
88
ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl, AbstractControl,
@@ -481,6 +481,7 @@ export class IgxDatePickerComponent extends PickerBaseDirective implements Contr
481481
private _injector: Injector,
482482
private _renderer: Renderer2,
483483
private platform: PlatformUtil,
484+
private cdr: ChangeDetectorRef,
484485
@Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions?: IDisplayDensityOptions,
485486
@Optional() @Inject(IGX_INPUT_GROUP_TYPE) protected _inputGroupType?: IgxInputGroupType) {
486487
super(element, _localeId, _displayDensityOptions, _inputGroupType);
@@ -750,6 +751,10 @@ export class IgxDatePickerComponent extends PickerBaseDirective implements Contr
750751
if (this._ngControl) {
751752
this._statusChanges$ =
752753
this._ngControl.statusChanges.subscribe(this.onStatusChanged.bind(this));
754+
if (this._ngControl.control.validator) {
755+
this.inputGroup.isRequired = this.required;
756+
this.cdr.detectChanges();
757+
}
753758
}
754759
}
755760

projects/igniteui-angular/src/lib/grids/columns/column.component.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import { MRLResizeColumnInfo, MRLColumnSizeInfo, IColumnPipeArgs } from './inter
5151
import { DropPosition } from '../moving/moving.service';
5252
import { IgxColumnGroupComponent } from './column-group.component';
5353
import { IColumnVisibilityChangingEventArgs, IPinColumnCancellableEventArgs, IPinColumnEventArgs } from '../common/events';
54-
import { PlatformUtil } from '../../core/utils';
54+
import { isConstructor, PlatformUtil } from '../../core/utils';
5555
import { CellType } from '../common/cell.interface';
5656
import { IgxGridCell } from '../grid-public-cell';
5757

@@ -1011,7 +1011,9 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy {
10111011
* @memberof IgxColumnComponent
10121012
*/
10131013
public set summaries(classRef: any) {
1014-
this._summaries = new classRef();
1014+
if (isConstructor(classRef)) {
1015+
this._summaries = new classRef();
1016+
}
10151017

10161018
if (this.grid) {
10171019
this.grid.summaryService.removeSummariesCachePerColumn(this.field);

projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-filtering.spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { TreeGridFunctions } from '../../test-utils/tree-grid-functions.spec';
77
import { configureTestSuite } from '../../test-utils/configure-suite';
88
import { IgxStringFilteringOperand, IgxNumberFilteringOperand, IgxDateFilteringOperand } from '../../data-operations/filtering-condition';
99
import { FilteringStrategy } from '../../data-operations/filtering-strategy';
10-
import { TreeGridFormattedValuesFilteringStrategy } from './tree-grid.filtering.strategy';
10+
import { TreeGridFormattedValuesFilteringStrategy, TreeGridMatchingRecordsOnlyFilteringStrategy } from './tree-grid.filtering.strategy';
1111
import { FilterMode } from '../common/enums';
1212
import { GridFunctions } from '../../test-utils/grid-functions.spec';
1313
import { wait } from '../../test-utils/ui-interactions.spec';
@@ -484,6 +484,26 @@ describe('IgxTreeGrid - Filtering actions #tGrid', () => {
484484
expect(treeGrid.rowList.length).toBe(4);
485485
expect(treeGrid.filteredData.map(rec => rec.ID)).toEqual([ 847, 225, 663, 141]);
486486
}));
487+
488+
it('should display only the filtered records when using TreeGridMatchingRecordsOnlyFilteringStrategy', fakeAsync(() => {
489+
expect(treeGrid.filterStrategy).toBeUndefined();
490+
treeGrid.filter('Name', 'Trevor', IgxStringFilteringOperand.instance().condition('contains'), true);
491+
tick(30);
492+
fix.detectChanges();
493+
494+
expect(treeGrid.rowList.length).toBe(3);
495+
496+
const matchingRecordsOnlyStrategy = new TreeGridMatchingRecordsOnlyFilteringStrategy();
497+
treeGrid.filterStrategy = matchingRecordsOnlyStrategy;
498+
fix.detectChanges();
499+
500+
treeGrid.filter('Name', 'Trevor', IgxStringFilteringOperand.instance().condition('contains'), true);
501+
tick(30);
502+
fix.detectChanges();
503+
504+
expect(treeGrid.rowList.length).toBe(1);
505+
expect(treeGrid.filteredData.map(rec => rec.ID)).toEqual([141]);
506+
}));
487507
});
488508
class CustomTreeGridFilterStrategy extends FilteringStrategy {
489509

projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.filtering.strategy.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,46 @@ export class TreeGridFormattedValuesFilteringStrategy extends TreeGridFilteringS
7474
return value;
7575
}
7676
}
77+
78+
export class TreeGridMatchingRecordsOnlyFilteringStrategy extends TreeGridFilteringStrategy {
79+
public filter(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree,
80+
advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): ITreeGridRecord[] {
81+
return this.filterImplementation(data, expressionsTree, advancedExpressionsTree, undefined, grid);
82+
}
83+
84+
private filterImplementation(data: ITreeGridRecord[], expressionsTree: IFilteringExpressionsTree,
85+
advancedExpressionsTree: IFilteringExpressionsTree, parent: ITreeGridRecord, grid?: GridType): ITreeGridRecord[] {
86+
let i: number;
87+
let rec: ITreeGridRecord;
88+
const len = data.length;
89+
const res: ITreeGridRecord[] = [];
90+
if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) {
91+
return data;
92+
}
93+
for (i = 0; i < len; i++) {
94+
rec = DataUtil.cloneTreeGridRecord(data[i]);
95+
rec.parent = parent;
96+
if (rec.children) {
97+
const filteredChildren = this.filterImplementation(rec.children, expressionsTree, advancedExpressionsTree, rec, grid);
98+
rec.children = filteredChildren.length > 0 ? filteredChildren : null;
99+
}
100+
if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) {
101+
res.push(rec);
102+
} else if (rec.children && rec.children.length > 0) {
103+
rec = this.setCorrectLevelToFilteredRecords(rec);
104+
res.push(...rec.children);
105+
}
106+
}
107+
return res;
108+
}
109+
110+
private setCorrectLevelToFilteredRecords(rec: ITreeGridRecord): ITreeGridRecord {
111+
if (rec.children && rec.children.length > 0) {
112+
rec.children.map(child => {
113+
child.level = child.level - 1;
114+
return this.setCorrectLevelToFilteredRecords(child);
115+
});
116+
}
117+
return rec;
118+
}
119+
}

0 commit comments

Comments
 (0)