diff --git a/documentation/specs/VerticalNav.md b/documentation/specs/VerticalNav.md new file mode 100644 index 00000000..a4d1a157 --- /dev/null +++ b/documentation/specs/VerticalNav.md @@ -0,0 +1,242 @@ +# `VerticalNav` Component Specification + +## Overview + +`VerticalNav` renders a vertical list of navigation links. + +### Prior Art + +- [Primer ``](https://primer.style/components/nav-list) +- [Paste ``](https://paste.twilio.design/components/sidebar-navigation) + +--- + +## Design + +`VerticalNav` will be a compound component consisting of `VerticalNav`, `VerticalNav.Item`, and `VerticalNav.Subnav`. A vertical list of links with expandable top-level items can be rendered with an `ExpandableVerticalNav`. + +`VerticalNav` uses React Stately's `useListState` hook behind the scenes. This not only handles managing the selection of items but provides consistency with other Easy UI component APIs. `ExpandableVerticalNav` uses React Stately's `useTreeState` hook to support expansion. + +`VerticalNav` can optionally render a logo at the top of the navigation with the `renderLogo` render prop. `VerticalNav` can optionally render a banner at the top of the navigation with the `renderBanner` render prop. + +`VerticalNav` supports rendering a supplementary action at the bottom of the navigation container using the `supplementaryAction` prop and the `VerticalNav.SupplementaryAction` component. `VerticalNav.SupplementaryAction` is a polymorphic component allowing for a custom element through `as`. + +`VerticalNav.Item` controls rendering individual links within a list. It contains props for the link's `label`, `icon`, and optionally a `children` prop to render nested subnavigation. Subnavigation within a `VerticalNav.Item` should use the `VerticalNav.Subnav` component. + +`VerticalNav.Item` is a polymorphic component. By default, it renders as an anchor element—``—but it can be rendered as a custom element using the `as` prop. e.g. ``. + +Selection and expansion is controlled by the consumer through the API provided by React Stately's `useListState` and `useTreeState` hooks—`selectedKey`, `expandedKeys`, and `onExpandedChange`. + +The component API is flexible by design. Constraints will be enforced at runtime to meet certain design requirements—e.g. limiting the levels of sub navigation. + +### API + +```ts +type BaseVerticalNavProps = AriaLabelingProps & { + children: ReactNode; + renderBanner?: () => ReactNode; + renderLogo?: () => ReactNode; + supplementaryAction?: ReactNode; +}; + +type VerticalNavProps = BaseVerticalNavProps & { + selectedKey?: Key; +}; + +type ExpandableVerticalNavProps = BaseVerticalNavProps & { + selectedKey?: Key; + expandedKeys?: Key[]; + onExpandedChange?: (keys: Key[]) => void; +}; + +type VerticalNavItemProps = ComponentProps & { + as?: T; + label: string; + icon?: IconSymbol; + children?: ReactNode; +}; + +type VerticalNavSubnavProps = { + selectedKey?: Key; +}; + +type VerticalNavSupplementaryAction = + ComponentProps & { + as?: T; + children: ReactNode; + }; +``` + +### Example Usage + +_Simple vertical nav:_ + +```tsx +import { VerticalNav } from "@easypost/easy-ui/VerticalNav"; + +function Sidebar() { + return ( + + + + + + + ); +} +``` + +_Dense vertical nav:_ + +```tsx +import { VerticalNav } from "@easypost/easy-ui/VerticalNav"; + +function Sidebar() { + return ( + + + + + + + + + + + + ); +} +``` + +_Expandable vertical nav:_ + +```tsx +import { + ExpandableVerticalNav, + VerticalNav, +} from "@easypost/easy-ui/VerticalNav"; + +function Sidebar() { + const [expandedKeys, setExpandedKeys] = useState(["1"]); + return ( + { + setExpandedKeys(keys); + }} + > + + + + + + + + + + + + + + + + ); +} +``` + +_Custom link component:_ + +```tsx +import Link from "next/link"; +import { VerticalNav } from "@easypost/easy-ui/VerticalNav"; + +function Sidebar() { + return ( + + + + + + + ); +} +``` + +_Rendering a logo:_ + +```tsx +import { VerticalNav } from "@easypost/easy-ui/VerticalNav"; + +function Sidebar() { + return ( + } + > + + + + + + ); +} +``` + +_Rendering a banner:_ + +```tsx +import { VerticalNav } from "@easypost/easy-ui/VerticalNav"; + +function Sidebar() { + return ( + } + selectedKey="1" + > + + + + + + ); +} +``` + +_Rendering a supplementary action:_ + +```tsx +import { VerticalNav } from "@easypost/easy-ui/VerticalNav"; + +function Sidebar() { + return ( + {}}> + Optional Bottom + + } + > + + + + + + ); +} +``` + +--- + +## Behavior + +### Accessibility + +- `VerticalNav` will be rendered as a `nav` element. +- `VerticalNav` should be labeled with `aria-label`. +- `VerticalNav.Item`s are links and should avoid being buttons or other clickable elements.