@shopify/shop-minis-react 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/components/MinisContainer.js +11 -10
  2. package/dist/components/MinisContainer.js.map +1 -1
  3. package/dist/components/atoms/content-wrapper.js.map +1 -1
  4. package/dist/components/atoms/video-player.js +28 -26
  5. package/dist/components/atoms/video-player.js.map +1 -1
  6. package/dist/components/commerce/product-card.js +106 -79
  7. package/dist/components/commerce/product-card.js.map +1 -1
  8. package/dist/components/commerce/product-link.js +124 -137
  9. package/dist/components/commerce/product-link.js.map +1 -1
  10. package/dist/components/commerce/search.js +20 -20
  11. package/dist/components/commerce/search.js.map +1 -1
  12. package/dist/components/ui/sonner.js +3 -1
  13. package/dist/components/ui/sonner.js.map +1 -1
  14. package/dist/hooks/content/useContent.js +12 -18
  15. package/dist/hooks/content/useContent.js.map +1 -1
  16. package/dist/hooks/navigation/useNavigateWithTransition.js +10 -11
  17. package/dist/hooks/navigation/useNavigateWithTransition.js.map +1 -1
  18. package/dist/hooks/product/useCuratedProducts.js +9 -11
  19. package/dist/hooks/product/useCuratedProducts.js.map +1 -1
  20. package/dist/hooks/product/usePopularProducts.js +9 -11
  21. package/dist/hooks/product/usePopularProducts.js.map +1 -1
  22. package/dist/hooks/product/useProduct.js +11 -17
  23. package/dist/hooks/product/useProduct.js.map +1 -1
  24. package/dist/hooks/product/useProductList.js +10 -21
  25. package/dist/hooks/product/useProductList.js.map +1 -1
  26. package/dist/hooks/product/useProductLists.js +11 -13
  27. package/dist/hooks/product/useProductLists.js.map +1 -1
  28. package/dist/hooks/product/useProductMedia.js +12 -18
  29. package/dist/hooks/product/useProductMedia.js.map +1 -1
  30. package/dist/hooks/product/useProductSearch.js +34 -27
  31. package/dist/hooks/product/useProductSearch.js.map +1 -1
  32. package/dist/hooks/product/useProductVariants.js +11 -14
  33. package/dist/hooks/product/useProductVariants.js.map +1 -1
  34. package/dist/hooks/product/useProducts.js +12 -11
  35. package/dist/hooks/product/useProducts.js.map +1 -1
  36. package/dist/hooks/product/useRecommendedProducts.js +11 -13
  37. package/dist/hooks/product/useRecommendedProducts.js.map +1 -1
  38. package/dist/hooks/shop/useRecommendedShops.js +11 -13
  39. package/dist/hooks/shop/useRecommendedShops.js.map +1 -1
  40. package/dist/hooks/shop/useShop.js +12 -11
  41. package/dist/hooks/shop/useShop.js.map +1 -1
  42. package/dist/hooks/user/useBuyerAttributes.js +8 -10
  43. package/dist/hooks/user/useBuyerAttributes.js.map +1 -1
  44. package/dist/hooks/user/useCurrentUser.js +7 -9
  45. package/dist/hooks/user/useCurrentUser.js.map +1 -1
  46. package/dist/hooks/user/useFollowedShops.js +11 -14
  47. package/dist/hooks/user/useFollowedShops.js.map +1 -1
  48. package/dist/hooks/user/useOrders.js +7 -9
  49. package/dist/hooks/user/useOrders.js.map +1 -1
  50. package/dist/hooks/user/useRecentProducts.js +11 -13
  51. package/dist/hooks/user/useRecentProducts.js.map +1 -1
  52. package/dist/hooks/user/useRecentShops.js +10 -13
  53. package/dist/hooks/user/useRecentShops.js.map +1 -1
  54. package/dist/hooks/user/useSavedProducts.js +10 -13
  55. package/dist/hooks/user/useSavedProducts.js.map +1 -1
  56. package/dist/index.js +269 -264
  57. package/dist/index.js.map +1 -1
  58. package/dist/internal/components/product-review-stars.js +78 -0
  59. package/dist/internal/components/product-review-stars.js.map +1 -0
  60. package/dist/internal/reactQuery/MinisQueryProvider.js +11 -0
  61. package/dist/internal/reactQuery/MinisQueryProvider.js.map +1 -0
  62. package/dist/internal/reactQuery/queryClient.js +33 -0
  63. package/dist/internal/reactQuery/queryClient.js.map +1 -0
  64. package/dist/internal/reactQuery/useShopActionInfiniteQuery.js +52 -0
  65. package/dist/internal/reactQuery/useShopActionInfiniteQuery.js.map +1 -0
  66. package/dist/internal/reactQuery/useShopActionQuery.js +37 -0
  67. package/dist/internal/reactQuery/useShopActionQuery.js.map +1 -0
  68. package/dist/mocks.js +178 -107
  69. package/dist/mocks.js.map +1 -1
  70. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/focusManager.js +45 -0
  71. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/focusManager.js.map +1 -0
  72. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/infiniteQueryBehavior.js +89 -0
  73. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/infiniteQueryBehavior.js.map +1 -0
  74. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/infiniteQueryObserver.js +55 -0
  75. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/infiniteQueryObserver.js.map +1 -0
  76. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/mutation.js +198 -0
  77. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/mutation.js.map +1 -0
  78. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/mutationCache.js +99 -0
  79. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/mutationCache.js.map +1 -0
  80. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/notifyManager.js +67 -0
  81. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/notifyManager.js.map +1 -0
  82. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/onlineManager.js +39 -0
  83. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/onlineManager.js.map +1 -0
  84. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/query.js +299 -0
  85. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/query.js.map +1 -0
  86. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/queryCache.js +80 -0
  87. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/queryCache.js.map +1 -0
  88. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/queryClient.js +215 -0
  89. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/queryClient.js.map +1 -0
  90. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/queryObserver.js +300 -0
  91. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/queryObserver.js.map +1 -0
  92. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/removable.js +25 -0
  93. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/removable.js.map +1 -0
  94. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/retryer.js +76 -0
  95. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/retryer.js.map +1 -0
  96. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/subscribable.js +21 -0
  97. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/subscribable.js.map +1 -0
  98. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/thenable.js +26 -0
  99. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/thenable.js.map +1 -0
  100. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/utils.js +176 -0
  101. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_query-core@5.86.0/node_modules/@tanstack/query-core/build/modern/utils.js.map +1 -0
  102. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/IsRestoringProvider.js +7 -0
  103. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/IsRestoringProvider.js.map +1 -0
  104. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/QueryClientProvider.js +17 -0
  105. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/QueryClientProvider.js.map +1 -0
  106. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/QueryErrorResetBoundary.js +19 -0
  107. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/QueryErrorResetBoundary.js.map +1 -0
  108. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/errorBoundaryUtils.js +21 -0
  109. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/errorBoundaryUtils.js.map +1 -0
  110. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/suspense.js +18 -0
  111. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/suspense.js.map +1 -0
  112. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/useBaseQuery.js +64 -0
  113. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/useBaseQuery.js.map +1 -0
  114. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/useInfiniteQuery.js +13 -0
  115. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/useInfiniteQuery.js.map +1 -0
  116. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/useQuery.js +9 -0
  117. package/dist/shop-minis-react/node_modules/.pnpm/@tanstack_react-query@5.86.0_react@19.1.0/node_modules/@tanstack/react-query/build/modern/useQuery.js.map +1 -0
  118. package/dist/shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/star-half.js +21 -0
  119. package/dist/shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/star-half.js.map +1 -0
  120. package/dist/shop-minis-react/node_modules/.pnpm/sonner@2.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/sonner/dist/index.js +4 -4
  121. package/dist/shop-minis-react/node_modules/.pnpm/sonner@2.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/sonner/dist/index.js.map +1 -1
  122. package/package.json +2 -7
  123. package/src/components/MinisContainer.tsx +6 -3
  124. package/src/components/atoms/content-wrapper.tsx +1 -1
  125. package/src/components/atoms/video-player.tsx +7 -0
  126. package/src/components/commerce/product-card.test.tsx +135 -0
  127. package/src/components/commerce/product-card.tsx +39 -5
  128. package/src/components/commerce/product-link.test.tsx +15 -3
  129. package/src/components/commerce/product-link.tsx +9 -25
  130. package/src/components/commerce/search.tsx +2 -2
  131. package/src/components/index.ts +1 -0
  132. package/src/components/ui/sonner.tsx +2 -2
  133. package/src/hooks/content/useContent.ts +6 -17
  134. package/src/hooks/navigation/useNavigateWithTransition.test.ts +46 -7
  135. package/src/hooks/navigation/useNavigateWithTransition.ts +4 -1
  136. package/src/hooks/product/useCuratedProducts.ts +4 -6
  137. package/src/hooks/product/usePopularProducts.ts +4 -6
  138. package/src/hooks/product/useProduct.ts +6 -17
  139. package/src/hooks/product/useProductList.ts +4 -19
  140. package/src/hooks/product/useProductLists.ts +4 -6
  141. package/src/hooks/product/useProductMedia.ts +6 -17
  142. package/src/hooks/product/useProductSearch.ts +19 -15
  143. package/src/hooks/product/useProductVariants.ts +5 -13
  144. package/src/hooks/product/useProducts.ts +8 -12
  145. package/src/hooks/product/useRecommendedProducts.ts +4 -6
  146. package/src/hooks/shop/useRecommendedShops.ts +4 -6
  147. package/src/hooks/shop/useShop.ts +8 -12
  148. package/src/hooks/user/useBuyerAttributes.ts +4 -6
  149. package/src/hooks/user/useCurrentUser.ts +4 -6
  150. package/src/hooks/user/useFollowedShops.ts +5 -13
  151. package/src/hooks/user/useOrders.ts +4 -6
  152. package/src/hooks/user/useRecentProducts.ts +4 -6
  153. package/src/hooks/user/useRecentShops.ts +5 -13
  154. package/src/hooks/user/useSavedProducts.ts +5 -13
  155. package/src/internal/components/product-review-stars.test.tsx +90 -0
  156. package/src/internal/components/product-review-stars.tsx +113 -0
  157. package/src/internal/reactQuery/MinisQueryProvider.test.tsx +38 -0
  158. package/src/internal/reactQuery/MinisQueryProvider.tsx +16 -0
  159. package/src/internal/reactQuery/index.ts +8 -0
  160. package/src/internal/reactQuery/queryClient.test.tsx +91 -0
  161. package/src/internal/reactQuery/queryClient.ts +43 -0
  162. package/src/internal/reactQuery/useShopActionInfiniteQuery.test.tsx +357 -0
  163. package/src/internal/reactQuery/useShopActionInfiniteQuery.ts +129 -0
  164. package/src/internal/reactQuery/useShopActionQuery.test.tsx +184 -0
  165. package/src/internal/reactQuery/useShopActionQuery.ts +74 -0
  166. package/src/mocks.ts +10 -2
  167. package/src/providers/ImagePickerProvider.test.tsx +3 -9
  168. package/dist/internal/useShopActionsDataFetching.js +0 -79
  169. package/dist/internal/useShopActionsDataFetching.js.map +0 -1
  170. package/dist/internal/useShopActionsPaginatedDataFetching.js +0 -96
  171. package/dist/internal/useShopActionsPaginatedDataFetching.js.map +0 -1
  172. package/src/hooks/product/useProductSearch.test.ts +0 -470
  173. package/src/internal/useShopActionsDataFetching.test.ts +0 -465
  174. package/src/internal/useShopActionsDataFetching.ts +0 -150
  175. package/src/internal/useShopActionsPaginatedDataFetching.ts +0 -188
  176. package/src/stories/Accordion.stories.tsx +0 -124
  177. package/src/stories/AddToCart.stories.tsx +0 -251
  178. package/src/stories/Alert.stories.tsx +0 -38
  179. package/src/stories/AlertDialog.stories.tsx +0 -48
  180. package/src/stories/Avatar.stories.tsx +0 -29
  181. package/src/stories/Badge.stories.tsx +0 -46
  182. package/src/stories/Button.stories.tsx +0 -81
  183. package/src/stories/Card.stories.tsx +0 -40
  184. package/src/stories/Checkbox.stories.tsx +0 -44
  185. package/src/stories/FavoriteButton.stories.tsx +0 -58
  186. package/src/stories/IconButton.stories.tsx +0 -68
  187. package/src/stories/ImageContentWrapper.stories.tsx +0 -65
  188. package/src/stories/Input.stories.tsx +0 -44
  189. package/src/stories/Label.stories.tsx +0 -19
  190. package/src/stories/List.stories.tsx +0 -64
  191. package/src/stories/MerchantCard.stories.tsx +0 -127
  192. package/src/stories/ProductCard.stories.tsx +0 -92
  193. package/src/stories/ProductLink.stories.tsx +0 -46
  194. package/src/stories/ProductVariantPrice.stories.tsx +0 -70
  195. package/src/stories/Progress.stories.tsx +0 -30
  196. package/src/stories/PullToRefreshList.stories.tsx +0 -122
  197. package/src/stories/QuantitySelector.stories.tsx +0 -78
  198. package/src/stories/RadioGroup.stories.tsx +0 -51
  199. package/src/stories/Search.stories.tsx +0 -37
  200. package/src/stories/Select.stories.tsx +0 -85
  201. package/src/stories/Skeleton.stories.tsx +0 -19
  202. package/src/stories/TextInput.stories.tsx +0 -26
  203. package/src/stories/Toaster.stories.tsx +0 -46
  204. package/src/stories/Touchable.stories.tsx +0 -40
  205. package/src/stories/VideoPlayer.stories.tsx +0 -129
