Skip to content

Commit 57ad8ae

Browse files
authored
Merge branch 'master' into mevtimov/feat-7344-master
2 parents 5c24c88 + 0989afe commit 57ad8ae

33 files changed

+1186
-149
lines changed

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,34 @@ export function cloneValue(value: any): any {
103103
return value;
104104
}
105105

106+
/**
107+
* Parse provided input to Date.
108+
* @param value input to parse
109+
* @returns Date if parse succeed or null
110+
* @hidden
111+
*/
112+
export function parseDate(value: any): Date | null {
113+
// if value is Invalid Date return null
114+
if (isDate(value)) {
115+
return !isNaN(value.getTime()) ? value : null;
116+
}
117+
return value ? new Date(value) : null;
118+
}
119+
120+
/**
121+
* Returns an array with unique dates only.
122+
* @param columnValues collection of date values (might be numbers or ISO 8601 strings)
123+
* @returns collection of unique dates.
124+
* @hidden
125+
*/
126+
export function uniqueDates(columnValues: any[]) {
127+
return columnValues.reduce((a, c) => {
128+
if (!a.cache[c.label]) { a.result.push(c); }
129+
a.cache[c.label] = true;
130+
return a;
131+
}, {result: [], cache: {}}).result;
132+
}
133+
106134
/**
107135
* Checks if provided variable is Object
108136
* @param value Value to check
@@ -119,8 +147,8 @@ export function isObject(value: any): boolean {
119147
* @returns true if provided variable is Date
120148
* @hidden
121149
*/
122-
export function isDate(value: any) {
123-
return Object.prototype.toString.call(value) === '[object Date]';
150+
export function isDate(value: any): boolean {
151+
return value instanceof Date;
124152
}
125153

126154
/**

projects/igniteui-angular/src/lib/data-operations/data-util.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ITreeGridRecord } from '../grids/tree-grid/public_api';
1515
import { cloneValue, mergeObjects, mkenum } from '../core/utils';
1616
import { Transaction, TransactionType, HierarchicalTransaction } from '../services/transaction/transaction';
1717
import { getHierarchy, isHierarchyMatch } from './operations';
18+
import { GridType } from '../grids/common/grid.interface';
1819

1920
/**
2021
* @hidden
@@ -31,25 +32,27 @@ export type DataType = (typeof DataType)[keyof typeof DataType];
3132
* @hidden
3233
*/
3334
export class DataUtil {
34-
public static sort<T>(data: T[], expressions: ISortingExpression[], sorting: IGridSortingStrategy = new IgxSorting()): T[] {
35-
return sorting.sort(data, expressions);
35+
public static sort<T>(data: T[], expressions: ISortingExpression[], sorting: IGridSortingStrategy = new IgxSorting(),
36+
grid?: GridType): T[] {
37+
return sorting.sort(data, expressions, grid);
3638
}
3739

3840
public static treeGridSort(hierarchicalData: ITreeGridRecord[],
3941
expressions: ISortingExpression[],
4042
sorting: IGridSortingStrategy = new IgxDataRecordSorting(),
41-
parent?: ITreeGridRecord): ITreeGridRecord[] {
43+
parent?: ITreeGridRecord,
44+
grid?: GridType): ITreeGridRecord[] {
4245
let res: ITreeGridRecord[] = [];
4346
hierarchicalData.forEach((hr: ITreeGridRecord) => {
4447
const rec: ITreeGridRecord = DataUtil.cloneTreeGridRecord(hr);
4548
rec.parent = parent;
4649
if (rec.children) {
47-
rec.children = DataUtil.treeGridSort(rec.children, expressions, sorting, rec);
50+
rec.children = DataUtil.treeGridSort(rec.children, expressions, sorting, rec, grid);
4851
}
4952
res.push(rec);
5053
});
5154

52-
res = DataUtil.sort(res, expressions, sorting);
55+
res = DataUtil.sort(res, expressions, sorting, grid);
5356

5457
return res;
5558
}
@@ -66,7 +69,7 @@ export class DataUtil {
6669
return rec;
6770
}
6871

69-
public static group<T>(data: T[], state: IGroupingState, grid: any = null,
72+
public static group<T>(data: T[], state: IGroupingState, grid: GridType = null,
7073
groupsRecords: any[] = [], fullResult: IGroupByResult = { data: [], metadata: [] }): IGroupByResult {
7174
const grouping = new IgxGrouping();
7275
groupsRecords.splice(0, groupsRecords.length);
@@ -105,11 +108,11 @@ export class DataUtil {
105108
return data.slice(index * recordsPerPage, (index + 1) * recordsPerPage);
106109
}
107110

108-
public static filter<T>(data: T[], state: IFilteringState): T[] {
111+
public static filter<T>(data: T[], state: IFilteringState, grid?: GridType): T[] {
109112
if (!state.strategy) {
110113
state.strategy = new FilteringStrategy();
111114
}
112-
return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree);
115+
return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree, grid);
113116
}
114117

115118
public static correctPagingState(state: IPagingState, length: number) {

projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ export class IgxDateFilteringOperand extends IgxFilteringOperand {
391391

392392
protected findValueInSet(target: any, searchVal: Set<any>) {
393393
if (!target) { return false; }
394-
return searchVal.has(new Date(target.getFullYear(), target.getMonth(), target.getDate()).toISOString());
394+
return searchVal.has(target.toISOString());
395395
}
396396
}
397397

projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('Unit testing FilteringStrategy', () => {
2323
searchVal: 1
2424
}
2525
];
26-
const res = fs.filter(data, expressionTree);
26+
const res = fs.filter(data, expressionTree, null, null);
2727
expect(dataGenerator.getValuesForColumn(res, 'number'))
2828
.toEqual([2, 3, 4]);
2929
});
@@ -65,7 +65,7 @@ describe('Unit testing FilteringStrategy', () => {
6565
searchVal: 'ROW'
6666
}
6767
];
68-
const res = filterstr.filter(data, expressionTree);
68+
const res = filterstr.filter(data, expressionTree, null, null);
6969
expect(dataGenerator.getValuesForColumn(res, 'number'))
7070
.toEqual([0]);
7171
});

projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface';
22
import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree';
3-
import { resolveNestedPath } from '../core/utils';
3+
import { resolveNestedPath, parseDate } from '../core/utils';
4+
import { GridType } from '../grids/common/grid.interface';
5+
6+
const DateType = 'date';
47

58
export interface IFilteringStrategy {
6-
filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree): any[];
9+
filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree,
10+
grid?: GridType): any[];
711
}
812

