From d1ce54b513ba46f35b1fd56d674148f04da2ae56 Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Tue, 14 Apr 2026 11:12:21 +0100 Subject: [PATCH] Merge pull request #32794 from overleaf/mj-tab-scroll [web] Allow scrolling with wheel in tab container GitOrigin-RevId: af81825288387675bdac5d734041299126d3b44c --- .../components/tabs/tabs-container.tsx | 9 +++++ .../source-editor/components/tabs.spec.tsx | 34 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/services/web/frontend/js/features/source-editor/components/tabs/tabs-container.tsx b/services/web/frontend/js/features/source-editor/components/tabs/tabs-container.tsx index 0fee481727..3415027eb7 100644 --- a/services/web/frontend/js/features/source-editor/components/tabs/tabs-container.tsx +++ b/services/web/frontend/js/features/source-editor/components/tabs/tabs-container.tsx @@ -59,6 +59,14 @@ export const TabsContainer = () => { setHovered(false) }, [throttledOnDragOver]) + const onWheel = useCallback((e: React.WheelEvent) => { + if (e.deltaY !== 0 && e.deltaX === 0) { + // if this is a purely vertical scroll, convert it to a horizontal scroll + // instead + e.currentTarget.scrollLeft += e.deltaY + } + }, []) + return (
{ onDragOver={onDragOver} onDrop={onDrop} onDragLeave={onDragLeave} + onWheel={onWheel} tabIndex={-1} > {tabs.map(tab => ( diff --git a/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx b/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx index e47b46ea34..2075c83244 100644 --- a/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/tabs.spec.tsx @@ -731,7 +731,7 @@ describe('File Tabs', function () { }) }) - describe('Tab scroll into view', function () { + describe('Tab scrolling', function () { it('scrolls the selected tab into view', function () { const manyDocs = [ { _id: DOC_IDS.main, name: 'main.tex' }, @@ -761,6 +761,38 @@ describe('File Tabs', function () { cy.findByRole('tab', { name: /main\.tex/ }).should('be.visible') }) + + it('scrolls horizontally on vertical mouse wheel', function () { + const manyDocs = [ + { _id: DOC_IDS.main, name: 'main.tex' }, + ...Array.from({ length: 10 }, (_, i) => ({ + _id: `ch${i + 1}`, + name: `chapter-${i + 1}.tex`, + })), + ] + const rootFolder = makeRootFolder(manyDocs) + mountTabs({ rootFolder }) + + // Open enough tabs to cause overflow + cy.then(() => selectDoc(DOC_IDS.main)) + for (let i = 1; i <= 10; i++) { + const id = `ch${i}` + cy.then(() => selectEntity(makeDocEntity(id, `chapter-${i}.tex`))) + } + + // Scroll back to the start + cy.findByRole('tablist').then($el => { + $el[0].scrollLeft = 0 + }) + + // Trigger a vertical wheel event on the tablist + cy.findByRole('tablist').trigger('wheel', { deltaY: 100, deltaX: 0 }) + + // scrollLeft should have increased + cy.findByRole('tablist').should($el => { + expect($el[0].scrollLeft).to.be.greaterThan(0) + }) + }) }) describe('SplitTestBadge', function () {