@@ -0,0 +1,129 @@
1
+ import {useCallback, useMemo} from 'react'
2
+
3
+ import {ShopActionResult} from '@shopify/shop-minis-platform/actions'
4
+ import {useInfiniteQuery} from '@tanstack/react-query'
5
+
6
+ import {DataHookFetchPolicy} from '../../types'
7
+
8
+ import {useShopMinisQueryClient} from './queryClient'
9
+
10
+ /**
11
+ * Helper to use React Query with Shop Actions (paginated)
12
+ * Replaces useShopActionsPaginatedDataFetching
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // Example: Fetching saved products
17
+ * const { getSavedProducts } = useShopActions()
18
+ * const { data, loading, error, hasNextPage, fetchMore, refetch } =
19
+ * useShopActionInfiniteQuery(
20
+ * ['savedProducts', { includeSensitive }], // Query key
21
+ * getSavedProducts, // Shop Action
22
+ * { includeSensitive }, // Params (excludes 'after')
23
+ * { skip: false } // Options
24
+ * )
25
+ * // data will be flattened array of products from all pages
26
+ * ```
27
+ */
28
+ export function useShopActionInfiniteQuery<
29
+ TData,
30
+ TParams extends {after?: string; fetchPolicy?: DataHookFetchPolicy},
31
+ >(
32
+ queryKey: unknown[],
33
+ action: (params: TParams) => Promise<
34
+ ShopActionResult<{
35
+ data: TData
36
+ pageInfo: {hasNextPage: boolean; endCursor: string | null}
37
+ }>
38
+ >,
39
+ params: Omit<TParams, 'after'>,
40
+ options?: {
41
+ skip?: boolean
42
+ }
43
+ ) {
44
+ const {skip = false} = options ?? {}
45
+
46
+ // Always use our SDK's QueryClient for isolation
47
+ const queryClient = useShopMinisQueryClient()
48
+
49
+ interface PageData {
50
+ data: TData
51
+ pageInfo: {hasNextPage: boolean; endCursor: string | null}
52
+ }
53
+
54
+ const {
55
+ data,
56
+ fetchNextPage,
57
+ hasNextPage,
58
+ isLoading,
59
+ error,
60
+ refetch: reactQueryRefetch,
61
+ } = useInfiniteQuery<
62
+ PageData,
63
+ Error,
64
+ {pages: PageData[]},
65
+ unknown[],
66
+ string | undefined
67
+ >(
68
+ {
69
+ queryKey,
70
+ queryFn: async ({pageParam}: {pageParam: string | undefined}) => {
71
+ const result = await action({
72
+ ...params,
73
+ after: pageParam,
74
+ } as TParams)
75
+
76
+ if (!result.ok) {
77
+ throw result.error
78
+ }
79
+
80
+ return result.data
81
+ },
82
+ getNextPageParam: (lastPage: PageData) =>
83
+ lastPage.pageInfo.hasNextPage ? lastPage.pageInfo.endCursor : undefined,
84
+ initialPageParam: undefined as string | undefined,
85
+ enabled: !skip,
86
+ // Caching disabled by default (handled by Apollo)
87
+ // fetchPolicy param is passed through to the action (Apollo layer)
88
+ },
89
+ queryClient
90
+ )
91
+
92
+ // Flatten paginated data
93
+ // For paginated queries, we expect TData to be an array type
94
+ // Each page.data is an array that we concatenate together
95
+ const flattenedData = useMemo(() => {
96
+ if (!data?.pages || data.pages.length === 0) return null
97
+
98
+ // If first page data is null/undefined, return null
99
+ const firstPageData = data.pages[0].data
100
+ if (firstPageData === null || firstPageData === undefined) return null
101
+
102
+ // If data is array type, flatten all pages
103
+ if (Array.isArray(firstPageData)) {
104
+ return data.pages.flatMap((page: PageData) => page.data as any) as TData
105
+ }
106
+
107
+ // If data is not an array, just return the first page's data
108
+ // (Though in practice, all Shop Minis paginated queries return arrays)
109
+ return firstPageData as TData
110
+ }, [data?.pages])
111
+
112
+ // Wrap React Query functions to match expected API
113
+ const fetchMore = useCallback(async () => {
114
+ await fetchNextPage()
115
+ }, [fetchNextPage])
116
+
117
+ const refetch = useCallback(async () => {
118
+ await reactQueryRefetch()
119
+ }, [reactQueryRefetch])
120
+
121
+ return {
122
+ data: flattenedData,
123
+ loading: isLoading,
124
+ error: error as Error | null,
125
+ hasNextPage: hasNextPage ?? false,
126
+ fetchMore,
127
+ refetch,
128
+ }
129
+ }
@@ -0,0 +1,184 @@
1
+ import React from 'react'
2
+
3
+ import {renderHook, waitFor} from '@testing-library/react'
4
+ import {describe, expect, it, vi} from 'vitest'
5
+
6
+ import {MinisQueryProvider} from './MinisQueryProvider'
7
+ import {useShopActionQuery} from './useShopActionQuery'
8
+
9
+ describe('useShopActionQuery', () => {
10
+ const wrapper = ({children}: {children: React.ReactNode}) => (
11
+ <MinisQueryProvider>{children}</MinisQueryProvider>
12
+ )
13
+
14
+ describe('Successful Data Fetching', () => {
15
+ it('fetches and returns data', async () => {
16
+ const mockAction = vi.fn().mockResolvedValue({
17
+ ok: true,
18
+ data: {data: {id: '1', name: 'Test Product'}},
19
+ })
20
+
21
+ const {result} = renderHook(
22
+ () => useShopActionQuery(['test-1'], mockAction, {}),
23
+ {wrapper}
24
+ )
25
+
26
+ await waitFor(() => {
27
+ expect(result.current.loading).toBe(false)
28
+ })
29
+
30
+ expect(result.current.data).toEqual({id: '1', name: 'Test Product'})
31
+ expect(result.current.error).toBeNull()
32
+ expect(mockAction).toHaveBeenCalledWith({})
33
+ })
34
+
35
+ it('returns null when data is undefined', async () => {
36
+ const mockAction = vi.fn().mockResolvedValue({
37
+ ok: true,
38
+ data: {data: undefined},
39
+ })
40
+
41
+ const {result} = renderHook(
42
+ () => useShopActionQuery(['test-undefined'], mockAction, {}),
43
+ {wrapper}
44
+ )
45
+
46
+ await waitFor(() => {
47
+ expect(result.current.loading).toBe(false)
48
+ })
49
+
50
+ expect(result.current.data).toBeNull()
51
+ })
52
+ })
53
+
54
+ describe('Error Handling', () => {
55
+ it('handles action errors (ok: false)', async () => {
56
+ const mockAction = vi.fn().mockRejectedValue(new Error('API Error'))
57
+
58
+ const {result} = renderHook(
59
+ () => useShopActionQuery(['test-action-error'], mockAction, {}),
60
+ {wrapper}
61
+ )
62
+
63
+ // Wait for loading to complete (after retries)
64
+ await waitFor(
65
+ () => {
66
+ expect(result.current.loading).toBe(false)
67
+ },
68
+ {timeout: 3000}
69
+ )
70
+
71
+ expect(result.current.data).toBeNull()
72
+ expect(result.current.error).toBeInstanceOf(Error)
73
+ expect(result.current.error?.message).toBe('API Error')
74
+ })
75
+ })
76
+
77
+ describe('Skip Parameter', () => {
78
+ it('does not fetch when skip is true', async () => {
79
+ const mockAction = vi.fn()
80
+
81
+ renderHook(
82
+ () => useShopActionQuery(['test-skip'], mockAction, {}, {skip: true}),
83
+ {wrapper}
84
+ )
85
+
86
+ // Wait a bit to ensure no fetch happens
87
+ await new Promise(resolve => setTimeout(resolve, 100))
88
+
89
+ expect(mockAction).not.toHaveBeenCalled()
90
+ })
91
+
92
+ it('fetches when skip is false', async () => {
93
+ const mockAction = vi.fn().mockResolvedValue({
94
+ ok: true,
95
+ data: {data: 'skip-false-data'},
96
+ })
97
+
98
+ const {result} = renderHook(
99
+ () =>
100
+ useShopActionQuery(
101
+ ['test-noskip-query'],
102
+ mockAction,
103
+ {},
104
+ {skip: false}
105
+ ),
106
+ {wrapper}
107
+ )
108
+
109
+ await waitFor(() => {
110
+ expect(result.current.data).toBe('skip-false-data')
111
+ })
112
+
113
+ expect(mockAction).toHaveBeenCalled()
114
+ })
115
+ })
116
+
117
+ describe('API Contract', () => {
118
+ it('returns expected shape', async () => {
119
+ const mockAction = vi.fn().mockResolvedValue({
120
+ ok: true,
121
+ data: {data: 'contract-test'},
122
+ })
123
+
124
+ const {result} = renderHook(
125
+ () => useShopActionQuery(['test-contract'], mockAction, {}),
126
+ {wrapper}
127
+ )
128
+
129
+ await waitFor(() => {
130
+ expect(result.current.loading).toBe(false)
131
+ })
132
+
133
+ // Verify all expected properties exist
134
+ expect(result.current).toHaveProperty('data')
135
+ expect(result.current).toHaveProperty('loading')
136
+ expect(result.current).toHaveProperty('error')
137
+ expect(result.current).toHaveProperty('refetch')
138
+
139
+ // Verify types
140
+ expect(typeof result.current.loading).toBe('boolean')
141
+ expect(typeof result.current.refetch).toBe('function')
142
+ })
143
+ })
144
+
145
+ describe('fetchPolicy Parameter', () => {
146
+ it('passes fetchPolicy to action', async () => {
147
+ const mockAction = vi.fn().mockResolvedValue({
148
+ ok: true,
149
+ data: {data: 'test'},
150
+ })
151
+
152
+ renderHook(
153
+ () =>
154
+ useShopActionQuery(['test-network-only'], mockAction, {
155
+ fetchPolicy: 'network-only',
156
+ }),
157
+ {wrapper}
158
+ )
159
+
160
+ await waitFor(() => {
161
+ expect(mockAction).toHaveBeenCalledWith({fetchPolicy: 'network-only'})
162
+ })
163
+ })
164
+
165
+ it('passes cache-first fetchPolicy to action', async () => {
166
+ const mockAction = vi.fn().mockResolvedValue({
167
+ ok: true,
168
+ data: {data: 'test'},
169
+ })
170
+
171
+ renderHook(
172
+ () =>
173
+ useShopActionQuery(['test-cache-first'], mockAction, {
174
+ fetchPolicy: 'cache-first',
175
+ }),
176
+ {wrapper}
177
+ )
178
+
179
+ await waitFor(() => {
180
+ expect(mockAction).toHaveBeenCalledWith({fetchPolicy: 'cache-first'})
181
+ })
182
+ })
183
+ })
184
+ })
@@ -0,0 +1,74 @@
1
+ import {useCallback} from 'react'
2
+
3
+ import {ShopActionResult} from '@shopify/shop-minis-platform/actions'
4
+ import {useQuery} from '@tanstack/react-query'
5
+
6
+ import {DataHookFetchPolicy} from '../../types'
7
+
8
+ import {useShopMinisQueryClient} from './queryClient'
9
+
10
+ /**
11
+ * Helper to use React Query with Shop Actions (non-paginated)
12
+ * Replaces useShopActionsDataFetching
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const { data, loading, error, refetch } = useShopActionQuery(
17
+ * ['product', id],
18
+ * getProduct,
19
+ * { id },
20
+ * { skip: false }
21
+ * )
22
+ * ```
23
+ */
24
+ export function useShopActionQuery<
25
+ TData,
26
+ TParams extends {fetchPolicy?: DataHookFetchPolicy},
27
+ >(
28
+ queryKey: unknown[],
29
+ action: (params: TParams) => Promise<ShopActionResult<{data: TData}>>,
30
+ params: TParams,
31
+ options?: {
32
+ skip?: boolean
33
+ }
34
+ ) {
35
+ const {skip = false} = options ?? {}
36
+
37
+ // Always use our SDK's QueryClient for isolation
38
+ const queryClient = useShopMinisQueryClient()
39
+
40
+ const {
41
+ data,
42
+ error,
43
+ isLoading,
44
+ refetch: reactQueryRefetch,
45
+ } = useQuery(
46
+ {
47
+ queryKey,
48
+ queryFn: async () => {
49
+ const result = await action(params)
50
+
51
+ if (!result.ok) {
52
+ throw result.error
53
+ }
54
+
55
+ return result.data.data
56
+ },
57
+ enabled: !skip,
58
+ // Caching disabled by default (handled by Apollo)
59
+ // fetchPolicy param is passed through to the action (Apollo layer)
60
+ },
61
+ queryClient
62
+ )
63
+
64
+ const refetch = useCallback(async () => {
65
+ await reactQueryRefetch()
66
+ }, [reactQueryRefetch])
67
+
68
+ return {
69
+ data: data ?? null,
70
+ loading: isLoading,
71
+ error: error as Error | null,
72
+ refetch,
73
+ }
74
+ }
package/src/mocks.ts CHANGED
@@ -378,7 +378,13 @@ export function makeMockActions(): ShopActions {
378
378
  pageInfo: createPagination(),
379
379
  },
