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

IgniteUI / igniteui-angular / 6799302264

08 Nov 2023 02:02PM CUT coverage: 92.278% (+0.02%) from 92.256%
6799302264

push

github

web-flow
Merge pull request #13644 from IgniteUI/ibarakov/feat-13524-15.1.x

feat(esf): improve search results accessibility - 15.1.x

15312 of 17989 branches covered (0.0%)

4 of 4 new or added lines in 1 file covered. (100.0%)

9 existing lines in 2 files now uncovered.

26863 of 29111 relevant lines covered (92.28%)

29733.65 hits per line

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

93.1
/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts
1
import {
2
    AfterViewInit,
3
    Component,
4
    ViewChild,
5
    ChangeDetectorRef,
6
    TemplateRef,
7
    Directive,
8
    OnDestroy,
9
    HostBinding
10
} from '@angular/core';
11
import { IgxInputDirective } from '../../../directives/input/input.directive';
12
import { DisplayDensity } from '../../../core/density';
13
import { IgxForOfDirective } from '../../../directives/for-of/for_of.directive';
14
import { FilteringExpressionsTree } from '../../../data-operations/filtering-expressions-tree';
15
import { FilteringLogic } from '../../../data-operations/filtering-expression.interface';
16
import { GridColumnDataType } from '../../../data-operations/data-util';
17
import {
18
    IgxBooleanFilteringOperand, IgxNumberFilteringOperand, IgxDateFilteringOperand,
19
    IgxStringFilteringOperand, IgxDateTimeFilteringOperand, IgxTimeFilteringOperand
2✔
20
} from '../../../data-operations/filtering-condition';
21
import { Subject } from 'rxjs';
×
22
import { IgxListComponent } from '../../../list/public_api';
23
import { IChangeCheckboxEventArgs, IgxCheckboxComponent } from '../../../checkbox/checkbox.component';
24
import { takeUntil } from 'rxjs/operators';
25
import { cloneHierarchicalArray, PlatformUtil } from '../../../core/utils';
×
26
import { BaseFilteringComponent } from './base-filtering.component';
27
import { ExpressionUI, FilterListItem } from './common';
2✔
28
import { IgxTreeComponent, ITreeNodeSelectionEvent } from '../../../tree/public_api';
29

30
@Directive({
31
    selector: '[igxExcelStyleLoading]'
2✔
32
})
33
export class IgxExcelStyleLoadingValuesTemplateDirective {
34
    public static ngTemplateContextGuard(_dir: IgxExcelStyleLoadingValuesTemplateDirective,
35
        ctx: unknown): ctx is undefined {
36
        return true
37
    };
38
    constructor(public template: TemplateRef<undefined>) { }
39
}
40

2✔
41
/**
2✔
42
 * A component used for presenting Excel style search UI.
43
 */
44
@Component({
45
    selector: 'igx-excel-style-search',
46
    templateUrl: './excel-style-search.component.html'
1,328!
47
})
×
48
export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
49
    private static readonly filterOptimizationThreshold = 2;
50

51
    /**
52
     * @hidden @internal
53
     */
54
    @HostBinding('class.igx-excel-filter__menu-main')
55
    public defaultClass = true;
56

×
57
    /**
58
     * @hidden @internal
1,328✔
59
     */
60
    @ViewChild('input', { read: IgxInputDirective, static: true })
61
    public searchInput: IgxInputDirective;
62

63
    /**
64
     * @hidden @internal
280✔
65
     */
59✔
66
    @ViewChild('list', { read: IgxListComponent, static: false })
67
    public list: IgxListComponent;
68

69
    /**
70
     * @hidden @internal
71
     */
72
    @ViewChild('selectAllCheckbox', { read: IgxCheckboxComponent, static: false })
73
    public selectAllCheckbox: IgxCheckboxComponent;
74

59✔
75
    /**
76
     * @hidden @internal
280✔
77
     */
78
    @ViewChild('addToCurrentFilterCheckbox', { read: IgxCheckboxComponent, static: false })
79
    public addToCurrentFilterCheckbox: IgxCheckboxComponent;
80

