Skip to content

Commit a2c28e5

Browse files
authored
Merge pull request #6754 from IgniteUI/dkamburov/pinning-config
feat(grid-pinning): Add pinning configuration api and a basic test #6676
2 parents 98b9efa + 860b19b commit a2c28e5

13 files changed

+219
-21
lines changed

projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-component.scss

+9
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@
245245
@extend %grid-cell--pinned-last !optional;
246246
}
247247

248+
@include e(th, $m: pinned-first) {
249+
@extend %grid-cell--pinned-first !optional;
250+
}
251+
248252
@include e(th, $m: fw) {
249253
@extend %grid-cell--fixed-width !optional;
250254
}
@@ -327,6 +331,11 @@
327331
@extend %grid-cell--pinned-last !optional;
328332
}
329333

334+
@include e(td, $m: pinned-first) {
335+
@extend %grid-cell--pinned !optional;
336+
@extend %grid-cell--pinned-first !optional;
337+
}
338+
330339
@include e(td, $m: pinned-end) {
331340
@extend %grid-cell-pinned--end !optional;
332341
}

projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss

+9
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,15 @@
13121312
}
13131313
}
13141314

1315+
%grid-cell--pinned-first {
1316+
border-#{$left}: map-get($cell-pin, 'style') map-get($cell-pin, 'color') !important;
1317+
1318+
&%grid-cell--editing {
1319+
border-#{$left}: map-get($cell-pin, 'style') --var($theme, 'cell-selected-background') !important;
1320+
}
1321+
}
1322+
1323+
13151324
%grid-cell-header {
13161325
flex-flow: row nowrap;
13171326
justify-content: space-between;

projects/igniteui-angular/src/lib/grids/cell.component.ts

+8
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,14 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
330330
@HostBinding('class.igx-grid__td--pinned-last')
331331
lastPinned = false;
332332

333+
/**
334+
* @hidden
335+
* @internal
336+
*/
337+
@Input()
338+
@HostBinding('class.igx-grid__td--pinned-first')
339+
firstPinned = false;
340+
333341
/**
334342
* Returns whether the cell is in edit mode.
335343
*/

projects/igniteui-angular/src/lib/grids/columns/column.component.ts

+23-3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
IgxFilterCellTemplateDirective
4545
} from './templates.directive';
4646
import { MRLResizeColumnInfo, MRLColumnSizeInfo } from './interfaces';
47+
import { ColumnPinningPosition } from '../common/enums';
4748

