• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.1
/projects/igniteui-angular/src/lib/grids/common/strategy.ts
1
import { cloneArray, parseDate, resolveNestedPath } from '../../core/utils';
2
import { IGroupByExpandState } from '../../data-operations/groupby-expand-state.interface';
3
import { IGroupByRecord } from '../../data-operations/groupby-record.interface';
4
import { IGroupingState } from '../../data-operations/groupby-state.interface';
5
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
6
import { IGroupByResult } from '../../data-operations/grouping-result.interface';
7
import { getHierarchy, isHierarchyMatch } from '../../data-operations/operations';
8
import { DefaultSortingStrategy, ISortingExpression } from '../../data-operations/sorting-strategy';
9
import { GridType } from './grid.interface';
10

11
const DATE_TYPE = 'date';
2✔
12
const TIME_TYPE = 'time';
2✔
13
const DATE_TIME_TYPE = 'dateTime';
2✔
14
const STRING_TYPE = 'string';
2✔
15

16
/**
17
 * Represents a sorting strategy for the grid data
18
 * Contains a single method sort that sorts the provided data based on the given sorting expressions
19
 */
20
export interface IGridSortingStrategy {
21
    /* blazorCSSuppress */
22
   /**
23
   * `data`: The array of data to be sorted. Could be of any type.
24
   * `expressions`: An array of sorting expressions that define the sorting rules. The expression contains information like file name, whether the letter case should be taken into account, etc.
25
   * `grid`: (Optional) The instance of the grid where the sorting is applied.
26
   * Returns a new array with the data sorted according to the sorting expressions.
27
   */
28
    sort(data: any[], expressions: ISortingExpression[], grid?: GridType): any[];
29
}
30

31
/**
32
 * Represents a grouping strategy for the grid data, extending the Sorting Strategy interface (contains a sorting method).
33
 */
34
export interface IGridGroupingStrategy extends IGridSortingStrategy {
35
    /* blazorCSSuppress */
36
  /**
37
   * The method groups the provided data based on the given grouping state and returns the result.
38
   * `data`: The array of data to be grouped. Could be of any type.
39
   * `state`: The grouping state that defines the grouping settings and expressions.
40
   * `grid`: (Optional) The instance of the grid where the grouping is applied.
41
   * `groupsRecords`: (Optional) An array that holds the records for each group.
42
   * `fullResult`: (Optional) The complete result of grouping including groups and summary data.
43
   * Returns an object containing the result of the grouping operation.
44
   */
45
    groupBy(data: any[], state: IGroupingState, grid?: any, groupsRecords?: any[], fullResult?: IGroupByResult): IGroupByResult;
46
}
47

48
/**
49
 * Represents a class implementing the IGridSortingStrategy interface.
50
 * It provides sorting functionality for grid data based on sorting expressions.
51
 */
