Skip to content

Commit 0f7cb22

Browse files
authored
fix(tabs): do not scroll to end in some instances (#4148)
* fix(tabs): non scroll to end * fix(tabs): missing current check * fix(tabs): include button widths for comparison
1 parent d0eea12 commit 0f7cb22

File tree

7 files changed

+70
-37
lines changed

7 files changed

+70
-37
lines changed

.changeset/healthy-squids-cheat.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@twilio-paste/code-block": patch
3+
"@twilio-paste/in-page-navigation": patch
4+
"@twilio-paste/tabs": patch
5+
"@twilio-paste/core": patch
6+
---
7+
8+
[Tabs, CodeBlock, InPageNavigation] fixed a bug where items in the tabs list may not complete the scroll, still showing the overflow right button.

packages/paste-core/components/code-block/src/CodeBlockTabList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export const CodeBlockTabList = React.forwardRef<HTMLDivElement, CodeBlockTabLis
8181
<OverflowButton
8282
position="left"
8383
onClick={() =>
84-
handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current)
84+
handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current)
8585
}
8686
visible={Boolean(elementOutOBoundsLeft)}
8787
element={element}
@@ -116,7 +116,7 @@ export const CodeBlockTabList = React.forwardRef<HTMLDivElement, CodeBlockTabLis
116116
<OverflowButton
117117
position="right"
118118
onClick={() =>
119-
handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current)
119+
handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current)
120120
}
121121
visible={Boolean(elementOutOBoundsRight)}
122122
element={element}

packages/paste-core/components/code-block/src/utlis.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export const useElementsOutOfBounds = (): {
1717
if (scrollContainer && listContainer) {
1818
const currentScrollContainerRightPosition = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().right;
1919
const currentScrollContainerXOffset = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().x;
20-
2120
let leftOutOfBounds: HTMLDivElement | null = null;
2221
let rightOutOfBounds: HTMLDivElement | null = null;
2322

@@ -30,14 +29,14 @@ export const useElementsOutOfBounds = (): {
3029
* Compares the left side of the tab with the left side of the scrollable container position
3130
* as the x value will not be 0 due to being offset in the screen.
3231
*/
33-
if (x < currentScrollContainerXOffset) {
32+
if (Math.round(x) < Math.round(currentScrollContainerXOffset - 28)) {
3433
leftOutOfBounds = tab;
3534
}
3635
/**
37-
* Compares the right side to the end of container with some buffer. Also ensure there are
36+
* Compares the right side to the end of container and button width. Also ensure there are
3837
* no value set as it loops through the array we don't want it to override the first value out of bounds.
3938
*/
40-
if (right > currentScrollContainerRightPosition + 10 && !rightOutOfBounds) {
39+
if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 28) && !rightOutOfBounds) {
4140
rightOutOfBounds = tab;
4241
}
4342
}
@@ -76,12 +75,20 @@ export const handleScrollDirection = (
7675
direction: "left" | "right",
7776
elementOutOBoundsLeft: HTMLDivElement | null,
7877
elementOutOBoundsRight: HTMLDivElement | null,
79-
listContainer: HTMLElement | null,
78+
scrollContainer: HTMLElement | null,
8079
): void => {
81-
if (listContainer) {
82-
const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight;
83-
if (elementToScrollTo) {
84-
elementToScrollTo.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
85-
}
80+
const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight;
81+
82+
if (scrollContainer && elementToScrollTo) {
83+
const elementRect = elementToScrollTo.getBoundingClientRect();
84+
const containerRect = scrollContainer.getBoundingClientRect();
85+
const containerScrollLeft = scrollContainer.scrollLeft;
86+
87+
// Calculate the new scroll position
88+
const newScrollLeft =
89+
containerScrollLeft + (elementRect.left - containerRect.left) - containerRect.width / 2 + elementRect.width / 2;
90+
91+
// Set the new scroll position
92+
scrollContainer.scrollTo({ left: newScrollLeft, behavior: "smooth" });
8693
}
8794
};

packages/paste-core/components/in-page-navigation/src/InPageNavigation.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ const InPageNavigation = React.forwardRef<HTMLDivElement, InPageNavigationProps>
196196
<OverflowButton
197197
position="left"
198198
onClick={() =>
199-
handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current)
199+
handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current)
200200
}
201201
visible={Boolean(elementOutOBoundsLeft)}
202202
element={element}
@@ -233,7 +233,7 @@ const InPageNavigation = React.forwardRef<HTMLDivElement, InPageNavigationProps>
233233
<OverflowButton
234234
position="right"
235235
onClick={() =>
236-
handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, listRef.current)
236+
handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current)
237237
}
238238
visible={Boolean(elementOutOBoundsRight)}
239239
element={element}

