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

IgniteUI / igniteui-angular / 6693326001

30 Oct 2023 01:14PM UTC coverage: 92.087% (-0.01%) from 92.101%
6693326001

push

github

web-flow
Merge pull request #13607 from IgniteUI/dmdimitrov/fix-esf-conditional-filter-dropdown-master

fix(esf): conditional filter drop down items focus - master

15273 of 17966 branches covered (0.0%)

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

26418 of 28688 relevant lines covered (92.09%)

30210.23 hits per line

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

96.13
/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts
1
import {
2
    Component, ChangeDetectionStrategy, Input, Output, EventEmitter, ContentChild, ViewChildren,
3
    QueryList, ViewChild, TemplateRef, DoCheck, AfterContentInit, HostBinding,
4
    OnInit, AfterViewInit, ContentChildren, CUSTOM_ELEMENTS_SCHEMA
5
} from '@angular/core';
6
import { NgIf, NgTemplateOutlet, NgClass, NgFor, NgStyle } from '@angular/common';
7

8
import { IgxGridBaseDirective } from '../grid-base.directive';
9
import { IgxGridNavigationService } from '../grid-navigation.service';
10
import { IgxGridAPIService } from './grid-api.service';
11
import { cloneArray, IBaseEventArgs } from '../../core/utils';
12
import { IGroupByRecord } from '../../data-operations/groupby-record.interface';
13
import { IgxGroupByRowTemplateDirective, IgxGridDetailTemplateDirective } from '../grid.directives';
14
import { IgxGridGroupByRowComponent } from './groupby-row.component';
15
import { IGroupByExpandState } from '../../data-operations/groupby-expand-state.interface';
16
import { IForOfState, IgxGridForOfDirective } from '../../directives/for-of/for_of.directive';
17
import { IgxColumnComponent } from '../columns/column.component';
18
import { take, takeUntil } from 'rxjs/operators';
19
import { IgxFilteringService } from '../filtering/grid-filtering.service';
20
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
21
import { IgxColumnResizingService } from '../resizing/resizing.service';
22
import { IgxGridSummaryService } from '../summaries/grid-summary.service';
23
import { IgxGridSelectionService } from '../selection/selection.service';
24
import { IgxForOfSyncService, IgxForOfScrollSyncService } from '../../directives/for-of/for_of.sync.service';
25
import { IgxGridMRLNavigationService } from '../grid-mrl-navigation.service';
26
import { FilterMode, GridInstanceType } from '../common/enums';
27
import { CellType, GridType, IgxGridMasterDetailContext, IgxGroupByRowSelectorTemplateContext, IgxGroupByRowTemplateContext, IGX_GRID_BASE, IGX_GRID_SERVICE_BASE, RowType } from '../common/grid.interface';
28
import { IgxGroupByRowSelectorDirective } from '../selection/row-selectors';
29
import { IgxGridCRUDService } from '../common/crud.service';
30
import { IgxGridRow, IgxGroupByRow, IgxSummaryRow } from '../grid-public-row';
31
import { IgxGridCell } from '../grid-public-cell';
32
import { ISortingExpression } from '../../data-operations/sorting-strategy';
33
import { IGridGroupingStrategy } from '../common/strategy';
34
import { IgxGridValidationService } from './grid-validation.service';
35
import { IgxGridDetailsPipe } from './grid.details.pipe';
36
import { IgxGridSummaryPipe } from './grid.summary.pipe';
37
import { IgxGridGroupingPipe, IgxGridPagingPipe, IgxGridSortingPipe, IgxGridFilteringPipe } from './grid.pipes';
38
import { IgxSummaryDataPipe } from '../summaries/grid-root-summary.pipe';
39
import { IgxGridTransactionPipe, IgxHasVisibleColumnsPipe, IgxGridRowPinningPipe, IgxGridAddRowPipe, IgxGridRowClassesPipe, IgxGridRowStylesPipe } from '../common/pipes';
40
import { IgxGridColumnResizerComponent } from '../resizing/resizer.component';
41
import { IgxRowEditTabStopDirective } from '../grid.rowEdit.directive';
42
import { IgxIconComponent } from '../../icon/icon.component';
43
import { IgxRippleDirective } from '../../directives/ripple/ripple.directive';
44
import { IgxButtonDirective } from '../../directives/button/button.directive';
45
import { IgxSnackbarComponent } from '../../snackbar/snackbar.component';
46
import { IgxCircularProgressBarComponent } from '../../progressbar/progressbar.component';
47
import { IgxOverlayOutletDirective, IgxToggleDirective } from '../../directives/toggle/toggle.directive';
48
import { IgxSummaryRowComponent } from '../summaries/summary-row.component';
49
import { IgxGridRowComponent } from './grid-row.component';
2✔
50
import { IgxTemplateOutletDirective } from '../../directives/template-outlet/template_outlet.directive';
51
import { IgxColumnMovingDropDirective } from '../moving/moving.drop.directive';
52
import { IgxGridDragSelectDirective } from '../selection/drag-select.directive';
53
import { IgxGridBodyDirective } from '../grid.common';
54
import { IgxGridHeaderRowComponent } from '../headers/grid-header-row.component';
55
import { IgxGridGroupByAreaComponent } from '../grouping/grid-group-by-area.component';
56
import { Observable, Subject } from 'rxjs';
57

58
let NEXT_ID = 0;
59

60
export interface IGroupingDoneEventArgs extends IBaseEventArgs {
61
    expressions: Array<ISortingExpression> | ISortingExpression;
62
    groupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
63
    ungroupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
64
}
65

66
/**
67
 * Grid provides a way to present and manipulate tabular data.
68
 *
69
 * @igxModule IgxGridModule
2✔
70
 * @igxGroup Grids & Lists
71
 * @igxKeywords grid, table
1,957✔
72
 * @igxTheme igx-grid-theme
1,957✔
73
 * @remarks
1,957✔
74
 * The Ignite UI Grid is used for presenting and manipulating tabular data in the simplest way possible.  Once data
1,957✔
75
 * has been bound, it can be manipulated through filtering, sorting & editing operations.
1,957✔
76
 * @example
1,957✔
77
 * ```html
1,957✔
78
 * <igx-grid [data]="employeeData" [autoGenerate]="false">
1,957✔
79
 *   <igx-column field="first" header="First Name"></igx-column>
1,957✔
80
 *   <igx-column field="last" header="Last Name"></igx-column>
81
 *   <igx-column field="role" header="Role"></igx-column>
82
 * </igx-grid>
83
 * ```
1,957✔
84
 */