52
export class IgxSorting implements IGridSortingStrategy {
53
    /* blazorSuppress */
54
    /**
55
   * Sorts the provided data based on the given sorting expressions.
56
   * `data`: The array of data to be sorted.
57
   * `expressions`: An array of sorting expressions that define the sorting rules. The expression contains information like file name, whether the letter case should be taken into account, etc.
58
   * `grid`: (Optional) The instance of the grid where the sorting is applied.
59
   * Returns a new array with the data sorted according to the sorting expressions.
60
   */
61
    public sort(data: any[], expressions: ISortingExpression[], grid?: GridType): any[] {
UNCOV
62
        return this.sortDataRecursive(data, expressions, 0, grid);
×
63
    }
64

65
    /**
66
   * Recursively groups the provided data based on the given grouping state and returns the grouped result.
67
   * Returns an array containing the grouped result.
68
   * @internal
69
   */
70
    protected groupDataRecursive(
71
        data: any[],
72
        state: IGroupingState,
73
        level: number,
74
        parent: IGroupByRecord,
75
        metadata: IGroupByRecord[],
76
        grid: GridType = null,
×
77
        groupsRecords: any[] = [],
×
78
        fullResult: IGroupByResult = { data: [], metadata: [] }
×
79
    ): any[] {
UNCOV
80
        const expressions = state.expressions;
×
UNCOV
81
        const expansion = state.expansion;
×
UNCOV
82
        let i = 0;
×
UNCOV
83
        let result = [];
×
UNCOV
84
        while (i < data.length) {
×
UNCOV
85
            const column = grid ? grid.getColumnByName(expressions[level].fieldName) : null;
×
UNCOV
86
            const isDate = column?.dataType === DATE_TYPE || column?.dataType === DATE_TIME_TYPE;
×
UNCOV
87
            const isTime = column?.dataType === TIME_TYPE || column?.dataType === DATE_TIME_TYPE;
×
UNCOV
88
            const isString = column?.dataType === STRING_TYPE;
×
UNCOV
89
            const group = this.groupedRecordsByExpression(data, i, expressions[level], isDate, isTime, isString);
×
UNCOV
90
            const groupRow: IGroupByRecord = {
×
91
                expression: expressions[level],
92
                level,
93
                records: cloneArray(group),
94
                value: this.getFieldValue(group[0], expressions[level].fieldName, isDate, isTime),
95
                groupParent: parent,
96
                groups: [],
97
                height: grid ? grid.renderedRowHeight : null,
×
98
                column
99
            };
UNCOV
100
            if (parent) {
×
UNCOV
101
                parent.groups.push(groupRow);
×
102
            } else {
UNCOV
103
                groupsRecords.push(groupRow);
×
104
            }
UNCOV
105
            const hierarchy = getHierarchy(groupRow);
×
UNCOV
106
            const expandState: IGroupByExpandState = expansion.find((s) =>
×
UNCOV
107
                isHierarchyMatch(
×
108
                    s.hierarchy || [{ fieldName: groupRow.expression.fieldName, value: groupRow.value }],
×
109
                    hierarchy,
110
                    expressions
111
                ));
UNCOV
112
            const expanded = expandState ? expandState.expanded : state.defaultExpanded;
×
113
            let recursiveResult;
UNCOV
114
            result.push(groupRow);
×
UNCOV
115
            metadata.push(null);
×
UNCOV
116
            fullResult.data.push(groupRow);
×
UNCOV
117
            fullResult.metadata.push(null);
×
UNCOV
118
            if (level < expressions.length - 1) {
×
UNCOV
119
                recursiveResult = this.groupDataRecursive(group, state, level + 1, groupRow,
×
120
                    expanded ? metadata : [], grid, groupsRecords, fullResult);
×
UNCOV
121
                if (expanded) {
×
UNCOV
122
                    result = result.concat(recursiveResult);
×
123
                }
124
            } else {
UNCOV
125
                for (const groupItem of group) {
×
UNCOV
126
                    fullResult.metadata.push(groupRow);
×
UNCOV
127
                    fullResult.data.push(groupItem);
×
128
                }
UNCOV
129
                if (expanded) {
×
UNCOV
130
                    metadata.push(...fullResult.metadata.slice(fullResult.metadata.length - group.length));
×
UNCOV
131
                    result.push(...fullResult.data.slice(fullResult.data.length - group.length));
×
132
                }
133
            }
UNCOV
134
            i += group.length;
×
135
        }
UNCOV
136
        return result;
×
137
    }
138

139
    /**
140
   * Retrieves the value of the specified field from the given object, considering date and time data types.
141
   * `key`: The key of the field to retrieve.
142
   * `isDate`: (Optional) Indicates if the field is of type Date.
143
   * `isTime`: (Optional) Indicates if the field is of type Time.
144
   * Returns the value of the specified field in the data object.
145
   * @internal
146
   */
147
    protected getFieldValue<T>(obj: T, key: string, isDate = false, isTime = false) {
×
UNCOV
148
        let resolvedValue = resolveNestedPath(obj, key);
×
UNCOV
149
        const date = parseDate(resolvedValue);
×
UNCOV
150
        if (date && isDate && isTime) {
×
UNCOV
151
            resolvedValue = date;
×
UNCOV
152
        } else if (date && isDate && !isTime) {
×
UNCOV
153
            resolvedValue = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
×
UNCOV
154
        } else if (date && isTime && !isDate) {
×
UNCOV
155
            resolvedValue = new Date(new Date().setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()));
×
156
        }
UNCOV
157
        return resolvedValue;
×
158
    }
159

160
    /**
161
   * Groups the records in the provided data array based on the given grouping expression.
162
   * `groupingComparer`: (Optional) A custom grouping comparator to determine the members of the group.
163
   * Returns an array containing the records that belong to the group.
164
   * @internal
165
   */
166
    private groupedRecordsByExpression<T>(
167
        data: T[],
168
        index: number,
169
        expression: IGroupingExpression,
170
        isDate = false,
×
171
        isTime = false,
×
172
        isString: boolean,
173
        groupingComparer?: (a: any, b: any, currRec: any, groupRec: any) => number
174
    ): T[] {
UNCOV
175
        const res = [];
×
UNCOV
176
        const key = expression.fieldName;
×
UNCOV
177
        const len = data.length;
×
UNCOV
178
        const groupRecord = data[index];
×
UNCOV
179
        let groupval = this.getFieldValue(groupRecord, key, isDate, isTime);
×
UNCOV
180
        res.push(groupRecord);
×
UNCOV
181
        index++;
×
UNCOV
182
        const comparer = expression.groupingComparer || groupingComparer || DefaultSortingStrategy.instance().compareValues;
×
UNCOV
183
        for (let i = index; i < len; i++) {
×
UNCOV
184
            const currRec = data[i];
×
UNCOV
185
            let fieldValue = this.getFieldValue(currRec, key, isDate, isTime);
×
UNCOV
186
            if (expression.ignoreCase && isString) {
×
187
                // when column's dataType is string but the value is number
UNCOV
188
                fieldValue = fieldValue?.toString().toLowerCase();
×
UNCOV
189
                groupval = groupval?.toString().toLowerCase();
×
190
            }
UNCOV
191
            if (comparer(fieldValue, groupval, currRec, groupRecord) === 0) {
×
UNCOV
192
                res.push(currRec);
×
193
            } else {
UNCOV
194
                break;
×
195
            }
196
        }
UNCOV
197
        return res;
×
198
    }
