Skip to content

Add expand to NavList #4686

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

Merged
merged 51 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3ff7b65
Add expand to `NavList`
TylerJDev Jun 19, 2024
c1bc0c6
Merge branch 'main' into add-expand-to-navlist
TylerJDev Jun 19, 2024
5e467d4
Improved semantics
TylerJDev Jun 19, 2024
f062eb5
Remove styles
TylerJDev Jun 20, 2024
ef46896
Add extra story
TylerJDev Jun 21, 2024
6912e9b
Add unit test coverage
khiga8 Jun 21, 2024
68b2ad7
Add Group unit test coverage
khiga8 Jun 21, 2024
52a885a
Add e2e test coverage
khiga8 Jun 25, 2024
2e79e6f
Add expanded to groups
TylerJDev Jun 25, 2024
c4d9b16
Merge branch 'main' into add-expand-to-navlist
TylerJDev Jun 25, 2024
fbed008
Fix import
TylerJDev Jun 25, 2024
d240bcf
Change structure
TylerJDev Jun 26, 2024
8c1d19f
Merge branch 'main' into add-expand-to-navlist
TylerJDev Jun 26, 2024
fd12d44
Update stories, add ref
TylerJDev Jun 28, 2024
f345fe4
Update tests, add focus target
TylerJDev Jun 28, 2024
4ac94e5
Add changeset
TylerJDev Jun 28, 2024
aac123f
Remove `GroupContent`
TylerJDev Jun 28, 2024
75f987a
Update to use context
TylerJDev Jul 2, 2024
3c0c942
Add useRef usage
TylerJDev Jul 2, 2024
5632d95
Change name to `NavList.ShowMoreItem`
TylerJDev Jul 3, 2024
aa6c068
Merge branch 'main' into add-expand-to-navlist
TylerJDev Jul 3, 2024
c71b358
Update docs
TylerJDev Jul 3, 2024
dcd5157
test(vrt): update snapshots
TylerJDev Jul 3, 2024
fa2e706
Update .changeset/many-rivers-deny.md
TylerJDev Jul 3, 2024
ab2462f
Address some feedback
TylerJDev Jul 12, 2024
88cbc80
Memoize id value
TylerJDev Jul 12, 2024
e128094
Merge branch 'main' into add-expand-to-navlist
TylerJDev Jan 22, 2025
b86c757
Update w/ new prop `Pages` based on suggestion from Primer patterns
TylerJDev Jan 28, 2025
36a8653
test(vrt): update snapshots
TylerJDev Jan 28, 2025
ab8a4d5
Remove story
TylerJDev Jan 28, 2025
8ea95ad
Remove story test
TylerJDev Jan 28, 2025
a1a9ea9
Remove the correct story
TylerJDev Jan 28, 2025
c6593c2
Add condition for css modules feature flag
TylerJDev Jan 28, 2025
7ad35ed
Update tests, docs, add ternary
TylerJDev Jan 29, 2025
5d0291e
Only allow for `Item` in `ShowMoreItem`
TylerJDev Feb 4, 2025
2949337
Fix lint issue
TylerJDev Feb 4, 2025
56f745b
Add dependencies
TylerJDev Feb 4, 2025
25ee7e6
Remove comment
TylerJDev Feb 4, 2025
8f1bc3b
Add data API
TylerJDev Feb 10, 2025
73bc443
Some clean up for `NavList`
TylerJDev Feb 11, 2025
9a6546e
More clean up
TylerJDev Feb 12, 2025
a83d02d
Use `useMemo` instead
TylerJDev Feb 12, 2025
c85fe50
Update docs
TylerJDev Feb 12, 2025
d40f901
Fix tests
TylerJDev Feb 12, 2025
dbbcd92
Remove auto-import
TylerJDev Feb 12, 2025
0981668
Add `key` to story
TylerJDev Feb 12, 2025
cfd37f8
PR review feedback
TylerJDev Feb 21, 2025
518b6f2
Type check
TylerJDev Feb 21, 2025
01d8ed2
Merge branch 'main' into add-expand-to-navlist
TylerJDev Feb 21, 2025
c94d612
Update packages/react/src/NavList/NavList.tsx
TylerJDev Feb 21, 2025
d22f63f
Merge branch 'main' into add-expand-to-navlist
TylerJDev Feb 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/many-rivers-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

Add `NavList.ShowMoreItem` component to support showing more content within a `NavList`.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions e2e/components/NavList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,60 @@ test.describe('NavList', () => {
})
}
})

