UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

653 lines (611 loc) 25 kB
# SideNavigation Import: `import { SideNavigation } from '@neo4j-ndl/react'` ## Props ### SideNavigation | Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `ariaLabel` | `string` | ✅ | | Aria label for the side navigation. Needed for menubar accessibility. | | `children` | `ReactNode` | | | Content displayed inside the side navigation | | `expandedWidth` | `Width<string \| number>` | | | Width used when expanded. Accepts any valid CSS width value or a number | | `isExpanded` | `boolean` | | `false` | Whether the side navigation is expanded | | `onPinButtonClick` | `(() => void)` | | | Callback function triggered when the pin button is clicked | | `ref` | `Ref<HTMLDivElement>` | | | A ref to apply to the root element. | | `shouldOverlayOnInteraction` | `boolean` | | `true` | Whether the side navigation expands on hover/focus when not pinned or compact | ### SideNavigation.CategoryHeader | Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `children` | `ReactNode` | | | Content displayed inside the side navigation category header | | `ref` | `Ref<HTMLLIElement>` | | | A ref to apply to the root element. | ### SideNavigation.CategoryItem | Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `badge` | `ItemBadgeProps` | | | Badge displayed inside the side navigation item. Represented by an object with a number and a type (e.g., 'info', 'warning', 'danger'). | | `children` | `ReactNode` | | | Content displayed inside the submenu popover. Typically a list of `SideNavigation.NavItem`. | | `icon` | `ReactNode` | | | Icon displayed inside the side navigation item | | `isActive` | `boolean` | | | Whether the side navigation item is selected | | `isMenuOpen` | `boolean` | | `false` | If true, the menu will be open. Otherwise, the menu will open and close based on the user hovering the item. | | `label` | `string` | | | Text label of the side navigation item | | `ref` | `Ref<HTMLButtonElement>` | | | A ref to apply to the root element. | ### SideNavigation.Divider _No props documented._ ### SideNavigation.ItemBadge | Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `number` | `number` | ✅ | | Number displayed inside the side navigation item badge | | `ref` | `Ref<HTMLSpanElement>` | | | A ref to apply to the root element. | | `type` | `'danger' \| 'info' \| 'warning'` | ✅ | | Type of the side navigation item badge | ### SideNavigation.ListItem | Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `children` | `ReactNode` | | | Content displayed inside the side navigation list item | | `ref` | `Ref<HTMLLIElement>` | | | A ref to apply to the root element. | ### SideNavigation.NavItem | Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | `as` | `("button" & ElementType<any, keyof IntrinsicElements>) \| ("symbol" & ElementType<any, keyof IntrinsicElements>) \| ... 177 more ... \| (FunctionComponent<...> & ElementType<...>)` | | | An override of the default HTML tag of the root of the component. Can also be another React component. What HTML element to render the root element as. | | `badge` | `ItemBadgeProps` | | | Badge displayed inside the side navigation item. Represented by an object with a number and a type (e.g., 'info', 'warning', 'danger'). | | `href` | `string` | | | The href of the link. Only applicable if `as` is `'a'`. | | `icon` | `ReactNode` | | | Icon displayed inside the side navigation item | | `isActive` | `boolean` | | | Whether the side navigation item is selected | | `label` | `string` | | | Text label of the side navigation item | | `onClick` | `MouseEventHandler<HTMLButtonElement>` | | | Event handler for when the button is clicked. Only applicable if `as` is `'button'`. | | `ref` | `any` | | | A ref to apply to the root element. | | `target` | `HTMLAttributeAnchorTarget` | | | The target of the link. Only applicable if `as` is `'a'`. | | `trailingElement` | `ReactNode` | | | Element displayed on the leading side of the side navigation item (e.g., counter) | ## Examples ### Side Nav Badges ```tsx import '@neo4j-ndl/base/lib/neo4j-ds-styles.css'; import { SideNavigation, Typography } from '@neo4j-ndl/react'; import { CodeBracketIconOutline, HomeIconOutline, MagnifyingGlassIconOutline, ServerIconOutline, WrenchScrewdriverIconOutline, } from '@neo4j-ndl/react/icons'; import { useState } from 'react'; const Component = () => { const [active, setActive] = useState<string | null>(null); const [isExpanded, setIsExpanded] = useState(false); return ( <div className="n-h-[calc(40vh-32px)] n-min-h-[700px] n-flex n-bg-neutral-bg-default"> <SideNavigation ariaLabel="Main side navigation" isExpanded={isExpanded} shouldOverlayOnInteraction={true} onPinButtonClick={() => setIsExpanded(!isExpanded)} > <SideNavigation.ListItem> <SideNavigation.NavItem icon={<HomeIconOutline />} label="Info Badge" isActive={active === 'nav-item-1'} onClick={() => setActive('nav-item-1')} badge={{ number: 1, type: 'info', }} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<CodeBracketIconOutline />} label="Warning Badge" isActive={active === 'nav-item-2'} onClick={() => setActive('nav-item-2')} badge={{ number: 1, type: 'warning', }} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<ServerIconOutline />} label="Critical Badge" isActive={active === 'nav-item-3'} onClick={() => setActive('nav-item-3')} badge={{ number: 1, type: 'danger', }} /> </SideNavigation.ListItem> <SideNavigation.Divider /> <SideNavigation.ListItem> <SideNavigation.CategoryItem icon={<WrenchScrewdriverIconOutline />} label="Category Item" isActive={['nav-item-4', 'nav-item-5', 'nav-item-6'].includes( active ?? '', )} badge={{ number: 3, type: 'danger', }} > <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Info Badge" isActive={active === 'nav-item-4'} onClick={() => setActive('nav-item-4')} badge={{ number: 1, type: 'info', }} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Warning Badge" isActive={active === 'nav-item-5'} onClick={() => setActive('nav-item-5')} badge={{ number: 1, type: 'warning', }} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Critical Badge" isActive={active === 'nav-item-6'} onClick={() => setActive('nav-item-6')} badge={{ number: 1, type: 'danger', }} /> </SideNavigation.ListItem> </SideNavigation.CategoryItem> </SideNavigation.ListItem> </SideNavigation> <div className="n-h-full n-w-full"> <div className="n-flex n-flex-col n-p-8 n-w-full n-max-w-[1024px] n-mx-auto"> <div className="n-flex n-flex-col n-gap-token-8 n-bg-neutral-bg-weak n-p-token-32 n-rounded-lg n-items-center n-justify-center"> <Typography variant="title-4">Side Nav with Badges</Typography> <Typography variant="body-medium"> Badges can be used to indicate that there are actions that need attention from the user on the specific page. The badge can have three different types of severity: <b className="n-text-primary-text"> info</b>, <b className="n-text-warning-text"> warning</b> and <b className="n-text-danger-text"> danger</b>. <br /> <br /> The badge on a <b>CategoryItem</b> should be the type of the highest priority badge of the items in the category. The number should be the total number of badges in the category. </Typography> </div> </div> </div> </div> ); }; export default Component; ``` ### Side Nav Compact ```tsx import '@neo4j-ndl/base/lib/neo4j-ds-styles.css'; import { SideNavigation, Typography } from '@neo4j-ndl/react'; import { AcademicCapIconOutline, ChartBarIconOutline, CodeBracketIconOutline, Cog6ToothIconOutline, HomeIconOutline, MagnifyingGlassIconOutline, ServerIconOutline, WrenchScrewdriverIconOutline, } from '@neo4j-ndl/react/icons'; import { useState } from 'react'; const Component = () => { const [active, setActive] = useState<string | null>(null); return ( <div className="n-h-[calc(40vh-32px)] n-min-h-[700px] n-flex n-bg-neutral-bg-default"> <SideNavigation isExpanded={false} shouldOverlayOnInteraction={false} ariaLabel="Main side navigation" > <SideNavigation.ListItem> <SideNavigation.NavItem icon={<HomeIconOutline />} label="Nav Item" isActive={active === 'nav-item-1'} onClick={() => setActive('nav-item-1')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<CodeBracketIconOutline />} label="Nav Item" isActive={active === 'nav-item-2'} onClick={() => setActive('nav-item-2')} /> </SideNavigation.ListItem> <SideNavigation.Divider /> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<ServerIconOutline />} label="Nav Item" isActive={active === 'nav-item-3'} onClick={() => setActive('nav-item-3')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.CategoryItem icon={<WrenchScrewdriverIconOutline />} label="Category Item" isActive={['nav-item-4', 'nav-item-5'].includes(active ?? '')} > <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Nested Nav Item" isActive={active === 'nav-item-4'} onClick={() => setActive('nav-item-4')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Nested Nav Item" isActive={active === 'nav-item-5'} onClick={() => setActive('nav-item-5')} /> </SideNavigation.ListItem> </SideNavigation.CategoryItem> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<ChartBarIconOutline />} label="Nav Item" isActive={active === 'nav-item-6'} onClick={() => setActive('nav-item-6')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<Cog6ToothIconOutline />} label="Nav Item" isActive={active === 'nav-item-7'} onClick={() => setActive('nav-item-7')} /> </SideNavigation.ListItem> <SideNavigation.Divider /> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<AcademicCapIconOutline />} label="Nav Item" isActive={active === 'nav-item-8'} onClick={() => setActive('nav-item-8')} /> </SideNavigation.ListItem> </SideNavigation> <div className="n-h-full n-w-full"> <div className="n-flex n-flex-col n-p-8 n-w-full n-max-w-[1024px] n-mx-auto"> <div className="n-flex n-flex-col n-gap-token-8 n-bg-neutral-bg-weak n-p-token-32 n-rounded-lg n-items-center n-justify-center"> <Typography variant="title-4">Compact Side Nav</Typography> <Typography variant="body-medium"> When using a compact side nav you must make sure to set a <b> label</b> on the NavItems, as the label will be used for the built in tooltip. It is also needed for screen readers to behave as correctly. </Typography> </div> </div> </div> </div> ); }; export default Component; ``` ### Side Nav Default ```tsx import '@neo4j-ndl/base/lib/neo4j-ds-styles.css'; import { SideNavigation, Typography } from '@neo4j-ndl/react'; import { AcademicCapIconOutline, ChartBarIconOutline, CodeBracketIconOutline, Cog6ToothIconOutline, HomeIconOutline, MagnifyingGlassIconOutline, ServerIconOutline, WrenchScrewdriverIconOutline, } from '@neo4j-ndl/react/icons'; import { useState } from 'react'; const Component = () => { const [active, setActive] = useState<string | null>(null); const [isExpanded, setIsExpanded] = useState(false); return ( <div className="n-h-[calc(40vh-32px)] n-min-h-[700px] n-flex n-bg-neutral-bg-default"> <SideNavigation isExpanded={isExpanded} shouldOverlayOnInteraction={true} onPinButtonClick={() => setIsExpanded(!isExpanded)} ariaLabel="Main side navigation" > <SideNavigation.CategoryHeader> Example Category Header </SideNavigation.CategoryHeader> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<HomeIconOutline />} label="Nav Item" isActive={active === 'nav-item-1'} onClick={() => setActive('nav-item-1')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<CodeBracketIconOutline />} label="Nav Item" isActive={active === 'nav-item-2'} onClick={() => setActive('nav-item-2')} /> </SideNavigation.ListItem> <SideNavigation.Divider /> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<ServerIconOutline />} label="Nav Item" isActive={active === 'nav-item-3'} onClick={() => setActive('nav-item-3')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.CategoryItem icon={<WrenchScrewdriverIconOutline />} label="Category Item" isActive={['nav-item-4', 'nav-item-5'].includes(active ?? '')} > <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Nested Nav Item" isActive={active === 'nav-item-4'} onClick={() => setActive('nav-item-4')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Nested Nav Item" isActive={active === 'nav-item-5'} onClick={() => setActive('nav-item-5')} /> </SideNavigation.ListItem> </SideNavigation.CategoryItem> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<ChartBarIconOutline />} label="Nav Item" isActive={active === 'nav-item-6'} onClick={() => setActive('nav-item-6')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<Cog6ToothIconOutline />} label="Nav Item" isActive={active === 'nav-item-7'} onClick={() => setActive('nav-item-7')} /> </SideNavigation.ListItem> <SideNavigation.Divider /> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<AcademicCapIconOutline />} label="Nav Item" isActive={active === 'nav-item-8'} onClick={() => setActive('nav-item-8')} /> </SideNavigation.ListItem> </SideNavigation> <div className="n-h-full n-w-full"> <div className="n-flex n-flex-col n-p-8 n-w-full n-max-w-[1024px] n-mx-auto"> <div className="n-flex n-flex-col n-gap-token-8 n-bg-neutral-bg-weak n-p-token-32 n-rounded-lg n-items-center n-justify-center"> <Typography variant="title-4">Default Side Nav</Typography> <Typography variant="body-medium"> This is the default behavior for a side nav. It expands on hover, and can be pinned to stay expanded. </Typography> </div> </div> </div> </div> ); }; export default Component; ``` ### Side Nav Secondary ```tsx import '@neo4j-ndl/base/lib/neo4j-ds-styles.css'; import { SideNavigation, Typography } from '@neo4j-ndl/react'; import { AcademicCapIconOutline, ChartBarIconOutline, CodeBracketIconOutline, Cog6ToothIconOutline, HomeIconOutline, MagnifyingGlassIconOutline, ServerIconOutline, WrenchScrewdriverIconOutline, } from '@neo4j-ndl/react/icons'; import { useState } from 'react'; const Component = () => { const [active, setActive] = useState<string>('nav-item-8'); const [secondaryActive, setSecondaryActive] = useState<string>('beginner'); const [isExpanded, setIsExpanded] = useState(false); return ( <div className="n-h-[calc(40vh-32px)] n-min-h-[700px] n-flex n-bg-neutral-bg-default n-overflow-hidden"> <SideNavigation isExpanded={isExpanded} shouldOverlayOnInteraction={true} onPinButtonClick={() => setIsExpanded(!isExpanded)} ariaLabel="Main side navigation" > <SideNavigation.ListItem> <SideNavigation.NavItem icon={<HomeIconOutline />} label="Nav Item" isActive={active === 'nav-item-1'} onClick={() => setActive('nav-item-1')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<CodeBracketIconOutline />} label="Nav Item" isActive={active === 'nav-item-2'} onClick={() => setActive('nav-item-2')} /> </SideNavigation.ListItem> <SideNavigation.Divider /> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<ServerIconOutline />} label="Nav Item" isActive={active === 'nav-item-3'} onClick={() => setActive('nav-item-3')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.CategoryItem icon={<WrenchScrewdriverIconOutline />} label="Category Item" isActive={['nav-item-4', 'nav-item-5'].includes(active)} > <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Nested Nav Item" isActive={active === 'nav-item-4'} onClick={() => setActive('nav-item-4')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<MagnifyingGlassIconOutline />} label="Nested Nav Item" isActive={active === 'nav-item-5'} onClick={() => setActive('nav-item-5')} /> </SideNavigation.ListItem> </SideNavigation.CategoryItem> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<ChartBarIconOutline />} label="Nav Item" isActive={active === 'nav-item-6'} onClick={() => setActive('nav-item-6')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<Cog6ToothIconOutline />} label="Nav Item" isActive={active === 'nav-item-7'} onClick={() => setActive('nav-item-7')} /> </SideNavigation.ListItem> <SideNavigation.Divider /> <SideNavigation.ListItem> <SideNavigation.NavItem icon={<AcademicCapIconOutline />} label="Nav Item" isActive={active === 'nav-item-8'} onClick={() => setActive('nav-item-8')} /> </SideNavigation.ListItem> </SideNavigation> <SideNavigation isExpanded={true} shouldOverlayOnInteraction={false} expandedWidth={220} ariaLabel="Secondary side navigation" > <SideNavigation.CategoryHeader> Category Header </SideNavigation.CategoryHeader> <SideNavigation.ListItem> <SideNavigation.NavItem label="Secondary Nav Item" isActive={secondaryActive === 'secondary-nav-item-1'} onClick={() => setSecondaryActive('secondary-nav-item-1')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem label="Secondary Nav Item" isActive={secondaryActive === 'secondary-nav-item-2'} onClick={() => setSecondaryActive('secondary-nav-item-2')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem label="Secondary Nav Item" isActive={secondaryActive === 'secondary-nav-item-3'} onClick={() => setSecondaryActive('secondary-nav-item-3')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem label="Secondary Nav Item" isActive={secondaryActive === 'secondary-nav-item-4'} onClick={() => setSecondaryActive('secondary-nav-item-4')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem label="Secondary Nav Item" isActive={secondaryActive === 'secondary-nav-item-5'} onClick={() => setSecondaryActive('secondary-nav-item-5')} /> </SideNavigation.ListItem> <SideNavigation.CategoryHeader> Category Header </SideNavigation.CategoryHeader> <SideNavigation.ListItem> <SideNavigation.NavItem label="Secondary Nav Item" icon={<MagnifyingGlassIconOutline />} isActive={secondaryActive === 'secondary-nav-item-6'} onClick={() => setSecondaryActive('secondary-nav-item-6')} /> </SideNavigation.ListItem> <SideNavigation.ListItem> <SideNavigation.NavItem label="Secondary Nav Item" icon={<MagnifyingGlassIconOutline />} isActive={secondaryActive === 'secondary-nav-item-7'} onClick={() => setSecondaryActive('secondary-nav-item-7')} /> </SideNavigation.ListItem> </SideNavigation> <div className="n-h-full n-w-full"> <div className="n-flex n-flex-col n-p-8 n-w-full n-max-w-[1024px] n-mx-auto"> <div className="n-flex n-flex-col n-gap-token-8 n-bg-neutral-bg-weak n-p-token-32 n-rounded-lg n-items-center n-justify-center"> <Typography variant="title-4">Secondary Side Nav</Typography> <Typography variant="body-medium"> If a page requires its own navigation, you can use a secondary side nav. The secondary side nav is always expanded and does not have a pin button. </Typography> </div> </div> </div> </div> ); }; export default Component; ```