@shopify/shop-minis-react 0.0.34 → 0.0.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/_virtual/index2.js +4 -4
  2. package/dist/_virtual/index3.js +4 -4
  3. package/dist/components/atoms/alert-dialog.js.map +1 -1
  4. package/dist/components/atoms/icon-button.js +12 -12
  5. package/dist/components/atoms/icon-button.js.map +1 -1
  6. package/dist/components/atoms/text-input.js +22 -0
  7. package/dist/components/atoms/text-input.js.map +1 -0
  8. package/dist/components/commerce/merchant-card.js +1 -0
  9. package/dist/components/commerce/merchant-card.js.map +1 -1
  10. package/dist/components/ui/input.js +15 -9
  11. package/dist/components/ui/input.js.map +1 -1
  12. package/dist/hooks/util/useKeyboardAvoidingView.js +23 -0
  13. package/dist/hooks/util/useKeyboardAvoidingView.js.map +1 -0
  14. package/dist/hooks/util/useShare.js +7 -6
  15. package/dist/hooks/util/useShare.js.map +1 -1
  16. package/dist/index.js +228 -222
  17. package/dist/index.js.map +1 -1
  18. package/dist/mocks.js +17 -10
  19. package/dist/mocks.js.map +1 -1
  20. package/dist/shop-minis-platform/src/types/share.js +5 -0
  21. package/dist/shop-minis-platform/src/types/share.js.map +1 -0
  22. package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +1 -1
  23. package/dist/utils/colors.js +1 -1
  24. package/dist/utils/image.js +5 -4
  25. package/dist/utils/image.js.map +1 -1
  26. package/package.json +21 -4
  27. package/src/components/atoms/alert-dialog.test.tsx +67 -0
  28. package/src/components/atoms/alert-dialog.tsx +13 -11
  29. package/src/components/atoms/favorite-button.test.tsx +56 -0
  30. package/src/components/atoms/icon-button.tsx +1 -1
  31. package/src/components/atoms/image.test.tsx +108 -0
  32. package/src/components/atoms/product-variant-price.test.tsx +128 -0
  33. package/src/components/atoms/text-input.test.tsx +104 -0
  34. package/src/components/atoms/text-input.tsx +31 -0
  35. package/src/components/commerce/merchant-card.test.tsx +261 -0
  36. package/src/components/commerce/merchant-card.tsx +2 -0
  37. package/src/components/commerce/product-card.test.tsx +364 -0
  38. package/src/components/commerce/product-link.test.tsx +483 -0
  39. package/src/components/commerce/quantity-selector.test.tsx +382 -0
  40. package/src/components/commerce/search.test.tsx +487 -0
  41. package/src/components/content/image-content-wrapper.test.tsx +92 -0
  42. package/src/components/index.ts +1 -0
  43. package/src/components/navigation/transition-link.test.tsx +155 -0
  44. package/src/components/ui/input.test.tsx +21 -0
  45. package/src/components/ui/input.tsx +10 -1
  46. package/src/hooks/content/useCreateImageContent.test.ts +352 -0
  47. package/src/hooks/index.ts +1 -0
  48. package/src/hooks/navigation/useNavigateWithTransition.test.ts +371 -0
  49. package/src/hooks/navigation/useViewTransitions.test.ts +469 -0
  50. package/src/hooks/product/useProductSearch.test.ts +470 -0
  51. package/src/hooks/storage/useAsyncStorage.test.ts +225 -0
  52. package/src/hooks/storage/useImageUpload.test.ts +322 -0
  53. package/src/hooks/util/useKeyboardAvoidingView.ts +37 -0
  54. package/src/hooks/util/useShare.ts +13 -3
  55. package/src/internal/useHandleAction.test.ts +265 -0
  56. package/src/internal/useShopActionsDataFetching.test.ts +465 -0
  57. package/src/mocks.ts +7 -1
  58. package/src/providers/ImagePickerProvider.test.tsx +467 -0
  59. package/src/stories/ProductCard.stories.tsx +2 -2
  60. package/src/stories/TextInput.stories.tsx +26 -0
  61. package/src/test-setup.ts +34 -0
  62. package/src/test-utils.tsx +167 -0
  63. package/src/utils/image.ts +1 -0