4849
/**
4950
* **Ignite UI for Angular Column** -
@@ -916,9 +917,16 @@ export class IgxColumnComponent implements AfterContentInit {
916917

917918
if (!this.pinned) {
918919
const indexInCollection = unpinnedColumns.indexOf(col);
919-
vIndex = indexInCollection === -1 ? -1 : pinnedColumns.length + indexInCollection;
920+
vIndex = indexInCollection === -1 ?
921+
-1 :
922+
(this.grid.isPinningToStart ?
923+
pinnedColumns.length + indexInCollection :
924+
indexInCollection);
920925
} else {
921-
vIndex = pinnedColumns.indexOf(col);
926+
const indexInCollection = pinnedColumns.indexOf(col);
927+
vIndex = this.grid.isPinningToStart ?
928+
indexInCollection :
929+
unpinnedColumns.length + indexInCollection;
922930
}
923931
this._vIndex = vIndex;
924932
return vIndex;
@@ -986,8 +994,20 @@ export class IgxColumnComponent implements AfterContentInit {
986994
}
987995

988996
get isLastPinned(): boolean {
989-
return this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
997+
return this.grid.isPinningToStart &&
998+
this.grid.pinnedColumns[this.grid.pinnedColumns.length - 1] === this;
990999
}
1000+
1001+
get isFirstPinned(): boolean {
1002+
return !this.grid.isPinningToStart && this.grid.pinnedColumns[0] === this;
1003+
}
1004+
1005+
get rightPinnedOffset(): string {
1006+
return this.pinned ?
1007+
- this.grid.pinnedWidth + 'px' :
1008+
null;
1009+
}
1010+
9911011
get gridRowSpan(): number {
9921012
return this.rowEnd && this.rowStart ? this.rowEnd - this.rowStart : 1;
9931013
}

projects/igniteui-angular/src/lib/grids/common/enums.ts

+10
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,13 @@ export enum ColumnDisplayOrder {
3232
Alphabetical = 'Alphabetical',
3333
DisplayOrder = 'DisplayOrder'
3434
}
35+
36+
export enum ColumnPinningPosition {
37+
Start,
38+
End
39+
}
40+
41+
export enum RowPinningPosition {
42+
Top,
43+
Bottom
44+
}

projects/igniteui-angular/src/lib/grids/common/grid.interface.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FilterMode } from './enums';
1+
import { FilterMode, ColumnPinningPosition, RowPinningPosition } from './enums';
22
import { DisplayDensity } from '../../core/displayDensity';
33
import { EventEmitter } from '@angular/core';
44
import { IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
@@ -67,3 +67,8 @@ export interface GridType extends IGridDataBindable {
6767
isDetailRecord(rec: any): boolean;
6868
isGroupByRecord(rec: any): boolean;
6969
}
70+
71+
export interface IPinningConfig {
72+
columns?: ColumnPinningPosition;
73+
rows?: RowPinningPosition;
74+
}

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

+30-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,14 @@ import { DeprecateProperty } from '../core/deprecateDecorators';
103103
import { IFilteringStrategy } from '../data-operations/filtering-strategy';
104104
import { IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective,
105105
IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective } from './grid/grid.directives';
106-
import { GridKeydownTargetType, GridSelectionMode, GridSummaryPosition, GridSummaryCalculationMode, FilterMode } from './common/enums';
106+
import {
107+
GridKeydownTargetType,
108+
GridSelectionMode,
109+
GridSummaryPosition,
110+
GridSummaryCalculationMode,
111+
FilterMode,
112+
ColumnPinningPosition
113+
} from './common/enums';
107114
import {
108115
IGridCellEventArgs,
109116
IRowSelectionEventArgs,
@@ -125,7 +132,7 @@ import {
125132
IRowToggleEventArgs
126133
} from './common/events';
127134
import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component';
128-
import { GridType } from './common/grid.interface';
135+
import { GridType, IPinningConfig } from './common/grid.interface';
129136
import { IgxDecimalPipeComponent, IgxDatePipeComponent } from './common/pipes';
130137
import { DropPosition } from './moving/moving.service';
131138
import { IgxHeadSelectorDirective, IgxRowSelectorDirective } from './selection/row-selectors';
@@ -795,6 +802,19 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
795802
@Input()
796803
public columnHidingTitle = '';
797804

805+
/**
806+
* Gets/Sets the initial pinning configuration.
807+
* @remarks
808+
* Allows to apply pinning the columns to the start or the end.
809+
* Note that pinning to both sides at a time is not allowed.
810+
* @example
811+
* ```html
812+
* <igx-grid [pinning]="pinningConfig"></igx-grid>
813+
* ```
814+
*/
815+
@Input()
816+
public pinning: IPinningConfig = { columns: ColumnPinningPosition.Start };
817+
798818
/**
799819
* Gets/Sets if the built-in column pinning UI should be shown in the toolbar.
800820
* @example
@@ -1575,6 +1595,14 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
15751595
@ContentChildren(IgxHeadSelectorDirective, { read: IgxHeadSelectorDirective, descendants: false })
15761596
public headSelectorsTemplates: QueryList<IgxHeadSelectorDirective>;
15771597

1598+
/**
1599+
* @hidden
1600+
* @internal
1601+
*/
1602+
get isPinningToStart() {
1603+
return this.pinning.columns !== ColumnPinningPosition.End;
1604+
}
1605+
15781606
/**
15791607
* @hidden
15801608
* @internal

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@
1616
</ng-container>
1717

1818
<ng-container *ngIf="!grid.hasColumnLayouts">
19-
<ng-container *ngIf="pinnedColumns.length > 0">
19+
<ng-container *ngIf="pinnedColumns.length > 0 && grid.isPinningToStart">
2020
<ng-template ngFor let-col [ngForOf]="pinnedColumns | igxNotGrouped">
2121
<ng-container *ngTemplateOutlet="col.visibleIndex === 0 && grid.hasDetails ? expandableCellTemplate : cellTemplate; context: getContext(col, this)"></ng-container>
2222
</ng-template>
2323
</ng-container>
2424
<ng-template igxGridFor let-col [igxGridForOf]="unpinnedColumns | igxNotGrouped" [igxForScrollContainer]="grid.parentVirtDir" [igxForScrollOrientation]="'horizontal'" [igxForContainerSize]='grid.unpinnedWidth' [igxForSizePropName]='"calcPixelWidth"' [igxForTrackBy]='grid.trackColumnChanges' #igxDirRef>
2525
<ng-container *ngTemplateOutlet="col.visibleIndex === 0 && grid.hasDetails ? expandableCellTemplate : cellTemplate; context: getContext(col, this)"></ng-container>
2626
</ng-template>
27+
<ng-container *ngIf="pinnedColumns.length > 0 && !grid.isPinningToStart">
28+
<ng-template ngFor let-col [ngForOf]="pinnedColumns | igxNotGrouped">
29+
<ng-container *ngTemplateOutlet="col.visibleIndex === 0 && grid.hasDetails ? expandableCellTemplate : cellTemplate; context: getContext(col, this)"></ng-container>
30+
</ng-template>
31+
</ng-container>
2732
</ng-container>
2833

2934
<ng-container *ngIf="grid.hasColumnLayouts">
@@ -81,11 +86,13 @@
8186
[formatter]="col.formatter"
8287
[row]="this"
8388
[lastPinned]="col.columnLayoutChild ? null : col.isLastPinned"
89+
[firstPinned]="col.columnLayoutChild ? null : col.isFirstPinned"
8490
[style.min-height.px]="grid.rowHeight || 32"
8591
[rowData]="rowData"
8692
[style.min-width]="col.width"
8793
[style.max-width]="col.width"
8894
[style.flex-basis]="col.width"
95+
[style.left]="col.rightPinnedOffset"
8996
[width]="col.getCellWidth()"
9097
[visibleColumnIndex]="col.visibleIndex"
9198
[value]="rowData[col.field]"

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

+9-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
</ng-template>
7171
</div>
7272
</ng-container>
73-
<ng-container *ngIf="pinnedColumns.length > 0">
73+
<ng-container *ngIf="pinnedColumns.length > 0 && isPinningToStart">
7474
<ng-template ngFor let-col [ngForOf]="pinnedColumns | igxTopLevel">
7575
<igx-grid-header-group [column]="col" [gridID]="id" [style.min-width]="getHeaderGroupWidth(col)"
7676
[style.flex-basis]="getHeaderGroupWidth(col)"></igx-grid-header-group>
@@ -83,6 +83,12 @@
8383
<igx-grid-header-group [column]="col" [gridID]="id" [style.min-width]="getHeaderGroupWidth(col)"
8484
[style.flex-basis]="getHeaderGroupWidth(col)"></igx-grid-header-group>
8585
</ng-template>
86+
<ng-container *ngIf="pinnedColumns.length > 0 && !isPinningToStart">
87+
<ng-template ngFor let-col [ngForOf]="pinnedColumns | igxTopLevel">
88+
<igx-grid-header-group [column]="col" [gridID]="id" [style.min-width]="getHeaderGroupWidth(col)"
89+
[style.flex-basis]="getHeaderGroupWidth(col)" [style.left]="col.rightPinnedOffset"></igx-grid-header-group>
90+
</ng-template>
91+
</ng-container>
8692
</div>
8793
<igx-grid-filtering-row #filteringRow [style.width.px]='calcWidth' *ngIf="filteringService.isFilterRowVisible"
8894
[column]="filteringService.filteredColumn"></igx-grid-filtering-row>
@@ -182,11 +188,12 @@
182188
</div>
183189

184190
<div class="igx-grid__scroll" [style.height]="'18px'" #scr [hidden]="isHorizontalScrollHidden">
185-
<div class="igx-grid__scroll-start" [style.width.px]='pinnedWidth' [style.min-width.px]='pinnedWidth' [hidden]="pinnedWidth === 0"></div>
191+
<div class="igx-grid__scroll-start" [style.width.px]='pinnedWidth' [style.min-width.px]='pinnedWidth' [hidden]="pinnedWidth === 0 || !isPinningToStart"></div>
186192
<div class="igx-grid__scroll-main" [style.width.px]='unpinnedWidth'>
187193
<ng-template igxGridFor [igxGridForOf]='[]' #scrollContainer>
188194
</ng-template>
189195
</div>
196+
<div class="igx-grid__scroll-end" [style.float]='"right"' [style.width.px]='pinnedWidth' [style.min-width.px]='pinnedWidth' [hidden]="pinnedWidth === 0 || isPinningToStart"></div>
190197
</div>
191198

192199
<div class="igx-grid__footer" #footer>

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

+84
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { configureTestSuite } from '../../test-utils/configure-suite';
1212
import { IgxGridHeaderGroupComponent } from '../headers/grid-header-group.component';
1313
import { IGridCellEventArgs } from '../common/events';
1414
import { IgxColumnComponent } from '../columns/column.component';
15+
import { ColumnPinningPosition } from '../common/enums';
16+
import { IPinningConfig } from '../common/grid.interface';
1517

1618
describe('IgxGrid - Column Pinning #grid ', () => {
1719
configureTestSuite();
@@ -412,6 +414,54 @@ describe('IgxGrid - Column Pinning #grid ', () => {
412414
}));
413415
});
414416

417+
describe('IgxGrid - Column Pinning to End', () => {
418+
configureTestSuite();
419+
420+
const COLUMN_HEADER_CLASS = '.igx-grid__th';
421+
const FIRST_PINNED_CELL_CSS = 'igx-grid__td--pinned-first';
422+
423+
beforeAll(async(() => {
424+
TestBed.configureTestingModule({
425+
declarations: [
426+
GridRightPinningComponent
427+
],
428+
imports: [NoopAnimationsModule, IgxGridModule]
429+
}).compileComponents();
430+
}));
431+
432+
it('should correctly initialize when there are initially pinned columns.', fakeAsync(() => {
433+
const fix = TestBed.createComponent(GridRightPinningComponent);
434+
435+
tick();
436+
fix.detectChanges();
437+
const grid = fix.componentInstance.instance;
438+
const firstPinnedIndex = grid.unpinnedColumns.length;
439+
const secondPinnedIndex = grid.unpinnedColumns.length + 1;
440+
// verify pinned/unpinned collections
441+
expect(grid.pinnedColumns.length).toEqual(2);
442+
expect(grid.unpinnedColumns.length).toEqual(9);
443+
444+
// verify DOM
445+
const firstIndexCell = grid.getCellByColumn(0, 'CompanyName');
446+
expect(firstIndexCell.visibleColumnIndex).toEqual(firstPinnedIndex);
447+
expect(firstIndexCell.nativeElement.classList.contains(FIRST_PINNED_CELL_CSS)).toBe(true);
448+
449+
const lastIndexCell = grid.getCellByColumn(0, 'ContactName');
450+
expect(lastIndexCell.visibleColumnIndex).toEqual(secondPinnedIndex);
451+
452+
const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
453+
454+
expect(headers[headers.length - 2].context.column.field).toEqual('CompanyName');
455+
456+
expect(headers[headers.length - 1].context.column.field).toEqual('ContactName');
457+
// expect(headers[secondPinnedIndex].parent.nativeElement.classList.contains(FIXED_HEADER_CSS)).toBe(true);
458+
459+
// verify container widths
460+
expect(grid.pinnedWidth).toEqual(400);
461+
expect(grid.unpinnedWidth + grid.scrollWidth).toEqual(400);
462+
}));
463+
});
464+
415465
/* tslint:disable */
416466
const companyData = [
417467
{ "ID": "ALFKI", "CompanyName": "Alfreds Futterkiste", "ContactName": "Maria Anders", "ContactTitle": "Sales Representative", "Address": "Obere Str. 57", "City": "Berlin", "Region": null, "PostalCode": "12209", "Country": "Germany", "Phone": "030-0074321", "Fax": "030-0076545" },
@@ -745,3 +795,37 @@ export class GridInitialPinningComponent {
745795
@ViewChild(IgxGridComponent, { read: IgxGridComponent, static: true })
746796
public instance: IgxGridComponent;
747797
}
798+
799+
@Component({
800+
template: `
801+
<igx-grid
802+
[pinning]='pinningConfig'
803+
[width]='"800px"'
804+
[height]='"300px"'
805+
[data]="data"
806+
(onColumnInit)="initColumns($event)"
807+
(onSelection)="cellSelected($event)"
808+
[autoGenerate]="true">
809+
</igx-grid>
810+
`
811+
})
812+
export class GridRightPinningComponent {
813+
public selectedCell;
814+
815+
public data = companyData;
816+
public pinningConfig: IPinningConfig = { columns: ColumnPinningPosition.End };
817+
818+
@ViewChild(IgxGridComponent, { read: IgxGridComponent, static: true })
819+
public instance: IgxGridComponent;
820+
821+
public initColumns(column: IgxColumnComponent) {
822+
if (column.field === 'CompanyName' || column.field === 'ContactName') {
823+
column.pinned = true;
824+
}
825+
column.width = '200px';
826+
}
827+
828+
public cellSelected(event: IGridCellEventArgs) {
829+
this.selectedCell = event.cell;
830+
}
831+
}

projects/igniteui-angular/src/lib/grids/headers/grid-header-group.component.ts

+8
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ export class IgxGridHeaderGroupComponent implements DoCheck {
122122
const classList = {
123123
'igx-grid__th--pinned': this.isPinned,
124124
'igx-grid__th--pinned-last': this.isLastPinned,
125+
'igx-grid__th--pinned-first': this.isFirstPinned,
125126
'igx-grid__drag-col-header': this.isHeaderDragged,
126127
'igx-grid__th--filtering': this.isFiltered
127128
};
@@ -169,6 +170,13 @@ export class IgxGridHeaderGroupComponent implements DoCheck {
169170
return !this.grid.hasColumnLayouts ? this.column.isLastPinned : false;
170171
}
171172

173+
/**
174+
* Gets whether the header group is stored in the first column of the right pinned area.
175+
*/
176+
get isFirstPinned(): boolean {
177+
return !this.grid.hasColumnLayouts ? this.column.isFirstPinned : false;
178+
}
179+
172180
@HostBinding('style.display')
173181
get groupDisplayStyle(): string {
174182
return this.grid.hasColumnLayouts && this.column.children && !isIE() ? 'flex' : '';

src/app/grid-column-pinning/grid-column-pinning.sample.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
</app-page-header>
55
<div class="sample-content">
66
<div class="sample-column">
7-
<igx-grid #grid1 [data]="data" [width]="'1200px'" [height]="'800px'" [rowSelectable]="true">
7+
<igx-grid [pinning]="pinningConfig" #grid1 [data]="data" [width]="'800px'" [height]="'800px'" [rowSelectable]="true">
88
<igx-column *ngFor="let c of columns" [sortable]="true" [field]="c.field" [header]="c.field" [width]="c.width" [pinned]='c.pinned'
99
[hidden]='c.hidden'>
1010
</igx-column>

0 commit comments

Comments
 (0)