81
    /**
82
     * @hidden @internal
5,763✔
83
     */
84
    @ViewChild('tree', { read: IgxTreeComponent, static: false })
85
    public tree: IgxTreeComponent;
86

87
    /**
88
     * @hidden @internal
477✔
89
     */
477!
90
    @ViewChild(IgxForOfDirective, { static: true })
477✔
91
    protected virtDir: IgxForOfDirective<any>;
92

93
    /**
94
     * @hidden @internal
95
     */
96
    @ViewChild('defaultExcelStyleLoadingValuesTemplate', { read: TemplateRef })
97
    protected defaultExcelStyleLoadingValuesTemplate: TemplateRef<any>;
985!
98

×
99
    /**
100
     * @hidden @internal
101
     */
985✔
102
    public get selectAllItem(): FilterListItem {
103
        if (!this._selectAllItem) {
104
            const selectAllItem = {
105
                isSelected: false,
396✔
106
                isFiltered: false,
396✔
107
                indeterminate: false,
396✔
108
                isSpecial: true,
396✔
109
                isBlanks: false,
110
                value: this.esf.grid.resourceStrings.igx_grid_excel_select_all,
111
                label: this.esf.grid.resourceStrings.igx_grid_excel_select_all
112
            };
396✔
113

396✔
114
            this._selectAllItem = selectAllItem;
115
        }
116

117
        return this._selectAllItem;
396✔
118
    }
870!
119

×
120
    /**
×
121
     * @hidden @internal
×
122
     */
×
123
    public get addToCurrentFilterItem(): FilterListItem {
124
        if (!this._addToCurrentFilterItem) {
125
            const addToCurrentFilterItem = {
396✔
126
                isSelected: false,
258✔
127
                isFiltered: false,
258✔
128
                indeterminate: false,
129
                isSpecial: true,
396✔
130
                isBlanks: false,
219✔
131
                value: this.esf.grid.resourceStrings.igx_grid_excel_add_to_filter,
219✔
132
                label: this.esf.grid.resourceStrings.igx_grid_excel_add_to_filter
133
            };
396✔
134

189✔
135
            this._addToCurrentFilterItem = addToCurrentFilterItem;
164✔
136
        }
164✔
137

138
        return this._addToCurrentFilterItem;
139
    }
396✔
140

24✔
141
    /**
142
     * @hidden @internal
396✔
143
     */
219✔
144
    public get isLoading() {
219✔
145
        return this._isLoading;
28✔
146
    }
147

219✔
148
    /**
3✔
149
     * @hidden @internal
150
     */
151
    public set isLoading(value: boolean) {
216✔
152
        this._isLoading = value;
153
        if (!(this.cdr as any).destroyed) {
219✔
154
            this.cdr.detectChanges();
219✔
155
        }
170✔
156
    }
170✔
157

158
    /**
159
     * @hidden @internal
160
     */
161
    public searchValue: any;
396✔
162

163
    /**
164
     * @hidden @internal
301✔
165
     */
301✔
166
    public displayedListData: FilterListItem[] = [];
167

168
    /**
169
     * @hidden @internal
170
     */
171
    public matchesCount: number;
7✔
172

7✔
173
    /**
174
     * @hidden @internal
175
     */
176
    public get valuesLoadingTemplate() {
177
        if (this.esf.grid?.excelStyleLoadingValuesTemplateDirective) {
178
            return this.esf.grid.excelStyleLoadingValuesTemplateDirective.template;
53✔
179
        } else {
53✔
180
            return this.defaultExcelStyleLoadingValuesTemplate;
53✔
181
        }
11✔
182
    }
69!
UNCOV
183

×
184
    private _isLoading;
185
    private _addToCurrentFilterItem: FilterListItem;
69✔
186
    private _selectAllItem: FilterListItem;
187
    private _hierarchicalSelectedItems: FilterListItem[];
11✔
188
    private destroy$ = new Subject<boolean>();
189

