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

IgniteUI / igniteui-angular / 26023601418

18 May 2026 08:57AM UTC coverage: 4.854% (-85.3%) from 90.174%
26023601418

Pull #17281

github

web-flow
Merge e7ce7a18e into 5a85df190
Pull Request #17281: feat: Added virtual scroll component and sample implementation

400 of 17347 branches covered (2.31%)

Branch coverage included in aggregate %.

63 of 222 new or added lines in 4 files covered. (28.38%)

27932 existing lines in 341 files now uncovered.

2022 of 32547 relevant lines covered (6.21%)

0.72 hits per line

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

0.63
/projects/igniteui-angular/grids/core/src/headers/grid-header.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    ChangeDetectorRef,
4
    Component,
5
    DoCheck,
6
    ElementRef,
7
    HostBinding,
8
    HostListener,
9
    inject,
10
    Input,
11
    OnDestroy,
12
    TemplateRef,
13
    ViewChild
14
} from '@angular/core';
15
import { IgxColumnResizingService } from '../resizing/resizing.service';
16
import { Subject } from 'rxjs';
17
import { GridType, IGX_GRID_BASE } from '../common/grid.interface';
18
import { GridSelectionMode } from '../common/enums';
19
import { SortingIndexPipe } from './pipes';
20
import { NgTemplateOutlet, NgClass } from '@angular/common';
21
import { IgxIconComponent } from 'igniteui-angular/icon';
22
import { ColumnType, ExpressionsTreeUtil, GridColumnDataType, SortingDirection } from 'igniteui-angular/core';
23

24
/**
25
 * @hidden
26
 */
