Skip to content

Group By Specification

Stamen Stoychev edited this page Jun 27, 2019 · 15 revisions

Group By Specification

Contents

  1. Overview
  2. User Stories
  3. Functionality
  4. ARIA support
  5. Assumptions and Limitations
  6. Test Scenarios
  7. References

Revision History

Version User Date Notes
0.1 Stamen Stoychev May 9, 2018 Initial draft
0.2 Stamen Stoychev May 10, 2018 API, keyboard nav, ARIA
0.3 Stamen Stoychev May 14, 2018 Grouping/sorting sync rules
0.4 Maya Kirova May 15, 2018 Initial test scenarios
0.5 Stamen Stoychev May 18, 2018 GroupBy pipe spec
0.6 Deyan Kamburov May 29, 2018 GroupBy toggle groups button
0.7 Martin Pavlov May 31, 2018 Adding visual designs
0.8 Stamen Stoychev June 11, 2018 Final updates
1.0 Stamen Stoychev June 22, 2018 Pipe and implementation amendments
1.1 Stamen Stoychev June 27, 2019 Paging integration changes

1. Overview

igxGrid Group By allows developers to enable grouping of records based on equality of specified properties.

Objectives

The feature includes the following :

  • Integration with sorting as grouping requires sorting to be performed on grouped columns
  • UI for choosing the property (column) to group by
  • UI for ungrouping columns
  • UI for expanding and collapsing of groups
  • Persistence of the groups expand/collapse states
  • UI for changing the sort order
  • Templating for the group records
  • Per-group summaries

2. User Stories

Developer:

  • Should be able to configure the columns that can be grouped and their sort direction
  • Should be able to initially group columns
  • Should be able to group or ungroup columns at run-time
  • Should be able to configure the group rows initial expand/collapse state
  • Should be able to expand/collapse group rows at run-time

End user:

  • Should be able to group columns in order to see similar data
  • Should be able to ungroup columns
  • Should be able to collapse grouped data that I'm not interested in
  • Should be able to expand grouped data that I'm interested in
  • Should be able to see summary information of the grouped data
  • Should be able to sort the grouped data
  • Should be able to navigate through the data using Paging functionality
  • Should be able to modify the grouped data
  • Should be able to re-order the grouped columns

Acceptance criteria

3. Functionality

3.1. End User Experience

Default view of the Group By Bar

00-grouping_default

Dragging a header changes the look of the group by bar

01-grouping_draggingacolumn

When all groups are minimized

02-grouping_allgroupsminimized

When the groups are expanded.

Note there is an icon in the header that collapses/expands groups 02-grouping_allgroupsexpanded

While a second column is dragged

03-grouping_dragasecondcolumn

Two groups

04-grouping_twogroups

Two groups expanded

04-grouping_twogroups_expanded

Pinning Included

05-grouping_withpinning

Proposed behavior of the toolbar -- it allows for grouping and ungrouping of columns, but not ordering.

06-grouping_toolbar

VD Specification

One Group

02-grouping_allgroupsexpanded

Two Groups with Row Selection

06-grouping_rowselect

Two Groups

04-grouping_twogroups_expanded

3.2. Developer Experience

The ability to group a column from the UI is enabled by setting its groupable property.

<igx-grid #grid1 [data]="data">
    <igx-column *ngFor="let c of columns" [field]="c.field" [groupable]="true">
    </igx-column>
</igx-grid>

Columns that are not set as groupable may still have grouping applied through the API, however, their group area pins are disabled for interaction and the end-user is only allowed to expand/collapse their group rows.

Both the current grouping applied and the groups expansion states are controllable through the grid's API.

There is a group area rendered, when any of the columns is groupable or there are groupingExpressions defined. It can also be rendered run-time, if the developer pushes a grouping expression.

The grouping expressions collection is of type Array<ISortingExpressions> (as grouping is the same as sorting on data level).

export enum SortingDirection {
    None = 0,
    Asc = 1,
    Desc = 2
}

export interface ISortingExpression {
   fieldName: string;
   dir: SortingDirection;
   ignoreCase?: boolean;
}

The input property groupingExpressions gets the current state and may be set to apply а new one. Similarly, users may call the groupBy method which accepts one or more expressions to add to the list:

grid.groupBy(expression: ISortingExpression | Array<ISortingExpression>);