190
    constructor(public cdr: ChangeDetectorRef, public esf: BaseFilteringComponent, protected platform: PlatformUtil) {
42✔
191
        esf.loadingStart.pipe(takeUntil(this.destroy$)).subscribe(() => {
42✔
192
            this.displayedListData = [];
42✔
193
            this.isLoading = true;
66✔
194
        });
2✔
195
        esf.loadingEnd.pipe(takeUntil(this.destroy$)).subscribe(() => {
2✔
196
            this.refreshSize();
197
            this.isLoading = false;
72✔
198
        });
1✔
199
        esf.initialized.pipe(takeUntil(this.destroy$)).subscribe(() => {
1✔
200
            requestAnimationFrame(() => {
201
                this.refreshSize();
202
                this.searchInput.nativeElement.focus();
39✔
203
            });
204
        });
205
        esf.columnChange.pipe(takeUntil(this.destroy$)).subscribe(() => {
53✔
206
            this.virtDir?.resetScrollPosition();
207
        });
208

209
        esf.listDataLoaded.pipe(takeUntil(this.destroy$)).subscribe(() => {
210
            this._selectAllItem = this.esf.listData[0];
211
            if (this.isHierarchical() && this.esf.listData[0].isSpecial) {
2✔
212
                this.esf.listData.splice(0, 1);
2✔
213
            }
2✔
214

36✔
215
            if (this.searchValue) {
216
                this.clearInput();
217
            } else {
218
                this.filterListData();
219
            }
220

8✔
221
            this.cdr.detectChanges();
4✔
222
            requestAnimationFrame(() => {
223
                this.refreshSize();
8✔
224
                this.searchInput.nativeElement.focus();
29✔
225
            });
226
        });
72✔
227
    }
8✔
228

8!
UNCOV
229
    public ngAfterViewInit() {
×
UNCOV
230
        requestAnimationFrame(this.refreshSize);
×
231
    }
232

8!
UNCOV
233
    public ngOnDestroy(): void {
×
234
        this.destroy$.next(true);
×
235
        this.destroy$.complete();
236
    }
237

8✔
238
    /**
8✔
239
     * @hidden @internal
240
     */
241
    public refreshSize = () => {
242
        if (this.virtDir) {
243
            this.virtDir.igxForContainerSize = this.containerSize;
244
            this.virtDir.igxForItemSize = this.itemSize;
245
            this.virtDir.recalcUpdateSizes();
5,763✔
246
            this.cdr.detectChanges();
5,763✔
247
        }
248
    }
123✔
249

123✔
250
    /**
251
     * @hidden @internal
94✔
252
     */
94✔
253
    public clearInput() {
5,546✔
254
        this.searchValue = null;
255
        this.filterListData();
5,763✔
256
    }
257

258
    /**
259
     * @hidden @internal
260
     */
261
    public onCheckboxChange(eventArgs: IChangeCheckboxEventArgs) {
5,763✔
262
        const selectedIndex = this.displayedListData.indexOf(eventArgs.checkbox.value);
1,870✔
263
        const selectAllBtn = this.displayedListData[0];
264

265
        if (selectedIndex === 0) {
266
            this.displayedListData.forEach(element => {
267
                if (element === this.addToCurrentFilterItem) {
3,893✔
268
                    return;
269
                }
270
                element.isSelected = eventArgs.checked;
271
            });
272

273
            selectAllBtn.indeterminate = false;
6,033✔
274
        } else {
275
            eventArgs.checkbox.value.isSelected = eventArgs.checked;
276
            const indexToStartSlicing = this.displayedListData.indexOf(this.addToCurrentFilterItem) > -1 ? 2 : 1;
277

278
            const slicedArray =
279
                this.displayedListData.slice(indexToStartSlicing, this.displayedListData.length);
280

41✔
281
            if (!slicedArray.find(el => el.isSelected === false)) {
282
                selectAllBtn.indeterminate = false;
2✔
283
                selectAllBtn.isSelected = true;
2✔
284
            } else if (!slicedArray.find(el => el.isSelected === true)) {
2✔
285
                selectAllBtn.indeterminate = false;
286
                selectAllBtn.isSelected = false;
2✔
287
            } else {
1✔
288
                selectAllBtn.indeterminate = true;
1✔
289
            }
290
        }
2✔
291
        eventArgs.checkbox.nativeCheckbox.nativeElement.blur();
292
    }
