AdminSidebar
ImplementedSticky admin sidebar with a logo slot and grouped navigation lists whose active row gets aria-current and a teal accent.
components/organisms/AdminSidebar/AdminSidebar.tsxSticky 247px admin sidebar with a logo slot and grouped navigation lists whose active row gets aria-current and a teal accent. For new dashboard work prefer the Sidebar molecule (portal mobile drawer with scrim + focus trap, gated on collapsible).
AdminSidebar
Organism — Admin family
Sticky w-[247px] h-screen aside with a logo slot and grouped nav lists (h3 label + role=list per group). Items with href render as real anchors; items without render as buttons firing onItemClick. The active row gets aria-current='page' + a teal left-accent; row heights honor --ep-tap-min. No mobile drawer — for responsive dashboards use the Sidebar molecule (portal drawer + focus trap; enable its collapsible prop).
Click a row — links would navigate (href="#…" here); "Exporter" has no href, so it is a button.
No button item fired yet.
This component supports light and dark mode.
Usage
Pass groups (real sections, not decoration — each heading labels its list), match activeId to the item id (never derived from the URL), and choose link vs button per item: href for real navigation, no href for panel toggles and exports. Pair with AdminLayout / AdminHeader for the full admin frame — the shell is a fixed 247px sticky column with its own scroll.
import { AdminSidebar } from '@epasslive/design-system'
const groups = [
{
id: 'commerce',
label: 'Commerce',
items: [
{ id: 'events', label: 'Événements', href: '/admin/events' },
{ id: 'orders', label: 'Commandes', href: '/admin/orders' },
],
},
{
id: 'finance',
label: 'Finance',
items: [
{ id: 'payouts', label: 'Versements', href: '/admin/payouts' },
{ id: 'export', label: 'Exporter' }, // no href — button row
],
},
]
<AdminSidebar
logo={<span className="font-semibold text-ep-text-heading">ePassLive</span>}
groups={groups}
activeId={active}
onItemClick={(id) => trackNav(id)}
/>Sidebar molecule migration
For responsive or mobile dashboards use the Sidebar molecule: it owns the mobile overlay pattern (portal + scrim + focus trap + Escape + scroll lock — see the molecule's docs) that this desktop-only organism lacks. The storefront dashboard already migrated — the DS-CORR-08-05 precedent; its regression test asserts this organism's attributes are gone from dashboard surfaces. Keep AdminSidebar for desktop-only admin frames composed with AdminLayout.
The API maps directly: groups → groups: SidebarGroup[] (items gain icon, badge, nested children), activeId → activeKey, onItemClick → onItemClick (1:1, same name), and logo / className keep their names.
Two migration caveats: the defaults drift — ariaLabel "Admin primary navigation" → "Primary navigation" and id "admin-sidebar" → "sidebar" — so keeping defaults changes the accessible name and the DOM id; and the mobile drawer is gated on collapsible (default false) — without it the molecule is desktop-sticky-only too. Compose it with collapsible, as AdminLayout's docs direct, for responsive frames.
Accessibility
The sidebar is a named <aside> containing a named <nav>; each group's list is labeled by its heading via aria-labelledby (WCAG 1.3.1). The active row carries aria-current="page" (WCAG 2.4.8) alongside the teal visual accent. Row heights meet the WCAG 2.5.5 target minimum via --ep-tap-min. The empty state (no groups) renders an em-dash in an aria-live="polite" paragraph.
Props
logo*React.ReactNodeBrand slot rendered at the top of the sidebar.
groups*AdminSidebarGroup[]Nav groups — one labeled list per group (see the type tables below).
activeIdstringMarks the matching item as current (aria-current='page' + teal accent).
onItemClick(id: string) => voidClick hook — fires for every item (links navigate AND fire; buttons only fire).
ariaLabelstring"Admin primary navigation"Accessible name for the aside and inner nav.
classNamestringClass on the aside shell.
idstringStable id (defaults to admin-sidebar).
Group definition
id*stringGroup identifier — keys the heading id.
label*stringGroup heading (rendered uppercase, small).
items*AdminSidebarItem[]The group's nav rows.
Item definition
id*stringItem identifier — matches activeId, used as React key.
label*stringRow text.
hrefstringWhen present the row is a real anchor; when absent it is a button type=button.
Props
AdminSidebar
logoReact.ReactNode—Brand slot rendered at the top of the sidebar.
groupsAdminSidebarGroup[]—Nav groups — one labeled list per group (id, label, items).
activeIdstring—Marks the matching item as current (aria-current="page" on link rows + teal accent).
onItemClick(id: string) => void—Click hook — fires for every item (links navigate AND fire; buttons only fire).
ariaLabelstring'Admin primary navigation'Accessible name for the inner <nav> landmark (one labelled landmark).
classNamestring—Extra classes on the <aside> shell.
idstring'admin-sidebar'Stable base id for the shell and group heading ids.
<AdminSidebar />