diff --git a/apps/web/src/components/ui/Collapsible.stories.tsx b/apps/web/src/components/ui/Collapsible.stories.tsx new file mode 100644 index 000000000..e738fe0d2 --- /dev/null +++ b/apps/web/src/components/ui/Collapsible.stories.tsx @@ -0,0 +1,44 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Collapsible, CollapsibleCard } from './collapsible'; +import { Button } from './button'; +import { Settings } from 'lucide-react'; + +const meta = { + title: 'UI/Collapsible', + component: Collapsible, + tags: ['autodocs'], + argTypes: { + defaultOpen: { control: 'boolean' }, + showChevron: { control: 'boolean' }, + open: { control: 'boolean' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + trigger:

Click to expand

, + children:
Content hidden by default
, + }, +}; + +export const CollapsibleCardExample: Story = { + render: () => ( + } + className="w-[400px]" + > +
+
+ Configure advanced options for your project. +
+
+ +
+
+
+ ), +}; diff --git a/apps/web/src/components/ui/Label.stories.tsx b/apps/web/src/components/ui/Label.stories.tsx new file mode 100644 index 000000000..f8b839903 --- /dev/null +++ b/apps/web/src/components/ui/Label.stories.tsx @@ -0,0 +1,29 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Label } from './label'; +import { Input } from './input'; + +const meta = { + title: 'UI/Label', + component: Label, + tags: ['autodocs'], + argTypes: { + children: { control: 'text' }, + }, + args: { + children: 'Email Address', + } +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithInput: Story = { + render: (args) => ( +
+
+ ), +}; diff --git a/apps/web/src/components/ui/ScrollArea.stories.tsx b/apps/web/src/components/ui/ScrollArea.stories.tsx new file mode 100644 index 000000000..da2b31129 --- /dev/null +++ b/apps/web/src/components/ui/ScrollArea.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ScrollArea } from './scroll-area'; + +const meta = { + title: 'UI/ScrollArea', + component: ScrollArea, + tags: ['autodocs'], + args: { + className: 'h-[200px] w-[350px] rounded-md border p-4', + } +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: (args) => ( + +

Tags

+ {Array.from({ length: 50 }).map((_, i, a) => ( +
+
+ Tag {a.length - i} +
+ {i !== a.length - 1 &&
} +
+ ))} + + ), +}; diff --git a/apps/web/src/components/ui/Sidebar.stories.tsx b/apps/web/src/components/ui/Sidebar.stories.tsx new file mode 100644 index 000000000..ae7c1bf96 --- /dev/null +++ b/apps/web/src/components/ui/Sidebar.stories.tsx @@ -0,0 +1,74 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Sidebar } from './Sidebar'; +import { Filter, User } from 'lucide-react'; + +const meta = { + title: 'UI/Sidebar', + component: Sidebar, + tags: ['autodocs'], + argTypes: { + position: { + control: 'radio', + options: ['left', 'right'], + }, + width: { control: 'text' }, + collapsible: { control: 'boolean' }, + }, + parameters: { + layout: 'fullscreen', + }, + decorators: [ + (Story) => ( +
+ +
+

Main Content

+

+ The sidebar sits alongside this content. +

+
+
+ ), + ], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const LeftSidebar: Story = { + args: { + title: 'Filters', + icon: , + position: 'left', + children: ( +
+
+
+
+
+ ), + }, +}; + +export const RightSidebar: Story = { + args: { + title: 'User Profile', + icon: , + position: 'right', + children: ( +
+
User details go here
+
+ ), + }, + decorators: [ + (Story) => ( +
+
+

Main Content

+
+ +
+ ), + ], +}; diff --git a/apps/web/src/components/ui/Skeleton.stories.tsx b/apps/web/src/components/ui/Skeleton.stories.tsx new file mode 100644 index 000000000..048106811 --- /dev/null +++ b/apps/web/src/components/ui/Skeleton.stories.tsx @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Skeleton } from './skeleton'; + +const meta = { + title: 'UI/Skeleton', + component: Skeleton, + tags: ['autodocs'], + argTypes: { + variant: { + control: 'select', + options: ['text', 'circular', 'rectangular'], + }, + width: { control: 'text' }, + height: { control: 'text' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + width: 200, + height: 20, + }, +}; + +export const CardLoading: Story = { + render: () => ( +
+ +
+ + +
+
+ ), +}; + +export const AvatarLoading: Story = { + render: () => ( +
+ +
+ + +
+
+ ), +}; + +export const Circular: Story = { + args: { + variant: 'circular', + width: 50, + height: 50, + }, +}; diff --git a/apps/web/src/components/ui/Toast.stories.tsx b/apps/web/src/components/ui/Toast.stories.tsx new file mode 100644 index 000000000..e58dfa957 --- /dev/null +++ b/apps/web/src/components/ui/Toast.stories.tsx @@ -0,0 +1,76 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { Toast } from './Toast'; +import { useState } from 'react'; +import { Button } from './button'; + +const meta = { + title: 'UI/Toast', + component: Toast, + tags: ['autodocs'], + argTypes: { + type: { + control: 'select', + options: ['success', 'error', 'info'], + }, + message: { control: 'text' }, + }, + args: { + id: '1', + type: 'info', + message: 'This is a toast message', + onClose: () => console.log('Close clicked'), + } +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const Success: Story = { + args: { + type: 'success', + message: 'Operation completed successfully!', + }, +}; + +export const Error: Story = { + args: { + type: 'error', + message: 'Something went wrong. Please try again.', + }, +}; + +export const ToastDemo: Story = { + render: () => { + const [toasts, setToasts] = useState>([]); + + const addToast = (type: 'success' | 'error' | 'info') => { + const id = Math.random().toString(36).substr(2, 9); + setToasts([...toasts, { id, type, message: `New ${type} toast message` }]); + }; + + const removeToast = (id: string) => { + setToasts(toasts.filter((t) => t.id !== id)); + }; + + return ( +
+
+ + + +
+
+ {toasts.map((toast) => ( + + ))} +
+
+ ); + }, +};