From 8d4cbcbb0734f32b5be79e35788c73417785a40a Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Mon, 31 Aug 2026 13:44:00 +0300 Subject: [PATCH 1/2] fix(splitter): preserve pane sizes after collapse --- .../splitter-pane/splitter-pane.component.ts | 44 ++++-- .../src/splitter/splitter.component.spec.ts | 127 ++++++++++++++++-- .../src/splitter/splitter.component.ts | 4 - 3 files changed, 145 insertions(+), 30 deletions(-) diff --git a/projects/igniteui-angular/splitter/src/splitter/splitter-pane/splitter-pane.component.ts b/projects/igniteui-angular/splitter/src/splitter/splitter-pane/splitter-pane.component.ts index 380065de59e..68fe4359a61 100644 --- a/projects/igniteui-angular/splitter/src/splitter/splitter-pane/splitter-pane.component.ts +++ b/projects/igniteui-angular/splitter/src/splitter/splitter-pane/splitter-pane.component.ts @@ -148,7 +148,13 @@ export class IgxSplitterPaneComponent { public maxHeight = '100%'; /** @hidden @internal */ - public owner; + public get owner() { + return this._owner; + } + public set owner(value) { + this._owner = value; + this.refreshFlex(); + } /** * Gets/Sets the size of the current pane. @@ -166,7 +172,7 @@ export class IgxSplitterPaneComponent { public set size(value) { this._size = value; - this.el.nativeElement.style.flex = this.flex; + this.refreshFlex(); } /** @hidden @internal */ @@ -180,7 +186,7 @@ export class IgxSplitterPaneComponent { } public set dragSize(val) { this._dragSize = val; - this.el.nativeElement.style.flex = this.flex; + this.refreshFlex(); } /** @@ -199,7 +205,7 @@ export class IgxSplitterPaneComponent { @HostBinding('style.flex') public get flex() { const size = this.dragSize || this.size; - const grow = this.isPercentageSize && !this.dragSize ? 1 : 0; + const grow = (this.isPercentageSize || this.hasCollapsedSibling) && !this.dragSize ? 1 : 0; return `${grow} ${grow} ${size}`; } @@ -213,15 +219,11 @@ export class IgxSplitterPaneComponent { */ @Input({ transform: booleanAttribute }) public set collapsed(value) { - if (this.owner) { - // reset sibling sizes when pane collapse state changes. - this._getSiblings().forEach(sibling => { - sibling.size = 'auto' - sibling.dragSize = null; - }); - } this._collapsed = value; this.display = this._collapsed ? 'none' : 'flex'; + if (this.owner) { + this._getSiblings().forEach(sibling => sibling.resetDragSize()); + } this.collapsedChange.emit(this._collapsed); } @@ -232,6 +234,7 @@ export class IgxSplitterPaneComponent { private _size = 'auto'; private _dragSize; private _collapsed = false; + private _owner: any; /** * Toggles the collapsed state of the pane. @@ -245,11 +248,24 @@ export class IgxSplitterPaneComponent { this.collapsed = !this.collapsed; } + private refreshFlex() { + this.el.nativeElement.style.flex = this.flex; + } + + private resetDragSize() { + this._dragSize = null; + this.refreshFlex(); + } + + private get hasCollapsedSibling() { + return this.owner ? this._getSiblings().some(sibling => sibling.collapsed) : false; + } + /** @hidden @internal */ - private _getSiblings() { - const panes = this.owner.panes.toArray(); + private _getSiblings(): IgxSplitterPaneComponent[] { + const panes: IgxSplitterPaneComponent[] = this.owner.panes.toArray(); const index = panes.indexOf(this); - const siblings = []; + const siblings: IgxSplitterPaneComponent[] = []; if (index !== 0) { siblings.push(panes[index - 1]); } diff --git a/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts b/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts index 6933bfa774b..e1eec0898a1 100644 --- a/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts +++ b/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts @@ -249,6 +249,76 @@ describe('IgxSplitter', () => { expect(pane2.collapsed).toBeFalsy(); }); + it('should preserve horizontal pane sizes after collapse and expand', () => { + const [pane1, pane2] = splitter.panes.toArray(); + const splitterBar = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS)).componentInstance; + pane1.size = '30%'; + pane2.size = '70%'; + fixture.detectChanges(); + + const pane1Width = pane1.element.offsetWidth; + const pane2Width = pane2.element.offsetWidth; + + splitterBar.onCollapsing(false); + fixture.detectChanges(); + splitterBar.onCollapsing(false); + fixture.detectChanges(); + + expect(pane1.size).toBe('30%'); + expect(pane2.size).toBe('70%'); + expect(pane1.element.offsetWidth).toBe(pane1Width); + expect(pane2.element.offsetWidth).toBe(pane2Width); + }); + + it('should preserve vertical pane sizes after collapse and expand', () => { + fixture.componentInstance.type = SplitterType.Vertical; + fixture.detectChanges(); + + const [pane1, pane2] = splitter.panes.toArray(); + const splitterBar = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS)).componentInstance; + pane1.element.parentElement.style.height = '600px'; + pane1.size = '30%'; + pane2.size = '70%'; + fixture.detectChanges(); + + const pane1Height = pane1.element.offsetHeight; + const pane2Height = pane2.element.offsetHeight; + + splitterBar.onCollapsing(false); + fixture.detectChanges(); + splitterBar.onCollapsing(false); + fixture.detectChanges(); + + expect(pane1.size).toBe('30%'); + expect(pane2.size).toBe('70%'); + expect(pane1.element.offsetHeight).toBe(pane1Height); + expect(pane2.element.offsetHeight).toBe(pane2Height); + }); + + it('should let a fixed-size pane fill space without losing its configured size', () => { + const [pane1, pane2] = splitter.panes.toArray(); + const splitterBar = fixture.debugElement.query(By.css(SPLITTERBAR_CLASS)).componentInstance; + pane1.element.parentElement.style.width = '600px'; + pane1.size = '100px'; + pane2.size = '100px'; + fixture.detectChanges(); + + const pane1Width = pane1.element.offsetWidth; + const pane2Width = pane2.element.offsetWidth; + + splitterBar.onCollapsing(false); + fixture.detectChanges(); + + expect(pane2.size).toBe('100px'); + expect(pane2.element.offsetWidth).toBeGreaterThan(pane2Width); + + splitterBar.onCollapsing(false); + fixture.detectChanges(); + + expect(pane1.element.offsetWidth).toBe(pane1Width); + expect(pane2.element.offsetWidth).toBe(pane2Width); + }); + it('should allow resize in % when pane size is auto.', () => { const pane1 = splitter.panes.toArray()[0]; const pane2 = splitter.panes.toArray()[1]; @@ -449,21 +519,50 @@ describe('IgxSplitter pane collapse', () => { splitter = fixture.componentInstance.splitter; })); - it('should reset sizes when pane is initially collapsed.', () => { + it('should preserve sizes and constraints when pane is initially collapsed.', () => { const panes = splitter.panes.toArray(); - panes.forEach(pane => { - expect(pane.size).toBe('auto'); - }); + expect(panes.map(pane => pane.size)).toEqual(['30%', '30%', '30%']); + expect(panes.map(pane => pane.minWidth)).toEqual(['10%', '20%', '5%']); + expect(panes.map(pane => pane.maxWidth)).toEqual(['40%', '50%', '35%']); + expect(panes[2].collapsed).toBeTrue(); + expect(panes[2].display).toBe('none'); + }); + + it('should let an initially fixed-size sibling fill space and restore its size', () => { + const fixedFixture = TestBed.createComponent(SplitterCollapsedPaneComponent); + fixedFixture.componentInstance.paneSizes = ['100px', '100px', '100px']; + fixedFixture.componentInstance.paneMinSizes = ['0', '0', '0']; + fixedFixture.componentInstance.paneMaxSizes = ['100%', '100%', '100%']; + fixedFixture.componentInstance.splitterWidth = '600px'; + fixedFixture.detectChanges(); + const fixedSplitter = fixedFixture.componentInstance.splitter; + const panes = fixedSplitter.panes.toArray(); + const splitterBar = fixedFixture.debugElement.queryAll(By.css(SPLITTERBAR_CLASS))[1].componentInstance; + + expect(panes.map(pane => pane.size)).toEqual(['100px', '100px', '100px']); + expect(panes[0].element.offsetWidth).toBe(100); + expect(panes[1].element.offsetWidth).toBeGreaterThan(100); + + splitterBar.onCollapsing(true); + fixedFixture.detectChanges(); + + expect(panes[2].collapsed).toBeFalse(); + expect(panes.map(pane => pane.size)).toEqual(['100px', '100px', '100px']); + expect(panes.map(pane => pane.element.offsetWidth)).toEqual([100, 100, 100]); }); - it('should reset sizes when pane is runtime collapsed.', () => { + it('should preserve sizes and clear drag sizes when pane is runtime collapsed.', () => { const panes = splitter.panes.toArray(); panes[0].size = '70%'; + panes[1].size = '20%'; + panes[2].size = '10%'; + panes[0].dragSize = '60%'; + panes[2].dragSize = '5%'; fixture.detectChanges(); panes[1].collapsed = true; fixture.detectChanges(); - panes.forEach(pane => { - expect(pane.size).toBe('auto'); - }); + expect(panes.map(pane => pane.size)).toEqual(['70%', '20%', '10%']); + expect(panes[0].dragSize).toBeNull(); + expect(panes[2].dragSize).toBeNull(); }); }); @@ -582,18 +681,18 @@ export class SplitterTogglePaneComponent extends SplitterTestComponent { @Component({ template: ` - - + +
Pane 1
- +
Pane 2
- +
Pane 3
@@ -603,6 +702,10 @@ export class SplitterTogglePaneComponent extends SplitterTestComponent { imports: [IgxSplitterComponent, IgxSplitterPaneComponent] }) export class SplitterCollapsedPaneComponent extends SplitterTestComponent { + public paneSizes = ['30%', '30%', '30%']; + public paneMinSizes = ['10%', '20%', '5%']; + public paneMaxSizes = ['40%', '50%', '35%']; + public splitterWidth: string; } @Component({ diff --git a/projects/igniteui-angular/splitter/src/splitter/splitter.component.ts b/projects/igniteui-angular/splitter/src/splitter/splitter.component.ts index 374ec9cfeeb..9a4ce01bff9 100644 --- a/projects/igniteui-angular/splitter/src/splitter/splitter.component.ts +++ b/projects/igniteui-angular/splitter/src/splitter/splitter.component.ts @@ -309,10 +309,6 @@ export class IgxSplitterComponent implements AfterContentInit { } }); this.assignFlexOrder(); - if (this.panes.filter(x => x.collapsed).length > 0) { - // if any panes are collapsed, reset sizes. - this.resetPaneSizes(); - } } /** From 2611ac1ff1c6657ec011ba2f9947bbcd5f1b4411 Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Mon, 31 Aug 2026 16:00:56 +0300 Subject: [PATCH 2/2] chore(*): fix failing test --- .../splitter/src/splitter/splitter.component.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts b/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts index e1eec0898a1..a112b8ac566 100644 --- a/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts +++ b/projects/igniteui-angular/splitter/src/splitter/splitter.component.spec.ts @@ -528,13 +528,15 @@ describe('IgxSplitter pane collapse', () => { expect(panes[2].display).toBe('none'); }); - it('should let an initially fixed-size sibling fill space and restore its size', () => { + it('should let an initially fixed-size sibling fill space and restore its size', async () => { const fixedFixture = TestBed.createComponent(SplitterCollapsedPaneComponent); fixedFixture.componentInstance.paneSizes = ['100px', '100px', '100px']; fixedFixture.componentInstance.paneMinSizes = ['0', '0', '0']; fixedFixture.componentInstance.paneMaxSizes = ['100%', '100%', '100%']; fixedFixture.componentInstance.splitterWidth = '600px'; fixedFixture.detectChanges(); + await fixedFixture.whenStable(); + fixedFixture.detectChanges(); const fixedSplitter = fixedFixture.componentInstance.splitter; const panes = fixedSplitter.panes.toArray(); const splitterBar = fixedFixture.debugElement.queryAll(By.css(SPLITTERBAR_CLASS))[1].componentInstance;