Skip to content

Commit ab91892

Browse files
authored
Merge pull request #6884 from IgniteUI/mkirova/row-pinning-filtering
Row pinning and filtering intergration
2 parents 383f9bf + 95e3fb0 commit ab91892

File tree

5 files changed

+131
-25
lines changed

5 files changed

+131
-25
lines changed

projects/igniteui-angular/src/lib/grids/api.service.ts

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ export class GridBaseAPIService <T extends IgxGridBaseDirective & GridType> {
8686
}
8787

8888
public get_row_by_key(rowSelector: any): IgxRowDirective<IgxGridBaseDirective & GridType> {
89+
if (!this.grid) {
90+
return null;
91+
}
8992
const primaryKey = this.grid.primaryKey;
9093
if (primaryKey !== undefined && primaryKey !== null) {
9194
return this.grid.dataRowList.find((row) => row.rowData[primaryKey] === rowSelector);

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

+25-4
Original file line numberDiff line numberDiff line change
@@ -1747,8 +1747,8 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
17471747
/**
17481748
* @hidden @internal
17491749
*/
1750-
@ViewChild('pinContainer', { static: false })
1751-
public pinContainer: ElementRef;
1750+
@ViewChildren('pinContainer', { read: ElementRef })
1751+
public pinContainers: QueryList<ElementRef>;
17521752

17531753
/**
17541754
* @hidden @internal
@@ -2410,6 +2410,9 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
24102410
*/
24112411
protected destroy$ = new Subject<any>();
24122412

2413+
protected _filteredPinnedData;
2414+
protected _filteredUnpinnedData;
2415+
24132416
/**
24142417
* @hidden
24152418
*/
@@ -2865,6 +2868,17 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
28652868
}
28662869
}
28672870