package/dist/mocks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mocks.js","sources":["../src/mocks.ts"],"sourcesContent":["import {Product, Gender, UserState} 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\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 console.log(`[Mock Action] ${String(key)}`, params)\n return Promise.resolve({ok: true as const, data: result})\n }) as ShopActions[K]\n}\n\nfunction makeMockActions(): ShopActions {\n const results: {\n [K in keyof ShopActions]: ShopActionDataType<ShopActions[K]>\n } = {\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 hideEntryPoint: 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: 'stored-value',\n setPersistedItem: undefined,\n removePersistedItem: undefined,\n getAllPersistedKeys: ['key1', 'key2', 'key3'],\n clearPersistedItems: undefined,\n getInternalPersistedItem: 'internal-value',\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 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: [createProduct('search-3', 'Search Product 3', '39.99')],\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 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: {data: createShop('shop-1', 'Sample Shop')},\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 },\n ],\n },\n generateUserToken: {\n data: {\n token: 'user-token-123',\n expiresAt: '2025-01-01',\n userState: UserState.VERIFIED,\n },\n },\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","featuredImages","_","i","createPagination","hasNext","createProductList","products","makeMockMethod","key","result","params","makeMockActions","results","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 {Product, Gender, UserState} 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\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 console.log(`[Mock Action] ${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 hideEntryPoint: 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: 'stored-value',\n setPersistedItem: undefined,\n removePersistedItem: undefined,\n getAllPersistedKeys: ['key1', 'key2', 'key3'],\n clearPersistedItems: undefined,\n getInternalPersistedItem: 'internal-value',\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: [createProduct('search-3', 'Search Product 3', '39.99')],\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 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: {data: createShop('shop-1', 'Sample Shop')},\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 },\n ],\n },\n generateUserToken: {\n data: {\n token: 'user-token-123',\n expiresAt: '2025-01-01',\n userState: UserState.VERIFIED,\n },\n },\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","featuredImages","_","i","createPagination","hasNext","createProductList","products","makeMockMethod","key","result","params","makeMockActions","results","UserState","mock","isMobile","userAgent","isIOS","isAndroid","injectMocks","force"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ var s = /* @__PURE__ */ ((e) => (e.Facebook = "facebook", e.FacebookStories = "facebookstories", e.Pagesmanager = "pagesmanager", e.Twitter = "twitter", e.Whatsapp = "whatsapp", e.Whatsappbusiness = "whatsappbusiness", e.Instagram = "instagram", e.InstagramStories = "instagramstories", e.Googleplus = "googleplus", e.Email = "email", e.Pinterest = "pinterest", e.Linkedin = "linkedin", e.Sms = "sms", e.Telegram = "telegram", e.Snapchat = "snapchat", e.Messenger = "messenger", e.Viber = "viber", e.Discord = "discord", e))(s || {});
2
+ export {
3
+ s as Social
4
+ };
5
+ //# sourceMappingURL=share.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.js","sources":["../../../../../shop-minis-platform/src/types/share.ts"],"sourcesContent":["// Based on react-native-share shareSingle parameters\nexport enum Social {\n Facebook = 'facebook',\n FacebookStories = 'facebookstories',\n Pagesmanager = 'pagesmanager',\n Twitter = 'twitter',\n Whatsapp = 'whatsapp',\n Whatsappbusiness = 'whatsappbusiness',\n Instagram = 'instagram',\n InstagramStories = 'instagramstories',\n Googleplus = 'googleplus',\n Email = 'email',\n Pinterest = 'pinterest',\n Linkedin = 'linkedin',\n Sms = 'sms',\n Telegram = 'telegram',\n Snapchat = 'snapchat',\n Messenger = 'messenger',\n Viber = 'viber',\n Discord = 'discord',\n}\n\nexport interface BaseShareSingleOptions {\n appId?: string\n urls?: string[]\n url?: string\n type?: string\n filename?: string\n message?: string\n title?: string\n subject?: string\n email?: string\n recipient?: string\n social: Exclude<Social, Social.FacebookStories | Social.InstagramStories>\n forceDialog?: boolean\n}\n\nexport interface InstagramShareSingleOptions\n extends BaseSocialStoriesShareSingleOptions {\n social: Social.InstagramStories\n appId: string\n}\n\nexport interface FacebookShareSingleOptions\n extends BaseSocialStoriesShareSingleOptions {\n social: Social.FacebookStories\n appId: string\n}\n\ninterface BaseSocialStoriesShareSingleOptions\n extends Omit<BaseShareSingleOptions, 'social'> {\n backgroundImage?: string\n stickerImage?: string\n backgroundBottomColor?: string\n backgroundTopColor?: string\n attributionURL?: string\n backgroundVideo?: string\n linkUrl?: string\n linkText?: string\n}\n"],"names":["Social"],"mappings":"AACY,IAAAA,sBAAAA,OACVA,EAAA,WAAW,YACXA,EAAA,kBAAkB,mBAClBA,EAAA,eAAe,gBACfA,EAAA,UAAU,WACVA,EAAA,WAAW,YACXA,EAAA,mBAAmB,oBACnBA,EAAA,YAAY,aACZA,EAAA,mBAAmB,oBACnBA,EAAA,aAAa,cACbA,EAAA,QAAQ,SACRA,EAAA,YAAY,aACZA,EAAA,WAAW,YACXA,EAAA,MAAM,OACNA,EAAA,WAAW,YACXA,EAAA,WAAW,YACXA,EAAA,YAAY,aACZA,EAAA,QAAQ,SACRA,EAAA,UAAU,WAlBAA,IAAAA,KAAA,CAAA,CAAA;"}
@@ -1,6 +1,6 @@
1
1
  import S from "../../../../../../../_virtual/window.js";
2
2
  import B from "../../../../../../../_virtual/document.js";
3
- import il from "../../../../../../../_virtual/index3.js";
3
+ import il from "../../../../../../../_virtual/index2.js";
4
4
  import ao from "../../../../../../../_virtual/browser-index.js";
5
5
  import xe from "../../../../@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/extends.js";
6
6
  import Kc from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/resolve-url.js";
@@ -1,4 +1,4 @@
1
- import o from "../_virtual/index2.js";
1
+ import o from "../_virtual/index3.js";
2
2
  const a = (r) => o(r).darken(0.2).isDark();
3
3
  export {
4
4
  a as isDarkColor
@@ -7,9 +7,10 @@ function U(t) {
7
7
  return i(e);
8
8
  } catch (e) {
9
9
  console.warn("Failed to decode thumbhash to data URL", e);
10
+ return;
10
11
  }
11
12
  }
12
- function f(t) {
13
+ function u(t) {
13
14
  return new Promise((e, n) => {
14
15
  const r = new FileReader();
15
16
  r.onloadend = () => e(r.result), r.onerror = n, r.readAsDataURL(t);
@@ -36,7 +37,7 @@ const o = {
36
37
  }, m = (t, e) => {
37
38
  const r = new RegExp(/\?+/g).test(t) ? "&" : "?";
38
39
  return `${t}${r}width=${e}`;
39
- }, u = (t) => {
40
+ }, f = (t) => {
40
41
  if (!t) return "";
41
42
  if (!t.startsWith("https://cdn.shopify.com"))
42
43
  return t;
@@ -44,8 +45,8 @@ const o = {
44
45
  return m(t, o[n]);
45
46
  };
46
47
  export {
47
- f as fileToDataUri,
48
- u as getResizedImageUrl,
48
+ u as fileToDataUri,
49
+ f as getResizedImageUrl,
49
50
  U as getThumbhashDataURL
50
51
  };
51
52
  //# sourceMappingURL=image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sources":["../../src/utils/image.ts"],"sourcesContent":["import {toUint8Array} from 'js-base64'\nimport {thumbHashToDataURL} from 'thumbhash'\n\n/**\n * Converts a thumbhash string to a data URL for use as an image placeholder\n * @param thumbhash Base64 encoded thumbhash string\n * @returns Data URL that can be used as image source or undefined if conversion fails\n */\nexport function getThumbhashDataURL(thumbhash?: string): string | undefined {\n if (!thumbhash) return\n\n try {\n const thumbhashArray = toUint8Array(thumbhash)\n return thumbHashToDataURL(thumbhashArray)\n } catch (error) {\n console.warn('Failed to decode thumbhash to data URL', error)\n }\n}\n\n/** Converts a file to a data URI\n * @param file The file to convert\n * @returns A promise that resolves to the data URI string\n */\nexport function fileToDataUri(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onloadend = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nconst ImageSizes = {\n xxsUrl: 32,\n xsUrl: 64,\n sUrl: 128,\n xxsmUrl: 256,\n xsmUrl: 384,\n smUrl: 512,\n mUrl: 640,\n lUrl: 1080,\n xlUrl: 2048,\n} as const\n\ntype Key = keyof typeof ImageSizes\n\n/**\n * Acceptable offset for image sizes. An image could use the size that is within this offset.\n */\nconst offsetPercentage = 0.05\n\nconst sortedImageSizes = Object.entries(ImageSizes).sort(\n ([, firstSize], [, secondSize]) => firstSize - secondSize\n)\n\nconst getImageSizeKeyWithSize = (size: number): Key => {\n for (const [key, imgSize] of sortedImageSizes) {\n const upperBoundSize = imgSize + imgSize * offsetPercentage\n if (size <= upperBoundSize) return key as Key\n }\n\n return 'xlUrl'\n}\n\nconst resizeImage = (imageUrl: string, width: number) => {\n const pattern = new RegExp(/\\?+/g)\n const delimiter = pattern.test(imageUrl) ? '&' : '?'\n return `${imageUrl}${delimiter}width=${width}`\n}\n\n/**\n * Optimizes Shopify CDN image URLs by adding a width parameter based on screen size\n * @param url The image URL to optimize\n * @returns The optimized URL with width parameter if it's a Shopify CDN image, otherwise returns the original URL\n */\n\nexport const getResizedImageUrl = (url?: string): string => {\n if (!url) return ''\n\n // Only process Shopify CDN images\n if (!url.startsWith('https://cdn.shopify.com')) {\n return url\n }\n\n const width = window.innerWidth ?? screen.width\n\n const key = getImageSizeKeyWithSize(width)\n\n return resizeImage(url, ImageSizes[key])\n}\n"],"names":["getThumbhashDataURL","thumbhash","thumbhashArray","toUint8Array","thumbHashToDataURL","error","fileToDataUri","file","resolve","reject","reader","ImageSizes","offsetPercentage","sortedImageSizes","firstSize","secondSize","getImageSizeKeyWithSize","size","key","imgSize","upperBoundSize","resizeImage","imageUrl","width","delimiter","getResizedImageUrl","url"],"mappings":";;AAQO,SAASA,EAAoBC,GAAwC;AAC1E,MAAKA;AAED,QAAA;AACI,YAAAC,IAAiBC,EAAaF,CAAS;AAC7C,aAAOG,EAAmBF,CAAc;AAAA,aACjCG,GAAO;AACN,cAAA,KAAK,0CAA0CA,CAAK;AAAA,IAAA;AAEhE;AAMO,SAASC,EAAcC,GAA6B;AACzD,SAAO,IAAI,QAAQ,CAACC,GAASC,MAAW;AAChC,UAAAC,IAAS,IAAI,WAAW;AAC9B,IAAAA,EAAO,YAAY,MAAMF,EAAQE,EAAO,MAAgB,GACxDA,EAAO,UAAUD,GACjBC,EAAO,cAAcH,CAAI;AAAA,EAAA,CAC1B;AACH;AAEA,MAAMI,IAAa;AAAA,EACjB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT,GAOMC,IAAmB,MAEnBC,IAAmB,OAAO,QAAQF,CAAU,EAAE;AAAA,EAClD,CAAC,CAAG,EAAAG,CAAS,GAAG,GAAGC,CAAU,MAAMD,IAAYC;AACjD,GAEMC,IAA0B,CAACC,MAAsB;AACrD,aAAW,CAACC,GAAKC,CAAO,KAAKN,GAAkB;AACvC,UAAAO,IAAiBD,IAAUA,IAAUP;AACvC,QAAAK,KAAQG,EAAuB,QAAAF;AAAA,EAAA;AAG9B,SAAA;AACT,GAEMG,IAAc,CAACC,GAAkBC,MAAkB;AAEvD,QAAMC,IADU,IAAI,OAAO,MAAM,EACP,KAAKF,CAAQ,IAAI,MAAM;AACjD,SAAO,GAAGA,CAAQ,GAAGE,CAAS,SAASD,CAAK;AAC9C,GAQaE,IAAqB,CAACC,MAAyB;AACtD,MAAA,CAACA,EAAY,QAAA;AAGjB,MAAI,CAACA,EAAI,WAAW,yBAAyB;AACpC,WAAAA;AAGH,QAAAH,IAAQ,OAAO,cAAc,OAAO,OAEpCL,IAAMF,EAAwBO,CAAK;AAEzC,SAAOF,EAAYK,GAAKf,EAAWO,CAAG,CAAC;AACzC;"}
1
+ {"version":3,"file":"image.js","sources":["../../src/utils/image.ts"],"sourcesContent":["import {toUint8Array} from 'js-base64'\nimport {thumbHashToDataURL} from 'thumbhash'\n\n/**\n * Converts a thumbhash string to a data URL for use as an image placeholder\n * @param thumbhash Base64 encoded thumbhash string\n * @returns Data URL that can be used as image source or undefined if conversion fails\n */\nexport function getThumbhashDataURL(thumbhash?: string): string | undefined {\n if (!thumbhash) return\n\n try {\n const thumbhashArray = toUint8Array(thumbhash)\n return thumbHashToDataURL(thumbhashArray)\n } catch (error) {\n console.warn('Failed to decode thumbhash to data URL', error)\n return undefined\n }\n}\n\n/** Converts a file to a data URI\n * @param file The file to convert\n * @returns A promise that resolves to the data URI string\n */\nexport function fileToDataUri(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onloadend = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nconst ImageSizes = {\n xxsUrl: 32,\n xsUrl: 64,\n sUrl: 128,\n xxsmUrl: 256,\n xsmUrl: 384,\n smUrl: 512,\n mUrl: 640,\n lUrl: 1080,\n xlUrl: 2048,\n} as const\n\ntype Key = keyof typeof ImageSizes\n\n/**\n * Acceptable offset for image sizes. An image could use the size that is within this offset.\n */\nconst offsetPercentage = 0.05\n\nconst sortedImageSizes = Object.entries(ImageSizes).sort(\n ([, firstSize], [, secondSize]) => firstSize - secondSize\n)\n\nconst getImageSizeKeyWithSize = (size: number): Key => {\n for (const [key, imgSize] of sortedImageSizes) {\n const upperBoundSize = imgSize + imgSize * offsetPercentage\n if (size <= upperBoundSize) return key as Key\n }\n\n return 'xlUrl'\n}\n\nconst resizeImage = (imageUrl: string, width: number) => {\n const pattern = new RegExp(/\\?+/g)\n const delimiter = pattern.test(imageUrl) ? '&' : '?'\n return `${imageUrl}${delimiter}width=${width}`\n}\n\n/**\n * Optimizes Shopify CDN image URLs by adding a width parameter based on screen size\n * @param url The image URL to optimize\n * @returns The optimized URL with width parameter if it's a Shopify CDN image, otherwise returns the original URL\n */\n\nexport const getResizedImageUrl = (url?: string): string => {\n if (!url) return ''\n\n // Only process Shopify CDN images\n if (!url.startsWith('https://cdn.shopify.com')) {\n return url\n }\n\n const width = window.innerWidth ?? screen.width\n\n const key = getImageSizeKeyWithSize(width)\n\n return resizeImage(url, ImageSizes[key])\n}\n"],"names":["getThumbhashDataURL","thumbhash","thumbhashArray","toUint8Array","thumbHashToDataURL","error","fileToDataUri","file","resolve","reject","reader","ImageSizes","offsetPercentage","sortedImageSizes","firstSize","secondSize","getImageSizeKeyWithSize","size","key","imgSize","upperBoundSize","resizeImage","imageUrl","width","delimiter","getResizedImageUrl","url"],"mappings":";;AAQO,SAASA,EAAoBC,GAAwC;AAC1E,MAAKA;AAED,QAAA;AACI,YAAAC,IAAiBC,EAAaF,CAAS;AAC7C,aAAOG,EAAmBF,CAAc;AAAA,aACjCG,GAAO;AACN,cAAA,KAAK,0CAA0CA,CAAK;AACrD;AAAA,IAAA;AAEX;AAMO,SAASC,EAAcC,GAA6B;AACzD,SAAO,IAAI,QAAQ,CAACC,GAASC,MAAW;AAChC,UAAAC,IAAS,IAAI,WAAW;AAC9B,IAAAA,EAAO,YAAY,MAAMF,EAAQE,EAAO,MAAgB,GACxDA,EAAO,UAAUD,GACjBC,EAAO,cAAcH,CAAI;AAAA,EAAA,CAC1B;AACH;AAEA,MAAMI,IAAa;AAAA,EACjB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT,GAOMC,IAAmB,MAEnBC,IAAmB,OAAO,QAAQF,CAAU,EAAE;AAAA,EAClD,CAAC,CAAG,EAAAG,CAAS,GAAG,GAAGC,CAAU,MAAMD,IAAYC;AACjD,GAEMC,IAA0B,CAACC,MAAsB;AACrD,aAAW,CAACC,GAAKC,CAAO,KAAKN,GAAkB;AACvC,UAAAO,IAAiBD,IAAUA,IAAUP;AACvC,QAAAK,KAAQG,EAAuB,QAAAF;AAAA,EAAA;AAG9B,SAAA;AACT,GAEMG,IAAc,CAACC,GAAkBC,MAAkB;AAEvD,QAAMC,IADU,IAAI,OAAO,MAAM,EACP,KAAKF,CAAQ,IAAI,MAAM;AACjD,SAAO,GAAGA,CAAQ,GAAGE,CAAS,SAASD,CAAK;AAC9C,GAQaE,IAAqB,CAACC,MAAyB;AACtD,MAAA,CAACA,EAAY,QAAA;AAGjB,MAAI,CAACA,EAAI,WAAW,yBAAyB;AACpC,WAAAA;AAGH,QAAAH,IAAQ,OAAO,cAAc,OAAO,OAEpCL,IAAMF,EAAwBO,CAAK;AAEzC,SAAOF,EAAYK,GAAKf,EAAWO,CAAG,CAAC;AACzC;"}
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.34",
4
+ "version": "0.0.36",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "engines": {
@@ -38,7 +38,7 @@
38
38
  "typescript": ">=5.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@shopify/shop-minis-platform": "0.0.11",
41
+ "@shopify/shop-minis-platform": "0.1.0",
42
42
  "@tailwindcss/vite": "4.1.8",
43
43
  "@types/color": "3.0.6",
44
44
  "@types/lodash": "4.17.20",
@@ -70,20 +70,29 @@
70
70
  "video.js": "8.23.3"
71
71
  },
72
72
  "devDependencies": {
73
+ "@playwright/test": "^1.54.2",
73
74
  "@shopify/generate-docs": "^0.16.6",
74
75
  "@storybook/addon-docs": "^9.0.16",
75
76
  "@storybook/react-vite": "^9.0.16",
77
+ "@testing-library/jest-dom": "^6.6.4",
78
+ "@testing-library/react": "^16.1.0",
79
+ "@testing-library/user-event": "^14.5.2",
76
80
  "@types/node": "^20.12.1",
77
81
  "@types/react": "^19.1.6",
78
82
  "@types/react-dom": "^19.1.2",
83
+ "@vitest/coverage-v8": "^2.1.8",
84
+ "@vitest/ui": "^2.1.8",
79
85
  "eslint": "^8.57.0",
80
86
  "eslint-plugin-storybook": "^9.0.16",
87
+ "jsdom": "^25.0.1",
81
88
  "react": "^19.1.0",
82
89
  "react-dom": "^19.1.0",
90
+ "serve": "^14.2.4",
83
91
  "storybook": "^9.0.16",
84
92
  "typescript": "^5.8.3",
85
93
  "vite": "^6.3.3",
86
- "vite-plugin-dts": "^4.3.0"
94
+ "vite-plugin-dts": "^4.3.0",
95
+ "vitest": "^2.1.8"
87
96
  },
88
97
  "keywords": [
89
98
  "react",
@@ -112,6 +121,14 @@
112
121
  "storybook": "storybook dev -p 6006",
113
122
  "build-storybook": "storybook build -o ../../web/public/minis-playground",
114
123
  "lint": "eslint . --format codeframe --cache",
115
- "lint:fix": "eslint . --format codeframe --cache --fix"
124
+ "lint:fix": "eslint . --format codeframe --cache --fix",
125
+ "test": "vitest",
126
+ "test:ci": "vitest run --reporter=verbose",
127
+ "test:ci:coverage": "vitest run --coverage --reporter=verbose",
128
+ "test:ui": "vitest --ui",
129
+ "test:coverage": "vitest --coverage",
130
+ "test:integration": "npx playwright test --config=playwright.integration.config.ts",
131
+ "test:integration:local": "./scripts/test-integration-local.sh",
132
+ "playwright:install": "npx playwright install"
116
133
  }
117
134
  }
@@ -0,0 +1,67 @@
1
+ import {describe, expect, it, vi} from 'vitest'
2
+
3
+ import {render, screen, userEvent} from '../../test-utils'
4
+
5
+ import {AlertDialogAtom} from './alert-dialog'
6
+
7
+ describe('AlertDialogAtom', () => {
8
+ const defaultProps = {
9
+ title: 'Confirm Action',
10
+ description: 'Are you sure you want to proceed?',
11
+ cancelButtonText: 'Cancel',
12
+ confirmationButtonText: 'Confirm',
13
+ open: false,
14
+ onOpenChange: vi.fn(),
15
+ onConfirmationAction: vi.fn(),
16
+ }
17
+
18
+ it('renders trigger element', () => {
19
+ render(
20
+ <AlertDialogAtom {...defaultProps}>
21
+ <button type="button">Open Dialog</button>
22
+ </AlertDialogAtom>
23
+ )
24
+
25
+ expect(
26
+ screen.getByRole('button', {name: /open dialog/i})
27
+ ).toBeInTheDocument()
28
+ })
29
+
30
+ it('shows dialog content when open', () => {
31
+ render(
32
+ <AlertDialogAtom {...defaultProps} open>
33
+ <button type="button">Open Dialog</button>
34
+ </AlertDialogAtom>
35
+ )
36
+
37
+ expect(screen.getByText(defaultProps.title)).toBeInTheDocument()
38
+ expect(screen.getByText(defaultProps.description)).toBeInTheDocument()
39
+ expect(
40
+ screen.getByRole('button', {name: defaultProps.cancelButtonText})
41
+ ).toBeInTheDocument()
42
+ expect(
43
+ screen.getByRole('button', {name: defaultProps.confirmationButtonText})
44
+ ).toBeInTheDocument()
45
+ })
46
+
47
+ it('calls onConfirmationAction when confirm button is clicked', async () => {
48
+ const user = userEvent.setup()
49
+ const onConfirmationAction = vi.fn()
50
+
51
+ render(
52
+ <AlertDialogAtom
53
+ {...defaultProps}
54
+ open
55
+ onConfirmationAction={onConfirmationAction}
56
+ >
57
+ <button type="button">Open Dialog</button>
58
+ </AlertDialogAtom>
59
+ )
60
+
61
+ await user.click(
62
+ screen.getByRole('button', {name: defaultProps.confirmationButtonText})
63
+ )
64
+
65
+ expect(onConfirmationAction).toHaveBeenCalledTimes(1)
66
+ })
67
+ })
@@ -12,16 +12,7 @@ import {
12
12
  AlertDialog as AlertDialogPrimitive,
13
13
  } from '../ui/alert-dialog'
14
14
 
15
- export const AlertDialogAtom = ({
16
- children,
17
- title,
18
- description,
19
- cancelButtonText,
20
- confirmationButtonText,
21
- open,
22
- onOpenChange,
23
- onConfirmationAction,
24
- }: {
15
+ export interface AlertDialogAtomProps {
25
16
  /** The trigger element that opens the alert dialog */
26
17
  children: React.ReactNode
27
18
  /** The title text shown in the alert dialog header */
@@ -38,7 +29,18 @@ export const AlertDialogAtom = ({
38
29
  onOpenChange: (open: boolean) => void
39
30
  /** Callback fired when the confirmation button is clicked */
40
31
  onConfirmationAction: () => void
41
- }) => {
32
+ }
33
+
34
+ export const AlertDialogAtom = ({
35
+ children,
36
+ title,
37
+ description,
38
+ cancelButtonText,
39
+ confirmationButtonText,
40
+ open,
41
+ onOpenChange,
42
+ onConfirmationAction,
43
+ }: AlertDialogAtomProps) => {
42
44
  const [isOpen, setIsOpen] = useState(open)
43
45
 
44
46
  return (
@@ -0,0 +1,56 @@
1
+ import {describe, expect, it, vi} from 'vitest'
2
+
3
+ import {render, screen, userEvent} from '../../test-utils'
4
+
5
+ import {FavoriteButton} from './favorite-button'
6
+
7
+ describe('FavoriteButton', () => {
8
+ it('renders unfilled state by default', () => {
9
+ render(<FavoriteButton />)
10
+
11
+ const button = screen.getByRole('button')
12
+ expect(button).toBeInTheDocument()
13
+ expect(button).toHaveClass('bg-button-overlay/30')
14
+ })
15
+
16
+ it('renders filled state when filled prop is true', () => {
17
+ render(<FavoriteButton filled />)
18
+
19
+ const button = screen.getByRole('button')
20
+ expect(button).toHaveClass('bg-primary')
21
+ })
22
+
23
+ it('handles click events', async () => {
24
+ const handleClick = vi.fn()
25
+ const user = userEvent.setup()
26
+
27
+ render(<FavoriteButton onClick={handleClick} />)
28
+
29
+ const button = screen.getByRole('button')
30
+ await user.click(button)
31
+
32
+ expect(handleClick).toHaveBeenCalledTimes(1)
33
+ })
34
+
35
+ it('toggles between filled and unfilled states', async () => {
36
+ const user = userEvent.setup()
37
+ let isFilled = false
38
+ const handleClick = vi.fn(() => {
39
+ isFilled = !isFilled
40
+ })
41
+
42
+ const {rerender} = render(
43
+ <FavoriteButton filled={isFilled} onClick={handleClick} />
44
+ )
45
+
46
+ const button = screen.getByRole('button')
47
+ expect(button).toHaveClass('bg-button-overlay/30')
48
+
49
+ await user.click(button)
50
+ expect(handleClick).toHaveBeenCalledTimes(1)
51
+
52
+ // Rerender with updated filled state
53
+ rerender(<FavoriteButton filled onClick={handleClick} />)
54
+ expect(button).toHaveClass('bg-primary')
55
+ })
56
+ })
@@ -30,7 +30,7 @@ export function IconButton({
30
30
  onClick={onClick}
31
31
  variant="icon"
32
32
  size={size}
33
- className={cn('bg-primary', buttonStyles)}
33
+ className={cn(buttonStyles)}
34
34
  stopPropagation
35
35
  >
36
36
  <Icon
@@ -0,0 +1,108 @@
1
+ import {describe, expect, it, vi} from 'vitest'
2
+
3
+ import {render, screen, waitFor} from '../../test-utils'
4
+
5
+ import {Image} from './image'
6
+
7
+ // Mock the util functions
8
+ vi.mock('../../utils', () => ({
9
+ getThumbhashDataURL: vi.fn((thumbhash?: string) =>
10
+ thumbhash ? `data:image/png;base64,${thumbhash}` : null
11
+ ),
12
+ getResizedImageUrl: vi.fn((url?: string) => url),
13
+ }))
14
+
15
+ describe('Image', () => {
16
+ it('renders with src and alt', () => {
17
+ render(<Image src="https://example.com/image.jpg" alt="Test image" />)
18
+
19
+ const img = screen.getByRole('img', {name: /test image/i})
20
+ expect(img).toBeInTheDocument()
21
+ expect(img).toHaveAttribute('src', 'https://example.com/image.jpg')
22
+ })
23
+
24
+ it('renders placeholder when no src provided', () => {
25
+ const {container} = render(<Image alt="No image" />)
26
+
27
+ // When no src is provided, Image component still renders an img element but without src
28
+ const img = container.querySelector('img')
29
+ expect(img).toBeInTheDocument()
30
+ expect(img).not.toHaveAttribute('src')
31
+ })
32
+
33
+ it('handles onLoad callback', async () => {
34
+ const handleLoad = vi.fn()
35
+ render(
36
+ <Image
37
+ src="https://example.com/image.jpg"
38
+ alt="Test image"
39
+ onLoad={handleLoad}
40
+ />
41
+ )
42
+
43
+ const img = screen.getByRole('img')
44
+
45
+ // Simulate image loading
46
+ img.dispatchEvent(new Event('load'))
47
+
48
+ await waitFor(() => {
49
+ expect(handleLoad).toHaveBeenCalled()
50
+ })
51
+ })
52
+
53
+ it('applies custom className', () => {
54
+ const {container} = render(
55
+ <Image
56
+ src="https://example.com/image.jpg"
57
+ alt="Test image"
58
+ className="custom-class"
59
+ />
60
+ )
61
+
62
+ const wrapper = container.firstChild as HTMLElement
63
+ expect(wrapper).toHaveClass('custom-class')
64
+ })
65
+
66
+ it('applies aspect ratio style', () => {
67
+ const {container} = render(
68
+ <Image
69
+ src="https://example.com/image.jpg"
70
+ alt="Test image"
71
+ aspectRatio="16/9"
72
+ />
73
+ )
74
+
75
+ const wrapper = container.firstChild as HTMLElement
76
+ expect(wrapper.style.aspectRatio).toBe('16/9')
77
+ })
78
+
79
+ it('uses thumbhash as background when provided', () => {
80
+ const {container} = render(
81
+ <Image
82
+ src="https://example.com/image.jpg"
83
+ alt="Test image"
84
+ thumbhash="testThumbhash"
85
+ />
86
+ )
87
+
88
+ const wrapper = container.firstChild as HTMLElement
89
+ expect(wrapper.style.backgroundImage).toContain(
90
+ 'data:image/png;base64,testThumbhash'
91
+ )
92
+ })
93
+
94
+ it('passes additional props to img element', () => {
95
+ render(
96
+ <Image
97
+ src="https://example.com/image.jpg"
98
+ alt="Test image"
99
+ loading="lazy"
100
+ data-testid="custom-image"
101
+ />
102
+ )
103
+
104
+ const img = screen.getByRole('img')
105
+ expect(img).toHaveAttribute('loading', 'lazy')
106
+ expect(img).toHaveAttribute('data-testid', 'custom-image')
107
+ })
108
+ })