Skip to content
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

feat: add optional Top-Header for Draggable Grouping & Header Grouping #1219

Merged
merged 2 commits into from
Jun 8, 2024
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
38 changes: 38 additions & 0 deletions docs/grid-functionalities/grouping-aggregators.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
- [Demo](#demo)
- [Description](#description)
- [Setup](#setup)
- [Draggable Dropzone Location](#draggable-dropzone-location)
- [Aggregators](#aggregators)
- [SortComparers](https://github.com/ghiscoding/slickgrid-universal/blob/master/packages/common/src/sortComparers/sortComparers.index.ts)
- [GroupTotalsFormatter](#group-totals-formatter)
Expand Down Expand Up @@ -62,6 +63,43 @@ export class Example {
}
```

### Draggable Dropzone Location

The Draggable Grouping can be located in either the Top-Header or the Pre-Header as described below.

#### Pre-Heaader
Draggable Grouping can be located in either the Pre-Header of the Top-Header, however when it is located in the Pre-Header then the Header Grouping will not be available (because both of them would conflict with each other). Note that prior to the version 8.1 of Aurelia-Slickgrid, the Pre-Header was the default and only available option.

```ts
this.gridOptions = {
createPreHeaderPanel: true,
showPreHeaderPanel: true,
preHeaderPanelHeight: 26,
draggableGrouping: {
// ... any draggable plugin option
},
}
```

#### Top-Heaader
##### requires v8.1 and higher
This is the preferred section since the Top-Header is on top of all headers (including pre-header) and it will always be the full grid width. Using the Top-Header also frees up the Pre-Header section for the potential use of Header Grouping.

When using Draggable Grouping and Header Grouping together, you need to enable both top-header and pre-header.
```ts
this.gridOptions = {
// we'll use top-header for the Draggable Grouping
createTopHeaderPanel: true,
showTopHeaderPanel: true,
topHeaderPanelHeight: 35,

// pre-header will include our Header Grouping (i.e. "Common Factor")
createPreHeaderPanel: true,
showPreHeaderPanel: true,
preHeaderPanelHeight: 26,
}
```

### Aggregators
The `Aggregators` is basically the accumulator, the logic that will do the sum (or any other aggregate we defined). We simply need to instantiate the `Aggregator` by passing the column definition `field` that will be used to accumulate. For example, if we have a column definition of Cost and we want to calculate it's sum, we can call the `Aggregator` as follow
```ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ export class AureliaSlickgridCustomElement {

if (gridOptions.enableTranslate) {
this.extensionService.translateAllExtensions(args.newLocale);
if (gridOptions.createPreHeaderPanel && !gridOptions.enableDraggableGrouping) {
if ((gridOptions.createPreHeaderPanel && gridOptions.createTopHeaderPanel) || (gridOptions.createPreHeaderPanel && !gridOptions.enableDraggableGrouping)) {
this.groupingService.translateGroupingAndColSpan();
}
}
Expand Down Expand Up @@ -1358,7 +1358,10 @@ export class AureliaSlickgridCustomElement {
}

// when using Grouping/DraggableGrouping/Colspan register its Service
if (this.gridOptions.createPreHeaderPanel && !this.gridOptions.enableDraggableGrouping && !this._registeredResources.some(r => r instanceof GroupingAndColspanService)) {
if (
((this.gridOptions.createPreHeaderPanel && this.gridOptions.createTopHeaderPanel) || (this.gridOptions.createPreHeaderPanel && !this.gridOptions.enableDraggableGrouping))
&& !this._registeredResources.some(r => r instanceof GroupingAndColspanService)
) {
this._registeredResources.push(this.groupingService);
}

Expand Down
68 changes: 40 additions & 28 deletions packages/demo/src/examples/slickgrid/example18.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class Example18 {
defineGrid() {
this.columnDefinitions = [
{
id: 'title', name: 'Title', field: 'title',
id: 'title', name: 'Title', field: 'title', columnGroup: 'Common Factor',
width: 70, minWidth: 50,
cssClass: 'cell-title',
filterable: true,
Expand All @@ -87,7 +87,7 @@ export class Example18 {
}
},
{
id: 'duration', name: 'Duration', field: 'duration',
id: 'duration', name: 'Duration', field: 'duration', columnGroup: 'Common Factor',
width: 70,
sortable: true,
filterable: true,
Expand All @@ -108,27 +108,8 @@ export class Example18 {
}
},
{
id: 'percentComplete', name: '% Complete', field: 'percentComplete',
minWidth: 70, width: 90,
formatter: Formatters.percentCompleteBar,
type: FieldType.number,
filterable: true,
filter: { model: Filters.compoundSlider },
sortable: true,
groupTotalsFormatter: GroupTotalFormatters.avgTotalsPercentage,
grouping: {
getter: 'percentComplete',
formatter: (g) => `% Complete: ${g.value} <span class="text-primary">(${g.count} items)</span>`,
aggregators: [
new Aggregators.Sum('cost')
],
aggregateCollapsed: false,
collapsed: false
},
params: { groupFormatterPrefix: '<i>Avg</i>: ' }
},
{
id: 'start', name: 'Start', field: 'start', minWidth: 60,
id: 'start', name: 'Start', field: 'start', columnGroup: 'Period',
minWidth: 60,
sortable: true,
filterable: true,
filter: { model: Filters.compoundDate },
Expand All @@ -147,7 +128,7 @@ export class Example18 {
}
},
{
id: 'finish', name: 'Finish', field: 'finish',
id: 'finish', name: 'Finish', field: 'finish', columnGroup: 'Period',
minWidth: 60,
sortable: true,
filterable: true,
Expand All @@ -167,7 +148,7 @@ export class Example18 {
}
},
{
id: 'cost', name: 'Cost', field: 'cost',
id: 'cost', name: 'Cost', field: 'cost', columnGroup: 'Analysis',
width: 90,
sortable: true,
filterable: true,
Expand All @@ -187,7 +168,27 @@ export class Example18 {
}
},
{
id: 'effortDriven', name: 'Effort-Driven', field: 'effortDriven',
id: 'percentComplete', name: '% Complete', field: 'percentComplete', columnGroup: 'Analysis',
minWidth: 70, width: 90,
formatter: Formatters.percentCompleteBar,
type: FieldType.number,
filterable: true,
filter: { model: Filters.compoundSlider },
sortable: true,
groupTotalsFormatter: GroupTotalFormatters.avgTotalsPercentage,
grouping: {
getter: 'percentComplete',
formatter: (g) => `% Complete: ${g.value} <span class="text-primary">(${g.count} items)</span>`,
aggregators: [
new Aggregators.Sum('cost')
],
aggregateCollapsed: false,
collapsed: false
},
params: { groupFormatterPrefix: '<i>Avg</i>: ' }
},
{
id: 'effortDriven', name: 'Effort-Driven', field: 'effortDriven', columnGroup: 'Analysis',
width: 80, minWidth: 20, maxWidth: 100,
cssClass: 'cell-effort-driven',
sortable: true,
Expand All @@ -214,9 +215,18 @@ export class Example18 {
rightPadding: 10
},
enableDraggableGrouping: true,

// pre-header will include our Header Grouping (i.e. "Common Factor")
// Draggable Grouping could be located in either the Pre-Header OR the new Top-Header
createPreHeaderPanel: true,
showPreHeaderPanel: true,
preHeaderPanelHeight: 40,
preHeaderPanelHeight: 30,

// when Top-Header is created, it will be used by the Draggable Grouping (otherwise the Pre-Header will be used)
createTopHeaderPanel: true,
showTopHeaderPanel: true,
topHeaderPanelHeight: 35,

showCustomFooter: true,
enableFiltering: true,
// you could debounce/throttle the input text filter if you have lots of data
Expand All @@ -234,7 +244,9 @@ export class Example18 {
draggableGrouping: {
dropPlaceHolderText: 'Drop a column header here to group by the column',
// groupIconCssClass: 'mdi mdi-drag-vertical',
deleteIconCssClass: 'mdi mdi-close',
deleteIconCssClass: 'mdi mdi-close text-color-danger',
sortAscIconCssClass: 'mdi mdi-arrow-up',
sortDescIconCssClass: 'mdi mdi-arrow-down',
onGroupChanged: (_e, args) => this.onGroupChanged(args),
onExtensionRegistered: (extension) => this.draggableGroupingPlugin = extension,
},
Expand Down
28 changes: 21 additions & 7 deletions test/cypress/e2e/example18.cy.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
describe('Example 18 - Draggable Grouping & Aggregators', () => {
const fullTitles = ['Title', 'Duration', '% Complete', 'Start', 'Finish', 'Cost', 'Effort-Driven'];
const preHeaders = ['Common Factor', 'Period', 'Analysis', ''];
const fullTitles = ['Title', 'Duration', 'Start', 'Finish', 'Cost', '% Complete', 'Effort-Driven'];
const GRID_ROW_HEIGHT = 35;

it('should display Example title', () => {
cy.visit(`${Cypress.config('baseUrl')}/example18`);
cy.get('h2').should('contain', 'Example 18: Draggable Grouping & Aggregators');
});

it('should have exact column titles on 1st grid', () => {
it('should have exact column (pre-header) grouping titles in grid', () => {
cy.get('#grid18')
.find('.slick-header-columns')
.find('.slick-preheader-panel .slick-header-columns')
.children()
.each(($child, index) => expect($child.text()).to.eq(preHeaders[index]));
});

it('should have exact column titles in grid', () => {
cy.get('#grid18')
.find('.slick-header:not(.slick-preheader-panel) .slick-header-columns')
.children()
.each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
});

it('should have a draggable dropzone on top of the grid in the top-header section', () => {
cy.get('#grid18')
.find('.slick-topheader-panel .slick-dropzone:visible')
.contains('Drop a column header here to group by the column');
});

describe('Grouping Tests', () => {
it('should "Group by Duration & sort groups by value" then Collapse All and expect only group titles', () => {
cy.get('[data-test="add-50k-rows-btn"]').click();
Expand Down Expand Up @@ -141,8 +155,8 @@ describe('Example 18 - Draggable Grouping & Aggregators', () => {
.should('exist');
});

it('should use the preheader Toggle All button and expect all groups to now be expanded', () => {
cy.get('.slick-preheader-panel .slick-group-toggle-all').click();
it('should use the topheader Toggle All button and expect all groups to now be expanded', () => {
cy.get('.slick-topheader-panel .slick-group-toggle-all').click();

cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0) .slick-group-toggle.expanded`).should('have.length', 1);
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0) .slick-group-title`).should('contain', 'Effort-Driven: False');
Expand All @@ -153,8 +167,8 @@ describe('Example 18 - Draggable Grouping & Aggregators', () => {
.should('have.css', 'marginLeft').and('eq', `15px`);
});

it('should use the preheader Toggle All button again and expect all groups to now be collapsed', () => {
cy.get('.slick-preheader-panel .slick-group-toggle-all').click();
it('should use the topheader Toggle All button again and expect all groups to now be collapsed', () => {
cy.get('.slick-topheader-panel .slick-group-toggle-all').click();

cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0) .slick-group-toggle.collapsed`).should('have.length', 1);
cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0) .slick-group-title`).should('contain', 'Effort-Driven: False');
Expand Down