Skip to content

Commit 7c2545c

Browse files
authored
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.
1 parent 5b4bcf5 commit 7c2545c

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
@@ -588,6 +588,26 @@ describe('MDC-based 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.spec.ts

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

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

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

src/material/tabs/tab-group.ts

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

309310
for (let i = 0; i < tabs.length; i++) {
310311
if (tabs[i].isActive) {
311312
// Assign both to the `_indexToSelect` and `_selectedIndex` so we don't fire a changed
312313
// event, otherwise the consumer may end up in an infinite loop in some edge cases like
313314
// adding a tab within the `selectedIndexChange` event.
314315
this._indexToSelect = this._selectedIndex = i;
316+
selectedTab = tabs[i];
315317
break;
316318
}
317319
}
320+
321+
// If we haven't found an active tab and a tab exists at the selected index, it means
322+
// that the active tab was swapped out. Since this won't be picked up by the rendering
323+
// loop in `ngAfterContentChecked`, we need to sync it up manually.
324+
if (!selectedTab && tabs[indexToSelect]) {
325+
Promise.resolve().then(() => {
326+
tabs[indexToSelect].isActive = true;
327+
this.selectedTabChange.emit(this._createChangeEvent(indexToSelect));
328+
});
329+
}
318330
}
319331

320332
this._changeDetectorRef.markForCheck();

0 commit comments

Comments
 (0)