27
@Component({
28
    changeDetection: ChangeDetectionStrategy.OnPush,
29
    selector: 'igx-grid-header',
30
    templateUrl: 'grid-header.component.html',
31
    imports: [IgxIconComponent, NgTemplateOutlet, NgClass, SortingIndexPipe]
32
})
33
export class IgxGridHeaderComponent implements DoCheck, OnDestroy {
3✔
UNCOV
34
    public grid = inject<GridType>(IGX_GRID_BASE);
×
UNCOV
35
    public colResizingService = inject(IgxColumnResizingService);
×
UNCOV
36
    public cdr = inject(ChangeDetectorRef);
×
UNCOV
37
    private ref = inject<ElementRef<HTMLElement>>(ElementRef);
×
38

39
    @Input()
40
    public column: ColumnType;
41

42
    /**
43
     * @hidden
44
     */
45
    @ViewChild('defaultESFHeaderIconTemplate', { read: TemplateRef, static: true })
46
    protected defaultESFHeaderIconTemplate: TemplateRef<any>;
47

48
    /**
49
     * @hidden
50
     */
51
    @ViewChild('defaultSortHeaderIconTemplate', { read: TemplateRef, static: true })
52
    protected defaultSortHeaderIconTemplate;
53

54
    /**
55
     * @hidden
56
     */
57
    @ViewChild('sortIconContainer', { read: ElementRef })
58
    protected sortIconContainer: ElementRef;
59

60
    @HostBinding('class.igx-grid-th--pinned')
61
    public get pinnedCss() {
UNCOV
62
        return this.isPinned;
×
63
    }
64

65
    @HostBinding('class.igx-grid-th--pinned-last')
66
    public get pinnedLastCss() {
UNCOV
67
        return this.isLastPinned;
×
68
    }
69

70
    @HostBinding('class.igx-grid-th--pinned-first')
71
    public get pinnedFirstCSS() {
UNCOV
72
        return this.isFirstPinned;
×
73
    }
74

75
    /**
76
     * Gets whether the header group is stored in the last column in the pinned area.
77
     */
78
    public get isLastPinned(): boolean {
UNCOV
79
        return !this.grid.hasColumnLayouts ? this.column.isLastPinned : false;
×
80
    }
81

82
    /**
83
     * Gets whether the header group is stored in the first column of the right pinned area.
84
     */
85
    public get isFirstPinned(): boolean {
UNCOV
86
        return !this.grid.hasColumnLayouts ? this.column.isFirstPinned : false;
×
87
    }
88

89
    /**
90
     * Gets whether the header group is stored in a pinned column.
91
     *
92
     * @memberof IgxGridHeaderGroupComponent
93
     */
94
    public get isPinned(): boolean {
UNCOV
95
        return this.column.pinned;
×
96
    }
97
    /**
98
     * @hidden
99
     */
100
    @Input()
101
    @HostBinding('attr.id')
102
    public id: string;
103

104
    /**
105
     * Returns the `aria-selected` of the header.
106
     */
107
    @HostBinding('attr.aria-selected')
108
    public get ariaSelected(): boolean {
UNCOV
109
        return this.column.selected;
×
110
    }
111

112
    /**
113
     * Returns the `aria-sort` of the header.
114
     */
115
    @HostBinding('attr.aria-sort')
116
    public get ariaSort() {
UNCOV
117
        return this.sortDirection === SortingDirection.Asc ? 'ascending'
×
118
            : this.sortDirection === SortingDirection.Desc ? 'descending' : null;
×
119
    }
120

121
    /**
122
     * @hidden
123
     */
124
    @HostBinding('attr.aria-colindex')
125
    public get ariaColIndx() {
UNCOV
126
        return this.column.index + 1;
×
127
    }
128

129
    /**
130
     * @hidden
131
     */
132
    @HostBinding('attr.aria-rowindex')
133
    public get ariaRowIndx() {
UNCOV
134
        return 1;
×
135
    }
136

137
    @HostBinding('class.igx-grid-th')
138
    public get columnGroupStyle() {
UNCOV
139
        return !this.column.columnGroup;
×
140
    }
141

142
    @HostBinding('class.asc')
143
    public get sortAscendingStyle() {
UNCOV
144
        return this.sortDirection === SortingDirection.Asc;
×
145
    }
146

147
    @HostBinding('class.desc')
148
    public get sortDescendingStyle() {
UNCOV
149
        return this.sortDirection === SortingDirection.Desc;
×
150
    }
151

152
    @HostBinding('class.igx-grid-th--number')
153
    public get numberStyle() {
UNCOV
154
        return this.column.dataType === GridColumnDataType.Number
×
155
            || this.column.dataType === GridColumnDataType.Currency
156
            || this.column.dataType === GridColumnDataType.Percent;
157
    }
158

159
    @HostBinding('class.igx-grid-th--sortable')
160
    public get sortableStyle() {
UNCOV
161
        return this.column.sortable;
×
162
    }
163

164
    @HostBinding('class.igx-grid-th--selectable')
165
    public get selectableStyle() {
UNCOV
166
        return this.selectable;
×
167
    }
168

169
    @HostBinding('class.igx-grid-th--filtrable')
170
    public get filterableStyle() {
UNCOV
171
        return this.column.filterable && this.grid.filteringService.isFilterRowVisible;
×
172
    }
173

174
    @HostBinding('class.igx-grid-th--sorted')
175
    public get sortedStyle() {
UNCOV
176
        return this.sorted;
×
177
    }
178

179
    @HostBinding('class.igx-grid-th--selected')
180
    public get selectedStyle() {
UNCOV
181
        return this.selected;
×
182
    }
183

184
    /**
185
     * @hidden
186
     */
187
    public get esfIconTemplate() {
UNCOV
188
        return this.grid.excelStyleHeaderIconTemplate || this.defaultESFHeaderIconTemplate;
×
189
    }
190

191
    /**
192
     * @hidden
193
     */
194
    public get sortIconTemplate() {
UNCOV
195
        if (this.sortDirection === SortingDirection.None && this.grid.sortHeaderIconTemplate) {
×
UNCOV
196
            return this.grid.sortHeaderIconTemplate;
×
UNCOV
197
        } else if (this.sortDirection === SortingDirection.Asc && this.grid.sortAscendingHeaderIconTemplate) {
×
UNCOV
198
            return this.grid.sortAscendingHeaderIconTemplate;
×
UNCOV
199
        } else if (this.sortDirection === SortingDirection.Desc && this.grid.sortDescendingHeaderIconTemplate) {
×
UNCOV
200
            return this.grid.sortDescendingHeaderIconTemplate;
×
201
        } else {
UNCOV
202
            return this.defaultSortHeaderIconTemplate;
×
203
        }
204
    }
205
    /**
206
     * @hidden
207
     */
208
    public get disabled() {
UNCOV
209
        const groupArea = this.grid.groupArea || this.grid.treeGroupArea;
×
UNCOV
210
        if (groupArea?.expressions && groupArea.expressions.length && groupArea.expressions.map(g => g.fieldName).includes(this.column.field)) {
×
UNCOV
211
            return true;
×
212
        }
UNCOV
213
        return false;
×
214
    }
215

216
    public get sorted() {
UNCOV
217
        return this.sortDirection !== SortingDirection.None;
×
218
    }
219

220
    public get filterIconClassName() {
UNCOV
221
        return this.column.filteringExpressionsTree || this.isAdvancedFilterApplied() ? 'igx-excel-filter__icon--filtered' : 'igx-excel-filter__icon';
×
222
    }
223

224
    public get selectable() {
UNCOV
225
        return this.grid.columnSelection !== GridSelectionMode.none &&
×
226
            this.column.applySelectableClass &&
227
            !this.column.selected &&
228
            !this.grid.filteringService.isFilterRowVisible;
229
    }
230

231
    public get selected() {
UNCOV
232
        return this.column.selected
×
233
            && (!this.grid.filteringService.isFilterRowVisible || this.grid.filteringService.filteredColumn !== this.column);
234
    }
235

236
    public get title() {
UNCOV
237
        return this.column.title || this.column.header || this.column.field;
×
238
    }
239

240
    public get nativeElement() {
UNCOV
241
        return this.ref.nativeElement;
×
242
    }
243

UNCOV
244
    public sortDirection = SortingDirection.None;
×
UNCOV
245
    protected _destroy$ = new Subject<boolean>();
×
246

247
    @HostListener('click', ['$event'])
248
    public onClick(event: MouseEvent) {
UNCOV
249
        if (!this.colResizingService.isColumnResizing) {
×
250

UNCOV
251
            if (this.grid.filteringService.isFilterRowVisible) {
×
UNCOV
252
                if (this.column.filterCellTemplate) {
×
UNCOV
253
                    this.grid.filteringRow.close();
×
UNCOV
254
                    return;
×
255
                }
256

UNCOV
257
                if (this.column.filterable && !this.column.columnGroup &&
×
258
                    !this.grid.filteringService.isFilterComplex(this.column.field)) {
UNCOV
259
                    this.grid.filteringService.filteredColumn = this.column;
×
260
                }
UNCOV
261
            } else if (this.grid.columnSelection !== GridSelectionMode.none && this.column.selectable) {
×
UNCOV
262
                const clearSelection = this.grid.columnSelection === GridSelectionMode.single || !event.ctrlKey;
×
UNCOV
263
                const rangeSelection = this.grid.columnSelection === GridSelectionMode.multiple && event.shiftKey;
×
264

UNCOV
265
                if (!this.column.selected || (this.grid.selectionService.getSelectedColumns().length > 1 && clearSelection)) {
×
UNCOV
266
                    this.grid.selectionService.selectColumn(this.column.field, clearSelection, rangeSelection, event);
×
267
                } else {
UNCOV
268
                    this.grid.selectionService.deselectColumn(this.column.field, event);
×
269
                }
270
            }
271
        }
UNCOV
272
        this.grid.theadRow.nativeElement.focus();
×
273
    }
274

275
    /**
276
     * @hidden
277
     */
278
    @HostListener('pointerenter')
279
    public onPinterEnter() {
UNCOV
280
        this.column.applySelectableClass = true;
×
281
    }
282

283
    /**
284
     * @hidden
285
     */
286
    @HostListener('pointerleave')
287
    public onPointerLeave() {
UNCOV
288
        this.column.applySelectableClass = false;
×
289
    }
290

291
    /**
292
     * @hidden @internal
293
     */
294
    public ngDoCheck() {
UNCOV
295
        this.getSortDirection();
×
UNCOV
296
        this.cdr.markForCheck();
×
297
    }
298

299
    /**
300
     * @hidden @internal
301
     */
302
    public ngOnDestroy(): void {
UNCOV
303
        this._destroy$.next(true);
×
UNCOV
304
        this._destroy$.complete();
×
305
    }
306

307
    /**
308
     * @hidden @internal
309
     */
310
    public onPointerDownIndicator(event) {
311
        // Stop propagation of pointer events to now allow column dragging using the header indicators.
UNCOV
312
        event.stopPropagation();
×
313
    }
314

315
    /**
316
     * @hidden @internal
317
     */
318
    public onFilteringIconClick(event) {
UNCOV
319
        event.stopPropagation();
×
UNCOV
320
        this.grid.filteringService.toggleFilterDropdown(this.nativeElement, this.column);
×
321
    }
322

323
    /**
324
     * @hidden @internal
325
     */
326
    public onSortingIconClick(event) {
UNCOV
327
        event.stopPropagation();
×
UNCOV
328
        this.triggerSort();
×
329
    }
330

331
    protected getSortDirection() {
UNCOV
332
        const expr = this.grid.sortingExpressions.find((x) => x.fieldName === this.column.field);
×
UNCOV
333
        this.sortDirection = expr ? expr.dir : SortingDirection.None;
×
334
    }
335

336
    protected isAdvancedFilterApplied() {
UNCOV
337
        if (!this.grid.advancedFilteringExpressionsTree) {
×
UNCOV
338
            return false;
×
339
        }
340
        return !!ExpressionsTreeUtil.find(this.grid.advancedFilteringExpressionsTree, this.column.field);
×
341
    }
342

343
    private triggerSort() {
UNCOV
344
        const groupingExpr = this.grid.groupingExpressions ?
×
345
            this.grid.groupingExpressions.find((expr) => expr.fieldName === this.column.field) :
×
346
            this.grid.groupArea?.expressions ? this.grid.groupArea?.expressions.find((expr) => expr.fieldName === this.column.field) : null;
×
UNCOV
347
        const sortDir = groupingExpr ?
×
348
            this.sortDirection + 1 > SortingDirection.Desc ? SortingDirection.Asc : SortingDirection.Desc
×
349
            : this.sortDirection + 1 > SortingDirection.Desc ? SortingDirection.None : this.sortDirection + 1;
×
UNCOV
350
        this.sortDirection = sortDir;
×
UNCOV
351
        this.grid.sort({
×
352
            fieldName: this.column.field, dir: this.sortDirection, ignoreCase: this.column.sortingIgnoreCase,
353
            strategy: this.column.sortStrategy
354
        });
355
    }
356
}
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

© 2026 Coveralls, Inc