mirror of
https://github.com/langgenius/dify.git
synced 2025-12-04 23:08:35 +00:00
422 lines
13 KiB
TypeScript
422 lines
13 KiB
TypeScript
import type { Meta, StoryObj } from '@storybook/nextjs'
|
||
import { useState } from 'react'
|
||
import Radio from '.'
|
||
|
||
const meta = {
|
||
title: 'Base/Data Entry/Radio',
|
||
component: Radio,
|
||
parameters: {
|
||
layout: 'centered',
|
||
docs: {
|
||
description: {
|
||
component: 'Radio component for single selection. Usually used with Radio.Group for multiple options.',
|
||
},
|
||
},
|
||
},
|
||
tags: ['autodocs'],
|
||
argTypes: {
|
||
checked: {
|
||
control: 'boolean',
|
||
description: 'Checked state (for standalone radio)',
|
||
},
|
||
value: {
|
||
control: 'text',
|
||
description: 'Value of the radio option',
|
||
},
|
||
disabled: {
|
||
control: 'boolean',
|
||
description: 'Disabled state',
|
||
},
|
||
children: {
|
||
control: 'text',
|
||
description: 'Label content',
|
||
},
|
||
},
|
||
} satisfies Meta<typeof Radio>
|
||
|
||
export default meta
|
||
type Story = StoryObj<typeof meta>
|
||
|
||
// Single radio demo
|
||
const SingleRadioDemo = (args: any) => {
|
||
const [checked, setChecked] = useState(args.checked || false)
|
||
|
||
return (
|
||
<div style={{ width: '300px' }}>
|
||
<Radio
|
||
{...args}
|
||
checked={checked}
|
||
onChange={() => setChecked(!checked)}
|
||
>
|
||
{args.children || 'Radio option'}
|
||
</Radio>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
// Default single radio
|
||
export const Default: Story = {
|
||
render: args => <SingleRadioDemo {...args} />,
|
||
args: {
|
||
checked: false,
|
||
disabled: false,
|
||
children: 'Single radio option',
|
||
},
|
||
}
|
||
|
||
// Checked state
|
||
export const Checked: Story = {
|
||
render: args => <SingleRadioDemo {...args} />,
|
||
args: {
|
||
checked: true,
|
||
disabled: false,
|
||
children: 'Selected option',
|
||
},
|
||
}
|
||
|
||
// Disabled state
|
||
export const Disabled: Story = {
|
||
render: args => <SingleRadioDemo {...args} />,
|
||
args: {
|
||
checked: false,
|
||
disabled: true,
|
||
children: 'Disabled option',
|
||
},
|
||
}
|
||
|
||
// Disabled and checked
|
||
export const DisabledChecked: Story = {
|
||
render: args => <SingleRadioDemo {...args} />,
|
||
args: {
|
||
checked: true,
|
||
disabled: true,
|
||
children: 'Disabled selected option',
|
||
},
|
||
}
|
||
|
||
// Radio Group - Basic
|
||
const RadioGroupDemo = () => {
|
||
const [value, setValue] = useState('option1')
|
||
|
||
return (
|
||
<div style={{ width: '400px' }}>
|
||
<Radio.Group value={value} onChange={setValue}>
|
||
<Radio value="option1">Option 1</Radio>
|
||
<Radio value="option2">Option 2</Radio>
|
||
<Radio value="option3">Option 3</Radio>
|
||
</Radio.Group>
|
||
<div className="mt-4 text-sm text-gray-600">
|
||
Selected: <span className="font-semibold">{value}</span>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const RadioGroup: Story = {
|
||
render: () => <RadioGroupDemo />,
|
||
}
|
||
|
||
// Radio Group - With descriptions
|
||
const RadioGroupWithDescriptionsDemo = () => {
|
||
const [value, setValue] = useState('basic')
|
||
|
||
return (
|
||
<div style={{ width: '500px' }}>
|
||
<h3 className="mb-3 text-sm font-medium text-gray-700">Select a plan</h3>
|
||
<Radio.Group value={value} onChange={setValue}>
|
||
<Radio value="basic">
|
||
<div>
|
||
<div className="font-medium">Basic Plan</div>
|
||
<div className="text-xs text-gray-500">Free forever - Perfect for personal use</div>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="pro">
|
||
<div>
|
||
<div className="font-medium">Pro Plan</div>
|
||
<div className="text-xs text-gray-500">$19/month - Advanced features for professionals</div>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="enterprise">
|
||
<div>
|
||
<div className="font-medium">Enterprise Plan</div>
|
||
<div className="text-xs text-gray-500">Custom pricing - Full features and support</div>
|
||
</div>
|
||
</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const RadioGroupWithDescriptions: Story = {
|
||
render: () => <RadioGroupWithDescriptionsDemo />,
|
||
}
|
||
|
||
// Radio Group - With disabled option
|
||
const RadioGroupWithDisabledDemo = () => {
|
||
const [value, setValue] = useState('available')
|
||
|
||
return (
|
||
<div style={{ width: '400px' }}>
|
||
<Radio.Group value={value} onChange={setValue}>
|
||
<Radio value="available">Available option</Radio>
|
||
<Radio value="disabled" disabled>Disabled option</Radio>
|
||
<Radio value="another">Another available option</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const RadioGroupWithDisabled: Story = {
|
||
render: () => <RadioGroupWithDisabledDemo />,
|
||
}
|
||
|
||
// Radio Group - Vertical layout
|
||
const VerticalLayoutDemo = () => {
|
||
const [value, setValue] = useState('email')
|
||
|
||
return (
|
||
<div style={{ width: '400px' }}>
|
||
<h3 className="mb-3 text-sm font-medium text-gray-700">Notification preferences</h3>
|
||
<Radio.Group value={value} onChange={setValue} className="flex-col gap-2">
|
||
<Radio value="email">Email notifications</Radio>
|
||
<Radio value="sms">SMS notifications</Radio>
|
||
<Radio value="push">Push notifications</Radio>
|
||
<Radio value="none">No notifications</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const VerticalLayout: Story = {
|
||
render: () => <VerticalLayoutDemo />,
|
||
}
|
||
|
||
// Real-world example - Settings panel
|
||
const SettingsPanelDemo = () => {
|
||
const [theme, setTheme] = useState('light')
|
||
const [language, setLanguage] = useState('en')
|
||
|
||
return (
|
||
<div style={{ width: '500px' }} className="rounded-lg border border-gray-200 bg-white p-6">
|
||
<h3 className="mb-6 text-lg font-semibold">Application Settings</h3>
|
||
|
||
<div className="space-y-6">
|
||
<div>
|
||
<h4 className="mb-3 text-sm font-medium text-gray-700">Theme</h4>
|
||
<Radio.Group value={theme} onChange={setTheme} className="flex-col gap-2">
|
||
<Radio value="light">Light mode</Radio>
|
||
<Radio value="dark">Dark mode</Radio>
|
||
<Radio value="auto">Auto (system preference)</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
|
||
<div className="border-t border-gray-200 pt-6">
|
||
<h4 className="mb-3 text-sm font-medium text-gray-700">Language</h4>
|
||
<Radio.Group value={language} onChange={setLanguage} className="flex-col gap-2">
|
||
<Radio value="en">English</Radio>
|
||
<Radio value="zh">中文 (Chinese)</Radio>
|
||
<Radio value="es">Español (Spanish)</Radio>
|
||
<Radio value="fr">Français (French)</Radio>
|
||
</Radio.Group>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mt-6 rounded-lg bg-blue-50 p-3">
|
||
<div className="text-xs text-gray-600">
|
||
<strong>Current settings:</strong> Theme: {theme}, Language: {language}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const SettingsPanel: Story = {
|
||
render: () => <SettingsPanelDemo />,
|
||
}
|
||
|
||
// Real-world example - Payment method selector
|
||
const PaymentMethodSelectorDemo = () => {
|
||
const [paymentMethod, setPaymentMethod] = useState('credit_card')
|
||
|
||
return (
|
||
<div style={{ width: '500px' }} className="rounded-lg border border-gray-200 bg-white p-6">
|
||
<h3 className="mb-4 text-lg font-semibold">Payment Method</h3>
|
||
<Radio.Group value={paymentMethod} onChange={setPaymentMethod} className="flex-col gap-3">
|
||
<Radio value="credit_card">
|
||
<div className="flex w-full items-center justify-between">
|
||
<div>
|
||
<div className="font-medium">Credit Card</div>
|
||
<div className="text-xs text-gray-500">Visa, Mastercard, Amex</div>
|
||
</div>
|
||
<div className="text-xs text-gray-400">💳</div>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="paypal">
|
||
<div className="flex w-full items-center justify-between">
|
||
<div>
|
||
<div className="font-medium">PayPal</div>
|
||
<div className="text-xs text-gray-500">Fast and secure</div>
|
||
</div>
|
||
<div className="text-xs text-gray-400">🅿️</div>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="bank_transfer">
|
||
<div className="flex w-full items-center justify-between">
|
||
<div>
|
||
<div className="font-medium">Bank Transfer</div>
|
||
<div className="text-xs text-gray-500">1-3 business days</div>
|
||
</div>
|
||
<div className="text-xs text-gray-400">🏦</div>
|
||
</div>
|
||
</Radio>
|
||
</Radio.Group>
|
||
|
||
<button className="mt-6 w-full rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700">
|
||
Continue with {paymentMethod.replace('_', ' ')}
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const PaymentMethodSelector: Story = {
|
||
render: () => <PaymentMethodSelectorDemo />,
|
||
}
|
||
|
||
// Real-world example - Shipping options
|
||
const ShippingOptionsDemo = () => {
|
||
const [shipping, setShipping] = useState('standard')
|
||
|
||
const shippingCosts = {
|
||
standard: 5.99,
|
||
express: 14.99,
|
||
overnight: 29.99,
|
||
}
|
||
|
||
return (
|
||
<div style={{ width: '500px' }} className="rounded-lg border border-gray-200 bg-white p-6">
|
||
<h3 className="mb-4 text-lg font-semibold">Shipping Method</h3>
|
||
<Radio.Group value={shipping} onChange={setShipping} className="flex-col gap-3">
|
||
<Radio value="standard">
|
||
<div className="flex w-full items-center justify-between">
|
||
<div>
|
||
<div className="font-medium">Standard Shipping</div>
|
||
<div className="text-xs text-gray-500">5-7 business days</div>
|
||
</div>
|
||
<div className="font-semibold text-gray-700">${shippingCosts.standard}</div>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="express">
|
||
<div className="flex w-full items-center justify-between">
|
||
<div>
|
||
<div className="font-medium">Express Shipping</div>
|
||
<div className="text-xs text-gray-500">2-3 business days</div>
|
||
</div>
|
||
<div className="font-semibold text-gray-700">${shippingCosts.express}</div>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="overnight">
|
||
<div className="flex w-full items-center justify-between">
|
||
<div>
|
||
<div className="font-medium">Overnight Shipping</div>
|
||
<div className="text-xs text-gray-500">Next business day</div>
|
||
</div>
|
||
<div className="font-semibold text-gray-700">${shippingCosts.overnight}</div>
|
||
</div>
|
||
</Radio>
|
||
</Radio.Group>
|
||
|
||
<div className="mt-6 border-t border-gray-200 pt-4">
|
||
<div className="flex items-center justify-between">
|
||
<span className="text-sm text-gray-600">Shipping cost:</span>
|
||
<span className="text-lg font-semibold text-gray-900">
|
||
${shippingCosts[shipping as keyof typeof shippingCosts]}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const ShippingOptions: Story = {
|
||
render: () => <ShippingOptionsDemo />,
|
||
}
|
||
|
||
// Real-world example - Survey question
|
||
const SurveyQuestionDemo = () => {
|
||
const [satisfaction, setSatisfaction] = useState('')
|
||
|
||
return (
|
||
<div style={{ width: '500px' }} className="rounded-lg border border-gray-200 bg-white p-6">
|
||
<h3 className="mb-2 text-base font-semibold">Customer Satisfaction Survey</h3>
|
||
<p className="mb-4 text-sm text-gray-600">How satisfied are you with our service?</p>
|
||
|
||
<Radio.Group value={satisfaction} onChange={setSatisfaction} className="flex-col gap-2">
|
||
<Radio value="very_satisfied">
|
||
<div className="flex items-center gap-2">
|
||
<span>😄</span>
|
||
<span>Very satisfied</span>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="satisfied">
|
||
<div className="flex items-center gap-2">
|
||
<span>🙂</span>
|
||
<span>Satisfied</span>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="neutral">
|
||
<div className="flex items-center gap-2">
|
||
<span>😐</span>
|
||
<span>Neutral</span>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="dissatisfied">
|
||
<div className="flex items-center gap-2">
|
||
<span>😟</span>
|
||
<span>Dissatisfied</span>
|
||
</div>
|
||
</Radio>
|
||
<Radio value="very_dissatisfied">
|
||
<div className="flex items-center gap-2">
|
||
<span>😢</span>
|
||
<span>Very dissatisfied</span>
|
||
</div>
|
||
</Radio>
|
||
</Radio.Group>
|
||
|
||
<button
|
||
className="mt-6 w-full rounded-lg bg-green-600 px-4 py-2 text-sm font-medium text-white hover:bg-green-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||
disabled={!satisfaction}
|
||
>
|
||
Submit Feedback
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const SurveyQuestion: Story = {
|
||
render: () => <SurveyQuestionDemo />,
|
||
}
|
||
|
||
// Interactive playground
|
||
const PlaygroundDemo = () => {
|
||
const [value, setValue] = useState('option1')
|
||
|
||
return (
|
||
<div style={{ width: '400px' }}>
|
||
<Radio.Group value={value} onChange={setValue}>
|
||
<Radio value="option1">Option 1</Radio>
|
||
<Radio value="option2">Option 2</Radio>
|
||
<Radio value="option3">Option 3</Radio>
|
||
<Radio value="option4" disabled>Disabled option</Radio>
|
||
</Radio.Group>
|
||
<div className="mt-4 text-sm text-gray-600">
|
||
Selected: <span className="font-semibold">{value}</span>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export const Playground: Story = {
|
||
render: () => <PlaygroundDemo />,
|
||
}
|