293

294
    /**
295
     * @hidden @internal
296
     */
297
    public onSelectAllCheckboxChange(eventArgs: IChangeCheckboxEventArgs) {
260✔
298
        this._selectAllItem.isSelected = eventArgs.checked;
299
        this._selectAllItem.indeterminate = false;
300
        const treeNodes = this.tree.nodes;
106✔
301
        treeNodes.forEach(node => (node.data as FilterListItem).isSelected = eventArgs.checked);
302
    }
260✔
303

2✔
304
    /**
2✔
305
     * @hidden @internal
306
     */
307
    public onNodeSelectionChange(eventArgs: ITreeNodeSelectionEvent) {
258!
308
        eventArgs.added.forEach(node => {
258✔
309
            (node.data as FilterListItem).isSelected = true;
310
        });
UNCOV
311
        eventArgs.removed.forEach(node => {
×
312
            (node.data as FilterListItem).isSelected = false;
313
        });
258✔
314

303✔
315
        this._hierarchicalSelectedItems = eventArgs.newSelection.map(item => item.data as FilterListItem);
1,286✔
316
        const selectAllBtn = this.selectAllItem;
222✔
317
        if (this._hierarchicalSelectedItems.length === 0) {
222✔
318
            selectAllBtn.indeterminate = false;
24✔
319
            selectAllBtn.isSelected = false;
320
        } else if (this._hierarchicalSelectedItems.length === this.tree.nodes.length) {
1,484✔
321
            selectAllBtn.indeterminate = false;
222!
322
            selectAllBtn.isSelected = true;
222✔
323
        } else {
222✔
324
            selectAllBtn.indeterminate = true;
29✔
325
            selectAllBtn.isSelected = false;
29✔
326
        }
495✔
327
    }
495✔
328

495✔
329
    /**
495✔
330
     * @hidden @internal
331
     */
29✔
332
    public get itemSize() {
333
        let itemSize = '40px';
334
        switch (this.esf.displayDensity) {
222✔
335
            case DisplayDensity.cosy: itemSize = '32px'; break;
222✔
336
            case DisplayDensity.compact: itemSize = '24px'; break;
222✔
337
            default: break;
222✔
338
        }
339
        return itemSize;
36✔
340
    }
36✔
341

10✔
342
    /**
40✔
343
     * @hidden @internal
10✔
344
     */
10✔
345
    public get containerSize() {
10✔
346
        if (this.esf.listData.length) {
10✔
347
            return this.list?.element.nativeElement.offsetHeight;
52✔
348
        }
52✔
349

21✔
350
        // GE Nov 1st, 2021 #10355 Return a numeric value, so the chunk size is calculated properly.
351
        // If we skip this branch, on applying the filter the _calculateChunkSize() method off the ForOfDirective receives
352
        // an igxForContainerSize = undefined, thus assigns the chunkSize to the igxForOf.length which leads to performance issues.
353
        return 0;
354
    }
211✔
355

356
    /**
357
     * @hidden @internal
358
     */
211✔
359
    public get applyButtonDisabled(): boolean {
72✔
360
        return (this._selectAllItem && !this._selectAllItem.isSelected && !this._selectAllItem.indeterminate) ||
26✔
361
            (this.displayedListData && this.displayedListData.length === 0);
26✔
362
    }
6✔
363

364
    /**
365
     * @hidden @internal
36✔
366
     */
21✔
367
    public onInputKeyDown(event: KeyboardEvent): void {
368
        switch (event.key) {
369
            case this.platform.KEYMAP.ENTER:
15✔
370
                event.preventDefault();
371
                this.applyFilter();
36✔
372

36✔
373
                return;
36✔
374
            case this.platform.KEYMAP.ESCAPE:
36✔
375
                if (this.searchValue) {
376
                    event.stopPropagation();
377
                    this.clearInput();
378
                }
379

380
                return;
42✔
381
        }
42✔
382
    }
42✔
383

10✔
384
    /**
1✔
385
     * @hidden @internal
386
     */
