Skip to content

Commit

Permalink
[UnderlineNav2]: Deprecate coarse input detection and its scroll beha…
Browse files Browse the repository at this point in the history
…viour (A11y sign-off remediations) (#2447)

* Discard the pointer types and scroll behaviour

* add changeset

* remove arrow btn styles

* introduce gap between items and remove 4px padding around the links

* [UnderlineNav2]: React router implementation fixes & docs improvement (#2448)

* react router implementation fixes

* add changeset

* [UnderlineNav2]: Add string type to the `counter` prop and display loading counter for all (#2449)

* string type for counters and fix loading issue

* add changeset

* improve docs

* update animation details

* Inverte the pulse effect

Co-authored-by: Daniel Guillan <danielguillan@github.com>

Co-authored-by: Daniel Guillan <danielguillan@github.com>

* Remove unnecessary styles (scroll behaviour styles)

* improve docs

Co-authored-by: Daniel Guillan <danielguillan@github.com>
  • Loading branch information
broccolinisoup and danielguillan committed Oct 25, 2022
1 parent ddac91d commit e03b5e4
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 409 deletions.
5 changes: 5 additions & 0 deletions .changeset/polite-dodos-behave.md
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

UnderlineNav2: Add support and docs for react router configuration
5 changes: 5 additions & 0 deletions .changeset/tough-peas-sort.md
@@ -0,0 +1,5 @@
---
'@primer/react': patch
---

UnderlineNav2: Add string type to the `counter` prop and display loading counter for all
5 changes: 5 additions & 0 deletions .changeset/witty-apples-think.md
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

UnderlineNav2: Deprecate coarse input detection and its scroll behaviour
150 changes: 79 additions & 71 deletions docs/content/drafts/UnderlineNav2.mdx
Expand Up @@ -17,9 +17,9 @@ import {UnderlineNav} from '@primer/react/drafts'

```jsx live drafts
<UnderlineNav aria-label="Repository">
<UnderlineNav.Item selected>Item 1</UnderlineNav.Item>
<UnderlineNav.Item>Item 2</UnderlineNav.Item>
<UnderlineNav.Item>Item 3</UnderlineNav.Item>
<UnderlineNav.Item selected>Code</UnderlineNav.Item>
<UnderlineNav.Item>Issues</UnderlineNav.Item>
<UnderlineNav.Item>Pull Requests</UnderlineNav.Item>
</UnderlineNav>
```

Expand Down Expand Up @@ -48,96 +48,99 @@ import {UnderlineNav} from '@primer/react/drafts'

### Overflow Behaviour

When overflow occurs, the component first hides icons if present to optimize for space and show as many items as possible. (Only for fine pointer devices)

#### Items Without Icons

```jsx live drafts
<UnderlineNav aria-label="Repository">
<UnderlineNav.Item selected icon={CodeIcon}>
Code
</UnderlineNav.Item>
<UnderlineNav.Item icon={IssueOpenedIcon} counter={30}>
Issues
</UnderlineNav.Item>
<UnderlineNav.Item icon={GitPullRequestIcon} counter={3}>
Pull Requests
</UnderlineNav.Item>
<UnderlineNav.Item icon={CommentDiscussionIcon}>Discussions</UnderlineNav.Item>
<UnderlineNav.Item icon={PlayIcon} counter={9}>
Actions
</UnderlineNav.Item>
<UnderlineNav.Item icon={ProjectIcon} counter={7}>
Projects
</UnderlineNav.Item>
<UnderlineNav.Item icon={ShieldLockIcon}>Security</UnderlineNav.Item>
<UnderlineNav.Item icon={GraphIcon}>Insights</UnderlineNav.Item>
<UnderlineNav.Item icon={GearIcon} counter={1}>
Settings
</UnderlineNav.Item>
</UnderlineNav>
Component first hides icons if they present to optimize for space and show as many items as possible. If there is still an overflow, it will display the items that don't fit in the `More` menu.

```javascript noinline live drafts
const Navigation = () => {
const items = [
{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', icon: PlayIcon, counter: 4},
{navigation: 'Projects', icon: ProjectIcon, counter: 9},
{navigation: 'Insights', icon: GraphIcon},
{navigation: 'Settings', icon: GearIcon, counter: 10},
{navigation: 'Security', icon: ShieldLockIcon}
]
const [selectedIndex, setSelectedIndex] = React.useState(0)
return (
<Box sx={{width: 750, border: '1px solid', borderBottom: 0, borderColor: 'border.default'}}>
<UnderlineNav aria-label="Repository">
{items.map((item, index) => (
<UnderlineNav.Item
key={item.navigation}
icon={item.icon}
selected={index === selectedIndex}
onSelect={e => {
setSelectedIndex(index)
e.preventDefault()
}}
counter={item.counter}
>
{item.navigation}
</UnderlineNav.Item>
))}
</UnderlineNav>
</Box>
)
}
render(<Navigation />)
```

#### Display `More` Menu

If there is still overflow, the component will behave depending on the pointer.
### Loading State For Counters

```jsx live drafts
<UnderlineNav aria-label="Repository">
<UnderlineNav.Item selected icon={CodeIcon}>
<UnderlineNav aria-label="Repository" loadingCounters={true}>
<UnderlineNav.Item counter={4} selected>
Code
</UnderlineNav.Item>
<UnderlineNav.Item icon={IssueOpenedIcon} counter={30}>
Issues
</UnderlineNav.Item>
<UnderlineNav.Item icon={GitPullRequestIcon} counter={3}>
Pull Requests
</UnderlineNav.Item>
<UnderlineNav.Item icon={CommentDiscussionIcon}>Discussions</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon} counter={9}>
Actions
</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon} counter={7}>
Projects
</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon}>Security</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon} counter={14}>
Insights
</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon} counter={1}>
Settings
</UnderlineNav.Item>
<UnderlineNav.Item icon={EyeIcon}>Wiki</UnderlineNav.Item>
<UnderlineNav.Item counter={44}>Issues</UnderlineNav.Item>
<UnderlineNav.Item>Pull Requests</UnderlineNav.Item>
</UnderlineNav>
```

### Loading state for counters
### With React Router

```jsx live drafts
<UnderlineNav aria-label="Repository" loadingCounters={true}>
<UnderlineNav.Item counter={4} selected>
Item 1
</UnderlineNav.Item>
<UnderlineNav.Item counter={44}>Item 2</UnderlineNav.Item>
<UnderlineNav.Item>Item 3</UnderlineNav.Item>
</UnderlineNav>
```jsx
import {Link} from 'react-router-dom'
import {UnderlineNav} from '@primer/react/drafts'
const Navigation = () => {
return (
<UnderlineNav aria-label="Repository">
<UnderlineNav.Item as={Link} to="code" counter={4} selected>
Code
</UnderlineNav.Item>
<UnderlineNav.Item counter={44} as={Link} to="issues">
Issues
</UnderlineNav.Item>
<UnderlineNav.Item as={Link} to="pulls">
Pull Requests
</UnderlineNav.Item>
</UnderlineNav>
)
}
```

