Skip to content

Commit f91a32f

Browse files
authored
Merge pull request #10394 from IgniteUI/vkombov/feat-10217-master
feat(grid): add new directives for re-templating header sorting indicators
2 parents 0d0499b + 8322470 commit f91a32f

File tree

12 files changed

+179
-52
lines changed

12 files changed

+179
-52
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ All notable changes for each version of this project will be documented in this
4848
- Exposed new input `buttonText` which sets the text that is displayed inside the dropdown button in the toolbar.
4949
- `IgxCombo`
5050
- Added `groupSortingDirection` input, which allows you to set groups sorting order.
51+
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
52+
- Added new directives for re-templating header sorting indicators - `IgxSortHeaderIconDirective`, `IgxSortAscendingHeaderIconDirective` and `IgxSortDescendingHeaderIconDirective`.
5153
- `IgxDialog`
5254
- Added `focusTrap` input to set whether the Tab key focus is trapped within the dialog when opened. Defaults to `true`.
5355

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,11 +1603,14 @@
16031603
}
16041604

16051605
.sort-icon {
1606-
width: rem(15px);
1607-
height: rem(15px);
1608-
min-width: rem(15px); /* yeah IE, it really needs to be 15px wide... */
1609-
font-size: rem(15px);
16101606
position: relative;
1607+
display: flex;
1608+
1609+
igx-icon {
1610+
width: rem(15px);
1611+
height: rem(15px);
1612+
font-size: rem(15px);
1613+
}
16111614

16121615
&::after {
16131616
content: attr(data-sortIndex);

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

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@ import { CharSeparatedValueData } from '../services/csv/char-separated-value-dat
9393
import { IgxColumnResizingService } from './resizing/resizing.service';
9494
import { IFilteringStrategy } from '../data-operations/filtering-strategy';
9595
import {
96-
IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective,
97-
IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective
96+
IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandIndicatorDirective,
97+
IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective,
98+
IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective
9899
} from './grid/grid.directives';
99100
import {
100101
GridKeydownTargetType,
@@ -765,8 +766,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
765766
* <igx-grid #grid [data]="localData" (rowAdd)="rowAdd($event)" [height]="'305px'" [autoGenerate]="true"></igx-grid>
766767
* ```
767768
*/
768-
@Output()
769-
public rowAdd = new EventEmitter<IGridEditEventArgs>();
769+
@Output()
770+
public rowAdd = new EventEmitter<IGridEditEventArgs>();
770771

771772
/**
772773
* Emitted after column is resized.
@@ -1252,6 +1253,24 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
12521253
@ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef })
12531254
public excelStyleHeaderIconTemplate: TemplateRef<any> = null;
12541255

1256+
/**
1257+
* The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order.
1258+
*/
1259+
@ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef })
1260+
public sortAscendingHeaderIconTemplate: TemplateRef<any> = null;
1261+
1262+
/**
1263+
* The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order.
1264+
*/
1265+
@ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef })
1266+
public sortDescendingHeaderIconTemplate: TemplateRef<any> = null;
1267+
1268+
/**
1269+
* The custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted.
1270+
*/
1271+
@ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef })
1272+
public sortHeaderIconTemplate: TemplateRef<any> = null;
1273+
12551274
/**
12561275
* @hidden
12571276
* @internal
@@ -3437,26 +3456,26 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
34373456
public setUpPaginator() {
34383457
if (this.paginator) {
34393458
this.paginator.pageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
3440-
.subscribe((page: number) => {
3441-
this.pageChange.emit(page);
3442-
});
3459+
.subscribe((page: number) => {
3460+
this.pageChange.emit(page);
3461+
});
34433462
this.paginator.pagingDone.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
3444-
.subscribe((args: IPageEventArgs) => {
3445-
this.selectionService.clear(true);
3446-
this.pagingDone.emit({ previous: args.previous, current: args.current });
3447-
this.crudService.endEdit(false);
3448-
this.pipeTrigger++;
3449-
this.navigateTo(0);
3450-
this.notifyChanges();
3451-
});
3463+
.subscribe((args: IPageEventArgs) => {
3464+
this.selectionService.clear(true);
3465+
this.pagingDone.emit({ previous: args.previous, current: args.current });
3466+
this.crudService.endEdit(false);
3467+
this.pipeTrigger++;
3468+
this.navigateTo(0);
3469+
this.notifyChanges();
3470+
});
34523471
this.paginator.perPageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init))
3453-
.subscribe((perPage: number) => {
3454-
this.selectionService.clear(true);
3455-
this.perPageChange.emit(perPage);
3456-
this.paginator.page = 0;
3457-
this.crudService.endEdit(false);
3458-
this.notifyChanges();
3459-
});
3472+
.subscribe((perPage: number) => {
3473+
this.selectionService.clear(true);
3474+
this.perPageChange.emit(perPage);
3475+
this.paginator.page = 0;
3476+
this.crudService.endEdit(false);
3477+
this.notifyChanges();
3478+
});
34603479
} else {
34613480
this.markForCheck();
34623481
}
@@ -3927,7 +3946,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
39273946
* ```
39283947
*/
39293948
public get columnsCollection(): IgxColumnComponent[] {
3930-
return this._rendered ? this._columns : [];
3949+
return this._rendered ? this._columns : [];
39313950
}
39323951

39333952
/**
@@ -5479,7 +5498,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
54795498
const selectedColumns = this.gridAPI.grid.selectedColumns();
54805499
const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
54815500
let selectedData;
5482-
if (event.type === 'copy'){
5501+
if (event.type === 'copy') {
54835502
selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders);
54845503
};
54855504

@@ -5981,8 +6000,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
59816000

59826001
protected subscribeToTransactions(): void {
59836002
this.transactionChange$.next();
5984-
this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$,this.transactionChange$)))
5985-
.subscribe(this.transactionStatusUpdate.bind(this));
6003+
this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$)))
6004+
.subscribe(this.transactionStatusUpdate.bind(this));
59866005
}
59876006

59886007
protected transactionStatusUpdate(event: StateUpdateEvent) {
@@ -6742,10 +6761,10 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
67426761
let selectionMap;
67436762
if (this.nativeElement.tagName.toLowerCase() === 'igx-hierarchical-grid' && selectionCollection.size > 0) {
67446763
selectionMap = isRemote ? Array.from(selectionCollection) :
6745-
Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
6746-
}else {
6764+
Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length);
6765+
} else {
67476766
selectionMap = isRemote ? Array.from(this.selectionService.selection) :
6748-
Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
6767+
Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length);
67496768
}
67506769

67516770
if (this.cellSelection === GridSelectionMode.single && activeEl) {

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import { IgxGridFilteringModule } from './filtering/base/filtering.module';
2929
import { IgxRowDirective } from './row.directive';
3030
import {
3131
IgxExcelStyleHeaderIconDirective,
32+
IgxSortAscendingHeaderIconDirective,
33+
IgxSortDescendingHeaderIconDirective,
34+
IgxSortHeaderIconDirective,
3235
IgxGroupAreaDropDirective,
3336
IgxHeaderCollapseIndicatorDirective,
3437
IgxHeaderExpandIndicatorDirective,
@@ -58,6 +61,9 @@ import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive';
5861
IgxHeaderExpandIndicatorDirective,
5962
IgxHeaderCollapseIndicatorDirective,
6063
IgxExcelStyleHeaderIconDirective,
64+
IgxSortAscendingHeaderIconDirective,
65+
IgxSortDescendingHeaderIconDirective,
66+
IgxSortHeaderIconDirective,
6167
IgxGroupAreaDropDirective,
6268
IgxGroupByMetaPipe
6369
],
@@ -93,6 +99,9 @@ import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive';
9399
IgxHeaderExpandIndicatorDirective,
94100
IgxHeaderCollapseIndicatorDirective,
95101
IgxExcelStyleHeaderIconDirective,
102+
IgxSortAscendingHeaderIconDirective,
103+
IgxSortDescendingHeaderIconDirective,
104+
IgxSortHeaderIconDirective,
96105
IgxGroupAreaDropDirective,
97106
IgxGroupByMetaPipe
98107
],

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,33 @@ export class IgxHeaderCollapseIndicatorDirective {
7272
export class IgxExcelStyleHeaderIconDirective {
7373
}
7474

75+
/**
76+
* @hidden
77+
*/
78+
@Directive({
79+
selector: '[igxSortHeaderIcon]'
80+
})
81+
export class IgxSortHeaderIconDirective {
82+
}
83+
84+
/**
85+
* @hidden
86+
*/
87+
@Directive({
88+
selector: '[igxSortAscendingHeaderIcon]'
89+
})
90+
export class IgxSortAscendingHeaderIconDirective {
91+
}
92+
93+
/**
94+
* @hidden
95+
*/
96+
@Directive({
97+
selector: '[igxSortDescendingHeaderIcon]'
98+
})
99+
export class IgxSortDescendingHeaderIconDirective {
100+
}
101+
75102
/**
76103
* @hidden
77104
*/

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,5 +549,26 @@ describe('IgxGrid - Grid Sorting #grid', () => {
549549
expect(grid.sorting.emit).toHaveBeenCalledTimes(2);
550550
expect(grid.sortingDone.emit).toHaveBeenCalledTimes(2);
551551
}));
552+
553+
it('Should allow setting custom templates for header sorting none/ascending/descending icons.', () => {
554+
fixture = TestBed.createComponent(SortByParityComponent);
555+
fixture.detectChanges();
556+
grid = fixture.componentInstance.grid;
557+
const fieldName = 'Name';
558+
const header = GridFunctions.getColumnHeader(fieldName, fixture, grid);
559+
let icon = GridFunctions.getHeaderSortIcon(header);
560+
561+
expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('unfold_more');
562+
563+
grid.sort({ fieldName, dir: SortingDirection.Asc, ignoreCase: false });
564+
fixture.detectChanges();
565+
icon = GridFunctions.getHeaderSortIcon(header);
566+
expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('expand_less');
567+
568+
grid.sort({ fieldName, dir: SortingDirection.Desc, ignoreCase: false });
569+
fixture.detectChanges();
570+
icon = GridFunctions.getHeaderSortIcon(header);
571+
expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('expand_more');
572+
});
552573
});
553574
});

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,28 @@
66
<igx-icon>more_vert</igx-icon>
77
</ng-template>
88