10✔
387
    public filterListData(): void {
388
        if (this.esf.column?.dataType === GridColumnDataType.Number ||
389
            this.esf.column?.dataType === GridColumnDataType.Currency ||
32✔
390
            this.esf.column?.dataType === GridColumnDataType.Percent) {
32✔
391
            this.rejectNonNumericalEntries();
32✔
392
        }
8✔
393

174✔
394
        if (!this.esf.listData || !this.esf.listData.length) {
395
            this.displayedListData = [];
396

42✔
397
            return;
13✔
398
        }
399

400
        let selectAllBtn;
66✔
401
        if (this._selectAllItem) {
402
            selectAllBtn = this._selectAllItem;
42✔
403
        } else {
36✔
404
            selectAllBtn = this.esf.listData[0];
20✔
405
        }
30✔
406

30✔
407
        if (!this.searchValue) {
28✔
408
            let anyFiltered = this.esf.listData.some(i => i.isFiltered);
2✔
409
            let anyUnfiltered = this.esf.listData.some(i => !i.isFiltered);
410
            selectAllBtn.indeterminate = anyFiltered && anyUnfiltered;
411
            if (this.isHierarchical() && this.tree) {
26!
412
                this._hierarchicalSelectedItems = this.tree.nodes.map(n => n.data as FilterListItem).filter(item => item.isFiltered);
26✔
413
            }
414

415
            this.esf.listData.forEach(i => i.isSelected = i.isFiltered);
416
            if (this.displayedListData !== this.esf.listData) {
2✔
417
                this.displayedListData = this.esf.listData;
418
                if (this.isHierarchical()) {
30✔
419
                    this.cdr.detectChanges();
420
                    this.tree.nodes.forEach(n => {
421
                        const item = n.data as FilterListItem;
422
                        n.selected = item.isSelected || item.isFiltered;
423
                        anyFiltered = anyFiltered || n.selected;
424
                        anyUnfiltered = anyUnfiltered || !n.selected;
425
                    });
426
                    selectAllBtn.indeterminate = anyFiltered && anyUnfiltered;
427
                }
54✔
428
            }
429
            selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all;
16✔
430
            this.matchesCount = this.displayedListData.length - 1;
2✔
431
            this.cdr.detectChanges();
2✔
432

433
            return;
16✔
434
        }
435

436
        const searchVal = this.searchValue.toLowerCase();
437
        if (this.isHierarchical()) {
46✔
438
            this._hierarchicalSelectedItems = [];
439
            this.esf.listData.forEach(i => i.isSelected = false);
9!
UNCOV
440
            const matchedData = cloneHierarchicalArray(this.esf.listData, 'children');
×
441
            this.displayedListData = this.hierarchicalSelectMatches(matchedData, searchVal);
50✔
442
            this.cdr.detectChanges();
443
            this.tree.nodes.forEach(n => {
16✔
444
                n.selected = true;
2✔
445
                if ((n.data as FilterListItem).label.toString().toLowerCase().indexOf(searchVal) > -1) {
446
                    this.expandAllParentNodes(n);
447
                }
448
            });
449
        } else {
450
            this.displayedListData = this.esf.listData.filter((it, i) => (i === 0 && it.isSpecial) ||
451
                (it.label !== null && it.label !== undefined) &&
452
                !it.isBlanks &&
36✔
453
                it.label.toString().toLowerCase().indexOf(searchVal) > -1);
36✔
454

36✔
455
            this.esf.listData.forEach(i => i.isSelected = false);
36✔
456
            this.displayedListData.forEach(i => i.isSelected = true);
36✔
457
            this.displayedListData.splice(1, 0, this.addToCurrentFilterItem);
458
            if (this.displayedListData.length === 2) {
459
                this.displayedListData = [];
6✔
460
            }
461
        }
42✔
462

463
        if (this.displayedListData.length > 2) {
464
            this.matchesCount = this.displayedListData.length - 2;
465
        } else {
466
            this.matchesCount = 0;
467
        }
12,849✔
468

469
        selectAllBtn.indeterminate = false;
470
        selectAllBtn.isSelected = true;
471
        selectAllBtn.label = this.esf.grid.resourceStrings.igx_grid_excel_select_all_search_results;
472
        this.cdr.detectChanges();
473
    }
