diff --git a/CHANGELOG.md b/CHANGELOG.md index 461c12a95c7..fe3e35f36d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,8 @@ All notable changes for each version of this project will be documented in this - Exposed new input `buttonText` which sets the text that is displayed inside the dropdown button in the toolbar. - `IgxCombo` - Added `groupSortingDirection` input, which allows you to set groups sorting order. +- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid` + - Added new directives for re-templating header sorting indicators - `IgxSortHeaderIconDirective`, `IgxSortAscendingHeaderIconDirective` and `IgxSortDescendingHeaderIconDirective`. - `IgxDialog` - Added `focusTrap` input to set whether the Tab key focus is trapped within the dialog when opened. Defaults to `true`. diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss index 6fe42c2dfe9..e1a9fee5710 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss @@ -1603,11 +1603,14 @@ } .sort-icon { - width: rem(15px); - height: rem(15px); - min-width: rem(15px); /* yeah IE, it really needs to be 15px wide... */ - font-size: rem(15px); position: relative; + display: flex; + + igx-icon { + width: rem(15px); + height: rem(15px); + font-size: rem(15px); + } &::after { content: attr(data-sortIndex); diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index c8ede753788..dd934e3f1c1 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -93,8 +93,9 @@ import { CharSeparatedValueData } from '../services/csv/char-separated-value-dat import { IgxColumnResizingService } from './resizing/resizing.service'; import { IFilteringStrategy } from '../data-operations/filtering-strategy'; import { - IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, - IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective + IgxRowExpandedIndicatorDirective, IgxRowCollapsedIndicatorDirective, IgxHeaderExpandIndicatorDirective, + IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective, IgxSortAscendingHeaderIconDirective, + IgxSortDescendingHeaderIconDirective, IgxSortHeaderIconDirective } from './grid/grid.directives'; import { GridKeydownTargetType, @@ -765,8 +766,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * * ``` */ - @Output() - public rowAdd = new EventEmitter(); + @Output() + public rowAdd = new EventEmitter(); /** * Emitted after column is resized. @@ -1252,6 +1253,24 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements @ContentChild(IgxExcelStyleHeaderIconDirective, { read: TemplateRef }) public excelStyleHeaderIconTemplate: TemplateRef = null; + /** + * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in asc order. + */ + @ContentChild(IgxSortAscendingHeaderIconDirective, { read: TemplateRef }) + public sortAscendingHeaderIconTemplate: TemplateRef = null; + + /** + * The custom template, if any, that should be used when rendering a header sorting indicator when columns are sorted in desc order. + */ + @ContentChild(IgxSortDescendingHeaderIconDirective, { read: TemplateRef }) + public sortDescendingHeaderIconTemplate: TemplateRef = null; + + /** + * The custom template, if any, that should be used when rendering a header sorting indicator when columns are not sorted. + */ + @ContentChild(IgxSortHeaderIconDirective, { read: TemplateRef }) + public sortHeaderIconTemplate: TemplateRef = null; + /** * @hidden * @internal @@ -3437,26 +3456,26 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements public setUpPaginator() { if (this.paginator) { this.paginator.pageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init)) - .subscribe((page: number) => { - this.pageChange.emit(page); - }); + .subscribe((page: number) => { + this.pageChange.emit(page); + }); this.paginator.pagingDone.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init)) - .subscribe((args: IPageEventArgs) => { - this.selectionService.clear(true); - this.pagingDone.emit({ previous: args.previous, current: args.current }); - this.crudService.endEdit(false); - this.pipeTrigger++; - this.navigateTo(0); - this.notifyChanges(); - }); + .subscribe((args: IPageEventArgs) => { + this.selectionService.clear(true); + this.pagingDone.emit({ previous: args.previous, current: args.current }); + this.crudService.endEdit(false); + this.pipeTrigger++; + this.navigateTo(0); + this.notifyChanges(); + }); this.paginator.perPageChange.pipe(takeWhile(() => !!this.paginator), filter(() => !this._init)) - .subscribe((perPage: number) => { - this.selectionService.clear(true); - this.perPageChange.emit(perPage); - this.paginator.page = 0; - this.crudService.endEdit(false); - this.notifyChanges(); - }); + .subscribe((perPage: number) => { + this.selectionService.clear(true); + this.perPageChange.emit(perPage); + this.paginator.page = 0; + this.crudService.endEdit(false); + this.notifyChanges(); + }); } else { this.markForCheck(); } @@ -3927,7 +3946,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements * ``` */ public get columnsCollection(): IgxColumnComponent[] { - return this._rendered ? this._columns : []; + return this._rendered ? this._columns : []; } /** @@ -5479,7 +5498,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements const selectedColumns = this.gridAPI.grid.selectedColumns(); const columnData = this.getSelectedColumnsData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders); let selectedData; - if (event.type === 'copy'){ + if (event.type === 'copy') { selectedData = this.getSelectedData(this.clipboardOptions.copyFormatters, this.clipboardOptions.copyHeaders); }; @@ -5981,8 +6000,8 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements protected subscribeToTransactions(): void { this.transactionChange$.next(); - this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$,this.transactionChange$))) - .subscribe(this.transactionStatusUpdate.bind(this)); + this.transactions.onStateUpdate.pipe(takeUntil(merge(this.destroy$, this.transactionChange$))) + .subscribe(this.transactionStatusUpdate.bind(this)); } protected transactionStatusUpdate(event: StateUpdateEvent) { @@ -6742,10 +6761,10 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements let selectionMap; if (this.nativeElement.tagName.toLowerCase() === 'igx-hierarchical-grid' && selectionCollection.size > 0) { selectionMap = isRemote ? Array.from(selectionCollection) : - Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length); - }else { + Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length); + } else { selectionMap = isRemote ? Array.from(this.selectionService.selection) : - Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length); + Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length); } if (this.cellSelection === GridSelectionMode.single && activeEl) { diff --git a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts index fd86432dee7..ac021753854 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-common.module.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-common.module.ts @@ -29,6 +29,9 @@ import { IgxGridFilteringModule } from './filtering/base/filtering.module'; import { IgxRowDirective } from './row.directive'; import { IgxExcelStyleHeaderIconDirective, + IgxSortAscendingHeaderIconDirective, + IgxSortDescendingHeaderIconDirective, + IgxSortHeaderIconDirective, IgxGroupAreaDropDirective, IgxHeaderCollapseIndicatorDirective, IgxHeaderExpandIndicatorDirective, @@ -58,6 +61,9 @@ import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive'; IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective, + IgxSortAscendingHeaderIconDirective, + IgxSortDescendingHeaderIconDirective, + IgxSortHeaderIconDirective, IgxGroupAreaDropDirective, IgxGroupByMetaPipe ], @@ -93,6 +99,9 @@ import { IgxGroupByMetaPipe } from './grouping/group-by-area.directive'; IgxHeaderExpandIndicatorDirective, IgxHeaderCollapseIndicatorDirective, IgxExcelStyleHeaderIconDirective, + IgxSortAscendingHeaderIconDirective, + IgxSortDescendingHeaderIconDirective, + IgxSortHeaderIconDirective, IgxGroupAreaDropDirective, IgxGroupByMetaPipe ], diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.directives.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.directives.ts index 7935aac5c48..2dce1656dfc 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.directives.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.directives.ts @@ -72,6 +72,33 @@ export class IgxHeaderCollapseIndicatorDirective { export class IgxExcelStyleHeaderIconDirective { } +/** + * @hidden + */ + @Directive({ + selector: '[igxSortHeaderIcon]' +}) +export class IgxSortHeaderIconDirective { +} + +/** + * @hidden + */ +@Directive({ + selector: '[igxSortAscendingHeaderIcon]' +}) +export class IgxSortAscendingHeaderIconDirective { +} + +/** + * @hidden + */ +@Directive({ + selector: '[igxSortDescendingHeaderIcon]' +}) +export class IgxSortDescendingHeaderIconDirective { +} + /** * @hidden */ diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.sorting.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.sorting.spec.ts index c995a76b912..08810f15587 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.sorting.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.sorting.spec.ts @@ -549,5 +549,26 @@ describe('IgxGrid - Grid Sorting #grid', () => { expect(grid.sorting.emit).toHaveBeenCalledTimes(2); expect(grid.sortingDone.emit).toHaveBeenCalledTimes(2); })); + + it('Should allow setting custom templates for header sorting none/ascending/descending icons.', () => { + fixture = TestBed.createComponent(SortByParityComponent); + fixture.detectChanges(); + grid = fixture.componentInstance.grid; + const fieldName = 'Name'; + const header = GridFunctions.getColumnHeader(fieldName, fixture, grid); + let icon = GridFunctions.getHeaderSortIcon(header); + + expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('unfold_more'); + + grid.sort({ fieldName, dir: SortingDirection.Asc, ignoreCase: false }); + fixture.detectChanges(); + icon = GridFunctions.getHeaderSortIcon(header); + expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('expand_less'); + + grid.sort({ fieldName, dir: SortingDirection.Desc, ignoreCase: false }); + fixture.detectChanges(); + icon = GridFunctions.getHeaderSortIcon(header); + expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('expand_more'); + }); }); }); diff --git a/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.html b/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.html index 146f92e8aad..f660f47979b 100644 --- a/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.html +++ b/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.html @@ -6,24 +6,28 @@ more_vert + + {{ sortDirection < 2 ? 'arrow_upward' : 'arrow_downward' }} + + - +
- - {{ sortDirection < 2 ? 'arrow_upward' : 'arrow_downward' }} - +
+ +
-
+
-
+ \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.ts b/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.ts index 07b83d68f42..ed68d5af856 100644 --- a/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.ts +++ b/projects/igniteui-angular/src/lib/grids/headers/grid-header.component.ts @@ -44,6 +44,12 @@ export class IgxGridHeaderComponent implements DoCheck, OnDestroy { @ViewChild('defaultESFHeaderIconTemplate', { read: TemplateRef, static: true }) protected defaultESFHeaderIconTemplate: TemplateRef; + /** + * @hidden + */ + @ViewChild('defaultSortHeaderIconTemplate', { read: TemplateRef, static: true }) + protected defaultSortHeaderIconTemplate; + /** * Returns the `aria-selected` of the header. */ @@ -131,6 +137,21 @@ export class IgxGridHeaderComponent implements DoCheck, OnDestroy { return this.grid.excelStyleHeaderIconTemplate || this.defaultESFHeaderIconTemplate; } + /** + * @hidden + */ + public get sortIconTemplate() { + if (this.sortDirection === SortingDirection.None && this.grid.sortHeaderIconTemplate) { + return this.grid.sortHeaderIconTemplate; + } else if (this.sortDirection === SortingDirection.Asc && this.grid.sortAscendingHeaderIconTemplate) { + return this.grid.sortAscendingHeaderIconTemplate; + } else if (this.sortDirection === SortingDirection.Desc && this.grid.sortDescendingHeaderIconTemplate) { + return this.grid.sortDescendingHeaderIconTemplate; + } else { + return this.defaultSortHeaderIconTemplate; + } + } + public get sorted() { return this.sortDirection !== SortingDirection.None; } diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index 0c25dd36446..fc76d90dc71 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -441,6 +441,9 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti this.headerCollapseIndicatorTemplate = this.rootGrid.headerCollapseIndicatorTemplate; this.headerExpandIndicatorTemplate = this.rootGrid.headerExpandIndicatorTemplate; this.excelStyleHeaderIconTemplate = this.rootGrid.excelStyleHeaderIconTemplate; + this.sortAscendingHeaderIconTemplate = this.rootGrid.sortAscendingHeaderIconTemplate; + this.sortDescendingHeaderIconTemplate = this.rootGrid.sortDescendingHeaderIconTemplate; + this.sortHeaderIconTemplate = this.rootGrid.sortHeaderIconTemplate; this.hasChildrenKey = this.parentIsland ? this.parentIsland.hasChildrenKey || this.rootGrid.hasChildrenKey : this.rootGrid.hasChildrenKey; diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts index 35de81a1d9d..5681573e413 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts @@ -1819,7 +1819,7 @@ export class GridFunctions { public static getColumnGroupHeaderCell(columnField: string, fix: ComponentFixture) { const headerTitle = fix.debugElement.queryAll(By.css(GROUP_HEADER_CLASS)) - .find(header => header.nativeElement.title === columnField); + .find(header => header.nativeElement.title === columnField); return headerTitle.parent; } @@ -1867,7 +1867,7 @@ export class GridFunctions { } public static getHeaderSortIcon(header: DebugElement): DebugElement { - return header.query(By.css(SORT_ICON_CLASS)); + return header.query(By.css(SORT_ICON_CLASS))?.query(By.css('igx-icon')); } public static getHeaderFilterIcon(header: DebugElement): DebugElement { diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts index 775c8d7577a..9d8e441a11f 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts @@ -7,7 +7,7 @@ import { } from './grid-base-components.spec'; import { IGridSelection } from './grid-interfaces.spec'; import { SampleTestData, DataParent } from './sample-test-data.spec'; -import { ColumnDefinitions, GridTemplateStrings, EventSubscriptions } from './template-strings.spec'; +import { ColumnDefinitions, GridTemplateStrings, EventSubscriptions, TemplateDefinitions } from './template-strings.spec'; import { IgxColumnComponent } from '../grids/columns/column.component'; import { IgxFilteringOperand, IgxNumberFilteringOperand } from '../data-operations/filtering-condition'; import { ExpressionUI } from '../grids/filtering/grid-filtering.service'; @@ -2276,7 +2276,10 @@ export class NoColumnWidthGridComponent extends BasicGridComponent { template: GridTemplateStrings.declareGrid( '', '', - ColumnDefinitions.idFirstLastNameSortable) + ColumnDefinitions.idFirstLastNameSortable, + '', + '', + TemplateDefinitions.sortIconTemplates) }) export class SortByParityComponent extends GridDeclaredColumnsComponent implements ISortingStrategy { public sort(data: any[], fieldName: string, dir: SortingDirection) { diff --git a/projects/igniteui-angular/src/lib/test-utils/template-strings.spec.ts b/projects/igniteui-angular/src/lib/test-utils/template-strings.spec.ts index 0af0edf9b6c..a0253b287fc 100644 --- a/projects/igniteui-angular/src/lib/test-utils/template-strings.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/template-strings.spec.ts @@ -19,14 +19,15 @@ export class GridTemplateStrings { `; public static declareGrid(attributes = ``, events = ``, columnDefinitions: ColumnDefinitions = ``, - toolbarDefinition = '', paginatorDefinition = '') { + toolbarDefinition = '', paginatorDefinition = '', templateDefinitions: TemplateDefinitions = '') { return ` - ${ toolbarDefinition } - ${ columnDefinitions} - ${ paginatorDefinition } + ${toolbarDefinition} + ${columnDefinitions} + ${paginatorDefinition} + ${templateDefinitions} `; } @@ -489,6 +490,20 @@ export class ColumnDefinitions { `; } +export class TemplateDefinitions { + public static sortIconTemplates = ` + + unfold_more + + + expand_less + + + expand_more + + `; +} + export class EventSubscriptions { public static columnInit = ` (columnInit)="columnInit($event)"`;