Skip to content

Commit b83d225

Browse files
crisbetoandrewseguin
authored andcommitted
fix(material/tabs): wrong scroll distance if selected tab is removed (#24118)
Fixes the case where the user has scrolled to the end of the tab list and the selected tab from the beginning is removed. Since we no longer have a selected tab, the scroll distance wasn't being updated. Fixes #24117. (cherry picked from commit 0a4fdb2)
1 parent 4d57553 commit b83d225

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,28 @@ describe('MDC-based MatTabHeader', () => {
308308
.withContext('Expected no ripple to show up after mousedown')
309309
.toBe(0);
310310
});
311+
312+
it('should update the scroll distance if a tab is removed and no tabs are selected', fakeAsync(() => {
313+
appComponent.selectedIndex = 0;
314+
appComponent.addTabsForScrolling();
315+
fixture.detectChanges();
316+
317+
// Focus the last tab so the header scrolls to the end.
318+
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
319+
fixture.detectChanges();
320+
expect(appComponent.tabHeader.scrollDistance).toBe(
321+
appComponent.tabHeader._getMaxScrollDistance(),
322+
);
323+
324+
// Remove the first two tabs which includes the selected tab.
325+
appComponent.tabs = appComponent.tabs.slice(2);
326+
fixture.detectChanges();
327+
tick();
328+
329+
expect(appComponent.tabHeader.scrollDistance).toBe(
330+
appComponent.tabHeader._getMaxScrollDistance(),
331+
);
332+
}));
311333
});
312334

313335
describe('rtl', () => {

src/material/tabs/paginated-tab-header.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,16 @@ export abstract class MatPaginatedTabHeader
213213
// We need to defer this to give the browser some time to recalculate
214214
// the element dimensions. The call has to be wrapped in `NgZone.run`,
215215
// because the viewport change handler runs outside of Angular.
216-
this._ngZone.run(() => Promise.resolve().then(realign));
216+
this._ngZone.run(() => {
217+
Promise.resolve().then(() => {
218+
// Clamp the scroll distance, because it can change with the number of tabs.
219+
this._scrollDistance = Math.max(
220+
0,
221+
Math.min(this._getMaxScrollDistance(), this._scrollDistance),
222+
);
223+
realign();
224+
});
225+
});
217226
this._keyManager.withHorizontalOrientation(this._getLayoutDirection());
218227
});
219228

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,28 @@ describe('MatTabHeader', () => {
305305
.withContext('Expected no ripple to show up after mousedown')
306306
.toBe(0);
307307
});
308+
309+
it('should update the scroll distance if a tab is removed and no tabs are selected', fakeAsync(() => {
310+
appComponent.selectedIndex = 0;
311+
appComponent.addTabsForScrolling();
312+
fixture.detectChanges();
313+
314+
// Focus the last tab so the header scrolls to the end.
315+
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
316+
fixture.detectChanges();
317+
expect(appComponent.tabHeader.scrollDistance).toBe(
318+
appComponent.tabHeader._getMaxScrollDistance(),
319+
);
320+
321+
// Remove the first two tabs which includes the selected tab.
322+
appComponent.tabs = appComponent.tabs.slice(2);
323+
fixture.detectChanges();
324+
tick();
325+
326+
expect(appComponent.tabHeader.scrollDistance).toBe(
327+
appComponent.tabHeader._getMaxScrollDistance(),
328+
);
329+
}));
308330
});
309331

310332
describe('rtl', () => {

0 commit comments

Comments
 (0)