540✔
474

475
    /**
476
     * @hidden @internal
65✔
477
     */
156✔
478
    public applyFilter() {
156✔
479
        const filterTree = new FilteringExpressionsTree(FilteringLogic.Or, this.esf.column.field);
2,808✔
480

156!
481
        let selectedItems = [];
156✔
482
        if (this.isHierarchical()) {
483
            if (this.addToCurrentFilterCheckbox && this.addToCurrentFilterCheckbox.checked) {
156✔
484
                this.addFilteredToSelectedItems(this.esf.listData);
15✔
485
            }
15✔
486

15✔
487
            selectedItems = this._hierarchicalSelectedItems;
488
        } else {
141✔
489
            const item = this.displayedListData[1];
55✔
490
            const addToCurrentFilterOptionVisible = item === this.addToCurrentFilterItem;
55✔
491
            selectedItems = addToCurrentFilterOptionVisible && item.isSelected ?
13✔
492
                this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected || el.isFiltered) :
13!
493
                this.esf.listData.slice(1, this.esf.listData.length).filter(el => el.isSelected);
13✔
494
        }
495

496
        let unselectedItem;
497
        if (this.isHierarchical()) {
498
            unselectedItem = this.esf.listData.find(el => el.isSelected === false);
156✔
499
        } else {
500
            unselectedItem = this.esf.listData.slice(1, this.esf.listData.length).find(el => el.isSelected === false);
501
        }
39✔
502

24✔
503
        if (unselectedItem) {
24✔
504
            if (selectedItems.length <= IgxExcelStyleSearchComponent.filterOptimizationThreshold) {
24✔
505
                selectedItems.forEach(element => {
24!
506
                    let condition = null;
24✔
507
                    if (element.value !== null && element.value !== undefined) {
508
                        if (this.esf.column.dataType === GridColumnDataType.Boolean) {
509
                            condition = this.createCondition(element.value.toString());
510
                        } else {
511
                            const filterCondition = this.esf.column.dataType === GridColumnDataType.Time ? 'at' : 'equals';
42✔
512
                            condition = this.createCondition(filterCondition);
21✔
513
                        }
21✔
514
                    } else {
515
                        condition = this.createCondition('empty');
516
                    }
517
                    filterTree.filteringOperands.push({
19✔
518
                        condition,
18!
519
                        fieldName: this.esf.column.field,
18✔
520
                        ignoreCase: this.esf.column.filteringIgnoreCase,
521
                        searchVal: element.value
18✔
522
                    });
2✔
523
                });
524
            } else {
525
                const blanksItemIndex = selectedItems.findIndex(e => e.value === null || e.value === undefined);
526
                let blanksItem: any;
527
                if (blanksItemIndex >= 0) {
48!
528
                    blanksItem = selectedItems[blanksItemIndex];
529
                    selectedItems.splice(blanksItemIndex, 1);
4✔
530
                }
531
                filterTree.filteringOperands.push({
532
                    condition: this.createCondition('in'),
533
                    fieldName: this.esf.column.field,
21✔
534
                    ignoreCase: this.esf.column.filteringIgnoreCase,
535
                    searchVal: new Set(this.esf.column.dataType === GridColumnDataType.Date ||
3✔
536
                        this.esf.column.dataType === GridColumnDataType.DateTime ?
UNCOV
537
                        selectedItems.map(d => d.value.toISOString()) : this.esf.column.dataType === GridColumnDataType.Time ?
×
538
                            selectedItems.map(e => e.value.toLocaleTimeString()) :
UNCOV
539
                            selectedItems.map(e => e.value))
×
540
                });
541

20✔
542
                if (blanksItem) {
543
                    filterTree.filteringOperands.push({
544
                        condition: this.createCondition('empty'),
545
                        fieldName: this.esf.column.field,
546
                        ignoreCase: this.esf.column.filteringIgnoreCase,
547
                        searchVal: blanksItem.value
548
                    });
106✔
549
                }
106✔
550
            }
1✔
551
            const grid = this.esf.grid;
1✔
552
            const col = this.esf.column;
553
            grid.filteringService.filterInternal(col.field, filterTree);
554
            this.esf.expressionsList = new Array<ExpressionUI>();
2✔
555
            grid.filteringService.generateExpressionsList(col.filteringExpressionsTree,
556
                grid.filteringLogic, this.esf.expressionsList);
557
        } else {
558
            this.esf.grid.filteringService.clearFilter(this.esf.column.field);
559
        }
