Skip to content

Commit 0a4fdb2

Browse files
authored
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.
1 parent 8c19346 commit 0a4fdb2

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
@@ -224,7 +224,16 @@ export abstract class MatPaginatedTabHeader
224224
// We need to defer this to give the browser some time to recalculate
225225
// the element dimensions. The call has to be wrapped in `NgZone.run`,
226226
// because the viewport change handler runs outside of Angular.
227-
this._ngZone.run(() => Promise.resolve().then(realign));
227+
this._ngZone.run(() => {
228+
Promise.resolve().then(() => {
229+
// Clamp the scroll distance, because it can change with the number of tabs.
230+
this._scrollDistance = Math.max(
231+
0,
232+
Math.min(this._getMaxScrollDistance(), this._scrollDistance),
233+
);
234+
realign();
235+
});
236+
});
228237
this._keyManager.withHorizontalOrientation(this._getLayoutDirection());
229238
});
230239

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)