Grouping can be cleared via the clearGrouping API method.The method can be called with no parameter, in which case all grouping in the grid will be cleared, or the name of the field for which to clear grouping.

To clear all grouping in the grid:

grid.clearGrouping();

To clear grouping from a particular column by its field name:

grid.clearGrouping(fieldName);

groupsRecords is a property that users may get a collection of groups created for the current data view from. It has the following signature:

public groupsRecords: IGroupByRecord[]

Using the IGroupRecord's records property yields the records part of this group while the groups property holds the child group records. This makes it easy to find the group row that is required and, e.g. toggle it through the API.

grid.toggleGroup(grid.groupsRecords[0]);

Group rows can be toggled all at once recursively via the grid's toggleAllGroupRows API method.

grid.toggleAllGroupRows();

Group by works in conjunction with sorting. Grouping expressions are essentially sorting ones and are processed by the sorting pipe. The grouping and sorting API ensure that passed expressions are synced between the two collections so that the following behavior is achieved:

  • The grouping expression collection contains all grouping expression in the order in which they were grouped. They are also added in the sorting expressions collection after which the sorting collection is re-arranged so that group expressions are always first. The sorting expression pipe sorts the data in the order of the sorting expressions so that it will always sort first by the sort expressions added as a result of grouping and then by the additional sort expressions.

  • When grouping by a new expressions collection each related field will be added to the grouping expression collection. If it is already available in the sorting expressions collection then the order of the sorting expressions will be re-arranged so that the sort expressions added as a result of grouping are first and are therefore applied first.

  • When sorting a new expressions collection each field from it available in the grouping expressions collection will just change its sort order and/or ignore case flag based on the new expression. If the sort order is set to None then grouping is removed.

Grouping is achieved through a pipe that must be called after the sorting one. It has the following structure:

export class IgxGridGroupingPipe implements PipeTransform {
    public transform(collection: any[], expression: ISortingExpression | ISortingExpression[],
                     expansion: IGroupByExpandState | IGroupByExpandState[], defaultExpanded: boolean,
                     id: string, pipeTrigger: number): any[] {
    }
}

It serves the purpose of processing the sorted data and creating the tree grouping structure based on the values equality. The values are compared with the SortingStrategy's comparer which can be overridden by the user. Afterwards, the tree is flattened based on the groups' expansion state producing a view that can be rendered with a structural directive (such as igxForOf).

This result of the grouping pipe is then sent to the paging one so that all group records participate in the paging process and are part of the total page size for each page. This can be observed in the following sample with a page size of 5:

1

Groups that span multiple pages are split between them. The group summary information is consistent for the whole group but the header itself is not created for each page:

2

Expanding and collapsing groups would change the paging state as it alters the total amount of items participating in paging:

3

The expand state has the following structure:

export interface IGroupByExpandState {
    expanded: boolean;
    hierarchy: Array<IGroupByKey>;
}

export interface IGroupByKey {
    fieldName: string;
    value: any;
}

It consists of a state and an identifier. The identifier is the list of groups in the grouping hierarchy that uniquely identifies it.

The expressions and expansion collections, the default expansion and the sorting strategy form the IGroupingState interface:

export interface IGroupingState {
    expressions: ISortingExpression[];
    expansion: IGroupByExpandState[];
    defaultExpanded: boolean;
    strategy?: ISortingStrategy;
}

As already mentioned the main grouping implementation is part of the sorting strategy further enforcing their connection. Users that implement custom sorting strategies can easily modify their groupby to work in tandem.

export interface ISortingStrategy {
    sort: (data: any[], expressions: ISortingExpression[]) => any[];
    groupBy: (data: any[], expressions: ISortingExpression[],
              expansion: IGroupByExpandState[], defaultExpanded: boolean) => IGroupByResult;
    compareValues: (a: any, b: any) => number;
}

Note: As shown, the groupBy method should produce an IGroupByResult with the following signature:

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

Where data is the same collection passed to the pipe and metadata holds each record's immediate IGroupByRecord for the same index.

The output of the final grouping pipe is an array that contains both data records to be templated through the IgxGridRowComponent and grouping records that are templated through the IgxGridGroupByRowComponent. The group record is an interface that has the following structure:

export class GroupedRecords extends Array<any> {}