199

200
    /**
201
   * Sorts the provided data array based on the given sorting expressions.
202
   * The method can be used when multiple sorting is performed, going through each one
203
   * Returns a new array with the data sorted according to the sorting expressions.
204
   * @internal
205
   */
206
    private sortDataRecursive<T>(
207
        data: T[],
208
        expressions: ISortingExpression[],
209
        expressionIndex = 0,
×
210
        grid: GridType
211
    ): T[] {
212
        let i: number;
213
        let j: number;
214
        let gbData: T[];
215
        let gbDataLen: number;
UNCOV
216
        const exprsLen = expressions.length;
×
UNCOV
217
        const dataLen = data.length;
×
218

UNCOV
219
        expressionIndex = expressionIndex || 0;
×
UNCOV
220
        if (expressionIndex >= exprsLen || dataLen <= 1) {
×
UNCOV
221
            return data;
×
222
        }
UNCOV
223
        const expr = expressions[expressionIndex];
×
UNCOV
224
        if (!expr.strategy) {
×
UNCOV
225
            expr.strategy = DefaultSortingStrategy.instance() as any;
×
226
        }
UNCOV
227
        const column = grid?.getColumnByName(expr.fieldName);
×
UNCOV
228
        const isDate = column?.dataType === DATE_TYPE || column?.dataType === DATE_TIME_TYPE;
×
UNCOV
229
        const isTime = column?.dataType === TIME_TYPE || column?.dataType === DATE_TIME_TYPE;
×
UNCOV
230
        const isString = column?.dataType === STRING_TYPE;
×
UNCOV
231
        data = expr.strategy.sort(data, expr.fieldName, expr.dir, expr.ignoreCase, this.getFieldValue, isDate, isTime, grid);
×
UNCOV
232
        if (expressionIndex === exprsLen - 1) {
×
UNCOV
233
            return data;
×
234
        }
235
        // in case of multiple sorting
UNCOV
236
        for (i = 0; i < dataLen; i++) {
×
UNCOV
237
            gbData = this.groupedRecordsByExpression(data, i, expr, isDate, isTime, isString, column?.groupingComparer);
×
UNCOV
238
            gbDataLen = gbData.length;
×
UNCOV
239
            if (gbDataLen > 1) {
×
UNCOV
240
                gbData = this.sortDataRecursive(gbData, expressions, expressionIndex + 1, grid);
×
241
            }
UNCOV
242
            for (j = 0; j < gbDataLen; j++) {
×
UNCOV
243
                data[i + j] = gbData[j];
×
244
            }
UNCOV
245
            i += gbDataLen - 1;
×
246
        }
UNCOV
247
        return data;
×
248
    }
249
}
250

251
/**
252
 * Represents a class implementing the IGridGroupingStrategy interface and extending the IgxSorting class.
253
 * It provides a method to group data based on the given grouping state.
254
 */
255
export class IgxGrouping extends IgxSorting implements IGridGroupingStrategy {
256
    /* blazorSuppress */
257
  /**
258
   * Groups the provided data based on the given grouping state.
259
   * Returns an object containing the result of the grouping operation.
260
   */
261
    public groupBy(data: any[], state: IGroupingState, grid?: any,
262
        groupsRecords?: any[], fullResult: IGroupByResult = { data: [], metadata: [] }): IGroupByResult {
×
UNCOV
263
        const metadata: IGroupByRecord[] = [];
×
UNCOV
264
        const grouping = this.groupDataRecursive(data, state, 0, null, metadata, grid, groupsRecords, fullResult);
×
UNCOV
265
        grid?.groupingPerformedSubject.next();
×
UNCOV
266
        return {
×
267
            data: grouping,
268
            metadata
269
        };
270
    }
271
}
272

273
/* csSuppress */
274
/**
275
 * Represents a class implementing the IGridSortingStrategy interface with a no-operation sorting strategy.
276
 * It performs no sorting and returns the data as it is.
277
 */
278
export class NoopSortingStrategy implements IGridSortingStrategy {
279
    private static _instance: NoopSortingStrategy = null;
2✔
280

281
    private constructor() { }
282

283
    public static instance(): NoopSortingStrategy {
UNCOV
284
        return this._instance || (this._instance = new NoopSortingStrategy());
×
285
    }
286

287
    /* csSuppress */
288
    public sort(data: any[]): any[] {
UNCOV
289
        return data;
×
290
    }
291
}
292

293
/**
294
 * Represents a class extending the IgxSorting class
295
 * Provides custom data record sorting.
296
 */
297
export class IgxDataRecordSorting extends IgxSorting {
298
   /**
299
   * Overrides the base method to retrieve the field value from the data object instead of the record object.
300
   * Returns the value of the specified field in the data object.
301
   */
302
    protected override getFieldValue(obj: any, key: string, isDate = false, isTime = false): any {
×
UNCOV
303
        return super.getFieldValue(obj.data, key, isDate, isTime);
×
304
    }
305
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc