@shopify/shop-minis-react 0.0.0-snapshot.20251211183509 → 0.0.0-snapshot.20251212144430
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.
- package/dist/components/commerce/merchant-card.js +1 -1
- package/dist/components/commerce/merchant-card.js.map +1 -1
- package/dist/components/commerce/product-card.js +1 -1
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/mocks.js +124 -90
- package/dist/mocks.js.map +1 -1
- package/eslint/config.cjs +0 -2
- package/eslint/index.cjs +0 -4
- package/package.json +2 -2
- package/src/components/commerce/merchant-card.tsx +1 -1
- package/src/components/commerce/product-card.tsx +1 -1
- package/src/mocks.ts +39 -5
- package/eslint/rules/asset-path-patterns.cjs +0 -30
- package/eslint/rules/no-dynamic-asset-paths.cjs +0 -78
- package/eslint/rules/no-hardcoded-asset-paths.cjs +0 -124
|
@@ -29,7 +29,7 @@ function B({
|
|
|
29
29
|
backgroundColor: n.backgroundColor
|
|
30
30
|
},
|
|
31
31
|
className: c(
|
|
32
|
-
"relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square",
|
|
32
|
+
"relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square isolate",
|
|
33
33
|
a
|
|
34
34
|
),
|
|
35
35
|
...r
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merchant-card.js","sources":["../../../src/components/commerce/merchant-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useCallback, useContext, useMemo} from 'react'\n\nimport {type Shop} from '@shopify/shop-minis-platform'\nimport {Star} from 'lucide-react'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {cn} from '../../lib/utils'\nimport {\n type ExtractedBrandTheme,\n extractBrandTheme,\n formatReviewCount,\n getFeaturedImages,\n normalizeRating,\n} from '../../utils'\nimport {isDarkColor} from '../../utils/colors'\nimport {Image} from '../atoms/image'\nimport {Touchable} from '../atoms/touchable'\n\ninterface MerchantCardContextValue {\n // Core data\n shop: Shop\n\n // Derived data\n cardTheme: ExtractedBrandTheme\n\n // UI configuration\n touchable: boolean\n featuredImagesLimit: number\n\n // Actions\n onClick: () => void\n}\n\nconst MerchantCardContext = createContext<MerchantCardContextValue | undefined>(\n undefined\n)\n\nfunction useMerchantCardContext() {\n const context = useContext(MerchantCardContext)\n if (!context) {\n throw new Error(\n 'useMerchantCardContext must be used within a MerchantCardProvider'\n )\n }\n return context\n}\n\nfunction MerchantCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, cardTheme, onClick} = useMerchantCardContext()\n\n const content = (\n <div\n style={{\n backgroundColor: cardTheme.backgroundColor,\n }}\n className={cn(\n 'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square',\n\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction MerchantCardImage({\n className,\n src,\n alt,\n thumbhash,\n ...props\n}: React.ComponentProps<'img'> & {\n src?: string\n alt?: string\n thumbhash?: string\n}) {\n if (!src) {\n return <div className=\"w-full h-full bg-gray-100\" />\n }\n\n if (thumbhash) {\n return (\n <Image\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n thumbhash={thumbhash}\n className={cn(className)}\n {...props}\n />\n )\n }\n\n return (\n <img\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n}\n\nfunction MerchantCardLogo({className, ...props}: React.ComponentProps<'div'>) {\n const {shop} = useMerchantCardContext()\n const {name, visualTheme} = shop\n\n const logoAverageColor = visualTheme?.brandSettings?.colors?.logoAverage\n const logoDominantColor = visualTheme?.brandSettings?.colors?.logoDominant\n const logoColor = logoAverageColor || logoDominantColor\n\n const logoBackgroundClassName = useMemo(\n () => (logoColor && isDarkColor(logoColor) ? 'bg-white' : 'bg-gray-800'),\n [logoColor]\n )\n\n const logoUrl = visualTheme?.logoImage?.url\n const altText = `${name} logo`\n\n return (\n <div\n data-slot=\"merchant-card-logo\"\n className={cn(\n 'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10',\n 'w-16 h-16 rounded-xl bg-white border-2 border-white shadow-sm',\n 'flex items-center justify-center overflow-hidden',\n logoBackgroundClassName,\n className\n )}\n {...props}\n >\n {logoUrl ? (\n <img\n src={logoUrl}\n alt={altText}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"w-full h-full bg-gray-200 flex items-center justify-center\">\n <span className=\"text-gray-600 font-semibold text-lg\">\n {name?.slice(0, 1)}\n </span>\n </div>\n )}\n </div>\n )\n}\n\nfunction MerchantCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {cardTheme} = useMerchantCardContext()\n\n const isDarkTheme = useMemo(() => {\n return (\n cardTheme.backgroundColor !== 'white' &&\n isDarkColor(cardTheme.backgroundColor)\n )\n }, [cardTheme.backgroundColor])\n\n const textColor = isDarkTheme ? 'text-primary-foreground' : 'text-foreground'\n\n return (\n <div\n data-slot=\"merchant-card-info\"\n className={cn(\n 'p-3 flex-shrink-0 flex flex-col min-w-0',\n textColor,\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MerchantCardName({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {shop} = useMerchantCardContext()\n const {name} = shop\n const nameContent = children ?? name\n\n return (\n <h3\n data-slot=\"merchant-card-name\"\n className={cn('text-sm font-medium truncate', className)}\n {...props}\n >\n {nameContent}\n </h3>\n )\n}\n\nfunction MerchantCardRating({\n className,\n\n ...props\n}: React.ComponentProps<'div'> & {\n rating?: number | null\n reviewCount?: number\n}) {\n const {shop} = useMerchantCardContext()\n\n const {\n reviewAnalytics: {averageRating, reviewCount},\n } = shop\n\n if (!averageRating || !reviewCount) return null\n\n return (\n <div\n data-slot=\"merchant-card-rating\"\n className={cn('flex items-center gap-1 text-xs', className)}\n {...props}\n >\n <Star className=\"h-3 w-3 fill-current\" />\n <span className=\"text-xs\">\n {normalizeRating(averageRating)} ({formatReviewCount(reviewCount)})\n </span>\n </div>\n )\n}\n\nfunction MerchantCardDefaultHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme, featuredImagesLimit} = useMerchantCardContext()\n const {visualTheme} = shop\n\n const featuredImages = useMemo(\n () => getFeaturedImages(visualTheme, featuredImagesLimit),\n [visualTheme, featuredImagesLimit]\n )\n\n const numberOfFeaturedImages = featuredImages?.length ?? 0\n\n const displayDefaultCover = () => {\n if (numberOfFeaturedImages > 0) {\n const heightClass = numberOfFeaturedImages === 2 ? 'h-full' : 'h-1/2'\n return featuredImages?.map((image, index) => (\n <div className={`z-0 w-1/2 ${heightClass}`} key={image.url || index}>\n <MerchantCardImage\n src={image.url}\n alt={image.altText ?? undefined}\n thumbhash={image.thumbhash ?? undefined}\n className=\"aspect-square\"\n />\n </div>\n ))\n } else if (cardTheme.type === 'coverImage') {\n return (\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n thumbhash={cardTheme.coverImageThumbhash}\n />\n )\n }\n\n return null\n }\n\n return (\n <div className=\"w-full h-full bg-muted relative flex flex-wrap overflow-hidden\">\n {withLogo && (\n <div className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10\">\n <MerchantCardLogo />\n </div>\n )}\n\n {displayDefaultCover()}\n </div>\n )\n}\n\nfunction MerchantCardBrandedHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme} = useMerchantCardContext()\n const wordmarkImage = shop.visualTheme?.brandSettings?.headerTheme?.wordmark\n\n return (\n <div className=\"size-full relative\">\n {cardTheme.type === 'coverImage' && (\n <>\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n alt={shop.name}\n thumbhash={cardTheme.coverImageThumbhash ?? undefined}\n />\n\n <div className=\"absolute inset-0 z-[1] bg-black/20\" />\n\n <div\n className=\"absolute bottom-0 z-[1] size-full\"\n style={{\n background: `linear-gradient(to top, ${cardTheme.backgroundColor} 0%, ${cardTheme.backgroundColor}00 40%)`,\n }}\n />\n </>\n )}\n\n {withLogo && (\n <div className=\"absolute inset-0 z-[1] flex items-center justify-center\">\n {wordmarkImage ? (\n <img\n src={wordmarkImage.url}\n alt={wordmarkImage.altText || shop.name}\n className=\"max-h-16 min-h-10 max-w-28 object-contain\"\n data-testid=\"store-data-wordmark\"\n />\n ) : (\n <MerchantCardLogo />\n )}\n </div>\n )}\n </div>\n )\n}\n\ninterface MerchantCardHeaderProps {\n isDefault?: boolean\n withLogo?: boolean\n}\n\nfunction MerchantCardHeader({\n isDefault,\n withLogo,\n className,\n ...props\n}: React.ComponentProps<'div'> & MerchantCardHeaderProps) {\n const {cardTheme} = useMerchantCardContext()\n\n const isBranded =\n cardTheme.type === 'coverImage' || cardTheme.type === 'brandColor'\n\n return (\n <div\n className={cn('relative overflow-hidden flex-1 flex-wrap', className)}\n {...props}\n >\n {isBranded && !isDefault ? (\n <MerchantCardBrandedHeader withLogo={withLogo} />\n ) : (\n <MerchantCardDefaultHeader withLogo={withLogo} />\n )}\n </div>\n )\n}\n\nexport interface MerchantCardProps {\n shop: Shop\n touchable?: boolean\n featuredImagesLimit?: number\n children?: React.ReactNode\n}\n\nfunction MerchantCard({\n shop,\n touchable = true,\n featuredImagesLimit = 4,\n children,\n}: MerchantCardProps) {\n const {navigateToShop} = useShopNavigation()\n\n const {id, visualTheme} = shop\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n navigateToShop({shopId: id})\n }, [navigateToShop, id, touchable])\n\n const cardTheme = useMemo(\n () => extractBrandTheme(visualTheme?.brandSettings),\n [visualTheme?.brandSettings]\n )\n\n const contextValue = useMemo<MerchantCardContextValue>(\n () => ({\n shop,\n cardTheme,\n touchable,\n featuredImagesLimit,\n onClick: handleClick,\n }),\n [shop, cardTheme, touchable, featuredImagesLimit, handleClick]\n )\n\n return (\n <MerchantCardContext.Provider value={contextValue}>\n {children ?? (\n <MerchantCardContainer>\n <MerchantCardHeader withLogo />\n <MerchantCardInfo>\n <MerchantCardName />\n <MerchantCardRating />\n </MerchantCardInfo>\n </MerchantCardContainer>\n )}\n </MerchantCardContext.Provider>\n )\n}\n\nexport {\n MerchantCard,\n MerchantCardContainer,\n MerchantCardHeader,\n MerchantCardInfo,\n MerchantCardName,\n MerchantCardRating,\n}\n"],"names":["MerchantCardContext","createContext","useMerchantCardContext","context","useContext","MerchantCardContainer","className","props","touchable","cardTheme","onClick","content","jsx","cn","Touchable","MerchantCardImage","src","alt","thumbhash","Image","MerchantCardLogo","shop","name","visualTheme","logoAverageColor","logoDominantColor","logoColor","logoBackgroundClassName","useMemo","isDarkColor","logoUrl","altText","MerchantCardInfo","textColor","MerchantCardName","children","nameContent","MerchantCardRating","averageRating","reviewCount","jsxs","Star","normalizeRating","formatReviewCount","MerchantCardDefaultHeader","withLogo","featuredImagesLimit","featuredImages","getFeaturedImages","numberOfFeaturedImages","heightClass","image","index","MerchantCardBrandedHeader","wordmarkImage","Fragment","MerchantCardHeader","isDefault","isBranded","MerchantCard","navigateToShop","useShopNavigation","id","handleClick","useCallback","extractBrandTheme","contextValue"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"merchant-card.js","sources":["../../../src/components/commerce/merchant-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useCallback, useContext, useMemo} from 'react'\n\nimport {type Shop} from '@shopify/shop-minis-platform'\nimport {Star} from 'lucide-react'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {cn} from '../../lib/utils'\nimport {\n type ExtractedBrandTheme,\n extractBrandTheme,\n formatReviewCount,\n getFeaturedImages,\n normalizeRating,\n} from '../../utils'\nimport {isDarkColor} from '../../utils/colors'\nimport {Image} from '../atoms/image'\nimport {Touchable} from '../atoms/touchable'\n\ninterface MerchantCardContextValue {\n // Core data\n shop: Shop\n\n // Derived data\n cardTheme: ExtractedBrandTheme\n\n // UI configuration\n touchable: boolean\n featuredImagesLimit: number\n\n // Actions\n onClick: () => void\n}\n\nconst MerchantCardContext = createContext<MerchantCardContextValue | undefined>(\n undefined\n)\n\nfunction useMerchantCardContext() {\n const context = useContext(MerchantCardContext)\n if (!context) {\n throw new Error(\n 'useMerchantCardContext must be used within a MerchantCardProvider'\n )\n }\n return context\n}\n\nfunction MerchantCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, cardTheme, onClick} = useMerchantCardContext()\n\n const content = (\n <div\n style={{\n backgroundColor: cardTheme.backgroundColor,\n }}\n className={cn(\n 'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square isolate',\n\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction MerchantCardImage({\n className,\n src,\n alt,\n thumbhash,\n ...props\n}: React.ComponentProps<'img'> & {\n src?: string\n alt?: string\n thumbhash?: string\n}) {\n if (!src) {\n return <div className=\"w-full h-full bg-gray-100\" />\n }\n\n if (thumbhash) {\n return (\n <Image\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n thumbhash={thumbhash}\n className={cn(className)}\n {...props}\n />\n )\n }\n\n return (\n <img\n data-slot=\"merchant-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n}\n\nfunction MerchantCardLogo({className, ...props}: React.ComponentProps<'div'>) {\n const {shop} = useMerchantCardContext()\n const {name, visualTheme} = shop\n\n const logoAverageColor = visualTheme?.brandSettings?.colors?.logoAverage\n const logoDominantColor = visualTheme?.brandSettings?.colors?.logoDominant\n const logoColor = logoAverageColor || logoDominantColor\n\n const logoBackgroundClassName = useMemo(\n () => (logoColor && isDarkColor(logoColor) ? 'bg-white' : 'bg-gray-800'),\n [logoColor]\n )\n\n const logoUrl = visualTheme?.logoImage?.url\n const altText = `${name} logo`\n\n return (\n <div\n data-slot=\"merchant-card-logo\"\n className={cn(\n 'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10',\n 'w-16 h-16 rounded-xl bg-white border-2 border-white shadow-sm',\n 'flex items-center justify-center overflow-hidden',\n logoBackgroundClassName,\n className\n )}\n {...props}\n >\n {logoUrl ? (\n <img\n src={logoUrl}\n alt={altText}\n className=\"w-full h-full object-cover\"\n />\n ) : (\n <div className=\"w-full h-full bg-gray-200 flex items-center justify-center\">\n <span className=\"text-gray-600 font-semibold text-lg\">\n {name?.slice(0, 1)}\n </span>\n </div>\n )}\n </div>\n )\n}\n\nfunction MerchantCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {cardTheme} = useMerchantCardContext()\n\n const isDarkTheme = useMemo(() => {\n return (\n cardTheme.backgroundColor !== 'white' &&\n isDarkColor(cardTheme.backgroundColor)\n )\n }, [cardTheme.backgroundColor])\n\n const textColor = isDarkTheme ? 'text-primary-foreground' : 'text-foreground'\n\n return (\n <div\n data-slot=\"merchant-card-info\"\n className={cn(\n 'p-3 flex-shrink-0 flex flex-col min-w-0',\n textColor,\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MerchantCardName({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {shop} = useMerchantCardContext()\n const {name} = shop\n const nameContent = children ?? name\n\n return (\n <h3\n data-slot=\"merchant-card-name\"\n className={cn('text-sm font-medium truncate', className)}\n {...props}\n >\n {nameContent}\n </h3>\n )\n}\n\nfunction MerchantCardRating({\n className,\n\n ...props\n}: React.ComponentProps<'div'> & {\n rating?: number | null\n reviewCount?: number\n}) {\n const {shop} = useMerchantCardContext()\n\n const {\n reviewAnalytics: {averageRating, reviewCount},\n } = shop\n\n if (!averageRating || !reviewCount) return null\n\n return (\n <div\n data-slot=\"merchant-card-rating\"\n className={cn('flex items-center gap-1 text-xs', className)}\n {...props}\n >\n <Star className=\"h-3 w-3 fill-current\" />\n <span className=\"text-xs\">\n {normalizeRating(averageRating)} ({formatReviewCount(reviewCount)})\n </span>\n </div>\n )\n}\n\nfunction MerchantCardDefaultHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme, featuredImagesLimit} = useMerchantCardContext()\n const {visualTheme} = shop\n\n const featuredImages = useMemo(\n () => getFeaturedImages(visualTheme, featuredImagesLimit),\n [visualTheme, featuredImagesLimit]\n )\n\n const numberOfFeaturedImages = featuredImages?.length ?? 0\n\n const displayDefaultCover = () => {\n if (numberOfFeaturedImages > 0) {\n const heightClass = numberOfFeaturedImages === 2 ? 'h-full' : 'h-1/2'\n return featuredImages?.map((image, index) => (\n <div className={`z-0 w-1/2 ${heightClass}`} key={image.url || index}>\n <MerchantCardImage\n src={image.url}\n alt={image.altText ?? undefined}\n thumbhash={image.thumbhash ?? undefined}\n className=\"aspect-square\"\n />\n </div>\n ))\n } else if (cardTheme.type === 'coverImage') {\n return (\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n thumbhash={cardTheme.coverImageThumbhash}\n />\n )\n }\n\n return null\n }\n\n return (\n <div className=\"w-full h-full bg-muted relative flex flex-wrap overflow-hidden\">\n {withLogo && (\n <div className=\"absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10\">\n <MerchantCardLogo />\n </div>\n )}\n\n {displayDefaultCover()}\n </div>\n )\n}\n\nfunction MerchantCardBrandedHeader({withLogo = false}: {withLogo?: boolean}) {\n const {shop, cardTheme} = useMerchantCardContext()\n const wordmarkImage = shop.visualTheme?.brandSettings?.headerTheme?.wordmark\n\n return (\n <div className=\"size-full relative\">\n {cardTheme.type === 'coverImage' && (\n <>\n <MerchantCardImage\n src={cardTheme.coverImageUrl}\n alt={shop.name}\n thumbhash={cardTheme.coverImageThumbhash ?? undefined}\n />\n\n <div className=\"absolute inset-0 z-[1] bg-black/20\" />\n\n <div\n className=\"absolute bottom-0 z-[1] size-full\"\n style={{\n background: `linear-gradient(to top, ${cardTheme.backgroundColor} 0%, ${cardTheme.backgroundColor}00 40%)`,\n }}\n />\n </>\n )}\n\n {withLogo && (\n <div className=\"absolute inset-0 z-[1] flex items-center justify-center\">\n {wordmarkImage ? (\n <img\n src={wordmarkImage.url}\n alt={wordmarkImage.altText || shop.name}\n className=\"max-h-16 min-h-10 max-w-28 object-contain\"\n data-testid=\"store-data-wordmark\"\n />\n ) : (\n <MerchantCardLogo />\n )}\n </div>\n )}\n </div>\n )\n}\n\ninterface MerchantCardHeaderProps {\n isDefault?: boolean\n withLogo?: boolean\n}\n\nfunction MerchantCardHeader({\n isDefault,\n withLogo,\n className,\n ...props\n}: React.ComponentProps<'div'> & MerchantCardHeaderProps) {\n const {cardTheme} = useMerchantCardContext()\n\n const isBranded =\n cardTheme.type === 'coverImage' || cardTheme.type === 'brandColor'\n\n return (\n <div\n className={cn('relative overflow-hidden flex-1 flex-wrap', className)}\n {...props}\n >\n {isBranded && !isDefault ? (\n <MerchantCardBrandedHeader withLogo={withLogo} />\n ) : (\n <MerchantCardDefaultHeader withLogo={withLogo} />\n )}\n </div>\n )\n}\n\nexport interface MerchantCardProps {\n shop: Shop\n touchable?: boolean\n featuredImagesLimit?: number\n children?: React.ReactNode\n}\n\nfunction MerchantCard({\n shop,\n touchable = true,\n featuredImagesLimit = 4,\n children,\n}: MerchantCardProps) {\n const {navigateToShop} = useShopNavigation()\n\n const {id, visualTheme} = shop\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n navigateToShop({shopId: id})\n }, [navigateToShop, id, touchable])\n\n const cardTheme = useMemo(\n () => extractBrandTheme(visualTheme?.brandSettings),\n [visualTheme?.brandSettings]\n )\n\n const contextValue = useMemo<MerchantCardContextValue>(\n () => ({\n shop,\n cardTheme,\n touchable,\n featuredImagesLimit,\n onClick: handleClick,\n }),\n [shop, cardTheme, touchable, featuredImagesLimit, handleClick]\n )\n\n return (\n <MerchantCardContext.Provider value={contextValue}>\n {children ?? (\n <MerchantCardContainer>\n <MerchantCardHeader withLogo />\n <MerchantCardInfo>\n <MerchantCardName />\n <MerchantCardRating />\n </MerchantCardInfo>\n </MerchantCardContainer>\n )}\n </MerchantCardContext.Provider>\n )\n}\n\nexport {\n MerchantCard,\n MerchantCardContainer,\n MerchantCardHeader,\n MerchantCardInfo,\n MerchantCardName,\n MerchantCardRating,\n}\n"],"names":["MerchantCardContext","createContext","useMerchantCardContext","context","useContext","MerchantCardContainer","className","props","touchable","cardTheme","onClick","content","jsx","cn","Touchable","MerchantCardImage","src","alt","thumbhash","Image","MerchantCardLogo","shop","name","visualTheme","logoAverageColor","logoDominantColor","logoColor","logoBackgroundClassName","useMemo","isDarkColor","logoUrl","altText","MerchantCardInfo","textColor","MerchantCardName","children","nameContent","MerchantCardRating","averageRating","reviewCount","jsxs","Star","normalizeRating","formatReviewCount","MerchantCardDefaultHeader","withLogo","featuredImagesLimit","featuredImages","getFeaturedImages","numberOfFeaturedImages","heightClass","image","index","MerchantCardBrandedHeader","wordmarkImage","Fragment","MerchantCardHeader","isDefault","isBranded","MerchantCard","navigateToShop","useShopNavigation","id","handleClick","useCallback","extractBrandTheme","contextValue"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-card.js","sources":["../../../src/components/commerce/product-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {useCallback, useContext, useMemo, useState} from 'react'\n\nimport {type Product, type ProductVariant} from '@shopify/shop-minis-platform'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\nimport {FavoriteButton} from './favorite-button'\n\n// Context definition\ninterface ProductCardContextValue {\n // Core data\n product: Product\n selectedProductVariant?: ProductVariant\n\n // UI configuration\n variant: 'default' | 'priceOverlay' | 'compact'\n touchable: boolean\n badgeText?: string\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n favoriteButtonDisabled: boolean\n reviewsDisabled: boolean\n\n // State\n isFavorited: boolean\n\n // Actions\n onClick: () => void\n onFavoriteToggle: () => void\n}\n\nconst ProductCardContext = React.createContext<\n ProductCardContextValue | undefined\n>(undefined)\n\nfunction useProductCardContext() {\n const context = useContext(ProductCardContext)\n if (!context) {\n throw new Error(\n 'ProductCard components must be used within a ProductCard provider'\n )\n }\n return context\n}\n\n// Primitive components (building blocks)\nfunction ProductCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, onClick} = useProductCardContext()\n\n const content = (\n <div\n className={cn(\n 'relative size-full overflow-hidden rounded-xl border-0',\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction ProductCardImageContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n\n return (\n <div\n data-slot=\"product-card-image-container\"\n className={cn(\n 'relative overflow-hidden rounded-xl border border-gray-200',\n 'aspect-square',\n variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive display image locally\n const displayImage = selectedProductVariant?.image || product.featuredImage\n const src = displayImage?.url\n const alt = displayImage?.altText || product.title\n const thumbhash = product.featuredImage?.thumbhash\n\n const renderImageElement = useCallback(\n (src: string) => {\n const imageElement = thumbhash ? (\n <Image\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n aspectRatio={1}\n thumbhash={thumbhash}\n objectFit=\"cover\"\n className={cn('size-full', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center size-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, favoriteButtonDisabled, onFavoriteToggle} =\n useProductCardContext()\n if (favoriteButtonDisabled) return null\n\n return (\n <div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>\n <FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />\n </div>\n )\n}\n\nfunction ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n if (variant !== 'default') {\n return null\n }\n\n return (\n <div\n data-testid=\"product-card-info\"\n className={cn('px-1 pt-2 pb-0 space-y-1', className)}\n {...props}\n />\n )\n}\n\nfunction ProductCardTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {product} = useProductCardContext()\n return (\n <h3\n data-slot=\"product-card-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900',\n 'truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children || product.title}\n </h3>\n )\n}\n\nfunction ProductCardReviewStars({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {product, reviewsDisabled} = useProductCardContext()\n const reviewAnalytics = product.reviewAnalytics\n\n if (reviewsDisabled || !reviewAnalytics?.averageRating) {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-review-stars\"\n className={cn('', className)}\n {...props}\n >\n <ProductReviewStars\n averageRating={reviewAnalytics.averageRating}\n reviewCount={reviewAnalytics.reviewCount}\n />\n </div>\n )\n}\n\nfunction ProductCardPrice({className}: {className?: string}) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive price data locally\n const displayPrice = selectedProductVariant?.price || product?.price\n const displayCompareAtPrice =\n selectedProductVariant?.compareAtPrice || product?.compareAtPrice\n\n return (\n <ProductVariantPrice\n amount={displayPrice?.amount || ''}\n currencyCode={displayPrice?.currencyCode || ''}\n compareAtPriceAmount={displayCompareAtPrice?.amount}\n compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}\n className={className}\n />\n )\n}\n\n// Special PriceOverlayBadge for price overlay variant\nfunction ProductCardPriceOverlayBadge() {\n const {product, selectedProductVariant, variant} = useProductCardContext()\n if (variant !== 'priceOverlay') return null\n const displayPrice = selectedProductVariant?.price || product.price\n const currencyCode = displayPrice?.currencyCode\n const amount = displayPrice?.amount\n\n if (!currencyCode || !amount) return null\n return (\n <ProductCardBadge position=\"top-left\">\n {formatMoney(amount, currencyCode)}\n </ProductCardBadge>\n )\n}\n\nexport interface ProductCardProps {\n /** The product to display in the card */\n product: Product\n /** Optional selected variant of the product to show specific variant data */\n selectedProductVariant?: ProductVariant\n /** Visual style variant of the card */\n variant?: 'default' | 'priceOverlay' | 'compact'\n /** Whether the card can be clicked/tapped to navigate to product details */\n touchable?: boolean\n /** Optional text to display in a badge on the card */\n badgeText?: string\n /** Visual style variant for the badge */\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n /** Callback fired when the product is clicked */\n onProductClick?: () => void\n /** Callback fired when the favorite button is toggled */\n onFavoriteToggled?: (isFavorited: boolean) => void\n /** Custom layout via children */\n children?: React.ReactNode\n /** Whether the favorite button is disabled */\n favoriteButtonDisabled?: boolean\n /** Whether review stars are disabled */\n reviewsDisabled?: boolean\n}\n\nfunction ProductCard({\n product,\n selectedProductVariant,\n variant = 'default',\n touchable = true,\n badgeText,\n badgeVariant,\n onProductClick,\n onFavoriteToggled,\n children,\n favoriteButtonDisabled = false,\n reviewsDisabled = false,\n}: ProductCardProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n\n onProductClick?.()\n\n navigateToProduct({\n productId: product.id,\n })\n }, [navigateToProduct, product.id, touchable, onProductClick])\n\n const handleFavoriteClick = useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n onFavoriteToggled?.(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n onFavoriteToggled?.(previousState)\n }\n }, [\n isFavoritedLocal,\n product.id,\n product.shop.id,\n product.defaultVariantId,\n selectedProductVariant?.id,\n saveProduct,\n unsaveProduct,\n onFavoriteToggled,\n ])\n\n const contextValue = useMemo<ProductCardContextValue>(\n () => ({\n // Core data\n product,\n selectedProductVariant,\n\n // UI configuration\n variant,\n touchable,\n badgeText,\n badgeVariant,\n favoriteButtonDisabled,\n reviewsDisabled,\n\n // State\n isFavorited: isFavoritedLocal,\n // Actions\n onClick: handleClick,\n onFavoriteToggle: handleFavoriteClick,\n }),\n [\n product,\n selectedProductVariant,\n variant,\n touchable,\n badgeText,\n badgeVariant,\n isFavoritedLocal,\n handleClick,\n handleFavoriteClick,\n favoriteButtonDisabled,\n reviewsDisabled,\n ]\n )\n\n return (\n <ProductCardContext.Provider value={contextValue}>\n {children ?? (\n <ProductCardContainer>\n <ProductCardImageContainer>\n <ProductCardImage />\n {variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}\n <ProductCardBadge />\n <ProductCardFavoriteButton />\n </ProductCardImageContainer>\n {variant === 'default' && (\n <ProductCardInfo>\n <ProductCardTitle />\n <ProductCardReviewStars />\n <ProductCardPrice />\n </ProductCardInfo>\n )}\n </ProductCardContainer>\n )}\n </ProductCardContext.Provider>\n )\n}\n\nexport {\n ProductCard,\n ProductCardContainer,\n ProductCardImageContainer,\n ProductCardImage,\n ProductCardBadge,\n ProductCardFavoriteButton,\n ProductCardInfo,\n ProductCardTitle,\n ProductCardReviewStars,\n ProductCardPrice,\n}\n"],"names":["ProductCardContext","React","useProductCardContext","context","useContext","ProductCardContainer","className","props","touchable","onClick","content","jsx","cn","Touchable","ProductCardImageContainer","variant","ProductCardImage","product","selectedProductVariant","displayImage","src","alt","thumbhash","renderImageElement","useCallback","Image","ProductCardBadge","position","children","badgeText","badgeVariant","Badge","ProductCardFavoriteButton","isFavorited","favoriteButtonDisabled","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardReviewStars","reviewsDisabled","reviewAnalytics","ProductReviewStars","ProductCardPrice","displayPrice","displayCompareAtPrice","ProductVariantPrice","ProductCardPriceOverlayBadge","currencyCode","amount","formatMoney","ProductCard","onProductClick","onFavoriteToggled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","isFavoritedLocal","setIsFavoritedLocal","useState","handleClick","handleFavoriteClick","previousState","contextValue","useMemo","jsxs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"product-card.js","sources":["../../../src/components/commerce/product-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {useCallback, useContext, useMemo, useState} from 'react'\n\nimport {type Product, type ProductVariant} from '@shopify/shop-minis-platform'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\nimport {FavoriteButton} from './favorite-button'\n\n// Context definition\ninterface ProductCardContextValue {\n // Core data\n product: Product\n selectedProductVariant?: ProductVariant\n\n // UI configuration\n variant: 'default' | 'priceOverlay' | 'compact'\n touchable: boolean\n badgeText?: string\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n favoriteButtonDisabled: boolean\n reviewsDisabled: boolean\n\n // State\n isFavorited: boolean\n\n // Actions\n onClick: () => void\n onFavoriteToggle: () => void\n}\n\nconst ProductCardContext = React.createContext<\n ProductCardContextValue | undefined\n>(undefined)\n\nfunction useProductCardContext() {\n const context = useContext(ProductCardContext)\n if (!context) {\n throw new Error(\n 'ProductCard components must be used within a ProductCard provider'\n )\n }\n return context\n}\n\n// Primitive components (building blocks)\nfunction ProductCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, onClick} = useProductCardContext()\n\n const content = (\n <div\n className={cn(\n 'relative size-full overflow-hidden rounded-xl border-0 isolate',\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction ProductCardImageContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n\n return (\n <div\n data-slot=\"product-card-image-container\"\n className={cn(\n 'relative overflow-hidden rounded-xl border border-gray-200',\n 'aspect-square',\n variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive display image locally\n const displayImage = selectedProductVariant?.image || product.featuredImage\n const src = displayImage?.url\n const alt = displayImage?.altText || product.title\n const thumbhash = product.featuredImage?.thumbhash\n\n const renderImageElement = useCallback(\n (src: string) => {\n const imageElement = thumbhash ? (\n <Image\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n aspectRatio={1}\n thumbhash={thumbhash}\n objectFit=\"cover\"\n className={cn('size-full', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center size-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, favoriteButtonDisabled, onFavoriteToggle} =\n useProductCardContext()\n if (favoriteButtonDisabled) return null\n\n return (\n <div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>\n <FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />\n </div>\n )\n}\n\nfunction ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n if (variant !== 'default') {\n return null\n }\n\n return (\n <div\n data-testid=\"product-card-info\"\n className={cn('px-1 pt-2 pb-0 space-y-1', className)}\n {...props}\n />\n )\n}\n\nfunction ProductCardTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {product} = useProductCardContext()\n return (\n <h3\n data-slot=\"product-card-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900',\n 'truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children || product.title}\n </h3>\n )\n}\n\nfunction ProductCardReviewStars({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {product, reviewsDisabled} = useProductCardContext()\n const reviewAnalytics = product.reviewAnalytics\n\n if (reviewsDisabled || !reviewAnalytics?.averageRating) {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-review-stars\"\n className={cn('', className)}\n {...props}\n >\n <ProductReviewStars\n averageRating={reviewAnalytics.averageRating}\n reviewCount={reviewAnalytics.reviewCount}\n />\n </div>\n )\n}\n\nfunction ProductCardPrice({className}: {className?: string}) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive price data locally\n const displayPrice = selectedProductVariant?.price || product?.price\n const displayCompareAtPrice =\n selectedProductVariant?.compareAtPrice || product?.compareAtPrice\n\n return (\n <ProductVariantPrice\n amount={displayPrice?.amount || ''}\n currencyCode={displayPrice?.currencyCode || ''}\n compareAtPriceAmount={displayCompareAtPrice?.amount}\n compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}\n className={className}\n />\n )\n}\n\n// Special PriceOverlayBadge for price overlay variant\nfunction ProductCardPriceOverlayBadge() {\n const {product, selectedProductVariant, variant} = useProductCardContext()\n if (variant !== 'priceOverlay') return null\n const displayPrice = selectedProductVariant?.price || product.price\n const currencyCode = displayPrice?.currencyCode\n const amount = displayPrice?.amount\n\n if (!currencyCode || !amount) return null\n return (\n <ProductCardBadge position=\"top-left\">\n {formatMoney(amount, currencyCode)}\n </ProductCardBadge>\n )\n}\n\nexport interface ProductCardProps {\n /** The product to display in the card */\n product: Product\n /** Optional selected variant of the product to show specific variant data */\n selectedProductVariant?: ProductVariant\n /** Visual style variant of the card */\n variant?: 'default' | 'priceOverlay' | 'compact'\n /** Whether the card can be clicked/tapped to navigate to product details */\n touchable?: boolean\n /** Optional text to display in a badge on the card */\n badgeText?: string\n /** Visual style variant for the badge */\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n /** Callback fired when the product is clicked */\n onProductClick?: () => void\n /** Callback fired when the favorite button is toggled */\n onFavoriteToggled?: (isFavorited: boolean) => void\n /** Custom layout via children */\n children?: React.ReactNode\n /** Whether the favorite button is disabled */\n favoriteButtonDisabled?: boolean\n /** Whether review stars are disabled */\n reviewsDisabled?: boolean\n}\n\nfunction ProductCard({\n product,\n selectedProductVariant,\n variant = 'default',\n touchable = true,\n badgeText,\n badgeVariant,\n onProductClick,\n onFavoriteToggled,\n children,\n favoriteButtonDisabled = false,\n reviewsDisabled = false,\n}: ProductCardProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n\n onProductClick?.()\n\n navigateToProduct({\n productId: product.id,\n })\n }, [navigateToProduct, product.id, touchable, onProductClick])\n\n const handleFavoriteClick = useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n onFavoriteToggled?.(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n onFavoriteToggled?.(previousState)\n }\n }, [\n isFavoritedLocal,\n product.id,\n product.shop.id,\n product.defaultVariantId,\n selectedProductVariant?.id,\n saveProduct,\n unsaveProduct,\n onFavoriteToggled,\n ])\n\n const contextValue = useMemo<ProductCardContextValue>(\n () => ({\n // Core data\n product,\n selectedProductVariant,\n\n // UI configuration\n variant,\n touchable,\n badgeText,\n badgeVariant,\n favoriteButtonDisabled,\n reviewsDisabled,\n\n // State\n isFavorited: isFavoritedLocal,\n // Actions\n onClick: handleClick,\n onFavoriteToggle: handleFavoriteClick,\n }),\n [\n product,\n selectedProductVariant,\n variant,\n touchable,\n badgeText,\n badgeVariant,\n isFavoritedLocal,\n handleClick,\n handleFavoriteClick,\n favoriteButtonDisabled,\n reviewsDisabled,\n ]\n )\n\n return (\n <ProductCardContext.Provider value={contextValue}>\n {children ?? (\n <ProductCardContainer>\n <ProductCardImageContainer>\n <ProductCardImage />\n {variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}\n <ProductCardBadge />\n <ProductCardFavoriteButton />\n </ProductCardImageContainer>\n {variant === 'default' && (\n <ProductCardInfo>\n <ProductCardTitle />\n <ProductCardReviewStars />\n <ProductCardPrice />\n </ProductCardInfo>\n )}\n </ProductCardContainer>\n )}\n </ProductCardContext.Provider>\n )\n}\n\nexport {\n ProductCard,\n ProductCardContainer,\n ProductCardImageContainer,\n ProductCardImage,\n ProductCardBadge,\n ProductCardFavoriteButton,\n ProductCardInfo,\n ProductCardTitle,\n ProductCardReviewStars,\n ProductCardPrice,\n}\n"],"names":["ProductCardContext","React","useProductCardContext","context","useContext","ProductCardContainer","className","props","touchable","onClick","content","jsx","cn","Touchable","ProductCardImageContainer","variant","ProductCardImage","product","selectedProductVariant","displayImage","src","alt","thumbhash","renderImageElement","useCallback","Image","ProductCardBadge","position","children","badgeText","badgeVariant","Badge","ProductCardFavoriteButton","isFavorited","favoriteButtonDisabled","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardReviewStars","reviewsDisabled","reviewAnalytics","ProductReviewStars","ProductCardPrice","displayPrice","displayCompareAtPrice","ProductVariantPrice","ProductCardPriceOverlayBadge","currencyCode","amount","formatMoney","ProductCard","onProductClick","onFavoriteToggled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","isFavoritedLocal","setIsFavoritedLocal","useState","handleClick","handleFavoriteClick","previousState","contextValue","useMemo","jsxs"],"mappings":";;;;;;;;;;;;;AAuCA,MAAMA,IAAqBC,EAAM,cAE/B,MAAS;AAEX,SAASC,IAAwB;AACzB,QAAAC,IAAUC,EAAWJ,CAAkB;AAC7C,MAAI,CAACG;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAEK,SAAAA;AACT;AAGA,SAASE,EAAqB;AAAA,EAC5B,WAAAC;AAAA,EACA,GAAGC;AACL,GAAgC;AAC9B,QAAM,EAAC,WAAAC,GAAW,SAAAC,EAAO,IAAIP,EAAsB,GAE7CQ,IACJ,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACAN;AAAA,MACF;AAAA,MACC,GAAGC;AAAA,IAAA;AAAA,EACN;AAGF,SAAIC,KAAaC,IAEb,gBAAAE;AAAA,IAACE;AAAA,IAAA;AAAA,MACC,SAAAJ;AAAA,MACA,UAAU,EAAC,SAAS,IAAG;AAAA,MACvB,YAAY;AAAA,QACV,SAAS,EAAC,MAAM,SAAS,UAAU,MAAM,MAAM,YAAW;AAAA,MAC5D;AAAA,MAEC,UAAAC;AAAA,IAAA;AAAA,EACH,IAIGA;AACT;AAEA,SAASI,EAA0B;AAAA,EACjC,WAAAR;AAAA,EACA,GAAGC;AACL,GAAgC;AACxB,QAAA,EAAC,SAAAQ,EAAO,IAAIb,EAAsB;AAGtC,SAAA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC;AAAA,QACT;AAAA,QACA;AAAA,QACAG,MAAY,YAAY,kBAAkB;AAAA,QAC1CT;AAAA,MACF;AAAA,MACC,GAAGC;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASS,EAAiB,EAAC,WAAAV,GAAW,GAAGC,KAAqC;AAC5E,QAAM,EAAC,SAAAU,GAAS,wBAAAC,EAAsB,IAAIhB,EAAsB,GAG1DiB,IAAeD,GAAwB,SAASD,EAAQ,eACxDG,IAAMD,GAAc,KACpBE,IAAMF,GAAc,WAAWF,EAAQ,OACvCK,IAAYL,EAAQ,eAAe,WAEnCM,IAAqBC;AAAA,IACzB,CAACJ,MACsBE,IACnB,gBAAAX;AAAA,MAACc;AAAA,MAAA;AAAA,QACC,aAAU;AAAA,QACV,KAAKL;AAAAA,QACL,KAAAC;AAAA,QACA,aAAa;AAAA,QACb,WAAAC;AAAA,QACA,WAAU;AAAA,QACV,WAAWV,EAAG,aAAaN,CAAS;AAAA,QACnC,GAAGC;AAAA,MAAA;AAAA,IAAA,IAGN,gBAAAI;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,aAAU;AAAA,QACV,KAAKS;AAAAA,QACL,KAAAC;AAAA,QACA,WAAWT,EAAG,0BAA0BN,CAAS;AAAA,QAChD,GAAGC;AAAA,MAAA;AAAA,IACN;AAAA,IAKJ,CAACc,GAAKf,GAAWC,GAAOe,CAAS;AAAA,EACnC;AAEA,SACG,gBAAAX,EAAA,OAAA,EAAI,WAAU,0DACZ,UACCS,IAAAG,EAAmBH,CAAG,IAErB,gBAAAT,EAAA,OAAA,EAAI,WAAU,4CAA2C,qBAAQ,CAAA,GAEtE;AAEJ;AAEA,SAASe,EAAiB;AAAA,EACxB,WAAApB;AAAA,EACA,UAAAqB,IAAW;AAAA,EACX,SAAAZ;AAAA,EACA,UAAAa;AAAA,EACA,GAAGrB;AACL,GAEG;AACD,QAAM,EAAC,WAAAsB,GAAW,cAAAC,EAAY,IAAI5B,EAAsB,GAElDQ,IAAUkB,KAAYC;AAExB,SAACnB,IAGH,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC;AAAA,QACT;AAAA,QACAe,MAAa,aAAa,iBAAiB;AAAA,MAC7C;AAAA,MAEA,UAAA,gBAAAhB;AAAA,QAACoB;AAAA,QAAA;AAAA,UACC,SAAShB,KAAWe,KAAgB;AAAA,UACpC,WAAWlB;AAAA,YACT,CAACkB,KACC,CAACf,KACD;AAAA,YACF;AAAA,YACAT;AAAA,UACF;AAAA,UACC,GAAGC;AAAA,UAEH,UAAAG;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EACF,IAtBmB;AAwBvB;AAEA,SAASsB,EAA0B;AAAA,EACjC,WAAA1B;AAAA,EACA,GAAGC;AACL,GAAgC;AAC9B,QAAM,EAAC,aAAA0B,GAAa,wBAAAC,GAAwB,kBAAAC,EAAA,IAC1CjC,EAAsB;AACxB,SAAIgC,IAA+B,OAGhC,gBAAAvB,EAAA,OAAA,EAAI,WAAWC,EAAG,kCAAkCN,CAAS,GAAI,GAAGC,GACnE,4BAAC6B,GAAe,EAAA,SAASD,GAAkB,QAAQF,EAAa,CAAA,GAClE;AAEJ;AAEA,SAASI,EAAgB,EAAC,WAAA/B,GAAW,GAAGC,KAAqC;AACrE,QAAA,EAAC,SAAAQ,EAAO,IAAIb,EAAsB;AACxC,SAAIa,MAAY,YACP,OAIP,gBAAAJ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,WAAWC,EAAG,4BAA4BN,CAAS;AAAA,MAClD,GAAGC;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAAS+B,EAAiB;AAAA,EACxB,WAAAhC;AAAA,EACA,UAAAsB;AAAA,EACA,GAAGrB;AACL,GAA+B;AACvB,QAAA,EAAC,SAAAU,EAAO,IAAIf,EAAsB;AAEtC,SAAA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC;AAAA,QACT;AAAA,QACA;AAAA,QACAN;AAAA,MACF;AAAA,MACC,GAAGC;AAAA,MAEH,eAAYU,EAAQ;AAAA,IAAA;AAAA,EACvB;AAEJ;AAEA,SAASsB,EAAuB;AAAA,EAC9B,WAAAjC;AAAA,EACA,GAAGC;AACL,GAAgC;AAC9B,QAAM,EAAC,SAAAU,GAAS,iBAAAuB,EAAe,IAAItC,EAAsB,GACnDuC,IAAkBxB,EAAQ;AAE5B,SAAAuB,KAAmB,CAACC,GAAiB,gBAChC,OAIP,gBAAA9B;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC,EAAG,IAAIN,CAAS;AAAA,MAC1B,GAAGC;AAAA,MAEJ,UAAA,gBAAAI;AAAA,QAAC+B;AAAA,QAAA;AAAA,UACC,eAAeD,EAAgB;AAAA,UAC/B,aAAaA,EAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IAC/B;AAAA,EACF;AAEJ;AAEA,SAASE,EAAiB,EAAC,WAAArC,KAAkC;AAC3D,QAAM,EAAC,SAAAW,GAAS,wBAAAC,EAAsB,IAAIhB,EAAsB,GAG1D0C,IAAe1B,GAAwB,SAASD,GAAS,OACzD4B,IACJ3B,GAAwB,kBAAkBD,GAAS;AAGnD,SAAA,gBAAAN;AAAA,IAACmC;AAAA,IAAA;AAAA,MACC,QAAQF,GAAc,UAAU;AAAA,MAChC,cAAcA,GAAc,gBAAgB;AAAA,MAC5C,sBAAsBC,GAAuB;AAAA,MAC7C,4BAA4BA,GAAuB;AAAA,MACnD,WAAAvC;AAAA,IAAA;AAAA,EACF;AAEJ;AAGA,SAASyC,IAA+B;AACtC,QAAM,EAAC,SAAA9B,GAAS,wBAAAC,GAAwB,SAAAH,EAAA,IAAWb,EAAsB;AACrE,MAAAa,MAAY,eAAuB,QAAA;AACjC,QAAA6B,IAAe1B,GAAwB,SAASD,EAAQ,OACxD+B,IAAeJ,GAAc,cAC7BK,IAASL,GAAc;AAE7B,SAAI,CAACI,KAAgB,CAACC,IAAe,yBAElCvB,GAAiB,EAAA,UAAS,YACxB,UAAYwB,EAAAD,GAAQD,CAAY,GACnC;AAEJ;AA2BA,SAASG,GAAY;AAAA,EACnB,SAAAlC;AAAA,EACA,wBAAAC;AAAA,EACA,SAAAH,IAAU;AAAA,EACV,WAAAP,IAAY;AAAA,EACZ,WAAAqB;AAAA,EACA,cAAAC;AAAA,EACA,gBAAAsB;AAAA,EACA,mBAAAC;AAAA,EACA,UAAAzB;AAAA,EACA,wBAAAM,IAAyB;AAAA,EACzB,iBAAAM,IAAkB;AACpB,GAAqB;AACb,QAAA,EAAC,mBAAAc,EAAiB,IAAIC,EAAkB,GACxC,EAAC,aAAAC,GAAa,eAAAC,EAAa,IAAIC,EAAwB,GAGvD,CAACC,GAAkBC,CAAmB,IAAIC,EAAS5C,EAAQ,WAAW,GAEtE6C,IAActC,EAAY,MAAM;AACpC,IAAKhB,MAEY4C,IAAA,GAECE,EAAA;AAAA,MAChB,WAAWrC,EAAQ;AAAA,IAAA,CACpB;AAAA,EAAA,GACA,CAACqC,GAAmBrC,EAAQ,IAAIT,GAAW4C,CAAc,CAAC,GAEvDW,IAAsBvC,EAAY,YAAY;AAClD,UAAMwC,IAAgBL;AAGtB,IAAAC,EAAoB,CAACI,CAAa,GAClCX,IAAoB,CAACW,CAAa;AAE9B,QAAA;AACF,MAAIA,IACF,MAAMP,EAAc;AAAA,QAClB,WAAWxC,EAAQ;AAAA,QACnB,QAAQA,EAAQ,KAAK;AAAA,QACrB,kBACEC,GAAwB,MAAMD,EAAQ;AAAA,MAAA,CACzC,IAED,MAAMuC,EAAY;AAAA,QAChB,WAAWvC,EAAQ;AAAA,QACnB,QAAQA,EAAQ,KAAK;AAAA,QACrB,kBACEC,GAAwB,MAAMD,EAAQ;AAAA,MAAA,CACzC;AAAA,YAEW;AAEd,MAAA2C,EAAoBI,CAAa,GACjCX,IAAoBW,CAAa;AAAA,IAAA;AAAA,EACnC,GACC;AAAA,IACDL;AAAA,IACA1C,EAAQ;AAAA,IACRA,EAAQ,KAAK;AAAA,IACbA,EAAQ;AAAA,IACRC,GAAwB;AAAA,IACxBsC;AAAA,IACAC;AAAA,IACAJ;AAAA,EAAA,CACD,GAEKY,IAAeC;AAAA,IACnB,OAAO;AAAA;AAAA,MAEL,SAAAjD;AAAA,MACA,wBAAAC;AAAA;AAAA,MAGA,SAAAH;AAAA,MACA,WAAAP;AAAA,MACA,WAAAqB;AAAA,MACA,cAAAC;AAAA,MACA,wBAAAI;AAAA,MACA,iBAAAM;AAAA;AAAA,MAGA,aAAamB;AAAA;AAAA,MAEb,SAASG;AAAA,MACT,kBAAkBC;AAAA,IAAA;AAAA,IAEpB;AAAA,MACE9C;AAAA,MACAC;AAAA,MACAH;AAAA,MACAP;AAAA,MACAqB;AAAA,MACAC;AAAA,MACA6B;AAAA,MACAG;AAAA,MACAC;AAAA,MACA7B;AAAA,MACAM;AAAA,IAAA;AAAA,EAEJ;AAGE,SAAA,gBAAA7B,EAACX,EAAmB,UAAnB,EAA4B,OAAOiE,GACjC,UAAArC,uBACEvB,GACC,EAAA,UAAA;AAAA,IAAA,gBAAA8D,EAACrD,GACC,EAAA,UAAA;AAAA,MAAA,gBAAAH,EAACK,GAAiB,EAAA;AAAA,MACjBD,MAAY,kBAAkB,gBAAAJ,EAACoC,GAA6B,CAAA,CAAA;AAAA,wBAC5DrB,GAAiB,EAAA;AAAA,wBACjBM,GAA0B,CAAA,CAAA;AAAA,IAAA,GAC7B;AAAA,IACCjB,MAAY,aACX,gBAAAoD,EAAC9B,GACC,EAAA,UAAA;AAAA,MAAA,gBAAA1B,EAAC2B,GAAiB,EAAA;AAAA,wBACjBC,GAAuB,EAAA;AAAA,wBACvBI,GAAiB,CAAA,CAAA;AAAA,IAAA,EACpB,CAAA;AAAA,EAAA,EAAA,CAEJ,EAEJ,CAAA;AAEJ;"}
|
package/dist/mocks.js
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
1
1
|
import { UserState as g } from "./shop-minis-platform/src/types/user.js";
|
|
2
2
|
import { MinisContentStatus as h } from "./shop-minis-platform/src/types/content.js";
|
|
3
|
-
const
|
|
3
|
+
const e = (o, r, t = "99.99", i) => ({
|
|
4
4
|
id: o,
|
|
5
5
|
title: r,
|
|
6
|
-
price: { amount:
|
|
6
|
+
price: { amount: t, currencyCode: "USD" },
|
|
7
7
|
...i && {
|
|
8
8
|
compareAtPrice: { amount: i, currencyCode: "USD" }
|
|
9
9
|
},
|
|
10
10
|
reviewAnalytics: { averageRating: 4.5, reviewCount: 10 },
|
|
11
|
-
shop:
|
|
11
|
+
shop: d("shop1", "Mock Shop"),
|
|
12
12
|
defaultVariantId: `variant-${o}`,
|
|
13
13
|
isFavorited: !1,
|
|
14
14
|
featuredImage: {
|
|
15
15
|
url: "https://cdn.shopify.com/static/sample-images/teapot.jpg",
|
|
16
16
|
altText: r
|
|
17
17
|
}
|
|
18
|
-
}),
|
|
19
|
-
const i =
|
|
20
|
-
url: `https://picsum.photos/400/400?random=${o}-${
|
|
18
|
+
}), d = (o, r, t) => {
|
|
19
|
+
const i = t?.themeType || "none", s = t?.withBrandSettings || i !== "none", l = t?.featuredImagesLimit || 3, m = Array.from({ length: l }, (I, c) => ({
|
|
20
|
+
url: `https://picsum.photos/400/400?random=${o}-${c}`,
|
|
21
21
|
sensitive: !1,
|
|
22
|
-
altText: `${r} featured image ${
|
|
22
|
+
altText: `${r} featured image ${c + 1}`
|
|
23
23
|
})), u = () => {
|
|
24
24
|
switch (i) {
|
|
25
25
|
case "coverImage":
|
|
26
26
|
return {
|
|
27
|
-
primary:
|
|
28
|
-
logoDominant:
|
|
29
|
-
logoAverage:
|
|
30
|
-
coverDominant:
|
|
27
|
+
primary: t?.primaryColor,
|
|
28
|
+
logoDominant: t?.logoDominantColor,
|
|
29
|
+
logoAverage: t?.logoAverageColor,
|
|
30
|
+
coverDominant: t?.coverDominantColor || "#FF6B35"
|
|
31
31
|
};
|
|
32
32
|
case "brandColor":
|
|
33
33
|
return {
|
|
34
|
-
primary:
|
|
35
|
-
logoDominant:
|
|
36
|
-
logoAverage:
|
|
37
|
-
coverDominant:
|
|
34
|
+
primary: t?.primaryColor || "#27AE60",
|
|
35
|
+
logoDominant: t?.logoDominantColor,
|
|
36
|
+
logoAverage: t?.logoAverageColor,
|
|
37
|
+
coverDominant: t?.coverDominantColor
|
|
38
38
|
};
|
|
39
39
|
case "logoColor":
|
|
40
40
|
return {
|
|
41
|
-
primary:
|
|
42
|
-
logoDominant:
|
|
43
|
-
logoAverage:
|
|
44
|
-
coverDominant:
|
|
41
|
+
primary: t?.primaryColor,
|
|
42
|
+
logoDominant: t?.logoDominantColor || "#E74C3C",
|
|
43
|
+
logoAverage: t?.logoAverageColor,
|
|
44
|
+
coverDominant: t?.coverDominantColor
|
|
45
45
|
};
|
|
46
46
|
default:
|
|
47
47
|
return {
|
|
48
|
-
primary:
|
|
49
|
-
logoDominant:
|
|
50
|
-
logoAverage:
|
|
51
|
-
coverDominant:
|
|
48
|
+
primary: t?.primaryColor,
|
|
49
|
+
logoDominant: t?.logoDominantColor,
|
|
50
|
+
logoAverage: t?.logoAverageColor,
|
|
51
|
+
coverDominant: t?.coverDominantColor
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
}, p = () => {
|
|
55
|
-
if (i === "coverImage" ||
|
|
55
|
+
if (i === "coverImage" || t?.coverImageUrl)
|
|
56
56
|
return {
|
|
57
57
|
id: `header-theme-${o}`,
|
|
58
58
|
coverImage: {
|
|
59
|
-
url:
|
|
59
|
+
url: t?.coverImageUrl || "https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=800&h=400&fit=crop",
|
|
60
60
|
altText: `${r} cover image`,
|
|
61
61
|
sensitive: !1,
|
|
62
62
|
thumbhash: "k9oGHQRnh493V4dIeHeXh4h3iIeI"
|
|
63
63
|
},
|
|
64
|
-
wordmark:
|
|
65
|
-
url:
|
|
64
|
+
wordmark: t?.wordmarkUrl || i === "coverImage" ? {
|
|
65
|
+
url: t?.wordmarkUrl || "https://merrypeople.com/cdn/shop/files/Transparent_Background_1.png?v=1696465429&width=1024",
|
|
66
66
|
altText: `${r} wordmark`,
|
|
67
67
|
sensitive: !1
|
|
68
68
|
} : void 0
|
|
69
69
|
};
|
|
70
|
-
if (
|
|
70
|
+
if (t?.wordmarkUrl)
|
|
71
71
|
return {
|
|
72
72
|
id: `header-theme-${o}`,
|
|
73
73
|
wordmark: {
|
|
74
|
-
url:
|
|
74
|
+
url: t.wordmarkUrl,
|
|
75
75
|
altText: `${r} wordmark`,
|
|
76
76
|
sensitive: !1
|
|
77
77
|
}
|
|
@@ -91,7 +91,7 @@ const t = (o, r, e = "99.99", i) => ({
|
|
|
91
91
|
url: `https://picsum.photos/100/100?random=${o}`,
|
|
92
92
|
sensitive: !1
|
|
93
93
|
},
|
|
94
|
-
brandSettings:
|
|
94
|
+
brandSettings: s ? {
|
|
95
95
|
id: `brand-settings-${o}`,
|
|
96
96
|
colors: {
|
|
97
97
|
id: `colors-${o}`,
|
|
@@ -104,19 +104,19 @@ const t = (o, r, e = "99.99", i) => ({
|
|
|
104
104
|
}, a = (o = !1) => ({
|
|
105
105
|
hasNextPage: o,
|
|
106
106
|
endCursor: o ? "cursor123" : null
|
|
107
|
-
}),
|
|
107
|
+
}), n = (o, r, t = []) => ({
|
|
108
108
|
id: o,
|
|
109
109
|
publicId: `public-${o}`,
|
|
110
110
|
name: r,
|
|
111
|
-
products:
|
|
111
|
+
products: t
|
|
112
112
|
});
|
|
113
113
|
function v(o, r) {
|
|
114
114
|
window._mockLogs = window._mockLogs || [], window._mockLogs.push({ action: o, params: r });
|
|
115
115
|
}
|
|
116
|
-
function
|
|
117
|
-
return (
|
|
116
|
+
function P(o, r) {
|
|
117
|
+
return (t) => (v(String(o), t), Promise.resolve({ ok: !0, data: r }));
|
|
118
118
|
}
|
|
119
|
-
function
|
|
119
|
+
function S() {
|
|
120
120
|
const o = {
|
|
121
121
|
translateContentUp: void 0,
|
|
122
122
|
translateContentDown: void 0,
|
|
@@ -192,47 +192,53 @@ function I() {
|
|
|
192
192
|
reportContentImpression: void 0,
|
|
193
193
|
getProductLists: {
|
|
194
194
|
data: [
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
n("list-1", "Wishlist"),
|
|
196
|
+
n("list-2", "Favorites")
|
|
197
197
|
],
|
|
198
198
|
pageInfo: a()
|
|
199
199
|
},
|
|
200
200
|
getProductList: {
|
|
201
|
-
data:
|
|
202
|
-
|
|
201
|
+
data: n("list-1", "Wishlist", [
|
|
202
|
+
e("prod-1", "Sample Product")
|
|
203
203
|
]),
|
|
204
204
|
pageInfo: a()
|
|
205
205
|
},
|
|
206
|
-
addProductList:
|
|
206
|
+
addProductList: n("list-3", "New List"),
|
|
207
207
|
removeProductList: void 0,
|
|
208
|
-
renameProductList:
|
|
208
|
+
renameProductList: n("list-1", "Updated Wishlist"),
|
|
209
209
|
addProductListItem: void 0,
|
|
210
210
|
removeProductListItem: void 0,
|
|
211
211
|
getRecommendedProducts: {
|
|
212
212
|
data: [
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
213
|
+
e("rec-1", "Recommended Product 1", "79.99"),
|
|
214
|
+
e("rec-2", "Recommended Product 2", "129.99"),
|
|
215
|
+
e("rec-3", "Recommended Product 3", "129.99"),
|
|
216
|
+
e("rec-4", "Recommended Product 4", "29.99"),
|
|
217
|
+
e("rec-5", "Recommended Product 5", "39.99"),
|
|
218
|
+
e("rec-6", "Recommended Product 6", "49.99"),
|
|
219
|
+
e("rec-7", "Recommended Product 7", "59.99"),
|
|
220
|
+
e("rec-8", "Recommended Product 8", "69.99"),
|
|
221
|
+
e("rec-9", "Recommended Product 9", "129.99")
|
|
222
222
|
],
|
|
223
223
|
pageInfo: a()
|
|
224
224
|
},
|
|
225
225
|
getRecommendedShops: {
|
|
226
226
|
data: [
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
d("shop-1", "Amazing Store"),
|
|
228
|
+
d("shop-2", "Best Deals Shop"),
|
|
229
|
+
d("shop-3", "Great Products"),
|
|
230
|
+
d("shop-4", "Top Brands"),
|
|
231
|
+
d("shop-5", "Exclusive Offers")
|
|
229
232
|
],
|
|
230
233
|
pageInfo: a()
|
|
231
234
|
},
|
|
232
235
|
searchProductsByShop: {
|
|
233
236
|
data: [
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
e("search-1", "Search Result 1", "59.99"),
|
|
238
|
+
e("search-2", "Search Result 2", "89.99"),
|
|
239
|
+
e("search-3", "Search Result 3", "119.99"),
|
|
240
|
+
e("search-4", "Search Result 4", "149.99"),
|
|
241
|
+
e("search-5", "Search Result 5", "179.99")
|
|
236
242
|
],
|
|
237
243
|
pageInfo: a()
|
|
238
244
|
},
|
|
@@ -249,7 +255,7 @@ function I() {
|
|
|
249
255
|
product: null
|
|
250
256
|
}
|
|
251
257
|
],
|
|
252
|
-
shop:
|
|
258
|
+
shop: d("shop-1", "Sample Shop")
|
|
253
259
|
}
|
|
254
260
|
],
|
|
255
261
|
pageInfo: a()
|
|
@@ -266,16 +272,16 @@ function I() {
|
|
|
266
272
|
showFeedbackSheet: void 0,
|
|
267
273
|
getPopularProducts: {
|
|
268
274
|
data: [
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
275
|
+
e("pop-1", "The Hero Snowboard", "702.95"),
|
|
276
|
+
e("pop-2", "Snow Jacket", "605.95", "702.00"),
|
|
277
|
+
e("pop-3", "Winter Gloves", "89.95"),
|
|
278
|
+
e("pop-4", "Summer Gloves", "89.95"),
|
|
279
|
+
e("pop-5", "Spring Gloves", "89.95"),
|
|
280
|
+
e("pop-6", "Playstation 5", "499.95"),
|
|
281
|
+
e("pop-7", "Xbox Series X", "499.95"),
|
|
282
|
+
e("pop-8", "Nintendo Switch", "299.95"),
|
|
283
|
+
e("pop-9", "Playstation 4", "299.95"),
|
|
284
|
+
e("pop-10", "Nintendo 3DS", "89.95")
|
|
279
285
|
],
|
|
280
286
|
pageInfo: a()
|
|
281
287
|
},
|
|
@@ -289,31 +295,51 @@ function I() {
|
|
|
289
295
|
},
|
|
290
296
|
getCuratedProducts: {
|
|
291
297
|
data: [
|
|
292
|
-
|
|
293
|
-
|
|
298
|
+
e("cur-1", "Curated Product 1", "79.99"),
|
|
299
|
+
e("cur-2", "Curated Product 2", "129.99")
|
|
294
300
|
],
|
|
295
301
|
pageInfo: a()
|
|
296
302
|
},
|
|
297
303
|
getSavedProducts: {
|
|
298
|
-
data: [
|
|
304
|
+
data: [
|
|
305
|
+
e("saved-1", "Saved Product 1", "49.99"),
|
|
306
|
+
e("saved-2", "Saved Product 2", "59.99"),
|
|
307
|
+
e("saved-3", "Saved Product 3", "69.99"),
|
|
308
|
+
e("saved-4", "Saved Product 4", "79.99"),
|
|
309
|
+
e("saved-5", "Saved Product 5", "89.99")
|
|
310
|
+
],
|
|
299
311
|
pageInfo: a()
|
|
300
312
|
},
|
|
301
313
|
getRecentProducts: {
|
|
302
|
-
data: [
|
|
314
|
+
data: [
|
|
315
|
+
e("recent-1", "Recent Product 1", "59.99"),
|
|
316
|
+
e("recent-2", "Recent Product 2", "69.99"),
|
|
317
|
+
e("recent-3", "Recent Product 3", "79.99"),
|
|
318
|
+
e("recent-4", "Recent Product 4", "89.99"),
|
|
319
|
+
e("recent-5", "Recent Product 5", "99.99")
|
|
320
|
+
],
|
|
303
321
|
pageInfo: a()
|
|
304
322
|
},
|
|
305
323
|
getProductSearch: {
|
|
306
324
|
data: [
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
325
|
+
e("search-1", "Search Product 1", "39.99"),
|
|
326
|
+
e("search-2", "Search Product 2", "19.99"),
|
|
327
|
+
e("search-3", "Search Product 3", "29.99"),
|
|
328
|
+
e("search-4", "Search Product 4", "49.99"),
|
|
329
|
+
e("search-5", "Search Product 5", "9.99")
|
|
312
330
|
],
|
|
313
331
|
pageInfo: a()
|
|
314
332
|
},
|
|
315
|
-
getProducts: {
|
|
316
|
-
|
|
333
|
+
getProducts: {
|
|
334
|
+
data: [
|
|
335
|
+
e("prod-1", "Product 1", "9.99"),
|
|
336
|
+
e("prod-2", "Product 2", "19.99"),
|
|
337
|
+
e("prod-3", "Product 3", "29.99"),
|
|
338
|
+
e("prod-4", "Product 4", "39.99"),
|
|
339
|
+
e("prod-5", "Product 5", "49.99")
|
|
340
|
+
]
|
|
341
|
+
},
|
|
342
|
+
getProduct: { data: e("prod-1", "Sample Product") },
|
|
317
343
|
getProductVariants: {
|
|
318
344
|
data: [
|
|
319
345
|
{
|
|
@@ -339,14 +365,22 @@ function I() {
|
|
|
339
365
|
pageInfo: a()
|
|
340
366
|
},
|
|
341
367
|
getShop: {
|
|
342
|
-
data:
|
|
368
|
+
data: d("shop-1", "Sample Shop", { featuredImagesLimit: 4 })
|
|
343
369
|
},
|
|
344
370
|
getRecentShops: {
|
|
345
|
-
data: [
|
|
371
|
+
data: [
|
|
372
|
+
d("recent-shop-1", "Recent Shop 1"),
|
|
373
|
+
d("recent-shop-2", "Recent Shop 2"),
|
|
374
|
+
d("recent-shop-3", "Recent Shop 3")
|
|
375
|
+
],
|
|
346
376
|
pageInfo: a()
|
|
347
377
|
},
|
|
348
378
|
getFollowedShops: {
|
|
349
|
-
data: [
|
|
379
|
+
data: [
|
|
380
|
+
d("followed-shop-1", "Followed Shop 1"),
|
|
381
|
+
d("followed-shop-2", "Followed Shop 2"),
|
|
382
|
+
d("followed-shop-3", "Followed Shop 3")
|
|
383
|
+
],
|
|
350
384
|
pageInfo: a()
|
|
351
385
|
},
|
|
352
386
|
previewProductInAr: void 0,
|
|
@@ -397,27 +431,27 @@ function I() {
|
|
|
397
431
|
reportError: void 0,
|
|
398
432
|
reportFetch: void 0
|
|
399
433
|
}, r = {};
|
|
400
|
-
for (const
|
|
401
|
-
Object.prototype.hasOwnProperty.call(o,
|
|
402
|
-
|
|
403
|
-
o[
|
|
434
|
+
for (const t in o)
|
|
435
|
+
Object.prototype.hasOwnProperty.call(o, t) && (r[t] = P(
|
|
436
|
+
t,
|
|
437
|
+
o[t]
|
|
404
438
|
));
|
|
405
439
|
return r;
|
|
406
440
|
}
|
|
407
|
-
const
|
|
408
|
-
const o = navigator.userAgent.toLowerCase(), r = /iphone|ipad|ipod/.test(o),
|
|
409
|
-
return r ||
|
|
441
|
+
const f = () => {
|
|
442
|
+
const o = navigator.userAgent.toLowerCase(), r = /iphone|ipad|ipod/.test(o), t = /android/.test(o);
|
|
443
|
+
return r || t;
|
|
410
444
|
}, y = ({ force: o } = {}) => {
|
|
411
|
-
|
|
445
|
+
f() && !o || window.minisSDK || (window.minisSDK = S(), window.minisParams = {
|
|
412
446
|
handle: "mock-handle",
|
|
413
447
|
initialUrl: "/mock-initial-url",
|
|
414
448
|
platform: "web"
|
|
415
449
|
});
|
|
416
450
|
};
|
|
417
451
|
export {
|
|
418
|
-
|
|
419
|
-
|
|
452
|
+
e as createProduct,
|
|
453
|
+
d as createShop,
|
|
420
454
|
y as injectMocks,
|
|
421
|
-
|
|
455
|
+
S as makeMockActions
|
|
422
456
|
};
|
|
423
457
|
//# sourceMappingURL=mocks.js.map
|
package/dist/mocks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mocks.js","sources":["../src/mocks.ts"],"sourcesContent":["import {\n Product,\n Gender,\n UserState,\n MinisContentStatus,\n} from '@shopify/shop-minis-platform'\nimport {ShopActions} from '@shopify/shop-minis-platform/actions'\n\n// Helper functions for common data structures\nexport const createProduct = (\n id: string,\n title: string,\n price = '99.99',\n compareAtPrice?: string\n): Product => ({\n id,\n title,\n price: {amount: price, currencyCode: 'USD'},\n ...(compareAtPrice && {\n compareAtPrice: {amount: compareAtPrice, currencyCode: 'USD'},\n }),\n reviewAnalytics: {averageRating: 4.5, reviewCount: 10},\n shop: createShop('shop1', 'Mock Shop'),\n defaultVariantId: `variant-${id}`,\n isFavorited: false,\n featuredImage: {\n url: `https://cdn.shopify.com/static/sample-images/teapot.jpg`,\n altText: title,\n },\n})\n\nexport const createShop = (\n id: string,\n name: string,\n options?: {\n themeType?: 'coverImage' | 'brandColor' | 'logoColor' | 'none'\n withBrandSettings?: boolean\n primaryColor?: string\n logoDominantColor?: string\n logoAverageColor?: string\n coverDominantColor?: string\n wordmarkUrl?: string\n coverImageUrl?: string\n featuredImagesLimit?: number\n }\n) => {\n // Determine theme configuration\n const themeType = options?.themeType || 'none'\n const shouldHaveBrandSettings =\n options?.withBrandSettings || themeType !== 'none'\n\n // Generate featured images\n const featuredImagesCount = options?.featuredImagesLimit || 3\n const featuredImages = Array.from({length: featuredImagesCount}, (_, i) => ({\n url: `https://picsum.photos/400/400?random=${id}-${i}`,\n sensitive: false,\n altText: `${name} featured image ${i + 1}`,\n }))\n\n // Configure colors based on theme type\n const getThemeColors = () => {\n switch (themeType) {\n case 'coverImage':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor || '#FF6B35',\n }\n case 'brandColor':\n return {\n primary: options?.primaryColor || '#27AE60',\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n case 'logoColor':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor || '#E74C3C',\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n default:\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n }\n }\n\n // Configure header theme\n const createHeaderTheme = () => {\n if (themeType === 'coverImage' || options?.coverImageUrl) {\n return {\n id: `header-theme-${id}`,\n coverImage: {\n url:\n options?.coverImageUrl ||\n 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=800&h=400&fit=crop',\n altText: `${name} cover image`,\n sensitive: false,\n thumbhash: 'k9oGHQRnh493V4dIeHeXh4h3iIeI',\n },\n wordmark:\n options?.wordmarkUrl || themeType === 'coverImage'\n ? {\n url:\n options?.wordmarkUrl ||\n 'https://merrypeople.com/cdn/shop/files/Transparent_Background_1.png?v=1696465429&width=1024',\n altText: `${name} wordmark`,\n sensitive: false,\n }\n : undefined,\n }\n }\n\n if (options?.wordmarkUrl) {\n return {\n id: `header-theme-${id}`,\n wordmark: {\n url: options.wordmarkUrl,\n altText: `${name} wordmark`,\n sensitive: false,\n },\n }\n }\n\n return undefined\n }\n\n return {\n id,\n name,\n primaryDomain: {\n url: `https://${name.toLowerCase().replace(/\\s+/g, '-')}.com`,\n },\n reviewAnalytics: {averageRating: 4.3, reviewCount: 50},\n visualTheme: {\n id: `visual-theme-${id}`,\n featuredImages,\n logoImage: {\n url: `https://picsum.photos/100/100?random=${id}`,\n sensitive: false,\n },\n brandSettings: shouldHaveBrandSettings\n ? {\n id: `brand-settings-${id}`,\n colors: {\n id: `colors-${id}`,\n ...getThemeColors(),\n },\n headerTheme: createHeaderTheme(),\n }\n : undefined,\n },\n }\n}\n\nconst createPagination = (hasNext = false) => ({\n hasNextPage: hasNext,\n endCursor: hasNext ? 'cursor123' : null,\n})\n\nconst createProductList = (id: string, name: string, products: any[] = []) => ({\n id,\n publicId: `public-${id}`,\n name,\n products,\n})\n\n// Helper type to extract the data type from a ShopAction\ntype ShopActionDataType<T> = T extends (\n ...args: any[]\n) => Promise<{ok: true; data: infer R} | {ok: false; error: any}>\n ? R\n : never\n\n// Use window._mockLogs instead of console.log so logs aren't stripped in production builds\n// This allows e2e tests to verify mock actions are being called\nexport interface MockLog {\n action: string\n params?: unknown\n}\n\ndeclare global {\n interface Window {\n _mockLogs?: MockLog[]\n }\n}\n\nfunction logMockAction(action: string, params?: unknown) {\n window._mockLogs = window._mockLogs || []\n window._mockLogs.push({action, params})\n}\n\nfunction makeMockMethod<K extends keyof ShopActions>(\n key: K,\n result: ShopActionDataType<ShopActions[K]>\n): ShopActions[K] {\n return ((params: Parameters<ShopActions[K]>[0]) => {\n logMockAction(String(key), params)\n return Promise.resolve({ok: true as const, data: result})\n }) as ShopActions[K]\n}\n\nexport function makeMockActions(): ShopActions {\n const results: {\n [K in keyof ShopActions]: ShopActionDataType<ShopActions[K]>\n } = {\n translateContentUp: undefined,\n translateContentDown: undefined,\n followShop: true,\n unfollowShop: false,\n favorite: undefined,\n unfavorite: undefined,\n getShopAppInformation: {\n appVersion: '1.0.0',\n buildNumber: '12345',\n buildId: 'dev-build-123',\n },\n productRecommendationImpression: undefined,\n productRecommendationClick: undefined,\n closeMini: undefined,\n getAccountInformation: {\n status: 'available',\n value: 'user@example.com',\n },\n getCurrentUser: {\n data: {\n displayName: 'John Doe',\n avatarImage: {url: 'https://example.com/avatar.jpg'},\n },\n },\n createOrderAttribution: undefined,\n addToCart: undefined,\n buyProduct: undefined,\n buyProducts: undefined,\n showErrorScreen: undefined,\n showErrorToast: undefined,\n getDeeplinkPaths: {\n matchers: ['/products', '/collections', '/cart'],\n },\n navigateToDeeplink: undefined,\n navigateToShop: undefined,\n navigateToProduct: undefined,\n navigateToOrder: undefined,\n navigateToCheckout: undefined,\n createImageUploadLink: {\n targets: [\n {\n url: 'https://example.com/upload',\n resourceUrl: 'https://example.com/resource',\n parameters: [{name: 'key', value: 'upload-123'}],\n },\n ],\n },\n completeImageUpload: {\n files: [\n {\n id: 'file-123',\n fileStatus: 'READY',\n image: {url: 'https://example.com/image.jpg'},\n },\n ],\n },\n getPersistedItem: null,\n setPersistedItem: undefined,\n removePersistedItem: undefined,\n getAllPersistedKeys: ['key1', 'key2', 'key3'],\n clearPersistedItems: undefined,\n getInternalPersistedItem: null,\n setInternalPersistedItem: undefined,\n removeInternalPersistedItem: undefined,\n getAllInternalPersistedKeys: ['internal-key1', 'internal-key2'],\n clearInternalPersistedItems: undefined,\n getSecret: 'secret-value',\n setSecret: undefined,\n removeSecret: undefined,\n reportInteraction: undefined,\n reportImpression: undefined,\n reportContentImpression: undefined,\n getProductLists: {\n data: [\n createProductList('list-1', 'Wishlist'),\n createProductList('list-2', 'Favorites'),\n ],\n pageInfo: createPagination(),\n },\n getProductList: {\n data: createProductList('list-1', 'Wishlist', [\n createProduct('prod-1', 'Sample Product'),\n ]),\n pageInfo: createPagination(),\n },\n addProductList: createProductList('list-3', 'New List'),\n removeProductList: undefined,\n renameProductList: createProductList('list-1', 'Updated Wishlist'),\n addProductListItem: undefined,\n removeProductListItem: undefined,\n getRecommendedProducts: {\n data: [\n createProduct('rec-1', 'Recommended Product 1', '79.99'),\n createProduct('rec-2', 'Recommended Product 2', '129.99'),\n createProduct('rec-3', 'Recommended Product 3', '129.99'),\n createProduct('rec-4', 'Recommended Product 4', '29.99'),\n createProduct('rec-5', 'Recommended Product 5', '39.99'),\n createProduct('rec-6', 'Recommended Product 6', '49.99'),\n createProduct('rec-7', 'Recommended Product 7', '59.99'),\n createProduct('rec-8', 'Recommended Product 8', '69.99'),\n createProduct('rec-9', 'Recommended Product 9', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getRecommendedShops: {\n data: [\n createShop('shop-1', 'Amazing Store'),\n createShop('shop-2', 'Best Deals Shop'),\n ],\n pageInfo: createPagination(),\n },\n searchProductsByShop: {\n data: [\n createProduct('search-1', 'Search Result 1', '59.99'),\n createProduct('search-2', 'Search Result 2', '89.99'),\n ],\n pageInfo: createPagination(),\n },\n getOrders: {\n data: [\n {\n id: 'order-1',\n name: '#1001',\n lineItems: [\n {\n productTitle: 'Sample Product',\n variantTitle: 'Medium',\n quantity: 2,\n product: null,\n },\n ],\n shop: createShop('shop-1', 'Sample Shop'),\n },\n ],\n pageInfo: createPagination(),\n },\n getBuyerAttributes: {\n data: {\n genderAffinity: 'NEUTRAL' as Gender,\n categoryAffinities: [\n {id: 'cat1', name: 'Electronics'},\n {id: 'cat2', name: 'Clothing'},\n ],\n },\n },\n showFeedbackSheet: undefined,\n getPopularProducts: {\n data: [\n createProduct('pop-1', 'The Hero Snowboard', '702.95'),\n createProduct('pop-2', 'Snow Jacket', '605.95', '702.00'),\n createProduct('pop-3', 'Winter Gloves', '89.95'),\n createProduct('pop-4', 'Summer Gloves', '89.95'),\n createProduct('pop-5', 'Spring Gloves', '89.95'),\n createProduct('pop-6', 'Playstation 5', '499.95'),\n createProduct('pop-7', 'Xbox Series X', '499.95'),\n createProduct('pop-8', 'Nintendo Switch', '299.95'),\n createProduct('pop-9', 'Playstation 4', '299.95'),\n createProduct('pop-10', 'Nintendo 3DS', '89.95'),\n ],\n pageInfo: createPagination(),\n },\n share: {\n message: 'Shared!',\n success: true,\n },\n shareSingle: {\n message: 'Shared!',\n success: true,\n },\n getCuratedProducts: {\n data: [\n createProduct('cur-1', 'Curated Product 1', '79.99'),\n createProduct('cur-2', 'Curated Product 2', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getSavedProducts: {\n data: [createProduct('saved-1', 'Saved Product 1', '49.99')],\n pageInfo: createPagination(),\n },\n getRecentProducts: {\n data: [createProduct('recent-1', 'Recent Product 1', '59.99')],\n pageInfo: createPagination(),\n },\n getProductSearch: {\n data: [\n createProduct('search-1', 'Search Product 1', '39.99'),\n createProduct('search-2', 'Search Product 2', '19.99'),\n createProduct('search-3', 'Search Product 3', '29.99'),\n createProduct('search-4', 'Search Product 4', '49.99'),\n createProduct('search-5', 'Search Product 5', '9.99'),\n ],\n pageInfo: createPagination(),\n },\n getProducts: {data: [createProduct('prod-2', 'Product 2', '19.99')]},\n getProduct: {data: createProduct('prod-1', 'Sample Product')},\n getProductVariants: {\n data: [\n {\n id: 'variant-1',\n title: 'Variant 1',\n isFavorited: false,\n image: {url: 'https://example.com/variant-1.jpg'},\n price: {amount: '19.99', currencyCode: 'USD'},\n compareAtPrice: {amount: '29.99', currencyCode: 'USD'},\n },\n ],\n pageInfo: createPagination(),\n },\n getProductMedia: {\n data: [\n {\n id: 'media-1',\n image: {url: 'https://example.com/media-1.jpg'},\n mediaContentType: 'IMAGE',\n alt: 'Sample product image',\n },\n ],\n pageInfo: createPagination(),\n },\n getShop: {\n data: createShop('shop-1', 'Sample Shop', {featuredImagesLimit: 4}),\n },\n getRecentShops: {\n data: [createShop('recent-shop-1', 'Recent Shop 1')],\n pageInfo: createPagination(),\n },\n getFollowedShops: {\n data: [createShop('followed-shop-1', 'Followed Shop 1')],\n pageInfo: createPagination(),\n },\n previewProductInAr: undefined,\n createContent: {\n data: {\n publicId: 'content-123',\n externalId: null,\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n description: 'This is a mock content item',\n visibility: ['DISCOVERABLE'],\n shareableUrl: 'https://example.com/content/123',\n products: null,\n },\n },\n getContent: {\n data: [\n {\n publicId: 'content-123',\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n visibility: ['DISCOVERABLE'],\n status: MinisContentStatus.READY,\n },\n ],\n },\n generateUserToken: {\n data: {\n token: 'user-token-123',\n expiresAt: '2025-01-01',\n userState: UserState.VERIFIED,\n },\n },\n navigateToCart: undefined,\n requestPermission: {\n granted: true,\n },\n reportError: undefined,\n reportFetch: undefined,\n } as const\n\n const mock: Partial<ShopActions> = {}\n for (const key in results) {\n if (Object.prototype.hasOwnProperty.call(results, key)) {\n // @ts-expect-error: dynamic assignment is safe due to exhaustive mapping\n mock[key] = makeMockMethod(\n key as keyof ShopActions,\n results[key as keyof typeof results]\n )\n }\n }\n return mock as ShopActions\n}\n\n// Detect if running on a mobile device\nconst isMobile = (): boolean => {\n const userAgent = navigator.userAgent.toLowerCase()\n const isIOS = /iphone|ipad|ipod/.test(userAgent)\n const isAndroid = /android/.test(userAgent)\n\n return isIOS || isAndroid\n}\n\nexport const injectMocks = ({force}: {force?: boolean} = {}) => {\n // Only inject mocks if we aren't on a mobile device or we force it\n if (isMobile() && !force) {\n return\n }\n\n if (!window.minisSDK) {\n window.minisSDK = makeMockActions()\n window.minisParams = {\n handle: 'mock-handle',\n initialUrl: '/mock-initial-url',\n platform: 'web',\n }\n }\n}\n"],"names":["createProduct","id","title","price","compareAtPrice","createShop","name","options","themeType","shouldHaveBrandSettings","featuredImagesCount","featuredImages","_","i","getThemeColors","createHeaderTheme","createPagination","hasNext","createProductList","products","logMockAction","action","params","makeMockMethod","key","result","makeMockActions","results","MinisContentStatus","UserState","mock","isMobile","userAgent","isIOS","isAndroid","injectMocks","force"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mocks.js","sources":["../src/mocks.ts"],"sourcesContent":["import {\n Product,\n Gender,\n UserState,\n MinisContentStatus,\n} from '@shopify/shop-minis-platform'\nimport {ShopActions} from '@shopify/shop-minis-platform/actions'\n\n// Helper functions for common data structures\nexport const createProduct = (\n id: string,\n title: string,\n price = '99.99',\n compareAtPrice?: string\n): Product => ({\n id,\n title,\n price: {amount: price, currencyCode: 'USD'},\n ...(compareAtPrice && {\n compareAtPrice: {amount: compareAtPrice, currencyCode: 'USD'},\n }),\n reviewAnalytics: {averageRating: 4.5, reviewCount: 10},\n shop: createShop('shop1', 'Mock Shop'),\n defaultVariantId: `variant-${id}`,\n isFavorited: false,\n featuredImage: {\n url: `https://cdn.shopify.com/static/sample-images/teapot.jpg`,\n altText: title,\n },\n})\n\nexport const createShop = (\n id: string,\n name: string,\n options?: {\n themeType?: 'coverImage' | 'brandColor' | 'logoColor' | 'none'\n withBrandSettings?: boolean\n primaryColor?: string\n logoDominantColor?: string\n logoAverageColor?: string\n coverDominantColor?: string\n wordmarkUrl?: string\n coverImageUrl?: string\n featuredImagesLimit?: number\n }\n) => {\n // Determine theme configuration\n const themeType = options?.themeType || 'none'\n const shouldHaveBrandSettings =\n options?.withBrandSettings || themeType !== 'none'\n\n // Generate featured images\n const featuredImagesCount = options?.featuredImagesLimit || 3\n const featuredImages = Array.from({length: featuredImagesCount}, (_, i) => ({\n url: `https://picsum.photos/400/400?random=${id}-${i}`,\n sensitive: false,\n altText: `${name} featured image ${i + 1}`,\n }))\n\n // Configure colors based on theme type\n const getThemeColors = () => {\n switch (themeType) {\n case 'coverImage':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor || '#FF6B35',\n }\n case 'brandColor':\n return {\n primary: options?.primaryColor || '#27AE60',\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n case 'logoColor':\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor || '#E74C3C',\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n default:\n return {\n primary: options?.primaryColor,\n logoDominant: options?.logoDominantColor,\n logoAverage: options?.logoAverageColor,\n coverDominant: options?.coverDominantColor,\n }\n }\n }\n\n // Configure header theme\n const createHeaderTheme = () => {\n if (themeType === 'coverImage' || options?.coverImageUrl) {\n return {\n id: `header-theme-${id}`,\n coverImage: {\n url:\n options?.coverImageUrl ||\n 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=800&h=400&fit=crop',\n altText: `${name} cover image`,\n sensitive: false,\n thumbhash: 'k9oGHQRnh493V4dIeHeXh4h3iIeI',\n },\n wordmark:\n options?.wordmarkUrl || themeType === 'coverImage'\n ? {\n url:\n options?.wordmarkUrl ||\n 'https://merrypeople.com/cdn/shop/files/Transparent_Background_1.png?v=1696465429&width=1024',\n altText: `${name} wordmark`,\n sensitive: false,\n }\n : undefined,\n }\n }\n\n if (options?.wordmarkUrl) {\n return {\n id: `header-theme-${id}`,\n wordmark: {\n url: options.wordmarkUrl,\n altText: `${name} wordmark`,\n sensitive: false,\n },\n }\n }\n\n return undefined\n }\n\n return {\n id,\n name,\n primaryDomain: {\n url: `https://${name.toLowerCase().replace(/\\s+/g, '-')}.com`,\n },\n reviewAnalytics: {averageRating: 4.3, reviewCount: 50},\n visualTheme: {\n id: `visual-theme-${id}`,\n featuredImages,\n logoImage: {\n url: `https://picsum.photos/100/100?random=${id}`,\n sensitive: false,\n },\n brandSettings: shouldHaveBrandSettings\n ? {\n id: `brand-settings-${id}`,\n colors: {\n id: `colors-${id}`,\n ...getThemeColors(),\n },\n headerTheme: createHeaderTheme(),\n }\n : undefined,\n },\n }\n}\n\nconst createPagination = (hasNext = false) => ({\n hasNextPage: hasNext,\n endCursor: hasNext ? 'cursor123' : null,\n})\n\nconst createProductList = (id: string, name: string, products: any[] = []) => ({\n id,\n publicId: `public-${id}`,\n name,\n products,\n})\n\n// Helper type to extract the data type from a ShopAction\ntype ShopActionDataType<T> = T extends (\n ...args: any[]\n) => Promise<{ok: true; data: infer R} | {ok: false; error: any}>\n ? R\n : never\n\n// Use window._mockLogs instead of console.log so logs aren't stripped in production builds\n// This allows e2e tests to verify mock actions are being called\nexport interface MockLog {\n action: string\n params?: unknown\n}\n\ndeclare global {\n interface Window {\n _mockLogs?: MockLog[]\n }\n}\n\nfunction logMockAction(action: string, params?: unknown) {\n window._mockLogs = window._mockLogs || []\n window._mockLogs.push({action, params})\n}\n\nfunction makeMockMethod<K extends keyof ShopActions>(\n key: K,\n result: ShopActionDataType<ShopActions[K]>\n): ShopActions[K] {\n return ((params: Parameters<ShopActions[K]>[0]) => {\n logMockAction(String(key), params)\n return Promise.resolve({ok: true as const, data: result})\n }) as ShopActions[K]\n}\n\nexport function makeMockActions(): ShopActions {\n const results: {\n [K in keyof ShopActions]: ShopActionDataType<ShopActions[K]>\n } = {\n translateContentUp: undefined,\n translateContentDown: undefined,\n followShop: true,\n unfollowShop: false,\n favorite: undefined,\n unfavorite: undefined,\n getShopAppInformation: {\n appVersion: '1.0.0',\n buildNumber: '12345',\n buildId: 'dev-build-123',\n },\n productRecommendationImpression: undefined,\n productRecommendationClick: undefined,\n closeMini: undefined,\n getAccountInformation: {\n status: 'available',\n value: 'user@example.com',\n },\n getCurrentUser: {\n data: {\n displayName: 'John Doe',\n avatarImage: {url: 'https://example.com/avatar.jpg'},\n },\n },\n createOrderAttribution: undefined,\n addToCart: undefined,\n buyProduct: undefined,\n buyProducts: undefined,\n showErrorScreen: undefined,\n showErrorToast: undefined,\n getDeeplinkPaths: {\n matchers: ['/products', '/collections', '/cart'],\n },\n navigateToDeeplink: undefined,\n navigateToShop: undefined,\n navigateToProduct: undefined,\n navigateToOrder: undefined,\n navigateToCheckout: undefined,\n createImageUploadLink: {\n targets: [\n {\n url: 'https://example.com/upload',\n resourceUrl: 'https://example.com/resource',\n parameters: [{name: 'key', value: 'upload-123'}],\n },\n ],\n },\n completeImageUpload: {\n files: [\n {\n id: 'file-123',\n fileStatus: 'READY',\n image: {url: 'https://example.com/image.jpg'},\n },\n ],\n },\n getPersistedItem: null,\n setPersistedItem: undefined,\n removePersistedItem: undefined,\n getAllPersistedKeys: ['key1', 'key2', 'key3'],\n clearPersistedItems: undefined,\n getInternalPersistedItem: null,\n setInternalPersistedItem: undefined,\n removeInternalPersistedItem: undefined,\n getAllInternalPersistedKeys: ['internal-key1', 'internal-key2'],\n clearInternalPersistedItems: undefined,\n getSecret: 'secret-value',\n setSecret: undefined,\n removeSecret: undefined,\n reportInteraction: undefined,\n reportImpression: undefined,\n reportContentImpression: undefined,\n getProductLists: {\n data: [\n createProductList('list-1', 'Wishlist'),\n createProductList('list-2', 'Favorites'),\n ],\n pageInfo: createPagination(),\n },\n getProductList: {\n data: createProductList('list-1', 'Wishlist', [\n createProduct('prod-1', 'Sample Product'),\n ]),\n pageInfo: createPagination(),\n },\n addProductList: createProductList('list-3', 'New List'),\n removeProductList: undefined,\n renameProductList: createProductList('list-1', 'Updated Wishlist'),\n addProductListItem: undefined,\n removeProductListItem: undefined,\n getRecommendedProducts: {\n data: [\n createProduct('rec-1', 'Recommended Product 1', '79.99'),\n createProduct('rec-2', 'Recommended Product 2', '129.99'),\n createProduct('rec-3', 'Recommended Product 3', '129.99'),\n createProduct('rec-4', 'Recommended Product 4', '29.99'),\n createProduct('rec-5', 'Recommended Product 5', '39.99'),\n createProduct('rec-6', 'Recommended Product 6', '49.99'),\n createProduct('rec-7', 'Recommended Product 7', '59.99'),\n createProduct('rec-8', 'Recommended Product 8', '69.99'),\n createProduct('rec-9', 'Recommended Product 9', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getRecommendedShops: {\n data: [\n createShop('shop-1', 'Amazing Store'),\n createShop('shop-2', 'Best Deals Shop'),\n createShop('shop-3', 'Great Products'),\n createShop('shop-4', 'Top Brands'),\n createShop('shop-5', 'Exclusive Offers'),\n ],\n pageInfo: createPagination(),\n },\n searchProductsByShop: {\n data: [\n createProduct('search-1', 'Search Result 1', '59.99'),\n createProduct('search-2', 'Search Result 2', '89.99'),\n createProduct('search-3', 'Search Result 3', '119.99'),\n createProduct('search-4', 'Search Result 4', '149.99'),\n createProduct('search-5', 'Search Result 5', '179.99'),\n ],\n pageInfo: createPagination(),\n },\n getOrders: {\n data: [\n {\n id: 'order-1',\n name: '#1001',\n lineItems: [\n {\n productTitle: 'Sample Product',\n variantTitle: 'Medium',\n quantity: 2,\n product: null,\n },\n ],\n shop: createShop('shop-1', 'Sample Shop'),\n },\n ],\n pageInfo: createPagination(),\n },\n getBuyerAttributes: {\n data: {\n genderAffinity: 'NEUTRAL' as Gender,\n categoryAffinities: [\n {id: 'cat1', name: 'Electronics'},\n {id: 'cat2', name: 'Clothing'},\n ],\n },\n },\n showFeedbackSheet: undefined,\n getPopularProducts: {\n data: [\n createProduct('pop-1', 'The Hero Snowboard', '702.95'),\n createProduct('pop-2', 'Snow Jacket', '605.95', '702.00'),\n createProduct('pop-3', 'Winter Gloves', '89.95'),\n createProduct('pop-4', 'Summer Gloves', '89.95'),\n createProduct('pop-5', 'Spring Gloves', '89.95'),\n createProduct('pop-6', 'Playstation 5', '499.95'),\n createProduct('pop-7', 'Xbox Series X', '499.95'),\n createProduct('pop-8', 'Nintendo Switch', '299.95'),\n createProduct('pop-9', 'Playstation 4', '299.95'),\n createProduct('pop-10', 'Nintendo 3DS', '89.95'),\n ],\n pageInfo: createPagination(),\n },\n share: {\n message: 'Shared!',\n success: true,\n },\n shareSingle: {\n message: 'Shared!',\n success: true,\n },\n getCuratedProducts: {\n data: [\n createProduct('cur-1', 'Curated Product 1', '79.99'),\n createProduct('cur-2', 'Curated Product 2', '129.99'),\n ],\n pageInfo: createPagination(),\n },\n getSavedProducts: {\n data: [\n createProduct('saved-1', 'Saved Product 1', '49.99'),\n createProduct('saved-2', 'Saved Product 2', '59.99'),\n createProduct('saved-3', 'Saved Product 3', '69.99'),\n createProduct('saved-4', 'Saved Product 4', '79.99'),\n createProduct('saved-5', 'Saved Product 5', '89.99'),\n ],\n pageInfo: createPagination(),\n },\n getRecentProducts: {\n data: [\n createProduct('recent-1', 'Recent Product 1', '59.99'),\n createProduct('recent-2', 'Recent Product 2', '69.99'),\n createProduct('recent-3', 'Recent Product 3', '79.99'),\n createProduct('recent-4', 'Recent Product 4', '89.99'),\n createProduct('recent-5', 'Recent Product 5', '99.99'),\n ],\n pageInfo: createPagination(),\n },\n getProductSearch: {\n data: [\n createProduct('search-1', 'Search Product 1', '39.99'),\n createProduct('search-2', 'Search Product 2', '19.99'),\n createProduct('search-3', 'Search Product 3', '29.99'),\n createProduct('search-4', 'Search Product 4', '49.99'),\n createProduct('search-5', 'Search Product 5', '9.99'),\n ],\n pageInfo: createPagination(),\n },\n getProducts: {\n data: [\n createProduct('prod-1', 'Product 1', '9.99'),\n createProduct('prod-2', 'Product 2', '19.99'),\n createProduct('prod-3', 'Product 3', '29.99'),\n createProduct('prod-4', 'Product 4', '39.99'),\n createProduct('prod-5', 'Product 5', '49.99'),\n ],\n },\n getProduct: {data: createProduct('prod-1', 'Sample Product')},\n getProductVariants: {\n data: [\n {\n id: 'variant-1',\n title: 'Variant 1',\n isFavorited: false,\n image: {url: 'https://example.com/variant-1.jpg'},\n price: {amount: '19.99', currencyCode: 'USD'},\n compareAtPrice: {amount: '29.99', currencyCode: 'USD'},\n },\n ],\n pageInfo: createPagination(),\n },\n getProductMedia: {\n data: [\n {\n id: 'media-1',\n image: {url: 'https://example.com/media-1.jpg'},\n mediaContentType: 'IMAGE',\n alt: 'Sample product image',\n },\n ],\n pageInfo: createPagination(),\n },\n getShop: {\n data: createShop('shop-1', 'Sample Shop', {featuredImagesLimit: 4}),\n },\n getRecentShops: {\n data: [\n createShop('recent-shop-1', 'Recent Shop 1'),\n createShop('recent-shop-2', 'Recent Shop 2'),\n createShop('recent-shop-3', 'Recent Shop 3'),\n ],\n pageInfo: createPagination(),\n },\n getFollowedShops: {\n data: [\n createShop('followed-shop-1', 'Followed Shop 1'),\n createShop('followed-shop-2', 'Followed Shop 2'),\n createShop('followed-shop-3', 'Followed Shop 3'),\n ],\n pageInfo: createPagination(),\n },\n previewProductInAr: undefined,\n createContent: {\n data: {\n publicId: 'content-123',\n externalId: null,\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n description: 'This is a mock content item',\n visibility: ['DISCOVERABLE'],\n shareableUrl: 'https://example.com/content/123',\n products: null,\n },\n },\n getContent: {\n data: [\n {\n publicId: 'content-123',\n image: {\n id: 'img-123',\n url: 'https://cdn.shopify.com/s/files/1/0633/6574/2742/files/Namnlosdesign-47.png?v=1740438079',\n width: 800,\n height: 600,\n },\n title: 'Mock Content',\n visibility: ['DISCOVERABLE'],\n status: MinisContentStatus.READY,\n },\n ],\n },\n generateUserToken: {\n data: {\n token: 'user-token-123',\n expiresAt: '2025-01-01',\n userState: UserState.VERIFIED,\n },\n },\n navigateToCart: undefined,\n requestPermission: {\n granted: true,\n },\n reportError: undefined,\n reportFetch: undefined,\n } as const\n\n const mock: Partial<ShopActions> = {}\n for (const key in results) {\n if (Object.prototype.hasOwnProperty.call(results, key)) {\n // @ts-expect-error: dynamic assignment is safe due to exhaustive mapping\n mock[key] = makeMockMethod(\n key as keyof ShopActions,\n results[key as keyof typeof results]\n )\n }\n }\n return mock as ShopActions\n}\n\n// Detect if running on a mobile device\nconst isMobile = (): boolean => {\n const userAgent = navigator.userAgent.toLowerCase()\n const isIOS = /iphone|ipad|ipod/.test(userAgent)\n const isAndroid = /android/.test(userAgent)\n\n return isIOS || isAndroid\n}\n\nexport const injectMocks = ({force}: {force?: boolean} = {}) => {\n // Only inject mocks if we aren't on a mobile device or we force it\n if (isMobile() && !force) {\n return\n }\n\n if (!window.minisSDK) {\n window.minisSDK = makeMockActions()\n window.minisParams = {\n handle: 'mock-handle',\n initialUrl: '/mock-initial-url',\n platform: 'web',\n }\n }\n}\n"],"names":["createProduct","id","title","price","compareAtPrice","createShop","name","options","themeType","shouldHaveBrandSettings","featuredImagesCount","featuredImages","_","i","getThemeColors","createHeaderTheme","createPagination","hasNext","createProductList","products","logMockAction","action","params","makeMockMethod","key","result","makeMockActions","results","MinisContentStatus","UserState","mock","isMobile","userAgent","isIOS","isAndroid","injectMocks","force"],"mappings":"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"}
|
package/eslint/config.cjs
CHANGED
|
@@ -38,9 +38,7 @@ module.exports = {
|
|
|
38
38
|
'react/no-danger': 'error',
|
|
39
39
|
|
|
40
40
|
// Shop Minis custom rules
|
|
41
|
-
'shop-minis/no-dynamic-asset-paths': 'warn',
|
|
42
41
|
'shop-minis/no-env-without-fallback': 'error',
|
|
43
|
-
'shop-minis/no-hardcoded-asset-paths': 'error',
|
|
44
42
|
'shop-minis/no-internal-imports': 'error',
|
|
45
43
|
'shop-minis/no-secrets': ['error'],
|
|
46
44
|
'shop-minis/prefer-sdk-components': 'warn',
|
package/eslint/index.cjs
CHANGED
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
* @fileoverview Custom ESLint rules for Shop Minis React SDK
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const noDynamicAssetPaths = require('./rules/no-dynamic-asset-paths.cjs')
|
|
8
7
|
const noEnvWithoutFallback = require('./rules/no-env-without-fallback.cjs')
|
|
9
|
-
const noHardcodedAssetPaths = require('./rules/no-hardcoded-asset-paths.cjs')
|
|
10
8
|
const noInternalImports = require('./rules/no-internal-imports.cjs')
|
|
11
9
|
const noSecrets = require('./rules/no-secrets.cjs')
|
|
12
10
|
const preferSdkComponents = require('./rules/prefer-sdk-components.cjs')
|
|
@@ -15,9 +13,7 @@ const validateManifest = require('./rules/validate-manifest.cjs')
|
|
|
15
13
|
|
|
16
14
|
module.exports = {
|
|
17
15
|
rules: {
|
|
18
|
-
'no-dynamic-asset-paths': noDynamicAssetPaths,
|
|
19
16
|
'no-env-without-fallback': noEnvWithoutFallback,
|
|
20
|
-
'no-hardcoded-asset-paths': noHardcodedAssetPaths,
|
|
21
17
|
'no-internal-imports': noInternalImports,
|
|
22
18
|
'no-secrets': noSecrets,
|
|
23
19
|
'prefer-sdk-components': preferSdkComponents,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopify/shop-minis-react",
|
|
3
3
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
4
|
-
"version": "0.0.0-snapshot.
|
|
4
|
+
"version": "0.0.0-snapshot.20251212144430",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typescript": ">=5.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@shopify/shop-minis-platform": "0.0.0-snapshot.
|
|
49
|
+
"@shopify/shop-minis-platform": "0.0.0-snapshot.20251212144430",
|
|
50
50
|
"@tailwindcss/vite": "4.1.8",
|
|
51
51
|
"@tanstack/react-query": "5.86.0",
|
|
52
52
|
"@types/color": "3.0.6",
|
|
@@ -58,7 +58,7 @@ function MerchantCardContainer({
|
|
|
58
58
|
backgroundColor: cardTheme.backgroundColor,
|
|
59
59
|
}}
|
|
60
60
|
className={cn(
|
|
61
|
-
'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square',
|
|
61
|
+
'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square isolate',
|
|
62
62
|
|
|
63
63
|
className
|
|
64
64
|
)}
|
package/src/mocks.ts
CHANGED
|
@@ -318,6 +318,9 @@ export function makeMockActions(): ShopActions {
|
|
|
318
318
|
data: [
|
|
319
319
|
createShop('shop-1', 'Amazing Store'),
|
|
320
320
|
createShop('shop-2', 'Best Deals Shop'),
|
|
321
|
+
createShop('shop-3', 'Great Products'),
|
|
322
|
+
createShop('shop-4', 'Top Brands'),
|
|
323
|
+
createShop('shop-5', 'Exclusive Offers'),
|
|
321
324
|
],
|
|
322
325
|
pageInfo: createPagination(),
|
|
323
326
|
},
|
|
@@ -325,6 +328,9 @@ export function makeMockActions(): ShopActions {
|
|
|
325
328
|
data: [
|
|
326
329
|
createProduct('search-1', 'Search Result 1', '59.99'),
|
|
327
330
|
createProduct('search-2', 'Search Result 2', '89.99'),
|
|
331
|
+
createProduct('search-3', 'Search Result 3', '119.99'),
|
|
332
|
+
createProduct('search-4', 'Search Result 4', '149.99'),
|
|
333
|
+
createProduct('search-5', 'Search Result 5', '179.99'),
|
|
328
334
|
],
|
|
329
335
|
pageInfo: createPagination(),
|
|
330
336
|
},
|
|
@@ -387,11 +393,23 @@ export function makeMockActions(): ShopActions {
|
|
|
387
393
|
pageInfo: createPagination(),
|
|
388
394
|
},
|
|
389
395
|
getSavedProducts: {
|
|
390
|
-
data: [
|
|
396
|
+
data: [
|
|
397
|
+
createProduct('saved-1', 'Saved Product 1', '49.99'),
|
|
398
|
+
createProduct('saved-2', 'Saved Product 2', '59.99'),
|
|
399
|
+
createProduct('saved-3', 'Saved Product 3', '69.99'),
|
|
400
|
+
createProduct('saved-4', 'Saved Product 4', '79.99'),
|
|
401
|
+
createProduct('saved-5', 'Saved Product 5', '89.99'),
|
|
402
|
+
],
|
|
391
403
|
pageInfo: createPagination(),
|
|
392
404
|
},
|
|
393
405
|
getRecentProducts: {
|
|
394
|
-
data: [
|
|
406
|
+
data: [
|
|
407
|
+
createProduct('recent-1', 'Recent Product 1', '59.99'),
|
|
408
|
+
createProduct('recent-2', 'Recent Product 2', '69.99'),
|
|
409
|
+
createProduct('recent-3', 'Recent Product 3', '79.99'),
|
|
410
|
+
createProduct('recent-4', 'Recent Product 4', '89.99'),
|
|
411
|
+
createProduct('recent-5', 'Recent Product 5', '99.99'),
|
|
412
|
+
],
|
|
395
413
|
pageInfo: createPagination(),
|
|
396
414
|
},
|
|
397
415
|
getProductSearch: {
|
|
@@ -404,7 +422,15 @@ export function makeMockActions(): ShopActions {
|
|
|
404
422
|
],
|
|
405
423
|
pageInfo: createPagination(),
|
|
406
424
|
},
|
|
407
|
-
getProducts: {
|
|
425
|
+
getProducts: {
|
|
426
|
+
data: [
|
|
427
|
+
createProduct('prod-1', 'Product 1', '9.99'),
|
|
428
|
+
createProduct('prod-2', 'Product 2', '19.99'),
|
|
429
|
+
createProduct('prod-3', 'Product 3', '29.99'),
|
|
430
|
+
createProduct('prod-4', 'Product 4', '39.99'),
|
|
431
|
+
createProduct('prod-5', 'Product 5', '49.99'),
|
|
432
|
+
],
|
|
433
|
+
},
|
|
408
434
|
getProduct: {data: createProduct('prod-1', 'Sample Product')},
|
|
409
435
|
getProductVariants: {
|
|
410
436
|
data: [
|
|
@@ -434,11 +460,19 @@ export function makeMockActions(): ShopActions {
|
|
|
434
460
|
data: createShop('shop-1', 'Sample Shop', {featuredImagesLimit: 4}),
|
|
435
461
|
},
|
|
436
462
|
getRecentShops: {
|
|
437
|
-
data: [
|
|
463
|
+
data: [
|
|
464
|
+
createShop('recent-shop-1', 'Recent Shop 1'),
|
|
465
|
+
createShop('recent-shop-2', 'Recent Shop 2'),
|
|
466
|
+
createShop('recent-shop-3', 'Recent Shop 3'),
|
|
467
|
+
],
|
|
438
468
|
pageInfo: createPagination(),
|
|
439
469
|
},
|
|
440
470
|
getFollowedShops: {
|
|
441
|
-
data: [
|
|
471
|
+
data: [
|
|
472
|
+
createShop('followed-shop-1', 'Followed Shop 1'),
|
|
473
|
+
createShop('followed-shop-2', 'Followed Shop 2'),
|
|
474
|
+
createShop('followed-shop-3', 'Followed Shop 3'),
|
|
475
|
+
],
|
|
442
476
|
pageInfo: createPagination(),
|
|
443
477
|
},
|
|
444
478
|
previewProductInAr: undefined,
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared patterns for asset path detection rules
|
|
3
|
-
* @fileoverview Regex patterns for detecting asset file extensions and path types
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Asset file extensions to detect
|
|
7
|
-
const ASSET_EXTENSIONS = /\.(png|jpe?g|gif|svg|webp|ico|bmp|avif)$/i
|
|
8
|
-
|
|
9
|
-
// Patterns that indicate a local path (not a remote URL)
|
|
10
|
-
const LOCAL_PATH_PATTERNS = [
|
|
11
|
-
/^\.?\//, // Starts with / or ./
|
|
12
|
-
/^\.\.\//, // Starts with ../
|
|
13
|
-
/\/src\//i, // Contains /src/
|
|
14
|
-
/\/assets\//i, // Contains /assets/
|
|
15
|
-
/\/images?\//i, // Contains /image/ or /images/
|
|
16
|
-
/\/public\//i, // Contains /public/
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
// Patterns that indicate a remote URL or data URL (should be allowed)
|
|
20
|
-
const REMOTE_PATTERNS = [
|
|
21
|
-
/^https?:\/\//i, // http:// or https://
|
|
22
|
-
/^data:/i, // data: URLs
|
|
23
|
-
/^blob:/i, // blob: URLs
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
module.exports = {
|
|
27
|
-
ASSET_EXTENSIONS,
|
|
28
|
-
LOCAL_PATH_PATTERNS,
|
|
29
|
-
REMOTE_PATTERNS,
|
|
30
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESLint rule to warn about dynamic asset paths in template literals
|
|
3
|
-
* @fileoverview Warns about template literals that may construct asset paths dynamically
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
ASSET_EXTENSIONS,
|
|
8
|
-
LOCAL_PATH_PATTERNS,
|
|
9
|
-
REMOTE_PATTERNS,
|
|
10
|
-
// eslint-disable-next-line import/extensions
|
|
11
|
-
} = require('./asset-path-patterns.cjs')
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Check if a template literal might contain an asset path but we can't be certain
|
|
15
|
-
* Returns true only for UNCERTAIN cases (ends with asset extension but doesn't
|
|
16
|
-
* start with a known local path pattern). Definite cases are handled by
|
|
17
|
-
* no-hardcoded-asset-paths rule.
|
|
18
|
-
* @param {object} node - The TemplateLiteral AST node
|
|
19
|
-
* @returns {boolean}
|
|
20
|
-
*/
|
|
21
|
-
function isUncertainAssetTemplateLiteral(node) {
|
|
22
|
-
const quasis = node.quasis
|
|
23
|
-
if (quasis.length === 0) return false
|
|
24
|
-
|
|
25
|
-
// Get the first static part to check if it starts with a local path
|
|
26
|
-
const firstPart = quasis[0].value.raw || quasis[0].value.cooked || ''
|
|
27
|
-
|
|
28
|
-
// Get the last static part to check if it ends with an asset extension
|
|
29
|
-
const lastPart =
|
|
30
|
-
quasis[quasis.length - 1].value.raw ||
|
|
31
|
-
quasis[quasis.length - 1].value.cooked ||
|
|
32
|
-
''
|
|
33
|
-
|
|
34
|
-
// Must end with an asset extension
|
|
35
|
-
if (!ASSET_EXTENSIONS.test(lastPart)) return false
|
|
36
|
-
|
|
37
|
-
// Check if it starts with a remote URL (only need to check first static part)
|
|
38
|
-
if (REMOTE_PATTERNS.some(pattern => pattern.test(firstPart))) return false
|
|
39
|
-
|
|
40
|
-
// If it starts with a local path pattern, it's a DEFINITE error (handled by other rule)
|
|
41
|
-
// We only want to flag UNCERTAIN cases here
|
|
42
|
-
if (LOCAL_PATH_PATTERNS.some(pattern => pattern.test(firstPart))) return false
|
|
43
|
-
|
|
44
|
-
// Ends with asset extension but doesn't start with known local path = uncertain
|
|
45
|
-
return true
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
module.exports = {
|
|
49
|
-
meta: {
|
|
50
|
-
type: 'suggestion',
|
|
51
|
-
docs: {
|
|
52
|
-
description:
|
|
53
|
-
'Warn about template literals that may construct asset paths dynamically',
|
|
54
|
-
category: 'Best Practices',
|
|
55
|
-
recommended: true,
|
|
56
|
-
url: 'https://vite.dev/guide/assets',
|
|
57
|
-
},
|
|
58
|
-
messages: {
|
|
59
|
-
noDynamicAssetPath:
|
|
60
|
-
'Template literal may contain a hardcoded asset path that will not work in production. Import assets instead of constructing paths dynamically. See: https://vite.dev/guide/assets',
|
|
61
|
-
},
|
|
62
|
-
schema: [],
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
create(context) {
|
|
66
|
-
return {
|
|
67
|
-
TemplateLiteral(node) {
|
|
68
|
-
// Only flag uncertain cases (definite cases handled by no-hardcoded-asset-paths)
|
|
69
|
-
if (isUncertainAssetTemplateLiteral(node)) {
|
|
70
|
-
context.report({
|
|
71
|
-
node,
|
|
72
|
-
messageId: 'noDynamicAssetPath',
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESLint rule to detect hardcoded local asset paths that won't work in production
|
|
3
|
-
* @fileoverview Prevents using un-imported hardcoded asset paths that work in Vite dev but fail in production
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
ASSET_EXTENSIONS,
|
|
8
|
-
LOCAL_PATH_PATTERNS,
|
|
9
|
-
REMOTE_PATTERNS,
|
|
10
|
-
// eslint-disable-next-line import/extensions
|
|
11
|
-
} = require('./asset-path-patterns.cjs')
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Check if a string looks like a local asset path
|
|
15
|
-
* @param {string} value - The string to check
|
|
16
|
-
* @returns {boolean}
|
|
17
|
-
*/
|
|
18
|
-
function isLocalAssetPath(value) {
|
|
19
|
-
if (typeof value !== 'string') return false
|
|
20
|
-
|
|
21
|
-
// Must have an asset extension
|
|
22
|
-
if (!ASSET_EXTENSIONS.test(value)) return false
|
|
23
|
-
|
|
24
|
-
// Skip remote URLs and data URLs
|
|
25
|
-
if (REMOTE_PATTERNS.some(pattern => pattern.test(value))) return false
|
|
26
|
-
|
|
27
|
-
// Check if it looks like a local path
|
|
28
|
-
return LOCAL_PATH_PATTERNS.some(pattern => pattern.test(value))
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Check if a template literal is definitely a local asset path
|
|
33
|
-
* Returns true only if we can determine with certainty it's a local path
|
|
34
|
-
* (starts with a local path pattern AND ends with an asset extension)
|
|
35
|
-
* @param {object} node - The TemplateLiteral AST node
|
|
36
|
-
* @returns {boolean}
|
|
37
|
-
*/
|
|
38
|
-
function isLocalAssetTemplateLiteral(node) {
|
|
39
|
-
const quasis = node.quasis
|
|
40
|
-
if (quasis.length === 0) return false
|
|
41
|
-
|
|
42
|
-
// Get the first static part to check if it starts with a local path
|
|
43
|
-
const firstPart = quasis[0].value.raw || quasis[0].value.cooked || ''
|
|
44
|
-
|
|
45
|
-
// Get the last static part to check if it ends with an asset extension
|
|
46
|
-
const lastPart =
|
|
47
|
-
quasis[quasis.length - 1].value.raw ||
|
|
48
|
-
quasis[quasis.length - 1].value.cooked ||
|
|
49
|
-
''
|
|
50
|
-
|
|
51
|
-
// Must end with an asset extension
|
|
52
|
-
if (!ASSET_EXTENSIONS.test(lastPart)) return false
|
|
53
|
-
|
|
54
|
-
// Check if it starts with a remote URL (only need to check first static part)
|
|
55
|
-
if (REMOTE_PATTERNS.some(pattern => pattern.test(firstPart))) return false
|
|
56
|
-
|
|
57
|
-
// Must start with a local path pattern (this makes it a definite error)
|
|
58
|
-
return LOCAL_PATH_PATTERNS.some(pattern => pattern.test(firstPart))
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
module.exports = {
|
|
62
|
-
meta: {
|
|
63
|
-
type: 'problem',
|
|
64
|
-
docs: {
|
|
65
|
-
description:
|
|
66
|
-
'Disallow hardcoded local asset paths that work in dev but fail in production',
|
|
67
|
-
category: 'Possible Errors',
|
|
68
|
-
recommended: true,
|
|
69
|
-
url: 'https://vite.dev/guide/assets',
|
|
70
|
-
},
|
|
71
|
-
messages: {
|
|
72
|
-
noHardcodedAssetPath:
|
|
73
|
-
'Hardcoded asset path "{{path}}" will not work in production. Import the asset instead: `import asset from "{{path}}"` then use the imported variable. See: https://vite.dev/guide/assets',
|
|
74
|
-
noHardcodedAssetPathTemplate:
|
|
75
|
-
'Template literal contains a hardcoded local asset path that will not work in production. Import assets instead of constructing paths. See: https://vite.dev/guide/assets',
|
|
76
|
-
},
|
|
77
|
-
schema: [],
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
create(context) {
|
|
81
|
-
return {
|
|
82
|
-
Literal(node) {
|
|
83
|
-
// Only check string literals
|
|
84
|
-
if (typeof node.value !== 'string') return
|
|
85
|
-
|
|
86
|
-
// Skip import/export declarations - those are the CORRECT way to use assets
|
|
87
|
-
const parent = node.parent
|
|
88
|
-
if (
|
|
89
|
-
parent &&
|
|
90
|
-
(parent.type === 'ImportDeclaration' ||
|
|
91
|
-
parent.type === 'ExportNamedDeclaration' ||
|
|
92
|
-
parent.type === 'ExportAllDeclaration' ||
|
|
93
|
-
(parent.type === 'CallExpression' &&
|
|
94
|
-
parent.callee &&
|
|
95
|
-
(parent.callee.name === 'require' ||
|
|
96
|
-
parent.callee.type === 'Import')))
|
|
97
|
-
) {
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (isLocalAssetPath(node.value)) {
|
|
102
|
-
context.report({
|
|
103
|
-
node,
|
|
104
|
-
messageId: 'noHardcodedAssetPath',
|
|
105
|
-
data: {
|
|
106
|
-
path: node.value,
|
|
107
|
-
},
|
|
108
|
-
})
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
TemplateLiteral(node) {
|
|
113
|
-
// Only flag template literals that are DEFINITELY local asset paths
|
|
114
|
-
// (start with local path pattern AND end with asset extension)
|
|
115
|
-
if (isLocalAssetTemplateLiteral(node)) {
|
|
116
|
-
context.report({
|
|
117
|
-
node,
|
|
118
|
-
messageId: 'noHardcodedAssetPathTemplate',
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
}
|