From 075c64961cb7400500df46b792866d39fba2d9e0 Mon Sep 17 00:00:00 2001 From: "Ghislain B." Date: Tue, 30 Dec 2025 00:52:48 -0500 Subject: [PATCH 01/66] feat!: switch to column `hidden` property and always keep all columns (#2281) * feat!: switch to column `hidden` property and always keep all columns --- .../src/examples/slickgrid/example14.html | 21 +- .../src/examples/slickgrid/example14.ts | 14 + .../src/examples/slickgrid/example32.ts | 2 +- .../src/examples/slickgrid/example43.ts | 19 +- .../aurelia/test/cypress/e2e/example14.cy.ts | 142 ++++++--- .../aurelia/test/cypress/e2e/example16.cy.ts | 2 +- .../aurelia/test/cypress/e2e/example44.cy.ts | 98 ++++++ .../src/examples/slickgrid/Example14.tsx | 34 ++- .../src/examples/slickgrid/Example15.tsx | 2 - .../src/examples/slickgrid/Example32.tsx | 2 +- .../src/examples/slickgrid/Example43.tsx | 19 +- demos/react/test/cypress/e2e/example14.cy.ts | 142 ++++++--- demos/react/test/cypress/e2e/example16.cy.ts | 2 +- demos/react/test/cypress/e2e/example44.cy.ts | 100 +++++- demos/vanilla/src/examples/example08.html | 12 +- demos/vanilla/src/examples/example08.ts | 9 + demos/vanilla/src/examples/example11.ts | 24 +- demos/vanilla/src/examples/example14.ts | 2 +- demos/vanilla/src/examples/example32.ts | 19 +- demos/vue/src/components/Example14.vue | 21 +- demos/vue/src/components/Example32.vue | 2 +- demos/vue/src/components/Example43.vue | 19 +- demos/vue/test/cypress/e2e/example14.cy.ts | 142 ++++++--- demos/vue/test/cypress/e2e/example16.cy.ts | 2 +- demos/vue/test/cypress/e2e/example44.cy.ts | 98 ++++++ .../grid-functionalities/grid-state-preset.md | 15 +- docs/grid-functionalities/row-based-edit.md | 2 +- docs/grid-functionalities/row-detail.md | 2 +- docs/grid-functionalities/row-selection.md | 4 +- .../grid-functionalities/Row-based-edit.md | 2 +- .../grid-functionalities/grid-state-preset.md | 15 +- .../docs/grid-functionalities/row-detail.md | 4 +- .../grid-functionalities/row-selection.md | 4 +- .../demos/examples/example14.component.html | 22 +- .../src/demos/examples/example14.component.ts | 14 + .../src/demos/examples/example16.component.ts | 12 +- .../src/demos/examples/example32.component.ts | 2 +- .../src/demos/examples/example43.component.ts | 19 +- .../angular-slickgrid.component.spec.ts | 31 +- .../components/angular-slickgrid.component.ts | 16 +- .../test/cypress/e2e/example14.cy.ts | 142 ++++++--- .../test/cypress/e2e/example16.cy.ts | 2 +- .../test/cypress/e2e/example44.cy.ts | 98 ++++++ .../grid-functionalities/Row-based-edit.md | 2 +- .../grid-functionalities/grid-state-preset.md | 15 +- .../docs/grid-functionalities/row-detail.md | 4 +- .../grid-functionalities/row-selection.md | 4 +- .../src/custom-elements/aurelia-slickgrid.ts | 16 +- .../grid-functionalities/Row-based-edit.md | 2 +- .../grid-functionalities/grid-state-preset.md | 15 +- .../docs/grid-functionalities/row-detail.md | 4 +- .../grid-functionalities/row-selection.md | 4 +- .../src/components/slickgrid-react.tsx | 16 +- .../grid-functionalities/Row-based-edit.md | 2 +- .../grid-functionalities/grid-state-preset.md | 15 +- .../docs/grid-functionalities/row-detail.md | 4 +- .../grid-functionalities/row-selection.md | 4 +- .../src/components/SlickgridVue.vue | 16 +- .../src/core/__tests__/slickGrid.spec.ts | 286 +++++++++++++++--- packages/common/src/core/slickGrid.ts | 208 +++++++++---- .../__tests__/extensionUtility.spec.ts | 58 +--- .../__tests__/slickCellMenu.plugin.spec.ts | 3 +- .../__tests__/slickColumnPicker.spec.ts | 126 ++++---- .../__tests__/slickGridMenu.spec.ts | 90 ++---- .../__tests__/slickHeaderButtons.spec.ts | 1 + .../__tests__/slickHeaderMenu.spec.ts | 69 ++--- .../src/extensions/extensionCommonUtils.ts | 56 ++-- .../common/src/extensions/extensionUtility.ts | 23 +- .../common/src/extensions/slickCellMenu.ts | 4 +- .../src/extensions/slickColumnPicker.ts | 6 +- .../common/src/extensions/slickGridMenu.ts | 14 +- .../src/extensions/slickHeaderButtons.ts | 2 +- .../common/src/extensions/slickHeaderMenu.ts | 63 ++-- .../common/src/interfaces/column.interface.ts | 2 + .../src/interfaces/currentColumn.interface.ts | 3 + .../src/interfaces/gridOption.interface.ts | 15 + .../__tests__/extension.service.spec.ts | 42 ++- .../services/__tests__/filter.service.spec.ts | 25 +- .../services/__tests__/grid.service.spec.ts | 86 ++---- .../__tests__/gridState.service.spec.ts | 127 ++++++-- .../__tests__/headerGrouping.service.spec.ts | 9 +- .../__tests__/resizer.service.spec.ts | 16 +- .../services/__tests__/shared.service.spec.ts | 21 -- .../services/__tests__/sort.service.spec.ts | 9 +- .../__tests__/treeData.service.spec.ts | 2 +- .../src/services/__tests__/utilities.spec.ts | 24 +- .../common/src/services/extension.service.ts | 34 ++- .../common/src/services/filter.service.ts | 13 +- packages/common/src/services/grid.service.ts | 45 ++- .../common/src/services/gridState.service.ts | 98 +++--- .../src/services/headerGrouping.service.ts | 14 +- .../common/src/services/resizer.service.ts | 2 +- .../common/src/services/shared.service.ts | 10 - packages/common/src/services/sort.service.ts | 7 +- .../common/src/services/treeData.service.ts | 2 +- packages/common/src/services/utilities.ts | 31 ++ .../__tests__/graphql.service.spec.ts | 25 -- .../graphql/src/services/graphql.service.ts | 5 +- .../__tests__/grid-odata.service.spec.ts | 8 +- .../odata/src/services/grid-odata.service.ts | 5 +- .../__tests__/slick-vanilla-grid.spec.ts | 31 +- .../components/slick-vanilla-grid-bundle.ts | 20 +- test/cypress/e2e/example04.cy.ts | 18 +- test/cypress/e2e/example07.cy.ts | 6 +- test/cypress/e2e/example08.cy.ts | 259 +++++++--------- test/cypress/e2e/example33.cy.ts | 98 ++++++ 106 files changed, 2407 insertions(+), 1192 deletions(-) diff --git a/demos/aurelia/src/examples/slickgrid/example14.html b/demos/aurelia/src/examples/slickgrid/example14.html index 2710620da4..d6d7b2de22 100644 --- a/demos/aurelia/src/examples/slickgrid/example14.html +++ b/demos/aurelia/src/examples/slickgrid/example14.html @@ -24,8 +24,25 @@

-

Grid 1 (with Header Grouping & Colspan)

- +

+ Grid 1 (with Header Grouping & Colspan) + +

+ +
diff --git a/demos/aurelia/src/examples/slickgrid/example14.ts b/demos/aurelia/src/examples/slickgrid/example14.ts index 9d8edcce5a..9b0321fa46 100644 --- a/demos/aurelia/src/examples/slickgrid/example14.ts +++ b/demos/aurelia/src/examples/slickgrid/example14.ts @@ -3,6 +3,7 @@ import { type AureliaGridInstance, type Column, type GridOption, type ItemMetada import './example14.scss'; // provide custom CSS/SASS styling export class Example14 { + aureliaGrid1!: AureliaGridInstance; aureliaGrid2!: AureliaGridInstance; gridObj2: any; columnDefinitions1: Column[] = []; @@ -12,6 +13,7 @@ export class Example14 { dataset1: any[] = []; dataset2: any[] = []; hideSubTitle = false; + isColspanSpreading = false; constructor() { this.definedGrid1(); @@ -24,6 +26,10 @@ export class Example14 { this.dataset2 = this.getData(500); } + aureliaGridReady1(aureliaGrid: AureliaGridInstance) { + this.aureliaGrid1 = aureliaGrid; + } + aureliaGridReady2(aureliaGrid: AureliaGridInstance) { this.aureliaGrid2 = aureliaGrid; this.gridObj2 = aureliaGrid.slickGrid; @@ -63,6 +69,7 @@ export class Example14 { gridMenu: { iconButtonContainer: 'preheader', // we can display the grid menu icon in either the preheader or in the column header (default) }, + spreadHiddenColspan: this.isColspanSpreading, }; } @@ -153,6 +160,13 @@ export class Example14 { }; } + spreadColspan() { + this.isColspanSpreading = !this.isColspanSpreading; + this.aureliaGrid1.slickGrid?.setOptions({ spreadHiddenColspan: this.isColspanSpreading }); + this.aureliaGrid1.slickGrid?.resetActiveCell(); + this.aureliaGrid1.slickGrid?.invalidate(); + } + toggleSubTitle() { this.hideSubTitle = !this.hideSubTitle; const action = this.hideSubTitle ? 'add' : 'remove'; diff --git a/demos/aurelia/src/examples/slickgrid/example32.ts b/demos/aurelia/src/examples/slickgrid/example32.ts index 501a634366..8f21ff964e 100644 --- a/demos/aurelia/src/examples/slickgrid/example32.ts +++ b/demos/aurelia/src/examples/slickgrid/example32.ts @@ -586,7 +586,7 @@ export class Example32 { // just for demo purposes, set it back to its original width const columns = this.aureliaGrid.slickGrid.getColumns() as Column[]; columns.forEach((col) => (col.width = col.originalWidth)); - this.aureliaGrid.slickGrid.setColumns(columns); + this.aureliaGrid.slickGrid.updateColumns(); this.aureliaGrid.slickGrid.autosizeColumns(); this.isUsingDefaultResize = true; } diff --git a/demos/aurelia/src/examples/slickgrid/example43.ts b/demos/aurelia/src/examples/slickgrid/example43.ts index 696cd6e89a..5283c13bd9 100644 --- a/demos/aurelia/src/examples/slickgrid/example43.ts +++ b/demos/aurelia/src/examples/slickgrid/example43.ts @@ -427,7 +427,8 @@ export class Example43 { } } - // update column definitions + // 1. update column definitions via grid.setColumns() + // this will shift colspan/rowspan to the left or right accordingly if (this.showEmployeeId) { this.columnDefinitions.unshift({ id: 'employeeID', name: 'Employee ID', field: 'employeeID', width: 100 }); } else { @@ -435,6 +436,22 @@ export class Example43 { } this.aureliaGrid.slickGrid.setColumns(this.columnDefinitions); + // --- OR --- + // 2. OR update via "hidden" column flag & increase/decrease column index accordingly in the metadata + // this approach will keep colspan/rowspan "as-is" but will hide the EmployeeID column + /* + const colDirIdx = newShowEmployeeId ? -1 : 1; + for (const row of Object.keys(this.metadata)) { + newMetadata[row] = { columns: {} }; + for (const col of Object.keys((this.metadata as any)[row].columns)) { + newMetadata[row].columns[Number(col) + colDirIdx] = (this.metadata as any)[row].columns[col]; + } + } + this.aureliaGrid.slickGrid?.setOptions({ frozenColumn: newShowEmployeeId ? 0 : 1 }); + this.aureliaGrid.slickGrid?.updateColumnById('employeeID', { hidden: !newShowEmployeeId }); + this.aureliaGrid.slickGrid?.updateColumns(); + */ + // update & remap rowspans this.metadata = newMetadata; this.aureliaGrid.slickGrid.remapAllColumnsRowSpan(); diff --git a/demos/aurelia/test/cypress/e2e/example14.cy.ts b/demos/aurelia/test/cypress/e2e/example14.cy.ts index 402545254b..91f7db34b6 100644 --- a/demos/aurelia/test/cypress/e2e/example14.cy.ts +++ b/demos/aurelia/test/cypress/e2e/example14.cy.ts @@ -21,16 +21,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); it('should have a frozen grid on page load with 3 columns on the left and 4 columns on the right', () => { - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid again', () => { @@ -48,14 +48,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Remove Frozen Columns" button to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { cy.get('[data-test="remove-frozen-column-button"]').click(); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid once again', () => { @@ -73,16 +73,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Set 3 Frozen Columns" button to switch frozen columns grid and expect 3 frozen columns on the left and 4 columns on the right', () => { cy.contains('Set 3 Frozen Columns').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have still exact Column Pre-Header & Column Header Titles in the grid', () => { @@ -102,14 +102,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { cy.contains('Unfreeze Columns/Rows').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should reapply 3 frozen columns on 2nd grid', () => { @@ -209,7 +209,7 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); describe('Colspan checks on 1st grid', () => { - it('should hide Finish column and still expect "5 days" to spread accross 3 column', () => { + it('should hide Finish column and expect "5 days" spread to drop from 3 to 2 columns (1x hidden column)', () => { cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); @@ -232,11 +232,64 @@ describe('Example 14 - Column Span & Header Grouping', () => { .should('contain', 'Hide Column') .click(); + // goto right + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').click(); + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0.active').should('contain', 'Task 1').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + // goto left + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active') + .contains(/(true|false)+$/) + .type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active').should('contain', 'Task 1'); + }); + + it('should reset Column Picker, click on Spread Hidden Coolumn button then hide Finish column and still expect "5 days" to spread accross 3 column', () => { + cy.get('#grid1').find('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(4)') + .children('label') + .should('contain', 'Period - Finish') + .click(); + + cy.get('.slick-column-picker .close').click(); + + cy.get('[data-test="spread-colspan-button"]').click(); cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); - cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r4').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4').contains(/\d+$/); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + cy.get('#grid1') + .find('.slick-pane-left .slick-header-columns .slick-header-column[role="columnheader"]:nth(3)') + .trigger('mouseover') + .children('.slick-header-menu-button') + .invoke('show') + .click(); + + cy.get('.slick-header-menu .slick-menu-command-list') + .should('be.visible') + .children('.slick-menu-item:nth-of-type(3)') + .children('.slick-menu-content') + .should('contain', 'Hide Column') + .click(); + + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r4').should('contain', '5 days'); cy.get('#grid1') - .find('[data-row=1] .slick-cell.l4.r4') + .find('[data-row=1] .slick-cell.l5.r5') .contains(/(true|false)+$/); }); @@ -292,15 +345,13 @@ describe('Example 14 - Column Span & Header Grouping', () => { describe('First Grid - Key Navigation', () => { it('should start at Task 1 and expect "Duration" to have colspan of 3 and show "% Complete" and "Effort Driven"', () => { cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') - .should('have.class', 'active') - .contains(/(true|false)+$/); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').contains(/(true|false)+$/); cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); @@ -312,16 +363,17 @@ describe('Example 14 - Column Span & Header Grouping', () => { .children('label') .should('contain', 'Period - Finish') .click(); + cy.get('.slick-column-picker .close').click(); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4') + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') .should('have.class', 'active') .contains(/(true|false)+$/); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('have.class', 'active'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); }); diff --git a/demos/aurelia/test/cypress/e2e/example16.cy.ts b/demos/aurelia/test/cypress/e2e/example16.cy.ts index a1a2073bb5..78dabd7ae7 100644 --- a/demos/aurelia/test/cypress/e2e/example16.cy.ts +++ b/demos/aurelia/test/cypress/e2e/example16.cy.ts @@ -408,7 +408,7 @@ describe('Example 16 - Row Move & Checkbox Selector Selector Plugins', () => { }); it('should add Edit/Delete columns and expect 2 new columns added at the beginning of the grid', () => { - const newExpectedColumns = ['', '', ...fullTitles]; + const newExpectedColumns = ['', '', '', '', 'Title', '% Complete', 'Start', 'Finish', 'Duration', 'Completed']; cy.get('[data-test="add-crud-columns-btn"]').click(); cy.get('#grid16') diff --git a/demos/aurelia/test/cypress/e2e/example44.cy.ts b/demos/aurelia/test/cypress/e2e/example44.cy.ts index 6f70275d7a..f399ed0364 100644 --- a/demos/aurelia/test/cypress/e2e/example44.cy.ts +++ b/demos/aurelia/test/cypress/e2e/example44.cy.ts @@ -463,4 +463,102 @@ describe('Example 44 - Column & Row Span', { retries: 0 }, () => { cy.get('[data-row=499] > .slick-cell.l5.r5.active').should('have.length', 1); }); }); + + describe('Hide Columns with colspan/rowspan', () => { + it('should hide Title column and expect other colspan/rowspan to simply move over and stay attached to same columns', () => { + cy.get('[data-row=499] > .slick-cell.l5.r5.active').type('{ctrl}{home}', { release: false }); + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + // Task 2 rowspan should be hidden now + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.not.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should start at "Revenue Growth" second cell down, then type "Arrow Right" key 2x times and expect 4th row "Policy Index" green section to still have a rowspan 3x and colspan of 4x', () => { + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').as('active_cell').click(); + cy.get('[data-row=3] > .slick-cell.l1.r1.active').should('have.length', 1); + cy.get('@active_cell').type('{rightarrow}{rightarrow}'); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should go up by 1x "Arrow Up" and expect blue section colspan of 3x', () => { + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').type('{uparrow}'); + cy.get('[data-row=2] > .slick-cell.l3.r5.active').should('not.have.class', 'rowspan'); + }); + + it('should "Revenue Growth" rowspan should now be at first column and "Policy Index" should now be at third column', () => { + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('not.exist'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + + it('should show again "Title" column and expect "Revenue Growth" and "Policy Index" columns to be moved to the right by 1 column', () => { + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('contain', 'Task 0'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('contain', 'Task 1'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('contain', 'Task 2'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + }); }); diff --git a/demos/react/src/examples/slickgrid/Example14.tsx b/demos/react/src/examples/slickgrid/Example14.tsx index 47f0542510..19df93c5f1 100644 --- a/demos/react/src/examples/slickgrid/Example14.tsx +++ b/demos/react/src/examples/slickgrid/Example14.tsx @@ -6,11 +6,12 @@ import './example14.scss'; // provide custom CSS/SASS styling const Example14: React.FC = () => { const [gridOptions1, setGridOptions1] = useState(undefined); const [gridOptions2, setGridOptions2] = useState(undefined); + const [isColspanSpreading, setIsColspanSpreading] = useState(false); const [columnDefinitions1, setColumnDefinitions1] = useState([]); const [columnDefinitions2, setColumnDefinitions2] = useState([]); const [dataset1, setDataset1] = useState([]); const [dataset2, setDataset2] = useState([]); - // const reactGridRef1 = useRef(null); + const reactGridRef1 = useRef(null); const reactGridRef2 = useRef(null); const [hideSubTitle, setHideSubTitle] = useState(false); @@ -21,9 +22,9 @@ const Example14: React.FC = () => { setDataset2(getData(500)); }, []); - // function reactGrid1Ready(reactGrid: SlickgridReactInstance) { - // reactGridRef1.current = reactGrid; - // } + function reactGrid1Ready(reactGrid: SlickgridReactInstance) { + reactGridRef1.current = reactGrid; + } function reactGrid2Ready(reactGrid: SlickgridReactInstance) { reactGridRef2.current = reactGrid; @@ -160,6 +161,14 @@ const Example14: React.FC = () => { }; } + function spreadColspan() { + const isSpreading = !isColspanSpreading; + setIsColspanSpreading(isSpreading); + reactGridRef1.current?.slickGrid?.setOptions({ spreadHiddenColspan: isSpreading }); + reactGridRef1.current?.slickGrid?.resetActiveCell(); + reactGridRef1.current?.slickGrid?.invalidate(); + } + return !gridOptions1 ? ( '' ) : ( @@ -198,8 +207,23 @@ const Example14: React.FC = () => {

Grid 1 (with Header Grouping & Colspan) +

- + + reactGrid1Ready($event.detail)} + />
diff --git a/demos/react/src/examples/slickgrid/Example15.tsx b/demos/react/src/examples/slickgrid/Example15.tsx index d69b346c23..24f37ad400 100644 --- a/demos/react/src/examples/slickgrid/Example15.tsx +++ b/demos/react/src/examples/slickgrid/Example15.tsx @@ -51,8 +51,6 @@ const Example15: React.FC = () => { /** Clear the Grid State from Local Storage and reset the grid to it's original state */ function clearGridStateFromLocalStorage() { - // reactGridRef.current?.slickGrid.setColumns(reactGridRef.current?.gridService.getAllColumnDefinitions()); - // reactGridRef.current?.slickGrid.autosizeColumns(); reactGridRef.current?.gridService.resetGrid(getColumnDefinitions()); reactGridRef.current?.paginationService!.changeItemPerPage(DEFAULT_PAGE_SIZE); setTimeout(() => (localStorage[LOCAL_STORAGE_KEY] = null)); diff --git a/demos/react/src/examples/slickgrid/Example32.tsx b/demos/react/src/examples/slickgrid/Example32.tsx index 4161f97c4b..67ccaa2fc6 100644 --- a/demos/react/src/examples/slickgrid/Example32.tsx +++ b/demos/react/src/examples/slickgrid/Example32.tsx @@ -586,7 +586,7 @@ const Example32: React.FC = () => { // just for demo purposes, set it back to its original width const columns = reactGridRef.current?.slickGrid.getColumns() as Column[]; columns.forEach((col) => (col.width = col.originalWidth)); - reactGridRef.current?.slickGrid.setColumns(columns); + reactGridRef.current?.slickGrid.updateColumns(); reactGridRef.current?.slickGrid.autosizeColumns(); setIsUsingDefaultResize(true); } diff --git a/demos/react/src/examples/slickgrid/Example43.tsx b/demos/react/src/examples/slickgrid/Example43.tsx index 6ade36e3df..b34f643539 100644 --- a/demos/react/src/examples/slickgrid/Example43.tsx +++ b/demos/react/src/examples/slickgrid/Example43.tsx @@ -418,7 +418,8 @@ export default function Example43() { } } - // update column definitions + // 1. update column definitions via grid.setColumns() + // this will shift colspan/rowspan to the left or right accordingly const cols: Column[] = reactGrid?.slickGrid.getColumns() || []; if (newShowEmployeeId) { cols.unshift({ id: 'employeeID', name: 'Employee ID', field: 'employeeID', width: 100 }); @@ -427,6 +428,22 @@ export default function Example43() { } reactGrid?.slickGrid.setColumns(cols || []); + // --- OR --- + // 2. OR update via "hidden" column flag & increase/decrease column index accordingly in the metadata + // this approach will keep colspan/rowspan "as-is" but will hide the EmployeeID column + /* + const colDirIdx = newShowEmployeeId ? -1 : 1; + for (const row of Object.keys(this.metadata)) { + newMetadata[row] = { columns: {} }; + for (const col of Object.keys((this.metadata as any)[row].columns)) { + newMetadata[row].columns[Number(col) + colDirIdx] = (this.metadata as any)[row].columns[col]; + } + } + reactGrid?.slickGrid?.setOptions({ frozenColumn: newShowEmployeeId ? 0 : 1 }); + reactGrid?.slickGrid?.updateColumnById('employeeID', { hidden: !newShowEmployeeId }); + reactGrid?.slickGrid?.updateColumns(); + */ + // update & remap rowspans metadataRef.current = newMetadata; reactGrid?.slickGrid.remapAllColumnsRowSpan(); diff --git a/demos/react/test/cypress/e2e/example14.cy.ts b/demos/react/test/cypress/e2e/example14.cy.ts index 402545254b..91f7db34b6 100644 --- a/demos/react/test/cypress/e2e/example14.cy.ts +++ b/demos/react/test/cypress/e2e/example14.cy.ts @@ -21,16 +21,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); it('should have a frozen grid on page load with 3 columns on the left and 4 columns on the right', () => { - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid again', () => { @@ -48,14 +48,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Remove Frozen Columns" button to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { cy.get('[data-test="remove-frozen-column-button"]').click(); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid once again', () => { @@ -73,16 +73,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Set 3 Frozen Columns" button to switch frozen columns grid and expect 3 frozen columns on the left and 4 columns on the right', () => { cy.contains('Set 3 Frozen Columns').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have still exact Column Pre-Header & Column Header Titles in the grid', () => { @@ -102,14 +102,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { cy.contains('Unfreeze Columns/Rows').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should reapply 3 frozen columns on 2nd grid', () => { @@ -209,7 +209,7 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); describe('Colspan checks on 1st grid', () => { - it('should hide Finish column and still expect "5 days" to spread accross 3 column', () => { + it('should hide Finish column and expect "5 days" spread to drop from 3 to 2 columns (1x hidden column)', () => { cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); @@ -232,11 +232,64 @@ describe('Example 14 - Column Span & Header Grouping', () => { .should('contain', 'Hide Column') .click(); + // goto right + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').click(); + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0.active').should('contain', 'Task 1').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + // goto left + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active') + .contains(/(true|false)+$/) + .type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active').should('contain', 'Task 1'); + }); + + it('should reset Column Picker, click on Spread Hidden Coolumn button then hide Finish column and still expect "5 days" to spread accross 3 column', () => { + cy.get('#grid1').find('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(4)') + .children('label') + .should('contain', 'Period - Finish') + .click(); + + cy.get('.slick-column-picker .close').click(); + + cy.get('[data-test="spread-colspan-button"]').click(); cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); - cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r4').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4').contains(/\d+$/); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + cy.get('#grid1') + .find('.slick-pane-left .slick-header-columns .slick-header-column[role="columnheader"]:nth(3)') + .trigger('mouseover') + .children('.slick-header-menu-button') + .invoke('show') + .click(); + + cy.get('.slick-header-menu .slick-menu-command-list') + .should('be.visible') + .children('.slick-menu-item:nth-of-type(3)') + .children('.slick-menu-content') + .should('contain', 'Hide Column') + .click(); + + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r4').should('contain', '5 days'); cy.get('#grid1') - .find('[data-row=1] .slick-cell.l4.r4') + .find('[data-row=1] .slick-cell.l5.r5') .contains(/(true|false)+$/); }); @@ -292,15 +345,13 @@ describe('Example 14 - Column Span & Header Grouping', () => { describe('First Grid - Key Navigation', () => { it('should start at Task 1 and expect "Duration" to have colspan of 3 and show "% Complete" and "Effort Driven"', () => { cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') - .should('have.class', 'active') - .contains(/(true|false)+$/); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').contains(/(true|false)+$/); cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); @@ -312,16 +363,17 @@ describe('Example 14 - Column Span & Header Grouping', () => { .children('label') .should('contain', 'Period - Finish') .click(); + cy.get('.slick-column-picker .close').click(); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4') + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') .should('have.class', 'active') .contains(/(true|false)+$/); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('have.class', 'active'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); }); diff --git a/demos/react/test/cypress/e2e/example16.cy.ts b/demos/react/test/cypress/e2e/example16.cy.ts index a1a2073bb5..78dabd7ae7 100644 --- a/demos/react/test/cypress/e2e/example16.cy.ts +++ b/demos/react/test/cypress/e2e/example16.cy.ts @@ -408,7 +408,7 @@ describe('Example 16 - Row Move & Checkbox Selector Selector Plugins', () => { }); it('should add Edit/Delete columns and expect 2 new columns added at the beginning of the grid', () => { - const newExpectedColumns = ['', '', ...fullTitles]; + const newExpectedColumns = ['', '', '', '', 'Title', '% Complete', 'Start', 'Finish', 'Duration', 'Completed']; cy.get('[data-test="add-crud-columns-btn"]').click(); cy.get('#grid16') diff --git a/demos/react/test/cypress/e2e/example44.cy.ts b/demos/react/test/cypress/e2e/example44.cy.ts index ab4ee2fcf1..f399ed0364 100644 --- a/demos/react/test/cypress/e2e/example44.cy.ts +++ b/demos/react/test/cypress/e2e/example44.cy.ts @@ -1,4 +1,4 @@ -describe('Example 44 - Column & Row Span', { retries: 1 }, () => { +describe('Example 44 - Column & Row Span', { retries: 0 }, () => { const GRID_ROW_HEIGHT = 30; const fullTitles = [ 'Title', @@ -463,4 +463,102 @@ describe('Example 44 - Column & Row Span', { retries: 1 }, () => { cy.get('[data-row=499] > .slick-cell.l5.r5.active').should('have.length', 1); }); }); + + describe('Hide Columns with colspan/rowspan', () => { + it('should hide Title column and expect other colspan/rowspan to simply move over and stay attached to same columns', () => { + cy.get('[data-row=499] > .slick-cell.l5.r5.active').type('{ctrl}{home}', { release: false }); + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + // Task 2 rowspan should be hidden now + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.not.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should start at "Revenue Growth" second cell down, then type "Arrow Right" key 2x times and expect 4th row "Policy Index" green section to still have a rowspan 3x and colspan of 4x', () => { + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').as('active_cell').click(); + cy.get('[data-row=3] > .slick-cell.l1.r1.active').should('have.length', 1); + cy.get('@active_cell').type('{rightarrow}{rightarrow}'); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should go up by 1x "Arrow Up" and expect blue section colspan of 3x', () => { + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').type('{uparrow}'); + cy.get('[data-row=2] > .slick-cell.l3.r5.active').should('not.have.class', 'rowspan'); + }); + + it('should "Revenue Growth" rowspan should now be at first column and "Policy Index" should now be at third column', () => { + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('not.exist'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + + it('should show again "Title" column and expect "Revenue Growth" and "Policy Index" columns to be moved to the right by 1 column', () => { + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('contain', 'Task 0'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('contain', 'Task 1'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('contain', 'Task 2'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + }); }); diff --git a/demos/vanilla/src/examples/example08.html b/demos/vanilla/src/examples/example08.html index 490c1b4bc9..83aefa28d1 100644 --- a/demos/vanilla/src/examples/example08.html +++ b/demos/vanilla/src/examples/example08.html @@ -12,7 +12,17 @@

-

Grid 1 (with Header Grouping & Colspan)

+

+ Grid 1 (with Header Grouping & ColSpan) + +

diff --git a/demos/vanilla/src/examples/example08.ts b/demos/vanilla/src/examples/example08.ts index 0aac4876d3..12d2291511 100644 --- a/demos/vanilla/src/examples/example08.ts +++ b/demos/vanilla/src/examples/example08.ts @@ -18,6 +18,7 @@ export default class Example08 { grid2SelectedOperator: OperatorString; grid2SearchValue: any; operatorList: OperatorString[] = ['=', '<', '<=', '>', '>=', '<>', 'StartsWith', 'EndsWith']; + isColspanSpreading = false; constructor() { this.definedGrid1(); @@ -95,6 +96,7 @@ export default class Example08 { getRowMetadata: (item: any, row: number) => this.renderDifferentColspan(item, row), }, }, + spreadHiddenColspan: this.isColspanSpreading, }; } @@ -263,6 +265,13 @@ export default class Example08 { this.updateFilter(); } + spreadColspan() { + this.isColspanSpreading = !this.isColspanSpreading; + this.sgb1.slickGrid?.setOptions({ spreadHiddenColspan: this.isColspanSpreading }); + this.sgb1.slickGrid?.resetActiveCell(); + this.sgb1.slickGrid?.invalidate(); + } + updateFilter() { this.sgb2.filterService.updateSingleFilter({ columnId: `${this.grid2SearchSelectedColumn?.id ?? ''}`, diff --git a/demos/vanilla/src/examples/example11.ts b/demos/vanilla/src/examples/example11.ts index 31234d908c..5df3e7982e 100644 --- a/demos/vanilla/src/examples/example11.ts +++ b/demos/vanilla/src/examples/example11.ts @@ -87,6 +87,8 @@ export default class Example11 { isUserDefined: false, columns: [...this.allColumnIds] .map((colId) => ({ columnId: `${colId}` })) + // OR the `hidden` props alternative + // .map((colId) => ({ columnId: `${colId}`, hidden: false })) .filter((col) => col.columnId !== 'product' && col.columnId !== 'countryOfOrigin'), // remove "Product", "Country of Origin" filters: [ { columnId: 'finish', operator: OperatorType.lessThanOrEqual, searchTerms: [`${this.currentYear}-01-01`] }, @@ -100,7 +102,11 @@ export default class Example11 { value: 'greaterCurrentYear', isSelected: false, isUserDefined: false, - columns: [...this.allColumnIds].map((colId) => ({ columnId: `${colId}` })).filter((col) => col.columnId !== 'cost'), // remove "Cost" + columns: [...this.allColumnIds] + .map((colId) => ({ columnId: `${colId}` })) + // OR the `hidden` props alternative + // .map((colId) => ({ columnId: `${colId}`, hidden: false })) + .filter((col) => col.columnId !== 'cost'), // remove "Cost" filters: [{ columnId: 'finish', operator: '>=', searchTerms: [`${this.currentYear + 1}-01-01`] }], sorters: [{ columnId: 'finish', direction: 'asc' }] as CurrentSorter[], }, @@ -451,6 +457,10 @@ export default class Example11 { ], onCommand: (e, args) => this.executeCommand(e, args), }, + + // the `hidden` props alternative, we could use column "hidden" props, then we could also include it in the saved state + // using this flag (below) will result in all columns included in the Grid State including hidden columns + // gridStateIncludeHiddenProps: true, }; const storedData = localStorage.getItem(LOCAL_STORAGE_KEY); @@ -737,8 +747,7 @@ export default class Example11 { } this.predefinedViews.forEach((viewSelect) => (viewSelect.isSelected = false)); // reset selection - const currentGridState = this.sgb.gridStateService.getCurrentGridState(); - const { columns, filters, sorters, pinning } = currentGridState; + const { columns, filters, sorters, pinning } = this.sgb.gridStateService.getCurrentGridState(); const viewName = await prompt('Please provide a name for the new View.'); if (viewName) { @@ -792,8 +801,7 @@ export default class Example11 { event.stopPropagation(); return; } - const currentGridState = this.sgb.gridStateService.getCurrentGridState(); - const { columns, filters, sorters, pinning } = currentGridState; + const { columns, filters, sorters, pinning } = this.sgb.gridStateService.getCurrentGridState(); if (this.currentSelectedViewPreset && filters) { const filterName = await prompt(`Update View name or click on OK to continue.`, this.currentSelectedViewPreset.label); @@ -830,7 +838,11 @@ export default class Example11 { this.sgb.gridService.clearPinning(); this.sgb.filterService.clearFilters(); this.sgb.sortService.clearSorting(); - this.sgb.gridStateService.changeColumnsArrangement([...this.columnDefinitions].map((col) => ({ columnId: `${col.id}` }))); + this.sgb.gridStateService.changeColumnsArrangement( + [...this.columnDefinitions].map((col) => ({ columnId: `${col.id}` })) + // OR the `hidden` props alternative + // [...this.columnDefinitions].map((col) => ({ columnId: `${col.id}`, hidden: false })) + ); } localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this.predefinedViews)); this.currentSelectedViewPreset = selectedView; diff --git a/demos/vanilla/src/examples/example14.ts b/demos/vanilla/src/examples/example14.ts index 542a72fd72..13ebaf8b2e 100644 --- a/demos/vanilla/src/examples/example14.ts +++ b/demos/vanilla/src/examples/example14.ts @@ -763,7 +763,7 @@ export default class Example14 { // just for demo purposes, set it back to its original width const columns = this.sgb.slickGrid?.getColumns() as Column[]; columns.forEach((col) => (col.width = col.originalWidth)); - this.sgb.slickGrid?.setColumns(columns); + this.sgb.slickGrid?.updateColumns(); this.sgb.slickGrid?.autosizeColumns(); // simple css class to change selected button in the UI diff --git a/demos/vanilla/src/examples/example32.ts b/demos/vanilla/src/examples/example32.ts index aad62c4c16..20f8e9fc15 100644 --- a/demos/vanilla/src/examples/example32.ts +++ b/demos/vanilla/src/examples/example32.ts @@ -446,7 +446,8 @@ export default class Example32 { } } - // update column definitions + // 1. update column definitions via grid.setColumns() + // this will shift colspan/rowspan to the left or right accordingly if (this.showEmployeeId) { this.columnDefinitions.unshift({ id: 'employeeID', name: 'Employee ID', field: 'employeeID', width: 100 }); } else { @@ -454,6 +455,22 @@ export default class Example32 { } this.sgb.slickGrid?.setColumns(this.columnDefinitions); + // --- OR --- + // 2. OR update via "hidden" column flag & increase/decrease column index accordingly in the metadata + // this approach will keep colspan/rowspan "as-is" but will hide the EmployeeID column + /* + const colDirIdx = this.showEmployeeId ? -1 : 1; + for (const row of Object.keys(this.metadata)) { + newMetadata[row] = { columns: {} }; + for (const col of Object.keys((this.metadata as any)[row].columns)) { + newMetadata[row].columns[Number(col) + colDirIdx] = (this.metadata as any)[row].columns[col]; + } + } + this.sgb.slickGrid?.setOptions({ frozenColumn: this.showEmployeeId ? 0 : 1 }); + this.sgb.slickGrid?.updateColumnById('employeeID', { hidden: !this.showEmployeeId }); + this.sgb.slickGrid?.updateColumns(); + */ + // update & remap rowspans this.metadata = newMetadata; this.sgb.slickGrid?.remapAllColumnsRowSpan(); diff --git a/demos/vue/src/components/Example14.vue b/demos/vue/src/components/Example14.vue index f4fbcc809c..c4958c379b 100644 --- a/demos/vue/src/components/Example14.vue +++ b/demos/vue/src/components/Example14.vue @@ -11,6 +11,7 @@ const columnDefinitions2: Ref = ref([]); const dataset1 = ref([]); const dataset2 = ref([]); const showSubTitle = ref(true); +let isColspanSpreading = ref(false); let vueGrid1!: SlickgridVueInstance; let vueGrid2!: SlickgridVueInstance; @@ -57,6 +58,7 @@ function definedGrid1() { gridMenu: { iconButtonContainer: 'preheader', // we can display the grid menu icon in either the preheader or in the column header (default) }, + spreadHiddenColspan: isColspanSpreading.value, }; } @@ -147,6 +149,13 @@ function renderDifferentColspan(item: any, row: number): ItemMetadata { }; } +function spreadColspan() { + isColspanSpreading.value = !isColspanSpreading.value; + vueGrid1.slickGrid?.setOptions({ spreadHiddenColspan: isColspanSpreading.value }); + vueGrid1.slickGrid?.resetActiveCell(); + vueGrid1.slickGrid?.invalidate(); +} + function toggleSubTitle() { showSubTitle.value = !showSubTitle.value; const action = showSubTitle.value ? 'remove' : 'add'; @@ -192,7 +201,17 @@ function vueGrid2Ready(grid: SlickgridVueInstance) {
-

Grid 1 (with Header Grouping & Colspan)

+

+ Grid 1 (with Header Grouping & Colspan) + +

(col.width = col.originalWidth)); - vueGrid.slickGrid.setColumns(columns); + vueGrid.slickGrid.updateColumns(); vueGrid.slickGrid.autosizeColumns(); isUsingDefaultResize.value = true; } diff --git a/demos/vue/src/components/Example43.vue b/demos/vue/src/components/Example43.vue index efd1200bae..79bd66f368 100644 --- a/demos/vue/src/components/Example43.vue +++ b/demos/vue/src/components/Example43.vue @@ -422,7 +422,8 @@ function toggleEmployeeIdVisibility() { } } - // update column definitions + // 1. update column definitions via grid.setColumns() + // this will shift colspan/rowspan to the left or right accordingly if (showEmployeeId.value) { columnDefinitions.value.unshift({ id: 'employeeID', name: 'Employee ID', field: 'employeeID', width: 100 }); } else { @@ -430,6 +431,22 @@ function toggleEmployeeIdVisibility() { } vueGrid.slickGrid.setColumns(columnDefinitions.value); + // --- OR --- + // 2. OR update via "hidden" column flag & increase/decrease column index accordingly in the metadata + // this approach will keep colspan/rowspan "as-is" but will hide the EmployeeID column + /* + const colDirIdx = showEmployeeId.value ? -1 : 1; + for (const row of Object.keys(this.metadata)) { + newMetadata[row] = { columns: {} }; + for (const col of Object.keys((this.metadata as any)[row].columns)) { + newMetadata[row].columns[Number(col) + colDirIdx] = (this.metadata as any)[row].columns[col]; + } + } + vueGrid.slickGrid?.setOptions({ frozenColumn: showEmployeeId.value ? 0 : 1 }); + vueGrid.slickGrid?.updateColumnById('employeeID', { hidden: !showEmployeeId.value }); + vueGrid.slickGrid?.updateColumns(); + */ + // update & remap rowspans metadata = newMetadata; vueGrid.slickGrid.remapAllColumnsRowSpan(); diff --git a/demos/vue/test/cypress/e2e/example14.cy.ts b/demos/vue/test/cypress/e2e/example14.cy.ts index 402545254b..91f7db34b6 100644 --- a/demos/vue/test/cypress/e2e/example14.cy.ts +++ b/demos/vue/test/cypress/e2e/example14.cy.ts @@ -21,16 +21,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); it('should have a frozen grid on page load with 3 columns on the left and 4 columns on the right', () => { - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid again', () => { @@ -48,14 +48,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Remove Frozen Columns" button to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { cy.get('[data-test="remove-frozen-column-button"]').click(); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid once again', () => { @@ -73,16 +73,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Set 3 Frozen Columns" button to switch frozen columns grid and expect 3 frozen columns on the left and 4 columns on the right', () => { cy.contains('Set 3 Frozen Columns').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have still exact Column Pre-Header & Column Header Titles in the grid', () => { @@ -102,14 +102,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { cy.contains('Unfreeze Columns/Rows').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should reapply 3 frozen columns on 2nd grid', () => { @@ -209,7 +209,7 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); describe('Colspan checks on 1st grid', () => { - it('should hide Finish column and still expect "5 days" to spread accross 3 column', () => { + it('should hide Finish column and expect "5 days" spread to drop from 3 to 2 columns (1x hidden column)', () => { cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); @@ -232,11 +232,64 @@ describe('Example 14 - Column Span & Header Grouping', () => { .should('contain', 'Hide Column') .click(); + // goto right + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').click(); + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0.active').should('contain', 'Task 1').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + // goto left + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active') + .contains(/(true|false)+$/) + .type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active').should('contain', 'Task 1'); + }); + + it('should reset Column Picker, click on Spread Hidden Coolumn button then hide Finish column and still expect "5 days" to spread accross 3 column', () => { + cy.get('#grid1').find('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(4)') + .children('label') + .should('contain', 'Period - Finish') + .click(); + + cy.get('.slick-column-picker .close').click(); + + cy.get('[data-test="spread-colspan-button"]').click(); cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); - cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r4').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4').contains(/\d+$/); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + cy.get('#grid1') + .find('.slick-pane-left .slick-header-columns .slick-header-column[role="columnheader"]:nth(3)') + .trigger('mouseover') + .children('.slick-header-menu-button') + .invoke('show') + .click(); + + cy.get('.slick-header-menu .slick-menu-command-list') + .should('be.visible') + .children('.slick-menu-item:nth-of-type(3)') + .children('.slick-menu-content') + .should('contain', 'Hide Column') + .click(); + + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r4').should('contain', '5 days'); cy.get('#grid1') - .find('[data-row=1] .slick-cell.l4.r4') + .find('[data-row=1] .slick-cell.l5.r5') .contains(/(true|false)+$/); }); @@ -292,15 +345,13 @@ describe('Example 14 - Column Span & Header Grouping', () => { describe('First Grid - Key Navigation', () => { it('should start at Task 1 and expect "Duration" to have colspan of 3 and show "% Complete" and "Effort Driven"', () => { cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') - .should('have.class', 'active') - .contains(/(true|false)+$/); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').contains(/(true|false)+$/); cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); @@ -312,16 +363,17 @@ describe('Example 14 - Column Span & Header Grouping', () => { .children('label') .should('contain', 'Period - Finish') .click(); + cy.get('.slick-column-picker .close').click(); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4') + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') .should('have.class', 'active') .contains(/(true|false)+$/); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('have.class', 'active'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); }); diff --git a/demos/vue/test/cypress/e2e/example16.cy.ts b/demos/vue/test/cypress/e2e/example16.cy.ts index a1a2073bb5..78dabd7ae7 100644 --- a/demos/vue/test/cypress/e2e/example16.cy.ts +++ b/demos/vue/test/cypress/e2e/example16.cy.ts @@ -408,7 +408,7 @@ describe('Example 16 - Row Move & Checkbox Selector Selector Plugins', () => { }); it('should add Edit/Delete columns and expect 2 new columns added at the beginning of the grid', () => { - const newExpectedColumns = ['', '', ...fullTitles]; + const newExpectedColumns = ['', '', '', '', 'Title', '% Complete', 'Start', 'Finish', 'Duration', 'Completed']; cy.get('[data-test="add-crud-columns-btn"]').click(); cy.get('#grid16') diff --git a/demos/vue/test/cypress/e2e/example44.cy.ts b/demos/vue/test/cypress/e2e/example44.cy.ts index 6f70275d7a..f399ed0364 100644 --- a/demos/vue/test/cypress/e2e/example44.cy.ts +++ b/demos/vue/test/cypress/e2e/example44.cy.ts @@ -463,4 +463,102 @@ describe('Example 44 - Column & Row Span', { retries: 0 }, () => { cy.get('[data-row=499] > .slick-cell.l5.r5.active').should('have.length', 1); }); }); + + describe('Hide Columns with colspan/rowspan', () => { + it('should hide Title column and expect other colspan/rowspan to simply move over and stay attached to same columns', () => { + cy.get('[data-row=499] > .slick-cell.l5.r5.active').type('{ctrl}{home}', { release: false }); + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + // Task 2 rowspan should be hidden now + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.not.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should start at "Revenue Growth" second cell down, then type "Arrow Right" key 2x times and expect 4th row "Policy Index" green section to still have a rowspan 3x and colspan of 4x', () => { + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').as('active_cell').click(); + cy.get('[data-row=3] > .slick-cell.l1.r1.active').should('have.length', 1); + cy.get('@active_cell').type('{rightarrow}{rightarrow}'); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should go up by 1x "Arrow Up" and expect blue section colspan of 3x', () => { + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').type('{uparrow}'); + cy.get('[data-row=2] > .slick-cell.l3.r5.active').should('not.have.class', 'rowspan'); + }); + + it('should "Revenue Growth" rowspan should now be at first column and "Policy Index" should now be at third column', () => { + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('not.exist'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + + it('should show again "Title" column and expect "Revenue Growth" and "Policy Index" columns to be moved to the right by 1 column', () => { + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('contain', 'Task 0'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('contain', 'Task 1'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('contain', 'Task 2'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + }); }); diff --git a/docs/grid-functionalities/grid-state-preset.md b/docs/grid-functionalities/grid-state-preset.md index db7b02965a..9210456c47 100644 --- a/docs/grid-functionalities/grid-state-preset.md +++ b/docs/grid-functionalities/grid-state-preset.md @@ -54,6 +54,8 @@ export class GridExample { } ``` +> **Note** since v10 you can now pass `true` as the argument to `gridStateService.getCurrentGridState(true)` which will return all columns, not just the visible columns but also include the hidden columns and their "hidden" properties. + ### Using Grid Presets & Filter SearchTerm(s) What happens when we use the grid `presets` and a [Filter Default SearchTerms](../column-functionalities/filters/Select-Filter.md#default-search-terms)? In this case, the `presets` will win over filter `searchTerms`. The cascading order of priorities is the following 1. Do we have any `presets`? Yes use them, else go to step 2 @@ -194,6 +196,10 @@ You can show/hide or even change a column position via the `presets`, yes `prese So let say that we want to hide the last Column on page load, we can just find the column by it's `id` that you want to hide and pass the new column definition to the `presets` (again make sure to follow the correct preset structure). +#### Option 1 + +Pass the Grid Presets with an array that has less `presets.columns`, whichever column(s) are missing will be considered hidden columns + ```ts this.columnDefinitions = [ // initial column definitions @@ -204,7 +210,7 @@ this.columnDefinitions = [ const mappedColumnDefinitions = this.columnDefinitions.map(col => ({ columnId: col.id, width: col.width })); mappedColumnDefinitions.pop(); -// then pass it to the presets +// then pass it to the grid presets (an array of columns minus the last column) this.gridOptions = { presets: { columns: mappedColumnDefinitions @@ -213,6 +219,11 @@ this.gridOptions = { ``` This would be the easiest way to do it. +#### Option 2 + +Since v10, the second alternative is to pass all the columns to `presets.columns` with some of them having the `hidden` properties. Both approaches are valid in v10, just choose whichever option you prefer. + +###### Summary As pointed out earlier, the `presets` requires a specific structure where the `columns` is the list of columns to show/hide with their possible widths. Also worth mentioning again that the position in the array is very important as it defines the position shown in the UI. ##### ViewModel @@ -231,4 +242,4 @@ this.gridOptions = { } }; ``` -You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but go manual is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` and that would work as well. \ No newline at end of file +You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but loading them manually is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` (maybe include the `hidden` prop as well) and that would work as well. \ No newline at end of file diff --git a/docs/grid-functionalities/row-based-edit.md b/docs/grid-functionalities/row-based-edit.md index 8935cad3dd..9eb226c940 100644 --- a/docs/grid-functionalities/row-based-edit.md +++ b/docs/grid-functionalities/row-based-edit.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Description The Row based editing plugin makes it possible to keep the grid readonly except for rows which the user explicitely toggles into edit mode. diff --git a/docs/grid-functionalities/row-detail.md b/docs/grid-functionalities/row-detail.md index 80de8826be..d082d22d87 100644 --- a/docs/grid-functionalities/row-detail.md +++ b/docs/grid-functionalities/row-detail.md @@ -6,7 +6,7 @@ - [Row Detail - View Component](#row-detail---view-component) - [Access Parent Component (grid) from the Child Component (row detail)](#access-parent-component-grid-from-the-child-component-row-detail) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Demo [Demo Page](https://ghiscoding.github.io/slickgrid-universal/#/example21) / [Demo ViewModel](https://github.com/ghiscoding/slickgrid-universal/blob/master/demos/vanilla/src/examples/example21.ts) diff --git a/docs/grid-functionalities/row-selection.md b/docs/grid-functionalities/row-selection.md index 19a9e8f3f2..6957bf613d 100644 --- a/docs/grid-functionalities/row-selection.md +++ b/docs/grid-functionalities/row-selection.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) - [Hybrid Selection Model (cell+row selection)](#hybrid-selection-model-and-drag-fill) ### Description @@ -336,7 +336,7 @@ export class Example1 { ``` ## Troubleshooting -### Adding a Column dynamically is removing the Row Selection, why is that? +### Adding a Column dynamically is removing the Row Selection column, why is that? The reason is because the Row Selection (checkbox) plugin is a special column and Slickgrid-Universal is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columnDefinitions`. To address this issue, you need to get the Slickgrid-Universal internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work. ```ts diff --git a/frameworks/angular-slickgrid/docs/grid-functionalities/Row-based-edit.md b/frameworks/angular-slickgrid/docs/grid-functionalities/Row-based-edit.md index fd12263a79..05d91cc4b4 100644 --- a/frameworks/angular-slickgrid/docs/grid-functionalities/Row-based-edit.md +++ b/frameworks/angular-slickgrid/docs/grid-functionalities/Row-based-edit.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Description The Row based editing plugin makes it possible to keep the grid readonly except for rows which the user explicitely toggles into edit mode. diff --git a/frameworks/angular-slickgrid/docs/grid-functionalities/grid-state-preset.md b/frameworks/angular-slickgrid/docs/grid-functionalities/grid-state-preset.md index 46cf1cd585..b09269fc36 100644 --- a/frameworks/angular-slickgrid/docs/grid-functionalities/grid-state-preset.md +++ b/frameworks/angular-slickgrid/docs/grid-functionalities/grid-state-preset.md @@ -58,6 +58,8 @@ export class GridDemoComponent { } ``` +> **Note** since v10 you can now pass `true` as the argument to `gridStateService.getCurrentGridState(true)` which will return all columns, not just the visible columns but also include the hidden columns and their "hidden" properties. + ### Using Grid Presets & Filter SearchTerm(s) What happens when we use the grid `presets` and a [Filter Default SearchTerms](../column-functionalities/filters/select-filter.md#default-search-terms)? In this case, the `presets` will win over filter `searchTerms`. The cascading order of priorities is the following 1. Do we have any `presets`? Yes use them, else go to step 2 @@ -209,6 +211,10 @@ You can show/hide or even change a column position via the `presets`, yes `prese So let say that we want to hide the last Column on page load, we can just find the column by it's `id` that you want to hide and pass the new column definition to the `presets` (again make sure to follow the correct preset structure). +#### Option 1 + +Pass the Grid Presets with an array that has less `presets.columns`, whichever column(s) are missing will be considered hidden columns + ```ts this.columnDefinitions = [ // initial column definitions @@ -219,7 +225,7 @@ this.columnDefinitions = [ const mappedColumnDefinitions = this.columnDefinitions.map(col => ({ columnId: col.id, width: col.width })); mappedColumnDefinitions.pop(); -// then pass it to the presets +// then pass it to the grid presets (an array of columns minus the last column) this.gridOptions = { presets: { columns: mappedColumnDefinitions @@ -228,6 +234,11 @@ this.gridOptions = { ``` This would be the easiest way to do it. +#### Option 2 + +Since v10, the second alternative is to pass all the columns to `presets.columns` with some of them having the `hidden` properties. Both approaches are valid in v10, just choose whichever option you prefer. + +###### Summary As pointed out earlier, the `presets` requires a specific structure where the `columns` is the list of columns to show/hide with their possible widths. Also worth mentioning again that the position in the array is very important as it defines the position shown in the UI. ```ts @@ -245,4 +256,4 @@ this.gridOptions = { } }; ``` -You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but go manual is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` and that would work as well. +You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but loading them manually is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` (maybe include the `hidden` prop as well) and that would work as well. diff --git a/frameworks/angular-slickgrid/docs/grid-functionalities/row-detail.md b/frameworks/angular-slickgrid/docs/grid-functionalities/row-detail.md index 0cd91f62a3..34e445d382 100644 --- a/frameworks/angular-slickgrid/docs/grid-functionalities/row-detail.md +++ b/frameworks/angular-slickgrid/docs/grid-functionalities/row-detail.md @@ -6,7 +6,7 @@ - [Row Detail - View Component](#row-detail---view-component) - [Access Parent Component (grid) from the Child Component (row detail)](#access-parent-component-grid-from-the-child-component-row-detail) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Demo [Demo Page](https://ghiscoding.github.io/angular-slickgrid-demos/#/rowdetail) / [Demo Component](https://github.com/ghiscoding/slickgrid-universal/blob/master/frameworks/angular-slickgrid/src/demos/examples/grid-rowdetail.component.ts) @@ -357,7 +357,7 @@ export class RowDetailViewComponent { ``` ## Troubleshooting -### Adding a Column dynamically is removing the Row Selection, why is that? +### Adding a Column dynamically is removing the Row Selection column, why is that? The reason is because the Row Selection (checkbox) plugin is a special column and Angular-Slickgrid is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columnDefinitions`. To address this issue, you need to get the Angular-Slickgrid internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work. ```html diff --git a/frameworks/angular-slickgrid/docs/grid-functionalities/row-selection.md b/frameworks/angular-slickgrid/docs/grid-functionalities/row-selection.md index 66f33b0823..6766676d41 100644 --- a/frameworks/angular-slickgrid/docs/grid-functionalities/row-selection.md +++ b/frameworks/angular-slickgrid/docs/grid-functionalities/row-selection.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) - [Hybrid Selection Model (cell+row selection)](#hybrid-selection-model-and-drag-fill) ### Description @@ -290,7 +290,7 @@ copyDraggedCellRange(args: OnDragReplaceCellsEventArgs) { ``` ## Troubleshooting -### Adding a Column dynamically is removing the Row Selection, why is that? +### Adding a Column dynamically is removing the Row Selection column, why is that? The reason is because the Row Selection (checkbox) plugin is a special column and Angular-Slickgrid is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columnDefinitions`. To address this issue, you need to get the Angular-Slickgrid internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work. ```html diff --git a/frameworks/angular-slickgrid/src/demos/examples/example14.component.html b/frameworks/angular-slickgrid/src/demos/examples/example14.component.html index e9b9c7f5cd..92da573bb1 100644 --- a/frameworks/angular-slickgrid/src/demos/examples/example14.component.html +++ b/frameworks/angular-slickgrid/src/demos/examples/example14.component.html @@ -29,8 +29,26 @@

-

Grid 1 (with Header Grouping & Colspan)

- +

+ Grid 1 (with Header Grouping & Colspan) + +

+ + +
diff --git a/frameworks/angular-slickgrid/src/demos/examples/example14.component.ts b/frameworks/angular-slickgrid/src/demos/examples/example14.component.ts index 332d5282fd..6cf0413b92 100644 --- a/frameworks/angular-slickgrid/src/demos/examples/example14.component.ts +++ b/frameworks/angular-slickgrid/src/demos/examples/example14.component.ts @@ -8,6 +8,7 @@ import { AngularSlickgridModule, type AngularGridInstance, type Column, type Gri imports: [AngularSlickgridModule], }) export class Example14Component implements OnInit { + angularGrid1!: AngularGridInstance; angularGrid2!: AngularGridInstance; gridObj2: any; columnDefinitions1!: Column[]; @@ -17,12 +18,17 @@ export class Example14Component implements OnInit { dataset1: any[] = []; dataset2: any[] = []; hideSubTitle = false; + isColspanSpreading = false; ngOnInit(): void { this.prepareGrid1(); this.prepareGrid2(); } + angularGridReady1(angularGrid: AngularGridInstance) { + this.angularGrid1 = angularGrid; + } + angularGridReady2(angularGrid: AngularGridInstance) { this.angularGrid2 = angularGrid; this.gridObj2 = angularGrid.slickGrid; @@ -61,6 +67,7 @@ export class Example14Component implements OnInit { exportWithFormatter: false, }, externalResources: [new ExcelExportService()], + spreadHiddenColspan: this.isColspanSpreading, }; this.dataset1 = this.getData(500); @@ -154,6 +161,13 @@ export class Example14Component implements OnInit { }; } + spreadColspan() { + this.isColspanSpreading = !this.isColspanSpreading; + this.angularGrid1.slickGrid?.setOptions({ spreadHiddenColspan: this.isColspanSpreading }); + this.angularGrid1.slickGrid?.resetActiveCell(); + this.angularGrid1.slickGrid?.invalidate(); + } + toggleSubTitle() { this.hideSubTitle = !this.hideSubTitle; const action = this.hideSubTitle ? 'add' : 'remove'; diff --git a/frameworks/angular-slickgrid/src/demos/examples/example16.component.ts b/frameworks/angular-slickgrid/src/demos/examples/example16.component.ts index a5ce836a9a..5f4cb62603 100644 --- a/frameworks/angular-slickgrid/src/demos/examples/example16.component.ts +++ b/frameworks/angular-slickgrid/src/demos/examples/example16.component.ts @@ -246,7 +246,7 @@ export class Example16Component implements OnInit { params: { iconCssClass: 'mdi mdi-pencil pointer' }, minWidth: 30, maxWidth: 30, - onCellClick: (clickEvent: Event, args: OnEventArgs) => { + onCellClick: (_clickEvent: Event, args: OnEventArgs) => { alert(`Technically we should Edit "Task ${args.dataContext.id}"`); }, }, @@ -260,7 +260,7 @@ export class Example16Component implements OnInit { params: { iconCssClass: 'mdi mdi-trash-can pointer' }, minWidth: 30, maxWidth: 30, - onCellClick: (e: Event, args: OnEventArgs) => { + onCellClick: (_e: Event, args: OnEventArgs) => { if (confirm('Are you sure?')) { this.angularGrid.gridService.deleteItemById(args.dataContext.id); } @@ -268,8 +268,12 @@ export class Example16Component implements OnInit { }, ]; - this.columnDefinitions.splice(0, 0, newCols[0], newCols[1]); - this.columnDefinitions = this.columnDefinitions.slice(); // or use spread operator [...cols] to trigger change + // NOTE if you use an Extensions (Checkbox Selector, Row Detail, ...) that modifies the column definitions in any way + // you MUST use "getAllColumnDefinitions()" from the GridService, using this will be ALL columns including the 1st column that is created internally + // for example if you use the Checkbox Selector (row selection), you MUST use the code below + const allColumns = this.angularGrid.gridService.getAllColumnDefinitions(); + allColumns.unshift(newCols[0], newCols[1]); + this.columnDefinitions = [...allColumns]; // (or use slice) reassign to column definitions for Aurelia to do dirty checking } } diff --git a/frameworks/angular-slickgrid/src/demos/examples/example32.component.ts b/frameworks/angular-slickgrid/src/demos/examples/example32.component.ts index 3e5d485ced..11187dc4d0 100644 --- a/frameworks/angular-slickgrid/src/demos/examples/example32.component.ts +++ b/frameworks/angular-slickgrid/src/demos/examples/example32.component.ts @@ -587,7 +587,7 @@ export class Example32Component implements OnInit { // just for demo purposes, set it back to its original width const columns = this.angularGrid.slickGrid.getColumns() as Column[]; columns.forEach((col) => (col.width = col.originalWidth)); - this.angularGrid.slickGrid.setColumns(columns); + this.angularGrid.slickGrid.updateColumns(); this.angularGrid.slickGrid.autosizeColumns(); this.isUsingDefaultResize = true; } diff --git a/frameworks/angular-slickgrid/src/demos/examples/example43.component.ts b/frameworks/angular-slickgrid/src/demos/examples/example43.component.ts index 04839d144b..580f354203 100644 --- a/frameworks/angular-slickgrid/src/demos/examples/example43.component.ts +++ b/frameworks/angular-slickgrid/src/demos/examples/example43.component.ts @@ -433,7 +433,8 @@ export class Example43Component implements OnInit { } } - // update column definitions + // 1. update column definitions via grid.setColumns() + // this will shift colspan/rowspan to the left or right accordingly if (this.showEmployeeId) { this.columnDefinitions.unshift({ id: 'employeeID', name: 'Employee ID', field: 'employeeID', width: 100 }); } else { @@ -441,6 +442,22 @@ export class Example43Component implements OnInit { } this.angularGrid.slickGrid.setColumns(this.columnDefinitions); + // --- OR --- + // 2. OR update via "hidden" column flag & increase/decrease column index accordingly in the metadata + // this approach will keep colspan/rowspan "as-is" but will hide the EmployeeID column + /* + const colDirIdx = this.showEmployeeId ? -1 : 1; + for (const row of Object.keys(this.metadata)) { + newMetadata[row] = { columns: {} }; + for (const col of Object.keys((this.metadata as any)[row].columns)) { + newMetadata[row].columns[Number(col) + colDirIdx] = (this.metadata as any)[row].columns[col]; + } + } + this.angularGrid.slickGrid?.setOptions({ frozenColumn: this.showEmployeeId ? 0 : 1 }); + this.angularGrid.slickGrid?.updateColumnById('employeeID', { hidden: !this.showEmployeeId }); + this.angularGrid.slickGrid?.updateColumns(); + */ + // update & remap rowspans this.metadata = newMetadata; this.angularGrid.slickGrid.remapAllColumnsRowSpan(); diff --git a/frameworks/angular-slickgrid/src/library/components/__tests__/angular-slickgrid.component.spec.ts b/frameworks/angular-slickgrid/src/library/components/__tests__/angular-slickgrid.component.spec.ts index e31c903031..4c01cd2d93 100644 --- a/frameworks/angular-slickgrid/src/library/components/__tests__/angular-slickgrid.component.spec.ts +++ b/frameworks/angular-slickgrid/src/library/components/__tests__/angular-slickgrid.component.spec.ts @@ -260,6 +260,8 @@ const mockGrid = { getRenderedRange: vi.fn(), getSelectionModel: vi.fn(), getScrollbarDimensions: vi.fn(), + updateColumnById: vi.fn(), + updateColumns: vi.fn(), updateRow: vi.fn(), render: vi.fn(), registerPlugin: vi.fn(), @@ -498,8 +500,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = expect(sharedService.frozenVisibleColumnId).toBe('name'); }); - it('should update "visibleColumns" in the Shared Service when "onColumnsReordered" event is triggered', () => { - const sharedVisibleColumnsSpy = vi.spyOn(SharedService.prototype, 'visibleColumns', 'set'); + it('should assign "hasColumnReordered: true" when "onColumnsReordered" event is triggered', () => { const newVisibleColumns = [ { id: 'lastName', field: 'lastName' }, { id: 'firstName', field: 'firstName' }, @@ -511,7 +512,6 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = expect(component.eventHandler).toEqual(slickEventHandler); expect(sharedService.hasColumnsReordered).toBe(true); - expect(sharedVisibleColumnsSpy).toHaveBeenCalledWith(newVisibleColumns); }); it('should change Dark Mode by using "setOptions" when triggered with "onSetOptions" event', () => { @@ -1762,7 +1762,7 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = it('should reflect columns in the grid', () => { const mockColsPresets = [{ columnId: 'firstName', width: 100 }]; - const mockCols = [{ id: 'firstName', field: 'firstName' }]; + const mockCols = [{ id: 'firstName', field: 'firstName', editorClass: undefined, hidden: false }]; const getAssocColSpy = vi.spyOn(gridStateServiceStub, 'getAssociatedGridColumns').mockReturnValue(mockCols); const setColSpy = vi.spyOn(mockGrid, 'setColumns'); @@ -1776,7 +1776,10 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = it('should reflect columns with an extra checkbox selection column in the grid when "enableCheckboxSelector" is set', () => { const mockColsPresets = [{ columnId: 'firstName', width: 100 }]; const mockCol = { id: 'firstName', field: 'firstName' }; - const mockCols = [{ id: '_checkbox_selector', field: '_checkbox_selector', editor: undefined }, mockCol]; + const mockCols = [ + { id: '_checkbox_selector', field: '_checkbox_selector', editor: undefined, editorClass: undefined, hidden: false }, + { ...mockCol, editorClass: undefined, hidden: false }, + ]; const getAssocColSpy = vi.spyOn(gridStateServiceStub, 'getAssociatedGridColumns').mockReturnValue([mockCol]); const setColSpy = vi.spyOn(mockGrid, 'setColumns'); @@ -1791,7 +1794,10 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = it('should reflect columns with an extra row detail column in the grid when "enableRowDetailView" is set', () => { const mockColsPresets = [{ columnId: 'firstName', width: 100 }]; const mockCol = { id: 'firstName', field: 'firstName' }; - const mockCols = [{ id: '_detail_selector', field: '_detail_selector', editor: undefined }, mockCol]; + const mockCols = [ + { id: '_detail_selector', field: '_detail_selector', editor: undefined, editorClass: undefined, hidden: false }, + { ...mockCol, editorClass: undefined, hidden: false }, + ]; const getAssocColSpy = vi.spyOn(gridStateServiceStub, 'getAssociatedGridColumns').mockReturnValue([mockCol]); const setColSpy = vi.spyOn(mockGrid, 'setColumns'); @@ -1806,7 +1812,10 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = it('should reflect columns with an extra row move column in the grid when "enableRowMoveManager" is set', () => { const mockColsPresets = [{ columnId: 'firstName', width: 100 }]; const mockCol = { id: 'firstName', field: 'firstName' }; - const mockCols = [{ id: '_move', field: '_move', editor: undefined }, mockCol]; + const mockCols = [ + { id: '_move', field: '_move', editor: undefined, editorClass: undefined, hidden: false }, + { ...mockCol, editorClass: undefined, hidden: false }, + ]; const getAssocColSpy = vi.spyOn(gridStateServiceStub, 'getAssociatedGridColumns').mockReturnValue([mockCol]); const setColSpy = vi.spyOn(mockGrid, 'setColumns'); @@ -1822,10 +1831,10 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = const mockColsPresets = [{ columnId: 'firstName', width: 100 }]; const mockCol = { id: 'firstName', field: 'firstName' }; const mockCols = [ - { id: '_move', field: '_move', editor: undefined }, - { id: '_checkbox_selector', field: '_checkbox_selector', editor: undefined }, - { id: '_detail_selector', field: '_detail_selector', editor: undefined }, - mockCol, + { id: '_move', field: '_move', editor: undefined, editorClass: undefined, hidden: false }, + { id: '_checkbox_selector', field: '_checkbox_selector', editor: undefined, editorClass: undefined, hidden: false }, + { id: '_detail_selector', field: '_detail_selector', editor: undefined, editorClass: undefined, hidden: false }, + { ...mockCol, editorClass: undefined, hidden: false }, ]; const getAssocColSpy = vi.spyOn(gridStateServiceStub, 'getAssociatedGridColumns').mockReturnValue([mockCol]); const setColSpy = vi.spyOn(mockGrid, 'setColumns'); diff --git a/frameworks/angular-slickgrid/src/library/components/angular-slickgrid.component.ts b/frameworks/angular-slickgrid/src/library/components/angular-slickgrid.component.ts index 53b6ff3dff..2f4917ec54 100644 --- a/frameworks/angular-slickgrid/src/library/components/angular-slickgrid.component.ts +++ b/frameworks/angular-slickgrid/src/library/components/angular-slickgrid.component.ts @@ -41,6 +41,7 @@ import { SlickGrid, SlickgridConfig, SlickGroupItemMetadataProvider, + sortPresetColumns, SortService, TreeDataService, unsubscribeAll, @@ -712,7 +713,6 @@ export class AngularSlickgridComponent implements AfterViewInit, On // save reference for all columns before they optionally become hidden/visible this.sharedService.allColumns = this._columnDefinitions; - this.sharedService.visibleColumns = this._columnDefinitions; // before certain extentions/plugins potentially adds extra columns not created by the user itself (RowMove, RowDetail, RowSelections) // we'll subscribe to the event and push back the change to the user so they always use full column defs array including extra cols @@ -1013,9 +1013,8 @@ export class AngularSlickgridComponent implements AfterViewInit, On if (this.options.enableTranslate) { this.extensionService.translateColumnHeaders(undefined, newColumns); - } else { - this.extensionService.renderColumnHeaders(newColumns, true); } + this.extensionService.renderColumnHeaders(newColumns, true); if (this.options?.enableAutoSizeColumns) { this.slickGrid.autosizeColumns(); @@ -1117,10 +1116,9 @@ export class AngularSlickgridComponent implements AfterViewInit, On } } - // when column are reordered, we need to update the visibleColumn array - this._eventHandler.subscribe(grid.onColumnsReordered, (_e, args) => { + // when column are reordered, we need to update SharedService flag + this._eventHandler.subscribe(grid.onColumnsReordered, () => { this.sharedService.hasColumnsReordered = true; - this.sharedService.visibleColumns = args.impactedColumns; }); this._eventHandler.subscribe(grid.onSetOptions, (_e, args) => { @@ -1447,9 +1445,9 @@ export class AngularSlickgridComponent implements AfterViewInit, On // We will use this when doing a resize by cell content, if user provided a `width` it won't override it. gridPresetColumns.forEach((col) => (col.originalWidth = col.width)); - // finally set the new presets columns (including checkbox selector if need be) - this.slickGrid.setColumns(gridPresetColumns); - this.sharedService.visibleColumns = gridPresetColumns; + // finally sort and set the new presets columns (including checkbox selector if need be) + const orderedColumns = sortPresetColumns(this._columnDefinitions, gridPresetColumns); + this.slickGrid.setColumns(orderedColumns); } } } diff --git a/frameworks/angular-slickgrid/test/cypress/e2e/example14.cy.ts b/frameworks/angular-slickgrid/test/cypress/e2e/example14.cy.ts index 402545254b..91f7db34b6 100644 --- a/frameworks/angular-slickgrid/test/cypress/e2e/example14.cy.ts +++ b/frameworks/angular-slickgrid/test/cypress/e2e/example14.cy.ts @@ -21,16 +21,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); it('should have a frozen grid on page load with 3 columns on the left and 4 columns on the right', () => { - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]> .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]> .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid again', () => { @@ -48,14 +48,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Remove Frozen Columns" button to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { cy.get('[data-test="remove-frozen-column-button"]').click(); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should have exact Column Pre-Header & Column Header Titles in the grid once again', () => { @@ -73,16 +73,16 @@ describe('Example 14 - Column Span & Header Grouping', () => { it('should click on the "Set 3 Frozen Columns" button to switch frozen columns grid and expect 3 frozen columns on the left and 4 columns on the right', () => { cy.contains('Set 3 Frozen Columns').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 2); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 3); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0]`).children().should('have.length', 4); + cy.get('#grid2').find('[data-row=0]').should('have.length', 2); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 3); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0]').children().should('have.length', 4); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(0)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-right > [data-row=0] > .slick-cell:nth(1)').should('contain', '01/05/2009'); }); it('should have still exact Column Pre-Header & Column Header Titles in the grid', () => { @@ -102,14 +102,14 @@ describe('Example 14 - Column Span & Header Grouping', () => { cy.contains('Unfreeze Columns/Rows').click({ force: true }); - cy.get('#grid2').find(`[data-row=0]`).should('have.length', 1); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0]`).children().should('have.length', 7); + cy.get('#grid2').find('[data-row=0]').should('have.length', 1); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0]').children().should('have.length', 7); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)`).should('contain', '0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)`).should('contain', 'Task 0'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)`).should('contain', '5 days'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)`).should('contain', '01/01/2009'); - cy.get('#grid2').find(`.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)`).should('contain', '01/05/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(0)').should('contain', '0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(1)').should('contain', 'Task 0'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(2)').should('contain', '5 days'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(3)').should('contain', '01/01/2009'); + cy.get('#grid2').find('.grid-canvas-left > [data-row=0] > .slick-cell:nth(4)').should('contain', '01/05/2009'); }); it('should reapply 3 frozen columns on 2nd grid', () => { @@ -209,7 +209,7 @@ describe('Example 14 - Column Span & Header Grouping', () => { }); describe('Colspan checks on 1st grid', () => { - it('should hide Finish column and still expect "5 days" to spread accross 3 column', () => { + it('should hide Finish column and expect "5 days" spread to drop from 3 to 2 columns (1x hidden column)', () => { cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); @@ -232,11 +232,64 @@ describe('Example 14 - Column Span & Header Grouping', () => { .should('contain', 'Hide Column') .click(); + // goto right + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').click(); + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0.active').should('contain', 'Task 1').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + // goto left + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active') + .contains(/(true|false)+$/) + .type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active').should('contain', 'Task 1'); + }); + + it('should reset Column Picker, click on Spread Hidden Coolumn button then hide Finish column and still expect "5 days" to spread accross 3 column', () => { + cy.get('#grid1').find('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(4)') + .children('label') + .should('contain', 'Period - Finish') + .click(); + + cy.get('.slick-column-picker .close').click(); + + cy.get('[data-test="spread-colspan-button"]').click(); cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); - cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r4').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r3').should('contain', '5 days'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l4.r4').contains(/\d+$/); + cy.get('#grid1') + .find('[data-row=1] .slick-cell.l5.r5') + .contains(/(true|false)+$/); + + cy.get('#grid1') + .find('.slick-pane-left .slick-header-columns .slick-header-column[role="columnheader"]:nth(3)') + .trigger('mouseover') + .children('.slick-header-menu-button') + .invoke('show') + .click(); + + cy.get('.slick-header-menu .slick-menu-command-list') + .should('be.visible') + .children('.slick-menu-item:nth-of-type(3)') + .children('.slick-menu-content') + .should('contain', 'Hide Column') + .click(); + + cy.get('#grid1').find('[data-row=1] .slick-cell.l0.r0').should('contain', 'Task 1'); + cy.get('#grid1').find('[data-row=2] .slick-cell.l0.r5').should('contain', 'Task 2'); + cy.get('#grid1').find('[data-row=1] .slick-cell.l1.r4').should('contain', '5 days'); cy.get('#grid1') - .find('[data-row=1] .slick-cell.l4.r4') + .find('[data-row=1] .slick-cell.l5.r5') .contains(/(true|false)+$/); }); @@ -292,15 +345,13 @@ describe('Example 14 - Column Span & Header Grouping', () => { describe('First Grid - Key Navigation', () => { it('should start at Task 1 and expect "Duration" to have colspan of 3 and show "% Complete" and "Effort Driven"', () => { cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') - .should('have.class', 'active') - .contains(/(true|false)+$/); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').contains(/(true|false)+$/); cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4').should('have.class', 'active').contains(/\d+$/).type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days'); + cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').contains(/\d+$/).type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3.active').should('contain', '5 days'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); @@ -312,16 +363,17 @@ describe('Example 14 - Column Span & Header Grouping', () => { .children('label') .should('contain', 'Period - Finish') .click(); + cy.get('.slick-column-picker .close').click(); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1').click().type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{rightArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4') + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{rightArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5') .should('have.class', 'active') .contains(/(true|false)+$/); - cy.get('#grid1 [data-row=1] > .slick-cell.l4.r4.active').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l1.r3').should('have.class', 'active').should('contain', '5 days').type('{leftArrow}'); - cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('have.class', 'active'); + cy.get('#grid1 [data-row=1] > .slick-cell.l5.r5.active').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l1.r4.active').should('contain', '5 days').type('{leftArrow}'); + cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0.active'); cy.get('#grid1 [data-row=1] > .slick-cell.l0.r0').should('contain', 'Task 1'); }); }); diff --git a/frameworks/angular-slickgrid/test/cypress/e2e/example16.cy.ts b/frameworks/angular-slickgrid/test/cypress/e2e/example16.cy.ts index a1a2073bb5..78dabd7ae7 100644 --- a/frameworks/angular-slickgrid/test/cypress/e2e/example16.cy.ts +++ b/frameworks/angular-slickgrid/test/cypress/e2e/example16.cy.ts @@ -408,7 +408,7 @@ describe('Example 16 - Row Move & Checkbox Selector Selector Plugins', () => { }); it('should add Edit/Delete columns and expect 2 new columns added at the beginning of the grid', () => { - const newExpectedColumns = ['', '', ...fullTitles]; + const newExpectedColumns = ['', '', '', '', 'Title', '% Complete', 'Start', 'Finish', 'Duration', 'Completed']; cy.get('[data-test="add-crud-columns-btn"]').click(); cy.get('#grid16') diff --git a/frameworks/angular-slickgrid/test/cypress/e2e/example44.cy.ts b/frameworks/angular-slickgrid/test/cypress/e2e/example44.cy.ts index 6f70275d7a..f399ed0364 100644 --- a/frameworks/angular-slickgrid/test/cypress/e2e/example44.cy.ts +++ b/frameworks/angular-slickgrid/test/cypress/e2e/example44.cy.ts @@ -463,4 +463,102 @@ describe('Example 44 - Column & Row Span', { retries: 0 }, () => { cy.get('[data-row=499] > .slick-cell.l5.r5.active').should('have.length', 1); }); }); + + describe('Hide Columns with colspan/rowspan', () => { + it('should hide Title column and expect other colspan/rowspan to simply move over and stay attached to same columns', () => { + cy.get('[data-row=499] > .slick-cell.l5.r5.active').type('{ctrl}{home}', { release: false }); + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + // Task 2 rowspan should be hidden now + cy.get('[data-row=2] > .slick-cell.l0.r0.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.not.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should start at "Revenue Growth" second cell down, then type "Arrow Right" key 2x times and expect 4th row "Policy Index" green section to still have a rowspan 3x and colspan of 4x', () => { + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').as('active_cell').click(); + cy.get('[data-row=3] > .slick-cell.l1.r1.active').should('have.length', 1); + cy.get('@active_cell').type('{rightarrow}{rightarrow}'); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + }); + + it('should go up by 1x "Arrow Up" and expect blue section colspan of 3x', () => { + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').type('{uparrow}'); + cy.get('[data-row=2] > .slick-cell.l3.r5.active').should('not.have.class', 'rowspan'); + }); + + it('should "Revenue Growth" rowspan should now be at first column and "Policy Index" should now be at third column', () => { + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('not.exist'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('not.exist'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + + it('should show again "Title" column and expect "Revenue Growth" and "Policy Index" columns to be moved to the right by 1 column', () => { + cy.get('.slick-header-column:nth(1)').trigger('mouseover').trigger('contextmenu').invoke('show'); + + cy.get('.slick-column-picker') + .find('.slick-column-picker-list') + .children('li:nth-child(1)') + .children('label') + .should('contain', 'Title') + .click(); + cy.get('.slick-column-picker .close').click(); + + cy.get(`[data-row=0] > .slick-cell.l0.r0`).should('contain', 'Task 0'); + cy.get(`[data-row=1] > .slick-cell.l0.r0`).should('contain', 'Task 1'); + cy.get(`[data-row=2] > .slick-cell.l0.r0`).should('contain', 'Task 2'); + + cy.get('[data-row=0] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=3] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 5) + ); + cy.get('[data-row=8] > .slick-cell.l1.r1.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 80) + ); + + cy.get('[data-row=2] > .slick-cell.l3.r5').should('not.have.class', 'rowspan'); + cy.get('[data-row=3] > .slick-cell.l3.r7.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 3) + ); + cy.get('[data-row=8] > .slick-cell.l3.r4.rowspan').should(($el) => + expect(parseInt(`${$el.outerHeight()}`, 10)).to.eq(GRID_ROW_HEIGHT * 492) + ); + }); + }); }); diff --git a/frameworks/aurelia-slickgrid/docs/grid-functionalities/Row-based-edit.md b/frameworks/aurelia-slickgrid/docs/grid-functionalities/Row-based-edit.md index 4238e6b550..0de13e42cb 100644 --- a/frameworks/aurelia-slickgrid/docs/grid-functionalities/Row-based-edit.md +++ b/frameworks/aurelia-slickgrid/docs/grid-functionalities/Row-based-edit.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Description The Row based editing plugin makes it possible to keep the grid readonly except for rows which the user explicitely toggles into edit mode. diff --git a/frameworks/aurelia-slickgrid/docs/grid-functionalities/grid-state-preset.md b/frameworks/aurelia-slickgrid/docs/grid-functionalities/grid-state-preset.md index d087e34f62..7bc7f1cb55 100644 --- a/frameworks/aurelia-slickgrid/docs/grid-functionalities/grid-state-preset.md +++ b/frameworks/aurelia-slickgrid/docs/grid-functionalities/grid-state-preset.md @@ -62,6 +62,8 @@ export class GridExample { } ``` +> **Note** since v10 you can now pass `true` as the argument to `gridStateService.getCurrentGridState(true)` which will return all columns, not just the visible columns but also include the hidden columns and their "hidden" properties. + ### Using Grid Presets & Filter SearchTerm(s) What happens when we use the grid `presets` and a [Filter Default SearchTerms](../column-functionalities/filters/select-filter.md#default-search-terms)? In this case, the `presets` will win over filter `searchTerms`. The cascading order of priorities is the following 1. Do we have any `presets`? Yes use them, else go to step 2 @@ -181,6 +183,10 @@ You can show/hide or even change a column position via the `presets`, yes `prese So let say that we want to hide the last Column on page load, we can just find the column by it's `id` that you want to hide and pass the new column definition to the `presets` (again make sure to follow the correct preset structure). +#### Option 1 + +Pass the Grid Presets with an array that has less `presets.columns`, whichever column(s) are missing will be considered hidden columns + ```ts this.columnDefinitions = [ // initial column definitions @@ -191,7 +197,7 @@ this.columnDefinitions = [ const mappedColumnDefinitions = this.columnDefinitions.map(col => ({ columnId: col.id, width: col.width })); mappedColumnDefinitions.pop(); -// then pass it to the presets +// then pass it to the grid presets (an array of columns minus the last column) this.gridOptions = { presets: { columns: mappedColumnDefinitions @@ -200,6 +206,11 @@ this.gridOptions = { ``` This would be the easiest way to do it. +#### Option 2 + +Since v10, the second alternative is to pass all the columns to `presets.columns` with some of them having the `hidden` properties. Both approaches are valid in v10, just choose whichever option you prefer. + +###### Summary As pointed out earlier, the `presets` requires a specific structure where the `columns` is the list of columns to show/hide with their possible widths. Also worth mentioning again that the position in the array is very important as it defines the position shown in the UI. ##### ViewModel @@ -218,4 +229,4 @@ this.gridOptions = { } }; ``` -You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but go manual is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` and that would work as well. +You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but loading them manually is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` (maybe include the `hidden` prop as well) and that would work as well. diff --git a/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-detail.md b/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-detail.md index 9eb7afef0c..e76ede23fb 100644 --- a/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-detail.md +++ b/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-detail.md @@ -6,7 +6,7 @@ - [Row Detail - View Component](#row-detail---view-component) - [Access Parent Component (grid) from the Child Component (row detail)](#access-parent-component-grid-from-the-child-component-row-detail) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Demo [Demo Page](https://ghiscoding.github.io/aurelia-slickgrid-demos/#/slickgrid/example19) / [Demo Component](https://github.com/ghiscoding/slickgrid-universal/blob/master/demos/aurelia/src/examples/slickgrid/example19.ts) @@ -341,7 +341,7 @@ export class DetailViewCustomElement{ ``` ## Troubleshooting -### Adding a Column dynamically is removing the Row Selection, why is that? +### Adding a Column dynamically is removing the Row Selection column, why is that? The reason is because the Row Selection (checkbox) plugin is a special column and Aurelia-Slickgrid is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columnDefinitions`. To address this issue, you need to get the Aurelia-Slickgrid internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work. ```html diff --git a/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-selection.md b/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-selection.md index 23b62ae8a3..e1b5700f15 100644 --- a/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-selection.md +++ b/frameworks/aurelia-slickgrid/docs/grid-functionalities/row-selection.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) - [Hybrid Selection Model (cell+row selection)](#hybrid-selection-model-and-drag-fill) ### Description @@ -297,7 +297,7 @@ export class Example { ``` ## Troubleshooting -### Adding a Column dynamically is removing the Row Selection, why is that? +### Adding a Column dynamically is removing the Row Selection column, why is that? The reason is because the Row Selection (checkbox) plugin is a special column and Aurelia-Slickgrid is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columnDefinitions`. To address this issue, you need to get the Aurelia-Slickgrid internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work. ```html diff --git a/frameworks/aurelia-slickgrid/src/custom-elements/aurelia-slickgrid.ts b/frameworks/aurelia-slickgrid/src/custom-elements/aurelia-slickgrid.ts index f4f28b85e4..a5cd5cd839 100644 --- a/frameworks/aurelia-slickgrid/src/custom-elements/aurelia-slickgrid.ts +++ b/frameworks/aurelia-slickgrid/src/custom-elements/aurelia-slickgrid.ts @@ -40,6 +40,7 @@ import { SlickGrid, SlickgridConfig, SlickGroupItemMetadataProvider, + sortPresetColumns, SortService, TreeDataService, type Observable, @@ -365,7 +366,6 @@ export class AureliaSlickgridCustomElement { // save reference for all columns before they optionally become hidden/visible this.sharedService.allColumns = this._columns; - this.sharedService.visibleColumns = this._columns; // TODO: revisit later, this conflicts with Grid State (Example 15) // before certain extentions/plugins potentially adds extra columns not created by the user itself (RowMove, RowDetail, RowSelections) @@ -784,10 +784,9 @@ export class AureliaSlickgridCustomElement { } } - // when column are reordered, we need to update the visibleColumn array - this._eventHandler.subscribe(grid.onColumnsReordered, (_e, args) => { + // when column are reordered, we need to update SharedService flag + this._eventHandler.subscribe(grid.onColumnsReordered, () => { this.sharedService.hasColumnsReordered = true; - this.sharedService.visibleColumns = args.impactedColumns; }); this._eventHandler.subscribe(grid.onSetOptions, (_e, args) => { @@ -1166,9 +1165,8 @@ export class AureliaSlickgridCustomElement { if (this.options.enableTranslate) { this.extensionService.translateColumnHeaders(undefined, newColumns); - } else { - this.extensionService.renderColumnHeaders(newColumns, true); } + this.extensionService.renderColumnHeaders(newColumns, true); if (this.options?.enableAutoSizeColumns) { this.grid.autosizeColumns(); @@ -1342,9 +1340,9 @@ export class AureliaSlickgridCustomElement { // We will use this when doing a resize by cell content, if user provided a `width` it won't override it. gridPresetColumns.forEach((col) => (col.originalWidth = col.width)); - // finally set the new presets columns (including checkbox selector if need be) - this.grid.setColumns(gridPresetColumns); - this.sharedService.visibleColumns = gridPresetColumns; + // finally sort and set the new presets columns (including checkbox selector if need be) + const orderedColumns = sortPresetColumns(this._columns, gridPresetColumns); + this.grid.setColumns(orderedColumns); } } } diff --git a/frameworks/slickgrid-react/docs/grid-functionalities/Row-based-edit.md b/frameworks/slickgrid-react/docs/grid-functionalities/Row-based-edit.md index 695a0cd585..48b3369c01 100644 --- a/frameworks/slickgrid-react/docs/grid-functionalities/Row-based-edit.md +++ b/frameworks/slickgrid-react/docs/grid-functionalities/Row-based-edit.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Description The Row based editing plugin makes it possible to keep the grid readonly except for rows which the user explicitely toggles into edit mode. diff --git a/frameworks/slickgrid-react/docs/grid-functionalities/grid-state-preset.md b/frameworks/slickgrid-react/docs/grid-functionalities/grid-state-preset.md index a80c2e93ac..cab55206db 100644 --- a/frameworks/slickgrid-react/docs/grid-functionalities/grid-state-preset.md +++ b/frameworks/slickgrid-react/docs/grid-functionalities/grid-state-preset.md @@ -73,6 +73,8 @@ const Example: React.FC = () => { } ``` +> **Note** since v10 you can now pass `true` as the argument to `gridStateService.getCurrentGridState(true)` which will return all columns, not just the visible columns but also include the hidden columns and their "hidden" properties. + ### Using Grid Presets & Filter SearchTerm(s) What happens when we use the grid `presets` and a [Filter Default SearchTerms](../column-functionalities/filters/select-filter.md#default-search-terms)? In this case, the `presets` will win over filter `searchTerms`. The cascading order of priorities is the following 1. Do we have any `presets`? Yes use them, else go to step 2 @@ -204,6 +206,10 @@ You can show/hide or even change a column position via the `presets`, yes `prese So let say that we want to hide the last Column on page load, we can just find the column by it's `id` that you want to hide and pass the new column definition to the `presets` (again make sure to follow the correct preset structure). +#### Option 1 + +Pass the Grid Presets with an array that has less `presets.columns`, whichever column(s) are missing will be considered hidden columns + ```ts const columnDefinitions = [ // initial column definitions @@ -214,7 +220,7 @@ const columnDefinitions = [ const mappedColumnDefinitions = columnDefinitions.map(col => ({ columnId: col.id, width: col.width })); mappedColumnDefinitions.pop(); -// then pass it to the presets +// then pass it to the grid presets (an array of columns minus the last column) const gridOptions = { presets: { columns: mappedColumnDefinitions @@ -223,6 +229,11 @@ const gridOptions = { ``` This would be the easiest way to do it. +#### Option 2 + +Since v10, the second alternative is to pass all the columns to `presets.columns` with some of them having the `hidden` properties. Both approaches are valid in v10, just choose whichever option you prefer. + +###### Summary As pointed out earlier, the `presets` requires a specific structure where the `columns` is the list of columns to show/hide with their possible widths. Also worth mentioning again that the position in the array is very important as it defines the position shown in the UI. ##### Component @@ -241,4 +252,4 @@ const gridOptions = { } }; ``` -You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but go manual is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` and that would work as well. +You could technically redefine by hand the complete list of `columns` that the `presets` requires. I would personally do it via the Column Definitions looping with `map()`, but loading them manually is also perfectly fine. You would just re-declare the `columns` again with the `id` and `width` (maybe include the `hidden` prop as well) and that would work as well. diff --git a/frameworks/slickgrid-react/docs/grid-functionalities/row-detail.md b/frameworks/slickgrid-react/docs/grid-functionalities/row-detail.md index 5ef5e570c8..2a3f0aeacf 100644 --- a/frameworks/slickgrid-react/docs/grid-functionalities/row-detail.md +++ b/frameworks/slickgrid-react/docs/grid-functionalities/row-detail.md @@ -6,7 +6,7 @@ - [Row Detail - View Component](#row-detail---view-component) - [Access Parent Component (grid) from the Child Component (row detail)](#access-parent-component-grid-from-the-child-component-row-detail) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) ### Demo [Demo Page](https://ghiscoding.github.io/slickgrid-react-demos/#/Example19) / [Demo ViewModel](https://github.com/ghiscoding/slickgrid-universal/blob/master/demos/react/src/examples/slickgrid/Example19.ts) @@ -424,7 +424,7 @@ const Example: React.FC = (props: Props) => { ``` ## Troubleshooting -### Adding a Column dynamically is removing the Row Selection, why is that? +### Adding a Column dynamically is removing the Row Selection column, why is that? The reason is because the Row Selection (checkbox) plugin is a special column and Slickgrid-React is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columnDefinitions`. To address this issue, you need to get the Slickgrid-React internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work. ```ts diff --git a/frameworks/slickgrid-react/docs/grid-functionalities/row-selection.md b/frameworks/slickgrid-react/docs/grid-functionalities/row-selection.md index cfe5510c52..199c9f0799 100644 --- a/frameworks/slickgrid-react/docs/grid-functionalities/row-selection.md +++ b/frameworks/slickgrid-react/docs/grid-functionalities/row-selection.md @@ -7,7 +7,7 @@ - [Disable External Button when having Empty Selection](#disable-external-button-when-having-empty-selection) - [Change Row Selections](#change-row-selections) - Troubleshooting - - [Adding a Column dynamically is removing the Row Selection, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-why-is-that) + - [Adding a Column dynamically is removing the Row Selection column, why is that?](#adding-a-column-dynamically-is-removing-the-row-selection-column-why-is-that) - [Hybrid Selection Model (cell+row selection)](#hybrid-selection-model-and-drag-fill) ### Description @@ -385,7 +385,7 @@ export default Example; ``` ## Troubleshooting -### Adding a Column dynamically is removing the Row Selection, why is that? +### Adding a Column dynamically is removing the Row Selection column, why is that? The reason is because the Row Selection (checkbox) plugin is a special column and slickgrid-react is adding an extra column dynamically for the Row Selection checkbox and that is **not** reflected in your local copy of `columnDefinitions`. To address this issue, you need to get the slickgrid-react internal copy of all columns (including the extra columns), you can get it via `getAllColumnDefinitions()` from the Grid Service and then you can use to that array and that will work. ```tsx diff --git a/frameworks/slickgrid-react/src/components/slickgrid-react.tsx b/frameworks/slickgrid-react/src/components/slickgrid-react.tsx index 3526bf949f..66bcd1eb77 100644 --- a/frameworks/slickgrid-react/src/components/slickgrid-react.tsx +++ b/frameworks/slickgrid-react/src/components/slickgrid-react.tsx @@ -22,6 +22,7 @@ import { SlickGrid, SlickgridConfig, SlickGroupItemMetadataProvider, + sortPresetColumns, SortService, TreeDataService, type AutocompleterEditor, @@ -509,7 +510,6 @@ export class SlickgridReact extends React.Component