2✔
560

561
        this.esf.closeDropdown();
562
    }
563

564
    /**
565
     * @hidden @internal
566
     */
567
    public isHierarchical() {
568
        return this.esf.isHierarchical;
569
    }
570

2✔
571
    /**
572
     * @hidden @internal
573
     */
574
    public isTreeEmpty() {
575
        return this.esf.isHierarchical && this.displayedListData.length === 0;
576
    }
577

578
    private hierarchicalSelectMatches(data: FilterListItem[], searchVal: string) {
579
        data.forEach(element => {
580
            element.indeterminate = false;
581
            element.isSelected = false;
582
            const node = this.tree.nodes.filter(n => (n.data as FilterListItem).label === element.label)[0];
583
            if (node) {
584
                node.expanded = false;
585
            }
586

587
            if (element.label.toString().toLowerCase().indexOf(searchVal) > -1) {
588
                element.isSelected = true;
589
                this.hierarchicalSelectAllChildren(element);
590
                this._hierarchicalSelectedItems.push(element);
591
            } else if (element.children.length > 0) {
592
                element.children = this.hierarchicalSelectMatches(element.children, searchVal);
593
                if (element.children.length > 0) {
594
                    element.isSelected = true;
595
                    if (node) {
596
                        node.expanded = true;
597
                    }
598
                }
599
            }
600
        });
601

602
        return data.filter(element => element.isSelected === true);
603
    }
604

605
    private hierarchicalSelectAllChildren(element: FilterListItem) {
606
        element.children.forEach(child => {
607
            child.indeterminate = false;
608
            child.isSelected = true;
609
            this._hierarchicalSelectedItems.push(child);
610
            if (child.children) {
611
                this.hierarchicalSelectAllChildren(child);
612
            }
613
        })
614
    }
615

616
    private expandAllParentNodes(node: any) {
617
        if (node.parentNode) {
618
            node.parentNode.expanded = true;
619
            this.expandAllParentNodes(node.parentNode);
620
        }
621
    }
622

623
    private addFilteredToSelectedItems(records: FilterListItem[]) {
624
        records.forEach(record => {
625
            if (record.children) {
626
                this.addFilteredToSelectedItems(record.children);
627
            }
628

629
            if (record.isFiltered && this._hierarchicalSelectedItems.indexOf(record) < 0) {
630
                this._hierarchicalSelectedItems.push(record);
631
            }
632
        })
633
    }
634

635
    private createCondition(conditionName: string) {
636
        switch (this.esf.column.dataType) {
637
            case GridColumnDataType.Boolean:
638
                return IgxBooleanFilteringOperand.instance().condition(conditionName);
639
            case GridColumnDataType.Number:
640
            case GridColumnDataType.Currency:
641
            case GridColumnDataType.Percent:
642
                return IgxNumberFilteringOperand.instance().condition(conditionName);
643
            case GridColumnDataType.Date:
644
                return IgxDateFilteringOperand.instance().condition(conditionName);
645
            case GridColumnDataType.Time:
646
                return IgxTimeFilteringOperand.instance().condition(conditionName);
647
            case GridColumnDataType.DateTime:
648
                return IgxDateTimeFilteringOperand.instance().condition(conditionName);
649
            default:
650
                return IgxStringFilteringOperand.instance().condition(conditionName);
651
        }
652
    }
653

654
    /**
655
     * @hidden @internal
656
     */
657
    private rejectNonNumericalEntries(): void {
658
        const regExp = /[^0-9\.,eE\-]/g;
659
        if (this.searchValue && regExp.test(this.searchValue)) {
660
            this.searchInput.value = this.searchValue.replace(regExp, '');
661
            this.searchValue = this.searchInput.value;
662
        }
663
    }
664
}
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