packages/paste-core/components/in-page-navigation/src/utils.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export const useElementsOutOfBounds = (): {
1717
if (scrollContainer && listContainer) {
1818
const currentScrollContainerRightPosition = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().right;
1919
const currentScrollContainerXOffset = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().x;
20-
2120
let leftOutOfBounds: HTMLDivElement | null = null;
2221
let rightOutOfBounds: HTMLDivElement | null = null;
2322

@@ -30,14 +29,14 @@ export const useElementsOutOfBounds = (): {
3029
* Compares the left side of the tab with the left side of the scrollable container position
3130
* as the x value will not be 0 due to being offset in the screen.
3231
*/
33-
if (x < currentScrollContainerXOffset) {
32+
if (Math.round(x) < Math.round(currentScrollContainerXOffset - 28)) {
3433
leftOutOfBounds = tab;
3534
}
3635
/**
37-
* Compares the right side to the end of container with some buffer. Also ensure there are
36+
* Compares the right side to the end of container and button width. Also ensure there are
3837
* no value set as it loops through the array we don't want it to override the first value out of bounds.
3938
*/
40-
if (right > currentScrollContainerRightPosition + 10 && !rightOutOfBounds) {
39+
if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 28) && !rightOutOfBounds) {
4140
rightOutOfBounds = tab;
4241
}
4342
}
@@ -76,12 +75,20 @@ export const handleScrollDirection = (
7675
direction: "left" | "right",
7776
elementOutOBoundsLeft: HTMLDivElement | null,
7877
elementOutOBoundsRight: HTMLDivElement | null,
79-
listContainer: HTMLElement | null,
78+
scrollContainer: HTMLElement | null,
8079
): void => {
81-
if (listContainer) {
82-
const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight;
83-
if (elementToScrollTo) {
84-
elementToScrollTo.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
85-
}
80+
const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight;
81+
82+
if (scrollContainer && elementToScrollTo) {
83+
const elementRect = elementToScrollTo.getBoundingClientRect();
84+
const containerRect = scrollContainer.getBoundingClientRect();
85+
const containerScrollLeft = scrollContainer.scrollLeft;
86+
87+
// Calculate the new scroll position
88+
const newScrollLeft =
89+
containerScrollLeft + (elementRect.left - containerRect.left) - containerRect.width / 2 + elementRect.width / 2;
90+
91+
// Set the new scroll position
92+
scrollContainer.scrollTo({ left: newScrollLeft, behavior: "smooth" });
8693
}
8794
};

packages/paste-core/components/tabs/src/TabList.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const HorizontalTabList: React.FC<React.PropsWithChildren<{ variant?: Variants;
7979
};
8080

8181
React.useEffect(() => {
82-
if (ref.current) {
82+
if (ref.current && scrollableRef.current) {
8383
scrollableRef.current?.addEventListener("scroll", handleScrollEvent);
8484
window.addEventListener("resize", handleScrollEvent);
8585
determineElementsOutOfBounds(scrollableRef.current, ref.current);
@@ -116,7 +116,9 @@ const HorizontalTabList: React.FC<React.PropsWithChildren<{ variant?: Variants;
116116
<Box display="flex" overflow="hidden">
117117
<OverflowButton
118118
position="left"
119-
onClick={() => handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, ref.current)}
119+
onClick={() =>
120+
handleScrollDirection("left", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current)
121+
}
120122
visible={Boolean(elementOutOBoundsLeft)}
121123
element={element}
122124
showShadow={showShadow}
@@ -147,7 +149,9 @@ const HorizontalTabList: React.FC<React.PropsWithChildren<{ variant?: Variants;
147149
</Box>
148150
<OverflowButton
149151
position="right"
150-
onClick={() => handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, ref.current)}
152+
onClick={() =>
153+
handleScrollDirection("right", elementOutOBoundsLeft, elementOutOBoundsRight, scrollableRef.current)
154+
}
151155
visible={Boolean(elementOutOBoundsRight)}
152156
element={element}
153157
showShadow={showShadow}

packages/paste-core/components/tabs/src/utils.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export const useElementsOutOfBounds = (): {
2323
if (scrollContainer && listContainer) {
2424
const currentScrollContainerRightPosition = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().right;
2525
const currentScrollContainerXOffset = (scrollContainer as HTMLDivElement)?.getBoundingClientRect().x;
26-
2726
let leftOutOfBounds: HTMLDivElement | null = null;
2827
let rightOutOfBounds: HTMLDivElement | null = null;
2928

@@ -36,14 +35,14 @@ export const useElementsOutOfBounds = (): {
3635
* Compares the left side of the tab with the left side of the scrollable container position
3736
* as the x value will not be 0 due to being offset in the screen.
3837
*/
39-
if (x < currentScrollContainerXOffset) {
38+
if (Math.round(x) < Math.round(currentScrollContainerXOffset - 28)) {
4039
leftOutOfBounds = tab;
4140
}
4241
/**
43-
* Compares the right side to the end of container with some buffer. Also ensure there are
42+
* Compares the right side to the end of container and button width. Also ensure there are
4443
* no value set as it loops through the array we don't want it to override the first value out of bounds.
4544
*/
46-
if (right > currentScrollContainerRightPosition + 10 && !rightOutOfBounds) {
45+
if (Math.round(right) > Math.round(currentScrollContainerRightPosition + 28) && !rightOutOfBounds) {
4746
rightOutOfBounds = tab;
4847
}
4948
}
@@ -82,12 +81,20 @@ export const handleScrollDirection = (
8281
direction: "left" | "right",
8382
elementOutOBoundsLeft: HTMLDivElement | null,
8483
elementOutOBoundsRight: HTMLDivElement | null,
85-
listContainer: HTMLElement | null,
84+
scrollContainer: HTMLElement | null,
8685
): void => {
87-
if (listContainer) {
88-
const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight;
89-
if (elementToScrollTo) {
90-
elementToScrollTo.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
91-
}
86+
const elementToScrollTo = direction === "left" ? elementOutOBoundsLeft : elementOutOBoundsRight;
87+
88+
if (scrollContainer && elementToScrollTo) {
89+
const elementRect = elementToScrollTo.getBoundingClientRect();
90+
const containerRect = scrollContainer.getBoundingClientRect();
91+
const containerScrollLeft = scrollContainer.scrollLeft;
92+
93+
// Calculate the new scroll position
94+
const newScrollLeft =
95+
containerScrollLeft + (elementRect.left - containerRect.left) - containerRect.width / 2 + elementRect.width / 2;
96+
97+
// Set the new scroll position
98+
scrollContainer.scrollTo({ left: newScrollLeft, behavior: "smooth" });
9299
}
93100
};

0 commit comments

Comments
 (0)