Skip to content

Conversation

pksjce
Copy link
Contributor

@pksjce pksjce commented Aug 25, 2025

Closes #https://github.com/github/primer/issues/5122

Changelog

Breadcrumbs now show an overflow menu with the following features

  • If no of items in the breadcrumbs is >5 then we show the menu
  • If the screen width can't accomodate the items then we show the menu
  • if hideRoot is false then we show the root/overflow menu/leaf

https://www.loom.com/share/8902798416b741c9959fc7bb501185a5

New

Changed

Removed

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Merge checklist

@Copilot Copilot AI review requested due to automatic review settings August 25, 2025 11:48
@pksjce pksjce requested a review from a team as a code owner August 25, 2025 11:48
@pksjce pksjce requested a review from francinelucca August 25, 2025 11:48
Copy link

changeset-bot bot commented Aug 25, 2025

🦋 Changeset detected

Latest commit: a24d75a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@primer/react Patch
@primer/styled-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Aug 25, 2025
Copy link
Contributor

👋 Hi, this pull request contains changes to the source code that github/github depends on. If you are GitHub staff, we recommend testing these changes with github/github using the integration workflow. Thanks!

Copilot

This comment was marked as spam.

Copy link
Contributor

github-actions bot commented Aug 25, 2025

size-limit report 📦

Path Size
packages/react/dist/browser.esm.js 90.78 KB (+0.84% 🔺)
packages/react/dist/browser.umd.js 90.98 KB (+0.84% 🔺)

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Member

@strackoverflow strackoverflow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great!

One edge case issue I uncovered (not necessarily blocking), is that when I have the root item shown and resize the window to a narrower viewport, at certain widths, the component seems to struggle with the root item getting hidden (which may be triggering another resize event), creating an infinite loop of hiding and showing the root crumb:

breadcrumbs.mov

/>
</ActionMenu.Anchor>
<ActionMenu.Overlay width="auto">
<ActionList role="menu">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this have the a11y enhancements in? if not, is the plan to follow up this PR with those enhancements? if so:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created subissues on that issue to tackle the ally issues!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I just mean feels weird to close the parent issue if there's still open subissues

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes yes, I won't be closing the parent issue. It's just an old description. I just want it linked to the issue

"@primer/react": patch
---

Spike for breadcrumbs overflow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be updated as it'll show up in the changelog

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I thought i did. I think its fine now. I think something with my cherry-pick messed it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now there's two 😅 , can you delete this one?

Comment on lines +100 to +102
// if (typeof window !== 'undefined') {
// effectiveOverflow = overflow
// }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

}
const menuElement = (
<li className={classes.BreadcrumbsItem} key="breadcrumbs-menu">
<BreadcrumbsMenuItem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from https://github.com/github/primer/issues/5122

Next Steps
Merge Breadcrumbs as currently implemented under a feature flag

But I don't see a FF here 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know! I debated it but I decided since its only addition of a prop and only that triggers the new feature, we don't need it. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the good thing about a FF is we can get away with changing more things within it to refine stuff, but if you don't foresee making any major changes (outside the pre-release window), then a prop is fine 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still undecided. There is definite change in the html structure and css to the original. I want to have a look at the review-lab here and decide

Copy link
Member

@joshblack joshblack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great! Just left some comments/questions/ideas.

One odd thing I noticed when working with the component is that when I saved the breadcrumb file, the layout would break 🤔 Curious if you ran into that too

Screen.Recording.2025-08-25.at.4.26.56.PM.mov

setChildArrayWidths(widths)
setRootItemWidth(listElementArray[0].offsetWidth)
}
}, [childArray.length])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will synchronize correctly if it depends on the length of the items instead of the items themselves changing.

Put another way, if the items are updated to be different items but of the same length then this code won't re-run and set up the state values we have for item and root width.

