import React, { useState } from 'react';
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
const Button: React.FC<ButtonProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
children,
onClick
}) => {
const baseStyles: React.CSSProperties = {
border: 'none',
borderRadius: '8px',
cursor: disabled ? 'not-allowed' : 'pointer',
fontWeight: 600,
transition: 'all 0.2s ease',
opacity: disabled ? 0.5 : 1,
};
const variants: Record<string, React.CSSProperties> = {
primary: { background: '#6366f1', color: 'white' },
secondary: { background: '#e5e7eb', color: '#374151' },
danger: { background: '#ef4444', color: 'white' },
ghost: { background: 'transparent', color: '#6366f1', border: '2px solid #6366f1' },
};
const sizes: Record<string, React.CSSProperties> = {
sm: { padding: '8px 16px', fontSize: '14px' },
md: { padding: '12px 24px', fontSize: '16px' },
lg: { padding: '16px 32px', fontSize: '18px' },
};
return (
<button
style={{ ...baseStyles, ...variants[variant], ...sizes[size] }}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
};
interface CardProps {
title: string;
description?: string;
children?: React.ReactNode;
}
const Card: React.FC<CardProps> = ({ title, description, children }) => (
<div style={{
background: 'white',
borderRadius: '12px',
padding: '24px',
boxShadow: '0 4px 6px -1px rgba(0,0,0,0.1)',
border: '1px solid #e5e7eb'
}}>
<h3 style={{ margin: '0 0 8px 0', color: '#1f2937' }}>{title}</h3>
{description && <p style={{ margin: '0 0 16px 0', color: '#6b7280', fontSize: '14px' }}>{description}</p>}
{children}
</div>
);
interface BadgeProps {
variant?: 'default' | 'success' | 'warning' | 'error';
children: React.ReactNode;
}
const Badge: React.FC<BadgeProps> = ({ variant = 'default', children }) => {
const colors: Record<string, { bg: string; text: string }> = {
default: { bg: '#e5e7eb', text: '#374151' },
success: { bg: '#d1fae5', text: '#065f46' },
warning: { bg: '#fef3c7', text: '#92400e' },
error: { bg: '#fee2e2', text: '#991b1b' },
};
return (
<span style={{
display: 'inline-block',
padding: '4px 12px',
borderRadius: '9999px',
fontSize: '12px',
fontWeight: 600,
background: colors[variant].bg,
color: colors[variant].text,
}}>
{children}
</span>
);
};
interface ControlsProps {
controls: {
name: string;
type: 'select' | 'boolean';
options?: string[];
value: any;
onChange: (value: any) => void;
}[];
}
const ControlsPanel: React.FC<ControlsProps> = ({ controls }) => (
<div style={{
background: '#f9fafb',
borderRadius: '8px',
padding: '16px',
marginTop: '16px'
}}>
<h4 style={{ margin: '0 0 12px 0', fontSize: '14px', color: '#6b7280' }}>โ๏ธ Controls</h4>
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
{controls.map((control) => (
<div key={control.name} style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
<label style={{ minWidth: '80px', fontSize: '14px', color: '#374151' }}>
{control.name}
</label>
{control.type === 'select' && (
<select
value={control.value}
onChange={(e) => control.onChange(e.target.value)}
style={{
padding: '6px 12px',
borderRadius: '6px',
border: '1px solid #d1d5db',
fontSize: '14px'
}}
>
{control.options?.map((opt) => (
<option key={opt} value={opt}>{opt}</option>
))}
</select>
)}
{control.type === 'boolean' && (
<input
type="checkbox"
checked={control.value}
onChange={(e) => control.onChange(e.target.checked)}
style={{ width: '18px', height: '18px' }}
/>
)}
</div>
))}
</div>
</div>
);
export default function App() {
const [activeStory, setActiveStory] = useState('Button');
const [buttonVariant, setButtonVariant] = useState<'primary' | 'secondary' | 'danger' | 'ghost'>('primary');
const [buttonSize, setButtonSize] = useState<'sm' | 'md' | 'lg'>('md');
const [buttonDisabled, setButtonDisabled] = useState(false);
const [badgeVariant, setBadgeVariant] = useState<'default' | 'success' | 'warning' | 'error'>('default');
const [clickCount, setClickCount] = useState(0);
const stories = ['Button', 'Card', 'Badge'];
return (
<div style={{ display: 'flex', minHeight: '100vh', background: '#f3f4f6' }}>
{}
<div style={{
width: '240px',
background: '#1f2937',
padding: '20px',
color: 'white'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '32px' }}>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
<rect width="32" height="32" rx="6" fill="#FF4785"/>
<path d="M21 8l.5-3.5 3.5.5V24l-3.5.5-.5-3.5" stroke="white" strokeWidth="2"/>
<path d="M11 12h10M11 16h10M11 20h6" stroke="white" strokeWidth="2" strokeLinecap="round"/>
</svg>
<span style={{ fontWeight: 700, fontSize: '18px' }}>Storybook</span>
</div>
<div style={{ fontSize: '12px', color: '#9ca3af', marginBottom: '8px', textTransform: 'uppercase' }}>
Components
</div>
{stories.map((story) => (
<button
key={story}
onClick={() => setActiveStory(story)}
style={{
display: 'block',
width: '100%',
padding: '10px 12px',
marginBottom: '4px',
background: activeStory === story ? '#374151' : 'transparent',
border: 'none',
borderRadius: '6px',
color: activeStory === story ? 'white' : '#9ca3af',
textAlign: 'left',
cursor: 'pointer',
fontSize: '14px'
}}
>
๐ฆ {story}
</button>
))}
</div>
{}
<div style={{ flex: 1, padding: '32px' }}>
<div style={{
background: 'white',
borderRadius: '12px',
padding: '32px',
boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '24px' }}>
<h1 style={{ margin: 0, fontSize: '24px', color: '#1f2937' }}>{activeStory}</h1>
<Badge variant="success">Stable</Badge>
</div>
{}
<div style={{
background: '#fafafa',
borderRadius: '8px',
padding: '48px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '200px',
border: '1px dashed #e5e7eb'
}}>
{activeStory === 'Button' && (
<div style={{ textAlign: 'center' }}>
<Button
variant={buttonVariant}
size={buttonSize}
disabled={buttonDisabled}
onClick={() => setClickCount(c => c + 1)}
>
Click Me!
</Button>
{clickCount > 0 && (
<p style={{ marginTop: '16px', color: '#6b7280', fontSize: '14px' }}>
Clicked {clickCount} time{clickCount !== 1 ? 's' : ''}
</p>
)}
</div>
)}
{activeStory === 'Card' && (
<Card
title="Welcome to Storybook"
description="Build UI components in isolation"
>
<Button variant="primary" size="sm">Learn More</Button>
</Card>
)}
{activeStory === 'Badge' && (
<div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
<Badge variant={badgeVariant}>Status Badge</Badge>
</div>
)}
</div>
{}
{activeStory === 'Button' && (
<ControlsPanel
controls={[
{
name: 'variant',
type: 'select',
options: ['primary', 'secondary', 'danger', 'ghost'],
value: buttonVariant,
onChange: setButtonVariant
},
{
name: 'size',
type: 'select',
options: ['sm', 'md', 'lg'],
value: buttonSize,
onChange: setButtonSize
},
{
name: 'disabled',
type: 'boolean',
value: buttonDisabled,
onChange: setButtonDisabled
}
]}
/>
)}
{activeStory === 'Badge' && (
<ControlsPanel
controls={[
{
name: 'variant',
type: 'select',
options: ['default', 'success', 'warning', 'error'],
value: badgeVariant,
onChange: setBadgeVariant
}
]}
/>
)}
{}
<div style={{
marginTop: '24px',
padding: '16px',
background: '#eff6ff',
borderRadius: '8px',
border: '1px solid #bfdbfe'
}}>
<p style={{ margin: 0, fontSize: '14px', color: '#1e40af' }}>
๐ก <strong>Note:</strong> This is a simulation of Storybook's interface.
Real Storybook runs as a separate development server with features like
auto-generated docs, accessibility testing, and visual regression testing.
</p>
</div>
</div>
</div>
</div>
);
}