Skip to content

Commit 04f4937

Browse files
crisbetoandrewseguin
authored andcommitted
fix(material/tabs): update tab state when active tab is swapped out (#24164)
Fixes that when the selected tab is swapped out, we weren't marking the new tab as active and emitting the `selectedTabChange` event. Fixes #24147. (cherry picked from commit 7c2545c)
1 parent e9734a9 commit 04f4937

File tree

3 files changed

+52
-0
lines changed

3 files changed

+52
-0
lines changed

src/material-experimental/mdc-tabs/tab-group.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,26 @@ describe('MDC-based MatTabGroup', () => {
589589

590590
expect(fixture.componentInstance.handleSelection).not.toHaveBeenCalled();
591591
}));
592+
593+
it('should update the newly-selected tab if the previously-selected tab is replaced', fakeAsync(() => {
594+
const component: MatTabGroup = fixture.debugElement.query(
595+
By.css('mat-tab-group'),
596+
)!.componentInstance;
597+
598+
spyOn(fixture.componentInstance, 'handleSelection');
599+
600+
fixture.componentInstance.tabs[fixture.componentInstance.selectedIndex] = {
601+
label: 'New',
602+
content: 'New',
603+
};
604+
fixture.detectChanges();
605+
tick();
606+
607+
expect(component._tabs.get(1)?.isActive).toBe(true);
608+
expect(fixture.componentInstance.handleSelection).toHaveBeenCalledWith(
609+
jasmine.objectContaining({index: 1}),
610+
);
611+
}));
592612
});
593613

594614
describe('async tabs', () => {

src/material/tabs/tab-group.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,26 @@ describe('MatTabGroup', () => {
588588

589589
expect(fixture.componentInstance.handleSelection).not.toHaveBeenCalled();
590590
}));
591+
592+
it('should update the newly-selected tab if the previously-selected tab is replaced', fakeAsync(() => {
593+
const component: MatTabGroup = fixture.debugElement.query(
594+
By.css('mat-tab-group'),
595+
)!.componentInstance;
596+
597+
spyOn(fixture.componentInstance, 'handleSelection');
598+
599+
fixture.componentInstance.tabs[fixture.componentInstance.selectedIndex] = {
600+
label: 'New',
601+
content: 'New',
602+
};
603+
fixture.detectChanges();
604+
tick();
605+
606+
expect(component._tabs.get(1)?.isActive).toBe(true);
607+
expect(fixture.componentInstance.handleSelection).toHaveBeenCalledWith(
608+
jasmine.objectContaining({index: 1}),
609+
);
610+
}));
591611
});
592612

593613
describe('async tabs', () => {

src/material/tabs/tab-group.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,16 +284,28 @@ export abstract class _MatTabGroupBase
284284
// explicit change that selects a different tab.
285285
if (indexToSelect === this._selectedIndex) {
286286
const tabs = this._tabs.toArray();
287+
let selectedTab: MatTab | undefined;
287288

288289
for (let i = 0; i < tabs.length; i++) {
289290
if (tabs[i].isActive) {
290291
// Assign both to the `_indexToSelect` and `_selectedIndex` so we don't fire a changed
291292
// event, otherwise the consumer may end up in an infinite loop in some edge cases like
292293
// adding a tab within the `selectedIndexChange` event.
293294
this._indexToSelect = this._selectedIndex = i;
295+
selectedTab = tabs[i];
294296
break;
295297
}
296298
}
299+
300+
// If we haven't found an active tab and a tab exists at the selected index, it means
301+
// that the active tab was swapped out. Since this won't be picked up by the rendering
302+
// loop in `ngAfterContentChecked`, we need to sync it up manually.
303+
if (!selectedTab && tabs[indexToSelect]) {
304+
Promise.resolve().then(() => {
305+
tabs[indexToSelect].isActive = true;
306+
this.selectedTabChange.emit(this._createChangeEvent(indexToSelect));
307+
});
308+
}
297309
}
298310

299311
this._changeDetectorRef.markForCheck();

0 commit comments

Comments
 (0)