9+
<ng-template #defaultSortHeaderIconTemplate>
10+
<igx-icon>{{ sortDirection < 2 ? 'arrow_upward' : 'arrow_downward' }}</igx-icon>
11+
</ng-template>
12+
913
<span class="igx-grid-th__title">
10-
<ng-container *ngTemplateOutlet="column.headerTemplate ? column.headerTemplate : defaultColumn; context: { $implicit: column, column: column}">
14+
<ng-container
15+
*ngTemplateOutlet="column.headerTemplate ? column.headerTemplate : defaultColumn; context: { $implicit: column, column: column}">
1116
</ng-container>
1217
</span>
1318
<ng-container *ngIf="!column.columnGroup">
1419
<div class="igx-grid-th__icons">
1520
<ng-container *ngIf="column.sortable">
16-
<igx-icon class="sort-icon"
17-
[attr.draggable]="false"
18-
[attr.data-sortIndex]="column.field | sortingIndex:grid.sortingExpressions"
19-
(click)="onSortingIconClick($event)">
20-
{{ sortDirection < 2 ? 'arrow_upward' : 'arrow_downward' }}
21-
</igx-icon>
21+
<div class="sort-icon" [attr.data-sortIndex]="column.field | sortingIndex:grid.sortingExpressions"
22+
[attr.draggable]="false" (click)="onSortingIconClick($event)" (pointerdown)="$event.stopPropagation()">
23+
<ng-container *ngTemplateOutlet="sortIconTemplate; context: { $implicit: this }"></ng-container>
24+
</div>
2225
</ng-container>
2326
<ng-container *ngIf="grid.allowFiltering && column.filterable && grid.filterMode === 'excelStyleFilter'">
24-
<div [ngClass]="filterIconClassName" (click)="onFilteringIconClick($event)" (pointerdown)="$event.stopPropagation()">
27+
<div [ngClass]="filterIconClassName" (click)="onFilteringIconClick($event)"
28+
(pointerdown)="$event.stopPropagation()">
2529
<ng-container *ngTemplateOutlet="esfIconTemplate; context: { $implicit: this }"></ng-container>
2630
</div>
2731
</ng-container>
2832
</div>
29-
</ng-container>
33+
</ng-container>

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export class IgxGridHeaderComponent implements DoCheck, OnDestroy {
4444
@ViewChild('defaultESFHeaderIconTemplate', { read: TemplateRef, static: true })
4545
protected defaultESFHeaderIconTemplate: TemplateRef<any>;
4646

47+
/**
48+
* @hidden
49+
*/
50+
@ViewChild('defaultSortHeaderIconTemplate', { read: TemplateRef, static: true })
51+
protected defaultSortHeaderIconTemplate;
52+
4753
/**
4854
* Returns the `aria-selected` of the header.
4955
*/
@@ -131,6 +137,21 @@ export class IgxGridHeaderComponent implements DoCheck, OnDestroy {
131137
return this.grid.excelStyleHeaderIconTemplate || this.defaultESFHeaderIconTemplate;
132138
}
133139

140+
/**
141+
* @hidden
142+
*/
143+
public get sortIconTemplate() {
144+
if (this.sortDirection === SortingDirection.None && this.grid.sortHeaderIconTemplate) {
145+
return this.grid.sortHeaderIconTemplate;
146+
} else if (this.sortDirection === SortingDirection.Asc && this.grid.sortAscendingHeaderIconTemplate) {
147+
return this.grid.sortAscendingHeaderIconTemplate;
148+
} else if (this.sortDirection === SortingDirection.Desc && this.grid.sortDescendingHeaderIconTemplate) {
149+
return this.grid.sortDescendingHeaderIconTemplate;
150+
} else {
151+
return this.defaultSortHeaderIconTemplate;
152+
}
153+
}
154+
134155
public get sorted() {
135156
return this.sortDirection !== SortingDirection.None;
136157
}

projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,9 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
441441
this.headerCollapseIndicatorTemplate = this.rootGrid.headerCollapseIndicatorTemplate;
442442
this.headerExpandIndicatorTemplate = this.rootGrid.headerExpandIndicatorTemplate;
443443
this.excelStyleHeaderIconTemplate = this.rootGrid.excelStyleHeaderIconTemplate;
444+
this.sortAscendingHeaderIconTemplate = this.rootGrid.sortAscendingHeaderIconTemplate;
445+
this.sortDescendingHeaderIconTemplate = this.rootGrid.sortDescendingHeaderIconTemplate;
446+
this.sortHeaderIconTemplate = this.rootGrid.sortHeaderIconTemplate;
444447
this.hasChildrenKey = this.parentIsland ?
445448
this.parentIsland.hasChildrenKey || this.rootGrid.hasChildrenKey :
446449
this.rootGrid.hasChildrenKey;

projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,7 +1819,7 @@ export class GridFunctions {
18191819

18201820
public static getColumnGroupHeaderCell(columnField: string, fix: ComponentFixture<any>) {
18211821
const headerTitle = fix.debugElement.queryAll(By.css(GROUP_HEADER_CLASS))
1822-
.find(header => header.nativeElement.title === columnField);
1822+
.find(header => header.nativeElement.title === columnField);
18231823
return headerTitle.parent;
18241824
}
18251825

@@ -1867,7 +1867,7 @@ export class GridFunctions {
18671867
}
18681868

18691869
public static getHeaderSortIcon(header: DebugElement): DebugElement {
1870-
return header.query(By.css(SORT_ICON_CLASS));
1870+
return header.query(By.css(SORT_ICON_CLASS))?.query(By.css('igx-icon'));
18711871
}
18721872

18731873
public static getHeaderFilterIcon(header: DebugElement): DebugElement {

0 commit comments

Comments
 (0)