@shopify/shop-minis-react 0.0.17 → 0.0.19

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 (96) hide show
  1. package/dist/_virtual/index3.js +6 -0
  2. package/dist/_virtual/index3.js.map +1 -0
  3. package/dist/_virtual/index4.js +5 -0
  4. package/dist/_virtual/index4.js.map +1 -0
  5. package/dist/_virtual/use-sync-external-store-shim.development.js +5 -0
  6. package/dist/_virtual/use-sync-external-store-shim.development.js.map +1 -0
  7. package/dist/_virtual/use-sync-external-store-shim.production.js +5 -0
  8. package/dist/_virtual/use-sync-external-store-shim.production.js.map +1 -0
  9. package/dist/components/atoms/alert-dialog.js +41 -0
  10. package/dist/components/atoms/alert-dialog.js.map +1 -0
  11. package/dist/components/atoms/thumbhash-image.js +54 -0
  12. package/dist/components/atoms/thumbhash-image.js.map +1 -0
  13. package/dist/components/commerce/merchant-card-skeleton.js +29 -0
  14. package/dist/components/commerce/merchant-card-skeleton.js.map +1 -0
  15. package/dist/components/commerce/merchant-card.js +28 -22
  16. package/dist/components/commerce/merchant-card.js.map +1 -1
  17. package/dist/components/commerce/product-card-skeleton.js +20 -0
  18. package/dist/components/commerce/product-card-skeleton.js.map +1 -0
  19. package/dist/components/commerce/product-card.js +105 -78
  20. package/dist/components/commerce/product-card.js.map +1 -1
  21. package/dist/components/navigation/transition-container.js +8 -0
  22. package/dist/components/navigation/transition-container.js.map +1 -0
  23. package/dist/components/navigation/transition-link.js +27 -0
  24. package/dist/components/navigation/transition-link.js.map +1 -0
  25. package/dist/components/ui/avatar.js +54 -0
  26. package/dist/components/ui/avatar.js.map +1 -0
  27. package/dist/components/ui/skeleton.js +16 -0
  28. package/dist/components/ui/skeleton.js.map +1 -0
  29. package/dist/hooks/navigation/useNavigateWithTransition.js +43 -0
  30. package/dist/hooks/navigation/useNavigateWithTransition.js.map +1 -0
  31. package/dist/hooks/navigation/useViewTransitions.js +45 -0
  32. package/dist/hooks/navigation/useViewTransitions.js.map +1 -0
  33. package/dist/index.js +215 -192
  34. package/dist/index.js.map +1 -1
  35. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-avatar@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1._hkz57sehyui4ndfh3rsqwxftli/node_modules/@radix-ui/react-avatar/dist/index.js +77 -0
  36. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-avatar@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1._hkz57sehyui4ndfh3rsqwxftli/node_modules/@radix-ui/react-avatar/dist/index.js.map +1 -0
  37. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +16 -0
  38. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js.map +1 -0
  39. package/dist/shop-minis-react/node_modules/.pnpm/js-base64@3.7.7/node_modules/js-base64/base64.js +21 -0
  40. package/dist/shop-minis-react/node_modules/.pnpm/js-base64@3.7.7/node_modules/js-base64/base64.js.map +1 -0
  41. package/dist/shop-minis-react/node_modules/.pnpm/react-router@7.7.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/react-router/dist/development/chunk-EF7DTUVF.js +1298 -0
  42. package/dist/shop-minis-react/node_modules/.pnpm/react-router@7.7.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/react-router/dist/development/chunk-EF7DTUVF.js.map +1 -0
  43. package/dist/shop-minis-react/node_modules/.pnpm/thumbhash@0.1.1/node_modules/thumbhash/thumbhash.js +145 -0
  44. package/dist/shop-minis-react/node_modules/.pnpm/thumbhash@0.1.1/node_modules/thumbhash/thumbhash.js.map +1 -0
  45. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js +68 -0
  46. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js.map +1 -0
  47. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js +56 -0
  48. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js.map +1 -0
  49. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +11 -0
  50. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js.map +1 -0
  51. package/dist/types/index.js +10 -0
  52. package/dist/types/index.js.map +1 -0
  53. package/dist/utils/image.js +15 -0
  54. package/dist/utils/image.js.map +1 -0
  55. package/package.json +13 -3
  56. package/src/components/atoms/alert-dialog.tsx +67 -0
  57. package/src/components/atoms/thumbhash-image.tsx +66 -0
  58. package/src/components/commerce/merchant-card-skeleton.tsx +31 -0
  59. package/src/components/commerce/merchant-card.tsx +5 -2
  60. package/src/components/commerce/product-card-skeleton.tsx +30 -0
  61. package/src/components/commerce/product-card.tsx +49 -8
  62. package/src/components/index.ts +9 -0
  63. package/src/components/navigation/transition-container.tsx +7 -0
  64. package/src/components/navigation/transition-link.tsx +48 -0
  65. package/src/components/ui/skeleton.tsx +13 -0
  66. package/src/hooks/index.ts +1 -0
  67. package/src/hooks/navigation/useNavigateWithTransition.ts +62 -0
  68. package/src/hooks/navigation/useViewTransitions.ts +79 -0
  69. package/src/index.css +1 -0
  70. package/src/mocks.ts +8 -2
  71. package/src/stories/Accordion.stories.tsx +124 -0
  72. package/src/stories/Alert.stories.tsx +38 -0
  73. package/src/stories/AlertDialog.stories.tsx +48 -0
  74. package/src/stories/Avatar.stories.tsx +29 -0
  75. package/src/stories/Badge.stories.tsx +46 -0
  76. package/src/stories/Button.stories.tsx +81 -0
  77. package/src/stories/Card.stories.tsx +40 -0
  78. package/src/stories/Checkbox.stories.tsx +44 -0
  79. package/src/stories/FavoriteButton.stories.tsx +58 -0
  80. package/src/stories/IconButton.stories.tsx +68 -0
  81. package/src/stories/Input.stories.tsx +44 -0
  82. package/src/stories/Label.stories.tsx +19 -0
  83. package/src/stories/MerchantCard.stories.tsx +55 -0
  84. package/src/stories/ProductCard.stories.tsx +85 -0
  85. package/src/stories/ProductLink.stories.tsx +46 -0
  86. package/src/stories/Progress.stories.tsx +30 -0
  87. package/src/stories/RadioGroup.stories.tsx +51 -0
  88. package/src/stories/Select.stories.tsx +85 -0
  89. package/src/stories/Skeleton.stories.tsx +19 -0
  90. package/src/stories/Toaster.stories.tsx +46 -0
  91. package/src/stories/Touchable.stories.tsx +40 -0
  92. package/src/styles/animations.css +90 -0
  93. package/src/styles/globals.css +8 -0
  94. package/src/styles/theme.css +1 -1
  95. package/src/types/index.ts +7 -1
  96. package/src/utils/image.ts +18 -0