85
@Component({
86
    changeDetection: ChangeDetectionStrategy.OnPush,
87
    preserveWhitespaces: false,
1,957✔
88
    providers: [
1,957✔
89
        IgxGridCRUDService,
1,957✔
90
        IgxGridNavigationService,
1,957✔
91
        IgxGridSummaryService,
1,957✔
92
        IgxGridSelectionService,
93
        IgxGridValidationService,
94
        { provide: IGX_GRID_SERVICE_BASE, useClass: IgxGridAPIService },
95
        { provide: IGX_GRID_BASE, useExisting: IgxGridComponent },
1,957✔
96
        IgxFilteringService,
97
        IgxColumnResizingService,
98
        IgxForOfSyncService,
99
        IgxForOfScrollSyncService
1,957✔
100
    ],
101
    selector: 'igx-grid',
102
    templateUrl: './grid.component.html',
221,009✔
103
    standalone: true,
104
    imports: [
105
        NgIf,
106
        NgClass,
107
        NgFor,
108
        NgStyle,
109
        NgTemplateOutlet,
110
        IgxGridGroupByAreaComponent,
111
        IgxGridHeaderRowComponent,
112
        IgxGridBodyDirective,
113
        IgxGridDragSelectDirective,
114
        IgxColumnMovingDropDirective,
115
        IgxGridForOfDirective,
116
        IgxTemplateOutletDirective,
117
        IgxGridRowComponent,
118
        IgxGridGroupByRowComponent,
119
        IgxSummaryRowComponent,
120
        IgxOverlayOutletDirective,
121
        IgxToggleDirective,
122
        IgxCircularProgressBarComponent,
123
        IgxSnackbarComponent,
1✔
124
        IgxButtonDirective,
125
        IgxRippleDirective,
126
        IgxIconComponent,
127
        IgxRowEditTabStopDirective,
128
        IgxGridColumnResizerComponent,
129
        IgxGridTransactionPipe,
130
        IgxHasVisibleColumnsPipe,
131
        IgxGridRowPinningPipe,
132
        IgxGridAddRowPipe,
133
        IgxGridRowClassesPipe,
134
        IgxGridRowStylesPipe,
20,258✔
135
        IgxSummaryDataPipe,
136
        IgxGridGroupingPipe,
137
        IgxGridPagingPipe,
99,337✔
138
        IgxGridSortingPipe,
139
        IgxGridFilteringPipe,
140
        IgxGridSummaryPipe,
2,034✔
141
        IgxGridDetailsPipe
2,034✔
142
    ],
2,034✔
143
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
2,034✔
144
})
91✔
145
export class IgxGridComponent extends IgxGridBaseDirective implements GridType, OnInit, DoCheck, AfterContentInit, AfterViewInit {
146
    /**
2,034✔
147
     * Emitted when a new chunk of data is loaded from virtualization.
18✔
148
     *
149
     * @example
2,034✔
150
     * ```typescript
2,034✔
151
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataPreLoad)='handleDataPreloadEvent()'></igx-grid>
891✔
152
     * ```
153
     */
154
    @Output()
2,034✔
155
    public dataPreLoad = new EventEmitter<IForOfState>();
1✔
156

157
    /**
158
     * @hidden
159
     */
4✔
160
    @Output()
161
    public groupingExpressionsChange = new EventEmitter<IGroupingExpression[]>();
162

1,274✔
163
    /**
164
     * @hidden @internal
165
     */
12,137✔
166
    @Output()
167
    public groupingExpansionStateChange = new EventEmitter<IGroupByExpandState[]>();
168

1,334,403✔
169
    /**
170
     * Emitted when columns are grouped/ungrouped.
171
     *
599✔
172
     * @remarks
114✔
173
     * The `groupingDone` event would be raised only once if several columns get grouped at once by calling
174
     * the `groupBy()` or `clearGrouping()` API methods and passing an array as an argument.
485✔
175
     * The event arguments provide the `expressions`, `groupedColumns` and `ungroupedColumns` properties, which contain
1✔
176
     * the `ISortingExpression` and the `IgxColumnComponent` related to the grouping/ungrouping operation.
177
     * Please note that `groupedColumns` and `ungroupedColumns` show only the **newly** changed columns (affected by the **last**
484✔
178
     * grouping/ungrouping operation), not all columns which are currently grouped/ungrouped.
484✔
179
     * columns.
484✔
180
     * @example
484✔
181
     * ```html
484✔
182
     * <igx-grid #grid [data]="localData" (groupingDone)="groupingDone($event)" [autoGenerate]="true"></igx-grid>
183
     * ```
389✔
184
     */
389✔
185
    @Output()
186
    public groupingDone = new EventEmitter<IGroupingDoneEventArgs>();
484✔
187

262✔
188
    /**
262✔
189
     * Gets/Sets whether created groups are rendered expanded or collapsed.
314✔
190
     *
262✔
191
     * @remarks
263✔
192
     * The default rendered state is expanded.
193
     * @example
262✔
194
     * ```html
262✔
195
     * <igx-grid #grid [data]="Data" [groupsExpanded]="false" [autoGenerate]="true"></igx-grid>
26✔
196
     * ```
197
     */
262✔
198
    @Input()
262✔
199
    public groupsExpanded = true;
200

201
    /**
202
     * Gets/Sets the template that will be rendered as a GroupBy drop area.
203
     *
262✔
204
     * @remarks
257✔
205
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
206
     * @example
207
     * ```html
208
     * <igx-grid [dropAreaTemplate]="dropAreaRef">
209
     * </igx-grid>
31,976✔
210
     * <ng-template #myDropArea>
211
     *      <span> Custom drop area! </span>
212
     * </ng-template>
80!
213
     * ```
80✔
214
     */
215
    @Input()
80✔
216
    public dropAreaTemplate: TemplateRef<void>;
80!
217

80✔
218
    /**
219
     * @hidden @internal
220
     */
221
    @ContentChild(IgxGridDetailTemplateDirective, { read: TemplateRef })
2,035✔
222
    public detailTemplateDirective: TemplateRef<IgxGridMasterDetailContext>;
223

224

34✔
225
    /**
15✔
226
     * Returns a reference to the master-detail template.
227
     * ```typescript
228
     * let detailTemplate = this.grid.detailTemplate;
19✔
229
     * ```
230
     *
34!
231
     * @memberof IgxColumnComponent
34✔
232
     */
233
    @Input('detailTemplate')
34✔
234
    public get detailTemplate(): TemplateRef<IgxGridMasterDetailContext> {
235
        return this._detailTemplate;
236
    }
20,235✔
237
    /**
238
     * Sets the master-detail template.
239
     * ```html
13✔
240
     * <ng-template #detailTemplate igxGridDetail let-dataItem>
241
     *    <div>
242
     *       <div><span class='categoryStyle'>City:</span> {{dataItem.City}}</div>
1✔
243
     *       <div><span class='categoryStyle'>Address:</span> {{dataItem.Address}}</div>
1✔
244
     *    </div>
245
     * </ng-template>
246
     * ```
2,993✔
247
     * ```typescript
248
     * @ViewChild("'detailTemplate'", {read: TemplateRef })
249
     * public detailTemplate: TemplateRef<any>;
250
     * this.grid.detailTemplate = this.detailTemplate;
251
     * ```
252
     *
142✔
253
     * @memberof IgxColumnComponent
142!
254
     */
×
255
    public set detailTemplate(template: TemplateRef<IgxGridMasterDetailContext>) {
256
        this._detailTemplate = template;
600✔
257
    }
574✔
258

142✔
259
    /**
142✔
260
     * @hidden @internal
261
     */
262
    @HostBinding('attr.role')
373✔
263
    public role = 'grid';
264

265
    /**
266
     * Gets/Sets the value of the `id` attribute.
267
     *
268
     * @remarks
269
     * If not provided it will be automatically generated.
270
     * @example
271
     * ```html
272
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [autoGenerate]="true"></igx-grid>
273
     * ```
274
     */
275
    @HostBinding('attr.id')
276
    @Input()
277
    public id = `igx-grid-${NEXT_ID++}`;
278

1✔
279
    /**
280
     * @hidden @internal
281
     */
282
    @ViewChild('record_template', { read: TemplateRef, static: true })
283
    protected recordTemplate: TemplateRef<any>;
284

1,519✔
285
    @ViewChild('detail_template_container', { read: TemplateRef, static: true })
286
    protected detailTemplateContainer: TemplateRef<any>;
287

288
    @ViewChild('group_template', { read: TemplateRef, static: true })
289
    protected defaultGroupTemplate: TemplateRef<any>;
290

291
    @ViewChild('summary_template', { read: TemplateRef, static: true })
292
    protected summaryTemplate: TemplateRef<any>;
293

2✔
294
    /**
295
     * @hidden @internal
296
     */
297
    @ContentChild(IgxGroupByRowTemplateDirective, { read: IgxGroupByRowTemplateDirective })
298
    protected groupTemplate: IgxGroupByRowTemplateDirective;
299

219,490✔
300
    /**
301
     * @hidden
302
     * @internal
303
     */
304
    @ContentChildren(IgxGroupByRowSelectorDirective, { read: TemplateRef, descendants: false })
305
    protected groupByRowSelectorsTemplates: QueryList<TemplateRef<IgxGroupByRowSelectorTemplateContext>>;
107,432✔
306

7,033✔
307
    @ViewChildren(IgxGridGroupByRowComponent, { read: IgxGridGroupByRowComponent })
308
    private _groupsRowList: QueryList<IgxGridGroupByRowComponent>;
100,399✔
309

1,328✔
310
    private _groupsRecords: IGroupByRecord[] = [];
311
    /**
99,071✔
312
     * Gets the hierarchical representation of the group by records.
1,519✔
313
     *
314
     * @example
315
     * ```typescript
97,552✔
316
     * let groupRecords = this.grid.groupsRecords;
317
     * ```
318
     */
319
    public get groupsRecords(): IGroupByRecord[] {
320
        return this._groupsRecords;
321
    }
322

118,610✔
323
    /**
324
     * @hidden @internal
325
     * Includes children of collapsed group rows.
326
     */
327
    public groupingResult: any[];
328

1,519!
329
    /**
330
     * @hidden @internal
331
     */
2,071✔
332
    public groupingMetadata: any[];
333

334
    /**
1✔
335
     * @hidden @internal
1✔
336
     * Does not include children of collapsed group rows.
337
     */
338
    public groupingFlatResult: any[];
339
    /**
340
     * @hidden
341
     */
342
    protected _groupingExpressions: IGroupingExpression[] = [];
343
    /**
344
     * @hidden
345
     */
346
    protected _groupingExpandState: IGroupByExpandState[] = [];
347
    /**
348
     * @hidden
349
     */
350
    protected _groupRowTemplate: TemplateRef<IgxGroupByRowTemplateContext>;
351

352
    /**
353
     * @hidden
354
     */
225✔
355
    protected _groupStrategy: IGridGroupingStrategy;
1✔
356
    /**
357
     * @hidden
224✔
358
     */
224✔
359
    protected groupingDiffer;
19✔
360
    private _data?: any[] | null;
361
    private _hideGroupedColumns = false;
362
    private _dropAreaMessage = null;
205✔
363
    private _showGroupArea = true;
364

223✔
365
    private _groupByRowSelectorTemplate: TemplateRef<IgxGroupByRowSelectorTemplateContext>;
366
    private _detailTemplate;
367

368

369
    /**
370
     * Gets/Sets the array of data that populates the `IgxGridComponent`.
371
     *
372
     * @example
373
     * ```html
374
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
375
     * ```
376
     */
377
    @Input()
378
    public get data(): any[] | null {
379
        return this._data;
380
    }
381

18✔
382
    public set data(value: any[] | null) {
18✔
383
        const dataLoaded = (!this._data || this._data.length === 0) && value && value.length > 0;
18✔
384
        this._data = value || [];
18✔
385
        this.summaryService.clearSummaryCache();
386
        if (!this._init) {
387
            this.validation.updateAll(this._data);
388
        }
389

390
        if (this.shouldGenerate) {
391
            this.setupColumns();
392
        }
393
        this.cdr.markForCheck();
394
        if (this.isPercentHeight) {
395
            this.notifyChanges(true);
396
        }
397
        // check if any columns have width auto and if so recalculate their auto-size on data loaded.
10,934✔
398
        if (dataLoaded && this._columns.some(x => (x as any)._width === 'auto')) {
10,934✔
399
            this.recalculateAutoSizes();
400
        }
401
    }
402

403
    /**
404
     * Gets/Sets the total number of records in the data source.
405
     *
406
     * @remarks
407
     * This property is required for remote grid virtualization to function when it is bound to remote data.
408
     * @example
409
     * ```typescript
410
     * const itemCount = this.grid1.totalItemCount;
411
     * this.grid1.totalItemCount = 55;
51✔
412
     * ```
51✔
413
     */
414
    @Input()
415
    public set totalItemCount(count) {
416
        this.verticalScrollContainer.totalItemCount = count;
417
    }
418

419
    public get totalItemCount() {
420
        return this.verticalScrollContainer.totalItemCount;
421
    }
422

423
    private get _gridAPI(): IgxGridAPIService {
424
        return this.gridAPI as IgxGridAPIService;
425
    }
4✔
426

4✔
427
    private childDetailTemplates: Map<any, any> = new Map();
428

429
    /**
430
     * @hidden @internal
431
     */
432
    public groupingPerformedSubject = new Subject<void>();
433

434
    /**
435
     * @hidden @internal
436
     */
437
    public groupingPerformed$: Observable<void> = this.groupingPerformedSubject.asObservable();
438

439
    /**
2✔
440
     * Gets/Sets the group by state.
2✔
441
     *
442
     * @example
443
     * ```typescript
444
     * let groupByState = this.grid.groupingExpressions;
445
     * this.grid.groupingExpressions = [...];
446
     * ```
447
     * @remarks
448
     * Supports two-way data binding.
449
     * @example
450
     * ```html
451
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpressions)]="model.groupingExpressions"></igx-grid>
452
     * ```
453
     */
×
454
    @Input()
×
455
    public get groupingExpressions(): IGroupingExpression[] {
456
        return this._groupingExpressions;
457
    }
458

459
    public set groupingExpressions(value: IGroupingExpression[]) {
460
        if (this.groupingExpressions === value) {
461
            return;
275,049✔
462
        }
463
        if (value && value.length > 10) {
464
            throw Error('Maximum amount of grouped columns is 10.');
465
        }
466
        const oldExpressions: IGroupingExpression[] = this.groupingExpressions;
467
        const newExpressions: IGroupingExpression[] = value;
468
        this._groupingExpressions = cloneArray(value);
469
        this.groupingExpressionsChange.emit(this._groupingExpressions);
470
        if (this._gridAPI.grid) {
471
            /* grouping and sorting are working separate from each other */
472
            this._applyGrouping();
473
            this.notifyChanges();
11✔
474
        }
11✔
475
        if (!this._init && JSON.stringify(oldExpressions) !== JSON.stringify(newExpressions) && this._columns) {
11✔
476
            const groupedCols: IgxColumnComponent[] = [];
477
            const ungroupedCols: IgxColumnComponent[] = [];
478
            const groupedColsArr = newExpressions.filter((obj) => !oldExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
479
            groupedColsArr.forEach((elem) => {
128,307✔
480
                groupedCols.push(this.getColumnByName(elem.fieldName));
481
            }, this);
482
            const ungroupedColsArr = oldExpressions.filter((obj) => !newExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
20,233✔
483
            ungroupedColsArr.forEach((elem) => {
484
                ungroupedCols.push(this.getColumnByName(elem.fieldName));
485
            }, this);
×
486
            this.notifyChanges();
×
487
            const groupingDoneArgs: IGroupingDoneEventArgs = {
488
                expressions: newExpressions,
489
                groupedColumns: groupedCols,
490
                ungroupedColumns: ungroupedCols
491
            };
492
            this.groupingPerformed$.pipe(take(1)).subscribe(() => {
2!
493
                this.groupingDone.emit(groupingDoneArgs);
494
            });
495
        }
496
    }
497

498
    /**
108,016✔
499
     * Gets/Sets a list of expansion states for group rows.
1,519✔
500
     *
1,519✔
501
     * @remarks
1,519✔
502
     * Includes only states that differ from the default one (controlled through groupsExpanded and states that the user has changed.
1,318✔
503
     * Contains the expansion state (expanded: boolean) and the unique identifier for the group row (Array).
1,318✔
504
     * Supports two-way data binding.
1,318✔
505
     * @example
506
     * ```html
507
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpansionState)]="model.groupingExpansionState"></igx-grid>
508
     * ```
509
     */
510
    @Input()
511
    public get groupingExpansionState() {
512
        return this._groupingExpandState;
513
    }
514

515
    public set groupingExpansionState(value) {
516
        if (value !== this._groupingExpandState) {
517
            this.groupingExpansionStateChange.emit(value);
201✔
518
        }
519
        this._groupingExpandState = value;
520
        if (this.gridAPI.grid) {
521
            this.cdr.detectChanges();
522
        }
523
    }
524

525
    /**
526
     * Gets/Sets whether the grouped columns should be hidden.
527
     *
106,497✔
528
     * @remarks
106,497✔
529
     * The default value is "false"
530
     * @example
531
     * ```html
205,961✔
532
     * <igx-grid #grid [data]="localData" [hideGroupedColumns]="true" [autoGenerate]="true"></igx-grid>
533
     * ```
534
     */
535
    @Input()
536
    public get hideGroupedColumns() {
537
        return this._hideGroupedColumns;
538
    }
539

540
    public set hideGroupedColumns(value: boolean) {
541
        if (value) {
18,657✔
542
            this.groupingDiffer = this.differs.find(this.groupingExpressions).create();
201✔
543
        } else {
544
            this.groupingDiffer = null;
545
        }
546
        if (this._columns && this.groupingExpressions) {
547
            this._setGroupColsVisibility(value);
548
        }
549

64!
550
        this._hideGroupedColumns = value;
551
    }
64✔
552

64✔
553
    /**
64✔
554
     * Gets/Sets the grouping strategy of the grid.
555
     *
556
     * @remarks The default IgxGrouping extends from IgxSorting and a custom one can be used as a `sortStrategy` as well.
557
     *
558
     * @example
559
     * ```html
560
     *  <igx-grid #grid [data]="localData" [groupStrategy]="groupStrategy"></igx-grid>
1,757✔
561
     * ```
1,713✔
562
     */
563
    @Input()
564
    public get groupStrategy(): IGridGroupingStrategy {
44✔
565
        return this._groupStrategy;
566
    }
567

568
    public set groupStrategy(value: IGridGroupingStrategy) {
569
        this._groupStrategy = value;
570
    }
571

1,957✔
572
    /**
1,957✔
573
     * Gets/Sets the message displayed inside the GroupBy drop area where columns can be dragged on.
2✔
574
     *
575
     * @remarks
1,957✔
576
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
2✔
577
     * @example
578
     * ```html
1,957✔
579
     * <igx-grid dropAreaMessage="Drop here to group!">
60✔
580
     *      <igx-column [groupable]="true" field="ID"></igx-column>
581
     * </igx-grid>
1,957✔
582
     * ```
10✔
583
     */
584
    @Input()
1,957✔
585
    public set dropAreaMessage(value: string) {
586
        this._dropAreaMessage = value;
587
        this.notifyChanges();
588
    }
589

590
    public get dropAreaMessage(): string {
1,970✔
591
        return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message;
1,970✔
592
    }
2,753✔
593

2,753✔
594
    /**
8✔
595
     * @hidden @internal
8!
596
     */
8✔
597
    public get groupsRowList() {
8✔
598
        const res = new QueryList<any>();
599
        if (!this._groupsRowList) {
600
            return res;
601
        }
1,970✔
602
        const rList = this._groupsRowList.filter(item => item.element.nativeElement.parentElement !== null)
364✔
603
            .sort((item1, item2) => item1.index - item2.index);
134✔
604
        res.reset(rList);
605
        return res;
230✔
606
    }
13✔
607

15✔
608
    /**
13✔
609
     * Gets the group by row selector template.
7✔
610
     */
611
    @Input()
612
    public get groupByRowSelectorTemplate(): TemplateRef <IgxGroupByRowSelectorTemplateContext> {
613
        return this._groupByRowSelectorTemplate || this.groupByRowSelectorsTemplates?.first;
614
    }
615

616
    /**
617
     * Sets the group by row selector template.
618
     * ```html
1,957✔
619
     * <ng-template #template igxGroupByRowSelector let-groupByRowContext>
1,497,899✔
620
     * {{ groupByRowContext.selectedCount }} / {{ groupByRowContext.totalCount  }}
1,957✔
621
     * </ng-template>
257✔
622
     * ```
257✔
623
     * ```typescript
257✔
624
     * @ViewChild("'template'", {read: TemplateRef })
625
     * public template: TemplateRef<any>;
626
     * this.grid.groupByRowSelectorTemplate = this.template;
627
     * ```
628
     */
629
    public set groupByRowSelectorTemplate(template: TemplateRef<IgxGroupByRowSelectorTemplateContext>) {
630
        this._groupByRowSelectorTemplate = template;
8,562✔
631
    }
36✔
632

36✔
633
    /**
14✔
634
     * @hidden @internal
21✔
635
     */
21✔
636
    public getDetailsContext(rowData, index): IgxGridDetailTemplateDirective {
20✔
637
        return {
638
            $implicit: rowData,
639
            index
14✔
640
        };
×
641
    }
×
642

643
    /**
644
     * @hidden @internal
645
     */
8,562✔
646
    public detailsViewFocused(container, rowIndex) {
647
        this.navigation.setActiveNode({ row: rowIndex });
648
    }
649

650
    /**
651
     * @hidden @internal
129✔
652
     */
653
    public override get hasDetails() {
654
        return !!this.detailTemplate;
655
    }
656

657
    /**
658
     * @hidden @internal
659
     */
660
    public getRowTemplate(rowData) {
661
        if (this.isGroupByRecord(rowData)) {
334✔
662
            return this.defaultGroupTemplate;
180✔
663
        } else if (this.isSummaryRow(rowData)) {
15✔
664
            return this.summaryTemplate;
15✔
665
        } else if (this.hasDetails && this.isDetailRecord(rowData)) {
220✔
666
            return this.detailTemplateContainer;
97✔
667
        } else {
97✔
668
            return this.recordTemplate;
669
        }
123✔
670
    }
671

15✔
672
    /**
15✔
673
     * @hidden @internal
674
     */
675
    public override isDetailRecord(record) {
165✔
676
        return record && record.detailsData !== undefined;
677
    }
678

679
    /**
680
     * @hidden @internal
681
     */
682
    public isDetailActive(rowIndex) {
683
        return this.navigation.activeNode ? this.navigation.activeNode.row === rowIndex : false;
684
    }
685

686
    /**
687
     * Gets/Sets the template reference for the group row.
688
     *
689
     * @example
581!
690
     * ```
×
691
     * const groupRowTemplate = this.grid.groupRowTemplate;
692
     * this.grid.groupRowTemplate = myRowTemplate;
581!
693
     * ```
581✔
694
     */
695
    @Input()
696
    public get groupRowTemplate(): TemplateRef<IgxGroupByRowTemplateContext> {
×
697
        return this._groupRowTemplate;
×
698
    }
699

700
    public set groupRowTemplate(template: TemplateRef<IgxGroupByRowTemplateContext>) {
581✔
701
        this._groupRowTemplate = template;
2✔
702
        this.notifyChanges();
703
    }
581✔
704

705
    /** @hidden @internal */
706
    public trackChanges: (index, rec) => any;
707

708
    /**
709
     * Groups by a new `IgxColumnComponent` based on the provided expression, or modifies an existing one.
710
     *
711
     * @remarks
712
     * Also allows for multiple columns to be grouped at once if an array of `ISortingExpression` is passed.
713
     * The `groupingDone` event would get raised only **once** if this method gets called multiple times with the same arguments.
714
     * @example
715
     * ```typescript
716
     * this.grid.groupBy({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
717
     * this.grid.groupBy([
161✔
718
     *     { fieldName: name1, dir: SortingDirection.Asc, ignoreCase: false },
287✔
719
     *     { fieldName: name2, dir: SortingDirection.Desc, ignoreCase: true },
222✔
720
     *     { fieldName: name3, dir: SortingDirection.Desc, ignoreCase: false }
161✔
721
     * ]);
161✔
722
     * ```
10✔
723
     */
724
    public groupBy(expression: IGroupingExpression | Array<IGroupingExpression>): void {
151✔
725
        if (this.checkIfNoColumnField(expression)) {
726
            return;
727
        }
728
        this.crudService.endEdit(false);
729
        if (expression instanceof Array) {
730
            this._gridAPI.groupBy_multiple(expression);
78✔
731
        } else {
3,909!
732
            this._gridAPI.groupBy(expression);
3,909✔
733
        }
734
        this.notifyChanges(true);
735
    }
736

737
    /**
738
     * Clears grouping for particular column, array of columns or all columns.
739
     *
740
     * @remarks
741
     * Clears all grouping in the grid, if no parameter is passed.
3,909✔
742
     * If a parameter is provided, clears grouping for a particular column or an array of columns.
743
     * @example
744
     * ```typescript
745
     * this.grid.clearGrouping(); //clears all grouping
746
     * this.grid.clearGrouping("ID"); //ungroups a single column
747
     * this.grid.clearGrouping(["ID", "Column1", "Column2"]); //ungroups multiple columns
748
     * ```
749
     * @param name Name of column or array of column names to be ungrouped.
750
     */
751
    public clearGrouping(name?: string | Array<string>): void {
752
        this._gridAPI.clear_groupby(name);
34,455✔
753
        this.calculateGridSizes();
2,213✔
754
        this.notifyChanges(true);
755
        this.groupingPerformedSubject.next();
756
    }
757

758
    /**
759
     * Returns if a group is expanded or not.
760
     *
761
     * @param group The group record.
762
     * @example
763
     * ```typescript
764
     * public groupRow: IGroupByRecord;
765
     * const expandedGroup = this.grid.isExpandedGroup(this.groupRow);
766
     * ```
445✔
767
     */
1,375✔
768
    public override isExpandedGroup(group: IGroupByRecord): boolean {
445!
769
        const state: IGroupByExpandState = this._getStateForGroupRow(group);
445!
770
        return state ? state.expanded : this.groupsExpanded;
×
771
    }
772

445✔
773
    /**
774
     * Toggles the expansion state of a group.
775
     *
776
     * @param groupRow The group record to toggle.
777
     * @example
778
     * ```typescript
779
     * public groupRow: IGroupByRecord;
780
     * const toggleExpGroup = this.grid.toggleGroup(this.groupRow);
781
     * ```
782
     */
783
    public toggleGroup(groupRow: IGroupByRecord) {
784
        this._toggleGroup(groupRow);
785
        this.notifyChanges();
786
    }
787

788
    /**
15✔
789
     * Select all rows within a group.
39✔
790
     *
15!
791
     * @param groupRow: The group record which rows would be selected.
15✔
792
     * @param clearCurrentSelection if true clears the current selection
793
     * @example
794
     * ```typescript
795
     * this.grid.selectRowsInGroup(this.groupRow, true);
89✔
796
     * ```
89✔
797
     */
798
    public selectRowsInGroup(groupRow: IGroupByRecord, clearPrevSelection?: boolean) {
799
        this._gridAPI.groupBy_select_all_rows_in_group(groupRow, clearPrevSelection);
18✔
800
        this.notifyChanges();
18✔
801
    }
802

803
    /**
804
     * Deselect all rows within a group.
805
     *
806
     * @param groupRow The group record which rows would be deselected.
807
     * @example
42,858✔
808
     * ```typescript
42,858✔
809
     * public groupRow: IGroupByRecord;
42,858✔
810
     * this.grid.deselectRowsInGroup(this.groupRow);
123✔
811
     * ```
812
     */
42,858✔
813
    public deselectRowsInGroup(groupRow: IGroupByRecord) {
20✔
814
        this._gridAPI.groupBy_deselect_all_rows_in_group(groupRow);
815
        this.notifyChanges();
816
    }
42,858✔
817

41,142✔
818
    /**
819
     * Expands the specified group and all of its parent groups.
42,858✔
820
     *
821
     * @param groupRow The group record to fully expand.
822
     * @example
823
     * ```typescript
824
     * public groupRow: IGroupByRecord;
825
     * this.grid.fullyExpandGroup(this.groupRow);
169✔
826
     * ```
169✔
827
     */
828
    public fullyExpandGroup(groupRow: IGroupByRecord) {
829
        this._fullyExpandGroup(groupRow);
830
        this.notifyChanges();
831
    }
832

3,650✔
833
    /**
834
     * @hidden @internal
835
     */
836
    public override isGroupByRecord(record: any): boolean {
837
        // return record.records instance of GroupedRecords fails under Webpack
838
        return record && record?.records && record.records?.length &&
839
         record.expression && record.expression?.fieldName;
61✔
840
    }
61✔
841

2✔
842
    /**
843
     * Toggles the expansion state of all group rows recursively.
61✔
844
     *
845
     * @example
846
     * ```typescript
847
     * this.grid.toggleAllGroupRows;
848
     * ```
849
     */
103✔
850
    public toggleAllGroupRows() {
851
        this.groupingExpansionState = [];
31✔
852
        this.groupsExpanded = !this.groupsExpanded;
31✔
853
        this.notifyChanges();
31!
854
    }
31✔
855

856
    /** @hidden @internal */
857
    public get hasGroupableColumns(): boolean {
103✔
858
        return this._columns.some((col) => col.groupable && !col.columnGroup);
859
    }
860

861
    /**
862
     * Returns whether the `IgxGridComponent` has group area.
863
     *
10,934✔
864
     * @example
865
     * ```typescript
866
     * let isGroupAreaVisible = this.grid.showGroupArea;
867
     * ```
868
     *
869
     * @example
51✔
870
     * ```html
871
     * <igx-grid #grid [data]="Data" [showGroupArea]="false"></igx-grid>
872
     * ```
873
     */
874
    @Input()
875
    public get showGroupArea(): boolean {
31✔
876
        return this._showGroupArea;
877
    }
878
    public set showGroupArea(value: boolean) {
879
        this._showGroupArea = value;
880
        this.notifyChanges(true);
881
    }
389✔
882

883
    /**
884
     * @hidden @internal
1,957✔
885
     */
117✔
886
    public override isColumnGrouped(fieldName: string): boolean {
117✔
887
        return this.groupingExpressions.find(exp => exp.fieldName === fieldName) ? true : false;
888
    }
889

890
    /**
225✔
891
     * @hidden @internal
20✔
892
     */
52✔
893
    public getContext(rowData: any, rowIndex: number, pinned?: boolean): any {
1✔
894
        if (this.isDetailRecord(rowData)) {
895
            const cachedData = this.childDetailTemplates.get(rowData.detailsData);
896
            const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : rowData.detailsData;
19✔
897
            if (cachedData) {
898
                const view = cachedData.view;
205✔
899
                const tmlpOutlet = cachedData.owner;
900
                return {
901
                    $implicit: rowData.detailsData,
46✔
902
                    moveView: view,
16✔
903
                    owner: tmlpOutlet,
6✔
904
                    index: this.dataView.indexOf(rowData),
6✔
905
                    templateID: {
906
                        type:'detailRow',
907
                        id: rowID
908
                    }
2✔
909
                };
910
            } else {
911
                // child rows contain unique grids, hence should have unique templates
912
                return {
913
                    $implicit: rowData.detailsData,
914
                    templateID: {
915
                        type:'detailRow',
916
                        id: rowID
917
                    },
918
                    index: this.dataView.indexOf(rowData)
919
                };
920
            }
921
        }
922
        return {
923
            $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData,
924
            index: this.getDataViewIndex(rowIndex, pinned),
925
            templateID: {
926
                type: this.isGroupByRecord(rowData) ? 'groupRow' : this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow',
927
                id: null
928
            },
929
            disabled: this.isGhostRecord(rowData)
930
        };
931
    }
932

933
    /**
934
     * @hidden @internal
935
     */
936
    public viewCreatedHandler(args) {
937
        if (args.context.templateID.type === 'detailRow') {
938
            this.childDetailTemplates.set(args.context.$implicit, args);
2✔
939
        }
940
    }
941

942
    /**
943
     * @hidden @internal
944
     */
945
    public viewMovedHandler(args) {
946
        if (args.context.templateID.type === 'detailRow') {
947
            // view was moved, update owner in cache
948
            const key = args.context.$implicit;
949
            const cachedData = this.childDetailTemplates.get(key);
950
            cachedData.owner = args.owner;
951
        }
952
    }
953

954
    /**
955
     * @hidden @internal
956
     */
957
    public get iconTemplate() {
958
        if (this.groupsExpanded) {
959
            return this.headerExpandedIndicatorTemplate || this.defaultExpandedTemplate;
960
        } else {
961
            return this.headerCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;
962
        }
963
    }
964

965
    /**
966
     * @hidden @internal
967
     */
968
    public override ngAfterContentInit() {
969
        super.ngAfterContentInit();
970
        if (this.allowFiltering && this.hasColumnLayouts) {
971
            this.filterMode = FilterMode.excelStyleFilter;
972
        }
973
        if (this.groupTemplate) {
974
            this._groupRowTemplate = this.groupTemplate.template;
975
        }
976

977
        if (this.detailTemplateDirective) {
978
            this._detailTemplate = this.detailTemplateDirective;
979
        }
980

981

982
        if (this.hideGroupedColumns && this._columns && this.groupingExpressions) {
983
            this._setGroupColsVisibility(this.hideGroupedColumns);
984
        }
985
        this._setupNavigationService();
986
    }
987

988
    /**
989
     * @hidden @internal
990
     */
991
    public override ngAfterViewInit() {
992
        super.ngAfterViewInit();
993
        this.verticalScrollContainer.beforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => {
994
            const rowData = view.context.$implicit;
995
            if (this.isDetailRecord(rowData)) {
996
                const cachedData = this.childDetailTemplates.get(rowData.detailsData);
997
                if (cachedData) {
998
                    const tmlpOutlet = cachedData.owner;
999
                    tmlpOutlet._viewContainerRef.detach(0);
1000
                }
1001
            }
1002
        });
1003

1004
        this.sortingExpressionsChange.pipe(takeUntil(this.destroy$)).subscribe((sortingExpressions: ISortingExpression[]) => {
1005
            if (!this.groupingExpressions || !this.groupingExpressions.length) {
1006
                return;
1007
            }
1008

1009
            sortingExpressions.forEach((sortExpr: ISortingExpression) => {
1010
                const fieldName = sortExpr.fieldName;
1011
                const groupingExpr = this.groupingExpressions.find(ex => ex.fieldName === fieldName);
1012
                if (groupingExpr) {
1013
                    groupingExpr.dir = sortExpr.dir;
1014
                }
1015
            });
1016
        });
1017
    }
1018

1019
    /**
1020
     * @hidden @internal
1021
     */
1022
    public override ngOnInit() {
1023
        super.ngOnInit();
1024
        this.trackChanges = (_, rec) => (rec?.detailsData !== undefined ? rec.detailsData : rec);
1025
        this.groupingDone.pipe(takeUntil(this.destroy$)).subscribe((args) => {
1026
            this.crudService.endEdit(false);
1027
            this.summaryService.updateSummaryCache(args);
1028
            this._headerFeaturesWidth = NaN;
1029
        });
1030
    }
1031

1032
    /**
1033
     * @hidden @internal
1034
     */
1035
    public override ngDoCheck(): void {
1036
        if (this.groupingDiffer && this._columns && !this.hasColumnLayouts) {
1037
            const changes = this.groupingDiffer.diff(this.groupingExpressions);
1038
            if (changes && this._columns.length > 0) {
1039
                changes.forEachAddedItem((rec) => {
1040
                    const col = this.getColumnByName(rec.item.fieldName);
1041
                    if (col) {
1042
                        col.hidden = true;
1043
                    }
1044
                });
1045
                changes.forEachRemovedItem((rec) => {
1046
                    const col = this.getColumnByName(rec.item.fieldName);
1047
                    col.hidden = false;
1048
                });
1049
            }
1050
        }
1051
        super.ngDoCheck();
1052
    }
1053

1054
    /**
1055
     * @hidden @internal
1056
     */
1057
    public dataLoading(event) {
1058
        this.dataPreLoad.emit(event);
1059
    }
1060

1061
    /**
1062
     *
1063
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
1064
     *
1065
     * @remarks
1066
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
1067
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
1068
     */
1069
    public override getSelectedData(formatters = false, headers = false): any[] {
1070
        if (this.groupingExpressions.length || this.hasDetails) {
1071
            const source = [];
1072

1073
            const process = (record) => {
1074
                if (record.expression || record.summaries || this.isDetailRecord(record)) {
1075
                    source.push(null);
1076
                    return;
1077
                }
1078
                source.push(record);
1079

1080
            };
1081

1082
            this.dataView.forEach(process);
1083
            return this.extractDataFromSelection(source, formatters, headers);
1084
        } else {
1085
            return super.getSelectedData(formatters, headers);
1086
        }
1087
    }
1088

1089
    /**
1090
     * Returns the `IgxGridRow` by index.
1091
     *
1092
     * @example
1093
     * ```typescript
1094
     * const myRow = grid.getRowByIndex(1);
1095
     * ```
1096
     * @param index
1097
     */
1098
    public getRowByIndex(index: number): RowType {
1099
        let row: RowType;
1100
        if (index < 0) {
1101
            return undefined;
1102
        }
1103
        if (this.dataView.length >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) {
1104
            row = this.createRow(index);
1105
        } else {
1106
            if (!(index < this.virtualizationState.startIndex) && !(index > this.virtualizationState.startIndex + this.virtualizationState.chunkSize)) {
1107
                row = this.createRow(index);
1108
            }
1109
        }
1110

1111
        if (this.pagingMode === 1 && this.page !== 0) {
1112
            row.index = index + this.perPage * this.page;
1113
        }
1114
        return row;
1115
    }
1116

1117
    /**
1118
     * Returns `IgxGridRow` object by the specified primary key.
1119
     *
1120
     * @remarks
1121
     * Requires that the `primaryKey` property is set.
1122
     * @example
1123
     * ```typescript
1124
     * const myRow = this.grid1.getRowByKey("cell5");
1125
     * ```
1126
     * @param keyValue
1127
     */
1128
    public getRowByKey(key: any): RowType {
1129
        const rec = this.filteredSortedData ? this.primaryKey ?
1130
            this.filteredSortedData.find(record => record[this.primaryKey] === key) :
1131
            this.filteredSortedData.find(record => record === key) : undefined;
1132
        const index = this.dataView.indexOf(rec);
1133
        if (index < 0 || index > this.dataView.length) {
1134
            return undefined;
1135
        }
1136

1137
        return new IgxGridRow(this, index, rec);
1138
    }
1139

1140
    /**
1141
     * @hidden @internal
1142
     */
1143
    public allRows(): RowType[] {
1144
        return this.dataView.map((rec, index) => {
1145
            this.pagingMode === 1 && this.page !== 0 ? index = index + this.perPage * this.page : index = this.dataRowList.first.index + index;
1146
            return this.createRow(index);
1147
        });
1148
    }
1149

1150
    /**
1151
     * Returns the collection of `IgxGridRow`s for current page.
1152
     *
1153
     * @hidden @internal
1154
     */
1155
    public dataRows(): RowType[] {
1156
        return this.allRows().filter(row => row instanceof IgxGridRow);
1157
    }
1158

1159
    /**
1160
     * Returns an array of the selected `IgxGridCell`s.
1161
     *
1162
     * @example
1163
     * ```typescript
1164
     * const selectedCells = this.grid.selectedCells;
1165
     * ```
1166
     */
1167
    public get selectedCells(): CellType[] {
1168
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
1169
            .reduce((a, b) => a.concat(b), []);
1170
    }
1171

1172
    /**
1173
     * Returns a `CellType` object that matches the conditions.
1174
     *
1175
     * @example
1176
     * ```typescript
1177
     * const myCell = this.grid1.getCellByColumn(2, "UnitPrice");
1178
     * ```
1179
     * @param rowIndex
1180
     * @param columnField
1181
     */
1182
    public getCellByColumn(rowIndex: number, columnField: string): CellType {
1183
        const row = this.getRowByIndex(rowIndex);
1184
        const column = this._columns.find((col) => col.field === columnField);
1185
        if (row && row instanceof IgxGridRow && !row.data?.detailsData && column) {
1186
            if (this.pagingMode === 1 && this.page !== 0) {
1187
                row.index = rowIndex + this.perPage * this.page;
1188
            }
1189
            return new IgxGridCell(this, row.index, column);
1190
        }
1191
    }
1192

1193
    /**
1194
     * Returns a `CellType` object that matches the conditions.
1195
     *
1196
     * @remarks
1197
     * Requires that the primaryKey property is set.
1198
     * @example
1199
     * ```typescript
1200
     * grid.getCellByKey(1, 'index');
1201
     * ```
1202
     * @param rowSelector match any rowID
1203
     * @param columnField
1204
     */
1205
    public getCellByKey(rowSelector: any, columnField: string): CellType {
1206
        const row = this.getRowByKey(rowSelector);
1207
        const column = this._columns.find((col) => col.field === columnField);
1208
        if (row && column) {
1209
            return new IgxGridCell(this, row.index, column);
1210
        }
1211
    }
1212

1213
    public override pinRow(rowID: any, index?: number): boolean {
1214
        const row = this.getRowByKey(rowID);
1215
        return super.pinRow(rowID, index, row);
1216
    }
1217

1218
    public override unpinRow(rowID: any): boolean {
1219
        const row = this.getRowByKey(rowID);
1220
        return super.unpinRow(rowID, row);
1221
    }
1222

1223
    /**
1224
     * @hidden @internal
1225
     */
1226
    public createRow(index: number, data?: any): RowType {
1227
        let row: RowType;
1228

1229
        const dataIndex = this._getDataViewIndex(index);
1230
        const rec = data ?? this.dataView[dataIndex];
1231

1232
        if (rec && this.isGroupByRecord(rec)) {
1233
            row = new IgxGroupByRow(this, index, rec);
1234
        }
1235
        if (rec && this.isSummaryRow(rec)) {
1236
            row = new IgxSummaryRow(this, index, rec.summaries, GridInstanceType.Grid);
1237
        }
1238
        // if found record is a no a groupby or summary row, return IgxGridRow instance
1239
        if (!row && rec) {
1240
            row = new IgxGridRow(this, index, rec);
1241
        }
1242

1243
        return row;
1244
    }
1245

1246
    /**
1247
     * @hidden @internal
1248
     */
1249
    protected override get defaultTargetBodyHeight(): number {
1250
        const allItems = this.totalItemCount || this.dataLength;
1251
        return this.renderedRowHeight * Math.min(this._defaultTargetRecordNumber,
1252
            this.paginator ? Math.min(allItems, this.perPage) : allItems);
1253
    }
1254

1255
    /**
1256
     * @hidden @internal
1257
     */
1258
    protected override getGroupAreaHeight(): number {
1259
        return this.groupArea ? this.getComputedHeight(this.groupArea.nativeElement) : 0;
1260
    }
1261

1262
    /**
1263
     * @hidden @internal
1264
     */
1265
    protected override onColumnsAddedOrRemoved() {
1266
        // update grouping states
1267
        this.groupablePipeTrigger++;
1268
        if (this.groupingExpressions && this.hideGroupedColumns) {
1269
            this._setGroupColsVisibility(this.hideGroupedColumns);
1270
        }
1271
        super.onColumnsAddedOrRemoved();
1272
    }
1273

1274
    /**
1275
     * @hidden @internal
1276
     */
1277
    protected override scrollTo(row: any | number, column: any | number): void {
1278
        if (this.groupingExpressions && this.groupingExpressions.length
1279
            && typeof (row) !== 'number') {
1280
            const rowIndex = this.groupingResult.indexOf(row);
1281
            const groupByRecord = this.groupingMetadata[rowIndex];
1282
            if (groupByRecord) {
1283
                this._fullyExpandGroup(groupByRecord);
1284
            }
1285
        }
1286

1287
        super.scrollTo(row, column, this.groupingFlatResult);
1288
    }
1289

1290
    /**
1291
     * @hidden @internal
1292
     */
1293
    protected _getStateForGroupRow(groupRow: IGroupByRecord): IGroupByExpandState {
1294
        return this._gridAPI.groupBy_get_expanded_for_group(groupRow);
1295
    }
1296

1297
    /**
1298
     * @hidden
1299
     */
1300
    protected _toggleGroup(groupRow: IGroupByRecord) {
1301
        this._gridAPI.groupBy_toggle_group(groupRow);
1302
    }
1303

1304
    /**
1305
     * @hidden @internal
1306
     */
1307
    protected _fullyExpandGroup(groupRow: IGroupByRecord) {
1308
        this._gridAPI.groupBy_fully_expand_group(groupRow);
1309
    }
1310

1311
    /**
1312
     * @hidden @internal
1313
     */
1314
    protected _applyGrouping() {
1315
        this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
1316
    }
1317

1318
    private _setupNavigationService() {
1319
        if (this.hasColumnLayouts) {
1320
            this.navigation = new IgxGridMRLNavigationService(this.platform);
1321
            this.navigation.grid = this;
1322
        }
1323
    }
1324

1325
    private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {
1326
        if (expression instanceof Array) {
1327
            for (const singleExpression of expression) {
1328
                if (!singleExpression.fieldName) {
1329
                    return true;
1330
                }
1331
            }
1332
            return false;
1333
        }
1334
        return !expression.fieldName;
1335
    }
1336

1337
    private _setGroupColsVisibility(value) {
1338
        if (this._columns.length > 0 && !this.hasColumnLayouts) {
1339
            this.groupingExpressions.forEach((expr) => {
1340
                const col = this.getColumnByName(expr.fieldName);
1341
                col.hidden = value;
1342
            });
1343
        }
1344
    }
1345
}
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