@snowcone-app/ui 0.1.43 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +18 -4
  3. package/package.json +9 -5
  4. package/src/components/CanvasIsolationBoundary.tsx +202 -0
  5. package/src/components/LoadingOverlayPrism.tsx +251 -0
  6. package/src/composed/AddToCart.tsx +229 -0
  7. package/src/composed/ArtAlignment.tsx +703 -0
  8. package/src/composed/ArtSelector.tsx +290 -0
  9. package/src/composed/ArtworkCustomizer.tsx +212 -0
  10. package/src/composed/CanvasEditor.tsx +79 -0
  11. package/src/composed/ColorPicker.tsx +111 -0
  12. package/src/composed/CurrentSelectionDisplay.tsx +86 -0
  13. package/src/composed/HeroProductImage.tsx +1071 -0
  14. package/src/composed/Lightbox.index.ts +2 -0
  15. package/src/composed/Lightbox.tsx +230 -0
  16. package/src/composed/PlacementClipShapeSelector.tsx +88 -0
  17. package/src/composed/PlacementTabs.tsx +179 -0
  18. package/src/composed/ProductCard.tsx +298 -0
  19. package/src/composed/ProductGallery.tsx +54 -0
  20. package/src/composed/ProductImage.tsx +129 -0
  21. package/src/composed/ProductList.tsx +147 -0
  22. package/src/composed/ProductOptions.tsx +305 -0
  23. package/src/composed/RealtimeMockup.tsx +121 -0
  24. package/src/composed/TileCount.tsx +348 -0
  25. package/src/composed/carousels/HeroCarousel.tsx +240 -0
  26. package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
  27. package/src/composed/carousels/index.ts +11 -0
  28. package/src/composed/carousels/types.ts +58 -0
  29. package/src/composed/grids/MasonryGrid.tsx +238 -0
  30. package/src/composed/grids/index.ts +9 -0
  31. package/src/composed/search/CurrentRefinements.tsx +80 -0
  32. package/src/composed/search/Filters.tsx +49 -0
  33. package/src/composed/search/FiltersButton.tsx +57 -0
  34. package/src/composed/search/FiltersDrawer.tsx +375 -0
  35. package/src/composed/search/ProductGrid.tsx +118 -0
  36. package/src/composed/search/ProductHit.tsx +56 -0
  37. package/src/composed/search/SearchBox.tsx +109 -0
  38. package/src/composed/search/SearchProvider.tsx +136 -0
  39. package/src/composed/search/facetConfig.ts +16 -0
  40. package/src/composed/search/index.ts +22 -0
  41. package/src/composed/search/meilisearchAdapter.ts +20 -0
  42. package/src/composed/search/types.ts +22 -0
  43. package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
  44. package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
  45. package/src/composed/zoom/ZoomOverlay.tsx +194 -0
  46. package/src/composed/zoom/index.ts +12 -0
  47. package/src/composed/zoom/types.ts +12 -0
  48. package/src/design-system/ColorPalette.tsx +126 -0
  49. package/src/design-system/ColorSwatch.tsx +49 -0
  50. package/src/design-system/DesignSystemPage.tsx +130 -0
  51. package/src/design-system/ThemeSwitcher.tsx +181 -0
  52. package/src/design-system/TypographyScale.tsx +106 -0
  53. package/src/design-system/index.ts +5 -0
  54. package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
  55. package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
  56. package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
  57. package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
  58. package/src/hooks/useBrand.ts +41 -0
  59. package/src/hooks/useCanvasContext.ts +127 -0
  60. package/src/hooks/useDeviceDetection.ts +64 -0
  61. package/src/hooks/useFocusTrap.ts +70 -0
  62. package/src/hooks/useImagePreloader.ts +268 -0
  63. package/src/hooks/useImageTransition.ts +608 -0
  64. package/src/hooks/usePlacementsProcessor.ts +74 -0
  65. package/src/hooks/useProductGallery.ts +193 -0
  66. package/src/hooks/useProductPage.ts +467 -0
  67. package/src/hooks/useRenderGuard.ts +96 -0
  68. package/src/hooks/useScrollDirection.ts +196 -0
  69. package/src/hooks/viewport/index.ts +25 -0
  70. package/src/hooks/viewport/useContainerWidth.ts +59 -0
  71. package/src/hooks/viewport/useMediaQuery.ts +52 -0
  72. package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
  73. package/src/hooks/viewport/useViewportDimensions.ts +135 -0
  74. package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
  75. package/src/hooks/visibility/index.ts +15 -0
  76. package/src/hooks/visibility/observerPool.ts +150 -0
  77. package/src/index.ts +240 -0
  78. package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
  79. package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
  80. package/src/layouts/hero-zoom/index.ts +30 -0
  81. package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
  82. package/src/layouts/hero-zoom/types.ts +113 -0
  83. package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
  84. package/src/layouts/index.ts +9 -0
  85. package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
  86. package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
  87. package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
  88. package/src/layouts/pdp/PDPLayout.tsx +246 -0
  89. package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
  90. package/src/layouts/pdp/index.ts +40 -0
  91. package/src/lib/env.ts +15 -0
  92. package/src/lib/locale.ts +167 -0
  93. package/src/lib/router.tsx +46 -0
  94. package/src/lib/utils.ts +6 -0
  95. package/src/lightbox/README.md +77 -0
  96. package/src/next/index.tsx +26 -0
  97. package/src/patterns/MockupPriorityProvider.tsx +1014 -0
  98. package/src/patterns/Product.tsx +850 -0
  99. package/src/patterns/ProductPageProvider.tsx +224 -0
  100. package/src/patterns/RealtimeProvider.tsx +1162 -0
  101. package/src/patterns/ShopProvider.tsx +603 -0
  102. package/src/personalization/PersonalizationBridge.tsx +235 -0
  103. package/src/personalization/PersonalizationContext.ts +29 -0
  104. package/src/personalization/PersonalizationInputs.tsx +110 -0
  105. package/src/personalization/PersonalizationProvider.tsx +407 -0
  106. package/src/personalization/canvas-stub.d.ts +22 -0
  107. package/src/personalization/index.ts +43 -0
  108. package/src/personalization/types.ts +48 -0
  109. package/src/personalization/usePersonalization.ts +32 -0
  110. package/src/personalization/usePersonalizationShimmer.ts +159 -0
  111. package/src/personalization/utils.ts +59 -0
  112. package/src/primitives/BrandLogo.tsx +65 -0
  113. package/src/primitives/BrandName.tsx +51 -0
  114. package/src/primitives/Button.tsx +123 -0
  115. package/src/primitives/ColorSwatch.tsx +221 -0
  116. package/src/primitives/DragHintAnimation.tsx +190 -0
  117. package/src/primitives/EdgeSwipeGuards.tsx +60 -0
  118. package/src/primitives/FloatingActionGroup.tsx +176 -0
  119. package/src/primitives/ProductPrice.tsx +171 -0
  120. package/src/primitives/ProgressiveBlur.tsx +295 -0
  121. package/src/primitives/ThemeToggle.tsx +125 -0
  122. package/src/primitives/__tests__/story-coverage.test.ts +98 -0
  123. package/src/primitives/accordion.tsx +280 -0
  124. package/src/primitives/badge.tsx +137 -0
  125. package/src/primitives/card.tsx +61 -0
  126. package/src/primitives/checkbox.tsx +56 -0
  127. package/src/primitives/collapsible.tsx +51 -0
  128. package/src/primitives/drawer.tsx +828 -0
  129. package/src/primitives/dropdown-menu.tsx +197 -0
  130. package/src/primitives/fieldset.tsx +73 -0
  131. package/src/primitives/index.ts +138 -0
  132. package/src/primitives/input.tsx +91 -0
  133. package/src/primitives/kbd.tsx +130 -0
  134. package/src/primitives/label.tsx +20 -0
  135. package/src/primitives/link.tsx +182 -0
  136. package/src/primitives/popover.tsx +80 -0
  137. package/src/primitives/radio-group.tsx +79 -0
  138. package/src/primitives/scroll-fade.tsx +159 -0
  139. package/src/primitives/select.tsx +170 -0
  140. package/src/primitives/separator.tsx +25 -0
  141. package/src/primitives/slider.tsx +221 -0
  142. package/src/primitives/spinner.tsx +72 -0
  143. package/src/primitives/stories/Accordion.stories.tsx +121 -0
  144. package/src/primitives/stories/Badge.stories.tsx +221 -0
  145. package/src/primitives/stories/Button.stories.tsx +185 -0
  146. package/src/primitives/stories/Card.stories.tsx +171 -0
  147. package/src/primitives/stories/Checkbox.stories.tsx +214 -0
  148. package/src/primitives/stories/Collapsible.stories.tsx +230 -0
  149. package/src/primitives/stories/Drawer.stories.tsx +378 -0
  150. package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
  151. package/src/primitives/stories/Fieldset.stories.tsx +212 -0
  152. package/src/primitives/stories/Input.stories.tsx +172 -0
  153. package/src/primitives/stories/Kbd.stories.tsx +183 -0
  154. package/src/primitives/stories/Label.stories.tsx +98 -0
  155. package/src/primitives/stories/Link.stories.tsx +260 -0
  156. package/src/primitives/stories/Popover.stories.tsx +178 -0
  157. package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
  158. package/src/primitives/stories/Select.stories.tsx +222 -0
  159. package/src/primitives/stories/Separator.stories.tsx +134 -0
  160. package/src/primitives/stories/Slider.stories.tsx +203 -0
  161. package/src/primitives/stories/Spinner.stories.tsx +142 -0
  162. package/src/primitives/stories/Surface.stories.tsx +257 -0
  163. package/src/primitives/stories/Switch.stories.tsx +131 -0
  164. package/src/primitives/stories/Tabs.stories.tsx +275 -0
  165. package/src/primitives/stories/TextField.stories.tsx +139 -0
  166. package/src/primitives/stories/Textarea.stories.tsx +148 -0
  167. package/src/primitives/stories/Tooltip.stories.tsx +119 -0
  168. package/src/primitives/surface.tsx +86 -0
  169. package/src/primitives/switch.tsx +35 -0
  170. package/src/primitives/tabs.tsx +206 -0
  171. package/src/primitives/text-field.tsx +84 -0
  172. package/src/primitives/textarea.tsx +50 -0
  173. package/src/primitives/tooltip.tsx +58 -0
  174. package/src/services/CanvasExportService.ts +518 -0
  175. package/src/styles/base.css +380 -0
  176. package/src/styles/defaults.css +280 -0
  177. package/src/styles/globals.css +1242 -0
  178. package/src/styles/index.css +17 -0
  179. package/src/styles/ne-themes.css +4740 -0
  180. package/src/styles/tailwind.css +11 -0
  181. package/src/styles/tokens.css +117 -0
  182. package/src/styles/utilities.css +188 -0
  183. package/src/themes/apply-theme.ts +449 -0
  184. package/src/themes/getThemeStyles.ts +454 -0
  185. package/src/themes/index.ts +48 -0
  186. package/src/themes/oklch-theme.ts +283 -0
  187. package/src/themes/presets.ts +989 -0
  188. package/src/themes/types.ts +386 -0
  189. package/src/themes/useTheme.tsx +450 -0
  190. package/src/utils/dev-warnings.ts +161 -0
  191. package/src/utils/devWarnings.ts +153 -0
  192. package/dist/styles.css +0 -1
