Skip to content

Commit

Permalink
fix(tabs): prevent vertical auto scroll (#4613)
Browse files Browse the repository at this point in the history
* chore(tabs): updated tabs overflow to fix scroll into view for horizontal scroll

* chore(tabs): updated golden image cache

---------

Co-authored-by: Rajdeep Chandra <rajdeepchandra@Rajdeeps-MacBook-Pro-2.local>
Co-authored-by: Rajdeep Chandra <rajdeepchandra@rajdeeps-mbp-2.macromedia.com>
  • Loading branch information
3 people authored Jul 19, 2024
1 parent 89b7f55 commit e1ef097
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 99 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ executors:
parameters:
current_golden_images_hash:
type: string
default: 019c0496b4ab51e1da329cae280244c60500cb86
default: 1dfeeaaaeb1eacac0bc3ef4a0aa30a0414d86025
wireit_cache_name:
type: string
default: wireit
Expand Down
74 changes: 72 additions & 2 deletions packages/tabs/src/Tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,38 @@ export const ScaledIndicator = {
},
};

/**
* Given that the scroll needs to be on the right side of the viewport.
* Returns the coordonate x it needs to scroll so that the tab with given index is visible.
*/
export function calculateScrollTargetForRightSide(
index: number,
direction: 'rtl' | 'ltr',
tabs: Tab[],
container: HTMLDivElement
): number {
const nextIndex = index + (direction === 'rtl' ? -1 : 1);
const nextTab = tabs[nextIndex];
const viewportEnd = container.scrollLeft + container.offsetWidth;
return nextTab ? nextTab.offsetLeft - container.offsetWidth : viewportEnd;
}

/**
* Given that the scroll needs to be on the left side of the viewport.
* Returns the coordonate x it needs to scroll so that the tab with given index is visible.
*/
export function calculateScrollTargetForLeftSide(
index: number,
direction: 'rtl' | 'ltr',
tabs: Tab[],
container: HTMLDivElement
): number {
const prevIndex = index + (direction === 'rtl' ? 1 : -1);
const prevTab = tabs[prevIndex];
const leftmostElement = direction === 'rtl' ? -container.offsetWidth : 0;
return prevTab ? prevTab.offsetLeft + prevTab.offsetWidth : leftmostElement;
}

/**
* @element sp-tabs
*
Expand Down Expand Up @@ -245,16 +277,54 @@ export class Tabs extends SizedMixin(Focusable, { noDefaultSize: true }) {
return complete;
}

private getNecessaryAutoScroll(index: number): number {
const selectedTab = this.tabs[index];
const selectionEnd = selectedTab.offsetLeft + selectedTab.offsetWidth;
const viewportEnd = this.tabList.scrollLeft + this.tabList.offsetWidth;
const selectionStart = selectedTab.offsetLeft;
const viewportStart = this.tabList.scrollLeft;

if (selectionEnd > viewportEnd) {
// Selection is on the right side, not visible.
return calculateScrollTargetForRightSide(
index,
this.dir,
this.tabs,
this.tabList
);
} else if (selectionStart < viewportStart) {
// Selection is on the left side, not visible.
return calculateScrollTargetForLeftSide(
index,
this.dir,
this.tabs,
this.tabList
);
}

return -1;
}

public async scrollToSelection(): Promise<void> {
if (!this.enableTabsScroll || !this.selected) {
return;
}

await this.updateComplete;
const selectedTab = this.tabs.find(

const selectedIndex = this.tabs.findIndex(
(tab) => tab.value === this.selected
);
selectedTab?.scrollIntoView();

if (selectedIndex !== -1 && this.tabList) {
// We have a selection, calculate the scroll needed to bring it into view
const scrollTarget = this.getNecessaryAutoScroll(selectedIndex);

// scrollTarget = -1 means it is already into view.
if (scrollTarget !== -1) {
this.tabList.scrollTo({ left: scrollTarget });
}
}
}

protected override updated(
Expand Down
23 changes: 22 additions & 1 deletion packages/tabs/stories/tabs-overflow.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/
import { TemplateResult } from '@spectrum-web-components/base';
import { html, TemplateResult } from '@spectrum-web-components/base';
import { OverflowProperties, renderTabsOverflowExample } from './index.js';

export default {
Expand All @@ -30,3 +30,24 @@ export const autoscroll = (args: OverflowProperties): TemplateResult => {
autoscroll.args = {
selected: 15,
};

// https://github.com/adobe/spectrum-web-components/issues/4590
export const autoscrollOnlyHorizontally = (
args: OverflowProperties
): TemplateResult => {
return html`
<style>
.container {
height: 500px;
overflow-y: scroll;
}
</style>
<div class="container">
<div style="height: 500px">There are some tabs down here!</div>
${renderTabsOverflowExample(args)}
</div>
`;
};
autoscrollOnlyHorizontally.args = {
selected: 15,
};
Loading

0 comments on commit e1ef097

Please # to comment.