From d8145c158ce5bb0a8c53e19bd5b2b7fc22220537 Mon Sep 17 00:00:00 2001 From: Swifty Date: Thu, 10 Oct 2024 18:35:05 +0200 Subject: [PATCH] tool(platform): Add storybooks to aid UI development (#8274) * storybook init * alert stories * Avatar stories * badge stories * button stories * calander stories * stories default * card stories * checkbox stories * formatting * added tailwind config * add collapsible story * added command story * rename use-toast.ts to tsx * added more stories * fix linting issues * added stories for input * added stories for label * Added tests to button story * added multiselect stories * added popover stories * added render stories * scroll area stories * more stories * Added rest of the stories for the default components * fmt * add test runner * added ci * fix tests * fixing ci * remove storybook from ci * removed styling * added new line --- autogpt_platform/frontend/.eslintrc.json | 2 +- autogpt_platform/frontend/.gitignore | 3 + autogpt_platform/frontend/.storybook/main.ts | 18 + .../frontend/.storybook/preview.ts | 15 + autogpt_platform/frontend/README.md | 47 ++ autogpt_platform/frontend/package.json | 19 +- .../src/components/ui/alert.stories.tsx | 90 ++++ .../src/components/ui/avatar.stories.tsx | 60 +++ .../src/components/ui/badge.stories.tsx | 59 +++ .../src/components/ui/button.stories.tsx | 219 +++++++++ .../src/components/ui/calendar.stories.tsx | 70 +++ .../src/components/ui/card.stories.tsx | 100 ++++ .../src/components/ui/checkbox.stories.tsx | 73 +++ .../src/components/ui/collapsible.stories.tsx | 84 ++++ .../src/components/ui/command.stories.tsx | 98 ++++ .../src/components/ui/data-table.stories.tsx | 72 +++ .../src/components/ui/dialog.stories.tsx | 102 ++++ .../components/ui/dropdown-menu.stories.tsx | 102 ++++ .../src/components/ui/form.stories.tsx | 169 +++++++ .../src/components/ui/icons.stories.tsx | 95 ++++ .../src/components/ui/input.stories.tsx | 70 +++ .../src/components/ui/label.stories.tsx | 49 ++ .../src/components/ui/multiselect.stories.tsx | 103 ++++ .../src/components/ui/popover.stories.tsx | 102 ++++ .../src/components/ui/render.stories.tsx | 70 +++ .../src/components/ui/scroll-area.stories.tsx | 126 +++++ .../src/components/ui/select.stories.tsx | 96 ++++ .../src/components/ui/separator.stories.tsx | 91 ++++ .../src/components/ui/sheet.stories.tsx | 83 ++++ .../src/components/ui/switch.stories.tsx | 55 +++ .../src/components/ui/table.stories.tsx | 113 +++++ .../src/components/ui/textarea.stories.tsx | 59 +++ .../src/components/ui/toast.stories.tsx | 100 ++++ .../src/components/ui/toaster.stories.tsx | 125 +++++ .../src/components/ui/tooltip.stories.tsx | 99 ++++ .../src/components/ui/use-toast.stories.tsx | 111 +++++ .../ui/{use-toast.ts => use-toast.tsx} | 0 .../frontend/src/stories/Button.stories.ts | 60 +++ .../frontend/src/stories/Button.tsx | 45 ++ .../frontend/src/stories/Configure.mdx | 451 ++++++++++++++++++ .../frontend/src/stories/Header.stories.ts | 33 ++ .../frontend/src/stories/Header.tsx | 71 +++ .../frontend/src/stories/Page.stories.ts | 32 ++ .../frontend/src/stories/Page.tsx | 91 ++++ .../src/stories/assets/accessibility.png | Bin 0 -> 42336 bytes .../src/stories/assets/accessibility.svg | 1 + .../src/stories/assets/addon-library.png | Bin 0 -> 467366 bytes .../frontend/src/stories/assets/assets.png | Bin 0 -> 3899 bytes .../src/stories/assets/avif-test-image.avif | Bin 0 -> 829 bytes .../frontend/src/stories/assets/context.png | Bin 0 -> 6119 bytes .../frontend/src/stories/assets/discord.svg | 1 + .../frontend/src/stories/assets/docs.png | Bin 0 -> 27875 bytes .../src/stories/assets/figma-plugin.png | Bin 0 -> 44246 bytes .../frontend/src/stories/assets/github.svg | 1 + .../frontend/src/stories/assets/share.png | Bin 0 -> 40767 bytes .../frontend/src/stories/assets/styling.png | Bin 0 -> 7237 bytes .../frontend/src/stories/assets/testing.png | Bin 0 -> 49313 bytes .../frontend/src/stories/assets/theming.png | Bin 0 -> 44374 bytes .../frontend/src/stories/assets/tutorials.svg | 1 + .../frontend/src/stories/assets/youtube.svg | 1 + .../frontend/src/stories/button.css | 30 ++ .../frontend/src/stories/header.css | 32 ++ .../frontend/src/stories/page.css | 69 +++ .../frontend/test-runner-jest.config.js | 16 + autogpt_platform/frontend/tsconfig.json | 8 +- 65 files changed, 3889 insertions(+), 3 deletions(-) create mode 100644 autogpt_platform/frontend/.storybook/main.ts create mode 100644 autogpt_platform/frontend/.storybook/preview.ts create mode 100644 autogpt_platform/frontend/src/components/ui/alert.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/avatar.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/badge.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/button.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/calendar.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/card.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/checkbox.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/collapsible.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/command.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/data-table.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/dialog.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/dropdown-menu.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/form.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/icons.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/input.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/label.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/multiselect.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/popover.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/render.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/scroll-area.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/select.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/separator.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/sheet.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/switch.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/table.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/textarea.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/toast.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/toaster.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/tooltip.stories.tsx create mode 100644 autogpt_platform/frontend/src/components/ui/use-toast.stories.tsx rename autogpt_platform/frontend/src/components/ui/{use-toast.ts => use-toast.tsx} (100%) create mode 100644 autogpt_platform/frontend/src/stories/Button.stories.ts create mode 100644 autogpt_platform/frontend/src/stories/Button.tsx create mode 100644 autogpt_platform/frontend/src/stories/Configure.mdx create mode 100644 autogpt_platform/frontend/src/stories/Header.stories.ts create mode 100644 autogpt_platform/frontend/src/stories/Header.tsx create mode 100644 autogpt_platform/frontend/src/stories/Page.stories.ts create mode 100644 autogpt_platform/frontend/src/stories/Page.tsx create mode 100644 autogpt_platform/frontend/src/stories/assets/accessibility.png create mode 100644 autogpt_platform/frontend/src/stories/assets/accessibility.svg create mode 100644 autogpt_platform/frontend/src/stories/assets/addon-library.png create mode 100644 autogpt_platform/frontend/src/stories/assets/assets.png create mode 100644 autogpt_platform/frontend/src/stories/assets/avif-test-image.avif create mode 100644 autogpt_platform/frontend/src/stories/assets/context.png create mode 100644 autogpt_platform/frontend/src/stories/assets/discord.svg create mode 100644 autogpt_platform/frontend/src/stories/assets/docs.png create mode 100644 autogpt_platform/frontend/src/stories/assets/figma-plugin.png create mode 100644 autogpt_platform/frontend/src/stories/assets/github.svg create mode 100644 autogpt_platform/frontend/src/stories/assets/share.png create mode 100644 autogpt_platform/frontend/src/stories/assets/styling.png create mode 100644 autogpt_platform/frontend/src/stories/assets/testing.png create mode 100644 autogpt_platform/frontend/src/stories/assets/theming.png create mode 100644 autogpt_platform/frontend/src/stories/assets/tutorials.svg create mode 100644 autogpt_platform/frontend/src/stories/assets/youtube.svg create mode 100644 autogpt_platform/frontend/src/stories/button.css create mode 100644 autogpt_platform/frontend/src/stories/header.css create mode 100644 autogpt_platform/frontend/src/stories/page.css create mode 100644 autogpt_platform/frontend/test-runner-jest.config.js diff --git a/autogpt_platform/frontend/.eslintrc.json b/autogpt_platform/frontend/.eslintrc.json index bffb357a7..bb8b1c099 100644 --- a/autogpt_platform/frontend/.eslintrc.json +++ b/autogpt_platform/frontend/.eslintrc.json @@ -1,3 +1,3 @@ { - "extends": "next/core-web-vitals" + "extends": ["next/core-web-vitals", "plugin:storybook/recommended"] } diff --git a/autogpt_platform/frontend/.gitignore b/autogpt_platform/frontend/.gitignore index cfe0cde0b..2611438bd 100644 --- a/autogpt_platform/frontend/.gitignore +++ b/autogpt_platform/frontend/.gitignore @@ -42,3 +42,6 @@ node_modules/ /playwright-report/ /blob-report/ /playwright/.cache/ + +*storybook.log +storybook-static diff --git a/autogpt_platform/frontend/.storybook/main.ts b/autogpt_platform/frontend/.storybook/main.ts new file mode 100644 index 000000000..d6f94d795 --- /dev/null +++ b/autogpt_platform/frontend/.storybook/main.ts @@ -0,0 +1,18 @@ +import type { StorybookConfig } from "@storybook/nextjs"; + +const config: StorybookConfig = { + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + addons: [ + "@storybook/addon-onboarding", + "@storybook/addon-links", + "@storybook/addon-essentials", + "@chromatic-com/storybook", + "@storybook/addon-interactions", + ], + framework: { + name: "@storybook/nextjs", + options: {}, + }, + staticDirs: ["../public"], +}; +export default config; diff --git a/autogpt_platform/frontend/.storybook/preview.ts b/autogpt_platform/frontend/.storybook/preview.ts new file mode 100644 index 000000000..2c5c6ea84 --- /dev/null +++ b/autogpt_platform/frontend/.storybook/preview.ts @@ -0,0 +1,15 @@ +import type { Preview } from "@storybook/react"; +import "../src/app/globals.css"; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/autogpt_platform/frontend/README.md b/autogpt_platform/frontend/README.md index c7fe91a26..a89d28f40 100644 --- a/autogpt_platform/frontend/README.md +++ b/autogpt_platform/frontend/README.md @@ -39,3 +39,50 @@ This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-opti ## Deploy TODO + +## Storybook + +Storybook is a powerful development environment for UI components. It allows you to build UI components in isolation, making it easier to develop, test, and document your components independently from your main application. + +### Purpose in the Development Process + +1. **Component Development**: Develop and test UI components in isolation. +2. **Visual Testing**: Easily spot visual regressions. +3. **Documentation**: Automatically document components and their props. +4. **Collaboration**: Share components with your team or stakeholders for feedback. + +### How to Use Storybook + +1. **Start Storybook**: + Run the following command to start the Storybook development server: + + ```bash + npm run storybook + ``` + + This will start Storybook on port 6006. Open [http://localhost:6006](http://localhost:6006) in your browser to view your component library. + +2. **Build Storybook**: + To build a static version of Storybook for deployment, use: + + ```bash + npm run build-storybook + ``` + +3. **Running Storybook Tests**: + Storybook tests can be run using: + + ```bash + npm run test-storybook + ``` + + For CI environments, use: + + ```bash + npm run test-storybook:ci + ``` + +4. **Writing Stories**: + Create `.stories.tsx` files alongside your components to define different states and variations of your components. + +By integrating Storybook into our development workflow, we can streamline UI development, improve component reusability, and maintain a consistent design system across the project. diff --git a/autogpt_platform/frontend/package.json b/autogpt_platform/frontend/package.json index ab1aaf2f6..c60260631 100644 --- a/autogpt_platform/frontend/package.json +++ b/autogpt_platform/frontend/package.json @@ -12,7 +12,11 @@ "format": "prettier --write .", "test": "playwright test", "test-ui": "playwright test --ui", - "gentests": "playwright codegen http://localhost:3000" + "gentests": "playwright codegen http://localhost:3000", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", + "test-storybook": "test-storybook", + "test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"npm run build-storybook -- --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && npm run test-storybook\"" }, "browserslist": [ "defaults" @@ -65,16 +69,29 @@ "zod": "^3.23.8" }, "devDependencies": { + "@chromatic-com/storybook": "^1.9.0", "@playwright/test": "^1.47.1", + "@storybook/addon-essentials": "^8.3.5", + "@storybook/addon-interactions": "^8.3.5", + "@storybook/addon-links": "^8.3.5", + "@storybook/addon-onboarding": "^8.3.5", + "@storybook/blocks": "^8.3.5", + "@storybook/nextjs": "^8.3.5", + "@storybook/react": "^8.3.5", + "@storybook/test": "^8.3.5", + "@storybook/test-runner": "^0.19.1", "@types/node": "^22.7.3", "@types/react": "^18", "@types/react-dom": "^18", "@types/react-modal": "^3.16.3", + "concurrently": "^9.0.1", "eslint": "^8", "eslint-config-next": "14.2.4", + "eslint-plugin-storybook": "^0.9.0", "postcss": "^8", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.6", + "storybook": "^8.3.5", "tailwindcss": "^3.4.1", "typescript": "^5" } diff --git a/autogpt_platform/frontend/src/components/ui/alert.stories.tsx b/autogpt_platform/frontend/src/components/ui/alert.stories.tsx new file mode 100644 index 000000000..178f10319 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/alert.stories.tsx @@ -0,0 +1,90 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Alert, AlertTitle, AlertDescription } from "./alert"; + +const meta = { + title: "UI/Alert", + component: Alert, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + variant: { + control: "select", + options: ["default", "destructive"], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: ( + <> + Default Alert + + This is a default alert description. + + + ), + }, +}; + +export const Destructive: Story = { + args: { + variant: "destructive", + children: ( + <> + Destructive Alert + + This is a destructive alert description. + + + ), + }, +}; + +export const WithIcon: Story = { + args: { + children: ( + <> + + + + + + Alert with Icon + This alert includes an icon. + + ), + }, +}; + +export const TitleOnly: Story = { + args: { + children: Alert with Title Only, + }, +}; + +export const DescriptionOnly: Story = { + args: { + children: ( + + This is an alert with only a description. + + ), + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/avatar.stories.tsx b/autogpt_platform/frontend/src/components/ui/avatar.stories.tsx new file mode 100644 index 000000000..b03eaba98 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/avatar.stories.tsx @@ -0,0 +1,60 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Avatar, AvatarImage, AvatarFallback } from "./avatar"; + +const meta = { + title: "UI/Avatar", + component: Avatar, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + // Add any specific controls for Avatar props here if needed + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: , + }, +}; + +export const WithFallback: Story = { + args: { + children: ( + <> + + CN + + ), + }, +}; + +export const FallbackOnly: Story = { + args: { + children: JD, + }, +}; + +export const CustomSize: Story = { + args: { + className: "h-16 w-16", + children: , + }, +}; + +export const CustomContent: Story = { + args: { + children: ( + + + 🚀 + + + ), + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/badge.stories.tsx b/autogpt_platform/frontend/src/components/ui/badge.stories.tsx new file mode 100644 index 000000000..f43e01501 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/badge.stories.tsx @@ -0,0 +1,59 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Badge } from "./badge"; + +const meta = { + title: "UI/Badge", + component: Badge, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + variant: { + control: "select", + options: ["default", "secondary", "destructive", "outline"], + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: "Badge", + }, +}; + +export const Secondary: Story = { + args: { + variant: "secondary", + children: "Secondary", + }, +}; + +export const Destructive: Story = { + args: { + variant: "destructive", + children: "Destructive", + }, +}; + +export const Outline: Story = { + args: { + variant: "outline", + children: "Outline", + }, +}; + +export const CustomContent: Story = { + args: { + children: ( + <> + 🚀 + Custom Content + + ), + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/button.stories.tsx b/autogpt_platform/frontend/src/components/ui/button.stories.tsx new file mode 100644 index 000000000..f19d44a5d --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/button.stories.tsx @@ -0,0 +1,219 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Button } from "./button"; +import { userEvent, within, expect } from "@storybook/test"; + +const meta = { + title: "UI/Button", + component: Button, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + variant: { + control: "select", + options: [ + "default", + "destructive", + "outline", + "secondary", + "ghost", + "link", + ], + }, + size: { + control: "select", + options: ["default", "sm", "lg", "primary", "icon"], + }, + disabled: { + control: "boolean", + }, + asChild: { + control: "boolean", + }, + children: { + control: "text", + }, + onClick: { action: "clicked" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: "Button", + }, +}; + +export const Interactive: Story = { + args: { + children: "Interactive Button", + }, + argTypes: { + onClick: { action: "clicked" }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button", { name: /Interactive Button/i }); + await userEvent.click(button); + await expect(button).toHaveFocus(); + }, +}; + +export const Variants: Story = { + render: (args) => ( +
+ + + + + + +
+ ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const buttons = canvas.getAllByRole("button"); + expect(buttons).toHaveLength(6); + for (const button of buttons) { + await userEvent.hover(button); + await expect(button).toHaveAttribute( + "class", + expect.stringContaining("hover:"), + ); + } + }, +}; + +export const Sizes: Story = { + render: (args) => ( +
+ + + + + +
+ ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const buttons = canvas.getAllByRole("button"); + expect(buttons).toHaveLength(5); + const sizes = ["sm", "default", "lg", "primary", "icon"]; + const sizeClasses = [ + "h-8 rounded-md px-3 text-xs", + "h-9 px-4 py-2", + "h-10 rounded-md px-8", + "md:h-14 md:w-44 rounded-2xl h-10 w-28", + "h-9 w-9", + ]; + buttons.forEach((button, index) => { + expect(button).toHaveAttribute( + "class", + expect.stringContaining(sizeClasses[index]), + ); + }); + }, +}; + +export const Disabled: Story = { + args: { + children: "Disabled Button", + disabled: true, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button", { name: /Disabled Button/i }); + await expect(button).toBeDisabled(); + await expect(button).toHaveStyle("pointer-events: none"); + await expect(button).not.toHaveFocus(); + }, +}; + +export const WithIcon: Story = { + args: { + children: ( + <> + + + + Button with Icon + + ), + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button", { name: /Button with Icon/i }); + const icon = button.querySelector("svg"); + await expect(icon).toBeInTheDocument(); + await expect(button).toHaveTextContent("Button with Icon"); + }, +}; + +export const LoadingState: Story = { + args: { + children: "Loading...", + disabled: true, + }, + render: (args) => ( + + ), + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button", { name: /Loading.../i }); + expect(button).toBeDisabled(); + const spinner = button.querySelector("svg"); + await expect(spinner).toHaveClass("animate-spin"); + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/calendar.stories.tsx b/autogpt_platform/frontend/src/components/ui/calendar.stories.tsx new file mode 100644 index 000000000..2414a7448 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/calendar.stories.tsx @@ -0,0 +1,70 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Calendar } from "./calendar"; + +const meta = { + title: "UI/Calendar", + component: Calendar, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + mode: { + control: "select", + options: ["single", "multiple", "range"], + }, + selected: { + control: "date", + }, + showOutsideDays: { + control: "boolean", + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const SingleSelection: Story = { + args: { + mode: "single", + selected: new Date(), + }, +}; + +export const MultipleSelection: Story = { + args: { + mode: "multiple", + selected: [ + new Date(), + new Date(new Date().setDate(new Date().getDate() + 5)), + ], + }, +}; + +export const RangeSelection: Story = { + args: { + mode: "range", + selected: { + from: new Date(), + to: new Date(new Date().setDate(new Date().getDate() + 7)), + }, + }, +}; + +export const HideOutsideDays: Story = { + args: { + showOutsideDays: false, + }, +}; + +export const CustomClassName: Story = { + args: { + className: "border rounded-lg shadow-lg", + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/card.stories.tsx b/autogpt_platform/frontend/src/components/ui/card.stories.tsx new file mode 100644 index 000000000..cc804aad5 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/card.stories.tsx @@ -0,0 +1,100 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +} from "./card"; + +const meta = { + title: "UI/Card", + component: Card, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + // Add any specific controls for Card props here if needed + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: ( + <> + + Card Title + Card Description + + +

Card Content

+
+ +

Card Footer

+
+ + ), + }, +}; + +export const HeaderOnly: Story = { + args: { + children: ( + + Header Only Card + This card has only a header. + + ), + }, +}; + +export const ContentOnly: Story = { + args: { + children: ( + +

This card has only content.

+
+ ), + }, +}; + +export const FooterOnly: Story = { + args: { + children: ( + +

This card has only a footer.

+
+ ), + }, +}; + +export const CustomContent: Story = { + args: { + children: ( + <> + + Custom Content + + +
+ + 🚀 + +
+
+ + +

Footer text

+
+ + ), + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/checkbox.stories.tsx b/autogpt_platform/frontend/src/components/ui/checkbox.stories.tsx new file mode 100644 index 000000000..3310c3f4c --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/checkbox.stories.tsx @@ -0,0 +1,73 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Checkbox } from "./checkbox"; + +const meta = { + title: "UI/Checkbox", + component: Checkbox, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + checked: { + control: "boolean", + }, + disabled: { + control: "boolean", + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const Checked: Story = { + args: { + checked: true, + }, +}; + +export const Unchecked: Story = { + args: { + checked: false, + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + }, +}; + +export const DisabledChecked: Story = { + args: { + disabled: true, + checked: true, + }, +}; + +export const WithLabel: Story = { + args: {}, + render: (args) => ( +
+ + +
+ ), +}; + +export const CustomSize: Story = { + args: { + className: "h-6 w-6", + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/collapsible.stories.tsx b/autogpt_platform/frontend/src/components/ui/collapsible.stories.tsx new file mode 100644 index 000000000..dbd22d10c --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/collapsible.stories.tsx @@ -0,0 +1,84 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { + Collapsible, + CollapsibleTrigger, + CollapsibleContent, +} from "./collapsible"; +import { Button } from "./button"; + +const meta = { + title: "UI/Collapsible", + component: Collapsible, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + + + + +

This is the collapsible content.

+
+
+ ), +}; + +export const OpenByDefault: Story = { + render: () => ( + + + + + +

This collapsible is open by default.

+
+
+ ), +}; + +export const CustomTrigger: Story = { + render: () => ( + + + + + +

Custom trigger example.

+
+
+ ), +}; + +export const NestedContent: Story = { + render: () => ( + + + + + +

Main Content

+

This is the main collapsible content.

+ + + + + +

This is nested collapsible content.

+
+
+
+
+ ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/command.stories.tsx b/autogpt_platform/frontend/src/components/ui/command.stories.tsx new file mode 100644 index 000000000..2d0aec23e --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/command.stories.tsx @@ -0,0 +1,98 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, +} from "./command"; + +const meta = { + title: "UI/Command", + component: Command, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + + + No results found. + + + Calendar + + + Search Emoji + + + Calculator + + + + + Profile + ⌘P + + + Billing + ⌘B + + + Settings + ⌘S + + + + + ), +}; + +export const WithDialog: Story = { + render: () => ( + + + + No results found. + + Calendar + Search Emoji + Calculator + + + + ), +}; + +export const CustomContent: Story = { + render: () => ( + + + + No fruits found. + + + 🍎 Apple + + + 🍌 Banana + + + 🍊 Orange + + + + + ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/data-table.stories.tsx b/autogpt_platform/frontend/src/components/ui/data-table.stories.tsx new file mode 100644 index 000000000..7fb34630a --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/data-table.stories.tsx @@ -0,0 +1,72 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { DataTable } from "./data-table"; +import { Button } from "./button"; + +const meta = { + title: "UI/DataTable", + component: DataTable, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const sampleData = [ + { id: 1, name: "John Doe", age: 30, city: "New York" }, + { id: 2, name: "Jane Smith", age: 25, city: "Los Angeles" }, + { id: 3, name: "Bob Johnson", age: 35, city: "Chicago" }, +]; + +const sampleColumns = [ + { accessorKey: "name", header: "Name" }, + { accessorKey: "age", header: "Age" }, + { accessorKey: "city", header: "City" }, +]; + +export const Default: Story = { + args: { + columns: sampleColumns, + data: sampleData, + filterPlaceholder: "Filter by name...", + filterColumn: "name", + }, +}; + +export const WithGlobalActions: Story = { + args: { + ...Default.args, + globalActions: [ + { + component: , + action: async (rows) => { + console.log("Deleting:", rows); + }, + }, + ], + }, +}; + +export const NoResults: Story = { + args: { + ...Default.args, + data: [], + }, +}; + +export const CustomFilterPlaceholder: Story = { + args: { + ...Default.args, + filterPlaceholder: "Search for a user...", + }, +}; + +export const WithoutFilter: Story = { + args: { + columns: sampleColumns, + data: sampleData, + filterPlaceholder: "", + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/dialog.stories.tsx b/autogpt_platform/frontend/src/components/ui/dialog.stories.tsx new file mode 100644 index 000000000..dcbfe943e --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/dialog.stories.tsx @@ -0,0 +1,102 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Button } from "./button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "./dialog"; + +const meta = { + title: "UI/Dialog", + component: Dialog, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + + + + + + Are you sure? + + This action cannot be undone. This will permanently delete your + account and remove your data from our servers. + + + + + + + + ), +}; + +export const WithForm: Story = { + render: () => ( + + + + + + + Edit profile + + Make changes to your profile here. Click save when you're done. + + +
+
+ + +
+
+ + +
+
+ + + +
+
+ ), +}; + +export const CustomContent: Story = { + render: () => ( + + + + + + + Custom Content + +
+ 🎉 +

This is a custom dialog content!

+
+ + + +
+
+ ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/dropdown-menu.stories.tsx b/autogpt_platform/frontend/src/components/ui/dropdown-menu.stories.tsx new file mode 100644 index 000000000..71130935a --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/dropdown-menu.stories.tsx @@ -0,0 +1,102 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Button } from "./button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "./dropdown-menu"; + +const meta = { + title: "UI/DropdownMenu", + component: DropdownMenu, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + + + + + My Account + + Profile + Billing + Team + Subscription + + + ), +}; + +export const WithDisabledItem: Story = { + render: () => ( + + + + + + Profile + Billing + Team (disabled) + Subscription + + + ), +}; + +export const WithIcons: Story = { + render: () => ( + + + + + + + 👤 Profile + + + 💳 Billing + + + 👥 Team + + + 📅 Subscription + + + + ), +}; + +export const NestedDropdowns: Story = { + render: () => ( + + + + + + Profile + Billing + + Team + + Add Member + Remove Member + + + Subscription + + + ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/form.stories.tsx b/autogpt_platform/frontend/src/components/ui/form.stories.tsx new file mode 100644 index 000000000..e10797b82 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/form.stories.tsx @@ -0,0 +1,169 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; + +import { + Form, + FormField, + FormItem, + FormLabel, + FormControl, + FormDescription, + FormMessage, +} from "./form"; +import { Input } from "./input"; +import { Button } from "./button"; + +const formSchema = z.object({ + username: z.string().min(2, { + message: "Username must be at least 2 characters.", + }), +}); + +const meta = { + title: "UI/Form", + component: Form, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const FormExample = () => { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: "", + }, + }); + + function onSubmit(values: z.infer) { + console.log(values); + } + + return ( +
+ + ( + + Username + + + + + This is your public display name. + + + + )} + /> + + + + ); +}; + +export const Default: Story = { + render: () => , +}; + +export const WithError: Story = { + render: () => { + const FormWithError = () => { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: "", + }, + }); + + React.useEffect(() => { + form.setError("username", { + type: "manual", + message: "This username is already taken.", + }); + }, [form]); + + function onSubmit(values: z.infer) { + console.log(values); + } + + return ( +
+ + ( + + Username + + + + + This is your public display name. + + + + )} + /> + + + + ); + }; + + return ; + }, +}; + +export const WithDefaultValue: Story = { + render: () => { + const FormWithDefaultValue = () => { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: "johndoe", + }, + }); + + function onSubmit(values: z.infer) { + console.log(values); + } + + return ( +
+ + ( + + Username + + + + + This is your public display name. + + + + )} + /> + + + + ); + }; + + return ; + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/icons.stories.tsx b/autogpt_platform/frontend/src/components/ui/icons.stories.tsx new file mode 100644 index 000000000..cadc91bdb --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/icons.stories.tsx @@ -0,0 +1,95 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { + IconUser, + IconUserPlus, + IconKey, + IconKeyPlus, + IconWorkFlow, + IconPlay, + IconSquare, + IconSquareActivity, + IconRefresh, + IconSave, + IconUndo2, + IconRedo2, + IconToyBrick, + IconCircleAlert, + IconCircleUser, + IconPackage2, + IconMegaphone, + IconMenu, + IconCoin, +} from "./icons"; + +const meta = { + title: "UI/Icons", + component: IconUser, // Add a component property + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + size: { + control: "select", + options: ["default", "sm", "lg"], + }, + className: { control: "text" }, + }, +} satisfies Meta; // Specify the type parameter + +export default meta; +type Story = StoryObj; + +const IconWrapper = ({ children }: { children: React.ReactNode }) => ( +
{children}
+); + +export const AllIcons: Story = { + render: (args) => ( + + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const DefaultSize: Story = { + args: { + size: "default", + }, +}; + +export const SmallSize: Story = { + args: { + size: "sm", + }, +}; + +export const LargeSize: Story = { + args: { + size: "lg", + }, +}; + +export const CustomColor: Story = { + args: { + className: "text-blue-500", + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/input.stories.tsx b/autogpt_platform/frontend/src/components/ui/input.stories.tsx new file mode 100644 index 000000000..fdfc58928 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/input.stories.tsx @@ -0,0 +1,70 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Input } from "./input"; + +const meta = { + title: "UI/Input", + component: Input, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + type: { + control: "select", + options: ["text", "password", "email", "number", "file"], + }, + placeholder: { control: "text" }, + disabled: { control: "boolean" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + placeholder: "Enter text...", + }, +}; + +export const Password: Story = { + args: { + type: "password", + placeholder: "Enter password...", + }, +}; + +export const Email: Story = { + args: { + type: "email", + placeholder: "Enter email...", + }, +}; + +export const Number: Story = { + args: { + type: "number", + placeholder: "Enter number...", + }, +}; + +export const File: Story = { + args: { + type: "file", + }, +}; + +export const Disabled: Story = { + args: { + placeholder: "Disabled input", + disabled: true, + }, +}; + +export const WithCustomClassName: Story = { + args: { + placeholder: "Custom class", + className: "border-2 border-blue-500", + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/label.stories.tsx b/autogpt_platform/frontend/src/components/ui/label.stories.tsx new file mode 100644 index 000000000..2d18f60dc --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/label.stories.tsx @@ -0,0 +1,49 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Label } from "./label"; + +const meta = { + title: "UI/Label", + component: Label, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + htmlFor: { control: "text" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: "Default Label", + }, +}; + +export const WithHtmlFor: Story = { + args: { + htmlFor: "example-input", + children: "Label with htmlFor", + }, +}; + +export const CustomContent: Story = { + args: { + children: ( + <> + 📝 + Custom Label Content + + ), + }, +}; + +export const WithClassName: Story = { + args: { + className: "text-blue-500 font-bold", + children: "Styled Label", + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/multiselect.stories.tsx b/autogpt_platform/frontend/src/components/ui/multiselect.stories.tsx new file mode 100644 index 000000000..2fe16feef --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/multiselect.stories.tsx @@ -0,0 +1,103 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { + MultiSelector, + MultiSelectorTrigger, + MultiSelectorInput, + MultiSelectorContent, + MultiSelectorList, + MultiSelectorItem, +} from "./multiselect"; + +const meta = { + title: "UI/MultiSelector", + component: MultiSelector, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + loop: { + control: "boolean", + }, + values: { + control: "object", + }, + onValuesChange: { action: "onValuesChange" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const MultiSelectorExample = (args: any) => { + const [values, setValues] = React.useState(args.values || []); + + return ( + + + + + + + Apple + Banana + Cherry + Date + Elderberry + + + + ); +}; + +export const Default: Story = { + render: (args) => , + args: { + values: [], + onValuesChange: (value: string[]) => {}, + }, +}; + +export const WithLoop: Story = { + render: (args) => , + args: { + values: [], + onValuesChange: (value: string[]) => {}, + loop: true, + }, +}; + +export const WithInitialValues: Story = { + render: (args) => , + args: { + values: ["apple", "banana"], + onValuesChange: (value: string[]) => {}, + }, +}; + +export const WithDisabledItem: Story = { + render: (args) => ( + + + + + + + Apple + Banana + + Cherry (Disabled) + + Date + Elderberry + + + + ), + args: { + values: [], + onValuesChange: (value: string[]) => {}, + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/popover.stories.tsx b/autogpt_platform/frontend/src/components/ui/popover.stories.tsx new file mode 100644 index 000000000..901e55149 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/popover.stories.tsx @@ -0,0 +1,102 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { Popover, PopoverTrigger, PopoverContent } from "./popover"; +import { Button } from "./button"; + +const meta = { + title: "UI/Popover", + component: Popover, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const PopoverExample = (args: any) => ( + + + + + +
+

Popover Content

+

This is the content of the popover.

+
+
+
+); + +export const Default: Story = { + render: () => , +}; + +export const AlignStart: Story = { + render: () => ( + + + + + +
+

Popover Content

+

This is the content of the popover.

+
+
+
+ ), +}; + +export const AlignEnd: Story = { + render: () => ( + + + + + +
+

Popover Content

+

This is the content of the popover.

+
+
+
+ ), +}; + +export const CustomOffset: Story = { + render: () => ( + + + + + +
+

Popover Content

+

This is the content of the popover.

+
+
+
+ ), +}; + +export const CustomContent: Story = { + render: () => ( + + + + + +
+

Custom Content

+

This popover has custom content.

+ +
+
+
+ ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/render.stories.tsx b/autogpt_platform/frontend/src/components/ui/render.stories.tsx new file mode 100644 index 000000000..a453f7912 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/render.stories.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { ContentRenderer } from "./render"; + +const meta = { + title: "UI/ContentRenderer", + component: ContentRenderer, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + value: { control: "text" }, + truncateLongData: { control: "boolean" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Text: Story = { + args: { + value: "This is a simple text content.", + }, +}; + +export const LongText: Story = { + args: { + value: + "This is a very long text that will be truncated when the truncateLongData prop is set to true. It contains more than 100 characters to demonstrate the truncation feature.", + truncateLongData: true, + }, +}; + +export const Image: Story = { + args: { + value: "https://example.com/image.jpg", + }, +}; + +export const Video: Story = { + args: { + value: "https://example.com/video.mp4", + }, +}; + +export const YouTubeVideo: Story = { + args: { + value: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + }, +}; + +export const JsonObject: Story = { + args: { + value: { key: "value", nested: { array: [1, 2, 3] } }, + }, +}; + +export const TruncatedJsonObject: Story = { + args: { + value: { + key: "value", + nested: { array: [1, 2, 3] }, + longText: + "This is a very long text that will be truncated when rendered as part of the JSON object.", + }, + truncateLongData: true, + }, +}; diff --git a/autogpt_platform/frontend/src/components/ui/scroll-area.stories.tsx b/autogpt_platform/frontend/src/components/ui/scroll-area.stories.tsx new file mode 100644 index 000000000..0e2ee1bfb --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/scroll-area.stories.tsx @@ -0,0 +1,126 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { ScrollArea } from "./scroll-area"; + +const meta = { + title: "UI/ScrollArea", + component: ScrollArea, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + className: { control: "text" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + className: "h-[200px] w-[350px] rounded-md border p-4", + children: ( +
+

This is a scrollable area with some content.

+ {Array(20) + .fill(0) + .map((_, i) => ( +
+ Item {i + 1} +
+ ))} +
+ ), + }, +}; + +export const HorizontalScroll: Story = { + args: { + className: "h-[100px] w-[350px] rounded-md border", + children: ( +
+ {Array(20) + .fill(0) + .map((_, i) => ( +
+ {i + 1} +
+ ))} +
+ ), + }, +}; + +export const NestedScrollAreas: Story = { + args: { + className: "h-[300px] w-[350px] rounded-md border p-4", + children: ( +
+

+ Outer Scroll Area +

+ {Array(3) + .fill(0) + .map((_, i) => ( +
+

Section {i + 1}

+ +
+
+ Inner Scroll Area +
+ {Array(10) + .fill(0) + .map((_, j) => ( +
+ Nested Item {j + 1} +
+ ))} +
+
+
+ ))} +
+ ), + }, +}; + +export const CustomScrollbarColors: Story = { + args: { + className: "h-[200px] w-[350px] rounded-md border p-4", + children: ( +
+

Customized scrollbar colors.

+ {Array(20) + .fill(0) + .map((_, i) => ( +
+ Item {i + 1} +
+ ))} +
+ ), + }, + parameters: { + backgrounds: { default: "dark" }, + }, + decorators: [ + (Story) => ( +
+ + +
+ ), + ], +}; diff --git a/autogpt_platform/frontend/src/components/ui/select.stories.tsx b/autogpt_platform/frontend/src/components/ui/select.stories.tsx new file mode 100644 index 000000000..0c6995779 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/select.stories.tsx @@ -0,0 +1,96 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectItem, +} from "./select"; + +const meta = { + title: "UI/Select", + component: Select, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + disabled: { + control: "boolean", + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const SelectExample = (args: any) => ( + +); + +export const Default: Story = { + render: (args) => , +}; + +export const Disabled: Story = { + render: (args) => , + args: { + disabled: true, + }, +}; + +export const WithPlaceholder: Story = { + render: (args) => ( + + ), +}; + +export const WithDefaultValue: Story = { + render: (args) => ( + + ), +}; + +export const CustomTriggerWidth: Story = { + render: (args) => ( + + ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/separator.stories.tsx b/autogpt_platform/frontend/src/components/ui/separator.stories.tsx new file mode 100644 index 000000000..2fb3a5f8f --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/separator.stories.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { Separator } from "./separator"; + +const meta = { + title: "UI/Separator", + component: Separator, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + orientation: { + control: "select", + options: ["horizontal", "vertical"], + }, + className: { control: "text" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const Horizontal: Story = { + args: { + orientation: "horizontal", + }, + decorators: [ + (Story) => ( +
+
Above
+ +
Below
+
+ ), + ], +}; + +export const Vertical: Story = { + args: { + orientation: "vertical", + }, + decorators: [ + (Story) => ( +
+
Left
+ +
Right
+
+ ), + ], +}; + +export const CustomStyle: Story = { + args: { + className: "bg-red-500", + }, + decorators: [ + (Story) => ( +
+
Above
+ +
Below
+
+ ), + ], +}; + +export const WithContent: Story = { + render: (args) => ( +
+

Radix Primitives

+

+ An open-source UI component library. +

+ +
+
Blog
+ +
Docs
+ +
Source
+
+
+ ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/sheet.stories.tsx b/autogpt_platform/frontend/src/components/ui/sheet.stories.tsx new file mode 100644 index 000000000..aa78d7336 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/sheet.stories.tsx @@ -0,0 +1,83 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { + Sheet, + SheetTrigger, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} from "./sheet"; +import { Button } from "./button"; + +const meta = { + title: "UI/Sheet", + component: Sheet, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const SheetDemo = ({ side }: { side: "top" | "right" | "bottom" | "left" }) => ( + + + + + + + Sheet Title + + This is a description of the sheet content. + + +
Sheet content goes here.
+ + + +
+
+); + +export const Default: Story = { + render: () => , +}; + +export const Left: Story = { + render: () => , +}; + +export const Top: Story = { + render: () => , +}; + +export const Bottom: Story = { + render: () => , +}; + +export const CustomContent: Story = { + render: () => ( + + + + + + + Custom Sheet + +
+

This sheet has custom content.

+
    +
  • Item 1
  • +
  • Item 2
  • +
  • Item 3
  • +
+
+
+
+ ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/switch.stories.tsx b/autogpt_platform/frontend/src/components/ui/switch.stories.tsx new file mode 100644 index 000000000..20f1b48ea --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/switch.stories.tsx @@ -0,0 +1,55 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Switch } from "./switch"; + +const meta = { + title: "UI/Switch", + component: Switch, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + checked: { control: "boolean" }, + disabled: { control: "boolean" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const Checked: Story = { + args: { + checked: true, + }, +}; + +export const Disabled: Story = { + args: { + disabled: true, + }, +}; + +export const CheckedAndDisabled: Story = { + args: { + checked: true, + disabled: true, + }, +}; + +export const WithLabel: Story = { + render: (args) => ( +
+ + +
+ ), +}; + +export const CustomSized: Story = { + render: (args) => , +}; diff --git a/autogpt_platform/frontend/src/components/ui/table.stories.tsx b/autogpt_platform/frontend/src/components/ui/table.stories.tsx new file mode 100644 index 000000000..9a51b5a97 --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/table.stories.tsx @@ -0,0 +1,113 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} from "./table"; + +const meta = { + title: "UI/Table", + component: Table, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + A list of your recent invoices. + + + Invoice + Status + Method + Amount + + + + + INV001 + Paid + Credit Card + $250.00 + + + INV002 + Pending + PayPal + $150.00 + + + + + Total + $400.00 + + +
+ ), +}; + +export const WithoutFooter: Story = { + render: () => ( + + + + Name + Email + Role + + + + + Alice Johnson + alice@example.com + Admin + + + Bob Smith + bob@example.com + User + + +
+ ), +}; + +export const WithCustomStyles: Story = { + render: () => ( + + + + + Column 1 + + + Column 2 + + + + + + Value 1 + Value 2 + + + Value 3 + Value 4 + + +
+ ), +}; diff --git a/autogpt_platform/frontend/src/components/ui/textarea.stories.tsx b/autogpt_platform/frontend/src/components/ui/textarea.stories.tsx new file mode 100644 index 000000000..a2a3c28da --- /dev/null +++ b/autogpt_platform/frontend/src/components/ui/textarea.stories.tsx @@ -0,0 +1,59 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Textarea } from "./textarea"; + +const meta = { + title: "UI/Textarea", + component: Textarea, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + placeholder: { control: "text" }, + disabled: { control: "boolean" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + placeholder: "Type your message here.", + }, +}; + +export const Disabled: Story = { + args: { + placeholder: "This textarea is disabled", + disabled: true, + }, +}; + +export const WithValue: Story = { + args: { + value: "This is some pre-filled text in the textarea.", + }, +}; + +export const CustomSized: Story = { + args: { + placeholder: "Custom sized textarea", + className: "w-[300px] h-[150px]", + }, +}; + +export const WithLabel: Story = { + render: (args) => ( +
+ +