Skip to content

Commit

Permalink
SubNav v2 (#824)
Browse files Browse the repository at this point in the history
  • Loading branch information
rezrah authored Nov 28, 2024
1 parent 4800771 commit 6e398ba
Show file tree
Hide file tree
Showing 27 changed files with 927 additions and 214 deletions.
32 changes: 32 additions & 0 deletions .changeset/shiny-taxis-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
'@primer/react-brand': minor
---

Updates to `SubNav` component

- New anchor-based navigation pattern available:

Use `variant="anchor"` on `SubNav.SubMenu` to apply anchor navigation as an alternative to the default dropdown-based submenu.

```jsx
<SubNav>
<SubNav.Heading href="#">Heading</SubNav.Heading>
<SubNav.Link href="#" aria-current="page">
Link with anchor navigation
<SubNav.SubMenu variant="anchor">
<SubNav.Link href="#">Anchor link one</SubNav.Link>
<SubNav.Link href="#">Anchor link two</SubNav.Link>
<SubNav.Link href="#">Anchor link three</SubNav.Link>
<SubNav.Link href="#">Anchor link four</SubNav.Link>
</SubNav.SubMenu>
</SubNav.Link>
<SubNav.Link href="#">Link</SubNav.Link>
<SubNav.Link href="#">Link</SubNav.Link>
</SubNav>
```

- Overlay now closes when user clicks outside of it

- Dropdown submenus now appear with white and black background and foreground colors respectively, irrespective of color mode.

- Various other visual updates to improve brand-alignment. These include adjustments to text size, weight, color and iconography.
36 changes: 30 additions & 6 deletions apps/docs/content/components/SubNav.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ description: A sub nav is a secondary navigation element, typically positioned b

import {Label} from '@primer/react'
import {PropTableValues} from '../../src/components'
import {SubNavSubMenuVariants} from '@primer/react-brand'

```js
import {SubNav} from '@primer/react-brand'
Expand Down Expand Up @@ -63,13 +64,15 @@ import {SubNav} from '@primer/react-brand'
</Container>
```

### Optional sub menu
### Optional submenu

Submenus appear as a dropdown by default.

```jsx live
<Container sx={{position: 'relative', height: 300}}>
<SubNav hasShadow>
<SubNav.Heading href="#">Features</SubNav.Heading>
<SubNav.Link href="#">
<SubNav.Link href="#" aria-current="page">
Actions
<SubNav.SubMenu>
<SubNav.Link href="#">Actions feature one</SubNav.Link>
Expand All @@ -79,9 +82,29 @@ import {SubNav} from '@primer/react-brand'
</SubNav.SubMenu>
</SubNav.Link>
<SubNav.Link href="#">Packages</SubNav.Link>
<SubNav.Link href="#">Copilot</SubNav.Link>
<SubNav.Link href="#">Code review</SubNav.Link>
</SubNav>
</Container>
```

### Optional anchor-based submenu

```jsx live
<Container sx={{position: 'relative', height: 300}}>
<SubNav>
<SubNav.Heading href="#">Features</SubNav.Heading>
<SubNav.Link href="#" aria-current="page">
Copilot
Actions
<SubNav.SubMenu variant="anchor">
<SubNav.Link href="#">Actions feature one</SubNav.Link>
<SubNav.Link href="#">Actions feature two</SubNav.Link>
<SubNav.Link href="#">Actions feature three</SubNav.Link>
<SubNav.Link href="#">Actions feature four</SubNav.Link>
</SubNav.SubMenu>
</SubNav.Link>
<SubNav.Link href="#">Packages</SubNav.Link>
<SubNav.Link href="#">Copilot</SubNav.Link>
<SubNav.Link href="#">Code review</SubNav.Link>
</SubNav>
</Container>
Expand Down Expand Up @@ -119,6 +142,7 @@ import {SubNav} from '@primer/react-brand'

### SubNav.SubMenu

| name | type | default | required | description |
| ---------- | ------------- | ------- | -------- | ---------------------------- |
| `children` | `SubNav.Link` | | `false` | Container for sub menu links |
| name | type | default | required | description |
| ---------- | --------------------------------------------------------------------- | ----------- | -------- | ------------------------------------- |
| `children` | `SubNav.Link` | | `false` | Container for submenu links |
| `variant` | <PropTableValues values={[...SubNavSubMenuVariants]} addLineBreaks /> | `'default'` | `false` | Alternative presentation for submenus |
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ module.exports = {
dark: 'var(--brand-color-text-default)',
},
active: {
value: 'var(--base-color-scale-blue-5)',
dark: 'var(--base-color-scale-blue-3)',
value: 'var(--brand-color-text-default)',
dark: 'var(--brand-color-text-default)',
},
},
subMenu: {
bgColor: {
value: 'var(--base-color-scale-white-0)',
dark: 'var(--base-color-scale-black-0)',
dark: 'var(--base-color-scale-white-0)',
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@

### Patch Changes

- [#568](https://github.com/primer/brand/pull/568) [`40a129d`](https://github.com/primer/brand/commit/40a129d78024612b625238d8a826fc06aa933465) Thanks [@rezrah](https://github.com/rezrah)! - Added support for optional `Button` and sub menu's in `SubNav` component.
- [#568](https://github.com/primer/brand/pull/568) [`40a129d`](https://github.com/primer/brand/commit/40a129d78024612b625238d8a826fc06aa933465) Thanks [@rezrah](https://github.com/rezrah)! - Added support for optional `Button` and submenu's in `SubNav` component.

Also added `fullWidth` prop to optionally remove the default component padding.

Expand Down
133 changes: 114 additions & 19 deletions packages/react/src/SubNav/SubNav.features.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import React from 'react'
import {Meta} from '@storybook/react'
import {INITIAL_VIEWPORTS} from '@storybook/addon-viewport'
import {linkTo} from '@storybook/addon-links'

import {SubNav} from './SubNav'
import bgPath from '../fixtures/images/background-stars.png'
import {ThemeProvider} from '../ThemeProvider'
import {Box} from '../Box'
import {Hero} from '../Hero'
import {Grid} from '../Grid'
import {Heading} from '../Heading'
import {Text} from '../Text'
import {RedlineBackground} from '../component-helpers'
import {Stack} from '../Stack'
import {expect, userEvent, within} from '@storybook/test'
import {Button} from '../Button'

export default {
title: 'Components/SubNav/Features',
Expand All @@ -20,7 +25,7 @@ export default {
},
} as Meta<typeof SubNav>

export const ExampleUsage = ({hasShadow, ...args}) => (
export const DropdownVariant = ({hasShadow, ...args}) => (
<main>
<Box paddingBlockStart={64} backgroundColor="subtle" style={{position: 'relative', zIndex: 32}}></Box>
<SubNav {...args} hasShadow={hasShadow}>
Expand Down Expand Up @@ -54,31 +59,35 @@ export const ExampleUsage = ({hasShadow, ...args}) => (
</Grid>
</main>
)
ExampleUsage.parameters = {
DropdownVariant.parameters = {
layout: 'fullscreen',
}

ExampleUsage.decorators = [
Story => (
<ThemeProvider colorMode="dark">
<div style={{backgroundColor: 'black', height: '100%', minHeight: '100dvh', backgroundImage: `url(${bgPath})`}}>
<Story />
</div>
</ThemeProvider>
),
]

export const NarrowExampleUsage = args => <ExampleUsage {...args} />
NarrowExampleUsage.parameters = {
export const NarrowDropdownVariant = args => <DropdownVariant {...args} />
NarrowDropdownVariant.parameters = {
layout: 'fullscreen',
viewport: {
defaultViewport: 'iphonex',
},
}

NarrowExampleUsage.decorators = [Story => <Story />]
export const NarrowDropdownVariantMenuOpen = args => <NarrowDropdownVariant {...args} />

NarrowDropdownVariantMenuOpen.parameters = {
layout: 'fullscreen',
viewport: {
defaultViewport: 'iphonex',
},
}
NarrowDropdownVariantMenuOpen.play = async ({canvasElement}) => {
const canvas = within(canvasElement)
await userEvent.click(canvas.getByTestId('SubNav-root-button'))
const overlayMenu = canvas.getByTestId('SubNav-root-overlay')
const firstLink = within(overlayMenu).getAllByRole('link')[0]
expect(firstLink).toHaveFocus()
}

export const WithShadow = args => <ExampleUsage {...args} hasShadow={true} />
export const WithShadow = args => <DropdownVariant {...args} hasShadow={true} />
WithShadow.parameters = {
layout: 'fullscreen',
}
Expand Down Expand Up @@ -137,6 +146,92 @@ export const LongerHeading = args => (
<SubNav.Action href="#">Call to action</SubNav.Action>
</SubNav>
)
FullWidth.parameters = {
LongerHeading.parameters = {
layout: 'fullscreen',
}

const AnchorNavVariantData = {
['Scale']: 'scale',
['AI']: 'ai',
['Security']: 'security',
['Reliability']: 'reliability',
}

export const AnchorNavVariant = args => (
<main>
<Box paddingBlockStart={64} backgroundColor="subtle" style={{position: 'relative', zIndex: 32}}></Box>
<SubNav {...args}>
<SubNav.Heading
href="https://github.com/enterprise/"
onClick={e => {
e.preventDefault()
linkTo('Components/SubNav/Features', 'AnchorNavVariant')
}}
>
Enterprise
</SubNav.Heading>
<SubNav.Link href="#" aria-current="page">
Overview
<SubNav.SubMenu variant="anchor">
{Object.entries(AnchorNavVariantData).map(([label, href]) => (
<SubNav.Link key={label} href={`#${href}`}>
{label}
</SubNav.Link>
))}
</SubNav.SubMenu>
</SubNav.Link>
<SubNav.Link href="https://github.com/enterprise/advanced-security">Advanced Security</SubNav.Link>
<SubNav.Link href="https://github.com/features/copilot#enterprise">Copilot Enterprise</SubNav.Link>
<SubNav.Link href="https://github.com/premium-support">Premium Support</SubNav.Link>
</SubNav>
<Box style={{paddingBlockEnd: '100dvh'}}>
{Object.entries(AnchorNavVariantData).map(([key, value]) => (
<RedlineBackground key={key} style={{scrollPaddingTop: 64}}>
<Stack key={value} id={value} direction="vertical" style={{justifyContent: 'center', height: 1000}}>
<Heading>{key}</Heading>
<Text as="p">SubNav is a component that allows users to navigate to different sections of a page.</Text>
<Button>Learn more</Button>
</Stack>
</RedlineBackground>
))}
</Box>
</main>
)
AnchorNavVariant.parameters = {
layout: 'fullscreen',
}

const customViewports = {
Narrow: {
name: 'Narrow',
styles: {
width: '280px',
height: '600px',
},
},
}

export const NarrowAnchorNavVariant = args => <AnchorNavVariant {...args} />
NarrowAnchorNavVariant.parameters = {
layout: 'fullscreen',
viewport: {
viewports: customViewports,
defaultViewport: 'Narrow',
},
}

export const NarrowAnchorNavVariantMenuOpen = args => <NarrowAnchorNavVariant {...args} />
NarrowAnchorNavVariantMenuOpen.parameters = {
layout: 'fullscreen',
viewport: {
viewports: customViewports,
defaultViewport: 'Narrow',
},
}
NarrowAnchorNavVariantMenuOpen.play = async ({canvasElement}) => {
const canvas = within(canvasElement)
await userEvent.click(canvas.getByTestId('SubNav-root-button'))
const overlayMenu = canvas.getByTestId('SubNav-root-overlay')
const firstLink = within(overlayMenu).getAllByRole('link')[0]
expect(firstLink).toHaveFocus()
}
Loading

0 comments on commit 6e398ba

Please sign in to comment.