Vertical tabs

Used for creating expanding and collapsing vertical navigation menus.

  • Install
    npm install @pluralsight/ps-design-system-verticaltabs
  • Import
    import VerticalTabs from '@pluralsight/ps-design-system-verticaltabs'

Examples

App side nav

import VerticalTabs from '@pluralsight/ps-design-system-verticaltabs'
import { PlaceholderIcon } from '@pluralsight/ps-design-system-icon'
import React from 'react'
const FLOW_NAV = [
{
collapsible: false,
header: {
href: '#',
icon: <PlaceholderIcon />,
id: 'reports-home',
title: 'Reports Home'
},
items: []
},
{
collapsible: true,
header: {
icon: <PlaceholderIcon />,
id: 'operational-reports',
title: 'Operational Reports'
},
items: [
{
href: '#',
id: 'work-log',
title: 'Work Log'
},
{
href: '#',
id: 'project-timeline',
title: 'Project Timeline'
},
{
href: '#',
id: 'leaderboard',
title: 'Leaderboard'
},
{
href: '#',
id: 'snapshot',
title: 'Snapshot'
},
{
href: '#',
id: 'daily-updates',
title: 'Daily Updates'
},
{
href: '#',
id: 'spot-check',
title: 'Spot Check'
}
]
},
{
collapsible: true,
header: {
icon: <PlaceholderIcon />,
id: 'review-collaborate',
title: 'Review & Collaborate'
},
items: [
{
href: '#',
id: 'review-workflow',
title: 'Review Workflow'
},
{
href: '#',
id: 'review-collab',
title: 'Review Collaboration'
},
{
href: '#',
id: 'pr-resolution',
title: 'PR Resolution'
},
{
href: '#',
id: 'knowledge-sharing',
title: 'Knowledge Sharing'
},
{
href: '#',
id: 'player-card',
title: 'Player Card'
}
]
},
{
collapsible: true,
header: {
icon: <PlaceholderIcon />,
id: 'delivery',
title: 'Delivery'
},
items: [
{
href: '#',
title: 'Retrospective'
}
]
},
{
collapsible: true,
header: {
icon: <PlaceholderIcon />,
id: 'fundamentals',
title: 'Fundamentals'
},
items: [
{
href: '#',
id: 'fundamentals-code',
title: 'Code'
},
{
href: '#',
id: 'fundamentals-submit',
title: 'Submit'
},
{
href: '#',
id: 'fundamentals-review',
title: 'Review'
}
]
}
]
const Example: React.FC = props => {
const [open, setOpen] = React.useState(false)
const [activeId, setActiveId] = React.useState('pr-resolution')
const isActive = id => activeId === id
const activate = (evt, id) => {
evt.preventDefault()
setActiveId(id)
}
return (
<div
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
style={{
display: 'flex',
height: '100%',
justifyContent: 'start',
minHeight: 500
}}
>
<div style={{ width: open ? 240 : 72 }}>
<VerticalTabs {...props} forceCollapsed={!open} hideLabels={!open}>
<VerticalTabs.Group>
{FLOW_NAV.map((section, sectionKey) => {
const sectionHeader = section.header && (
<VerticalTabs.Tier1.Header icon={section.header.icon}>
{section.header.title}
</VerticalTabs.Tier1.Header>
)
return (
<VerticalTabs.Tier1
collapsible={section.collapsible}
key={sectionKey}
header={sectionHeader}
>
{section.items.map((item, itemKey) => {
const active = isActive(item.id)
const itemHeader = (
<VerticalTabs.Tier2.Header
href={item.href}
onClick={evt => activate(evt, item.id)}
>
{item.title}
</VerticalTabs.Tier2.Header>
)
return (
<VerticalTabs.Tier2
active={active}
header={itemHeader}
key={itemKey}
/>
)
})}
</VerticalTabs.Tier1>
)
})}
</VerticalTabs.Group>
</VerticalTabs>
</div>
</div>
)
}
export default Example
import VerticalTabs from '@pluralsight/ps-design-system-verticaltabs'
import React from 'react'
function Example() {
return (
<VerticalTabs>
<VerticalTabs.Tier1
header={
<VerticalTabs.Tier1.Header href="#">
Tier 1 link
</VerticalTabs.Tier1.Header>
}
/>
<VerticalTabs.Tier2
header={
<VerticalTabs.Tier2.Header href="#">
Tier 2 link
</VerticalTabs.Tier2.Header>
}
/>
</VerticalTabs>
)
}
export default Example

Tiers as buttons