export interface IGroupByRecord {
    expression: ISortingExpression;
    level: number;
    records: GroupedRecords;
    value: any;
    groups?: IGroupByRecord[];
    groupParent: IGroupByRecord;
}

Default or initial expand or collapse state of the groups is defined by the groupsExpanded (default is true). This means that when the end user groups a column the resulting groups will be expanded or collapsed depending of the value of this option.

Each row current expand/collapse state is kept in groupingExpansionState option.

Grouping persists through all grid operations and is reapplied through pipe triggers. The expansion states persist in the same way. Clearing expansions states is done automatically when ungrouping a field, for each grouping level under it in the grouping hierarchy or when changing the grouping order, for each level under the highest in the hierarchy that had its position changed.

When grouping is applied, there is a button in the header area for collapsing and expanding all groups. What this button does under the hood is to clear the groupingExpansionState and changes the value of groupsExpanded. When clicking the button the groups are going to be collapse or expand depending on the current groupsExpanded value. Also note that the default expand state for the groups is changed, which means that if another grouping is applied, the expand state of the groups will be retained.

3.3. Globalization/Localization

Describe any special localization requirements such as the number of localizable strings, regional formats

3.4. User Interface

3.5. Navigation

Elements from the grouping UI participate in the document's tab sequence. Both column chips in the grid's toolbar and group rows inside the grid's body are focusable and controllable with the keyboard. Elements in the body follow the natural tab sequence of body elements and chips in the toolbar follow the toolbar's one.

Keyboard controls include the following:

  • For group rows (focus should be on the row)

    • Alt + ↓ / → - expands the group
    • Alt + ↑ / ← - collapse the group
  • For group igxChip components in the group by area (focus should be on the chip)

    • SHIFT + LEFT - moves the focused chip left, changing the grouping order, if possible
    • SHIFT + RIGHT - moves the focused chip right, changing the grouping order, if possible
    • SPACE - changes the sorting direction
    • DELETE - ungroups the field
    • The seperate elements of the chip are also focusable and can be interacted with using the ENTER key.

3.6. API

Options

  1. IgxGridComponent

    Name Description Type Default value Valid values
    groupingExpressions A list of expressions to group by Array<ISortingExpression> null [{ fieldName: "Name", dir: SortingDirection.Asc, ignoreCase: true }]
    groupingExpansionState A list of expansions states based on a composite grouping key consisting of list of the column names and value uniquely identifying a group row Array<IGroupByExpandState> null [ [{ fieldName: "Name", value: "Angela Seamons" }], expanded: true }]
    groupsExpanded Controls whether created groups are rendered expanded or not boolean true true, false
    groupsRowList A list of visible group rows QueryList<IgxGridGroupByRowComponent> []
    groupsRecords All groups in hierarchy reflecting the current groups state. IGroupByRecord[] []
  2. IgxGridColumnComponent

    Name Description Type Default value Valid values
    groupable Controls if the column may be grouped through the UI boolean false

Properties

Name Description Type

Methods

  1. IgxGridColumnComponent
Name Description Return type Parameters
groupBy Adds modifies a single or multiple fields for grouping void
isExpandedGroup Returns if a group is expanded or not boolean group: IGroupByRecord
clearGrouping Removes grouping for a single or all field void name?: string
toggleGroup Toggles the expansion state of a group void group: IGroupByRecord
getGroup Gets a group record by its composite key IGroupByRecord field: string, value: any
toggleAllGroupRows Toggles the expansion state of a all groups recursively void void

Events

Name Description Cancelable Parameters
onGroupingDone Fires after a new column is grouped or ungrouped no ISortingExpression

4. ARIA Support

  1. aria-expanded (true|false) for each group row
  2. aria-describedby (grid id and column name) for each group row
  3. aria-selected for selectable elements

5. Assumptions and Limitations

Assumptions Limitation Notes
The predefined igxGrid styles allow for up to 10 (ten) levels of grouping
Horizontal touch scrolling works only on data rows, because the group rows are fixed in the view area

6. Test Scenarios

Automation

