Skip to content
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

Add Nav component with some Storybook configuration #290

Merged
merged 17 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 16 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
3 changes: 3 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export const parameters = {
<Stories />
</>
),
canvas: {
sourceState: 'shown',
},
},
options: {
storySort: {
Expand Down
64 changes: 64 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@radix-ui/react-checkbox": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.1-rc.6",
"@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-switch": "^1.0.0",
"@radix-ui/react-tabs": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/system/Link/Link.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import type { StoryObj } from '@storybook/react';
import { Link } from '..';

export default {
title: 'Navigation/Link',
component: Link,
title: 'Link',
};

type Story = StoryObj< typeof Link >;
Expand Down
176 changes: 176 additions & 0 deletions src/system/Nav/Nav.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/**
* External dependencies
*/
import type { StoryObj } from '@storybook/react';

/**
* Internal dependencies
*/
import { Nav, NavItem } from '../../system';

export default {
title: 'Navigation/Nav',
component: Nav,
parameters: {
docs: {
description: {
component: `
A navigation menu is a list of links used to navigate a website. It is usually placed in a prominent position at the top of a site, or anywhere that needs a linked-navigation.

## Guidance

### When to use the Nav component

- To link internal or external links in a menu format.
- To link to pages that are not part of the main navigation.

### When to consider something else

- If you have content inside the same page that will not affect the page Route/URL, use [Tabs](/docs/tabs--docs) component instead.
- If you are planning to have buttons in your navigation, use another navigation solution, for example: [Dropdown](/docs/dropdown--docs) component instead.

## Accessibility Considerations guidance

This component is based on the Radix Navigation Menu primitive, so it contains all the accessibility features from the primitive.

- Adheres to the [navigation role requirements.](https://www.w3.org/TR/wai-aria-1.2/#navigation)
- Keyboard Interactions: https://www.radix-ui.com/primitives/docs/components/navigation-menu#keyboard-interactions

### Usability guidance

Pick one of the available variants:

- primary
- secondary
- display
- link
- tabs

### Usage with Next.js framwork

~~~jsx filename="index.jsx"
import Link from 'next/link';

<Nav.Primary label="Etc">
<NavItem.Primary
active
href="https://google.com"
asChild // This is important to pass the link styles to the child
>
<Link href={ \`/orgs/\${ id }/sso/configurations/\${ idP }/edit/\${ tab.path }\` }>
Your page name
</Link>
</NavItem.Primary>
</Nav.Primary>
~~~

-------

## Component Properties
`,
},
},
},
};

type Story = StoryObj< typeof Nav >;

export const Default: Story = {
render: () => (
<>
<p>
<strong>Variant: Primary</strong>
</p>
<Nav.Primary sx={ { mb: 4 } } label="Nav Primary">
<NavItem.Primary active href="#">
PHP
</NavItem.Primary>
<NavItem.Primary href="https://wordpress.com">WordPress</NavItem.Primary>
<NavItem.Primary href="htpps://newrelic.com/">New Relic</NavItem.Primary>
<NavItem.Primary disabled href="https://google.com/">
Not accessible
</NavItem.Primary>
</Nav.Primary>
</>
),
};

export const Secondary: Story = {
render: () => (
<>
<p>
<strong>Variant: Secondary</strong>
</p>
<Nav.Secondary sx={ { mb: 4 } } label="Nav Secondary">
<NavItem.Secondary active href="#">
PHP
</NavItem.Secondary>
<NavItem.Secondary href="https://wordpress.com">WordPress</NavItem.Secondary>
<NavItem.Secondary href="htpps://newrelic.com/">New Relic</NavItem.Secondary>
<NavItem.Secondary disabled href="https://google.com/">
Not accessible
</NavItem.Secondary>
</Nav.Secondary>
</>
),
};

export const Display: Story = {
render: () => (
<>
<p>
<strong>Variant: Display</strong>
</p>
<Nav.Display sx={ { mb: 4 } } label="Nav Display">
<NavItem.Display active href="#">
PHP
</NavItem.Display>
<NavItem.Display href="https://wordpress.com">WordPress</NavItem.Display>
<NavItem.Display href="htpps://newrelic.com/">New Relic</NavItem.Display>
<NavItem.Display disabled href="https://google.com/">
Not accessible
</NavItem.Display>
</Nav.Display>
</>
),
};

export const Link: Story = {
render: () => (
<>
<p>
<strong>Variant: Link</strong>
</p>
<Nav.Link sx={ { mb: 4 } } label="Nav Link">
<NavItem.Link active href="#">
PHP
</NavItem.Link>
<NavItem.Link href="https://wordpress.com">WordPress</NavItem.Link>
<NavItem.Link href="htpps://newrelic.com/">New Relic</NavItem.Link>
<NavItem.Link disabled href="https://google.com/">
Not accessible
</NavItem.Link>
</Nav.Link>
</>
),
};

export const Tab: Story = {
render: () => (
<>
<p>
<strong>Variant: Tab</strong>
</p>
<Nav.Tab sx={ { mb: 4 } } label="Nav Tab">
<NavItem.Tab active href="#">
PHP
</NavItem.Tab>
<NavItem.Tab href="https://wordpress.com">WordPress</NavItem.Tab>
<NavItem.Tab href="htpps://newrelic.com/">New Relic</NavItem.Tab>
<NavItem.Tab disabled href="https://google.com/">
Not accessible
</NavItem.Tab>
</Nav.Tab>
</>
),
};
51 changes: 51 additions & 0 deletions src/system/Nav/Nav.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/** @jsxImportSource theme-ui */
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
/**
* External dependencies
*/
import { render, screen } from '@testing-library/react';
import { axe } from 'jest-axe';
import { ThemeUIProvider } from 'theme-ui';

/**
* Internal dependencies
*/
import { Nav, NavItem } from './';

import { theme } from '../';

const renderWithTheme = children =>
render( <ThemeUIProvider theme={ theme }>{ children }</ThemeUIProvider> );

const renderComponent = () =>
renderWithTheme(
<Nav.Primary variant="primary" label="Main">
<NavItem.Primary href="#">PHP</NavItem.Primary>
<NavItem.Primary href="https://wordpress.com">WordPress</NavItem.Primary>
<NavItem.Primary active href="htpps://newrelic.com/">
New Relic
</NavItem.Primary>
<NavItem.Primary disabled href="https://google.com/">
Not accessible
</NavItem.Primary>
</Nav.Primary>
);

describe( '<Nav />', () => {
it( 'renders the Nav component with default value visible', async () => {
const { container } = renderComponent();

// Should find the nav label
expect( screen.getByLabelText( 'Main' ) ).toBeInTheDocument();

// Should find all links
expect( screen.queryByText( 'PHP' ) ).toBeInTheDocument();
expect( screen.queryByText( 'WordPress' ) ).toBeInTheDocument();
expect( screen.queryByText( 'New Relic' ) ).toBeInTheDocument();
expect( screen.queryByText( 'Not accessible' ) ).toHaveAttribute( 'aria-disabled', 'true' );

// Check for accessibility issues
expect( await axe( container ) ).toHaveNoViolations();
} );
} );
Loading