import type { Meta, StoryObj } from '@storybook/react'; import { Sidebar, useSidebar } from './sidebar'; import { useLayout } from '../../../contexts/LayoutContext'; import { BrowserRouter } from 'react-router-dom'; import { Home, Users, Settings, BarChart, Plus, Clock, FileEdit, ArrowRightLeft, PanelLeft, Menu, Trash2, Map, Archive, ShoppingCart, Package, Tag, Truck, CreditCard, Bell, Shield, Key, Database, Server, Activity, TrendingUp, PieChart, BarChart2, FileText, Folder, MessageSquare, Mail, Phone, Globe, Layers, Zap, } from 'lucide-react'; import React, { useState } from 'react'; import { Badge } from '../../ui/badge'; import { Button } from '../../ui/button'; import { Progress } from '../../ui/progress'; const meta: Meta = { title: 'Layout/Sidebar', component: Sidebar, decorators: [ Story => (
), ], parameters: { layout: 'fullscreen', }, }; export default meta; type Story = StoryObj; export const Default: Story = { args: { variant: 'default', expanded: false, user: { email: 'admin@example.com' }, onLogout: () => console.log('Logged out'), location: { pathname: '/home' }, routes: [ { path: '/home', label: 'Home', icon: Home }, { path: '/dashboard', label: 'Dashboard', icon: Users }, { path: '/settings', label: 'Settings', icon: Settings }, ], footer: { showUser: true, showSettings: true, showLogout: true, }, }, render: args => { const { sidebarWidth: contextWidth } = useLayout(); const [isExpanded, setIsExpanded] = useState(args.expanded); // In Storybook, we match the sidebar widths (280 expanded, 80 collapsed) const currentWidth = isExpanded ? 280 : 80; return (
setIsExpanded(!isExpanded)} width={currentWidth} navigate={() => {}} />

Traditional System Navigation

Click on the menu icon to expand

); }, }; export const DefaultWithGroups: Story = { args: { variant: 'default', expanded: false, user: { name: 'Admin', email: 'admin@example.com' }, onLogout: () => console.log('Logged out'), location: { pathname: '/home' }, navigationGroups: [ { id: 'main', label: 'Principal', items: [ { path: '/home', label: 'Início', icon: Home }, { path: '/dashboard', label: 'Dashboard', icon: BarChart, children: [ { path: '/dashboard/overview', label: 'Visão Geral', icon: BarChart }, { path: '/dashboard/reports', label: 'Relatórios', icon: FileEdit }, { path: '/dashboard/analytics', label: 'Analytics', icon: Map }, ], }, ], }, { id: 'admin', label: 'Administração', items: [ { path: '/users', label: 'Usuários', icon: Users, children: [ { path: '/users/list', label: 'Lista de Usuários', icon: Users }, { path: '/users/roles', label: 'Perfis de Acesso', icon: Settings }, ], }, { path: '/settings', label: 'Configurações', icon: Settings }, ], }, ], footer: { showUser: true, showSettings: false, showLogout: true, }, }, render: args => { const [isExpanded, setIsExpanded] = useState(args.expanded); const currentWidth = isExpanded ? 280 : 80; return (
setIsExpanded(!isExpanded)} width={currentWidth} navigate={() => {}} />

Navigation with Groups and Children

Expand and click the arrow button on items to see sub-menus

); }, }; export const Assistant: Story = { args: { variant: 'assistant', expanded: false, user: { email: 'admin@example.com' }, onLogout: () => console.log('Logged out'), location: { pathname: '/assistant/refatoracao' }, search: { show: true, placeholder: 'Buscar tópicos...', filter: { show: true, content: (
Filter by Status
Active Archived Pending
), }, }, fixedArea: { show: true, icon: Plus, onClick: () => console.log('New Conversation clicked'), content: ( ), }, navigationGroups: [ { id: 'recent', label: 'Recent', icon: Clock, items: [ { path: '/assistant/refatoracao', label: 'Edit Sidebar', description: 'Active now', actions: [ { label: 'Rename', icon: FileEdit, onClick: () => console.log('Open rename...') }, { label: 'Move', icon: ArrowRightLeft, children: [ { label: 'Active Projects', onClick: () => console.log('Moved to Active Projects'), }, { label: 'Monitoring', onClick: () => console.log('Moved to Monitoring') }, { label: 'Archive', onClick: () => console.log('Moved to Archive') }, ], }, { label: 'Clear', icon: Trash2, onClick: () => console.log('History Cleared!'), variant: 'destructive', }, ], }, ], }, { id: 'projects', label: 'Project Monitoring', icon: Map, actions: [ { label: 'New Category', icon: Plus, onClick: () => console.log('Create new category...'), }, { label: 'Archive Group', icon: Archive, onClick: () => console.log('Archiving group...'), }, ], items: [ { path: '/assistant/br163', label: 'Panel Status', icon: () =>
, description: (
Label 67%
), }, ], }, ], }, render: args => { const [isExpanded, setIsExpanded] = useState(args.expanded); const currentWidth = isExpanded ? 280 : 80; return (
setIsExpanded(!isExpanded)} width={currentWidth} navigate={() => {}} />

Assistant Mode Content

Click on the menu icon to expand

); }, }; export const Collapsed: Story = { args: { ...Default.args, expanded: false, }, render: args => { const [isExpanded, setIsExpanded] = useState(args.expanded); const currentWidth = isExpanded ? 280 : 80; return (
setIsExpanded(!isExpanded)} width={currentWidth} navigate={() => {}} />

Compact Navigation

Click on the menu icon to expand

); }, }; export const NoFooter: Story = { args: { ...Default.args, showFooter: false, }, render: args => { const [isExpanded, setIsExpanded] = useState(args.expanded); const currentWidth = isExpanded ? 280 : 80; return (
setIsExpanded(!isExpanded)} width={currentWidth} navigate={() => {}} />

Sidebar without Footer

Click on the menu icon to expand

); }, }; export const Autonomous: Story = { args: { ...Default.args, expanded: undefined, onToggle: undefined, }, render: args => { return (

Autonomous Sidebar

This instance is managing its own state internally. No `expanded` or `onToggle` props are passed.

Note: Layout coordination (content padding) must be handled manually when using autonomous mode without LayoutProvider.

); }, }; /** * Desktop view with sub-items expanded. * Items with `children` show a ChevronRight button; clicking it opens a * dropdown listing the child routes positioned to the right of the sidebar. */ export const WithSubitemsDesktop: Story = { name: 'With Sub-items (Desktop)', args: { variant: 'default', expanded: true, user: { name: 'Admin', email: 'admin@example.com' }, onLogout: () => console.log('Logged out'), location: { pathname: '/dashboard' }, navigationGroups: [ { id: 'main', label: 'Principal', items: [ { path: '/home', label: 'Início', icon: Home }, { path: '/dashboard', label: 'Dashboard', icon: BarChart, children: [ { path: '/dashboard/overview', label: 'Visão Geral', icon: BarChart }, { path: '/dashboard/reports', label: 'Relatórios', icon: FileEdit }, { path: '/dashboard/analytics', label: 'Analytics', icon: Map }, { path: '/dashboard/trends', label: 'Tendências', icon: TrendingUp }, { path: '/dashboard/kpis', label: 'KPIs', icon: Activity }, { path: '/dashboard/charts', label: 'Gráficos', icon: PieChart }, { path: '/dashboard/comparison', label: 'Comparativos', icon: BarChart2 }, ], }, { path: '/reports', label: 'Relatórios', icon: FileText, children: [ { path: '/reports/sales', label: 'Vendas', icon: ShoppingCart }, { path: '/reports/financial', label: 'Financeiro', icon: CreditCard }, { path: '/reports/operational', label: 'Operacional', icon: Layers }, { path: '/reports/exports', label: 'Exportações', icon: FileEdit }, ], }, { path: '/catalog', label: 'Catálogo', icon: Package, children: [ { path: '/catalog/products', label: 'Produtos', icon: Package }, { path: '/catalog/categories', label: 'Categorias', icon: Tag }, { path: '/catalog/inventory', label: 'Estoque', icon: Database }, ], }, ], }, { id: 'commerce', label: 'Comercial', items: [ { path: '/orders', label: 'Pedidos', icon: ShoppingCart, children: [ { path: '/orders/pending', label: 'Pendentes', icon: Clock }, { path: '/orders/processing', label: 'Em Andamento', icon: Zap }, { path: '/orders/shipped', label: 'Enviados', icon: Truck }, { path: '/orders/delivered', label: 'Entregues', icon: Package }, { path: '/orders/cancelled', label: 'Cancelados', icon: Trash2 }, ], }, { path: '/logistics', label: 'Logística', icon: Truck, children: [ { path: '/logistics/carriers', label: 'Transportadoras', icon: Truck }, { path: '/logistics/tracking', label: 'Rastreamento', icon: Map }, { path: '/logistics/warehouses', label: 'Armazéns', icon: Archive }, ], }, { path: '/billing', label: 'Faturamento', icon: CreditCard, children: [ { path: '/billing/invoices', label: 'Faturas', icon: FileText }, { path: '/billing/payments', label: 'Pagamentos', icon: CreditCard }, { path: '/billing/refunds', label: 'Estornos', icon: ArrowRightLeft }, ], }, ], }, { id: 'communication', label: 'Comunicação', items: [ { path: '/notifications', label: 'Notificações', icon: Bell, children: [ { path: '/notifications/email', label: 'E-mail', icon: Mail }, { path: '/notifications/sms', label: 'SMS', icon: Phone }, { path: '/notifications/push', label: 'Push', icon: Bell }, ], }, { path: '/messages', label: 'Mensagens', icon: MessageSquare, children: [ { path: '/messages/inbox', label: 'Caixa de Entrada', icon: Mail }, { path: '/messages/sent', label: 'Enviadas', icon: MessageSquare }, { path: '/messages/templates', label: 'Templates', icon: FileText }, ], }, { path: '/integrations', label: 'Integrações', icon: Globe }, ], }, { id: 'admin', label: 'Administração', items: [ { path: '/users', label: 'Usuários', icon: Users, children: [ { path: '/users/list', label: 'Lista de Usuários', icon: Users }, { path: '/users/roles', label: 'Perfis de Acesso', icon: Shield }, { path: '/users/permissions', label: 'Permissões', icon: Key }, { path: '/users/audit', label: 'Auditoria', icon: Activity }, ], }, { path: '/infrastructure', label: 'Infraestrutura', icon: Server, children: [ { path: '/infrastructure/servers', label: 'Servidores', icon: Server }, { path: '/infrastructure/databases', label: 'Bancos de Dados', icon: Database }, { path: '/infrastructure/backups', label: 'Backups', icon: Archive }, ], }, { path: '/files', label: 'Arquivos', icon: Folder, children: [ { path: '/files/documents', label: 'Documentos', icon: FileText }, { path: '/files/media', label: 'Mídia', icon: Layers }, { path: '/files/exports', label: 'Exportações', icon: FileEdit }, ], }, { path: '/settings', label: 'Configurações', icon: Settings }, ], }, ], footer: { showUser: true, showSettings: false, showLogout: true, }, }, render: args => { const [isExpanded, setIsExpanded] = useState(true); const currentWidth = isExpanded ? 280 : 80; return (
setIsExpanded(!isExpanded)} width={currentWidth} navigate={() => {}} />

Desktop — Subitens

Itens com subitens exibem um botão{' '} ChevronRight ao final da linha. Clique nele para abrir o dropdown de rotas filhas.

Use o botão de toggle para alternar entre expandido e recolhido.

); }, }; /** * Mobile view with sub-items. * Simulates a narrow viewport (375 px) so the sidebar renders in its * full-screen overlay mode. When expanded the sidebar covers the entire * container; clicking a nav item (or the back-arrow toggle) closes it. * * Sub-item dropdowns behave identically to desktop — the ChevronRight * button opens a `DropdownMenuContent` positioned to the right. * This story exists so the mobile layout can be inspected and adjusted. */ export const WithSubitemsMobile: Story = { name: 'With Sub-items (Mobile)', parameters: { viewport: { defaultViewport: 'mobile1' }, }, args: { variant: 'default', expanded: false, user: { name: 'Admin', email: 'admin@example.com' }, onLogout: () => console.log('Logged out'), location: { pathname: '/dashboard' }, navigationGroups: [ { id: 'main', label: 'Principal', items: [ { path: '/home', label: 'Início', icon: Home }, { path: '/dashboard', label: 'Dashboard', icon: BarChart, children: [ { path: '/dashboard/overview', label: 'Visão Geral', icon: BarChart }, { path: '/dashboard/reports', label: 'Relatórios', icon: FileEdit }, { path: '/dashboard/analytics', label: 'Analytics', icon: Map }, { path: '/dashboard/trends', label: 'Tendências', icon: TrendingUp }, { path: '/dashboard/kpis', label: 'KPIs', icon: Activity }, { path: '/dashboard/charts', label: 'Gráficos', icon: PieChart }, { path: '/dashboard/comparison', label: 'Comparativos', icon: BarChart2 }, ], }, { path: '/reports', label: 'Relatórios', icon: FileText, children: [ { path: '/reports/sales', label: 'Vendas', icon: ShoppingCart }, { path: '/reports/financial', label: 'Financeiro', icon: CreditCard }, { path: '/reports/operational', label: 'Operacional', icon: Layers }, { path: '/reports/exports', label: 'Exportações', icon: FileEdit }, ], }, { path: '/catalog', label: 'Catálogo', icon: Package, children: [ { path: '/catalog/products', label: 'Produtos', icon: Package }, { path: '/catalog/categories', label: 'Categorias', icon: Tag }, { path: '/catalog/inventory', label: 'Estoque', icon: Database }, ], }, ], }, { id: 'commerce', label: 'Comercial', items: [ { path: '/orders', label: 'Pedidos', icon: ShoppingCart, children: [ { path: '/orders/pending', label: 'Pendentes', icon: Clock }, { path: '/orders/processing', label: 'Em Andamento', icon: Zap }, { path: '/orders/shipped', label: 'Enviados', icon: Truck }, { path: '/orders/delivered', label: 'Entregues', icon: Package }, { path: '/orders/cancelled', label: 'Cancelados', icon: Trash2 }, ], }, { path: '/logistics', label: 'Logística', icon: Truck, children: [ { path: '/logistics/carriers', label: 'Transportadoras', icon: Truck }, { path: '/logistics/tracking', label: 'Rastreamento', icon: Map }, { path: '/logistics/warehouses', label: 'Armazéns', icon: Archive }, ], }, { path: '/billing', label: 'Faturamento', icon: CreditCard, children: [ { path: '/billing/invoices', label: 'Faturas', icon: FileText }, { path: '/billing/payments', label: 'Pagamentos', icon: CreditCard }, { path: '/billing/refunds', label: 'Estornos', icon: ArrowRightLeft }, ], }, ], }, { id: 'communication', label: 'Comunicação', items: [ { path: '/notifications', label: 'Notificações', icon: Bell, children: [ { path: '/notifications/email', label: 'E-mail', icon: Mail }, { path: '/notifications/sms', label: 'SMS', icon: Phone }, { path: '/notifications/push', label: 'Push', icon: Bell }, ], }, { path: '/messages', label: 'Mensagens', icon: MessageSquare, children: [ { path: '/messages/inbox', label: 'Caixa de Entrada', icon: Mail }, { path: '/messages/sent', label: 'Enviadas', icon: MessageSquare }, { path: '/messages/templates', label: 'Templates', icon: FileText }, ], }, { path: '/integrations', label: 'Integrações', icon: Globe }, ], }, { id: 'admin', label: 'Administração', items: [ { path: '/users', label: 'Usuários', icon: Users, children: [ { path: '/users/list', label: 'Lista de Usuários', icon: Users }, { path: '/users/roles', label: 'Perfis de Acesso', icon: Shield }, { path: '/users/permissions', label: 'Permissões', icon: Key }, { path: '/users/audit', label: 'Auditoria', icon: Activity }, ], }, { path: '/infrastructure', label: 'Infraestrutura', icon: Server, children: [ { path: '/infrastructure/servers', label: 'Servidores', icon: Server }, { path: '/infrastructure/databases', label: 'Bancos de Dados', icon: Database }, { path: '/infrastructure/backups', label: 'Backups', icon: Archive }, ], }, { path: '/files', label: 'Arquivos', icon: Folder, children: [ { path: '/files/documents', label: 'Documentos', icon: FileText }, { path: '/files/media', label: 'Mídia', icon: Layers }, { path: '/files/exports', label: 'Exportações', icon: FileEdit }, ], }, { path: '/settings', label: 'Configurações', icon: Settings }, ], }, ], footer: { showUser: true, showSettings: false, showLogout: true, }, }, render: args => { const [isExpanded, setIsExpanded] = useState(false); return ( /* Constrain to ~375 px to reproduce a mobile viewport inside Storybook */
{/* Page content visible when sidebar is closed */} {!isExpanded && (
Minha Aplicação

Mobile — Subitens

Clique no ícone de menu (☰) no topo para abrir a sidebar em modo tela-cheia. Itens com subitens mostram um{' '} ChevronRight ao final da linha.

)} setIsExpanded(prev => !prev)} width={375} navigate={() => setIsExpanded(false)} />
); }, }; /** * Compound Component API — demonstrates using Sidebar.Root + sub-components * for full layout control. This is the recommended pattern for advanced * customization without forking the component. */ export const CompoundComponentAPI: Story = { name: 'Compound Component API', render: () => { const [isExpanded, setIsExpanded] = React.useState(true); const currentWidth = isExpanded ? 280 : 80; const groups = [ { id: 'main', label: 'Principal', items: [ { path: '/home', label: 'Início', icon: Home }, { path: '/dashboard', label: 'Dashboard', icon: BarChart }, ], }, { id: 'admin', label: 'Administração', items: [ { path: '/users', label: 'Usuários', icon: Users }, { path: '/settings', label: 'Configurações', icon: Settings }, ], }, ]; return (
{/* Compound Component usage */} setIsExpanded(prev => !prev)} width={currentWidth} location={{ pathname: '/home' }} navigate={() => {}} > console.log('logout')} showUser showSettings showLogout />

Compound Component API

Built with <Sidebar.Root> , <Sidebar.Header>,{' '} <Sidebar.Nav>, and{' '} <Sidebar.Footer>.

Each sub-component reads shared state from{' '} SidebarContext via{' '} Sidebar.Root.

); }, }; /** * Headless Hook — demonstrates using `useSidebar` directly for fully custom rendering. * The hook manages all state; the UI is entirely up to the consumer. */ export const HeadlessHook: Story = { name: 'Headless Hook (useSidebar)', render: () => { const groups = [ { id: 'main', label: 'Principal', items: [ { path: '/home', label: 'Início', icon: Home }, { path: '/dashboard', label: 'Dashboard', icon: BarChart }, { path: '/users', label: 'Usuários', icon: Users }, ], }, ]; const { expanded, toggleExpanded, navigationGroups: resolvedGroups, } = useSidebar({ defaultExpanded: true, navigationGroups: groups }); const width = expanded ? 280 : 80; return (
{/* Fully custom sidebar built with useSidebar hook */}

Headless Hook

State managed by useSidebar(). The entire UI is custom — no Sidebar component used.

Sidebar is currently: {expanded ? 'Expanded' : 'Collapsed'}

); }, };