Skeleton.stories.tsx
1 import type { Meta, StoryObj } from '@storybook/react'; 2 import { Skeleton, SkeletonText, StatSkeleton } from '../src/components/Skeleton'; 3 import { Card, CardHeader, CardContent } from '../src/components/Card/Card'; 4 5 const meta: Meta<typeof Skeleton> = { 6 title: 'Components/Skeleton', 7 component: Skeleton, 8 parameters: { 9 layout: 'centered', 10 }, 11 tags: ['autodocs'], 12 argTypes: { 13 variant: { 14 control: 'select', 15 options: ['text', 'circular', 'rectangular', 'rounded'], 16 }, 17 animation: { 18 control: 'select', 19 options: ['pulse', 'wave', 'none'], 20 }, 21 width: { 22 control: 'text', 23 }, 24 height: { 25 control: 'text', 26 }, 27 }, 28 }; 29 30 export default meta; 31 type Story = StoryObj<typeof Skeleton>; 32 33 // Variants 34 export const Text: Story = { 35 args: { 36 variant: 'text', 37 width: 200, 38 }, 39 }; 40 41 export const Circular: Story = { 42 args: { 43 variant: 'circular', 44 width: 48, 45 height: 48, 46 }, 47 }; 48 49 export const Rectangular: Story = { 50 args: { 51 variant: 'rectangular', 52 width: 200, 53 height: 100, 54 }, 55 }; 56 57 export const Rounded: Story = { 58 args: { 59 variant: 'rounded', 60 width: 200, 61 height: 100, 62 }, 63 }; 64 65 // Animations 66 export const Pulse: Story = { 67 args: { 68 variant: 'rectangular', 69 width: 200, 70 height: 60, 71 animation: 'pulse', 72 }, 73 }; 74 75 export const Wave: Story = { 76 args: { 77 variant: 'rectangular', 78 width: 200, 79 height: 60, 80 animation: 'wave', 81 }, 82 }; 83 84 export const NoAnimation: Story = { 85 args: { 86 variant: 'rectangular', 87 width: 200, 88 height: 60, 89 animation: 'none', 90 }, 91 }; 92 93 // Composite skeletons 94 export const TextBlock: Story = { 95 render: () => <SkeletonText lines={3} />, 96 }; 97 98 export const TextBlockSmallGap: Story = { 99 render: () => <SkeletonText lines={4} gap="sm" />, 100 }; 101 102 export const TextBlockLargeGap: Story = { 103 render: () => <SkeletonText lines={3} gap="lg" />, 104 }; 105 106 export const StatSkeletonDefault: Story = { 107 render: () => <StatSkeleton />, 108 }; 109 110 export const StatSkeletonNoLabel: Story = { 111 render: () => <StatSkeleton showLabel={false} />, 112 }; 113 114 // Gallery views 115 export const AllVariants: Story = { 116 render: () => ( 117 <div className="flex flex-wrap gap-6 items-center"> 118 <div className="text-center"> 119 <Skeleton variant="text" width={120} /> 120 <p className="text-xs text-gray-500 mt-2">Text</p> 121 </div> 122 <div className="text-center"> 123 <Skeleton variant="circular" width={48} height={48} /> 124 <p className="text-xs text-gray-500 mt-2">Circular</p> 125 </div> 126 <div className="text-center"> 127 <Skeleton variant="rectangular" width={80} height={60} /> 128 <p className="text-xs text-gray-500 mt-2">Rectangular</p> 129 </div> 130 <div className="text-center"> 131 <Skeleton variant="rounded" width={80} height={60} /> 132 <p className="text-xs text-gray-500 mt-2">Rounded</p> 133 </div> 134 </div> 135 ), 136 }; 137 138 export const AllAnimations: Story = { 139 render: () => ( 140 <div className="flex flex-col gap-4"> 141 <div className="flex items-center gap-4"> 142 <span className="w-16 text-sm text-gray-500">Pulse</span> 143 <Skeleton variant="rounded" width={200} height={40} animation="pulse" /> 144 </div> 145 <div className="flex items-center gap-4"> 146 <span className="w-16 text-sm text-gray-500">Wave</span> 147 <Skeleton variant="rounded" width={200} height={40} animation="wave" /> 148 </div> 149 <div className="flex items-center gap-4"> 150 <span className="w-16 text-sm text-gray-500">None</span> 151 <Skeleton variant="rounded" width={200} height={40} animation="none" /> 152 </div> 153 </div> 154 ), 155 }; 156 157 // Use cases 158 export const ProfileCard: Story = { 159 name: 'Use Case: Profile Card Loading', 160 render: () => ( 161 <div className="flex items-center gap-4 p-4 border rounded-lg w-80"> 162 <Skeleton variant="circular" width={56} height={56} /> 163 <div className="flex-1"> 164 <Skeleton variant="text" width="60%" className="mb-2" /> 165 <Skeleton variant="text" width="40%" /> 166 </div> 167 </div> 168 ), 169 }; 170 171 export const ArticleCard: Story = { 172 name: 'Use Case: Article Card Loading', 173 render: () => ( 174 <div className="w-80 border rounded-lg overflow-hidden"> 175 <Skeleton variant="rectangular" width="100%" height={160} /> 176 <div className="p-4"> 177 <Skeleton variant="text" width="80%" className="mb-3" height={20} /> 178 <SkeletonText lines={2} gap="sm" /> 179 <div className="flex items-center gap-2 mt-4"> 180 <Skeleton variant="circular" width={24} height={24} /> 181 <Skeleton variant="text" width={100} /> 182 </div> 183 </div> 184 </div> 185 ), 186 }; 187 188 export const DashboardStats: Story = { 189 name: 'Use Case: Dashboard Stats Loading', 190 render: () => ( 191 <div className="grid grid-cols-4 gap-4"> 192 {[1, 2, 3, 4].map((i) => ( 193 <Card key={i} size="sm"> 194 <CardContent className="pt-4"> 195 <StatSkeleton /> 196 </CardContent> 197 </Card> 198 ))} 199 </div> 200 ), 201 }; 202 203 export const TableRow: Story = { 204 name: 'Use Case: Table Row Loading', 205 render: () => ( 206 <div className="w-full max-w-2xl"> 207 {[1, 2, 3].map((i) => ( 208 <div key={i} className="flex items-center gap-4 p-4 border-b"> 209 <Skeleton variant="text" width={80} /> 210 <Skeleton variant="text" width={120} className="flex-1" /> 211 <Skeleton variant="text" width={60} /> 212 <Skeleton variant="rounded" width={80} height={28} /> 213 </div> 214 ))} 215 </div> 216 ), 217 }; 218 219 export const TransactionList: Story = { 220 name: 'Use Case: Transaction List Loading', 221 render: () => ( 222 <div className="w-80 space-y-3"> 223 {[1, 2, 3].map((i) => ( 224 <div key={i} className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg"> 225 <Skeleton variant="circular" width={40} height={40} /> 226 <div className="flex-1"> 227 <Skeleton variant="text" width="70%" className="mb-1" /> 228 <Skeleton variant="text" width="50%" height={12} /> 229 </div> 230 <div className="text-right"> 231 <Skeleton variant="text" width={60} className="mb-1" /> 232 <Skeleton variant="text" width={40} height={12} /> 233 </div> 234 </div> 235 ))} 236 </div> 237 ), 238 };