Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tabs] Scroll by width of the first visible tab if only one tab is partially visible #32778

Merged
merged 3 commits into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/mui-material/src/Tabs/Tabs.js
Expand Up @@ -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];
Expand All @@ -455,6 +472,7 @@ const Tabs = React.forwardRef(function Tabs(inProps, ref) {
}
totalSize += tab[clientSize];
}

return totalSize;
};

Expand Down
50 changes: 50 additions & 0 deletions packages/mui-material/src/Tabs/Tabs.test.js
Expand Up @@ -692,6 +692,56 @@ describe('<Tabs />', () => {
clock.tick(1000);
expect(tablistContainer.scrollLeft).equal(100);
});

it('should horizontally scroll by width of partially visible item', () => {
const { container, getByRole, getAllByRole } = render(
<Tabs value={0} variant="scrollable" scrollButtons style={{ width: 200 }}>
<Tab style={{ width: 220, minWidth: 'auto' }} />
<Tab style={{ width: 200, minWidth: 'auto' }} />
<Tab style={{ width: 200, minWidth: 'auto' }} />
</Tabs>,
);
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(
<Tabs
value={0}
variant="scrollable"
orientation="vertical"
scrollButtons
style={{ height: 100 }}
>
<Tab style={{ height: 48 }} />
<Tab style={{ height: 60 }} />
<Tab style={{ height: 60 }} />
</Tabs>,
);
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', () => {
Expand Down