Basic

  • GroupBy allows grouping by columns with different data types: Number, String, Date and Boolean.
  • GroupBy allows grouping by multiple columns.
  • GroupBy allows expanding/collapsing groups.
  • GroupBy allows changing the order of the groupBy columns.
  • GroupBy allows setting initially expanded/collapsed state for group rows.
  • onGroupingDone event fires after a column is grouped/ungrouped and event args contains correct sorting expressions.
  • GroupBy allows setting a custom template for the group row content.
  • ARIA attributes are properly applied to the group row elements - aria-expanded, aria-describedby.
  • Expand/Collapse icons should be focusable (tabbable) and should allow toggling via Enter/Space key.
  • Group area is rendered when groupBy API method is invoked
  • Group area is rendered when there is a groupable column
  • GroupBy should apply the chips correctly when there are grouping expressions applied and reordered
  • GroupBy should allow dragging headers and dropping them to group area and transforming them to chips
  • GroupBy allows reordering chips and reflect their state to the groups state accordingly
  • GroupBy should allow changing the grouping direction of the groups from the chips
  • GroupBy should render disabled non-interactable chip for column that does not allow grouping.
  • Grid should be able to collapse or expand all group rows via toggleAllGroupRows API

Integration

  • Sorting
    • When sorting is applied on a non-grouped field the data in the existing groups is sorted.
    • When sorting is applied on an already grouped field the new sort order and/or ignore case is applied to the group. In case sort order is set to None via the API the grouping for that field is removed.
    • When grouping is applied on an already sorted field grouping is applied with the specified sort order and ignore case.
    • When changing sort order for grouped column via the sorting UI (clicking the column header) only two states should be applied - ascending and descending. State "None" should be skipped.
  • Paging
    • When paging is applied the data is split based on flat records + group records view. Page count and page size include both item types.
    • Expanding/Collapsing groups affect the page count/page size.
    • Group row's summary should be calculated based on all data records that belong to the group, even if they don't belong to the current page.
    • If a group spans multiple pages, its group header is rendered only on the first one;
  • Virtualization
    • Group rows are virtualized.
    • Expanding/Collapsing rows recalculates the visible chunk data and scrollbar size so that there are no empty spaces.
    • Group row expansion state is persisted when moving between different virtualization frames.
    • Group rows remain static when scrolling horizontally so that their content is always visible.
  • Filtering
    • Filtering filters by the data records and renders their related groups. Group expand state is kept intact i.e. if a group row is collapsed and there are data rows that match the filter the group row will still be collapsed after the filtering.
  • Selection
    • Group rows cannot be selected, only focused, when clicked or navigated into.
    • Keyboard navigation via arrow keys should focus the group rows and allow navigating to the next data cell.
    • When navigating from a data cell to a group row via the arrow keys, the data cell should be deselected.
    • When navigating vertically in a column between data and group cells, navigation should always continue in the same column.
  • Row Selectors
    • No row selector is rendered for the group rows.
    • Group rows are not selected when all data records are selected via the header checkbox.
  • Resizing
    • Resizing a column does not break group rows layout.
  • Summaries
    • Summaries take into account only the data records. Group rows are disregarded.
  • Hiding
    • Hiding/Showing a column does not break group rows layout.
  • Pinning
    • Pinning/Unpinning a column does not break group rows layout.
  • Updating
    • After updating a cell for a column that is grouped through the UI, its record should be relocated in the correct group.
    • Deleting via the API all items from a group should remove the group row as well.
    • Adding new records via the API should include them in the correct group.
    • Updating records via the API should move them to the correct group.

Manual

Basic

  • Test with displayDensity: compact, cosy, comfortable.
  • Test grouping of columns using touch gestures (column can be dragged and dropped in the grouping area using touch).
  • Test expand/collapse of group rows using touch.
  • Test initially grouped columns correctly displayed inside the group area
  • Test changing sorting direction of the grouped columns using the group area on desktop and touch.
  • Test changing grouping order by dragging chips around inside the group area on desktop and touch.
  • Test group chips spanning on a new row when there are too many chips to fit in the group area on desktop and touch.
  • Test grouping by dragging a column when chips are spanned on multiple rows on desktop and touch.
  • Test changing grouping order by dragging chips around inside the group area when chips are spanned on multiple rows on desktop and touch.
  • Test keyboard navigation inside the group area.

Integration

  • Column Moving
    • Test drag and drop a column inside the group area that is not groupable should not group that column.
    • Test drag and drop a chip from the group area over a column header should not display Column Moving drop icon and should not move columns.

7. References

Specify all referenced external sources.

Clone this wiki locally
You can’t perform that action at this time.