From 8d95dc9f9cb35d1cbd199fd20cc3024f92ad639f Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Fri, 23 Oct 2026 17:12:52 +0300 Subject: [PATCH 1/2] fix(pivot-grid): span collapsed and summary row headers across columns In the horizontal row layout, size collapsed and summary row headers to the combined width of the columns they span instead of the first one, and do not measure auto-sized dimensions again from them. Closes #17222 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 7 ++ .../pivot-grid/src/pivot-grid.component.ts | 3 +- .../grids/pivot-grid/src/pivot-grid.spec.ts | 117 ++++++++++++++++++ ...ot-row-dimension-header-group.component.ts | 3 +- 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcec5bb04c2..d40e5643cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,13 @@ All notable changes for each version of this project will be documented in this - The dialog header no longer throws a missing locale data error for a `locale` without registered Angular locale data when `provideIgniteIntl()` is used. - The `aria-valuemin` and `aria-valuemax` of the selected seconds item no longer depend on the milliseconds of the value. +## 22.2.4 + +### Bug Fixes + +- `IgxPivotGridComponent` + - In the horizontal row layout, collapsed and summary row headers now fill all the columns they span (#17222). + ## 22.2.3 ### Bug Fixes diff --git a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts index d95efa40e35..da065a5f77d 100644 --- a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts +++ b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts @@ -2336,7 +2336,8 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni protected autoSizeDimensionsInView() { if (!this.hasDimensionsToAutosize) return; for (const dim of this.visibleRowDimensions) { - if (dim.width === 'auto') { + // Skip sized dimensions, their merged headers span several columns. + if (dim.width === 'auto' && !dim.autoWidth) { const contentWidths = []; const relatedDims = PivotUtil.flatten([dim]).map((x: IPivotDimension) => x.memberName); const contentCollection = this.getContentCollection(dim); diff --git a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts index be3597fc0da..69cc67396e6 100644 --- a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts +++ b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts @@ -3926,6 +3926,123 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(rowDimensionsCol4.length).toBe(0); }); + it("should size the collapsed and summary row headers to all the columns they span.", () => { + pivotGrid.pivotConfiguration.rows = [ + { + memberName: 'All cities', + memberFunction: () => 'All Cities', + enabled: true, + horizontalSummary: true, + childLevel: { + memberName: 'City', + enabled: true + } + }, { + memberFunction: () => 'AllProducts', + memberName: 'AllProducts', + enabled: true, + horizontalSummary: true, + childLevel: { + memberFunction: (data) => data.ProductCategory, + memberName: 'ProductCategory', + enabled: true, + width: '120px' + } + } + ]; + pivotGrid.pipeTrigger++; + fixture.detectChanges(); + + const getContentCells = (header: string) => fixture.debugElement + .queryAll(By.directive(IgxPivotRowDimensionContentComponent)) + .filter(x => x.componentInstance.rowDimensionColumn.header === header); + const expectHeadersToSpanTheirColumns = () => { + const contentCells = fixture.debugElement.queryAll(By.directive(IgxPivotRowDimensionContentComponent)); + for (const cell of contentCells) { + const content = cell.componentInstance as IgxPivotRowDimensionContentComponent; + const spannedWidth = pivotGrid.visibleRowDimensions + .slice(content.layout.colStart - 1, content.layout.colEnd - 1) + .reduce((width, dim) => width + pivotGrid.rowDimensionWidthToPixels(dim), 0); + const header = cell.query(By.directive(IgxPivotRowDimensionHeaderComponent)).nativeElement as HTMLElement; + expect(header.getBoundingClientRect().width, content.rowDimensionColumn.header).toBe(spannedWidth); + } + }; + + // the summary headers span all columns to their right + const citiesSummary = getContentCells('All Cities Total'); + expect(citiesSummary.length).toBe(1); + expect(citiesSummary[0].componentInstance.layout.colStart).toBe(1); + expect(citiesSummary[0].componentInstance.layout.colEnd).toBe(5); + const productsSummaries = getContentCells('AllProducts Total'); + expect(productsSummaries.length).toBe(6); + productsSummaries.forEach(x => { + expect(x.componentInstance.layout.colStart).toBe(3); + expect(x.componentInstance.layout.colEnd).toBe(5); + }); + expectHeadersToSpanTheirColumns(); + + // the collapsed header spans the empty ProductCategory column + getContentCells('AllProducts')[0].query(By.directive(IgxIconComponent)).nativeElement.click(); + fixture.detectChanges(); + const collapsedProducts = getContentCells('AllProducts')[0].componentInstance.layout; + expect(collapsedProducts.colStart).toBe(3); + expect(collapsedProducts.colEnd).toBe(5); + expectHeadersToSpanTheirColumns(); + + // the same applies when the summaries are on top + pivotGrid.pivotUI = { horizontalSummariesPosition: PivotSummaryPosition.Top }; + fixture.detectChanges(); + expect(getContentCells('All Cities Total')[0].componentInstance.layout.colEnd).toBe(5); + expectHeadersToSpanTheirColumns(); + }); + + it("should not widen an auto-sized row dimension to the columns its summary headers span.", async () => { + fixture.componentInstance.pivotConfigHierarchy = { + columns: pivotGrid.pivotConfiguration.columns, + values: pivotGrid.pivotConfiguration.values, + rows: [ + { + memberName: 'All cities', + memberFunction: () => 'All Cities', + enabled: false, + width: 'auto', + childLevel: { + memberName: 'City', + enabled: true + } + }, { + memberFunction: () => 'AllProducts', + memberName: 'AllProducts', + enabled: true, + width: 'auto', + horizontalSummary: true, + childLevel: { + memberFunction: (data) => data.ProductCategory, + memberName: 'ProductCategory', + enabled: true + } + } + ] + }; + fixture.detectChanges(); + await waitForAnimationFrame(); + fixture.detectChanges(); + + const [allCities, allProducts] = pivotGrid.pivotConfiguration.rows; + const productsWidth = allProducts.autoWidth; + expect(productsWidth).toBeGreaterThan(0); + + // Enabling another auto dimension reruns sizing. + pivotGrid.toggleDimension(allCities); + await frames(2); + fixture.detectChanges(); + await frames(2); + fixture.detectChanges(); + + expect(allCities.autoWidth).toBeGreaterThan(0); + expect(allProducts.autoWidth).toBe(productsWidth); + }); + it("should render summary rows when enabled.", () => { pivotGrid.pivotConfiguration.rows = [ { diff --git a/projects/igniteui-angular/grids/pivot-grid/src/pivot-row-dimension-header-group.component.ts b/projects/igniteui-angular/grids/pivot-grid/src/pivot-row-dimension-header-group.component.ts index 81da4ddd8a6..d91a997c6f9 100644 --- a/projects/igniteui-angular/grids/pivot-grid/src/pivot-row-dimension-header-group.component.ts +++ b/projects/igniteui-angular/grids/pivot-grid/src/pivot-row-dimension-header-group.component.ts @@ -145,7 +145,8 @@ export class IgxPivotRowDimensionHeaderGroupComponent extends IgxGridHeaderGroup protected getHeaderWidthFromDimension() { if (this.grid.hasHorizontalLayout) { - return this.parent.width === -1 ? 'fit-content' : this.width; + // Collapsed and summary headers can span several columns. + return this.parent.width === -1 ? 'fit-content' : `${this.parent.width}px`; } return this.grid.rowDimensionWidth(this.parent.rootDimension); } From 5a46dcea48166ca054bac42f026d37c39f3b69f6 Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Fri, 23 Oct 2026 19:08:22 +0300 Subject: [PATCH 2/2] fix(pivot-grid): stop merged row header resizing at the first column Collapsed and summary row headers span several dimensions, but resizing changes only the first one, so measure that dimension for the resize line limits instead of the merged header. Closes #17222 Co-Authored-By: Claude Opus 5.5 --- .../pivot-grid/pivot-resizing.service.ts | 5 +- .../grids/pivot-grid/src/pivot-grid.spec.ts | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/grids/core/src/resizing/pivot-grid/pivot-resizing.service.ts b/projects/igniteui-angular/grids/core/src/resizing/pivot-grid/pivot-resizing.service.ts index ed155c3183e..176948b5c73 100644 --- a/projects/igniteui-angular/grids/core/src/resizing/pivot-grid/pivot-resizing.service.ts +++ b/projects/igniteui-angular/grids/core/src/resizing/pivot-grid/pivot-resizing.service.ts @@ -19,7 +19,10 @@ export class IgxPivotColumnResizingService extends IgxColumnResizingService { * @hidden */ public override getColumnHeaderRenderedWidth() { - return this.rowHeaderGroup.header.nativeElement.getBoundingClientRect().width; + const headerWidth = this.rowHeaderGroup.header.nativeElement.getBoundingClientRect().width; + // A collapsed or summary header spans several dimensions, but only the first one is resized. + const rowDimWidth = this.rowHeaderGroup.grid.rowDimensionWidthToPixels(this.rowHeaderGroup.parent.rootDimension); + return rowDimWidth < headerWidth ? rowDimWidth : headerWidth; } protected override _handlePixelResize(diff: number, column: ColumnType) { diff --git a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts index 69cc67396e6..9b1bdc1865a 100644 --- a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts +++ b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.spec.ts @@ -4284,6 +4284,55 @@ describe('IgxPivotGrid #pivotGrid', () => { expect(rowHeaders[0].componentInstance.column.width).toEqual('400px'); }); + it("should stop the resizer of a summary row header at the minimum width of its first dimension.", async () => { + pivotGrid.pivotConfiguration.rows = [ + { + memberName: 'All cities', + memberFunction: () => 'All Cities', + enabled: true, + childLevel: { + memberName: 'City', + enabled: true + } + }, { + memberFunction: () => 'AllProducts', + memberName: 'AllProducts', + enabled: true, + horizontalSummary: true, + childLevel: { + memberFunction: (data) => data.ProductCategory, + memberName: 'ProductCategory', + enabled: true, + width: '120px' + } + } + ]; + pivotGrid.pipeTrigger++; + fixture.detectChanges(); + + const summaryHeader = fixture.debugElement.queryAll(By.directive(IgxPivotRowDimensionHeaderGroupComponent)) + .find(x => x.componentInstance.column.header === 'AllProducts Total'); + const allProducts = pivotGrid.pivotConfiguration.rows[1]; + const rootWidth = pivotGrid.rowDimensionWidthToPixels(allProducts); + const minWidth = summaryHeader.componentInstance.column.minWidthPx; + // the summary header spans the ProductCategory column as well, but only AllProducts is resized + expect(summaryHeader.componentInstance.header.nativeElement.getBoundingClientRect().width).toBe(rootWidth + 120); + useFakeClock(); + UIInteractions.simulateMouseEvent('mousedown', GridFunctions.getHeaderResizeArea(summaryHeader).nativeElement, 500, 0); + await advance(fixture, RESIZE_DEBOUNCE); + await nextFrame(); + const resizer = GridFunctions.getResizer(fixture).nativeElement; + const startLeft = parseFloat(resizer.style.left); + + UIInteractions.simulateMouseEvent('mousemove', resizer, -500, 5); + await frames(2); + expect(startLeft - parseFloat(resizer.style.left)).toBe(rootWidth - minWidth); + + UIInteractions.simulateMouseEvent('mouseup', resizer, -500, 5); + fixture.detectChanges(); + expect(allProducts.width).toBe(`${minWidth}px`); + }); + it("should allow sorting each dimension column from the hierarchy.", () => { pivotGrid.pivotConfiguration.rows = [ {