From 3e54a923b4c99dd371db756e727a0aca6e22594c Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 27 Dec 2017 10:09:03 +0200 Subject: [PATCH] fix(tabs): maintain selected tab when new tabs are added or removed Maintains the `selectedIndex` of the current tab when a new tab is added or removed. Previously, changing the amount of tabs would shift the array, causing a different tab to be selected. Fixes #7738. --- src/lib/tabs/tab-group.spec.ts | 48 ++++++++++++++++++++++++++++++++-- src/lib/tabs/tab-group.ts | 10 +++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/lib/tabs/tab-group.spec.ts b/src/lib/tabs/tab-group.spec.ts index 20dacb751b94..865dd959b766 100644 --- a/src/lib/tabs/tab-group.spec.ts +++ b/src/lib/tabs/tab-group.spec.ts @@ -282,14 +282,17 @@ describe('MatTabGroup', () => { expect(tabs[3].origin).toBeGreaterThanOrEqual(0); // Add a new tab in the beginning and select it, expect an origin < than 0 (animate left) - fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'}); fixture.componentInstance.selectedIndex = 0; fixture.detectChanges(); tick(); + fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'}); + fixture.detectChanges(); + tick(); + tabs = component._tabs.toArray(); expect(tabs[0].origin).toBeLessThan(0); - })); + })); it('should update selected index if the last tab removed while selected', fakeAsync(() => { @@ -309,6 +312,47 @@ describe('MatTabGroup', () => { expect(component.selectedIndex).toBe(numberOfTabs - 2); })); + + it('should maintain the selected tab if a new tab is added', () => { + fixture.detectChanges(); + const component: MatTabGroup = + fixture.debugElement.query(By.css('mat-tab-group')).componentInstance; + + fixture.componentInstance.selectedIndex = 1; + fixture.detectChanges(); + + // Add a new tab at the beginning. + fixture.componentInstance.tabs.unshift({label: 'New tab', content: 'at the start'}); + fixture.detectChanges(); + + expect(component.selectedIndex).toBe(2); + expect(component._tabs.toArray()[2].isActive).toBe(true); + }); + + + it('should maintain the selected tab if a tab is removed', () => { + // Add a couple of tabs so we have more to work with. + fixture.componentInstance.tabs.push( + {label: 'New tab', content: 'with new content'}, + {label: 'Another new tab', content: 'with newer content'} + ); + + // Select the second-to-last tab. + fixture.componentInstance.selectedIndex = 3; + fixture.detectChanges(); + + const component: MatTabGroup = + fixture.debugElement.query(By.css('mat-tab-group')).componentInstance; + + // Remove a tab right before the selected one. + fixture.componentInstance.tabs.splice(2, 1); + fixture.detectChanges(); + + expect(component.selectedIndex).toBe(1); + expect(component._tabs.toArray()[1].isActive).toBe(true); + }); + + }); describe('async tabs', () => { diff --git a/src/lib/tabs/tab-group.ts b/src/lib/tabs/tab-group.ts index fa4dbadb4f12..462a14b43f6b 100644 --- a/src/lib/tabs/tab-group.ts +++ b/src/lib/tabs/tab-group.ts @@ -200,6 +200,16 @@ export class MatTabGroup extends _MatTabGroupMixinBase implements AfterContentIn // Subscribe to changes in the amount of tabs, in order to be // able to re-render the content as new tabs are added or removed. this._tabsSubscription = this._tabs.changes.subscribe(() => { + const tabs = this._tabs.toArray(); + + // Maintain the previously-selected tab if a new tab is added or removed. + for (let i = 0; i < tabs.length; i++) { + if (tabs[i].isActive) { + this._indexToSelect = i; + break; + } + } + this._subscribeToTabLabels(); this._changeDetectorRef.markForCheck(); });