Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/lazy-lions-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

UnderlineNav2: Introduce "keeping the selected item always visible" functionality
6 changes: 3 additions & 3 deletions docs/content/drafts/UnderlineNav2.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {UnderlineNav} from '@primer/react/drafts'
Issues
</UnderlineNav.Item>
<UnderlineNav.Item icon={GitPullRequestIcon} counter={3}>
Pull requests
Pull Requests
</UnderlineNav.Item>
<UnderlineNav.Item icon={CommentDiscussionIcon}>Discussions</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon} counter={9}>
Expand All @@ -61,7 +61,7 @@ When overflow occurs, the component first hides icons if present to optimize for
Issues
</UnderlineNav.Item>
<UnderlineNav.Item icon={GitPullRequestIcon} counter={3}>
Pull requests
Pull Requests
</UnderlineNav.Item>
<UnderlineNav.Item icon={CommentDiscussionIcon}>Discussions</UnderlineNav.Item>
<UnderlineNav.Item icon={PlayIcon} counter={9}>
Expand Down Expand Up @@ -91,7 +91,7 @@ If there is still overflow, the component will behave depending on the pointer.
Issues
</UnderlineNav.Item>
<UnderlineNav.Item icon={GitPullRequestIcon} counter={3}>
Pull requests
Pull Requests
</UnderlineNav.Item>
<UnderlineNav.Item icon={CommentDiscussionIcon}>Discussions</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon} counter={9}>
Expand Down
118 changes: 63 additions & 55 deletions src/UnderlineNav2/UnderlineNav.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import React from 'react'
import '@testing-library/jest-dom/extend-expect'
import {fireEvent, render} from '@testing-library/react'
import {CodeIcon, EyeIcon} from '@primer/octicons-react'
import {
IconProps,
CodeIcon,
IssueOpenedIcon,
GitPullRequestIcon,
CommentDiscussionIcon,
ProjectIcon,
ShieldLockIcon,
GraphIcon
} from '@primer/octicons-react'

import {UnderlineNav} from '.'

Expand All @@ -25,46 +34,61 @@ Object.defineProperty(window.Element.prototype, 'scrollTo', {
value: jest.fn(),
writable: true
})

const ResponsiveUnderlineNav = ({
selectedItemText = 'Code',
loadingCounters = false
}: {
selectedItemText?: string
loadingCounters?: boolean
}) => {
const items: {navigation: string; icon?: React.FC<IconProps>; counter?: number}[] = [
{navigation: 'Code', icon: CodeIcon},
{navigation: 'Issues', icon: IssueOpenedIcon, counter: 120},
{navigation: 'Pull Requests', icon: GitPullRequestIcon, counter: 13},
{navigation: 'Discussions', icon: CommentDiscussionIcon, counter: 5},
{navigation: 'Actions', counter: 4},
{navigation: 'Projects', icon: ProjectIcon, counter: 9},
{navigation: 'Insights', icon: GraphIcon},
{navigation: 'Settings', counter: 10},
{navigation: 'Security', icon: ShieldLockIcon}
]
return (
<UnderlineNav label="Repository" loadingCounters={loadingCounters}>
{items.map(item => (
<UnderlineNav.Item
key={item.navigation}
icon={item.icon}
selected={item.navigation === selectedItemText}
counter={item.counter}
>
{item.navigation}
</UnderlineNav.Item>
))}
</UnderlineNav>
)
}

describe('UnderlineNav', () => {
test('selected nav', () => {
const {getByText} = render(
<UnderlineNav label="Test nav">
<UnderlineNav.Item selected>Item 1</UnderlineNav.Item>
<UnderlineNav.Item>Item 2</UnderlineNav.Item>
<UnderlineNav.Item>Item 3</UnderlineNav.Item>
</UnderlineNav>
)
const selectedNavLink = getByText('Item 1').closest('a')
it('renders aria-current attribute to be pages when an item is selected', () => {
const {getByText} = render(<ResponsiveUnderlineNav />)
const selectedNavLink = getByText('Code').closest('a')

expect(selectedNavLink?.getAttribute('aria-current')).toBe('page')
})
test('basic nav functionality', () => {
const {container} = render(
<UnderlineNav label="Test nav">
<UnderlineNav.Item selected>Item 1</UnderlineNav.Item>
<UnderlineNav.Item>Item 2</UnderlineNav.Item>
<UnderlineNav.Item>Item 3</UnderlineNav.Item>
</UnderlineNav>
)
it('renders aria-label attribute correctly', () => {
const {container} = render(<ResponsiveUnderlineNav />)
expect(container.getElementsByTagName('nav').length).toEqual(1)
const nav = container.getElementsByTagName('nav')[0]

expect(nav.getAttribute('aria-label')).toBe('Test nav')
expect(nav.getAttribute('aria-label')).toBe('Repository')
})
test('with icons', () => {
const {container} = render(
<UnderlineNav label="Test nav">
<UnderlineNav.Item icon={CodeIcon}>Code</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon} counter={6}>
Issues
</UnderlineNav.Item>
<UnderlineNav.Item>Pull Request</UnderlineNav.Item>
</UnderlineNav>
)
it('renders icons correctly', () => {
const {container} = render(<ResponsiveUnderlineNav />)
const nav = container.getElementsByTagName('nav')[0]
expect(nav.getElementsByTagName('svg').length).toEqual(2)
expect(nav.getElementsByTagName('svg').length).toEqual(7)
})
test('should fire onSelect on click and keypress', async () => {
it('fires onSelect on click and keypress', async () => {
const onSelect = jest.fn()
const {getByText} = render(
<UnderlineNav label="Test nav">
Expand All @@ -79,32 +103,16 @@ describe('UnderlineNav', () => {
fireEvent.keyPress(item, {key: 'Enter', code: 13, charCode: 13})
expect(onSelect).toHaveBeenCalledTimes(2)
})
test('respect counter prop', () => {
const {getByText} = render(
<UnderlineNav label="Test nav" align="right">
<UnderlineNav.Item counter={8} selected>
Item 1
</UnderlineNav.Item>
<UnderlineNav.Item>Item 2</UnderlineNav.Item>
<UnderlineNav.Item>Item 3</UnderlineNav.Item>
</UnderlineNav>
)
const item = getByText('Item 1').closest('a')
const counter = item?.getElementsByTagName('span')[2]
it('respects counter prop', () => {
const {getByText} = render(<ResponsiveUnderlineNav />)
const item = getByText('Issues').closest('a')
const counter = item?.getElementsByTagName('span')[3]
expect(counter?.className).toContain('CounterLabel')
expect(counter?.textContent).toBe('8')
expect(counter?.textContent).toBe('120')
})
test('respect loadingCounters prop', () => {
const {getByText} = render(
<UnderlineNav label="Test nav" loadingCounters={true}>
<UnderlineNav.Item selected counter={4}>
Item 1
</UnderlineNav.Item>
<UnderlineNav.Item>Item 2</UnderlineNav.Item>
<UnderlineNav.Item>Item 3</UnderlineNav.Item>
</UnderlineNav>
)
const item = getByText('Item 1').closest('a')
it('respects loadingCounters prop', () => {
const {getByText} = render(<ResponsiveUnderlineNav loadingCounters={true} />)
const item = getByText('Actions').closest('a')
const loadingCounter = item?.getElementsByTagName('span')[2]
expect(loadingCounter?.className).toContain('LoadingCounter')
expect(loadingCounter?.textContent).toBe('')
Expand Down
Loading