@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.
- package/dist/components/MinisContainer.js +11 -10
- package/dist/components/MinisContainer.js.map +1 -1
- package/dist/components/atoms/content-wrapper.js.map +1 -1
- package/dist/components/atoms/video-player.js +28 -26
- package/dist/components/atoms/video-player.js.map +1 -1
- package/dist/components/commerce/product-card.js +106 -79
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/components/commerce/product-link.js +124 -137
- package/dist/components/commerce/product-link.js.map +1 -1
- package/dist/components/commerce/search.js +20 -20
- package/dist/components/commerce/search.js.map +1 -1
- package/dist/components/ui/sonner.js +3 -1
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/hooks/content/useContent.js +12 -18
- package/dist/hooks/content/useContent.js.map +1 -1
- package/dist/hooks/navigation/useNavigateWithTransition.js +10 -11
- package/dist/hooks/navigation/useNavigateWithTransition.js.map +1 -1
- package/dist/hooks/product/useCuratedProducts.js +9 -11
- package/dist/hooks/product/useCuratedProducts.js.map +1 -1
- package/dist/hooks/product/usePopularProducts.js +9 -11
- package/dist/hooks/product/usePopularProducts.js.map +1 -1
- package/dist/hooks/product/useProduct.js +11 -17
- package/dist/hooks/product/useProduct.js.map +1 -1
- package/dist/hooks/product/useProductList.js +10 -21
- package/dist/hooks/product/useProductList.js.map +1 -1
- package/dist/hooks/product/useProductLists.js +11 -13
- package/dist/hooks/product/useProductLists.js.map +1 -1
- package/dist/hooks/product/useProductMedia.js +12 -18
- package/dist/hooks/product/useProductMedia.js.map +1 -1
- package/dist/hooks/product/useProductSearch.js +34 -27
- package/dist/hooks/product/useProductSearch.js.map +1 -1
- package/dist/hooks/product/useProductVariants.js +11 -14
- package/dist/hooks/product/useProductVariants.js.map +1 -1
- package/dist/hooks/product/useProducts.js +12 -11
- package/dist/hooks/product/useProducts.js.map +1 -1
- package/dist/hooks/product/useRecommendedProducts.js +11 -13
- package/dist/hooks/product/useRecommendedProducts.js.map +1 -1
- package/dist/hooks/shop/useRecommendedShops.js +11 -13
- package/dist/hooks/shop/useRecommendedShops.js.map +1 -1
- package/dist/hooks/shop/useShop.js +12 -11
- package/dist/hooks/shop/useShop.js.map +1 -1
- package/dist/hooks/user/useBuyerAttributes.js +8 -10
- package/dist/hooks/user/useBuyerAttributes.js.map +1 -1
- package/dist/hooks/user/useCurrentUser.js +7 -9
- package/dist/hooks/user/useCurrentUser.js.map +1 -1
- package/dist/hooks/user/useFollowedShops.js +11 -14
- package/dist/hooks/user/useFollowedShops.js.map +1 -1
- package/dist/hooks/user/useOrders.js +7 -9
- package/dist/hooks/user/useOrders.js.map +1 -1
- package/dist/hooks/user/useRecentProducts.js +11 -13
- package/dist/hooks/user/useRecentProducts.js.map +1 -1
- package/dist/hooks/user/useRecentShops.js +10 -13
- package/dist/hooks/user/useRecentShops.js.map +1 -1
- package/dist/hooks/user/useSavedProducts.js +10 -13
- package/dist/hooks/user/useSavedProducts.js.map +1 -1
- package/dist/index.js +269 -264
- package/dist/index.js.map +1 -1
- package/dist/internal/components/product-review-stars.js +78 -0
- package/dist/internal/components/product-review-stars.js.map +1 -0
- package/dist/internal/reactQuery/MinisQueryProvider.js +11 -0
- package/dist/internal/reactQuery/MinisQueryProvider.js.map +1 -0
- package/dist/internal/reactQuery/queryClient.js +33 -0
- package/dist/internal/reactQuery/queryClient.js.map +1 -0
- package/dist/internal/reactQuery/useShopActionInfiniteQuery.js +52 -0
- package/dist/internal/reactQuery/useShopActionInfiniteQuery.js.map +1 -0
- package/dist/internal/reactQuery/useShopActionQuery.js +37 -0
- package/dist/internal/reactQuery/useShopActionQuery.js.map +1 -0
- package/dist/mocks.js +178 -107
- package/dist/mocks.js.map +1 -1
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/package.json +2 -7
- package/src/components/MinisContainer.tsx +6 -3
- package/src/components/atoms/content-wrapper.tsx +1 -1
- package/src/components/atoms/video-player.tsx +7 -0
- package/src/components/commerce/product-card.test.tsx +135 -0
- package/src/components/commerce/product-card.tsx +39 -5
- package/src/components/commerce/product-link.test.tsx +15 -3
- package/src/components/commerce/product-link.tsx +9 -25
- package/src/components/commerce/search.tsx +2 -2
- package/src/components/index.ts +1 -0
- package/src/components/ui/sonner.tsx +2 -2
- package/src/hooks/content/useContent.ts +6 -17
- package/src/hooks/navigation/useNavigateWithTransition.test.ts +46 -7
- package/src/hooks/navigation/useNavigateWithTransition.ts +4 -1
- package/src/hooks/product/useCuratedProducts.ts +4 -6
- package/src/hooks/product/usePopularProducts.ts +4 -6
- package/src/hooks/product/useProduct.ts +6 -17
- package/src/hooks/product/useProductList.ts +4 -19
- package/src/hooks/product/useProductLists.ts +4 -6
- package/src/hooks/product/useProductMedia.ts +6 -17
- package/src/hooks/product/useProductSearch.ts +19 -15
- package/src/hooks/product/useProductVariants.ts +5 -13
- package/src/hooks/product/useProducts.ts +8 -12
- package/src/hooks/product/useRecommendedProducts.ts +4 -6
- package/src/hooks/shop/useRecommendedShops.ts +4 -6
- package/src/hooks/shop/useShop.ts +8 -12
- package/src/hooks/user/useBuyerAttributes.ts +4 -6
- package/src/hooks/user/useCurrentUser.ts +4 -6
- package/src/hooks/user/useFollowedShops.ts +5 -13
- package/src/hooks/user/useOrders.ts +4 -6
- package/src/hooks/user/useRecentProducts.ts +4 -6
- package/src/hooks/user/useRecentShops.ts +5 -13
- package/src/hooks/user/useSavedProducts.ts +5 -13
- package/src/internal/components/product-review-stars.test.tsx +90 -0
- package/src/internal/components/product-review-stars.tsx +113 -0
- package/src/internal/reactQuery/MinisQueryProvider.test.tsx +38 -0
- package/src/internal/reactQuery/MinisQueryProvider.tsx +16 -0
- package/src/internal/reactQuery/index.ts +8 -0
- package/src/internal/reactQuery/queryClient.test.tsx +91 -0
- package/src/internal/reactQuery/queryClient.ts +43 -0
- package/src/internal/reactQuery/useShopActionInfiniteQuery.test.tsx +357 -0
- package/src/internal/reactQuery/useShopActionInfiniteQuery.ts +129 -0
- package/src/internal/reactQuery/useShopActionQuery.test.tsx +184 -0
- package/src/internal/reactQuery/useShopActionQuery.ts +74 -0
- package/src/mocks.ts +10 -2
- package/src/providers/ImagePickerProvider.test.tsx +3 -9
- package/dist/internal/useShopActionsDataFetching.js +0 -79
- package/dist/internal/useShopActionsDataFetching.js.map +0 -1
- package/dist/internal/useShopActionsPaginatedDataFetching.js +0 -96
- package/dist/internal/useShopActionsPaginatedDataFetching.js.map +0 -1
- package/src/hooks/product/useProductSearch.test.ts +0 -470
- package/src/internal/useShopActionsDataFetching.test.ts +0 -465
- package/src/internal/useShopActionsDataFetching.ts +0 -150
- package/src/internal/useShopActionsPaginatedDataFetching.ts +0 -188
- package/src/stories/Accordion.stories.tsx +0 -124
- package/src/stories/AddToCart.stories.tsx +0 -251
- package/src/stories/Alert.stories.tsx +0 -38
- package/src/stories/AlertDialog.stories.tsx +0 -48
- package/src/stories/Avatar.stories.tsx +0 -29
- package/src/stories/Badge.stories.tsx +0 -46
- package/src/stories/Button.stories.tsx +0 -81
- package/src/stories/Card.stories.tsx +0 -40
- package/src/stories/Checkbox.stories.tsx +0 -44
- package/src/stories/FavoriteButton.stories.tsx +0 -58
- package/src/stories/IconButton.stories.tsx +0 -68
- package/src/stories/ImageContentWrapper.stories.tsx +0 -65
- package/src/stories/Input.stories.tsx +0 -44
- package/src/stories/Label.stories.tsx +0 -19
- package/src/stories/List.stories.tsx +0 -64
- package/src/stories/MerchantCard.stories.tsx +0 -127
- package/src/stories/ProductCard.stories.tsx +0 -92
- package/src/stories/ProductLink.stories.tsx +0 -46
- package/src/stories/ProductVariantPrice.stories.tsx +0 -70
- package/src/stories/Progress.stories.tsx +0 -30
- package/src/stories/PullToRefreshList.stories.tsx +0 -122
- package/src/stories/QuantitySelector.stories.tsx +0 -78
- package/src/stories/RadioGroup.stories.tsx +0 -51
- package/src/stories/Search.stories.tsx +0 -37
- package/src/stories/Select.stories.tsx +0 -85
- package/src/stories/Skeleton.stories.tsx +0 -19
- package/src/stories/TextInput.stories.tsx +0 -26
- package/src/stories/Toaster.stories.tsx +0 -46
- package/src/stories/Touchable.stories.tsx +0 -40
- 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: [
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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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"}
|