diff --git a/components/ui/fee-estimate-dialog.stories.tsx b/components/ui/fee-estimate-dialog.stories.tsx new file mode 100644 index 0000000..00d8639 --- /dev/null +++ b/components/ui/fee-estimate-dialog.stories.tsx @@ -0,0 +1,205 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { FeeEstimateDialog } from "./fee-estimate-dialog" +import { Button } from "./button" +import React from "react" + +const meta = { + title: "UI/FeeEstimateDialog", + component: FeeEstimateDialog, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta + +export default meta +type Story = StoryObj + +// Normal fee estimate (no warning) +export const NormalFee: Story = { + render: () => { + const [open, setOpen] = React.useState(false) + + return ( + <> + + + + ) + }, +} + +// High fee warning state +export const HighFeeWarning: Story = { + render: () => { + const [open, setOpen] = React.useState(false) + + return ( + <> + + + + ) + }, +} + +// Large transaction with normal fees +export const LargeTransaction: Story = { + render: () => { + const [open, setOpen] = React.useState(false) + + return ( + <> + + + + ) + }, +} + +// Small transaction with high relative fees +export const SmallTransactionHighFeeRatio: Story = { + render: () => { + const [open, setOpen] = React.useState(false) + + return ( + <> + + + + ) + }, +} + +// Very high fees scenario +export const VeryHighFees: Story = { + render: () => { + const [open, setOpen] = React.useState(false) + + return ( + <> + + + + ) + }, +} + +// Minimal fees +export const MinimalFees: Story = { + render: () => { + const [open, setOpen] = React.useState(false) + + return ( + <> + + + + ) + }, +} + +// Comparison: Normal vs High Fee Side by Side (in separate components) +export const StateComparison: Story = { + render: () => { + const [normalOpen, setNormalOpen] = React.useState(false) + const [highOpen, setHighOpen] = React.useState(false) + + return ( +
+
+

Normal Fee

+ + +
+
+

High Fee Warning

+ + +
+
+ ) + }, +} diff --git a/components/ui/input.stories.tsx b/components/ui/input.stories.tsx new file mode 100644 index 0000000..5885afc --- /dev/null +++ b/components/ui/input.stories.tsx @@ -0,0 +1,117 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { Input } from "./input" + +const meta = { + title: "UI/Input", + component: Input, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + disabled: { control: "boolean" }, + type: { control: "select", options: ["text", "email", "password", "number", "file"] }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +// Default input state +export const Default: Story = { + args: { + placeholder: "Enter text...", + type: "text", + }, +} + +// Disabled input +export const Disabled: Story = { + args: { + placeholder: "Disabled input", + disabled: true, + type: "text", + }, +} + +// Invalid state with aria-invalid +export const Invalid: Story = { + args: { + placeholder: "Invalid input", + "aria-invalid": true, + type: "text", + }, +} + +// File input type +export const FileInput: Story = { + args: { + type: "file", + }, +} + +// Email input +export const EmailInput: Story = { + args: { + type: "email", + placeholder: "your@email.com", + }, +} + +// Password input +export const PasswordInput: Story = { + args: { + type: "password", + placeholder: "Enter password...", + }, +} + +// Number input +export const NumberInput: Story = { + args: { + type: "number", + placeholder: "0", + }, +} + +// With value +export const WithValue: Story = { + args: { + type: "text", + value: "Sample input value", + disabled: false, + }, +} + +// Large field +export const LargeField: Story = { + args: { + type: "text", + placeholder: "Large input field", + className: "w-96", + }, +} + +// Comparison of states +export const StateComparison: Story = { + render: () => ( +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ ), +} diff --git a/components/ui/label.stories.tsx b/components/ui/label.stories.tsx new file mode 100644 index 0000000..66e0e58 --- /dev/null +++ b/components/ui/label.stories.tsx @@ -0,0 +1,125 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { Label } from "./label" +import { Input } from "./input" + +const meta = { + title: "UI/Label", + component: Label, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta + +export default meta +type Story = StoryObj + +// Basic label +export const Default: Story = { + args: { + children: "Email Address", + htmlFor: "email-input", + }, +} + +// Label with input (common pattern) +export const WithInput: Story = { + render: () => ( +
+ + +
+ ), +} + +// Disabled state +export const DisabledState: Story = { + render: () => ( +
+ + +
+ ), +} + +// Required field indicator +export const RequiredField: Story = { + render: () => ( +
+ + +
+ ), +} + +// Label with error message +export const WithError: Story = { + render: () => ( +
+ + +

Username already taken

+
+ ), +} + +// Label with helper text +export const WithHelperText: Story = { + render: () => ( +
+ + +

Minimum 8 characters

+
+ ), +} + +// Form field group +export const FormFieldGroup: Story = { + render: () => ( +
+
+ + +
+
+ + +
+
+ + +
+
+ ), +} + +// Label styling variations +export const StylingVariations: Story = { + render: () => ( +
+
+ + +
+
+ + +
+
+ + +
+
+ ), +} diff --git a/components/ui/select.stories.tsx b/components/ui/select.stories.tsx new file mode 100644 index 0000000..3f4dbec --- /dev/null +++ b/components/ui/select.stories.tsx @@ -0,0 +1,220 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { Select } from "./select" +import React from "react" + +const meta = { + title: "UI/Select", + component: Select, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta + +export default meta +type Story = StoryObj + +// Default select with placeholder +export const Default: Story = { + render: () => { + const [value, setValue] = React.useState("") + const items = [ + { value: "option1", label: "Option 1" }, + { value: "option2", label: "Option 2" }, + { value: "option3", label: "Option 3" }, + ] + + return ( +
+ setValue(newValue)} + items={items} + /> +
+ ) + }, +} + +// Disabled select +export const Disabled: Story = { + render: () => { + const items = [ + { value: "option1", label: "Option 1" }, + { value: "option2", label: "Option 2" }, + { value: "option3", label: "Option 3" }, + ] + + return ( +
+ setValue(newValue)} + placeholder="Select from many options" + items={items} + /> +
+ ) + }, +} + +// Token selection (common use case) +export const TokenSelection: Story = { + render: () => { + const [value, setValue] = React.useState("") + const tokens = [ + { value: "usdc", label: "USDC" }, + { value: "usdt", label: "USDT" }, + { value: "eur", label: "EUR" }, + { value: "xlm", label: "XLM" }, + ] + + return ( +
+ + setValue(newValue)} + placeholder="Select recurrence" + items={recurrenceOptions} + /> +
+ ) + }, +} + +// Select with error state +export const WithError: Story = { + render: () => { + const [value, setValue] = React.useState("") + const items = [ + { value: "option1", label: "Option 1" }, + { value: "option2", label: "Option 2" }, + ] + + return ( +
+ + +
+
+ +