2871+
public setFilterData(data, pinned: boolean) {
2872+
if (this.hasPinnedRecords && pinned) {
2873+
this._filteredPinnedData = data;
2874+
this.filteredData = [... this._filteredPinnedData, ... this._filteredUnpinnedData];
2875+
} else if (this.hasPinnedRecords && !pinned) {
2876+
this._filteredUnpinnedData = data;
2877+
} else {
2878+
this.filteredData = data;
2879+
}
2880+
}
2881+
28682882
/**
28692883
* @hidden
28702884
* @internal
@@ -2956,6 +2970,12 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
29562970
vertScrDC.addEventListener('scroll', this.scrollHandler);
29572971
vertScrDC.addEventListener('wheel', () => this.wheelHandler());
29582972

2973+
this.pinContainers.changes.subscribe((c) => {
2974+
if (this.hasPinnedRecords) {
2975+
// on row pin containers change grid sizes should be recalculated.
2976+
this.calculateGridSizes();
2977+
}
2978+
});
29592979
}
29602980

29612981
/**
@@ -4140,8 +4160,9 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
41404160
}
41414161

41424162
get pinnedRowHeight() {
4143-
const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
4144-
return this._pinnedRecordIDs.length > 0 ? containerHeight : 0;
4163+
const pinContainer = this.pinContainers && this.pinContainers.length > 0 ? this.pinContainers.first : null;
4164+
const containerHeight = pinContainer ? pinContainer.nativeElement.offsetHeight : 0;
4165+
return this.hasPinnedRecords ? containerHeight : 0;
41454166
}
41464167

41474168
get totalHeight() {

projects/igniteui-angular/src/lib/grids/grid/grid.component.html

+24-19
Original file line numberDiff line numberDiff line change
@@ -108,22 +108,29 @@
108108
<span *ngIf="hasMovableColumns && draggedColumn && pinnedColumns.length > 0"
109109
[igxColumnMovingDrop]="headerContainer" [attr.droppable]="true" id="left"
110110
class="igx-grid__scroll-on-drag-pinned" [style.left.px]="pinnedWidth"></span>
111-
<ng-template #pinnedRecordsTemplate>
112-
<ng-container *ngFor="let rowData of data
113-
| gridTransaction:id:pipeTrigger
114-
| visibleColumns:hasVisibleColumns
115-
| rowPinning:id:true:pipeTrigger
116-
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger
117-
| gridSort:sortingExpressions:sortStrategy:id:pipeTrigger
118-
| gridDetails:hasDetails:expansionStates:pipeTrigger; let rowIndex = index">
119-
<ng-container *ngTemplateOutlet="getRowTemplate(rowData); context: getContext(rowData, rowIndex, true)">
111+
<ng-container *ngTemplateOutlet="hasPinnedRecords && isRowPinningToTop ? pinnedRecordsTemplate : null">
112+
</ng-container>
113+
<ng-template #pinnedRecordsTemplate>
114+
<ng-container *ngIf='data
115+
| gridTransaction:id:pipeTrigger
116+
| visibleColumns:hasVisibleColumns
117+
| rowPinning:id:true:pipeTrigger
118+
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger:true
119+
| gridSort:sortingExpressions:sortStrategy:id:pipeTrigger
120+
| gridDetails:hasDetails:expansionStates:pipeTrigger as pinnedData'>
121+
<div #pinContainer *ngIf='pinnedData.length > 0'
122+
[ngClass]="{
123+
'igx-grid__tr--pinned-bottom': !isRowPinningToTop,
124+
'igx-grid__tr--pinned-top': isRowPinningToTop
125+
}"
126+
class='igx-grid__tr--pinned' [style.bottom.px]=' !isRowPinningToTop ? pinnedBottom : null'>
127+
<ng-container *ngFor="let rowData of pinnedData; let rowIndex = index">
128+
<ng-container *ngTemplateOutlet="getRowTemplate(rowData); context: getContext(rowData, rowIndex, true)">
129+
</ng-container>
120130
</ng-container>
121-
</ng-container>
122-
</ng-template>
123-
<div #pinContainer *ngIf='hasPinnedRecords && isRowPinningToTop' class='igx-grid__tr--pinned igx-grid__tr--pinned-top'>
124-
<ng-container *ngTemplateOutlet="pinnedRecordsTemplate">
125-
</ng-container>
126-
</div>
131+
</div>
132+
</ng-container>
133+
</ng-template>
127134
<ng-template igxGridFor let-rowData [igxGridForOf]="data
128135
| gridTransaction:id:pipeTrigger
129136
| visibleColumns:hasVisibleColumns
@@ -148,10 +155,8 @@
148155
(onBeforeViewDetach)='viewDetachHandler($event)'>
149156
</ng-template>
150157
</ng-template>
151-
<div #pinContainer *ngIf='hasPinnedRecords && !isRowPinningToTop' class='igx-grid__tr--pinned igx-grid__tr--pinned-bottom' [style.bottom.px]='pinnedBottom'>
152-
<ng-container *ngTemplateOutlet="pinnedRecordsTemplate">
153-
</ng-container>
154-
</div>
158+
<ng-container *ngTemplateOutlet="hasPinnedRecords && !isRowPinningToTop ? pinnedRecordsTemplate : null">
159+
</ng-container>
155160
<ng-template #record_template let-rowIndex="index" let-rowData>
156161
<igx-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData" #row>
157162
</igx-grid-row>

projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export class IgxGridFilteringPipe implements PipeTransform {
135135

136136
public transform(collection: any[], expressionsTree: IFilteringExpressionsTree,
137137
filterStrategy: IFilteringStrategy,
138-
advancedExpressionsTree: IFilteringExpressionsTree, id: string, pipeTrigger: number, filteringPipeTrigger: number) {
138+
advancedExpressionsTree: IFilteringExpressionsTree, id: string, pipeTrigger: number, filteringPipeTrigger: number, pinned?) {
139139
const grid = this.gridAPI.grid;
140140
const state = {
141141
expressionsTree: expressionsTree,
@@ -148,7 +148,7 @@ export class IgxGridFilteringPipe implements PipeTransform {
148148
}
149149

150150
const result = DataUtil.filter(cloneArray(collection), state);
151-
grid.filteredData = result;
151+
grid.setFilterData(result, pinned);
152152
return result;
153153
}
154154
}

projects/igniteui-angular/src/lib/grids/grid/row-pinning.spec.ts

+77
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { GridFunctions } from '../../test-utils/grid-functions.spec';
1313
import { SortingDirection } from '../../data-operations/sorting-expression.interface';
1414
import { IgxGridTransaction } from '../tree-grid';
1515
import { IgxTransactionService } from '../../services';
16+
import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition';
1617

1718
describe('Row Pinning #grid', () => {
1819
const FIXED_ROW_CONTAINER = '.igx-grid__tr--pinned ';
@@ -226,7 +227,20 @@ describe('Row Pinning #grid', () => {
226227
expect(grid.getRowByIndex(1).rowID).toBe(fix.componentInstance.data[1]);
227228
});
228229

230+
it('should allow pinning onInit', () => {
231+
expect(() => {
232+
fix = TestBed.createComponent(GridRowPinningComponent);
233+
grid = fix.componentInstance.instance;
234+
grid.pinRow(fix.componentInstance.data[1]);
235+
fix.detectChanges();
236+
}).not.toThrow();
237+
expect(grid.pinnedRows.length).toBe(1);
238+
expect(grid.getRowByIndex(0).rowID).toBe(fix.componentInstance.data[1]);
239+
});
240+
229241
it('should pin rows when columns are grouped.', () => {
242+
grid.height = '550px';
243+
fix.detectChanges();
230244
// pin 1st and 2nd data row
231245
grid.pinRow(fix.componentInstance.data[0]);
232246
grid.pinRow(fix.componentInstance.data[1]);
@@ -258,6 +272,68 @@ describe('Row Pinning #grid', () => {
258272
expect(groupRows[0].groupRow.records[0].ID).toEqual('BLAUS');
259273
});
260274

275+
it('should apply filtering to both pinned and unpinned rows.', () => {
276+
grid.getRowByIndex(1).pin();
277+
fix.detectChanges();
278+
grid.getRowByIndex(5).pin();
279+
fix.detectChanges();
280+
281+
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
282+
expect(pinRowContainer[0].children.length).toBe(2);
283+
expect(pinRowContainer[0].children[0].context.rowID).toBe(fix.componentInstance.data[1]);
284+
expect(pinRowContainer[0].children[1].context.rowID).toBe(fix.componentInstance.data[5]);
285+
286+
grid.filter('ID', 'B', IgxStringFilteringOperand.instance().condition('contains'), false);
287+
fix.detectChanges();
288+
289+
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
290+
expect(pinRowContainer[0].children.length).toBe(1);
291+
expect(pinRowContainer[0].children[0].context.rowID).toBe(fix.componentInstance.data[5]);
292+
});
293+
294+
it('should remove pinned container and recalculate sizes when all pinned records are filtered out.', () => {
295+
grid.getRowByIndex(1).pin();
296+
fix.detectChanges();
297+
let pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
298+
expect(pinRowContainer.length).toBe(1);
299+
300+
let expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
301+
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
302+
303+
grid.filter('ID', 'B', IgxStringFilteringOperand.instance().condition('startsWith'), false);
304+
fix.detectChanges();
305+
306+
pinRowContainer = fix.debugElement.queryAll(By.css(FIXED_ROW_CONTAINER));
307+
expect(pinRowContainer.length).toBe(0);
308+
309+
expect(grid.pinnedRowHeight).toBe(0);
310+
expectedHeight = parseInt(grid.height, 10) - grid.pinnedRowHeight - 18 - grid.theadRow.nativeElement.offsetHeight;
311+
expect(grid.calcHeight - expectedHeight).toBeLessThanOrEqual(1);
312+
});
313+
314+
it('should return correct filterData collection.', () => {
315+
grid.getRowByIndex(1).pin();
316+
fix.detectChanges();
317+
grid.getRowByIndex(5).pin();
318+
fix.detectChanges();
319+
320+
grid.filter('ID', 'B', IgxStringFilteringOperand.instance().condition('contains'), false);
321+
fix.detectChanges();
322+
323+
let gridFilterData = grid.filteredData;
324+
expect(gridFilterData.length).toBe(7);
325+
expect(gridFilterData[0].ID).toBe('BLAUS');
326+
expect(gridFilterData[1].ID).toBe('BERGS');
327+
328+
fix.componentInstance.pinningConfig = { columns: ColumnPinningPosition.Start, rows: RowPinningPosition.Bottom };
329+
fix.detectChanges();
330+
331+
gridFilterData = grid.filteredData;
332+
expect(gridFilterData.length).toBe(7);
333+
expect(gridFilterData[0].ID).toBe('BLAUS');
334+
expect(gridFilterData[1].ID).toBe('BERGS');
335+
});
336+
261337
it('should apply sorting to both pinned and unpinned rows.', () => {
262338
grid.getRowByIndex(1).pin();
263339
grid.getRowByIndex(5).pin();
@@ -482,6 +558,7 @@ describe('Row Pinning #grid', () => {
482558
@Component({
483559
template: `
484560
<igx-grid
561+
[allowFiltering]='true'
485562
[pinning]='pinningConfig'
486563
[width]='"800px"'
487564
[height]='"500px"'

0 commit comments

Comments
 (0)