diff --git a/.changeset/breadcrumbs-shrink-in-flex-row.md b/.changeset/breadcrumbs-shrink-in-flex-row.md new file mode 100644 index 00000000000..74d6ef66ad6 --- /dev/null +++ b/.changeset/breadcrumbs-shrink-in-flex-row.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Breadcrumbs: In `menu` and `menu-with-root` overflow modes, collapse crumbs as soon as they no longer fit beside sibling content in a flex row, so the collapse point no longer depends on crumb length or resize direction. diff --git a/packages/react/src/Breadcrumbs/Breadcrumbs.module.css b/packages/react/src/Breadcrumbs/Breadcrumbs.module.css index 7f0a0e49277..2e52b9091d6 100644 --- a/packages/react/src/Breadcrumbs/Breadcrumbs.module.css +++ b/packages/react/src/Breadcrumbs/Breadcrumbs.module.css @@ -10,6 +10,13 @@ margin-bottom: 0; } +/* The menu modes fit the crumbs to the width the parent offers. As a flex item the nav would + otherwise never shrink below its crumbs, so they would always measure as fitting and nothing + would collapse until the parent was narrower than every crumb together. */ +.BreadcrumbsBase:where([data-overflow^='menu']) { + min-width: 0; +} + [data-overflow='menu'], [data-overflow='menu-with-root'] { & .BreadcrumbsList { diff --git a/packages/react/src/Breadcrumbs/__tests__/BreadcrumbsOverflow.test.tsx b/packages/react/src/Breadcrumbs/__tests__/BreadcrumbsOverflow.test.tsx index a8abd563646..2bea4fbc9d8 100644 --- a/packages/react/src/Breadcrumbs/__tests__/BreadcrumbsOverflow.test.tsx +++ b/packages/react/src/Breadcrumbs/__tests__/BreadcrumbsOverflow.test.tsx @@ -441,6 +441,33 @@ describe('Breadcrumbs overflow regressions (real layout)', () => { } }) + it('collapses when it shares a flex row with a sibling that can shrink', async () => { + // Mirrors a header region where the crumbs sit beside a search field. If the nav kept its + // crumbs' width, the sibling would absorb the squeeze and the crumbs would always measure as + // fitting, so nothing would collapse until the row was narrower than the crumbs alone. + // Whole-pixel widths, so rounding can't decide whether the crumbs fit. + const ui = (width: number) => ( +
+ + {['owner', 'repository'].map((label, index) => ( + + {label} + + ))} + +
+
+ ) + const {rerender} = render(ui(1600)) + await nextFrame() + expect(menuButton()).toBeNull() + + // The crumbs only fit if the sibling gives up everything above its minimum width. + rerender(ui(screen.getByRole('list').getBoundingClientRect().width + 100)) + await nextFrame() + expect(visibleLinks()).toEqual(['repository']) + }) + it('starts with the overflow menu closed after switching to wrap and back', async () => { const user = userEvent.setup() const ui = (overflow: 'wrap' | 'menu') =>
{crumbs(overflow, labels)}