import VerticalTabs from '@pluralsight/ps-design-system-verticaltabs'
import React from 'react'
function Example() {
return (
<VerticalTabs>
<VerticalTabs.Tier1
header={
<VerticalTabs.Tier1.Header onClick={() => {}}>
Tier 1 link
</VerticalTabs.Tier1.Header>
}
/>
<VerticalTabs.Tier2
header={
<VerticalTabs.Tier2.Header onClick={() => {}}>
Tier 2 link
</VerticalTabs.Tier2.Header>
}
/>
</VerticalTabs>
)
}
export default Example

Tiers w/React Router

Many users of this component are using it in conjunction with react-router. If you'd like to do the same and use VerticalTabs.Tier1 or VerticalTabs.Tier2 to trigger react-router links, you can follow this pattern.

import VerticalTabs from '@pluralsight/ps-design-system-verticaltabs'
import React from 'react'
import { BrowserRouter as Router, withRouter } from 'react-router-dom'
const HeaderLink = withRouter(props => {
const { history, onClick, to, ...rest } = props
const handleClick = evt => {
evt.preventDefault()
if (onClick) onClick(evt)
props.history.push(to)
}
return (
<VerticalTabs.Tier1.Header
href={props.to}
onClick={handleClick}
{...rest}
/>
)
})
function Example() {
return (
<VerticalTabs>
<Router>
<VerticalTabs.Tier1
header={
<HeaderLink to="/other">React-router Link as DS button</HeaderLink>
}
/>
</Router>
</VerticalTabs>
)
}
export default Example

Start collapsed

Each VerticalTabs.Tier1 item can start collapsed if the collapse and collapsible prop are both set.

import { PlaceholderIcon } from '@pluralsight/ps-design-system-icon'
import VerticalTabs from '@pluralsight/ps-design-system-verticaltabs'
import * as React from 'react'
const EXAMPLE_NAV = [
{
collapsible: true,
collapsed: true,
header: {
icon: <PlaceholderIcon />,
title: 'Starts collapsed'
},
items: [
{
title: 'See me after opening'
}
]
},
{
collapsible: true,
header: {
icon: <PlaceholderIcon />,
title: 'Starts open'
},
items: [
{
href: '#',
title: 'See me on load'
}
]
}
]
const Example: React.FC = () => {
return (
<VerticalTabs>
<VerticalTabs.Group>
{EXAMPLE_NAV.map((section, sectionKey) => (
<VerticalTabs.Tier1
collapsible={section.collapsible}
collapsed={section.collapsed}
key={sectionKey}
header={
<VerticalTabs.Tier1.Header icon={section.header.icon}>
{section.header.title}
</VerticalTabs.Tier1.Header>
}
>
{section.items.map((item, itemKey) => (
<VerticalTabs.Tier2
header={
<VerticalTabs.Tier2.Header href={item.href}>
{item.title}
</VerticalTabs.Tier2.Header>
}
key={itemKey}
/>
))}
</VerticalTabs.Tier1>
))}
</VerticalTabs.Group>
</VerticalTabs>
)
}
export default Example

Accessibility

WCAG 2.1 AA Compliance

100% axe-core tests
Manual audit

WAI-ARIA Patterns: Disclosure

Props

VerticalTabs

Name
Type
Description
Default
forceCollapsedbooleanforce lists to be collapsedfalse
hideLabelsbooleanhide text labelsfalse

VerticalTabs.Group

Name
Type
Description
Default
childrenVerticalTabs.Tier1[]VerticalTabs.Tier1 components
headerVerticalTabs.Group.Headerslot group header into position

VerticalTabs.Group.Header

Name
Type
Description
Default
childrenstringheader text
tagNamestringchange default tag of header root tagh2

VerticalTabs.CollapsibleGroup

Name
Type
Description
Default
childrenVerticalTabs.Tier1VerticalTabs.Tier1 components
headerVerticalTabs.CollapsibleGroupslot group header into position
startOpenbooleangroup in open is open on first render
groupButtonAriaLabelstringcustom label for collapsible group toggle toggle for screenreaders

VerticalTabs.CollapsibleGroup.Header

Name
Type
Description
Default
childrenstringheader text
tagNamestringchange default tag of header root tagh2

VerticalTabs.Tier1

Name
Type
Description
Default
activebooleanchanges visual state to active tab tierfalse
childrenVerticalTabs.Tier2VerticalTabs.Tier2 components
collapsedbooleanstart item collapsed if collapsible also truefalse
collapsiblebooleanallows collapsing of childrenfalse
headerVerticalTabs.Tier1.Headerslot tier1 header into position

VerticalTabs.Tier1.Header

Name
Type
Description
Default
childrenstringheader text
iconIconIcon component

VerticalTabs.Tier2

Name
Type
Description
Default
activebooleanchanges visual state to active tab tier
headerVerticalTabs.Tier2.Headerslot tier2 header into position

VerticalTabs.Tier2.Header

Name
Type
Description
Default
childrenstringheader text