diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-25 17:23:53 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:15:27 +0100 |
| commit | c21a137e1991af1331fe5768fc6bac15ea9230b1 (patch) | |
| tree | 80569408dbed888273a15d9ae543f553f2798a9b /src/components/organisms/nav | |
| parent | 73e12fe8ae059ef70bbdf8716af421cb72aec76c (diff) | |
refactor(components): extract MainNav component from toolbar
Diffstat (limited to 'src/components/organisms/nav')
6 files changed, 178 insertions, 0 deletions
diff --git a/src/components/organisms/nav/index.ts b/src/components/organisms/nav/index.ts index ad899e0..957a64a 100644 --- a/src/components/organisms/nav/index.ts +++ b/src/components/organisms/nav/index.ts @@ -1,2 +1,3 @@ export * from './breadcrumbs'; +export * from './main-nav'; export * from './pagination'; diff --git a/src/components/organisms/nav/main-nav/index.ts b/src/components/organisms/nav/main-nav/index.ts new file mode 100644 index 0000000..6b3662f --- /dev/null +++ b/src/components/organisms/nav/main-nav/index.ts @@ -0,0 +1 @@ +export * from './main-nav'; diff --git a/src/components/organisms/nav/main-nav/main-nav.module.scss b/src/components/organisms/nav/main-nav/main-nav.module.scss new file mode 100644 index 0000000..3f94678 --- /dev/null +++ b/src/components/organisms/nav/main-nav/main-nav.module.scss @@ -0,0 +1,40 @@ +@use "../../../../styles/abstracts/functions" as fun; +@use "../../../../styles/abstracts/mixins" as mix; + +.modal { + @include mix.dimensions("md") { + padding: 0; + background: transparent; + border: none; + box-shadow: none; + } +} + +.checkbox { + &:not(:checked) { + ~ .modal { + opacity: 0; + visibility: hidden; + + @include mix.media("screen") { + @include mix.dimensions(null, "sm") { + transform: translateX(-100vw); + } + + @include mix.dimensions("sm") { + transform: perspective(#{fun.convert-px(400)}) + translate3d(0, 0, #{fun.convert-px(-400)}); + transform-origin: 100% -50%; + } + } + + @include mix.media("screen") { + @include mix.dimensions("md") { + opacity: 1; + visibility: visible; + transform: none; + } + } + } + } +} diff --git a/src/components/organisms/nav/main-nav/main-nav.stories.tsx b/src/components/organisms/nav/main-nav/main-nav.stories.tsx new file mode 100644 index 0000000..6333f2c --- /dev/null +++ b/src/components/organisms/nav/main-nav/main-nav.stories.tsx @@ -0,0 +1,71 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Icon } from '../../../atoms'; +import { MainNav } from './main-nav'; + +/** + * MainNav - Storybook Meta + */ +export default { + title: 'Organisms/Nav/MainNav', + component: MainNav, + argTypes: { + items: { + description: 'The main nav items.', + type: { + name: 'object', + required: true, + value: {}, + }, + }, + }, +} as ComponentMeta<typeof MainNav>; + +const Template: ComponentStory<typeof MainNav> = (args) => ( + <MainNav {...args} /> +); + +/** + * MainNav Stories - Default + */ +export const Default = Template.bind({}); +Default.args = { + items: [ + { id: 'home', label: 'Home', href: '#home' }, + { id: 'blog', label: 'Blog', href: '#blog' }, + { id: 'projects', label: 'Projects', href: '#projects' }, + { id: 'contact', label: 'Contact', href: '#contact' }, + ], +}; + +/** + * MainNav Stories - WithLogo + */ +export const WithLogo = Template.bind({}); +WithLogo.args = { + items: [ + { + id: 'home', + label: 'Home', + href: '#home', + logo: <Icon aria-hidden shape="home" />, + }, + { + id: 'blog', + label: 'Blog', + href: '#blog', + logo: <Icon aria-hidden shape="posts-stack" />, + }, + { + id: 'projects', + label: 'Projects', + href: '#projects', + logo: <Icon aria-hidden shape="computer" />, + }, + { + id: 'contact', + label: 'Contact', + href: '#contact', + logo: <Icon aria-hidden shape="envelop" />, + }, + ], +}; diff --git a/src/components/organisms/nav/main-nav/main-nav.test.tsx b/src/components/organisms/nav/main-nav/main-nav.test.tsx new file mode 100644 index 0000000..86c1eb5 --- /dev/null +++ b/src/components/organisms/nav/main-nav/main-nav.test.tsx @@ -0,0 +1,18 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { MainNav } from './main-nav'; + +const items = [ + { id: 'home', label: 'Home', href: '#home' }, + { id: 'blog', label: 'Blog', href: '#blog' }, + { id: 'projects', label: 'Projects', href: '#projects' }, + { id: 'contact', label: 'Contact', href: '#contact' }, +]; + +describe('MainNav', () => { + it('renders a list of nav items', () => { + render(<MainNav items={items} />); + + expect(rtlScreen.getAllByRole('link')).toHaveLength(items.length); + }); +}); diff --git a/src/components/organisms/nav/main-nav/main-nav.tsx b/src/components/organisms/nav/main-nav/main-nav.tsx new file mode 100644 index 0000000..5a19399 --- /dev/null +++ b/src/components/organisms/nav/main-nav/main-nav.tsx @@ -0,0 +1,47 @@ +import { + type ForwardRefRenderFunction, + type ReactNode, + forwardRef, +} from 'react'; +import { Nav, type NavProps } from '../../../atoms'; +import { NavItem, NavLink, NavList } from '../../../molecules'; + +export type MainNavItem = { + id: string; + href: string; + label: string; + logo?: ReactNode; +}; + +export type MainNavProps = Omit<NavProps, 'children'> & { + /** + * The main nav items. + */ + items: MainNavItem[]; +}; + +const MainNavWithRef: ForwardRefRenderFunction<HTMLElement, MainNavProps> = ( + { className = '', items, ...props }, + ref +) => { + const wrapperClass = `${className}`; + + return ( + <Nav {...props} className={wrapperClass} ref={ref}> + <NavList isInline spacing="2xs"> + {items.map(({ id, ...link }) => ( + <NavItem key={id}> + <NavLink {...link} isStack variant="main" /> + </NavItem> + ))} + </NavList> + </Nav> + ); +}; + +/** + * MainNav component + * + * Render the main navigation. + */ +export const MainNav = forwardRef(MainNavWithRef); |