test.describe('With expand', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-navlist--with-expand',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`NavList.With expand.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-navlist--with-expand',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})

test.describe('With group expand', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-navlist--with-group-expand',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`NavList.With group expand.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-navlist--with-group-expand',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})
})
16 changes: 16 additions & 0 deletions packages/react/src/NavList/NavList.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,22 @@
"description": "href when the TrailingAction is rendered as a link."
}
]
},
{
"name": "NavList.ShowMoreItem",
"props": [
{
"name": "label",
"type": "string",
"defaultValue": "",
"required": true,
"description": "Acccessible name for the control."
},
{
"name": "ref",
"type": "React.RefObject<HTMLButtonElement>"
}
]
}
]
}
78 changes: 78 additions & 0 deletions packages/react/src/NavList/NavList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,82 @@ export const WithTrailingActionInSubItem = () => {
)
}

export const WithExpand: StoryFn = () => (
<PageLayout>
<PageLayout.Pane position="start">
<NavList>
<NavList.Item href="#" aria-current="page">
Item 1
</NavList.Item>
<NavList.Item href="#">Item 2</NavList.Item>
<NavList.Item href="#">Item 3</NavList.Item>
<NavList.ShowMoreItem label="Show more">
<NavList.Item>Item 4</NavList.Item>
<NavList.Item>Item 5</NavList.Item>
<NavList.Item>Item 6</NavList.Item>
<NavList.Item>Item 7</NavList.Item>
<NavList.Item>Item 8</NavList.Item>
<NavList.Item>Item 9</NavList.Item>
</NavList.ShowMoreItem>
</NavList>
</PageLayout.Pane>
<PageLayout.Content></PageLayout.Content>
</PageLayout>
)

export const ExpandWithPages: StoryFn = () => (
<PageLayout>
<PageLayout.Pane position="start">
<NavList>
<NavList.Item href="#" aria-current="page">
Item 1
</NavList.Item>
<NavList.Item href="#">Item 2</NavList.Item>
<NavList.Item href="#">Item 3</NavList.Item>
<NavList.ShowMoreItem pages={2} label="Show more">
<NavList.Item>Item 4</NavList.Item>
<NavList.Item>Item 5</NavList.Item>
<NavList.Item>Item 6</NavList.Item>
<NavList.Item>Item 7</NavList.Item>
<NavList.Item>Item 8</NavList.Item>
<NavList.Item>Item 9</NavList.Item>
</NavList.ShowMoreItem>
</NavList>
</PageLayout.Pane>
<PageLayout.Content></PageLayout.Content>
</PageLayout>
)

export const WithGroupExpand = () => (
<PageLayout>
<PageLayout.Pane position="start">
<NavList>
<NavList.Group title="Group 1">
<NavList.Item aria-current="true" href="#">
Item 1A
</NavList.Item>
<NavList.Item href="#">Item 1B</NavList.Item>
<NavList.Item href="#">Item 1C</NavList.Item>
<NavList.ShowMoreItem label="More">
<NavList.Item>Item 1D</NavList.Item>
<NavList.Item>Item 1E</NavList.Item>
<NavList.Item>Item 1F</NavList.Item>
</NavList.ShowMoreItem>
</NavList.Group>
<NavList.Group title="Group 2">
<NavList.Item href="#">Item 2A</NavList.Item>
<NavList.Item href="#">Item 2B</NavList.Item>
<NavList.Item href="#">Item 2C</NavList.Item>
<NavList.ShowMoreItem label="Show">
<NavList.Item>Item 2D</NavList.Item>
<NavList.Item>Item 2E</NavList.Item>
<NavList.Item>Item 2F</NavList.Item>
</NavList.ShowMoreItem>
</NavList.Group>
</NavList>
</PageLayout.Pane>
<PageLayout.Content></PageLayout.Content>
</PageLayout>
)

export default meta
126 changes: 125 additions & 1 deletion packages/react/src/NavList/NavList.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {render, fireEvent} from '@testing-library/react'
import {render, fireEvent, act} from '@testing-library/react'
import React from 'react'
import {ThemeProvider} from '..'
import {NavList} from './NavList'
Expand Down Expand Up @@ -381,3 +381,127 @@ describe('NavList.Item with NavList.SubNav', () => {
})
})
})