380
380
  getProductSearch: {
381
- data: [createProduct('search-3', 'Search Product 3', '39.99')],
381
+ data: [
382
+ createProduct('search-1', 'Search Product 1', '39.99'),
383
+ createProduct('search-2', 'Search Product 2', '19.99'),
384
+ createProduct('search-3', 'Search Product 3', '29.99'),
385
+ createProduct('search-4', 'Search Product 4', '49.99'),
386
+ createProduct('search-5', 'Search Product 5', '9.99'),
387
+ ],
382
388
  pageInfo: createPagination(),
383
389
  },
384
390
  getProducts: {data: [createProduct('prod-2', 'Product 2', '19.99')]},
@@ -407,7 +413,9 @@ export function makeMockActions(): ShopActions {
407
413
  ],
408
414
  pageInfo: createPagination(),
409
415
  },
410
- getShop: {data: createShop('shop-1', 'Sample Shop')},
416
+ getShop: {
417
+ data: createShop('shop-1', 'Sample Shop', {featuredImagesLimit: 4}),
418
+ },
411
419
  getRecentShops: {
412
420
  data: [createShop('recent-shop-1', 'Recent Shop 1')],
413
421
  pageInfo: createPagination(),
@@ -160,9 +160,7 @@ describe('ImagePickerProvider', () => {
160
160
  configurable: true,
161
161
  })
162
162
 
163
- await act(async () => {
164
- fireEvent.change(galleryInput)
165
- })
163
+ fireEvent.change(galleryInput)
166
164
 
167
165
  // Check that the promise resolved with the file
168
166
  await vi.waitFor(() => {
@@ -427,9 +425,7 @@ describe('ImagePickerProvider', () => {
427
425
  configurable: true,
428
426
  })
429
427
 
430
- await act(async () => {
431
- fireEvent.change(cameraInput)
432
- })
428
+ fireEvent.change(cameraInput)
433
429
 
434
430
  await vi.waitFor(() => {
435
431
  const cameraFile = document.querySelector('[data-testid="camera-file"]')
@@ -718,9 +714,7 @@ describe('ImagePickerProvider', () => {
718
714
  configurable: true,
719
715
  })
720
716
 
721
- await act(async () => {
722
- fireEvent.change(galleryInput)
723
- })
717
+ fireEvent.change(galleryInput)
724
718
 
725
719
  // Check that input value was cleared
726
720
  expect(galleryInput.value).toBe('')
@@ -1,79 +0,0 @@
1
- import { useState as b, useCallback as u, useMemo as v, useEffect as M } from "react";
2
- import { MiniError as P, formatError as V } from "../utils/errors.js";
3
- const A = (c, d, f) => {
4
- const [n, l] = b({
5
- data: null,
6
- loading: !0,
7
- error: null
8
- }), h = f?.skip === !0, { validator: g, hook: t } = f, w = u(
9
- (s) => {
10
- try {
11
- return g?.(s), null;
12
- } catch (e) {
13
- return e instanceof Error ? e : new P({
14
- hook: t,
15
- message: "Validation failed"
16
- });
17
- }
18
- },
19
- [g, t]
20
- ), y = v(() => d, [JSON.stringify(d)]), a = u(
21
- async (s, {
22
- setLoading: e = !0,
23
- setError: p = !0,
24
- resetOnError: E = !0,
25
- throwOnError: O = !0
26
- } = {}) => {
27
- let m = null, i = null;
28
- l((r) => ({
29
- ...r,
30
- loading: e ? !0 : r.loading
31
- }));
32
- try {
33
- const r = await c({ ...y, ...s });
34
- if (r.ok)
35
- i = w(r.data.data), l((S) => ({
36
- ...S,
37
- data: r.data.data,
38
- loading: !1,
39
- error: i ?? null
40
- }));
41
- else
42
- throw r.error;
43
- } catch (r) {
44
- m = V({ hook: t }, r);
45
- }
46
- const o = i || m;
47
- if (o && (p || E) && l((r) => ({
48
- data: E ? null : r.data,
49
- loading: !1,
50
- error: o
51
- })), o && O)
52
- throw o;
53
- },
54
- [c, y, t, w]
55
- ), k = u(async () => {
56
- await a({ fetchPolicy: "network-only" }, {
57
- setLoading: !1,
58
- resetOnError: !1,
59
- throwOnError: !0
60
- });
61
- }, [a]);
62
- return M(() => {
63
- h || a(
64
- {},
65
- {
66
- throwOnError: !1
67
- }
68
- );
69
- }, [a, h]), {
70
- data: n.data,
71
- loading: n.loading,
72
- error: n.error,
73
- refetch: k
74
- };
75
- };
76
- export {
77
- A as useShopActionsDataFetching
78
- };
79
- //# sourceMappingURL=useShopActionsDataFetching.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useShopActionsDataFetching.js","sources":["../../src/internal/useShopActionsDataFetching.ts"],"sourcesContent":["import {useCallback, useEffect, useMemo, useState} from 'react'\n\nimport {ShopActionResult} from '@shopify/shop-minis-platform/actions'\n\nimport {DataHookFetchPolicy, DataHookReturnsBase} from '../types'\nimport {formatError, MiniError} from '../utils/errors'\n\nexport interface ShopActionsDataFetchingResult<R> extends DataHookReturnsBase {\n data: R | null\n}\n\nexport const useShopActionsDataFetching = <\n S = unknown,\n P extends {fetchPolicy?: DataHookFetchPolicy} = {\n fetchPolicy?: DataHookFetchPolicy\n },\n>(\n action: (params: P) => Promise<ShopActionResult<{data: S}>>,\n params: P,\n options: {\n skip?: boolean\n hook?: string\n validator?: (data: S) => void\n }\n): ShopActionsDataFetchingResult<S> => {\n const [state, setState] = useState<{\n data: S | null\n loading: boolean\n error: Error | null\n }>({\n data: null,\n loading: true,\n error: null,\n })\n\n const skip = options?.skip === true\n const {validator, hook} = options\n const runValidator = useCallback(\n (dataToValidate: S) => {\n try {\n validator?.(dataToValidate)\n return null\n } catch (err) {\n if (err instanceof Error) return err\n\n return new MiniError({\n hook,\n message: 'Validation failed',\n })\n }\n },\n [validator, hook]\n )\n\n // Params object is recreated on every render, so we need to memoize it.\n // We don't know what's inside the params object, but we can stringify it.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stableParams = useMemo(() => params, [JSON.stringify(params)])\n\n // There's a lot of complexity here because each type of fetch has different side effects if we are trying to\n // stay close to how Apollo client works. eg:\n // - Initial fetch: set loading, set error, set data, reset on error (don't throw)\n // - change params fetch: set loading, set error, set data, reset on error (don't throw)\n // - refetch fetch: don't set loading, set error, update data, leave data as is was on error (also throw)\n // - fetchMore fetch: don't set loading, don't set error, update data, leave data as is was on error (also throw)\n const fetch = useCallback(\n async (\n extraParams?: Partial<P>,\n {\n setLoading = true,\n setError = true,\n resetOnError = true,\n throwOnError = true,\n }: {\n setLoading?: boolean\n setError?: boolean\n resetOnError?: boolean\n throwOnError?: boolean\n } = {}\n ) => {\n let queryError: Error | null = null\n let validationError: Error | null = null\n\n setState(curState => ({\n ...curState,\n loading: setLoading ? true : curState.loading,\n }))\n\n try {\n const result = await action({...stableParams, ...extraParams})\n\n if (result.ok) {\n validationError = runValidator(result.data.data)\n\n setState(curState => ({\n ...curState,\n data: result.data.data,\n loading: false,\n error: validationError ?? null,\n }))\n } else {\n throw result.error\n }\n } catch (err) {\n queryError = formatError({hook}, err)\n }\n\n const error = validationError || queryError\n\n if (error && (setError || resetOnError)) {\n setState(curState => ({\n data: resetOnError ? null : curState.data,\n loading: false,\n error,\n }))\n }\n\n if (error && throwOnError) {\n throw error\n }\n },\n [action, stableParams, hook, runValidator]\n )\n\n const refetch = useCallback(async () => {\n await fetch({fetchPolicy: 'network-only'} as Partial<P>, {\n setLoading: false,\n resetOnError: false,\n throwOnError: true,\n })\n }, [fetch])\n\n useEffect(() => {\n if (skip) return\n\n fetch(\n {},\n {\n throwOnError: false,\n }\n )\n }, [fetch, skip])\n\n return {\n data: state.data,\n loading: state.loading,\n error: state.error,\n refetch,\n }\n}\n"],"names":["useShopActionsDataFetching","action","params","options","state","setState","useState","skip","validator","hook","runValidator","useCallback","dataToValidate","err","MiniError","stableParams","useMemo","fetch","extraParams","setLoading","setError","resetOnError","throwOnError","queryError","validationError","curState","result","formatError","error","refetch","useEffect"],"mappings":";;AAWO,MAAMA,IAA6B,CAMxCC,GACAC,GACAC,MAKqC;AACrC,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAIvB;AAAA,IACD,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,EAAA,CACR,GAEKC,IAAOJ,GAAS,SAAS,IACzB,EAAC,WAAAK,GAAW,MAAAC,EAAA,IAAQN,GACpBO,IAAeC;AAAA,IACnB,CAACC,MAAsB;AACjB,UAAA;AACF,eAAAJ,IAAYI,CAAc,GACnB;AAAA,eACAC,GAAK;AACR,eAAAA,aAAe,QAAcA,IAE1B,IAAIC,EAAU;AAAA,UACnB,MAAAL;AAAA,UACA,SAAS;AAAA,QAAA,CACV;AAAA,MAAA;AAAA,IAEL;AAAA,IACA,CAACD,GAAWC,CAAI;AAAA,EAClB,GAKMM,IAAeC,EAAQ,MAAMd,GAAQ,CAAC,KAAK,UAAUA,CAAM,CAAC,CAAC,GAQ7De,IAAQN;AAAA,IACZ,OACEO,GACA;AAAA,MACE,YAAAC,IAAa;AAAA,MACb,UAAAC,IAAW;AAAA,MACX,cAAAC,IAAe;AAAA,MACf,cAAAC,IAAe;AAAA,IACjB,IAKI,OACD;AACH,UAAIC,IAA2B,MAC3BC,IAAgC;AAEpC,MAAAnB,EAAS,CAAaoB,OAAA;AAAA,QACpB,GAAGA;AAAA,QACH,SAASN,IAAa,KAAOM,EAAS;AAAA,MAAA,EACtC;AAEE,UAAA;AACI,cAAAC,IAAS,MAAMzB,EAAO,EAAC,GAAGc,GAAc,GAAGG,GAAY;AAE7D,YAAIQ,EAAO;AACS,UAAAF,IAAAd,EAAagB,EAAO,KAAK,IAAI,GAE/CrB,EAAS,CAAaoB,OAAA;AAAA,YACpB,GAAGA;AAAA,YACH,MAAMC,EAAO,KAAK;AAAA,YAClB,SAAS;AAAA,YACT,OAAOF,KAAmB;AAAA,UAAA,EAC1B;AAAA;AAEF,gBAAME,EAAO;AAAA,eAERb,GAAK;AACZ,QAAAU,IAAaI,EAAY,EAAC,MAAAlB,EAAI,GAAGI,CAAG;AAAA,MAAA;AAGtC,YAAMe,IAAQJ,KAAmBD;AAUjC,UARIK,MAAUR,KAAYC,MACxBhB,EAAS,CAAaoB,OAAA;AAAA,QACpB,MAAMJ,IAAe,OAAOI,EAAS;AAAA,QACrC,SAAS;AAAA,QACT,OAAAG;AAAA,MAAA,EACA,GAGAA,KAASN;AACL,cAAAM;AAAA,IAEV;AAAA,IACA,CAAC3B,GAAQc,GAAcN,GAAMC,CAAY;AAAA,EAC3C,GAEMmB,IAAUlB,EAAY,YAAY;AACtC,UAAMM,EAAM,EAAC,aAAa,kBAA+B;AAAA,MACvD,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,cAAc;AAAA,IAAA,CACf;AAAA,EAAA,GACA,CAACA,CAAK,CAAC;AAEV,SAAAa,EAAU,MAAM;AACd,IAAIvB,KAEJU;AAAA,MACE,CAAC;AAAA,MACD;AAAA,QACE,cAAc;AAAA,MAAA;AAAA,IAElB;AAAA,EAAA,GACC,CAACA,GAAOV,CAAI,CAAC,GAET;AAAA,IACL,MAAMH,EAAM;AAAA,IACZ,SAASA,EAAM;AAAA,IACf,OAAOA,EAAM;AAAA,IACb,SAAAyB;AAAA,EACF;AACF;"}
@@ -1,96 +0,0 @@
1
- import { useState as A, useCallback as l, useMemo as O, useEffect as D } from "react";
2
- import { MiniError as M, formatError as b } from "../utils/errors.js";
3
- const V = (g, c, h) => {
4
- const [r, f] = A({
5
- data: null,
6
- pageInfo: { hasNextPage: !1, endCursor: null },
7
- loading: !1,
8
- error: null
9
- }), p = h?.skip === !0, { validator: y, hook: t } = h, w = l(
10
- (d) => {
11
- try {
12
- return y?.(d), null;
13
- } catch (o) {
14
- return o instanceof Error ? o : new M({
15
- hook: t,
16
- message: "Validation failed"
17
- });
18
- }
19
- },
20
- [y, t]
21
- ), I = O(() => c, [JSON.stringify(c)]), e = l(
22
- async (d, {
23
- setLoading: o = !0,
24
- setError: N = !0,
25
- resetOnError: i = !0,
26
- throwOnError: k = !0,
27
- appendData: C = !1
28
- } = {}) => {
29
- let E = null, u = null;
30
- f((a) => ({
31
- ...a,
32
- loading: o ? !0 : a.loading
33
- }));
34
- try {
35
- const a = await g({ ...I, ...d });
36
- if (a.ok)
37
- u = w(a.data.data), f((s) => {
38
- let P = a.data.data;
39
- return C && s.data && Array.isArray(s.data) && Array.isArray(a.data.data) && (P = [...s.data, ...a.data.data]), {
40
- ...s,
41
- data: P,
42
- pageInfo: a.data.pageInfo,
43
- loading: !1,
44
- error: u ?? null
45
- };
46
- });
47
- else
48
- throw a.error;
49
- } catch (a) {
50
- console.log("caught 1", a), E = b({ hook: t }, a);
51
- }
52
- const n = u || E;
53
- if (n && (N || i) && f((a) => ({
54
- data: i ? null : a.data,
55
- pageInfo: i ? { hasNextPage: !1, endCursor: null } : a.pageInfo,
56
- loading: !1,
57
- error: n
58
- })), n && k)
59
- throw n;
60
- },
61
- [g, I, t, w]
62
- ), m = l(async () => {
63
- await e({ fetchPolicy: "network-only" }, {
64
- setLoading: !1,
65
- resetOnError: !1,
66
- throwOnError: !0
67
- });
68
- }, [e]), x = l(async () => {
69
- !r.pageInfo.hasNextPage || !r.pageInfo.endCursor || await e({ after: r.pageInfo.endCursor }, {
70
- setLoading: !1,
71
- setError: !1,
72
- resetOnError: !1,
73
- throwOnError: !0,
74
- appendData: !0
75
- });
76
- }, [r.pageInfo.hasNextPage, r.pageInfo.endCursor, e]);
77
- return D(() => {
78
- p || e(
79
- {},
80
- {
81
- throwOnError: !1
82
- }
83
- );
84
- }, [e, p]), {
85
- data: r.data,
86
- loading: r.loading,
87
- error: r.error,
88
- hasNextPage: r.pageInfo.hasNextPage,
89
- refetch: m,
90
- fetchMore: x
91
- };
92
- };
93
- export {
94
- V as useShopActionsPaginatedDataFetching
95
- };
96
- //# sourceMappingURL=useShopActionsPaginatedDataFetching.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useShopActionsPaginatedDataFetching.js","sources":["../../src/internal/useShopActionsPaginatedDataFetching.ts"],"sourcesContent":["import {useCallback, useEffect, useMemo, useState} from 'react'\n\nimport {ShopAction, PaginationInfo} from '@shopify/shop-minis-platform/actions'\n\nimport {DataHookFetchPolicy, PaginatedDataHookReturnsBase} from '../types'\nimport {formatError, MiniError} from '../utils/errors'\n\nexport interface ShopActionsDataFetchingResult<S>\n extends PaginatedDataHookReturnsBase {\n data: S | null\n}\n\nexport const useShopActionsPaginatedDataFetching = <\n S = unknown,\n P extends {after?: string; fetchPolicy?: DataHookFetchPolicy} = {\n after?: undefined\n fetchPolicy?: DataHookFetchPolicy\n },\n>(\n action: ShopAction<P, {data: S; pageInfo: PaginationInfo}>,\n params: P,\n options: {\n skip?: boolean\n hook?: string\n validator?: (data: S) => void\n }\n): ShopActionsDataFetchingResult<S> => {\n const [state, setState] = useState<{\n data: S | null\n pageInfo: PaginationInfo\n loading: boolean\n error: Error | null\n }>({\n data: null,\n pageInfo: {hasNextPage: false, endCursor: null},\n loading: false,\n error: null,\n })\n\n const skip = options?.skip === true\n const {validator, hook} = options\n const runValidator = useCallback(\n (dataToValidate: S): Error | null => {\n try {\n validator?.(dataToValidate)\n return null\n } catch (err) {\n if (err instanceof Error) return err\n\n return new MiniError({\n hook,\n message: 'Validation failed',\n })\n }\n },\n [validator, hook]\n )\n\n // Params object is recreated on every render, so we need to memoize it.\n // We don't know what's inside the params object, but we can stringify it.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stableParams = useMemo(() => params, [JSON.stringify(params)])\n\n // There's a lot of complexity here because each type of fetch has different side effects if we are trying to\n // stay close to how Apollo client works. eg:\n // - Initial fetch: set loading, set error, set data, reset on error (don't throw)\n // - change params fetch: set loading, set error, set data, reset on error (don't throw)\n // - refetch fetch: don't set loading, set error, update data, leave data as is was on error (also throw)\n // - fetchMore fetch: don't set loading, don't set error, update data, leave data as is was on error (also throw)\n const fetch = useCallback(\n async (\n extraParams?: Partial<P>,\n {\n setLoading = true,\n setError = true,\n resetOnError = true,\n throwOnError = true,\n appendData = false,\n }: {\n setLoading?: boolean\n setError?: boolean\n resetOnError?: boolean\n throwOnError?: boolean\n appendData?: boolean\n } = {}\n ) => {\n let queryError: Error | null = null\n let validationError: Error | null = null\n\n setState(curState => ({\n ...curState,\n loading: setLoading ? true : curState.loading,\n }))\n\n try {\n const result = await action({...stableParams, ...extraParams})\n\n if (result.ok) {\n validationError = runValidator(result.data.data)\n\n setState(curState => {\n let newData = result.data.data\n\n if (\n appendData &&\n curState.data &&\n Array.isArray(curState.data) &&\n Array.isArray(result.data.data)\n ) {\n newData = [...curState.data, ...result.data.data] as S\n }\n\n return {\n ...curState,\n data: newData,\n pageInfo: result.data.pageInfo,\n loading: false,\n error: validationError ?? null,\n }\n })\n } else {\n throw result.error\n }\n } catch (err) {\n console.log('caught 1', err)\n queryError = formatError({hook}, err)\n }\n\n const error = validationError || queryError\n\n if (error && (setError || resetOnError)) {\n setState(curState => ({\n data: resetOnError ? null : curState.data,\n pageInfo: resetOnError\n ? {hasNextPage: false, endCursor: null}\n : curState.pageInfo,\n loading: false,\n error,\n }))\n }\n\n if (error && throwOnError) {\n throw error\n }\n },\n [action, stableParams, hook, runValidator]\n )\n\n const refetch = useCallback(async () => {\n await fetch({fetchPolicy: 'network-only'} as Partial<P>, {\n setLoading: false,\n resetOnError: false,\n throwOnError: true,\n })\n }, [fetch])\n\n const fetchMore = useCallback(async () => {\n if (!state.pageInfo.hasNextPage || !state.pageInfo.endCursor) return\n\n await fetch({after: state.pageInfo.endCursor} as Partial<P>, {\n setLoading: false,\n setError: false,\n resetOnError: false,\n throwOnError: true,\n appendData: true,\n })\n }, [state.pageInfo.hasNextPage, state.pageInfo.endCursor, fetch])\n\n useEffect(() => {\n if (skip) return\n\n fetch(\n {},\n {\n throwOnError: false,\n }\n )\n }, [fetch, skip])\n\n return {\n data: state.data,\n loading: state.loading,\n error: state.error,\n hasNextPage: state.pageInfo.hasNextPage,\n refetch,\n fetchMore,\n }\n}\n"],"names":["useShopActionsPaginatedDataFetching","action","params","options","state","setState","useState","skip","validator","hook","runValidator","useCallback","dataToValidate","err","MiniError","stableParams","useMemo","fetch","extraParams","setLoading","setError","resetOnError","throwOnError","appendData","queryError","validationError","curState","result","newData","formatError","error","refetch","fetchMore","useEffect"],"mappings":"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"}