From 25441a0aece2866635500feb544f47129a02ac04 Mon Sep 17 00:00:00 2001 From: "@casey_baggz_omni" Date: Wed, 29 May 2024 09:03:44 -0500 Subject: [PATCH 01/14] docs: create nav-menu route --- docs/app/react/button/doc.mdx | 2 +- .../nav-menu/components/show-preview.tsx | 33 ++++++++ docs/app/react/nav-menu/doc.mdx | 81 +++++++++++++++++++ docs/app/react/nav-menu/page.tsx | 21 +++++ docs/app/react/show/doc.mdx | 2 +- docs/app/react/side-nav.json | 7 ++ 6 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 docs/app/react/nav-menu/components/show-preview.tsx create mode 100644 docs/app/react/nav-menu/doc.mdx create mode 100644 docs/app/react/nav-menu/page.tsx diff --git a/docs/app/react/button/doc.mdx b/docs/app/react/button/doc.mdx index 9afed0e1..d30e66f9 100644 --- a/docs/app/react/button/doc.mdx +++ b/docs/app/react/button/doc.mdx @@ -164,7 +164,7 @@ define function Button(props: ButtonProps): ReactNode | null ### Props -The `Button` component the following props: +The `Button` component accepts the following props: | Name | Default | Description | | -------- | ------- | ------------------------------------------------------------- | diff --git a/docs/app/react/nav-menu/components/show-preview.tsx b/docs/app/react/nav-menu/components/show-preview.tsx new file mode 100644 index 00000000..2a074da8 --- /dev/null +++ b/docs/app/react/nav-menu/components/show-preview.tsx @@ -0,0 +1,33 @@ +'use client' + +import { Logout } from '@cerberus-design/icons' +import { Button, Show } from '@cerberus-design/react' +import { useState } from 'react' + +export default function ShowPreview() { + const [authenticated, setAuthenticated] = useState(false) + + function handleAuthenticate() { + setAuthenticated(true) + } + + function handleUnauthenticate() { + setAuthenticated(false) + } + + return ( + + Sign in + + } + > + + + ) +} diff --git a/docs/app/react/nav-menu/doc.mdx b/docs/app/react/nav-menu/doc.mdx new file mode 100644 index 00000000..b9e4539b --- /dev/null +++ b/docs/app/react/nav-menu/doc.mdx @@ -0,0 +1,81 @@ +--- +npm: '@cerberus-design/react' +source: 'components/Show.tsx' +recipe: '' +--- + +import { + WhenToUseAdmonition, + WhenNotToUseAdmonition, +} from '@/app/components/Admonition' +import CodePreview from '@/app/components/CodePreview' +import ShowPreview from '@/app/react/show/components/show-preview' + +# Nav Menu + +A menu that uses a [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) to show and hide navigation links. + +```ts +import { NavMenu } from '@cerberus-design/react' +``` + + + +## Usage + +}> +```tsx title="nav.tsx" +import { Show, Button } from '@cerberus-design/react' +import { Logout } from '@cerberus-design/icons' + +function Nav() { + const [authenticated, setAuthenticated] = useState(false) + + function handleAuthenticate() { + setAuthenticated(true) + } + + function handleUnauthenticate() { + setAuthenticated(false) + } + + return ( + + Sign in + + } + > + + + ) +} +``` + + + + +## API + +```ts showLineNumbers=false +export interface ShowProps { + when: boolean | null | undefined + fallback?: ReactNode +} + +define function Show(props: ShowProps): ReactNode | null +``` + +### Props + +The `NavMenu` component accepts the following props: + +| Name | Default | Description | +| -------- | ------- | ------------------------------------------------------------- | +| when | false | The condition to evalue for showing the children or fallback. | +| fallback | null | The content to render when the condition is false. | diff --git a/docs/app/react/nav-menu/page.tsx b/docs/app/react/nav-menu/page.tsx new file mode 100644 index 00000000..bdca81fd --- /dev/null +++ b/docs/app/react/nav-menu/page.tsx @@ -0,0 +1,21 @@ +import ApiLinks from '@/app/components/ApiLinks' +import OnThisPage from '../../components/OnThisPage' +import { PageMainContent, PageSections } from '../../components/PageLayout' +import Doc, { frontmatter } from './doc.mdx' + +export default function NavMenuPage() { + return ( + <> + + +
+ +
+
+ + + + + + ) +} diff --git a/docs/app/react/show/doc.mdx b/docs/app/react/show/doc.mdx index 1d802185..f6d909c1 100644 --- a/docs/app/react/show/doc.mdx +++ b/docs/app/react/show/doc.mdx @@ -73,7 +73,7 @@ define function Show(props: ShowProps): ReactNode | null ### Props -The `Show` component the following props: +The `Show` component accepts the following props: | Name | Default | Description | | -------- | ------- | ------------------------------------------------------------- | diff --git a/docs/app/react/side-nav.json b/docs/app/react/side-nav.json index 2a06b13e..d4430c64 100644 --- a/docs/app/react/side-nav.json +++ b/docs/app/react/side-nav.json @@ -25,6 +25,13 @@ }, { "id": "2:b", + "label": "Nav Menu", + "route": "/react/nav-menu", + "tag": "", + "type": "route" + }, + { + "id": "2:c", "label": "Show", "route": "/react/show", "tag": "", From 3f23366adf13859fb0564aaa011741f8e318bd01 Mon Sep 17 00:00:00 2001 From: "@casey_baggz_omni" Date: Wed, 29 May 2024 10:10:22 -0500 Subject: [PATCH 02/14] feat(react): create NavMenuTrigger.tsx --- docs/app/react/nav-menu/doc.mdx | 3 + packages/react/src/components/Button.tsx | 4 + .../react/src/components/NavMenuTrigger.tsx | 58 +++++++++++++ packages/react/src/context/nav-menu.tsx | 52 +++++++++++ packages/react/src/index.ts | 2 + .../react/components/NavMenuTrigger.test.tsx | 86 +++++++++++++++++++ tests/react/context/navMenu.test.tsx | 47 ++++++++++ 7 files changed, 252 insertions(+) create mode 100644 packages/react/src/components/NavMenuTrigger.tsx create mode 100644 packages/react/src/context/nav-menu.tsx create mode 100644 tests/react/components/NavMenuTrigger.test.tsx create mode 100644 tests/react/context/navMenu.test.tsx diff --git a/docs/app/react/nav-menu/doc.mdx b/docs/app/react/nav-menu/doc.mdx index b9e4539b..09c12116 100644 --- a/docs/app/react/nav-menu/doc.mdx +++ b/docs/app/react/nav-menu/doc.mdx @@ -5,6 +5,7 @@ recipe: '' --- import { + NoteAdmonition, WhenToUseAdmonition, WhenNotToUseAdmonition, } from '@/app/components/Admonition' @@ -23,6 +24,8 @@ import { NavMenu } from '@cerberus-design/react' ## Usage + + }> ```tsx title="nav.tsx" import { Show, Button } from '@cerberus-design/react' diff --git a/packages/react/src/components/Button.tsx b/packages/react/src/components/Button.tsx index df17ce6c..3ef226ca 100644 --- a/packages/react/src/components/Button.tsx +++ b/packages/react/src/components/Button.tsx @@ -8,6 +8,10 @@ export interface ButtonProps extends ButtonHTMLAttributes { shape?: 'sharp' | 'rounded' } +/** + * A component that allows the user to perform actions + * @description https://github.com/omnifed/cerberus/blob/main/packages/react/src/components/Button.tsx + */ export function Button(props: ButtonProps) { const { palette, usage, shape, ...nativeProps } = props return ( diff --git a/packages/react/src/components/NavMenuTrigger.tsx b/packages/react/src/components/NavMenuTrigger.tsx new file mode 100644 index 00000000..b464681d --- /dev/null +++ b/packages/react/src/components/NavMenuTrigger.tsx @@ -0,0 +1,58 @@ +import { + forwardRef, + type ButtonHTMLAttributes, + type ElementType, + type ForwardedRef, +} from 'react' +import { Show } from './Show' +import type { ButtonProps } from './Button' +import { cx } from '@cerberus-design/styled-system/css' +import { button } from '@cerberus-design/styled-system/recipes' + +export interface NavMenuTriggerProps + extends ButtonHTMLAttributes, + ButtonProps { + as?: ElementType +} + +function NavMenuTriggerEL( + props: NavMenuTriggerProps, + ref: ForwardedRef, +) { + const { as, palette, usage, shape, ...nativeProps } = props + const hasAs = Boolean(as) + const AsSub: ElementType = as! + + return ( + + {props.children} + + } + > + {hasAs && } + + ) +} + +/** + * A component that allows the user to trigger a navigation menu. + * @description https://github.com/omnifed/cerberus/blob/main/packages/react/src/components/NavMenuTrigger.tsx + */ +export const NavMenuTrigger = forwardRef< + HTMLButtonElement, + NavMenuTriggerProps +>(NavMenuTriggerEL) diff --git a/packages/react/src/context/nav-menu.tsx b/packages/react/src/context/nav-menu.tsx new file mode 100644 index 00000000..452906b5 --- /dev/null +++ b/packages/react/src/context/nav-menu.tsx @@ -0,0 +1,52 @@ +'use client' + +import { + createContext, + useContext, + useMemo, + useRef, + type PropsWithChildren, + type RefObject, +} from 'react' + +export type NavTriggerRef = RefObject +export type NavMenuRef = RefObject + +export interface NavMenuContextValue { + triggerRef: NavTriggerRef | null + menuRef: NavMenuRef | null +} + +export const NavMenuContext = createContext({ + triggerRef: null, + menuRef: null, +}) + +export function NavMenuProvider(props: PropsWithChildren) { + const triggerRef = useRef(null) + const menuRef = useRef(null) + + const value = useMemo( + () => ({ + triggerRef, + menuRef, + }), + [], + ) + + return ( + + {props.children} + + ) +} + +export function useNavMenuContext() { + const context = useContext(NavMenuContext) + + if (!context) { + throw new Error('useNavMenuContext must be used within a NavMenuProvider.') + } + + return context +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 98801ccd..ef57ac7c 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,10 +1,12 @@ // components export * from './components/Button' +export * from './components/NavMenuTrigger' export * from './components/Show' // context +export * from './context/nav-menu' export * from './context/theme' // hooks diff --git a/tests/react/components/NavMenuTrigger.test.tsx b/tests/react/components/NavMenuTrigger.test.tsx new file mode 100644 index 00000000..3443d1d4 --- /dev/null +++ b/tests/react/components/NavMenuTrigger.test.tsx @@ -0,0 +1,86 @@ +import { describe, test, expect, afterEach } from 'bun:test' +import { cleanup, render, screen } from '@testing-library/react' +import { NavMenuTrigger } from '@cerberus-design/react' +import { setupStrictMode } from '@/utils' +import type { PropsWithChildren } from 'react' + +describe('NavMenuTrigger', () => { + setupStrictMode() + afterEach(cleanup) + + test('should render a default button element', () => { + render(it works) + expect(screen.getByText(/it works/i)).toBeTruthy() + }) + + test('should render a action button', () => { + render(it works) + expect( + screen + .getByText(/it works/i) + .classList.contains('cerberus-button--palette_action'), + ).toBeTrue() + }) + + test('should render a danger button', () => { + render(it works) + expect( + screen + .getByText(/it works/i) + .classList.contains('cerberus-button--palette_danger'), + ).toBeTrue() + }) + + test('should render a text button', () => { + render(it works) + expect( + screen + .getByText(/it works/i) + .classList.contains('cerberus-button--usage_text'), + ).toBeTrue() + }) + + test('should render an outline button', () => { + render(it works) + expect( + screen + .getByText(/it works/i) + .classList.contains('cerberus-button--usage_outline'), + ).toBeTrue() + }) + + test('should render a filled button', () => { + render(it works) + expect( + screen + .getByText(/it works/i) + .classList.contains('cerberus-button--usage_filled'), + ).toBeTrue() + }) + + test('should render a sharp button', () => { + render(it works) + expect( + screen + .getByText(/it works/i) + .classList.contains('cerberus-button--shape_sharp'), + ).toBeTrue() + }) + + test('should render a rounded button', () => { + render(it works) + expect( + screen + .getByText(/it works/i) + .classList.contains('cerberus-button--shape_rounded'), + ).toBeTrue() + }) + + test('should render an alternate button', () => { + function Link(props: PropsWithChildren) { + return {props.children} + } + render(it works) + expect(screen.getByText(/it works/i).tagName).toBe('A') + }) +}) diff --git a/tests/react/context/navMenu.test.tsx b/tests/react/context/navMenu.test.tsx new file mode 100644 index 00000000..3929d812 --- /dev/null +++ b/tests/react/context/navMenu.test.tsx @@ -0,0 +1,47 @@ +import { describe, test, expect, afterEach, mock, spyOn } from 'bun:test' +import { render, screen, cleanup, renderHook } from '@testing-library/react' +import { + NavMenuProvider, + NavMenuTrigger, + useNavMenuContext, +} from '@cerberus-design/react' +import { setupStrictMode } from '@/utils' + +describe('useNavMenuContext', () => { + setupStrictMode() + + function NavMenuTest() { + const state = useNavMenuContext() + + return ( + + ) + } + + afterEach(cleanup) + + test('should export a theme', () => { + render(, { wrapper: NavMenuProvider }) + expect(screen.getByText(/trigger/i)).toBeTruthy() + }) + + test('should throw an error if used outside of NavMenuProvider', () => { + // don't clog up the console with errors + spyOn(console, 'error').mockImplementation(() => null) + mock.module('react', () => { + return { useContext: () => null } + }) + + expect(() => renderHook(() => useNavMenuContext())).toThrow( + 'useNavMenuContext must be used within a NavMenuProvider', + ) + mock.restore() + }) +}) From 6f8d676096a387881134361f830d8648b1ce2b5a Mon Sep 17 00:00:00 2001 From: "@casey_baggz_omni" Date: Wed, 29 May 2024 10:27:48 -0500 Subject: [PATCH 03/14] feat(react): create nav-menu.aria --- docs/app/react/nav-menu/doc.mdx | 2 +- .../react/src/aria-helpers/nav-menu.aria.ts | 11 +++ .../react/src/components/NavMenuTrigger.tsx | 18 +++- .../src/context/{nav-menu.tsx => navMenu.tsx} | 0 packages/react/src/index.ts | 6 +- tests/react/aria-helpers/navMenuAria.test.ts | 20 ++++ .../react/components/NavMenuTrigger.test.tsx | 96 +++++++++++++++++-- tests/react/context/navMenu.test.tsx | 4 +- 8 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 packages/react/src/aria-helpers/nav-menu.aria.ts rename packages/react/src/context/{nav-menu.tsx => navMenu.tsx} (100%) create mode 100644 tests/react/aria-helpers/navMenuAria.test.ts diff --git a/docs/app/react/nav-menu/doc.mdx b/docs/app/react/nav-menu/doc.mdx index 09c12116..1688a0c0 100644 --- a/docs/app/react/nav-menu/doc.mdx +++ b/docs/app/react/nav-menu/doc.mdx @@ -17,7 +17,7 @@ import ShowPreview from '@/app/react/show/components/show-preview' A menu that uses a [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) to show and hide navigation links. ```ts -import { NavMenu } from '@cerberus-design/react' +import { NavMenu, NavMenuTrigger } from '@cerberus-design/react' ``` diff --git a/packages/react/src/aria-helpers/nav-menu.aria.ts b/packages/react/src/aria-helpers/nav-menu.aria.ts new file mode 100644 index 00000000..d2de2168 --- /dev/null +++ b/packages/react/src/aria-helpers/nav-menu.aria.ts @@ -0,0 +1,11 @@ +export interface NavTriggerAriaValues { + controls: string + expanded: boolean +} + +export function createNavTriggerProps(values: NavTriggerAriaValues) { + return { + ['aria-controls']: values.controls, + ['aria-expanded']: values.expanded, + } +} diff --git a/packages/react/src/components/NavMenuTrigger.tsx b/packages/react/src/components/NavMenuTrigger.tsx index b464681d..afe69b21 100644 --- a/packages/react/src/components/NavMenuTrigger.tsx +++ b/packages/react/src/components/NavMenuTrigger.tsx @@ -4,14 +4,19 @@ import { type ElementType, type ForwardedRef, } from 'react' -import { Show } from './Show' -import type { ButtonProps } from './Button' import { cx } from '@cerberus-design/styled-system/css' import { button } from '@cerberus-design/styled-system/recipes' +import { + createNavTriggerProps, + type NavTriggerAriaValues, +} from '../aria-helpers/nav-menu.aria' +import { Show } from './Show' +import type { ButtonProps } from './Button' export interface NavMenuTriggerProps extends ButtonHTMLAttributes, - ButtonProps { + ButtonProps, + NavTriggerAriaValues { as?: ElementType } @@ -19,7 +24,9 @@ function NavMenuTriggerEL( props: NavMenuTriggerProps, ref: ForwardedRef, ) { - const { as, palette, usage, shape, ...nativeProps } = props + const { as, palette, usage, shape, controls, expanded, ...nativeProps } = + props + const ariaProps = createNavTriggerProps({ controls, expanded }) const hasAs = Boolean(as) const AsSub: ElementType = as! @@ -29,6 +36,7 @@ function NavMenuTriggerEL( fallback={ } > - {hasAs && } + {hasAs && ( + + )} ) } - -/** - * A component that allows the user to trigger a navigation menu. - * @description https://github.com/omnifed/cerberus/blob/main/packages/react/src/components/NavMenuTrigger.tsx - */ -export const NavMenuTrigger = forwardRef< - HTMLButtonElement, - NavMenuTriggerProps ->(NavMenuTriggerEL) diff --git a/packages/react/src/context/navMenu.tsx b/packages/react/src/context/navMenu.tsx index 452906b5..fee8e20a 100644 --- a/packages/react/src/context/navMenu.tsx +++ b/packages/react/src/context/navMenu.tsx @@ -2,9 +2,11 @@ import { createContext, + useCallback, useContext, useMemo, useRef, + useState, type PropsWithChildren, type RefObject, } from 'react' @@ -15,28 +17,39 @@ export type NavMenuRef = RefObject export interface NavMenuContextValue { triggerRef: NavTriggerRef | null menuRef: NavMenuRef | null + expanded: boolean + onToggle: () => void } export const NavMenuContext = createContext({ triggerRef: null, menuRef: null, + expanded: false, + onToggle: () => {}, }) -export function NavMenuProvider(props: PropsWithChildren) { +export function NavMenu(props: PropsWithChildren) { const triggerRef = useRef(null) const menuRef = useRef(null) + const [expanded, setExpanded] = useState(false) + + const handleToggle = useCallback(() => { + setExpanded((prev) => !prev) + }, []) const value = useMemo( () => ({ triggerRef, menuRef, + expanded, + onToggle: handleToggle, }), - [], + [expanded, handleToggle], ) return ( - {props.children} + ) } diff --git a/tests/react/components/NavMenuTrigger.test.tsx b/tests/react/components/NavMenuTrigger.test.tsx index 05dddaaa..22d7c0e8 100644 --- a/tests/react/components/NavMenuTrigger.test.tsx +++ b/tests/react/components/NavMenuTrigger.test.tsx @@ -1,8 +1,8 @@ -import { describe, test, expect, afterEach } from 'bun:test' +import { describe, test, expect, afterEach, jest } from 'bun:test' import { cleanup, render, screen } from '@testing-library/react' -import { NavMenuTrigger } from '@cerberus-design/react' -import { setupStrictMode } from '@/utils' -import type { PropsWithChildren } from 'react' +import { NavMenu, NavMenuTrigger } from '@cerberus-design/react' +import { setupStrictMode, user } from '@/utils' +import { forwardRef, type ForwardedRef, type PropsWithChildren } from 'react' describe('NavMenuTrigger', () => { setupStrictMode() @@ -13,7 +13,9 @@ describe('NavMenuTrigger', () => { controls: 'menu', expanded: false, } - render(it works) + render(it works, { + wrapper: NavMenu, + }) expect(screen.getByText(/it works/i)).toBeTruthy() expect(screen.getByText(/it works/i).getAttribute('aria-controls')).toBe( 'menu', @@ -28,7 +30,9 @@ describe('NavMenuTrigger', () => { controls: 'menu-1', expanded: true, } - render(it works) + render(it works, { + wrapper: NavMenu, + }) expect( screen .getByText(/it works/i) @@ -45,6 +49,9 @@ describe('NavMenuTrigger', () => { it works , + { + wrapper: NavMenu, + }, ) expect( screen @@ -62,6 +69,9 @@ describe('NavMenuTrigger', () => { it works , + { + wrapper: NavMenu, + }, ) expect( screen @@ -79,6 +89,9 @@ describe('NavMenuTrigger', () => { it works , + { + wrapper: NavMenu, + }, ) expect( screen @@ -96,6 +109,9 @@ describe('NavMenuTrigger', () => { it works , + { + wrapper: NavMenu, + }, ) expect( screen @@ -113,6 +129,9 @@ describe('NavMenuTrigger', () => { it works , + { + wrapper: NavMenu, + }, ) expect( screen @@ -130,6 +149,9 @@ describe('NavMenuTrigger', () => { it works , + { + wrapper: NavMenu, + }, ) expect( screen @@ -143,13 +165,20 @@ describe('NavMenuTrigger', () => { controls: 'menu', expanded: false, } - function Link(props: PropsWithChildren) { - return + function LinkEL( + props: PropsWithChildren, + ref: ForwardedRef, + ) { + return } + const Link = forwardRef(LinkEL) render( it works , + { + wrapper: NavMenu, + }, ) expect(screen.getByText(/it works/i).tagName).toBe('A') expect(screen.getByText(/it works/i).getAttribute('aria-controls')).toBe( @@ -159,4 +188,21 @@ describe('NavMenuTrigger', () => { 'false', ) }) + + test('should accept a custom onClick event', async () => { + const handleClick = jest.fn() + const ariaProps = { + controls: 'menu', + } + render( + + it works + , + { + wrapper: NavMenu, + }, + ) + await user.click(screen.getByText(/it works/i)) + expect(handleClick).toHaveBeenCalledTimes(1) + }) }) diff --git a/tests/react/context/navMenu.test.tsx b/tests/react/context/navMenu.test.tsx index 58e691f4..629c4504 100644 --- a/tests/react/context/navMenu.test.tsx +++ b/tests/react/context/navMenu.test.tsx @@ -1,7 +1,7 @@ import { describe, test, expect, afterEach, mock, spyOn } from 'bun:test' import { render, screen, cleanup, renderHook } from '@testing-library/react' import { - NavMenuProvider, + NavMenu, NavMenuTrigger, useNavMenuContext, } from '@cerberus-design/react' @@ -9,32 +9,29 @@ import { setupStrictMode } from '@/utils' describe('useNavMenuContext', () => { setupStrictMode() + afterEach(cleanup) function NavMenuTest() { - const state = useNavMenuContext() - return ( - + ) } afterEach(cleanup) test('should export a theme', () => { - render(, { wrapper: NavMenuProvider }) + render() expect(screen.getByText(/trigger/i)).toBeTruthy() }) - test('should throw an error if used outside of NavMenuProvider', () => { + test('should throw an error if used outside of NavMenu', () => { // don't clog up the console with errors spyOn(console, 'error').mockImplementation(() => null) mock.module('react', () => { @@ -42,7 +39,7 @@ describe('useNavMenuContext', () => { }) expect(() => renderHook(() => useNavMenuContext())).toThrow( - 'useNavMenuContext must be used within a NavMenuProvider', + 'useNavMenuContext must be used within a NavMenu', ) mock.restore() }) From 5a6a4dcf5539b1470669dcec877cdc2e12582ff3 Mon Sep 17 00:00:00 2001 From: "@casey_baggz_omni" Date: Wed, 29 May 2024 12:34:38 -0500 Subject: [PATCH 05/14] feat(react): make NavMenuList.tsx --- docs/app/react/nav-menu/doc.mdx | 39 ++++++----------- packages/react/src/components/NavMenuList.tsx | 37 ++++++++++++++++ packages/react/src/index.ts | 1 + tests/react/components/navMenuList.test.tsx | 42 +++++++++++++++++++ tests/react/context/navMenu.test.tsx | 32 ++++++++++---- 5 files changed, 116 insertions(+), 35 deletions(-) create mode 100644 packages/react/src/components/NavMenuList.tsx create mode 100644 tests/react/components/navMenuList.test.tsx diff --git a/docs/app/react/nav-menu/doc.mdx b/docs/app/react/nav-menu/doc.mdx index c841a7ae..3fde028e 100644 --- a/docs/app/react/nav-menu/doc.mdx +++ b/docs/app/react/nav-menu/doc.mdx @@ -17,7 +17,7 @@ import ShowPreview from '@/app/react/show/components/show-preview' A menu that uses a [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) to show and hide navigation links. ```ts -import { NavMenu, NavMenuTrigger } from '@cerberus-design/react' +import { NavMenu, NavMenuTrigger, NavMenuList } from '@cerberus-design/react' ``` @@ -26,34 +26,21 @@ import { NavMenu, NavMenuTrigger } from '@cerberus-design/react' }> ```tsx title="nav.tsx" -import { Show, Button } from '@cerberus-design/react' -import { Logout } from '@cerberus-design/icons' +import { + NavMenu, + NavMenuTrigger, + NavMenuList, + NavMenuLink +} from '@cerberus-design/react' function Nav() { - const [authenticated, setAuthenticated] = useState(false) - - function handleAuthenticate() { - setAuthenticated(true) - } - - function handleUnauthenticate() { - setAuthenticated(false) - } - return ( - - Sign in - - } - > - - + + Features + + Something + + ) } ``` diff --git a/packages/react/src/components/NavMenuList.tsx b/packages/react/src/components/NavMenuList.tsx new file mode 100644 index 00000000..1501a9aa --- /dev/null +++ b/packages/react/src/components/NavMenuList.tsx @@ -0,0 +1,37 @@ +import type { AnchorHTMLAttributes, ElementType, HTMLAttributes } from 'react' +import { useNavMenuContext } from '../context/navMenu' +import { Show } from './Show' + +// + +export interface NavMenuListProps extends HTMLAttributes {} + +export function NavMenuList(props: NavMenuListProps) { + const { menuRef, expanded } = useNavMenuContext() + return ( + +