describe('NavList.ShowMoreItem', () => {
function NavListWithExpand() {
return (
<NavList>
<NavList.Item href="#">Item 1</NavList.Item>
<NavList.Item href="#">Item 2</NavList.Item>
<NavList.ShowMoreItem label="More">
<NavList.Item href="#">Item 3</NavList.Item>
<NavList.Item href="#">Item 4</NavList.Item>
</NavList.ShowMoreItem>
</NavList>
)
}

it('renders with button', () => {
const {queryByRole} = render(<NavListWithExpand />)
const button = queryByRole('button', {name: 'More'})
expect(button).toBeInTheDocument()
})

it('renders button as child of <ul>', () => {
const {queryByRole} = render(<NavListWithExpand />)
const button = queryByRole('button', {name: 'More'})
const buttonParent = button!.parentElement as HTMLButtonElement

expect(buttonParent).toBeInTheDocument()
expect(buttonParent.tagName).toEqual('LI')
expect(buttonParent.parentElement?.tagName).toEqual('UL')
})

it('hides items inside of NavList.ShowMoreItem by default', () => {
const {queryByRole} = render(<NavListWithExpand />)

expect(queryByRole('link', {name: 'Item 1'})).toBeInTheDocument()
expect(queryByRole('link', {name: 'Item 2'})).toBeInTheDocument()
expect(queryByRole('link', {name: 'Item 3'})).not.toBeInTheDocument()
expect(queryByRole('link', {name: 'Item 4'})).not.toBeInTheDocument()
})

it('shows items inside of NavList.ShowMoreItem when expand button is activated', () => {
const {queryByRole} = render(<NavListWithExpand />)

act(() => {
queryByRole('button', {name: 'More'})?.click()
})

expect(queryByRole('link', {name: 'Item 1'})).toBeInTheDocument()
expect(queryByRole('link', {name: 'Item 2'})).toBeInTheDocument()
expect(queryByRole('link', {name: 'Item 3'})).toBeInTheDocument()
expect(queryByRole('link', {name: 'Item 4'})).toBeInTheDocument()

expect(queryByRole('button', {name: 'More'})).not.toBeInTheDocument()
})

it('removes expand button after it is activated', () => {
const {queryByRole} = render(<NavListWithExpand />)

act(() => {
queryByRole('button', {name: 'More'})?.click()
})

expect(queryByRole('button', {name: 'More'})).not.toBeInTheDocument()
})

it('places focus on the first of the newly shown list item', () => {
const {queryByRole} = render(<NavListWithExpand />)

act(() => {
queryByRole('button', {name: 'More'})?.click()
})

expect(queryByRole('link', {name: 'Item 3'})).toHaveFocus()
})
})

describe('NavList.ShowMoreItem with Group', () => {
function NavListWithExpand() {
return (
<NavList>
<NavList.Group title="Group 1">
<NavList.Item aria-current="true" href="#">
Item 1A
</NavList.Item>
<NavList.Item href="#">Item 1B</NavList.Item>
<NavList.Item href="#">Item 1C</NavList.Item>
<NavList.ShowMoreItem label="More">
<NavList.Item>Item 1D</NavList.Item>
<NavList.Item>Item 1E</NavList.Item>
<NavList.Item>Item 1F</NavList.Item>
</NavList.ShowMoreItem>
</NavList.Group>
<NavList.Group title="Group 2">
<NavList.Item href="#">Item 2A</NavList.Item>
<NavList.Item href="#">Item 2B</NavList.Item>
<NavList.Item href="#">Item 2C</NavList.Item>
<NavList.ShowMoreItem label="Show">
<NavList.Item>Item 2D</NavList.Item>
<NavList.Item>Item 2E</NavList.Item>
<NavList.Item>Item 2F</NavList.Item>
</NavList.ShowMoreItem>
</NavList.Group>
</NavList>
)
}

it('renders expand buttons for each group', () => {
const {queryByRole} = render(<NavListWithExpand />)

expect(queryByRole('button', {name: 'More'})).toBeInTheDocument()
expect(queryByRole('button', {name: 'Show'})).toBeInTheDocument()
})

it('renders expand buttons as within <ul>', () => {
const {queryByRole} = render(<NavListWithExpand />)

const group1Button = queryByRole('button', {name: 'More'})
const buttonParent = group1Button?.parentElement as HTMLUListElement

expect(buttonParent).toBeInTheDocument()
expect(buttonParent.tagName).toEqual('LI')
expect(buttonParent.parentElement!.tagName).toEqual('UL')
})
})
Loading
Loading