Skip to content

Commit 946cc67

Browse files
authored
fix(material/tabs): update MatTab _scrollToLabel function to always display a label from its start (#26736) (#26737)
1 parent 5c5617d commit 946cc67

File tree

3 files changed

+41
-22
lines changed

3 files changed

+41
-22
lines changed

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,11 @@ describe('MatTabHeader', () => {
272272
// Focus on the last tab, expect this to be the maximum scroll distance.
273273
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
274274
fixture.detectChanges();
275-
expect(appComponent.tabHeader.scrollDistance).toBe(
276-
appComponent.tabHeader._getMaxScrollDistance(),
275+
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
276+
appComponent.tabHeader.focusIndex,
277277
);
278+
const viewLength = appComponent.getViewLength();
279+
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);
278280

279281
// Focus on the first tab, expect this to be the maximum scroll distance.
280282
appComponent.tabHeader.focusIndex = 0;
@@ -331,9 +333,11 @@ describe('MatTabHeader', () => {
331333
// Focus the last tab so the header scrolls to the end.
332334
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
333335
fixture.detectChanges();
334-
expect(appComponent.tabHeader.scrollDistance).toBe(
335-
appComponent.tabHeader._getMaxScrollDistance(),
336+
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
337+
appComponent.tabHeader.focusIndex,
336338
);
339+
const viewLength = appComponent.getViewLength();
340+
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);
337341

338342
// Remove the first two tabs which includes the selected tab.
339343
appComponent.tabs = appComponent.tabs.slice(2);
@@ -362,9 +366,8 @@ describe('MatTabHeader', () => {
362366
// Focus on the last tab, expect this to be the maximum scroll distance.
363367
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
364368
fixture.detectChanges();
365-
expect(appComponent.tabHeader.scrollDistance).toBe(
366-
appComponent.tabHeader._getMaxScrollDistance(),
367-
);
369+
const {offsetLeft} = appComponent.getSelectedLabel(appComponent.tabHeader.focusIndex);
370+
expect(offsetLeft).toBe(0);
368371

369372
// Focus on the first tab, expect this to be the maximum scroll distance.
370373
appComponent.tabHeader.focusIndex = 0;
@@ -757,4 +760,12 @@ class SimpleTabHeaderApp {
757760
this.tabs.push({label: 'new'});
758761
}
759762
}
763+
764+
getViewLength() {
765+
return this.tabHeader._tabListContainer.nativeElement.offsetWidth;
766+
}
767+
768+
getSelectedLabel(index: number) {
769+
return this.tabHeader._items.toArray()[this.tabHeader.focusIndex].elementRef.nativeElement;
770+
}
760771
}

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,6 @@ const passiveEventListenerOptions = normalizePassiveListenerOptions({
5757
*/
5858
export type ScrollDirection = 'after' | 'before';
5959

60-
/**
61-
* The distance in pixels that will be overshot when scrolling a tab label into view. This helps
62-
* provide a small affordance to the label next to it.
63-
*/
64-
const EXAGGERATED_OVERSCROLL = 60;
65-
6660
/**
6761
* Amount of milliseconds to wait before starting to scroll the header automatically.
6862
* Set a little conservatively in order to handle fake events dispatched on touch devices.
@@ -524,10 +518,13 @@ export abstract class MatPaginatedTabHeader
524518

525519
if (labelBeforePos < beforeVisiblePos) {
526520
// Scroll header to move label to the before direction
527-
this.scrollDistance -= beforeVisiblePos - labelBeforePos + EXAGGERATED_OVERSCROLL;
521+
this.scrollDistance -= beforeVisiblePos - labelBeforePos;
528522
} else if (labelAfterPos > afterVisiblePos) {
529523
// Scroll header to move label to the after direction
530-
this.scrollDistance += labelAfterPos - afterVisiblePos + EXAGGERATED_OVERSCROLL;
524+
this.scrollDistance += Math.min(
525+
labelAfterPos - afterVisiblePos,
526+
labelBeforePos - beforeVisiblePos,
527+
);
531528
}
532529
}
533530

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,11 @@ describe('MDC-based MatTabHeader', () => {
266266
// Focus on the last tab, expect this to be the maximum scroll distance.
267267
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
268268
fixture.detectChanges();
269-
expect(appComponent.tabHeader.scrollDistance).toBe(
270-
appComponent.tabHeader._getMaxScrollDistance(),
269+
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
270+
appComponent.tabHeader.focusIndex,
271271
);
272+
const viewLength = appComponent.getViewLength();
273+
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);
272274

273275
// Focus on the first tab, expect this to be the maximum scroll distance.
274276
appComponent.tabHeader.focusIndex = 0;
@@ -329,9 +331,11 @@ describe('MDC-based MatTabHeader', () => {
329331
// Focus the last tab so the header scrolls to the end.
330332
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
331333
fixture.detectChanges();
332-
expect(appComponent.tabHeader.scrollDistance).toBe(
333-
appComponent.tabHeader._getMaxScrollDistance(),
334+
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
335+
appComponent.tabHeader.focusIndex,
334336
);
337+
const viewLength = appComponent.getViewLength();
338+
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);
335339

336340
// Remove the first two tabs which includes the selected tab.
337341
appComponent.tabs = appComponent.tabs.slice(2);
@@ -360,9 +364,8 @@ describe('MDC-based MatTabHeader', () => {
360364
// Focus on the last tab, expect this to be the maximum scroll distance.
361365
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
362366
fixture.detectChanges();
363-
expect(appComponent.tabHeader.scrollDistance).toBe(
364-
appComponent.tabHeader._getMaxScrollDistance(),
365-
);
367+
const {offsetLeft} = appComponent.getSelectedLabel(appComponent.tabHeader.focusIndex);
368+
expect(offsetLeft).toBe(0);
366369

367370
// Focus on the first tab, expect this to be the maximum scroll distance.
368371
appComponent.tabHeader.focusIndex = 0;
@@ -757,4 +760,12 @@ class SimpleTabHeaderApp {
757760
this.tabs.push({label: 'new'});
758761
}
759762
}
763+
764+
getViewLength() {
765+
return this.tabHeader._tabListContainer.nativeElement.offsetWidth;
766+
}
767+
768+
getSelectedLabel(index: number) {
769+
return this.tabHeader._items.toArray()[this.tabHeader.focusIndex].elementRef.nativeElement;
770+
}
760771
}

0 commit comments

Comments
 (0)