Skip to content

Separate grouping and sorting #11355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { ITreeGridRecord } from '../tree-grid/tree-grid.interfaces';
import { State, Transaction, TransactionService } from '../../services/transaction/transaction';
import { GridColumnDataType } from '../../data-operations/data-util';
import { IgxFilteringOperand } from '../../data-operations/filtering-condition';
import { IColumnPipeArgs, MRLResizeColumnInfo } from '../columns/interfaces';
import { IColumnPipeArgs, ISortingOptions, MRLResizeColumnInfo } from '../columns/interfaces';
import { IgxSummaryResult } from '../summaries/grid-summary';
import { ISortingExpression, ISortingStrategy, SortingDirection } from '../../data-operations/sorting-strategy';
import { IGridGroupingStrategy, IGridSortingStrategy } from './strategy';
Expand Down Expand Up @@ -444,6 +444,7 @@ export interface GridType extends IGridDataBindable {
records?: Map<any, ITreeGridRecord>;
processedExpandedFlatData?: any[] | null;
processedRecords?: Map<any, ITreeGridRecord>;
treeGroupArea?: any;

activeNodeChange: EventEmitter<IActiveNodeChangeEventArgs>;
gridKeydown: EventEmitter<IGridKeydownEventArgs>;
Expand Down Expand Up @@ -499,6 +500,7 @@ export interface GridType extends IGridDataBindable {
filteringExpressionsTreeChange: EventEmitter<IFilteringExpressionsTree>;
advancedFilteringExpressionsTree: IFilteringExpressionsTree;
advancedFilteringExpressionsTreeChange: EventEmitter<IFilteringExpressionsTree>;
sortingOptions: ISortingOptions;

batchEditing: boolean;
groupingExpansionState?: IGroupByExpandState[];
Expand Down Expand Up @@ -606,7 +608,6 @@ export interface FlatGridType extends GridType {
toggleGroup(groupRow: IGroupByRecord): void;
clearGrouping(field: string): void;
groupBy(expression: IGroupingExpression | Array<IGroupingExpression>): void;
sortGrouping(expression: IGroupingExpression | Array<IGroupingExpression>): void;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ import { ConnectedPositioningStrategy } from '../services/overlay/position/conne
import { ContainerPositionStrategy } from '../services/overlay/position/container-position-strategy';
import { AbsoluteScrollStrategy } from '../services/overlay/scroll/absolute-scroll-strategy';
import { Action, StateUpdateEvent, TransactionEventOrigin } from '../services/transaction/transaction';
import { ISortingExpression, SortingDirection } from '../data-operations/sorting-strategy';
import { ISortingExpression } from '../data-operations/sorting-strategy';
import { IGridSortingStrategy } from './common/strategy';
import { IgxGridExcelStyleFilteringComponent } from './filtering/excel-style/grid.excel-style-filtering.component';
import { IgxGridHeaderComponent } from './headers/grid-header.component';
Expand Down Expand Up @@ -4543,15 +4543,10 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements

if (expression instanceof Array) {
for (const each of expression) {
if (each.dir === SortingDirection.None) {
this.gridAPI.remove_grouping_expression(each.fieldName);
}
this.gridAPI.prepare_sorting_expression([sortingState], each);
}
} else {
if (expression.dir === SortingDirection.None) {
this.gridAPI.remove_grouping_expression(expression.fieldName);
} else if (this._sortingOptions.mode === 'single') {
if (this._sortingOptions.mode === 'single') {
this.columns.forEach((col) => {
if (!(col.field === expression.fieldName)) {
this.clearSort(col.field);
Expand Down
29 changes: 7 additions & 22 deletions projects/igniteui-angular/src/lib/grids/grid/grid-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,11 @@ export class IgxGridAPIService extends GridBaseAPIService<GridType> implements G

public clear_groupby(name?: string | Array<string>) {
const groupingState = cloneArray(this.grid.groupingExpressions);
const sortingState = cloneArray(this.grid.sortingExpressions);

if (name) {
const names = typeof name === 'string' ? [name] : name;
const groupedCols = groupingState.filter((state) => names.indexOf(state.fieldName) < 0);
const newSortingExpr = sortingState.filter((state) => names.indexOf(state.fieldName) < 0);
this.grid.groupingExpressions = groupedCols;
this.grid.sortingExpressions = newSortingExpr;
names.forEach((colName) => {
const grExprIndex = groupingState.findIndex((exp) => exp.fieldName === colName);
const grpExpandState = this.grid.groupingExpansionState;
Expand All @@ -54,13 +51,6 @@ export class IgxGridAPIService extends GridBaseAPIService<GridType> implements G
// clear all
this.grid.groupingExpressions = [];
this.grid.groupingExpansionState = [];
for (const grExpr of groupingState) {
const sortExprIndex = sortingState.findIndex((exp) => exp.fieldName === grExpr.fieldName);
if (sortExprIndex > -1) {
sortingState.splice(sortExprIndex, 1);
}
}
this.grid.sortingExpressions = sortingState;
}
}

Expand Down Expand Up @@ -132,19 +122,14 @@ export class IgxGridAPIService extends GridBaseAPIService<GridType> implements G

public arrange_sorting_expressions() {
const groupingState = this.grid.groupingExpressions;
this.grid.groupingExpressions.sort((a, b) => {
const groupExprA = groupingState.find((expr) => expr.fieldName === a.fieldName);
const groupExprB = groupingState.find((expr) => expr.fieldName === b.fieldName);
if (groupExprA && groupExprB) {
return groupingState.indexOf(groupExprA) > groupingState.indexOf(groupExprB) ? 1 : -1;
} else if (groupExprA) {
return -1;
} else if (groupExprB) {
return 1;
} else {
return 0;
const sortingState = cloneArray(this.grid.sortingExpressions);
for (const grExpr of groupingState) {
const sortExprIndex = sortingState.findIndex((exp) => exp.fieldName === grExpr.fieldName);
if (sortExprIndex > -1) {
sortingState.splice(sortExprIndex, 1);
}
});
}
this.grid.sortingExpressions = sortingState;
}

public get_groupBy_record_id(gRow: IGroupByRecord): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
| gridAddRow:true:pipeTrigger
| gridRowPinning:id:true:pipeTrigger
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger:true
| gridSort:sortingExpressions:sortStrategy:id:pipeTrigger:true as pinnedData'>
| gridSort:sortingExpressions:groupingExpressions:sortStrategy:id:pipeTrigger:true as pinnedData'>
<div #pinContainer *ngIf='pinnedData.length > 0'
[ngClass]="{
'igx-grid__tr--pinned-bottom': !isRowPinningToTop,
Expand All @@ -70,7 +70,7 @@
| gridTransaction:id:pipeTrigger
| visibleColumns:hasVisibleColumns
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger
| gridSort:sortingExpressions:sortStrategy:id:pipeTrigger
| gridSort:sortingExpressions:groupingExpressions:sortStrategy:id:pipeTrigger
| gridGroupBy:groupingExpressions:groupingExpansionState:groupStrategy:groupsExpanded:id:groupsRecords:pipeTrigger
| gridPaging:paginator?.page:paginator?.perPage:id:pipeTrigger
| gridSummary:hasSummarizedColumns:summaryCalculationMode:summaryPosition:id:showSummaryOnCollapse:pipeTrigger:summaryPipeTrigger
Expand Down
10 changes: 3 additions & 7 deletions projects/igniteui-angular/src/lib/grids/grid/grid.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,14 +365,10 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType,
this._groupingExpressions = cloneArray(value);
this.groupingExpressionsChange.emit(this._groupingExpressions);
if (this._gridAPI.grid) {
/* grouping should work in conjunction with sorting
and without overriding separate sorting expressions */
debugger;
/* grouping and sorting are working separate from each other */
this._applyGrouping();
this._gridAPI.arrange_sorting_expressions();
this.notifyChanges();
} else {
// setter called before grid is registered in grid API service
this.sortingExpressions.unshift.apply(this.sortingExpressions, this._groupingExpressions);
}
if (!this._init && JSON.stringify(oldExpressions) !== JSON.stringify(newExpressions) && this.columnList) {
const groupedCols: IgxColumnComponent[] = [];
Expand Down Expand Up @@ -1239,7 +1235,7 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType,
* @hidden @internal
*/
protected _applyGrouping() {
this._gridAPI.sort_multiple(this._groupingExpressions);
this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
}

private _setupNavigationService() {
Expand Down
48 changes: 16 additions & 32 deletions projects/igniteui-angular/src/lib/grids/grid/grid.groupby.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,11 @@ describe('IgxGrid - GroupBy #grid', () => {
const icon = chip.nativeElement.querySelector('[igxsuffix]').textContent.trim();

expect(content).toBe(grouping[index].fieldName);
expect(content).toBe(sorting[index].fieldName);

if (icon === SORTING_ICON_ASC_CONTENT) {
expect(grouping[index].dir).toBe(SortingDirection.Asc);
expect(sorting[index].dir).toBe(SortingDirection.Asc);
} else {
expect(grouping[index].dir).toBe(SortingDirection.Desc);
expect(sorting[index].dir).toBe(SortingDirection.Desc);
}
});
};
Expand Down Expand Up @@ -741,7 +738,7 @@ describe('IgxGrid - GroupBy #grid', () => {

}));

it('should disallow setting sorting state None to grouped column when sorting via the UI.', fakeAsync(() => {
it('should not be able to sort the column when is already grouped by', fakeAsync(() => {
const fix = TestBed.createComponent(DefaultGridComponent);
const grid = fix.componentInstance.instance;
fix.componentInstance.enableSorting = true;
Expand All @@ -752,27 +749,9 @@ describe('IgxGrid - GroupBy #grid', () => {
fix.detectChanges();

const headers = fix.debugElement.queryAll(By.css(COLUMN_HEADER_CLASS));
// click header sort icon
GridFunctions.clickHeaderSortIcon(headers[0]);
tick();
fix.detectChanges();

const sortingIcon = fix.debugElement.query(By.css('.sort-icon'));
expect(sortingIcon.nativeElement.textContent.trim()).toEqual(SORTING_ICON_ASC_CONTENT);

// click header sort icon again
GridFunctions.clickHeaderSortIcon(headers[0]);
tick();
fix.detectChanges();

expect(sortingIcon.nativeElement.textContent.trim()).toEqual(SORTING_ICON_DESC_CONTENT);

// click header sort icon again
GridFunctions.clickHeaderSortIcon(headers[0]);
tick();
fix.detectChanges();
expect(sortingIcon.nativeElement.textContent.trim()).toEqual(SORTING_ICON_ASC_CONTENT);

//header sort icon should not be displayed
const sortIcon = headers[0].query(By.css('.sort-icon'));
expect(sortIcon).toBeNull()
}));

it('should group by the specified field when grouping by an already sorted field.', fakeAsync(() => {
Expand All @@ -783,13 +762,17 @@ describe('IgxGrid - GroupBy #grid', () => {
grid.sort({ fieldName: 'ProductName', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();

expect(grid.sortingExpressions.length).toBe(1);

grid.groupBy({
fieldName: 'ProductName', dir: SortingDirection.Asc, ignoreCase: false
});
fix.detectChanges();
const groupRows = grid.groupsRowList.toArray();
// verify group order
checkGroups(groupRows, [null, '', 'Ignite UI for Angular', 'Ignite UI for JavaScript', 'NetAdvantage']);
expect(grid.sortingExpressions.length).toBe(0);
expect(grid.groupingExpressions.length).toBe(1);
}));

it('should allow grouping of already sorted column', waitForAsync(() => {
Expand Down Expand Up @@ -3090,7 +3073,8 @@ describe('IgxGrid - GroupBy #grid', () => {
[{ fieldName: 'Released', dir: SortingDirection.Asc, ignoreCase: false }];
fix.detectChanges();

expect(grid.sortingExpressions.length).toEqual(2);
//grouping expressions should not affect grouping expressions
expect(grid.sortingExpressions.length).toEqual(1);
expect(grid.groupingExpressions.length).toEqual(1);

const groupRows = grid.groupsRowList.toArray();
Expand Down Expand Up @@ -3312,10 +3296,11 @@ describe('IgxGrid - GroupBy #grid', () => {
fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, strategy: DefaultSortingStrategy.instance()
});
fix.detectChanges();

expect(grid.sortingExpressions).toEqual([
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, strategy: DefaultSortingStrategy.instance() },
{ fieldName: 'Downloads', dir: SortingDirection.Asc, ignoreCase: false, strategy: DefaultSortingStrategy.instance() },
{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false, strategy: DefaultSortingStrategy.instance() }
{ fieldName: 'ID', dir: SortingDirection.Desc, ignoreCase: false, strategy: DefaultSortingStrategy.instance() },
{ fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, strategy: DefaultSortingStrategy.instance() }
]);
expect(grid.groupingExpressions).toEqual([{
fieldName: 'Released', dir: SortingDirection.Desc, ignoreCase: false, strategy: DefaultSortingStrategy.instance()
Expand Down Expand Up @@ -3450,10 +3435,9 @@ describe('IgxGrid - GroupBy #grid', () => {
grid.groupingExpressions = grExprs;
fix.detectChanges();

// check sorting expressions order - grouping should be applied first
expect(grid.sortingExpressions.length).toBe(2);
expect(grid.sortingExpressions[0]).toBe(grExprs[0]);
expect(grid.sortingExpressions[1]).toBe(sExprs[0]);
// check grouping expressions override sorting expressions - grouping should be applied first
expect(grid.sortingExpressions.length).toBe(1);
expect(grid.sortingExpressions[0]).toBe(sExprs[0]);

dataRows = grid.dataRowList.toArray();
const expectedReleaseRecsOrder = [true, false, true, false, false, null, true, true];
Expand Down
4 changes: 2 additions & 2 deletions projects/igniteui-angular/src/lib/grids/grid/grid.pipes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export class IgxGridSortingPipe implements PipeTransform {

constructor(@Inject(IGX_GRID_BASE) private grid: GridType) { }

public transform(collection: any[], expressions: ISortingExpression[], sorting: IGridSortingStrategy,
public transform(collection: any[], sortExpressions: ISortingExpression[], groupExpressions: IGroupingExpression[], sorting: IGridSortingStrategy,
id: string, pipeTrigger: number, pinned?): any[] {
let result: any[];

const expressions = groupExpressions.concat(sortExpressions);
if (!expressions.length) {
result = collection;
} else {
Expand Down
60 changes: 60 additions & 0 deletions projects/igniteui-angular/src/lib/grids/grid/grid.sorting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,5 +570,65 @@ describe('IgxGrid - Grid Sorting #grid', () => {
icon = GridFunctions.getHeaderSortIcon(header);
expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('expand_more');
});

it('Should be able to set single sorting mode and sort one column at a time', fakeAsync(() => {
fixture = TestBed.createComponent(SortByParityComponent);
fixture.detectChanges();
grid = fixture.componentInstance.grid;
const fieldName = 'Name';
const fieldLastName = 'LastName';
const header = GridFunctions.getColumnHeader(fieldName, fixture, grid);
const headerLastName = GridFunctions.getColumnHeader(fieldLastName, fixture, grid);
let icon = GridFunctions.getHeaderSortIcon(header);

grid.sortingOptions = {mode: 'single'};
fixture.detectChanges();
debugger;

expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('unfold_more');

GridFunctions.clickHeaderSortIcon(header);
tick(30);
fixture.detectChanges();

icon = GridFunctions.getHeaderSortIcon(header);
expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('expand_less');

GridFunctions.clickHeaderSortIcon(headerLastName);
tick(30);
fixture.detectChanges();

icon = GridFunctions.getHeaderSortIcon(header);
const iconLastName = GridFunctions.getHeaderSortIcon(headerLastName);
expect(icon.nativeElement.textContent.toLowerCase().trim()).toBe('unfold_more');
expect(iconLastName.nativeElement.textContent.toLowerCase().trim()).toBe('expand_less');
}));


it('should not display sorting index when sorting mode is set to "single"', fakeAsync(() => {
fixture = TestBed.createComponent(SortByParityComponent);
fixture.detectChanges();
grid = fixture.componentInstance.grid;
const fieldName = 'Name';
const fieldLastName = 'LastName';
const header = GridFunctions.getColumnHeader(fieldName, fixture, grid);
const headerLastName = GridFunctions.getColumnHeader(fieldLastName, fixture, grid);

grid.sortingOptions = {mode: 'single'};
fixture.detectChanges();

GridFunctions.clickHeaderSortIcon(header);
tick(30);
fixture.detectChanges();
expect(GridFunctions.getColumnSortingIndex(header)).toBeNull();
expect(grid.sortingExpressions.length).toBe(1);

GridFunctions.clickHeaderSortIcon(headerLastName);
tick(30);
fixture.detectChanges();

expect(GridFunctions.getColumnSortingIndex(headerLastName)).toBeNull();
expect(grid.sortingExpressions.length).toBe(1);
}));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DisplayDensity } from '../../core/displayDensity';
import { PlatformUtil } from '../../core/utils';
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
import { SortingDirection } from '../../data-operations/sorting-strategy';
import { GridType } from '../common/grid.interface';
import { FlatGridType, GridType } from '../common/grid.interface';
import { IgxColumnMovingDragDirective } from '../moving/moving.drag.directive';

/**
Expand Down Expand Up @@ -51,7 +51,7 @@ export abstract class IgxGroupByAreaDirective {

/** The parent grid containing the component. */
@Input()
public grid: GridType;
public grid: FlatGridType | GridType;

/**
* The group-by expressions provided by the parent grid.
Expand Down Expand Up @@ -107,15 +107,15 @@ export abstract class IgxGroupByAreaDirective {

public handleKeyDown(id: string, event: KeyboardEvent) {
if (this.platform.isActivationKey(event)) {
this.updateSorting(id);
this.updateGroupSorting(id);
}
}

public handleClick(id: string) {
if (!this.grid.getColumnByName(id).groupable) {
return;
}
this.updateSorting(id);
this.updateGroupSorting(id);
}

public onDragDrop(event) {
Expand Down Expand Up @@ -158,10 +158,11 @@ export abstract class IgxGroupByAreaDirective {
return newExpressions;
}

protected updateSorting(id: string) {
const expr = this.grid.sortingExpressions.find(e => e.fieldName === id);
protected updateGroupSorting(id: string) {
const expr = this.expressions.find(e => e.fieldName === id);
expr.dir = 3 - expr.dir;
this.grid.sort(expr);
this.grid.pipeTrigger++;
this.grid.notifyChanges();
}

protected expressionsChanged() {
Expand Down
Loading