• 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

0.77
/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.service.ts
1
import { Injectable } from '@angular/core';
2
import { GridSelectionMode } from '../common/enums';
3
import { IgxGridSelectionService } from '../selection/selection.service';
4
import { ITreeGridRecord } from './tree-grid.interfaces';
5

6

7
@Injectable()
8
export class IgxTreeGridSelectionService extends IgxGridSelectionService {
2✔
9
    private rowsToBeSelected: Set<any>;
10
    private rowsToBeIndeterminate: Set<any>;
11

12
    /** Select specified rows. No event is emitted. */
13
    public override selectRowsWithNoEvent(rowIDs: any[], clearPrevSelection?): void {
UNCOV
14
        if (this.grid && this.grid.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
15
            this.cascadeSelectRowsWithNoEvent(rowIDs, clearPrevSelection);
×
UNCOV
16
            return;
×
17
        }
UNCOV
18
        super.selectRowsWithNoEvent(rowIDs, clearPrevSelection);
×
19
    }
20

21
    /** Deselect specified rows. No event is emitted. */
22
    public override deselectRowsWithNoEvent(rowIDs: any[]): void {
UNCOV
23
        if (this.grid.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
24
            this.cascadeDeselectRowsWithNoEvent(rowIDs);
×
UNCOV
25
            return;
×
26
        }
UNCOV
27
        super.deselectRowsWithNoEvent(rowIDs);
×
28
    }
29

30
    public override emitRowSelectionEvent(newSelection, added, removed, event?): boolean {
UNCOV
31
        if (this.grid.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
32
            this.emitCascadeRowSelectionEvent(newSelection, added, removed, event);
×
UNCOV
33
            return;
×
34
        }
35

UNCOV
36
        super.emitRowSelectionEvent(newSelection, added, removed, event);
×
37
    }
38

39
    public updateCascadeSelectionOnFilterAndCRUD(
40
        parents: Set<any>,
41
        crudRowID?: any,
42
        visibleRowIDs: Set<any> = null) {
×
UNCOV
43
        if (visibleRowIDs === null) {
×
44
            // if the tree grid has flat structure
45
            // do not explicitly handle the selection state of the rows
UNCOV
46
            if (!parents.size) {
×
47
                return;
×
48
            }
UNCOV
49
            visibleRowIDs = new Set(this.getRowIDs(this.allData));
×
UNCOV
50
            this.rowsToBeSelected = new Set(this.rowSelection);
×
UNCOV
51
            this.rowsToBeIndeterminate = new Set(this.indeterminateRows);
×
UNCOV
52
            if (crudRowID) {
×
UNCOV
53
                this.rowSelection.delete(crudRowID);
×
54
            }
55
        }
UNCOV
56
        if (!parents.size) {
×
UNCOV
57
            this.rowSelection = new Set(this.rowsToBeSelected);
×
UNCOV
58
            this.indeterminateRows = new Set(this.rowsToBeIndeterminate);
×
59
            // TODO: emit selectionChangeD event, calculate its args through the handleAddedAndRemovedArgs method
UNCOV
60
            this.clearHeaderCBState();
×
UNCOV
61
            this.selectedRowsChange.next(this.getSelectedRows());
×
UNCOV
62
            return;
×
63
        }
UNCOV
64
        const newParents = new Set<any>();
×
UNCOV
65
        parents.forEach(parent => {
×
UNCOV
66
            this.handleRowSelectionState(parent, visibleRowIDs);
×
UNCOV
67
            if (parent && parent.parent) {
×
UNCOV
68
                newParents.add(parent.parent);
×
69
            }
70
        });
UNCOV
71
        this.updateCascadeSelectionOnFilterAndCRUD(newParents, null, visibleRowIDs);
×
72
    }
73

74
    private cascadeSelectRowsWithNoEvent(rowIDs: any[], clearPrevSelection?: boolean): void {
UNCOV
75
        if (clearPrevSelection) {
×
UNCOV
76
            this.indeterminateRows.clear();
×
UNCOV
77
            this.rowSelection.clear();
×
UNCOV
78
            this.calculateRowsNewSelectionState({ added: rowIDs, removed: [] });
×
79
        } else {
UNCOV
80
            const oldSelection = this.getSelectedRows();
×
UNCOV
81
            const newSelection = [...oldSelection, ...rowIDs];
×
UNCOV
82
            const args = { oldSelection, newSelection };
×
83

84
            // retrieve only the rows without their parents/children which has to be added to the selection
UNCOV
85
            this.handleAddedAndRemovedArgs(args);
×
86

UNCOV
87
            this.calculateRowsNewSelectionState(args);
×
88
        }
UNCOV
89
        this.rowSelection = new Set(this.rowsToBeSelected);
×
UNCOV
90
        this.indeterminateRows = new Set(this.rowsToBeIndeterminate);
×
UNCOV
91
        this.clearHeaderCBState();
×
UNCOV
92
        this.selectedRowsChange.next(this.getSelectedRows());
×
93
    }
94

95
    private cascadeDeselectRowsWithNoEvent(rowIDs: any[]): void {
UNCOV
96
        const args = { added: [], removed: rowIDs };
×
UNCOV
97
        this.calculateRowsNewSelectionState(args);
×
98

UNCOV
99
        this.rowSelection = new Set(this.rowsToBeSelected);
×
UNCOV
100
        this.indeterminateRows = new Set(this.rowsToBeIndeterminate);
×
UNCOV
101
        this.clearHeaderCBState();
×
UNCOV
102
        this.selectedRowsChange.next(this.getSelectedRows());
×
103
    }
104

105
    public get selectionService(): IgxGridSelectionService {
106
        return this.grid.selectionService;
×
107
    }
108

109
    private emitCascadeRowSelectionEvent(newSelection, added, removed, event?): boolean {
UNCOV
110
        const currSelection = this.getSelectedRows();
×
UNCOV
111
        if (this.areEqualCollections(currSelection, newSelection)) {
×
112
            return;
×
113
        }
114

UNCOV
115
        const args = {
×
116
            owner: this.grid, oldSelection: this.getSelectedRowsData(), newSelection,
117
            added, removed, event, cancel: false
118
        };
119

UNCOV
120
        this.calculateRowsNewSelectionState(args, !!this.grid.primaryKey);
×
UNCOV
121
        args.newSelection = Array.from(this.grid.gridAPI.get_all_data().filter(r => this.rowsToBeSelected.has(this.grid.primaryKey ? r[this.grid.primaryKey] : r)));
×
122

123
        // retrieve rows/parents/children which has been added/removed from the selection
UNCOV
124
        this.handleAddedAndRemovedArgs(args);
×
125

UNCOV
126
        this.grid.rowSelectionChanging.emit(args);
×
127

UNCOV
128
        if (args.cancel) {
×
UNCOV
129
            return;
×
130
        }
UNCOV
131
        const newSelectionIDs = args.newSelection.map(r => this.grid.primaryKey? r[this.grid.primaryKey] : r)
×
132
        // if args.newSelection hasn't been modified
UNCOV
133
        if (this.areEqualCollections(Array.from(this.rowsToBeSelected), newSelectionIDs)) {
×
UNCOV
134
            this.rowSelection = new Set(this.rowsToBeSelected);
×
UNCOV
135
            this.indeterminateRows = new Set(this.rowsToBeIndeterminate);
×
UNCOV
136
            this.clearHeaderCBState();
×
UNCOV
137
            this.selectedRowsChange.next(this.getSelectedRows());
×
138
        } else {
139
            // select the rows within the modified args.newSelection with no event
UNCOV
140
            this.cascadeSelectRowsWithNoEvent(newSelectionIDs, true);
×
141
        }
142
    }
143

144

145
    /**
146
     * retrieve the rows which should be added/removed to/from the old selection
147
     */
148
    private handleAddedAndRemovedArgs(args: any) {
UNCOV
149
        const newSelectionSet = new Set(args.newSelection);
×
UNCOV
150
        const oldSelectionSet = new Set(args.oldSelection);
×
UNCOV
151
        args.removed = args.oldSelection.filter(x => !newSelectionSet.has(x));
×
UNCOV
152
        args.added = args.newSelection.filter(x => !oldSelectionSet.has(x));
×
153
    }
154

155
    /**
156
     * adds to rowsToBeProcessed set all visible children of the rows which was initially within the rowsToBeProcessed set
157
     *
158
     * @param rowsToBeProcessed set of the rows (without their parents/children) to be selected/deselected
159
     * @param visibleRowIDs list of all visible rowIds
160
     * @returns a new set with all direct parents of the rows within rowsToBeProcessed set
161
     */
162
    private collectRowsChildrenAndDirectParents(rowsToBeProcessed: Set<any>, visibleRowIDs: Set<any>, adding: boolean, shouldConvert: boolean): Set<any> {
UNCOV
163
        const processedRowsParents = new Set<any>();
×
UNCOV
164
        Array.from(rowsToBeProcessed).forEach((row) => {
×
UNCOV
165
            const rowID = shouldConvert ? row[this.grid.primaryKey] : row;
×
UNCOV
166
            this.selectDeselectRow(rowID, adding);
×
UNCOV
167
            const rowTreeRecord = this.grid.gridAPI.get_rec_by_id(rowID);
×
UNCOV
168
            const rowAndAllChildren = this.get_all_children(rowTreeRecord);
×
UNCOV
169
            rowAndAllChildren.forEach(r => {
×
UNCOV
170
                if (visibleRowIDs.has(r.key)) {
×
UNCOV
171
                    this.selectDeselectRow(r.key, adding);
×
172
                }
173
            });
UNCOV
174
            if (rowTreeRecord && rowTreeRecord.parent) {
×
UNCOV
175
                processedRowsParents.add(rowTreeRecord.parent);
×
176
            }
177
        });
UNCOV
178
        return processedRowsParents;
×
179
    }
180

181

182
    /**
183
     * populates the rowsToBeSelected and rowsToBeIndeterminate sets
184
     * with the rows which will be eventually in selected/indeterminate state
185
     */
186
    private calculateRowsNewSelectionState(args: any, shouldConvert = false) {
×
UNCOV
187
        this.rowsToBeSelected = new Set<any>(args.oldSelection ? shouldConvert ? args.oldSelection.map(r => r[this.grid.primaryKey]) : args.oldSelection : this.getSelectedRows());
×
UNCOV
188
        this.rowsToBeIndeterminate = new Set<any>(this.getIndeterminateRows());
×
189

UNCOV
190
        const visibleRowIDs = new Set(this.getRowIDs(this.allData));
×
191

UNCOV
192
        const removed = new Set(args.removed);
×
UNCOV
193
        const added = new Set(args.added);
×
194

UNCOV
195
        if (removed && removed.size) {
×
UNCOV
196
            let removedRowsParents = new Set<any>();
×
197

UNCOV
198
            removedRowsParents = this.collectRowsChildrenAndDirectParents(removed, visibleRowIDs, false, shouldConvert);
×
199

UNCOV
200
            Array.from(removedRowsParents).forEach((parent) => {
×
UNCOV
201
                this.handleParentSelectionState(parent, visibleRowIDs);
×
202
            });
203
        }
204

UNCOV
205
        if (added && added.size) {
×
UNCOV
206
            let addedRowsParents = new Set<any>();
×
207

UNCOV
208
            addedRowsParents = this.collectRowsChildrenAndDirectParents(added, visibleRowIDs, true, shouldConvert);
×
209

UNCOV
210
            Array.from(addedRowsParents).forEach((parent) => {
×
UNCOV
211
                this.handleParentSelectionState(parent, visibleRowIDs);
×
212
            });
213
        }
214
    }
215

216
    /**
217
     * recursively handle the selection state of the direct and indirect parents
218
     */
219
    private handleParentSelectionState(treeRow: ITreeGridRecord, visibleRowIDs: Set<any>) {
UNCOV
220
        if (!treeRow) {
×
221
            return;
×
222
        }
UNCOV
223
        this.handleRowSelectionState(treeRow, visibleRowIDs);
×
UNCOV
224
        if (treeRow.parent) {
×
UNCOV
225
            this.handleParentSelectionState(treeRow.parent, visibleRowIDs);
×
226
        }
227
    }
228

229
    /**
230
     * Handle the selection state of a given row based the selection states of its direct children
231
     */
232
    private handleRowSelectionState(treeRow: ITreeGridRecord, visibleRowIDs: Set<any>) {
UNCOV
233
        let visibleChildren = [];
×
UNCOV
234
        if (treeRow && treeRow.children) {
×
UNCOV
235
            visibleChildren = treeRow.children.filter(child => visibleRowIDs.has(child.key));
×
236
        }
UNCOV
237
        if (visibleChildren.length) {
×
UNCOV
238
            if (visibleChildren.every(row => this.rowsToBeSelected.has(row.key))) {
×
UNCOV
239
                this.selectDeselectRow(treeRow.key, true);
×
UNCOV
240
            } else if (visibleChildren.some(row => this.rowsToBeSelected.has(row.key) || this.rowsToBeIndeterminate.has(row.key))) {
×
UNCOV
241
                this.rowsToBeIndeterminate.add(treeRow.key);
×
UNCOV
242
                this.rowsToBeSelected.delete(treeRow.key);
×
243
            } else {
UNCOV
244
                this.selectDeselectRow(treeRow.key, false);
×
245
            }
246
        } else {
247
            // if the children of the row has been deleted and the row was selected do not change its state
UNCOV
248
            if (this.isRowSelected(treeRow.key)) {
×
249
                this.selectDeselectRow(treeRow.key, true);
×
250
            } else {
UNCOV
251
                this.selectDeselectRow(treeRow.key, false);
×
252
            }
253
        }
254
    }
255

256
    private get_all_children(record: ITreeGridRecord): any[] {
UNCOV
257
        const children = [];
×
UNCOV
258
        if (record && record.children && record.children.length) {
×
UNCOV
259
            for (const child of record.children) {
×
UNCOV
260
                children.push(...this.get_all_children(child));
×
UNCOV
261
                children.push(child);
×
262
            }
263
        }
UNCOV
264
        return children;
×
265

266
    }
267

268
    private selectDeselectRow(rowID: any, select: boolean) {
UNCOV
269
        if (select) {
×
UNCOV
270
            this.rowsToBeSelected.add(rowID);
×
UNCOV
271
            this.rowsToBeIndeterminate.delete(rowID);
×
272
        } else {
UNCOV
273
            this.rowsToBeSelected.delete(rowID);
×
UNCOV
274
            this.rowsToBeIndeterminate.delete(rowID);
×
275
        }
276
    }
277

278
}
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