From b453dfe1078eaeab7ea30af2feb7e0eb3e2392cf Mon Sep 17 00:00:00 2001 From: Xavi Abad <77491413+xabg2@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:31:07 +0100 Subject: [PATCH 01/15] feat: sidenav component --- src/components/sidenav/Sidenav.tsx | 122 +++++++++++ .../components/sidenav/Sidenav.stories.tsx | 192 ++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 src/components/sidenav/Sidenav.tsx create mode 100644 src/stories/components/sidenav/Sidenav.stories.tsx diff --git a/src/components/sidenav/Sidenav.tsx b/src/components/sidenav/Sidenav.tsx new file mode 100644 index 0000000..6f9d3ed --- /dev/null +++ b/src/components/sidenav/Sidenav.tsx @@ -0,0 +1,122 @@ +import { Icon, IconWeight, DotsNineIcon } from '@phosphor-icons/react'; +import { ReactNode } from 'react'; + +export interface SidenavOption { + id: number; + title: string; + icon: Icon; + iconSize?: number; + weight?: IconWeight; + notifications?: number; + subsection?: boolean; +} + +export interface SidenavHeader { + logo: string; + title: string; +} + +export interface SidenavStorage { + used: string; + total: string; + percentage: number; + onUpgradeClick: () => void; + upgradeLabel: string; +} + +export interface SidenavProps { + header: SidenavHeader; + primaryAction?: ReactNode; + options: SidenavOption[]; + activeOptionId: number; + showSubsections: boolean; + storage?: SidenavStorage; + onOptionClick: (optionId: number, isSubsection: boolean) => void; + onMenuClick: () => void; +} + +export const Sidenav = ({ + header, + primaryAction, + options, + activeOptionId, + showSubsections, + storage, + onOptionClick, + onMenuClick, +}: SidenavProps) => { + return ( +
{header.title}
{option.title}
{option.notifications}
{storage.used}
/
{storage.total}
New message
+ Mail +
+ Inbox +
+ 5 +
+ Sent +
+ Drafts +
+ 2 +
+ Labels +
+ 2.8 GB +
+ / +
+ 4 GB +
{title}
{label}
{notifications}
{usage}
{limit}
- Mail -
- Inbox -
- 5 -
- Sent -
- Drafts -
- 2 -
- Labels -
- 2.8 GB -
- / -
- 4 GB -
+
Inbox
5
Sent
Drafts
2
Labels