@@ -0,0 +1,30 @@
1
+ import * as React from 'react'
2
+
3
+ import {cn} from '../../lib/utils'
4
+ import {Skeleton} from '../ui/skeleton'
5
+
6
+ interface ProductCardSkeletonProps extends React.ComponentProps<'div'> {
7
+ variant?: 'default' | 'priceOverlay' | 'compact'
8
+ }
9
+
10
+ function ProductCardSkeleton({
11
+ className,
12
+ variant = 'default',
13
+ ...props
14
+ }: ProductCardSkeletonProps) {
15
+ return (
16
+ <div className={cn('relative w-full', className)} {...props}>
17
+ <div className="aspect-square w-full overflow-hidden rounded-xl border border-gray-100">
18
+ <Skeleton className="h-full w-full" />
19
+ </div>
20
+ {variant === 'default' ? (
21
+ <div className="mt-3 mb-3">
22
+ <Skeleton className="mb-2 h-4 w-2/3" />
23
+ <Skeleton className="mb-2 h-5 w-1/3" />
24
+ </div>
25
+ ) : null}
26
+ </div>
27
+ )
28
+ }
29
+
30
+ export {ProductCardSkeleton}
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react'
2
+ import {useCallback} from 'react'
2
3
 
3
4
  import {type Product, type ProductVariant} from '@shopify/shop-minis-platform'