// Show: [root breadcrumb, overflow menu, leaf breadcrumb]
return [rootElement, menuElement, ...visibleElements]
}
}, [overflow, menuItems, effectiveHideRoot, visibleItems, rootItem, children])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm understanding right, I think on every resize this value will be memoized is that right? Even if the items aren't changing. Would it make sense in handleResize to compare the two lists (visible items, menu items) to see if they have changed before updating?

I'm also curious if, with React compiler, we need to use memo for this 🤔 Curious what you've run into for that 👀

const BreadcrumbsMenuItem = React.forwardRef<HTMLButtonElement, BreadcrumbsMenuItemProps>(
({items, 'aria-label': ariaLabel, ...rest}, ref) => {
return (
<ActionMenu>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my personal preference would be to have this use the disclosure pattern before getting this in since using a menu for navigation is not preferred.

I assume with this work that if we went down that route we would have to do more from scratch since that's not supported in ActionMenu at the moment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, We need to use a pattern involving Details. I tried it once on Friday and it was not straightforward. Will give it another go, now that I have a clean slate

Comment on lines +21 to +22
overflow?: 'wrap' | 'menu'
hideRoot?: boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely not sure what the right names would be, but what would you think of something like:

Suggested change
overflow?: 'wrap' | 'menu'
hideRoot?: boolean
overflow?: 'wrap' | 'hide' | 'hide-root'

So that we wouldn't have the case of hideRoot only making sense with overflow="menu". It would also avoid the confusion here with it not being a menu since this would be a popup with a list of links versus role="menu"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this may be an improvement, but do you think it makes the api more convoluted? menu is what it practically looks like. hide and hide-root as values dont semantically make sense in this context

Copy link
Member

@siddharthkp siddharthkp Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I correct in saying that hideRoot would only be relevant with overflow: menu? If yes, how do you feel about overflow: 'menu' | 'menu-with-hidden-root'? A little long but keeping related options together?

setMenuButtonWidth(measuredWidth)
}
}
}, [menuItems.length]) // Re-measure when menu button appears/disappears
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One idea for measuring the menu button could be to measure it in a ref callback, curious if that would make sense or not 🤔 Would avoid the need for us to have an effect for it. Hopefully this doesn't violate the rules of hooks too 😅 lol

Comment on lines +152 to +153
const itemToHide = currentVisibleItems.slice(0)[0]
const itemToHideWidth = currentVisibleItemWidths.slice(0)[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the call to .slice(0)[0] for here? Is it to do a deep copy on the item in the array or or something else?

Comment on lines +156 to +157
currentVisibleItems = [...currentVisibleItems.slice(1)]
currentVisibleItemWidths = [...currentVisibleItemWidths.slice(1)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One idea, I think .slice(1) would create a new array here so we could avoid spreading it into a new array

Suggested change
currentVisibleItems = [...currentVisibleItems.slice(1)]
currentVisibleItemWidths = [...currentVisibleItemWidths.slice(1)]
currentVisibleItems = currentVisibleItems.slice(1)
currentVisibleItemWidths = currentVisibleItemWidths.slice(1)

Comment on lines +177 to +178
visibleItems: [...currentVisibleItems],
menuItems: [...currentMenuItems],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One idea with the spread:

Suggested change
visibleItems: [...currentVisibleItems],
menuItems: [...currentMenuItems],
visibleItems: currentVisibleItems,
menuItems: currentMenuItems,

Let me know if these spread suggestions aren't applicable here btw!

<BreadcrumbsMenuItem
ref={menuButtonRef}
items={effectiveMenuItems}
aria-label={`${effectiveMenuItems.length} more breadcrumb items`}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a good use for the character specifically on toggle element (got the idea from spectrum: https://react-spectrum.adobe.com/react-spectrum/Breadcrumbs.html) and that might mean we don't need a tooltip on this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember Katie specifically mentioned that its good to use the iconbutton for the toggle element. Is that what you mean here? Also the tooltip is the same as they used in context-region

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants