Skip to content

Commit d91e78e

Browse files
TylerJDevCopilotsiddharthkp
authored
UnderlineNav: Add overflow: hidden when calculating items for overflow menu (#7504)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Siddharth Kshetrapal <siddharthkp@github.com>
1 parent bc0640f commit d91e78e

File tree

3 files changed

+43
-10
lines changed

3 files changed

+43
-10
lines changed

.changeset/clever-geese-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react': patch
3+
---
4+
5+
UnderlineNav: Adds `overflow: hidden` when the "More" button isn't present

packages/react/src/UnderlineNav/UnderlineNav.tsx

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ const overflowEffect = (
4747
childArray: Array<React.ReactElement<any>>,
4848
childWidthArray: ChildWidthArray,
4949
noIconChildWidthArray: ChildWidthArray,
50-
updateListAndMenu: (props: ResponsiveProps, iconsVisible: boolean) => void,
50+
updateListAndMenu: (props: ResponsiveProps, iconsVisible: boolean, overflowMeasured: boolean) => void,
5151
) => {
5252
let iconsVisible = true
5353
if (childWidthArray.length === 0) {
54-
updateListAndMenu({items: childArray, menuItems: []}, iconsVisible)
54+
updateListAndMenu({items: childArray, menuItems: []}, iconsVisible, false)
55+
return
5556
}
5657
const numberOfItemsPossible = calculatePossibleItems(childWidthArray, navWidth)
5758
const numberOfItemsWithoutIconPossible = calculatePossibleItems(noIconChildWidthArray, navWidth)
@@ -104,7 +105,7 @@ const overflowEffect = (
104105
}
105106
}
106107
}
107-
updateListAndMenu({items, menuItems}, iconsVisible)
108+
updateListAndMenu({items, menuItems}, iconsVisible, true)
108109
}
109110

110111
export const getValidChildren = (children: React.ReactNode) => {
@@ -166,6 +167,8 @@ export const UnderlineNav = forwardRef(
166167
const [iconsVisible, setIconsVisible] = useState<boolean>(true)
167168
const [childWidthArray, setChildWidthArray] = useState<ChildWidthArray>([])
168169
const [noIconChildWidthArray, setNoIconChildWidthArray] = useState<ChildWidthArray>([])
170+
// Track whether the initial overflow calculation is complete to prevent CLS
171+
const [isOverflowMeasured, setIsOverflowMeasured] = useState(false)
169172

170173
const validChildren = getValidChildren(children)
171174

@@ -209,7 +212,7 @@ export const UnderlineNav = forwardRef(
209212
prospectiveListItem: React.ReactElement<any>,
210213
indexOfProspectiveListItem: number,
211214
event: React.MouseEvent<HTMLAnchorElement> | React.KeyboardEvent<HTMLAnchorElement>,
212-
callback: (props: ResponsiveProps, displayIcons: boolean) => void,
215+
callback: (props: ResponsiveProps, displayIcons: boolean, overflowMeasured: boolean) => void,
213216
) => {
214217
// get the selected menu item's width
215218
const widthToFitIntoList = getItemsWidth(prospectiveListItem.props.children)
@@ -229,7 +232,7 @@ export const UnderlineNav = forwardRef(
229232
const updatedMenuItems = [...menuItems]
230233
// Add itemsToAddToMenu array's items to the menu at the index of the prospectiveListItem and remove 1 count of items (prospectiveListItem)
231234
updatedMenuItems.splice(indexOfProspectiveListItem, 1, ...itemsToAddToMenu)
232-
callback({items: updatedItemList, menuItems: updatedMenuItems}, false)
235+
callback({items: updatedItemList, menuItems: updatedMenuItems}, false, true)
233236
}
234237
// How many items do we need to pull in to the menu to make room for the selected menu item.
235238
function getBreakpointForItemSwapping(widthToFitIntoList: number, availableSpace: number) {
@@ -245,10 +248,17 @@ export const UnderlineNav = forwardRef(
245248
return breakpoint
246249
}
247250

248-
const updateListAndMenu = useCallback((props: ResponsiveProps, displayIcons: boolean) => {
249-
setResponsiveProps(props)
250-
setIconsVisible(displayIcons)
251-
}, [])
251+
const updateListAndMenu = useCallback(
252+
(props: ResponsiveProps, displayIcons: boolean, overflowMeasured: boolean) => {
253+
setResponsiveProps(props)
254+
setIconsVisible(displayIcons)
255+
256+
if (overflowMeasured) {
257+
setIsOverflowMeasured(true)
258+
}
259+
},
260+
[],
261+
)
252262
const setChildrenWidth = useCallback((size: ChildSize) => {
253263
setChildWidthArray(arr => {
254264
const newArr = [...arr, size]
@@ -330,7 +340,14 @@ export const UnderlineNav = forwardRef(
330340
}}
331341
>
332342
{ariaLabel && <VisuallyHidden as="h2">{`${ariaLabel} navigation`}</VisuallyHidden>}
333-
<UnderlineWrapper as={as} aria-label={ariaLabel} className={className} ref={navRef} data-variant={variant}>
343+
<UnderlineWrapper
344+
as={as}
345+
aria-label={ariaLabel}
346+
className={className}
347+
ref={navRef}
348+
data-variant={variant}
349+
data-overflow-measured={isOverflowMeasured ? 'true' : 'false'}
350+
>
334351
<UnderlineItemList ref={listRef} role="list">
335352
{listItems}
336353
{menuItems.length > 0 && (

packages/react/src/internal/components/UnderlineTabbedInterface.module.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@
1212
/* stylelint-disable-next-line primer/box-shadow */
1313
box-shadow: inset 0 -1px var(--borderColor-muted);
1414

15+
/* Hide overflow until calculation is complete to prevent CLS */
16+
overflow: visible;
17+
18+
&[data-overflow-measured='false'] {
19+
overflow: hidden;
20+
}
21+
22+
&[data-overflow-measured='true'] {
23+
overflow: visible;
24+
}
25+
1526
&[data-variant='flush'] {
1627
/* stylelint-disable-next-line primer/spacing */
1728
padding-inline: unset;

0 commit comments

Comments
 (0)