Skip to content

Commit

Permalink
fix(grid): applying grouping-related changes for sort strategy #2734 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ChronosSF authored and kdinev committed Nov 2, 2018
1 parent 1aa1910 commit 5534016
Show file tree
Hide file tree
Showing 19 changed files with 174 additions and 75 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable changes for each version of this project will be documented in this
### General

- `sortStrategy` input exposed to provide custom sort strategy for the `IgxColumnComponent`. The custom strategy should implement the `ISortingStrategy` interface, or can extend the base `SortingStrategy` class and override all or some of its public/protected members.
- `groupingComparer` input exposed to provide custom grouping compare function for the `IgxColumnComponent`. The function receives two values and should return `0` if they are to considered members of the same group.

### Bug fixes

Expand Down
2 changes: 1 addition & 1 deletion projects/igniteui-angular/src/lib/chips/chip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IgxPrefixDirective } from './../directives/prefix/prefix.directive';
import { IgxConnectorDirective } from './connector.directive';
import { IgxLabelDirective } from './../directives/label/label.directive';
import { IgxSuffixDirective } from './../directives/suffix/suffix.directive';
import { DisplayDensity } from 'dist/igniteui-angular/lib/core/utils';
import { DisplayDensity } from '../core/utils';
import { UIInteractions} from '../test-utils/ui-interactions.spec';