4
5
  import {cva, type VariantProps} from 'class-variance-authority'
5
- import {Heart} from 'lucide-react'
6
6
  import {Slot as SlotPrimitive} from 'radix-ui'
7
7
 
8
8
  import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
@@ -10,6 +10,7 @@ import {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'
10
10
  import {formatMoney} from '../../lib/formatMoney'
11
11
  import {cn} from '../../lib/utils'
12
12
  import {FavoriteButton} from '../atoms/favorite-button'
13
+ import {ThumbhashImage} from '../atoms/thumbhash-image'
13
14
  import {Touchable} from '../atoms/touchable'
14
15
  import {Badge} from '../ui/badge'
15
16
 
@@ -96,7 +97,7 @@ function ProductCardImageContainer({
96
97
  // Ensure the product image is stretched to the full size of the container (can't use width/height: 100% because of flex)
97
98
  'flex justify-stretch items-stretch',
98
99
  'relative overflow-hidden rounded-xl border border-gray-200',
99
- 'w-full aspect-square',
100
+ 'w-full aspect-square',
100
101
  variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',
101
102
  className
102
103
  )}
@@ -109,23 +110,50 @@ function ProductCardImage({
109
110
  className,
110
111
  src,
111
112
  alt,
113
+ aspectRatio,
114
+ thumbhash,
112
115
  ...props
113
116
  }: React.ComponentProps<'img'> & {
114
117
  src?: string
115
118
  alt?: string
119
+ aspectRatio?: number
120
+ thumbhash?: string
116
121
  }) {
117
- return (
118
- <div className="bg-gray-100 flex items-center justify-center">
119
- {src ? (
122
+ const renderImageElement = useCallback(
123
+ (src: string) => {
124
+ if (thumbhash) {
125
+ return (
126
+ <ThumbhashImage
127
+ data-slot="product-card-image"
128
+ src={src}
129
+ alt={alt}
130
+ aspectRatio={aspectRatio}
131
+ thumbhash={thumbhash}
132
+ className={cn('w-full h-full object-cover', className)}
133
+ {...props}
134
+ />
135
+ )
136
+ }
137
+
138
+ return (
120
139
  <img
121
140
  data-slot="product-card-image"
122
141
  src={src}
123
142
  alt={alt}
124
- className={cn('w-full h-full object-cover', className)}
143
+ className={cn('w-full h-full', className)}
125
144
  {...props}
126
145
  />
146
+ )
147
+ },
148
+ [alt, aspectRatio, className, props, thumbhash]
149
+ )
150
+
151
+ return (
152
+ <div className="bg-gray-100 flex items-center justify-center w-full h-full">
153
+ {src ? (
154
+ renderImageElement(src)
127
155
  ) : (
128
- <div className="text-gray-400 text-sm">No Image</div>
156
+ <div className="text-gray-400 text-sm w-full text-center">No Image</div>
129
157
  )}
130
158
  </div>
131
159
  )
@@ -249,13 +277,21 @@ function ProductCardOriginalPrice({
249
277
  }
250
278
 
251
279
  export interface ProductCardProps {
280
+ /** The product to display in the card */
252
281
  product: Product
282
+ /** Optional selected variant of the product to show specific variant data */
253
283
  selectedProductVariant?: ProductVariant
284
+ /** Visual style variant of the card */
254
285
  variant?: 'default' | 'priceOverlay' | 'compact'
286
+ /** Whether the card can be clicked/tapped to navigate to product details */
255
287
  touchable?: boolean
288
+ /** Optional text to display in a badge on the card */
256
289
  badgeText?: string
290
+ /** Visual style variant for the badge */
257
291
  badgeVariant?: 'default' | 'secondary' | 'destructive' | 'outline'
292
+ /** Callback fired when the favorite button is toggled */
258
293
  onFavoriteToggled?: (isFavorited: boolean) => void
294
+ /** Optional ID for the section containing this card */
259
295
  sectionId?: string
260
296
  }
261
297
 
@@ -351,7 +387,12 @@ function ProductCard({
351
387
  onPress={handlePress}
352
388
  >
353
389
  <ProductCardImageContainer variant={variant}>
354
- <ProductCardImage src={imageUrl} alt={imageAltText} />
390
+ <ProductCardImage
391
+ src={imageUrl}
392
+ alt={imageAltText}
393
+ aspectRatio={1}
394
+ thumbhash={featuredImage?.thumbhash ?? undefined}
395
+ />
355
396
 
356
397
  {/* Price overlay badge for priceOverlay variant */}
357
398
  {variant === 'priceOverlay' && currencyCode && amount && (
@@ -3,15 +3,23 @@ export * from './MinisContainer'
3
3
  export * from './commerce/product-card'
4
4
  export * from './commerce/product-link'
5
5
  export * from './commerce/merchant-card'
6
+ export * from './commerce/product-card-skeleton'
7
+ export * from './commerce/merchant-card-skeleton'
8
+
9
+ export * from './navigation/transition-container'
10
+ export * from './navigation/transition-link'
6
11
 
7
12
  export * from './atoms/button'
8
13
  export * from './atoms/favorite-button'
9
14
  export * from './atoms/icon-button'
15
+ export * from './atoms/thumbhash-image'
10
16
  export * from './atoms/touchable'
17
+ export * from './atoms/alert-dialog'
11
18
 
12
19
  export * from './ui/accordion'
13
20
  export * from './ui/alert'
14
21
  export * from './ui/alert-dialog'
22
+ export * from './ui/avatar'
15
23
  export * from './ui/card'
16
24
  export * from './ui/carousel'
17
25
  export * from './ui/checkbox'
@@ -27,3 +35,4 @@ export * from './ui/select'
27
35
  export * from './ui/separator'
28
36
  export * from './ui/sheet'
29
37
  export * from './ui/sonner'
38
+ export * from './ui/skeleton'
@@ -0,0 +1,7 @@
1
+ import {useViewTransitions} from '../../hooks/navigation/useViewTransitions'
2
+
3
+ export function TransitionContainer({children}: {children: React.ReactNode}) {
4
+ useViewTransitions()
5
+
6
+ return children
7
+ }
@@ -0,0 +1,48 @@
1
+ import {forwardRef, AnchorHTMLAttributes} from 'react'
2
+
3
+ import {useHref} from 'react-router'
4
+
5
+ import {useNavigateWithTransition} from '../../hooks/navigation/useNavigateWithTransition'
6
+
7
+ type TransitionLinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
8
+ to: string
9
+ }
10
+
11
+ const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i
12
+
13
+ export const TransitionLink = forwardRef<
14
+ HTMLAnchorElement,
15
+ TransitionLinkProps
16
+ >(({onClick, to, children, ...props}, forwardedRef) => {
17
+ const transitionNavigate = useNavigateWithTransition()
18
+
19
+ const isAbsolute = typeof to === 'string' && ABSOLUTE_URL_REGEX.test(to)
20
+
21
+ if (isAbsolute) {
22
+ console.warn(
23
+ `TransitionLink: absolute URLs are not supported. Please update to a valid relative path.`
24
+ )
25
+ }
26
+
27
+ const href = useHref(to)
28
+
29
+ const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
30
+ if (onClick) onClick(event)
31
+
32
+ if (!event.defaultPrevented) {
33
+ event.preventDefault()
34
+ transitionNavigate(to)
35
+ }
36
+ }
37
+
38
+ return (
39
+ <a
40
+ {...props}
41
+ onClick={handleClick}
42
+ href={isAbsolute ? undefined : href}
43
+ ref={forwardedRef}
44
+ >
45
+ {children}
46
+ </a>
47
+ )
48
+ })
@@ -0,0 +1,13 @@
1
+ import {cn} from '../../lib/utils'
2
+
3
+ function Skeleton({className, ...props}: React.ComponentProps<'div'>) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn('bg-accent animate-pulse rounded-md', className)}
8
+ {...props}
9
+ />
10
+ )
11
+ }
12
+
13
+ export {Skeleton}
@@ -31,6 +31,7 @@ export * from './storage/useImageUpload'
31
31
  export * from './navigation/useShopNavigation'
32
32
  export * from './navigation/useCloseMini'
33
33
  export * from './navigation/useDeeplink'
34
+ export * from './navigation/useNavigateWithTransition'
34
35
 
35
36
  // - Shop Hooks
36
37
  export * from './shop/useShop'
@@ -0,0 +1,62 @@
1
+ import {useLocation, useNavigate, NavigateOptions} from 'react-router'
2
+
3
+ import {DATA_NAVIGATION_TYPE_ATTRIBUTE} from '../../types'
4
+
5
+ export function useNavigateWithTransition() {
6
+ const navigate = useNavigate()
7
+ const location = useLocation()
8
+
9
+ const transitionNavigate = (
10
+ to: string | number,
11
+ options?: NavigateOptions
12
+ ) => {
13
+ if (typeof to === 'number') {
14
+ // Delta navigation - no options parameter
15
+ if (document.startViewTransition) {
16
+ const transition = document.startViewTransition(() => {
17
+ navigate(to)
18
+ })
19
+
20
+ transition.finished
21
+ .then(() => {
22
+ document.documentElement.removeAttribute(
23
+ DATA_NAVIGATION_TYPE_ATTRIBUTE
24
+ )
25
+ })
26
+ .catch(error => {
27
+ console.error('View transition error:', error)
28
+ })
29
+ } else {
30
+ return navigate(to)
31
+ }
32
+ return
33
+ }
34
+
35
+ const isSameRoute = to === location.pathname
36
+
37
+ // Path navigation - with options
38
+ if (document.startViewTransition) {
39
+ const transition = document.startViewTransition(() => {
40
+ navigate(to, {
41
+ preventScrollReset: true,
42
+ replace: isSameRoute,
43
+ ...options,
44
+ })
45
+ })
46
+
47
+ transition.finished
48
+ .then(() => {
49
+ document.documentElement.removeAttribute(
50
+ DATA_NAVIGATION_TYPE_ATTRIBUTE
51
+ )
52
+ })
53
+ .catch(error => {
54
+ console.error('View transition error:', error)
55
+ })
56
+ } else {
57
+ return navigate(to, options)
58
+ }
59
+ }
60
+
61
+ return transitionNavigate
62
+ }
@@ -0,0 +1,79 @@
1
+ import {useEffect} from 'react'
2
+
3
+ import {useNavigationType, useLocation} from 'react-router'
4
+
5
+ import {DATA_NAVIGATION_TYPE_ATTRIBUTE, NAVIGATION_TYPES} from '../../types'
6
+
7
+ export function useViewTransitions() {
8
+ const location = useLocation()
9
+ const navType = useNavigationType()
10
+
11
+ useEffect(() => {
12
+ let isAndroidBackPress = false
13
+
14
+ const handleAndroidBackPress = () => {
15
+ isAndroidBackPress = true
16
+
17
+ if (document.startViewTransition) {
18
+ const transition = document.startViewTransition(() => {
19
+ document.documentElement.setAttribute(
20
+ DATA_NAVIGATION_TYPE_ATTRIBUTE,
21
+ NAVIGATION_TYPES.backward
22
+ )
23
+ })
24
+
25
+ transition.finished
26
+ .then(() => {
27
+ document.documentElement.removeAttribute(
28
+ DATA_NAVIGATION_TYPE_ATTRIBUTE
29
+ )
30
+ })
31
+ .catch(error => {
32
+ console.error('View transition error:', error)
33
+ })
34
+ }
35
+ }
36
+
37
+ const handlePopstate = (event: PopStateEvent) => {
38
+ // If the ios back gesture is used, we don't want to trigger view transition
39
+ if (event.hasUAVisualTransition && !isAndroidBackPress) {
40
+ document.documentElement.setAttribute(
41
+ DATA_NAVIGATION_TYPE_ATTRIBUTE,
42
+ NAVIGATION_TYPES.none
43
+ )
44
+ }
45
+ }
46
+
47
+ window.addEventListener('androidbackpressed', handleAndroidBackPress)
48
+ window.addEventListener('popstate', handlePopstate)
49
+
50
+ return () => {
51
+ window.removeEventListener('popstate', handlePopstate)
52
+ window.removeEventListener('androidbackpressed', handleAndroidBackPress)
53
+ }
54
+ }, [location])
55
+
56
+ useEffect(() => {
57
+ const currentNavType = document.documentElement.getAttribute(
58
+ DATA_NAVIGATION_TYPE_ATTRIBUTE
59
+ )
60
+
61
+ if (!currentNavType) {
62
+ if (navType === 'PUSH') {
63
+ document.documentElement.setAttribute(
64
+ DATA_NAVIGATION_TYPE_ATTRIBUTE,
65
+ NAVIGATION_TYPES.forward
66
+ )
67
+ } else if (navType === 'POP') {
68
+ document.documentElement.setAttribute(
69
+ DATA_NAVIGATION_TYPE_ATTRIBUTE,
70
+ NAVIGATION_TYPES.backward
71
+ )
72
+ }
73
+ }
74
+
75
+ return () => {
76
+ document.documentElement.removeAttribute(DATA_NAVIGATION_TYPE_ATTRIBUTE)
77
+ }
78
+ }, [navType, location])
79
+ }
package/src/index.css CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  @import './styles/theme.css';
5
5
  @import './styles/globals.css';
6
+ @import './styles/animations.css';
6
7
 
7
8
  /* When consumed as npm package, this path resolves from the consuming app's location */
8
9
  @source '../node_modules/@shopify/shop-minis-react/src';
package/src/mocks.ts CHANGED
@@ -2,7 +2,7 @@ import {Product, Gender} from '@shopify/shop-minis-platform'
2
2
  import {ShopActions} from '@shopify/shop-minis-platform/actions'
3
3
 
4
4
  // Helper functions for common data structures
5
- const createProduct = (
5
+ export const createProduct = (
6
6
  id: string,
7
7
  title: string,
8
8
  price = '99.99',
@@ -21,7 +21,7 @@ const createProduct = (
21
21
  featuredImage: {url: `https://picsum.photos/400/400`, altText: title},
22
22
  })
23
23
 
24
- const createShop = (id: string, name: string) => ({
24
+ export const createShop = (id: string, name: string) => ({
25
25
  id,
26
26
  name,
27
27
  isFollowing: false,
@@ -29,6 +29,11 @@ const createShop = (id: string, name: string) => ({
29
29
  url: `https://${name.toLowerCase().replace(/\s+/g, '-')}.com`,
30
30
  },
31
31
  reviewAnalytics: {averageRating: 4.3, reviewCount: 50},
32
+ visualTheme: {
33
+ id: 'visual-theme-1',
34
+ featuredImages: [{url: `https://picsum.photos/400/400`, sensitive: false}],
35
+ logoImage: {url: `https://picsum.photos/100/400`, sensitive: false},
36
+ },
32
37
  })
33
38
 
34
39
  const createPagination = (hasNext = false) => ({
@@ -288,6 +293,7 @@ export const injectMocks = () => {
288
293
  window.minisParams = {
289
294
  handle: 'mock-handle',
290
295
  initialUrl: '/mock-initial-url',
296
+ platform: 'ios',
291
297
  }
292
298
  }
293
299
 
@@ -0,0 +1,124 @@
1
+ import {
2
+ Accordion,
3
+ AccordionContent,
4
+ AccordionItem,
5
+ AccordionTrigger,
6
+ } from '../components/ui/accordion'
7
+
8
+ import type {Meta, StoryObj} from '@storybook/react-vite'
9
+
10
+ const meta = {
11
+ title: 'UI/Accordion',
12
+ component: Accordion,
13
+ parameters: {},
14
+ args: {
15
+ type: 'single',
16
+ collapsible: false,
17
+ },
18
+ tags: ['autodocs'],
19
+ } satisfies Meta<typeof Accordion>
20
+
21
+ export default meta
22
+ type Story = StoryObj<typeof meta>
23
+
24
+ export const NonCollapsible: Story = {
25
+ args: {
26
+ type: 'single',
27
+ collapsible: false,
28
+ },
29
+ render: args => (
30
+ <Accordion {...args}>
31
+ <AccordionItem value="item-2">
32
+ <AccordionTrigger>Can i collapse this?</AccordionTrigger>
33
+ <AccordionContent>
34
+ No. When you set the collapsible prop to false, the accordion is not
35
+ collapsible.
36
+ </AccordionContent>
37
+ </AccordionItem>
38
+ </Accordion>
39
+ ),
40
+ }
41
+
42
+ export const Collapsible: Story = {
43
+ args: {
44
+ type: 'single',
45
+ collapsible: true,
46
+ },
47
+ render: args => (
48
+ <Accordion {...args}>
49
+ <AccordionItem value="item-2">
50
+ <AccordionTrigger>Can i collapse this?</AccordionTrigger>
51
+ <AccordionContent>
52
+ Yes. When you set the collapsible prop to true, you can collapse the
53
+ </AccordionContent>
54
+ </AccordionItem>
55
+ </Accordion>
56
+ ),
57
+ }
58
+
59
+ export const Single: Story = {
60
+ args: {
61
+ type: 'single',
62
+ },
63
+ render: args => (
64
+ <Accordion {...args}>
65
+ <AccordionItem value="item-1">
66
+ <AccordionTrigger>Single item only</AccordionTrigger>
67
+ <AccordionContent>
68
+ With the single type, only one item can be open at a time.
69
+ </AccordionContent>
70
+ </AccordionItem>
71
+ <AccordionItem value="item-2">
72
+ <AccordionTrigger>Another item</AccordionTrigger>
73
+ <AccordionContent>
74
+ Opening this item will close the previous one.
75
+ </AccordionContent>
76
+ </AccordionItem>
77
+ </Accordion>
78
+ ),
79
+ }
80
+
81
+ export const Multiple: Story = {
82
+ args: {
83
+ type: 'multiple',
84
+ },
85
+ render: args => (
86
+ <Accordion {...args}>
87
+ <AccordionItem value="item-1">
88
+ <AccordionTrigger>Can I open multiple items?</AccordionTrigger>
89
+ <AccordionContent>
90
+ Yes, you can open multiple items at the same time with the multiple
91
+ type.
92
+ </AccordionContent>
93
+ </AccordionItem>
94
+ <AccordionItem value="item-2">
95
+ <AccordionTrigger>Is it customizable?</AccordionTrigger>
96
+ <AccordionContent>
97
+ Yes, you can customize the styling and behavior to match your needs.
98
+ </AccordionContent>
99
+ </AccordionItem>
100
+ <AccordionItem value="item-1">
101
+ <AccordionTrigger>Long content example</AccordionTrigger>
102
+ <AccordionContent>
103
+ <div className="space-y-4">
104
+ <p>
105
+ This is a longer content example that demonstrates how the
106
+ accordion handles substantial amounts of text and content.
107
+ </p>
108
+ <p>
109
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
110
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
111
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
112
+ nisi ut aliquip ex ea commodo consequat.
113
+ </p>
114
+ <ul className="list-disc list-inside space-y-1">
115
+ <li>First item in the list</li>
116
+ <li>Second item in the list</li>
117
+ <li>Third item in the list</li>
118
+ </ul>
119
+ </div>
120
+ </AccordionContent>
121
+ </AccordionItem>
122
+ </Accordion>
123
+ ),
124
+ }
@@ -0,0 +1,38 @@
1
+ import {Alert, AlertTitle, AlertDescription} from '../components/ui/alert'
2
+
3
+ import type {Meta, StoryObj} from '@storybook/react-vite'
4
+
5
+ const meta = {
6
+ title: 'UI/Alert',
7
+ render: ({variant}) => (
8
+ <Alert variant={variant}>
9
+ <AlertTitle>Alert Title</AlertTitle>
10
+ <AlertDescription>
11
+ This is a default alert with a title and description.
12
+ </AlertDescription>
13
+ </Alert>
14
+ ),
15
+
16
+ parameters: {},
17
+ tags: ['autodocs'],
18
+ argTypes: {
19
+ variant: {
20
+ control: 'radio',
21
+ options: ['default', 'destructive'],
22
+ },
23
+ },
24
+ } satisfies Meta<typeof Alert>
25
+
26
+ export default meta
27
+ type Story = StoryObj<typeof meta>
28
+
29
+ export const Default: Story = {
30
+ args: {
31
+ variant: 'default',
32
+ },
33
+ }
34
+ export const Destructive: Story = {
35
+ args: {
36
+ variant: 'destructive',
37
+ },
38
+ }
@@ -0,0 +1,48 @@
1
+ import {fn} from 'storybook/test'
2
+
3
+ import {
4
+ AlertDialog,
5
+ AlertDialogAction,
6
+ AlertDialogCancel,
7
+ AlertDialogContent,
8
+ AlertDialogDescription,
9
+ AlertDialogFooter,
10
+ AlertDialogHeader,
11
+ AlertDialogTitle,
12
+ AlertDialogTrigger,
13
+ Button,
14
+ } from '../components'
15
+
16
+ import type {Meta, StoryObj} from '@storybook/react-vite'
17
+
18
+ const meta = {
19
+ title: 'Ui/AlertDialog',
20
+ render: () => (
21
+ <AlertDialog>
22
+ <AlertDialogTrigger asChild>
23
+ <Button variant="outline">Open Alert Dialog</Button>
24
+ </AlertDialogTrigger>
25
+ <AlertDialogContent>
26
+ <AlertDialogHeader>
27
+ <AlertDialogTitle>Are you sure?</AlertDialogTitle>
28
+ <AlertDialogDescription>
29
+ This action cannot be undone.
30
+ </AlertDialogDescription>
31
+ </AlertDialogHeader>
32
+ <AlertDialogFooter>
33
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
34
+ <AlertDialogAction onClick={() => fn()}>Continue</AlertDialogAction>
35
+ </AlertDialogFooter>
36
+ </AlertDialogContent>
37
+ </AlertDialog>
38
+ ),
39
+ parameters: {},
40
+ tags: ['autodocs'],
41
+ argTypes: {},
42
+ args: {},
43
+ } satisfies Meta<typeof AlertDialog>
44
+
45
+ export default meta
46
+ type Story = StoryObj<typeof meta>
47
+
48
+ export const Default: Story = {}
@@ -0,0 +1,29 @@
1
+ import {Avatar, AvatarFallback, AvatarImage} from '../components/ui/avatar'
2
+
3
+ import type {StoryObj} from '@storybook/react-vite'
4
+
5
+ interface AvatarProps {
6
+ src: 'https://github.com/shadcn.png'
7
+ userInitials: string
8
+ }
9
+
10
+ const meta = {
11
+ title: 'UI/Avatar',
12
+ render: ({userInitials}: AvatarProps) => (
13
+ <Avatar>
14
+ <AvatarImage src="https://github.com/shadcn.png" />
15
+ <AvatarFallback>{userInitials}</AvatarFallback>
16
+ </Avatar>
17
+ ),
18
+ args: {
19
+ className: 'size-12',
20
+ src: 'https://github.com/shadcn.png',
21
+ userInitials: 'CN',
22
+ },
23
+ tags: ['autodocs'],
24
+ }
25
+
26
+ export default meta
27
+ type Story = StoryObj<typeof meta>
28
+
29
+ export const Default: Story = {}