## Props

### UnderlineNav

<PropsTable>
<PropsTableRow name="children" required type="UnderlineNav.Item[]" />
<PropsTableRow
name="aria-label"
type="string"
description="A unique name for the rendered 'nav' landmark. It will also be used to label the arrow buttons that control the scroll behaviour on coarse pointer devices. (I.e. 'Scroll ${aria-label} left/right')"
description="A unique name for the rendered 'nav' landmark. It will also be used to label the arrow
buttons that control the scroll behaviour on coarse pointer devices. (I.e.
'Scroll ${aria-label} left/right')
"
/>
<PropsTableRow
name="loadingCounters"
type="boolean"
defaultValue="false"
description="Whether all of the counters are in loading state"
description="Whether the navigation items are in loading state. Component waits for all the counters to finish loading to prevent multiple layout shifts."
/>
<PropsTableRow
name="afterSelect"
Expand All @@ -150,18 +153,23 @@ If there is still overflow, the component will behave depending on the pointer.
### UnderlineNav.Item

<PropsTable>
<PropsTableRow
name="href"
type="string"
description="The URL that the item navigates to. 'href' is passed to the underlying '<a>' element. If 'as' is specified, the component may need different props and 'href' is ignored. (Required prop for the react router is 'to' for example)"
/>
<PropsTableRow name="icon" type="Component" description="The leading icon comes before item label" />
<PropsTableRow name="selected" type="boolean" description="Whether the link is selected" />
<PropsTableRow
name="onSelect"
type="(event) => void"
description="The handler that gets called when a nav link is selected"
description="The handler that gets called when a nav link is selected. For example, a manual route binding can be done here(I.e. 'navigate(href)' for the react router.)"
/>
<PropsTableRow
name="as"
type="string | Component"
type="string | React.ElementType"
defaultValue="a"
description="What kind of component needs to be rendered"
description="The underlying element to render — either a HTML element name or a React component."
/>
<PropsTableSxRow />
</PropsTable>
Expand Down
8 changes: 4 additions & 4 deletions src/UnderlineNav2/LoadingCounter.tsx
Expand Up @@ -2,13 +2,13 @@ import styled, {keyframes} from 'styled-components'
import {get} from '../constants'

const loading = keyframes`
from { opacity: 0.4; }
to { opacity: 0.8; }
from { opacity: 1; }
to { opacity: 0.2; }
`

export const LoadingCounter = styled.span`
animation: ${loading} 1.2s linear infinite alternate;
background-color: ${get('colors.neutral.emphasis')};
animation: ${loading} 1.2s ease-in-out infinite alternate;
background-color: ${get('colors.neutral.muted')};
border-color: ${get('colors.border.default')};
width: 1.5rem;
height: 1rem; // 16px
Expand Down
5 changes: 0 additions & 5 deletions src/UnderlineNav2/UnderlineNav.test.tsx
Expand Up @@ -30,11 +30,6 @@ Object.defineProperty(window, 'matchMedia', {
}))
})

Object.defineProperty(window.Element.prototype, 'scrollTo', {
value: jest.fn(),
writable: true
})

const ResponsiveUnderlineNav = ({
selectedItemText = 'Code',
loadingCounters = false
Expand Down

0 comments on commit e03b5e4

Please sign in to comment.