@Component({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {
} from '@angular/core/testing';
import { DataGenerator } from './test-util/data-generator';

import { IGroupByResult, DefaultSortingStrategy } from './sorting-strategy';
import { DefaultSortingStrategy } from './sorting-strategy';
import { cloneArray } from '../core/utils';
import { ISortingExpression, SortingDirection } from './sorting-expression.interface';
import { DataUtil } from './data-util';
import { IGroupByResult } from './grouping-strategy';
import { IGroupingState } from './groupby-state.interface';
import { IGroupByRecord } from './groupby-record.interface';
import { FilteringStrategy } from './filtering-strategy';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IFilteringState } from './filtering-state.interface';

import { IGroupByResult, IgxSorting } from './sorting-strategy';
import { IgxSorting } from './sorting-strategy';
import { IGroupByResult, IgxGrouping } from './grouping-strategy';

import { IPagingState, PagingError } from './paging-state.interface';

Expand All @@ -22,8 +23,8 @@ export class DataUtil {
return sorting.sort(data, expressions);
}
public static group<T>(data: T[], state: IGroupingState): IGroupByResult {
const sorting = new IgxSorting();
return sorting.groupBy(data, state.expressions);
const grouping = new IgxGrouping();
return grouping.groupBy(data, state.expressions);
}
public static restoreGroups(groupData: IGroupByResult, state: IGroupingState, groupsRecords: any[] = []): any[] {
if (state.expressions.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { IGroupByExpandState } from './groupby-expand-state.interface';
import { ISortingExpression } from './sorting-expression.interface';
import { ISortingStrategy} from './sorting-strategy';
import { IGroupingExpression } from './grouping-expression.interface';

export interface IGroupingState {
expressions: ISortingExpression[];
expressions: IGroupingExpression[];
expansion: IGroupByExpandState[];
defaultExpanded: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { DataGenerator } from './test-util/data-generator';
import { DefaultSortingStrategy } from './sorting-strategy';
import { SortingDirection } from './sorting-expression.interface';
import { IGroupByRecord } from './groupby-record.interface';
import { IgxGrouping } from './grouping-strategy';

describe('Unit testing GroupingStrategy', () => {
let dataGenerator: DataGenerator;
let data: object[];
const grouping = new IgxGrouping();
beforeEach(() => {
dataGenerator = new DataGenerator();
data = dataGenerator.data;
});

it('should group by a field', () => {
const expr = [{
dir: SortingDirection.Asc,
fieldName: 'boolean',
ignoreCase: false,
strategy: DefaultSortingStrategy.instance()
}];
const res = grouping.sort(data, expr);
const gres = grouping.groupBy(res, expr);
expect(dataGenerator.getValuesForColumn(gres.data, 'boolean'))
.toEqual([false, false, false, true, true]);
const group1: IGroupByRecord = gres.metadata[0];
const group2: IGroupByRecord = gres.metadata[3];
expect(gres.metadata[1]).toEqual(group1);
expect(gres.metadata[2]).toEqual(group1);
expect(gres.metadata[4]).toEqual(group2);
expect(group1.level).toEqual(0);
expect(group2.level).toEqual(0);
expect(group1.records).toEqual(gres.data.slice(0, 3));
expect(group2.records).toEqual(gres.data.slice(3, 5));
expect(group1.value).toEqual(false);
expect(group2.value).toEqual(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ISortingExpression } from './sorting-expression.interface';

export interface IGroupingExpression extends ISortingExpression {
groupingComparer?: (a: any, b: any) => number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { IGroupByRecord } from './groupby-record.interface';
import { ISortingExpression } from './sorting-expression.interface';
import { IgxSorting } from './sorting-strategy';

export interface IGroupByResult {
data: any[];
metadata: IGroupByRecord[];
}

export class IgxGrouping extends IgxSorting {
public groupBy(data: any[], expressions: ISortingExpression[]): IGroupByResult {
const metadata: IGroupByRecord[] = [];
const grouping = this.groupDataRecursive(data, expressions, 0, null, metadata);
return {
data: grouping,
metadata: metadata
};
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,5 @@ describe('Unit testing SortingStrategy', () => {
expect(dataGenerator.getValuesForColumn(res, 'number'))
.toEqual([4, 0, 1, 2, 3]);
});
it('tests `groupBy`', () => {
const expr = [{
dir: SortingDirection.Asc,
fieldName: 'boolean',
ignoreCase: false,
strategy: DefaultSortingStrategy.instance()
}];
const res = sorting.sort(data, expr);
const gres = sorting.groupBy(res, expr);
expect(dataGenerator.getValuesForColumn(gres.data, 'boolean'))
.toEqual([false, false, false, true, true]);
const group1: IGroupByRecord = gres.metadata[0];
const group2: IGroupByRecord = gres.metadata[3];
expect(gres.metadata[1]).toEqual(group1);
expect(gres.metadata[2]).toEqual(group1);
expect(gres.metadata[4]).toEqual(group2);
expect(group1.level).toEqual(0);
expect(group2.level).toEqual(0);
expect(group1.records).toEqual(gres.data.slice(0, 3));
expect(group2.records).toEqual(gres.data.slice(3, 5));
expect(group1.value).toEqual(false);
expect(group2.value).toEqual(true);
});

});
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cloneArray } from '../core/utils';
import { IGroupByRecord } from './groupby-record.interface';
import { ISortingExpression, SortingDirection } from './sorting-expression.interface';
import { IGroupingExpression } from './grouping-expression.interface';

export interface ISortingStrategy {
sort: (data: any[], fieldName: string, dir: SortingDirection, ignoreCase: boolean) => any[];
Expand Down Expand Up @@ -53,24 +54,14 @@ export class DefaultSortingStrategy implements ISortingStrategy {
}
}

export interface IGroupByResult {
data: any[];
metadata: IGroupByRecord[];
}

export class IgxSorting {
public sort(data: any[], expressions: ISortingExpression[]): any[] {
return this.sortDataRecursive(data, expressions);
}
public groupBy(data: any[], expressions: ISortingExpression[]): IGroupByResult {
const metadata: IGroupByRecord[] = [];
const grouping = this.groupDataRecursive(data, expressions, 0, null, metadata);
return {
data: grouping,
metadata: metadata
};
}
private groupedRecordsByExpression<T>(data: T[], index: number, expression: ISortingExpression): T[] {

private groupedRecordsByExpression(data: any[],
index: number,
expression: IGroupingExpression): any[] {
let i;
let groupval;
const res = [];
Expand All @@ -79,8 +70,9 @@ export class IgxSorting {
res.push(data[index]);
groupval = data[index][key];
index++;
const comparer = expression.groupingComparer || DefaultSortingStrategy.instance().compareValues;
for (i = index; i < len; i++) {
if (data[i][key] === groupval) {
if (comparer(data[i][key], groupval) === 0) {
res.push(data[i]);
} else {
break;
Expand Down Expand Up @@ -121,7 +113,7 @@ export class IgxSorting {
}
return data;
}
private groupDataRecursive<T>(data: T[], expressions: ISortingExpression[], level: number,
protected groupDataRecursive<T>(data: T[], expressions: ISortingExpression[], level: number,
parent: IGroupByRecord, metadata: IGroupByRecord[]): T[] {
let i = 0;
let result = [];
Expand Down
4 changes: 2 additions & 2 deletions projects/igniteui-angular/src/lib/grid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ Here is a list of all public methods exposed by **igx-grid**:
|`findPrev(text: string, caseSensitive?: boolean)`|Highlights all occurrences of the specified text and marks the previous occurrence as active.|
|`clearSearch(text: string, caseSensitive?: boolean)`|Removes all search highlights from the grid.|
|`refreshSearch()`|Refreshes the current search.|
|`groupBy(expression: ISortingExpression)`| Groups by a new column based on the provided expression or modifies an existing one.
|`groupBy(expressions: Array)`| Groups columns based on the provided array of sorting expressions.
|`groupBy(expression: IGroupingExpression)`| Groups by a new column based on the provided expression or modifies an existing one.
|`groupBy(expressions: Array<IGroupingExpression>)`| Groups columns based on the provided array of grouping expressions.
|`clearGrouping()`| Clears all grouping in the grid.
|`clearGrouping(fieldName: string)`| Clear grouping from a particular column.
|`isExpandedGroup(group: IGroupByRecord )`| Returns if a group is expanded or not.
Expand Down
26 changes: 26 additions & 0 deletions projects/igniteui-angular/src/lib/grid/column.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,28 @@ export class IgxColumnComponent implements AfterContentInit {
*/
public set sortStrategy(classRef: ISortingStrategy) {
this._sortStrategy = classRef;
}
/**
* Gets the function that compares values for grouping.
* ```typescript
* let groupingComparer = this.column.groupingComparer'
* ```
* @memberof IgxColumnComponent
*/
@Input()
public get groupingComparer(): (a: any, b: any) => number {
return this._groupingComparer;
}
/**
* Sets a custom function to compare values for grouping.
* Subsequent values in the sorted data that the function returns 0 for are grouped.
* ```typescript
* this.column.groupingComparer = (a: any, b: any) => { return a === b ? 0 : -1; }
* ```
* @memberof IgxColumnComponent
*/
public set groupingComparer(funcRef: (a: any, b: any) => number) {
this._groupingComparer = funcRef;
}
/**
* Gets the default minimum `width` of the column.
Expand Down Expand Up @@ -743,6 +765,10 @@ export class IgxColumnComponent implements AfterContentInit {
*@hidden
*/
protected _sortStrategy: ISortingStrategy = DefaultSortingStrategy.instance();
/**
*@hidden
*/
protected _groupingComparer: (a: any, b: any) => number;
/**
*@hidden
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { IgxGridAPIService } from './api.service';
import { IgxColumnComponent } from './column.component';
import { IgxColumnMovingService } from './grid.common';
import { isFirefox } from '../core/utils';
import { IgxGridComponent } from './grid.component';

/**
* @hidden
Expand Down Expand Up @@ -224,7 +225,7 @@ export class IgxGridHeaderComponent implements OnInit, DoCheck, AfterViewInit {
}
}

get grid(): any {
get grid(): IgxGridComponent {
return this.gridAPI.get(this.gridID);
}

Expand Down
9 changes: 7 additions & 2 deletions projects/igniteui-angular/src/lib/grid/grid.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,8 +576,13 @@ export class IgxGroupAreaDropDirective extends IgxDropDirective {
const column: IgxColumnComponent = drag.column;
const isGrouped = column.grid.groupingExpressions.findIndex((item) => item.fieldName === column.field) !== -1;
if (column.groupable && !isGrouped) {
column.grid.groupBy({ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase,
strategy: column.sortStrategy });
column.grid.groupBy({
fieldName: column.field,
dir: SortingDirection.Asc,
ignoreCase: column.sortingIgnoreCase,
strategy: column.sortStrategy,
groupingComparer: column.groupingComparer
});
}
}
}
Expand Down
28 changes: 7 additions & 21 deletions projects/igniteui-angular/src/lib/grid/grid.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { DataType, DataUtil } from '../data-operations/data-util';
import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface';
import { IGroupByExpandState } from '../data-operations/groupby-expand-state.interface';
import { IGroupByRecord } from '../data-operations/groupby-record.interface';
import { IGroupingExpression } from '../data-operations/grouping-expression.interface';
import { ISortingExpression } from '../data-operations/sorting-expression.interface';
import { IForOfState, IgxForOfDirective } from '../directives/for-of/for_of.directive';
import { IgxTextHighlightDirective } from '../directives/text-highlight/text-highlight.directive';
Expand Down Expand Up @@ -284,7 +285,7 @@ export class IgxGridComponent implements OnInit, OnDestroy, AfterContentInit, Af
* @memberof IgxGridComponent
*/
@Input()
get groupingExpressions(): ISortingExpression[] {
get groupingExpressions(): IGroupingExpression[] {
return this._groupingExpressions;
}

Expand All @@ -299,7 +300,7 @@ export class IgxGridComponent implements OnInit, OnDestroy, AfterContentInit, Af
* ```
* @memberof IgxGridComponent
*/
set groupingExpressions(value: ISortingExpression[]) {
set groupingExpressions(value: IGroupingExpression[]) {
if (value && value.length > 10) {
throw Error('Maximum amount of grouped columns is 10.');
}
Expand Down Expand Up @@ -2721,13 +2722,12 @@ export class IgxGridComponent implements OnInit, OnDestroy, AfterContentInit, Af
* ```
* @memberof IgxGridComponent
*/
public groupBy(expression: ISortingExpression | Array<ISortingExpression>): void;
public groupBy(...rest): void {
public groupBy(expression: IGroupingExpression | Array<IGroupingExpression>): void {
this.gridAPI.submit_value(this.id);
if (rest.length === 1 && rest[0] instanceof Array) {
this._groupByMultiple(rest[0]);
if (expression instanceof Array) {
this.gridAPI.groupBy_multiple(this.id, expression);
} else {
this._groupBy(rest[0]);
this.gridAPI.groupBy(this.id, expression);
}
this.cdr.detectChanges();
this.calculateGridSizes();
Expand Down Expand Up @@ -3419,20 +3419,6 @@ export class IgxGridComponent implements OnInit, OnDestroy, AfterContentInit, Af
return width - this.getPinnedWidth(takeHidden);
}

/**
* @hidden
*/
protected _groupBy(expression: ISortingExpression) {
this.gridAPI.groupBy(this.id, expression);
}

/**
* @hidden
*/
protected _groupByMultiple(expressions: ISortingExpression[]) {
this.gridAPI.groupBy_multiple(this.id, expressions);
}

/**
* @hidden
*/
Expand Down
Loading

0 comments on commit 5534016

Please sign in to comment.