@@ -0,0 +1,472 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import React from 'react';
3
+ import { Icon } from '@iconify/react';
4
+ import { Product } from '../../patterns/Product';
5
+ import { ProductOptions } from '../../composed/ProductOptions';
6
+ import { AddToCart } from '../../composed/AddToCart';
7
+ import {
8
+ Accordion,
9
+ AccordionItem,
10
+ AccordionTrigger,
11
+ AccordionContent,
12
+ } from '../../primitives/accordion';
13
+ import type { CatalogProduct } from '@snowcone-app/sdk';
14
+
15
+ /**
16
+ * PDPInfoPanel - Product Detail Page Info Panel
17
+ *
18
+ * The right-side panel on a Product Detail Page (PDP) that displays:
19
+ * - Product name and price
20
+ * - Rating and favorite button
21
+ * - Artwork customizer (optional)
22
+ * - Product options (size, color, etc.) - uses real ProductOptions component
23
+ * - Add to Cart button - uses real AddToCart component
24
+ * - Product details accordion (description, materials, care, shipping)
25
+ *
26
+ * This story uses REAL components with Product context.
27
+ */
28
+
29
+ // Helper to generate all combinations for sizes and colors
30
+ const sizes = ['XS', 'S', 'M', 'L', 'XL', '2XL'];
31
+ const colors = ['Black', 'White', 'Navy', 'Heather Gray'];
32
+
33
+ const generateCombinations = (sizeList: string[], colorList: string[], price: number) => {
34
+ const combinations: { price: number; variantId: string; Size: string; Color: string }[] = [];
35
+ for (const size of sizeList) {
36
+ for (const color of colorList) {
37
+ combinations.push({
38
+ price,
39
+ variantId: `${size.toLowerCase()}-${color.toLowerCase().replace(' ', '-')}`,
40
+ Size: size,
41
+ Color: color,
42
+ });
43
+ }
44
+ }
45
+ return combinations;
46
+ };
47
+
48
+ // Mock product data that matches CatalogProduct interface
49
+ const mockProduct: CatalogProduct = {
50
+ id: 'classic-cotton-tshirt',
51
+ name: 'Classic Cotton T-Shirt',
52
+ slug: 'classic-cotton-tshirt',
53
+ price: 2999, // $29.99 in cents
54
+ options: {
55
+ attributes: {
56
+ Size: {
57
+ type: 'select',
58
+ affectsCombinations: true,
59
+ choices: sizes.map(label => ({ label })),
60
+ },
61
+ Color: {
62
+ type: 'swatch',
63
+ affectsCombinations: true,
64
+ choices: [
65
+ { label: 'Black', hex: '#1a1a1a' },
66
+ { label: 'White', hex: '#ffffff' },
67
+ { label: 'Navy', hex: '#1e3a5f' },
68
+ { label: 'Heather Gray', hex: '#9ca3af' },
69
+ ],
70
+ },
71
+ },
72
+ combinations: generateCombinations(sizes, colors, 2999),
73
+ attributesList: ['Size', 'Color'],
74
+ },
75
+ description: ['Premium quality with exceptional comfort and style.'],
76
+ };
77
+
78
+ // Product with minimal options
79
+ const minimalProduct: CatalogProduct = {
80
+ id: 'simple-product',
81
+ name: 'Simple Product',
82
+ slug: 'simple-product',
83
+ price: 1999,
84
+ options: {
85
+ attributes: {
86
+ Size: {
87
+ type: 'select',
88
+ affectsCombinations: true,
89
+ choices: [{ label: 'One Size' }],
90
+ },
91
+ Color: {
92
+ type: 'swatch',
93
+ affectsCombinations: true,
94
+ choices: [{ label: 'Natural', hex: '#f5f5dc' }],
95
+ },
96
+ },
97
+ combinations: [{ price: 1999, variantId: 'one-size-natural' }],
98
+ attributesList: ['Size', 'Color'],
99
+ },
100
+ };
101
+
102
+ // Product with many colors
103
+ const manyColorsSizes = ['S', 'M', 'L'];
104
+ const manyColorsColors = ['Black', 'White', 'Navy', 'Red', 'Green', 'Purple', 'Orange', 'Pink'];
105
+ const manyColorsProduct: CatalogProduct = {
106
+ id: 'colorful-product',
107
+ name: 'Colorful T-Shirt',
108
+ slug: 'colorful-product',
109
+ price: 2999,
110
+ options: {
111
+ attributes: {
112
+ Size: {
113
+ type: 'select',
114
+ affectsCombinations: true,
115
+ choices: manyColorsSizes.map(label => ({ label })),
116
+ },
117
+ Color: {
118
+ type: 'swatch',
119
+ affectsCombinations: true,
120
+ choices: [
121
+ { label: 'Black', hex: '#1a1a1a' },
122
+ { label: 'White', hex: '#ffffff' },
123
+ { label: 'Navy', hex: '#1e3a5f' },
124
+ { label: 'Red', hex: '#dc2626' },
125
+ { label: 'Green', hex: '#16a34a' },
126
+ { label: 'Purple', hex: '#9333ea' },
127
+ { label: 'Orange', hex: '#ea580c' },
128
+ { label: 'Pink', hex: '#ec4899' },
129
+ ],
130
+ },
131
+ },
132
+ combinations: generateCombinations(manyColorsSizes, manyColorsColors, 2999),
133
+ attributesList: ['Size', 'Color'],
134
+ },
135
+ };
136
+
137
+ interface PDPInfoPanelProps {
138
+ product: CatalogProduct;
139
+ tag?: string;
140
+ rating?: number;
141
+ reviewCount?: number;
142
+ originalPrice?: string;
143
+ showArtworkCustomizer?: boolean;
144
+ onAddToCart?: (cartDetail: any) => void;
145
+ className?: string;
146
+ }
147
+
148
+ const PDPInfoPanel = ({
149
+ product,
150
+ tag,
151
+ rating = 4.2,
152
+ reviewCount = 143,
153
+ originalPrice,
154
+ showArtworkCustomizer = false,
155
+ onAddToCart,
156
+ className,
157
+ }: PDPInfoPanelProps) => {
158
+ const [isFavorite, setIsFavorite] = React.useState(false);
159
+
160
+ // Format price from cents
161
+ const formatPrice = (cents: number) => `$${(cents / 100).toFixed(2)}`;
162
+
163
+ return (
164
+ <Product productData={product} initialSelection={{ Size: 'M', Color: product.options?.attributes?.Color?.choices?.[0]?.label || 'Black' }}>
165
+ <div
166
+ className={`bg-background/85 backdrop-blur-sm rounded-3xl p-6 shadow-lg max-h-[calc(100vh-11rem)] overflow-y-auto ${className || ''}`}
167
+ >
168
+ <div className="flex flex-col gap-6">
169
+ {/* Tag, Product Name and Price */}
170
+ <div className="flex flex-col gap-3">
171
+ {/* Tag */}
172
+ {tag && (
173
+ <span className="text-sm font-medium text-primary">{tag}</span>
174
+ )}
175
+
176
+ {/* Name and Price */}
177
+ <div className="flex items-end justify-between gap-4">
178
+ <h1 className="text-2xl font-bold text-foreground leading-tight">{product.name}</h1>
179
+ <div className="flex items-baseline gap-2 shrink-0">
180
+ <span className="text-lg font-semibold text-foreground">
181
+ {formatPrice(product.price)}
182
+ </span>
183
+ {originalPrice && (
184
+ <span className="text-sm text-muted-foreground line-through">
185
+ {originalPrice}
186
+ </span>
187
+ )}
188
+ </div>
189
+ </div>
190
+
191
+ {/* Rating and Favorite */}
192
+ <div className="flex items-center justify-start gap-2">
193
+ <button
194
+ onClick={() => setIsFavorite(!isFavorite)}
195
+ className="w-10 h-10 bg-foreground/5 rounded-full flex items-center justify-center hover:bg-foreground/10 transition-colors"
196
+ >
197
+ <Icon
198
+ icon={isFavorite ? 'gravity-ui:heart-fill' : 'gravity-ui:heart'}
199
+ className={`w-5 h-5 ${isFavorite ? 'text-red-500' : 'text-muted-foreground/60'}`}
200
+ />
201
+ </button>
202
+ <div className="h-10 bg-foreground/5 rounded-full px-3 flex items-center gap-1 text-sm font-caption text-muted-foreground">
203
+ <Icon
204
+ icon="gravity-ui:star"
205
+ className="w-4 h-4 text-muted-foreground"
206
+ />
207
+ <span className="text-sm font-medium">{rating}</span>
208
+ <span className="text-sm">({reviewCount})</span>
209
+ </div>
210
+ </div>
211
+ </div>
212
+
213
+ {/* Artwork Customizer Placeholder */}
214
+ {showArtworkCustomizer && (
215
+ <div className="flex flex-col gap-4">
216
+ <div className="aspect-square bg-muted rounded-2xl flex items-center justify-center border-2 border-dashed border-border">
217
+ <div className="text-center text-muted-foreground">
218
+ <Icon icon="gravity-ui:picture" className="w-12 h-12 mx-auto mb-2 opacity-50" />
219
+ <p className="text-sm">Artwork Customizer</p>
220
+ <p className="text-xs opacity-70">Drag to position artwork</p>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ )}
225
+
226
+ {/* Product Options - REAL COMPONENT */}
227
+ <ProductOptions />
228
+
229
+ {/* Add to Cart - REAL COMPONENT */}
230
+ <AddToCart onClick={onAddToCart} />
231
+
232
+ {/* Product Details Accordion */}
233
+ <Accordion type="single" collapsible className="w-full [&>*]:border-0">
234
+ <AccordionItem value="description">
235
+ <AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
236
+ Description
237
+ </AccordionTrigger>
238
+ <AccordionContent>
239
+ <p className="text-base font-caption text-muted-foreground leading-relaxed">
240
+ {product.description?.[0] || 'Premium quality with exceptional comfort and style.'}
241
+ </p>
242
+ </AccordionContent>
243
+ </AccordionItem>
244
+
245
+ <AccordionItem value="materials">
246
+ <AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
247
+ Materials
248
+ </AccordionTrigger>
249
+ <AccordionContent>
250
+ <p className="text-base font-caption text-muted-foreground leading-relaxed">
251
+ 100% organic cotton with sustainable manufacturing process.
252
+ </p>
253
+ </AccordionContent>
254
+ </AccordionItem>
255
+
256
+ <AccordionItem value="care">
257
+ <AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
258
+ Care Instructions
259
+ </AccordionTrigger>
260
+ <AccordionContent>
261
+ <p className="text-base font-caption text-muted-foreground leading-relaxed">
262
+ Machine wash cold, tumble dry low. Do not bleach.
263
+ </p>
264
+ </AccordionContent>
265
+ </AccordionItem>
266
+
267
+ <AccordionItem value="shipping">
268
+ <AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
269
+ Shipping & Returns
270
+ </AccordionTrigger>
271
+ <AccordionContent>
272
+ <p className="text-base font-caption text-muted-foreground leading-relaxed">
273
+ Free shipping on orders over $50. 30-day return policy.
274
+ </p>
275
+ </AccordionContent>
276
+ </AccordionItem>
277
+ </Accordion>
278
+ </div>
279
+ </div>
280
+ </Product>
281
+ );
282
+ };
283
+
284
+ const meta: Meta<typeof PDPInfoPanel> = {
285
+ title: 'Layouts/PDP Info Panel',
286
+ component: PDPInfoPanel,
287
+ parameters: {
288
+ layout: 'centered',
289
+ docs: {
290
+ description: {
291
+ component: `
292
+ The PDP Info Panel uses **real ProductOptions and AddToCart components** with Product context.
293
+
294
+ ## Structure
295
+
296
+ 1. **Header Section**: Product name, price, and discount
297
+ 2. **Social Actions**: Favorite button and rating display
298
+ 3. **Artwork Customizer** (optional): For print-on-demand products
299
+ 4. **Product Options**: Real \`<ProductOptions />\` component with context
300
+ 5. **Add to Cart**: Real \`<AddToCart />\` component with validation
301
+ 6. **Details Accordion**: Description, materials, care, shipping
302
+
303
+ ## Usage
304
+
305
+ \`\`\`tsx
306
+ <Product productData={productData}>
307
+ <div className="bg-background/85 backdrop-blur-sm rounded-3xl p-6">
308
+ {/* Product Name + Price */}
309
+ {/* Rating + Favorite */}
310
+ <ProductOptions />
311
+ <AddToCart onClick={(detail) => console.log(detail)} />
312
+ {/* Accordion */}
313
+ </div>
314
+ </Product>
315
+ \`\`\`
316
+ `,
317
+ },
318
+ },
319
+ },
320
+ tags: ['autodocs'],
321
+ argTypes: {
322
+ tag: {
323
+ control: 'text',
324
+ description: 'Product tag/badge (e.g., "Upgraded", "New", "Sale")',
325
+ },
326
+ showArtworkCustomizer: {
327
+ control: 'boolean',
328
+ description: 'Show artwork customizer placeholder',
329
+ },
330
+ rating: {
331
+ control: { type: 'number', min: 0, max: 5, step: 0.1 },
332
+ description: 'Product rating (0-5)',
333
+ },
334
+ reviewCount: {
335
+ control: 'number',
336
+ description: 'Number of reviews',
337
+ },
338
+ originalPrice: {
339
+ control: 'text',
340
+ description: 'Original price (for showing discount)',
341
+ },
342
+ },
343
+ };
344
+
345
+ export default meta;
346
+ type Story = StoryObj<typeof meta>;
347
+
348
+ export const Default: Story = {
349
+ args: {
350
+ product: mockProduct,
351
+ tag: 'Upgraded',
352
+ },
353
+ render: (args) => (
354
+ <div className="w-[380px]">
355
+ <PDPInfoPanel {...args} />
356
+ </div>
357
+ ),
358
+ };
359
+
360
+ export const WithArtworkCustomizer: Story = {
361
+ args: {
362
+ product: mockProduct,
363
+ showArtworkCustomizer: true,
364
+ },
365
+ render: (args) => (
366
+ <div className="w-[380px]">
367
+ <PDPInfoPanel {...args} />
368
+ </div>
369
+ ),
370
+ };
371
+
372
+ export const OnSale: Story = {
373
+ args: {
374
+ product: mockProduct,
375
+ originalPrice: '$39.99',
376
+ },
377
+ render: (args) => (
378
+ <div className="w-[380px]">
379
+ <PDPInfoPanel {...args} />
380
+ </div>
381
+ ),
382
+ };
383
+
384
+ export const HighlyRated: Story = {
385
+ args: {
386
+ product: mockProduct,
387
+ rating: 4.9,
388
+ reviewCount: 2847,
389
+ },
390
+ render: (args) => (
391
+ <div className="w-[380px]">
392
+ <PDPInfoPanel {...args} />
393
+ </div>
394
+ ),
395
+ };
396
+
397
+ export const MinimalOptions: Story = {
398
+ args: {
399
+ product: minimalProduct,
400
+ },
401
+ render: (args) => (
402
+ <div className="w-[380px]">
403
+ <PDPInfoPanel {...args} />
404
+ </div>
405
+ ),
406
+ };
407
+
408
+ export const ManyColors: Story = {
409
+ args: {
410
+ product: manyColorsProduct,
411
+ },
412
+ render: (args) => (
413
+ <div className="w-[380px]">
414
+ <PDPInfoPanel {...args} />
415
+ </div>
416
+ ),
417
+ };
418
+
419
+ // Full panel in context (simulating desktop layout with overlapping panel)
420
+ const demoMockups = [
421
+ { id: 'front', label: 'Front' },
422
+ { id: 'back', label: 'Back' },
423
+ { id: 'detail', label: 'Detail' },
424
+ ];
425
+
426
+ export const InContext: Story = {
427
+ args: {
428
+ product: mockProduct,
429
+ tag: 'Upgraded',
430
+ showArtworkCustomizer: true,
431
+ },
432
+ parameters: {
433
+ layout: 'fullscreen',
434
+ },
435
+ render: (args) => (
436
+ <div className="min-h-screen bg-background relative">
437
+ {/* Full-width Hero Images - content centered in visible area (left of panel) */}
438
+ <div className="w-full">
439
+ {demoMockups.map((mockup) => (
440
+ <div
441
+ key={mockup.id}
442
+ className="relative w-full"
443
+ style={{ height: '70vh' }}
444
+ >
445
+ <div className="w-full h-full bg-gradient-to-br from-muted to-muted/50 flex items-center justify-center">
446
+ {/* Content offset to left to center in visible area */}
447
+ <div
448
+ className="text-center text-muted-foreground"
449
+ style={{ marginRight: '30%' }}
450
+ >
451
+ <Icon
452
+ icon="gravity-ui:t-shirt"
453
+ className="w-32 h-32 mx-auto mb-4 opacity-20"
454
+ />
455
+ <p className="text-xl font-medium opacity-60">{mockup.label} View</p>
456
+ <p className="text-sm opacity-40">Product mockup</p>
457
+ </div>
458
+ </div>
459
+ </div>
460
+ ))}
461
+ </div>
462
+
463
+ {/* Overlapping Info Panel - fixed on right side */}
464
+ <div
465
+ className="hidden md:block fixed right-8 top-8 w-[30%] xl:w-[35%] max-w-96 z-10"
466
+ style={{ maxHeight: 'calc(100vh - 4rem)' }}
467
+ >
468
+ <PDPInfoPanel {...args} />
469
+ </div>
470
+ </div>
471
+ ),
472
+ };