Skip to content

Commit

Permalink
fix(material/tabs): update MatTab _scrollToLabel function to always d…
Browse files Browse the repository at this point in the history
…isplay a label from its start (#26736) (#26737)

(cherry picked from commit 946cc67)
  • Loading branch information
clamli authored and crisbeto committed Mar 15, 2023
1 parent 7dfcf8f commit 06ed54c
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 22 deletions.
25 changes: 18 additions & 7 deletions src/material/legacy-tabs/tab-header.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,11 @@ describe('MatTabHeader', () => {
// Focus on the last tab, expect this to be the maximum scroll distance.
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
fixture.detectChanges();
expect(appComponent.tabHeader.scrollDistance).toBe(
appComponent.tabHeader._getMaxScrollDistance(),
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
appComponent.tabHeader.focusIndex,
);
const viewLength = appComponent.getViewLength();
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);

// Focus on the first tab, expect this to be the maximum scroll distance.
appComponent.tabHeader.focusIndex = 0;
Expand Down Expand Up @@ -331,9 +333,11 @@ describe('MatTabHeader', () => {
// Focus the last tab so the header scrolls to the end.
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
fixture.detectChanges();
expect(appComponent.tabHeader.scrollDistance).toBe(
appComponent.tabHeader._getMaxScrollDistance(),
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
appComponent.tabHeader.focusIndex,
);
const viewLength = appComponent.getViewLength();
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);

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

// Focus on the first tab, expect this to be the maximum scroll distance.
appComponent.tabHeader.focusIndex = 0;
Expand Down Expand Up @@ -757,4 +760,12 @@ class SimpleTabHeaderApp {
this.tabs.push({label: 'new'});
}
}

getViewLength() {
return this.tabHeader._tabListContainer.nativeElement.offsetWidth;
}

getSelectedLabel(index: number) {
return this.tabHeader._items.toArray()[this.tabHeader.focusIndex].elementRef.nativeElement;
}
}
13 changes: 5 additions & 8 deletions src/material/tabs/paginated-tab-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,6 @@ const passiveEventListenerOptions = normalizePassiveListenerOptions({
*/
export type ScrollDirection = 'after' | 'before';

/**
* The distance in pixels that will be overshot when scrolling a tab label into view. This helps
* provide a small affordance to the label next to it.
*/
const EXAGGERATED_OVERSCROLL = 60;

/**
* Amount of milliseconds to wait before starting to scroll the header automatically.
* Set a little conservatively in order to handle fake events dispatched on touch devices.
Expand Down Expand Up @@ -524,10 +518,13 @@ export abstract class MatPaginatedTabHeader

if (labelBeforePos < beforeVisiblePos) {
// Scroll header to move label to the before direction
this.scrollDistance -= beforeVisiblePos - labelBeforePos + EXAGGERATED_OVERSCROLL;
this.scrollDistance -= beforeVisiblePos - labelBeforePos;
} else if (labelAfterPos > afterVisiblePos) {
// Scroll header to move label to the after direction
this.scrollDistance += labelAfterPos - afterVisiblePos + EXAGGERATED_OVERSCROLL;
this.scrollDistance += Math.min(
labelAfterPos - afterVisiblePos,
labelBeforePos - beforeVisiblePos,
);
}
}

Expand Down
25 changes: 18 additions & 7 deletions src/material/tabs/tab-header.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,11 @@ describe('MDC-based MatTabHeader', () => {
// Focus on the last tab, expect this to be the maximum scroll distance.
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
fixture.detectChanges();
expect(appComponent.tabHeader.scrollDistance).toBe(
appComponent.tabHeader._getMaxScrollDistance(),
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
appComponent.tabHeader.focusIndex,
);
const viewLength = appComponent.getViewLength();
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);

// Focus on the first tab, expect this to be the maximum scroll distance.
appComponent.tabHeader.focusIndex = 0;
Expand Down Expand Up @@ -329,9 +331,11 @@ describe('MDC-based MatTabHeader', () => {
// Focus the last tab so the header scrolls to the end.
appComponent.tabHeader.focusIndex = appComponent.tabs.length - 1;
fixture.detectChanges();
expect(appComponent.tabHeader.scrollDistance).toBe(
appComponent.tabHeader._getMaxScrollDistance(),
const {offsetLeft, offsetWidth} = appComponent.getSelectedLabel(
appComponent.tabHeader.focusIndex,
);
const viewLength = appComponent.getViewLength();
expect(appComponent.tabHeader.scrollDistance).toBe(offsetLeft + offsetWidth - viewLength);

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

// Focus on the first tab, expect this to be the maximum scroll distance.
appComponent.tabHeader.focusIndex = 0;
Expand Down Expand Up @@ -757,4 +760,12 @@ class SimpleTabHeaderApp {
this.tabs.push({label: 'new'});
}
}

getViewLength() {
return this.tabHeader._tabListContainer.nativeElement.offsetWidth;
}

getSelectedLabel(index: number) {
return this.tabHeader._items.toArray()[this.tabHeader.focusIndex].elementRef.nativeElement;
}
}

0 comments on commit 06ed54c

Please sign in to comment.