913
export class NoopFilteringStrategy implements IFilteringStrategy {
@@ -22,17 +26,17 @@ export class NoopFilteringStrategy implements IFilteringStrategy {
2226

2327
export abstract class BaseFilteringStrategy implements IFilteringStrategy {
2428
public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree,
25-
advancedExpressionsTree?: IFilteringExpressionsTree): any[];
29+
advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[];
2630

27-
protected abstract getFieldValue(rec: object, fieldName: string): any;
31+
protected abstract getFieldValue(rec: object, fieldName: string, isDate: boolean): any;
2832

29-
public findMatchByExpression(rec: object, expr: IFilteringExpression): boolean {
33+
public findMatchByExpression(rec: object, expr: IFilteringExpression, isDate?: boolean): boolean {
3034
const cond = expr.condition;
31-
const val = this.getFieldValue(rec, expr.fieldName);
35+
const val = this.getFieldValue(rec, expr.fieldName, isDate);
3236
return cond.logic(val, expr.searchVal, expr.ignoreCase);
3337
}
3438

35-
public matchRecord(rec: object, expressions: IFilteringExpressionsTree | IFilteringExpression): boolean {
39+
public matchRecord(rec: object, expressions: IFilteringExpressionsTree | IFilteringExpression, grid?: GridType): boolean {
3640
if (expressions) {
3741
if (expressions instanceof FilteringExpressionsTree) {
3842
const expressionsTree = expressions as IFilteringExpressionsTree;
@@ -42,7 +46,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy {
4246
if (expressionsTree.filteringOperands && expressionsTree.filteringOperands.length) {
4347
for (let i = 0; i < expressionsTree.filteringOperands.length; i++) {
4448
operand = expressionsTree.filteringOperands[i];
45-
matchOperand = this.matchRecord(rec, operand);
49+
matchOperand = this.matchRecord(rec, operand, grid);
4650

4751
// Return false if at least one operand does not match and the filtering logic is And
4852
if (!matchOperand && operator === FilteringLogic.And) {
@@ -61,7 +65,9 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy {
6165
return true;
6266
} else {
6367
const expression = expressions as IFilteringExpression;
64-
return this.findMatchByExpression(rec, expression);
68+
const isDate = grid && grid.getColumnByName(expression.fieldName) ?
69+
grid.getColumnByName(expression.fieldName).dataType === DateType : false;
70+
return this.findMatchByExpression(rec, expression, isDate);
6571
}
6672
}
6773

@@ -78,7 +84,8 @@ export class FilteringStrategy extends BaseFilteringStrategy {
7884
return this._instace || (this._instace = new this());
7985
}
8086

81-
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree): T[] {
87+
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree: IFilteringExpressionsTree,
88+
grid: GridType): T[] {
8289
let i;
8390
let rec;
8491
const len = data.length;
@@ -88,14 +95,16 @@ export class FilteringStrategy extends BaseFilteringStrategy {
8895
}
8996
for (i = 0; i < len; i++) {
9097
rec = data[i];
91-
if (this.matchRecord(rec, expressionsTree) && this.matchRecord(rec, advancedExpressionsTree)) {
98+
if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) {
9299
res.push(rec);
93100
}
94101
}
95102
return res;
96103
}
97104

98-
protected getFieldValue(rec: object, fieldName: string): any {
99-
return resolveNestedPath(rec, fieldName);
105+
protected getFieldValue(rec: object, fieldName: string, isDate: boolean = false): any {
106+
let value = resolveNestedPath(rec, fieldName);
107+
value = value && isDate ? parseDate(value) : value;
108+
return value;
100109
}
101110
}

projects/igniteui-angular/src/lib/data-operations/sorting-strategy.ts

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
import { cloneArray, resolveNestedPath } from '../core/utils';
1+
import { cloneArray, resolveNestedPath, parseDate } from '../core/utils';
22
import { IGroupByRecord } from './groupby-record.interface';
33
import { ISortingExpression, SortingDirection } from './sorting-expression.interface';
44
import { IGroupingExpression } from './grouping-expression.interface';
55
import { IGroupingState } from './groupby-state.interface';
66
import { IGroupByExpandState } from './groupby-expand-state.interface';
77
import { IGroupByResult } from './grouping-result.interface';
88
import { getHierarchy, isHierarchyMatch } from './operations';
9+
import { GridType } from '../grids/common/grid.interface';
10+
11+
const DATE_TYPE = 'date';
912

1013
export interface ISortingStrategy {
1114
sort: (data: any[],
1215
fieldName: string,
1316
dir: SortingDirection,
1417
ignoreCase: boolean,
15-
valueResolver: (obj: any, key: string) => any) => any[];
18+
valueResolver: (obj: any, key: string, isDate?: boolean) => any,
19+
isDate?: boolean) => any[];
1620
}
1721

1822
export class DefaultSortingStrategy implements ISortingStrategy {
@@ -28,11 +32,12 @@ export class DefaultSortingStrategy implements ISortingStrategy {
2832
fieldName: string,
2933
dir: SortingDirection,
3034
ignoreCase: boolean,
31-
valueResolver: (obj: any, key: string) => any) {
35+
valueResolver: (obj: any, key: string, isDate?: boolean) => any,
36+
isDate?: boolean) {
3237
const key = fieldName;
3338
const reverse = (dir === SortingDirection.Desc ? -1 : 1);
3439
const cmpFunc = (obj1, obj2) => {
35-
return this.compareObjects(obj1, obj2, key, reverse, ignoreCase, valueResolver);
40+
return this.compareObjects(obj1, obj2, key, reverse, ignoreCase, valueResolver, isDate);
3641
};
3742
return this.arraySort(data, cmpFunc);
3843
}
@@ -56,9 +61,10 @@ export class DefaultSortingStrategy implements ISortingStrategy {
5661
key: string,
5762
reverse: number,
5863
ignoreCase: boolean,
59-
valueResolver: (obj: any, key: string) => any) {
60-
let a = valueResolver(obj1, key);
61-
let b = valueResolver(obj2, key);
64+
valueResolver: (obj: any, key: string, isDate?: boolean) => any,
65+
isDate: boolean) {
66+
let a = valueResolver(obj1, key, isDate);
67+
let b = valueResolver(obj2, key, isDate);
6268
if (ignoreCase) {
6369
a = a && a.toLowerCase ? a.toLowerCase() : a;
6470
b = b && b.toLowerCase ? b.toLowerCase() : b;
@@ -72,7 +78,7 @@ export class DefaultSortingStrategy implements ISortingStrategy {
7278
}
7379

7480
export interface IGridSortingStrategy {
75-
sort(data: any[], expressions: ISortingExpression[]): any[];
81+
sort(data: any[], expressions: ISortingExpression[], grid?: GridType): any[];
7682
}
7783

7884
export class NoopSortingStrategy implements IGridSortingStrategy {
@@ -90,24 +96,25 @@ export class NoopSortingStrategy implements IGridSortingStrategy {
9096
}
9197

9298
export class IgxSorting implements IGridSortingStrategy {
93-
public sort(data: any[], expressions: ISortingExpression[]): any[] {
94-
return this.sortDataRecursive(data, expressions);
99+
public sort(data: any[], expressions: ISortingExpression[], grid?: GridType): any[] {
100+
return this.sortDataRecursive(data, expressions, 0, grid);
95101
}
96102

97103
private groupedRecordsByExpression(data: any[],
98104
index: number,
99-
expression: IGroupingExpression): any[] {
105+
expression: IGroupingExpression,
106+
isDate: boolean = false): any[] {
100107
let i;
101108
let groupval;
102109
const res = [];
103110
const key = expression.fieldName;
104111
const len = data.length;
105112
res.push(data[index]);
106-
groupval = this.getFieldValue(data[index], key);
113+
groupval = this.getFieldValue(data[index], key, isDate);
107114
index++;
108115
const comparer = expression.groupingComparer || DefaultSortingStrategy.instance().compareValues;
109116
for (i = index; i < len; i++) {
110-
if (comparer(this.getFieldValue(data[i], key), groupval) === 0) {
117+
if (comparer(this.getFieldValue(data[i], key, isDate), groupval) === 0) {
111118
res.push(data[i]);
112119
} else {
113120
break;
@@ -117,7 +124,8 @@ export class IgxSorting implements IGridSortingStrategy {
117124
}
118125
private sortDataRecursive<T>(data: T[],
119126
expressions: ISortingExpression[],
120-
expressionIndex: number = 0): T[] {
127+
expressionIndex: number = 0,
128+
grid: GridType): T[] {
121129
let i;
122130
let j;
123131
let expr: ISortingExpression;
@@ -133,16 +141,18 @@ export class IgxSorting implements IGridSortingStrategy {
133141
if (!expr.strategy) {
134142
expr.strategy = DefaultSortingStrategy.instance();
135143
}
136-
data = expr.strategy.sort(data, expr.fieldName, expr.dir, expr.ignoreCase, this.getFieldValue);
144+
const isDate = grid && grid.getColumnByName(expr.fieldName) ?
145+
grid.getColumnByName(expr.fieldName).dataType === DATE_TYPE : false;
146+
data = expr.strategy.sort(data, expr.fieldName, expr.dir, expr.ignoreCase, this.getFieldValue, isDate);
137147
if (expressionIndex === exprsLen - 1) {
138148
return data;
139149
}
140150
// in case of multiple sorting
141151
for (i = 0; i < dataLen; i++) {
142-
gbData = this.groupedRecordsByExpression(data, i, expr);
152+
gbData = this.groupedRecordsByExpression(data, i, expr, isDate);
143153
gbDataLen = gbData.length;
144154
if (gbDataLen > 1) {
145-
gbData = this.sortDataRecursive(gbData, expressions, expressionIndex + 1);
155+
gbData = this.sortDataRecursive(gbData, expressions, expressionIndex + 1, grid);
146156
}
147157
for (j = 0; j < gbDataLen; j++) {
148158
data[i + j] = gbData[j];
@@ -152,20 +162,21 @@ export class IgxSorting implements IGridSortingStrategy {
152162
return data;
153163
}
154164
protected groupDataRecursive<T>(data: T[], state: IGroupingState, level: number,
155-
parent: IGroupByRecord, metadata: IGroupByRecord[], grid: any = null,
165+
parent: IGroupByRecord, metadata: IGroupByRecord[], grid: GridType = null,
156166
groupsRecords: any[] = [], fullResult: IGroupByResult = { data: [], metadata: [] }): T[] {
157167
const expressions = state.expressions;
158168
const expansion = state.expansion;
159169
let i = 0;
160170
let result = [];
161171
while (i < data.length) {
162-
const group = this.groupedRecordsByExpression(data, i, expressions[level]);
163172
const column = grid ? grid.getColumnByName(expressions[level].fieldName) : null;
173+
const isDate = column?.dataType === DATE_TYPE;
174+
const group = this.groupedRecordsByExpression(data, i, expressions[level], isDate);
164175
const groupRow: IGroupByRecord = {
165176
expression: expressions[level],
166177
level,
167178
records: cloneArray(group),
168-
value: this.getFieldValue(group[0], expressions[level].fieldName),
179+
value: this.getFieldValue(group[0], expressions[level].fieldName, isDate),
169180
groupParent: parent,
170181
groups: [],
171182
height: grid ? grid.renderedRowHeight : null,
@@ -205,14 +216,14 @@ export class IgxSorting implements IGridSortingStrategy {
205216
}
206217
return result;
207218
}
208-
protected getFieldValue(obj: any, key: string): any {
209-
return resolveNestedPath(obj, key);
219+
protected getFieldValue(obj: any, key: string, isDate: boolean = false): any {
220+
return isDate ? parseDate(resolveNestedPath(obj, key)) : resolveNestedPath(obj, key);
210221
}
211222
}
212223

213224
export class IgxDataRecordSorting extends IgxSorting {
214225

215-
protected getFieldValue(obj: any, key: string): any {
216-
return resolveNestedPath(obj.data, key);
226+
protected getFieldValue(obj: any, key: string, isDate: boolean = false): any {
227+
return isDate ? parseDate(resolveNestedPath(obj.data, key)) : resolveNestedPath(obj.data, key);
217228
}
218229
}

0 commit comments

Comments
 (0)