From 66f7a9dc7da4da17dcfafdbd1427371d1fbce679 Mon Sep 17 00:00:00 2001 From: frankkluijtmans Date: Fri, 10 Jun 2022 10:03:53 +0200 Subject: [PATCH] [Tabs] Scroll by width of the first visible tab if only one tab is partially visible (#32778) --- packages/mui-material/src/Tabs/Tabs.js | 18 ++++++++ packages/mui-material/src/Tabs/Tabs.test.js | 50 +++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/packages/mui-material/src/Tabs/Tabs.js b/packages/mui-material/src/Tabs/Tabs.js index a27405deaa8cb4..d7aeec1be71fb8 100644 --- a/packages/mui-material/src/Tabs/Tabs.js +++ b/packages/mui-material/src/Tabs/Tabs.js @@ -443,10 +443,27 @@ const Tabs = React.forwardRef(function Tabs(inProps, ref) { scroll(scrollValue); }; + const getFirstVisibleTab = (tabs) => { + const containerSize = tabsRef.current[clientSize]; + const containerStartBound = Math.round(tabsRef.current[scrollStart]); + const containerEndBound = Math.round(containerStartBound + containerSize); + + const offset = vertical ? 'offsetTop' : 'offsetLeft'; + return tabs.find((tab) => { + const centerPoint = tab[offset] + tab[clientSize] / 2; + return centerPoint >= containerStartBound && centerPoint <= containerEndBound; + }); + }; + const getScrollSize = () => { const containerSize = tabsRef.current[clientSize]; let totalSize = 0; const children = Array.from(tabListRef.current.children); + const firstVisibleTab = getFirstVisibleTab(children); + + if (firstVisibleTab && firstVisibleTab[clientSize] > containerSize) { + return firstVisibleTab[clientSize]; + } for (let i = 0; i < children.length; i += 1) { const tab = children[i]; @@ -455,6 +472,7 @@ const Tabs = React.forwardRef(function Tabs(inProps, ref) { } totalSize += tab[clientSize]; } + return totalSize; }; diff --git a/packages/mui-material/src/Tabs/Tabs.test.js b/packages/mui-material/src/Tabs/Tabs.test.js index d1682e562cd718..31c26a6f248d50 100644 --- a/packages/mui-material/src/Tabs/Tabs.test.js +++ b/packages/mui-material/src/Tabs/Tabs.test.js @@ -692,6 +692,56 @@ describe('', () => { clock.tick(1000); expect(tablistContainer.scrollLeft).equal(100); }); + + it('should horizontally scroll by width of partially visible item', () => { + const { container, getByRole, getAllByRole } = render( + + + + + , + ); + const tablistContainer = getByRole('tablist').parentElement; + const tabs = getAllByRole('tab'); + Object.defineProperty(tablistContainer, 'clientWidth', { value: 200 }); + Object.defineProperty(tabs[0], 'clientWidth', { value: 220 }); + Object.defineProperty(tabs[1], 'clientWidth', { value: 200 }); + Object.defineProperty(tabs[2], 'clientWidth', { value: 200 }); + Object.defineProperty(tablistContainer, 'scrollWidth', { value: 620 }); + + tablistContainer.scrollLeft = 0; + fireEvent.click(findScrollButton(container, 'right')); + clock.tick(1000); + expect(tablistContainer.scrollLeft).equal(220); + }); + + it('should vertically scroll by width of partially visible item', () => { + const { container, getByRole, getAllByRole } = render( + + + + + , + ); + const tablistContainer = getByRole('tablist').parentElement; + const tabs = getAllByRole('tab'); + Object.defineProperty(tablistContainer, 'clientHeight', { value: 100 }); + Object.defineProperty(tabs[0], 'clientHeight', { value: 48 }); + Object.defineProperty(tabs[1], 'clientHeight', { value: 60 }); + Object.defineProperty(tabs[2], 'clientHeight', { value: 60 }); + Object.defineProperty(tablistContainer, 'scrollHeight', { value: 168 }); + + tablistContainer.scrollTop = 0; + fireEvent.click(findScrollButton(container, 'right')); + clock.tick(1000); + expect(tablistContainer.scrollTop).equal(48); + }); }); describe('scroll into view behavior', () => {