@shopify/shop-minis-react 0.3.4 → 0.4.1
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/_virtual/index3.js +2 -5
- package/dist/_virtual/index3.js.map +1 -1
- package/dist/_virtual/index4.js +5 -2
- package/dist/_virtual/index4.js.map +1 -1
- package/dist/components/MinisContainer.js +11 -10
- package/dist/components/MinisContainer.js.map +1 -1
- package/dist/hooks/content/useContent.js +12 -18
- package/dist/hooks/content/useContent.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/hooks/util/useImagePicker.js +13 -6
- package/dist/hooks/util/useImagePicker.js.map +1 -1
- 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/internal/utils/resizeImage.js +61 -0
- package/dist/internal/utils/resizeImage.js.map +1 -0
- package/dist/providers/ImagePickerProvider.js +123 -102
- package/dist/providers/ImagePickerProvider.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/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +1 -1
- package/package.json +2 -7
- package/src/components/MinisContainer.tsx +6 -3
- package/src/hooks/content/useContent.ts +6 -17
- 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/hooks/util/useImagePicker.test.tsx +193 -0
- package/src/hooks/util/useImagePicker.ts +24 -5
- 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/internal/utils/resizeImage.test.ts +314 -0
- package/src/internal/utils/resizeImage.ts +108 -0
- package/src/providers/ImagePickerProvider.test.tsx +32 -1
- package/src/providers/ImagePickerProvider.tsx +108 -65
- 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,193 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import {renderHook} from '@testing-library/react'
|
|
4
|
+
import {describe, expect, it, vi, beforeEach} from 'vitest'
|
|
5
|
+
|
|
6
|
+
import {useImagePicker} from './useImagePicker'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
OpenCameraParams,
|
|
10
|
+
OpenGalleryParams,
|
|
11
|
+
} from '../../providers/ImagePickerProvider'
|
|
12
|
+
|
|
13
|
+
// Mock the ImagePickerProvider context
|
|
14
|
+
const mockOpenCamera = vi.fn()
|
|
15
|
+
const mockOpenGallery = vi.fn()
|
|
16
|
+
|
|
17
|
+
vi.mock('../../providers/ImagePickerProvider', () => ({
|
|
18
|
+
useImagePickerContext: () => ({
|
|
19
|
+
openCamera: mockOpenCamera,
|
|
20
|
+
openGallery: mockOpenGallery,
|
|
21
|
+
}),
|
|
22
|
+
ImagePickerProvider: ({children}: {children: React.ReactNode}) => children,
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
describe('useImagePicker', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.clearAllMocks()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('openCamera', () => {
|
|
31
|
+
it('calls context openCamera with provided params', async () => {
|
|
32
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
33
|
+
mockOpenCamera.mockResolvedValue(mockFile)
|
|
34
|
+
|
|
35
|
+
const {result} = renderHook(() => useImagePicker())
|
|
36
|
+
|
|
37
|
+
const params: OpenCameraParams = {
|
|
38
|
+
cameraFacing: 'front',
|
|
39
|
+
quality: 'high',
|
|
40
|
+
customQuality: {size: 2000, compression: 0.9},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const file = await result.current.openCamera(params)
|
|
44
|
+
|
|
45
|
+
expect(mockOpenCamera).toHaveBeenCalledWith(params)
|
|
46
|
+
expect(file).toBe(mockFile)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('calls context openCamera with default params when none provided', async () => {
|
|
50
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
51
|
+
mockOpenCamera.mockResolvedValue(mockFile)
|
|
52
|
+
|
|
53
|
+
const {result} = renderHook(() => useImagePicker())
|
|
54
|
+
|
|
55
|
+
const file = await result.current.openCamera()
|
|
56
|
+
|
|
57
|
+
expect(mockOpenCamera).toHaveBeenCalledWith({
|
|
58
|
+
cameraFacing: undefined,
|
|
59
|
+
quality: undefined,
|
|
60
|
+
customQuality: undefined,
|
|
61
|
+
})
|
|
62
|
+
expect(file).toBe(mockFile)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('supports partial params', async () => {
|
|
66
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
67
|
+
mockOpenCamera.mockResolvedValue(mockFile)
|
|
68
|
+
|
|
69
|
+
const {result} = renderHook(() => useImagePicker())
|
|
70
|
+
|
|
71
|
+
const file = await result.current.openCamera({quality: 'low'})
|
|
72
|
+
|
|
73
|
+
expect(mockOpenCamera).toHaveBeenCalledWith({
|
|
74
|
+
cameraFacing: undefined,
|
|
75
|
+
quality: 'low',
|
|
76
|
+
customQuality: undefined,
|
|
77
|
+
})
|
|
78
|
+
expect(file).toBe(mockFile)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('openGallery', () => {
|
|
83
|
+
it('calls context openGallery with provided params', async () => {
|
|
84
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
85
|
+
mockOpenGallery.mockResolvedValue(mockFile)
|
|
86
|
+
|
|
87
|
+
const {result} = renderHook(() => useImagePicker())
|
|
88
|
+
|
|
89
|
+
const params: OpenGalleryParams = {
|
|
90
|
+
quality: 'medium',
|
|
91
|
+
customQuality: {size: 1500, compression: 0.8},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const file = await result.current.openGallery(params)
|
|
95
|
+
|
|
96
|
+
expect(mockOpenGallery).toHaveBeenCalledWith(params)
|
|
97
|
+
expect(file).toBe(mockFile)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('calls context openGallery with default params when none provided', async () => {
|
|
101
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
102
|
+
mockOpenGallery.mockResolvedValue(mockFile)
|
|
103
|
+
|
|
104
|
+
const {result} = renderHook(() => useImagePicker())
|
|
105
|
+
|
|
106
|
+
const file = await result.current.openGallery()
|
|
107
|
+
|
|
108
|
+
expect(mockOpenGallery).toHaveBeenCalledWith({
|
|
109
|
+
quality: undefined,
|
|
110
|
+
customQuality: undefined,
|
|
111
|
+
})
|
|
112
|
+
expect(file).toBe(mockFile)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('supports partial params', async () => {
|
|
116
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
117
|
+
mockOpenGallery.mockResolvedValue(mockFile)
|
|
118
|
+
|
|
119
|
+
const {result} = renderHook(() => useImagePicker())
|
|
120
|
+
|
|
121
|
+
const file = await result.current.openGallery({quality: 'original'})
|
|
122
|
+
|
|
123
|
+
expect(mockOpenGallery).toHaveBeenCalledWith({
|
|
124
|
+
quality: 'original',
|
|
125
|
+
customQuality: undefined,
|
|
126
|
+
})
|
|
127
|
+
expect(file).toBe(mockFile)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('Error handling', () => {
|
|
132
|
+
it('propagates errors from openCamera', async () => {
|
|
133
|
+
const error = new Error('Camera permission denied')
|
|
134
|
+
mockOpenCamera.mockRejectedValue(error)
|
|
135
|
+
|
|
136
|
+
const {result} = renderHook(() => useImagePicker())
|
|
137
|
+
|
|
138
|
+
await expect(result.current.openCamera()).rejects.toThrow(
|
|
139
|
+
'Camera permission denied'
|
|
140
|
+
)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('propagates errors from openGallery', async () => {
|
|
144
|
+
const error = new Error('Gallery access denied')
|
|
145
|
+
mockOpenGallery.mockRejectedValue(error)
|
|
146
|
+
|
|
147
|
+
const {result} = renderHook(() => useImagePicker())
|
|
148
|
+
|
|
149
|
+
await expect(result.current.openGallery()).rejects.toThrow(
|
|
150
|
+
'Gallery access denied'
|
|
151
|
+
)
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
describe('Quality settings', () => {
|
|
156
|
+
it('passes through all quality options for camera', async () => {
|
|
157
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
158
|
+
mockOpenCamera.mockResolvedValue(mockFile)
|
|
159
|
+
|
|
160
|
+
const {result} = renderHook(() => useImagePicker())
|
|
161
|
+
|
|
162
|
+
// Test each quality setting
|
|
163
|
+
const qualities = ['low', 'medium', 'high', 'original'] as const
|
|
164
|
+
|
|
165
|
+
for (const quality of qualities) {
|
|
166
|
+
await result.current.openCamera({quality})
|
|
167
|
+
expect(mockOpenCamera).toHaveBeenLastCalledWith({
|
|
168
|
+
cameraFacing: undefined,
|
|
169
|
+
quality,
|
|
170
|
+
customQuality: undefined,
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('passes through all quality options for gallery', async () => {
|
|
176
|
+
const mockFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
177
|
+
mockOpenGallery.mockResolvedValue(mockFile)
|
|
178
|
+
|
|
179
|
+
const {result} = renderHook(() => useImagePicker())
|
|
180
|
+
|
|
181
|
+
// Test each quality setting
|
|
182
|
+
const qualities = ['low', 'medium', 'high', 'original'] as const
|
|
183
|
+
|
|
184
|
+
for (const quality of qualities) {
|
|
185
|
+
await result.current.openGallery({quality})
|
|
186
|
+
expect(mockOpenGallery).toHaveBeenLastCalledWith({
|
|
187
|
+
quality,
|
|
188
|
+
customQuality: undefined,
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
})
|
|
@@ -1,24 +1,43 @@
|
|
|
1
|
+
import {useCallback} from 'react'
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
|
-
CameraFacing,
|
|
3
4
|
useImagePickerContext,
|
|
5
|
+
OpenCameraParams,
|
|
6
|
+
OpenGalleryParams,
|
|
4
7
|
} from '../../providers/ImagePickerProvider'
|
|
5
8
|
|
|
6
9
|
interface UseImagePickerReturns {
|
|
7
10
|
/**
|
|
8
11
|
* Opens the camera to take a photo.
|
|
9
12
|
*/
|
|
10
|
-
openCamera: (
|
|
13
|
+
openCamera: (params?: OpenCameraParams) => Promise<File>
|
|
11
14
|
/**
|
|
12
15
|
* Opens the gallery to select an image.
|
|
13
16
|
*/
|
|
14
|
-
openGallery: () => Promise<File>
|
|
17
|
+
openGallery: (params?: OpenGalleryParams) => Promise<File>
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
export function useImagePicker(): UseImagePickerReturns {
|
|
18
21
|
const {openCamera, openGallery} = useImagePickerContext()
|
|
19
22
|
|
|
23
|
+
const openCameraWithQuality = useCallback(
|
|
24
|
+
async ({cameraFacing, quality, customQuality}: OpenCameraParams = {}) => {
|
|
25
|
+
const file = await openCamera({cameraFacing, quality, customQuality})
|
|
26
|
+
return file
|
|
27
|
+
},
|
|
28
|
+
[openCamera]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const openGalleryWithQuality = useCallback(
|
|
32
|
+
async ({quality, customQuality}: OpenGalleryParams = {}) => {
|
|
33
|
+
const file = await openGallery({quality, customQuality})
|
|
34
|
+
return file
|
|
35
|
+
},
|
|
36
|
+
[openGallery]
|
|
37
|
+
)
|
|
38
|
+
|
|
20
39
|
return {
|
|
21
|
-
openCamera,
|
|
22
|
-
openGallery,
|
|
40
|
+
openCamera: openCameraWithQuality,
|
|
41
|
+
openGallery: openGalleryWithQuality,
|
|
23
42
|
}
|
|
24
43
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {QueryClient} from '@tanstack/react-query'
|
|
2
|
+
import {renderHook} from '@testing-library/react'
|
|
3
|
+
import {describe, expect, it} from 'vitest'
|
|
4
|
+
|
|
5
|
+
import {MinisQueryProvider} from './MinisQueryProvider'
|
|
6
|
+
import {useShopMinisQueryClient} from './queryClient'
|
|
7
|
+
|
|
8
|
+
describe('MinisQueryProvider', () => {
|
|
9
|
+
it('provides QueryClient to children', () => {
|
|
10
|
+
const {result} = renderHook(() => useShopMinisQueryClient(), {
|
|
11
|
+
wrapper: MinisQueryProvider,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
expect(result.current).toBeInstanceOf(QueryClient)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('allows hooks to work within provider', () => {
|
|
18
|
+
const {result} = renderHook(() => useShopMinisQueryClient(), {
|
|
19
|
+
wrapper: MinisQueryProvider,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// Should not throw
|
|
23
|
+
expect(() => result.current).not.toThrow()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('provides consistent QueryClient across renders', () => {
|
|
27
|
+
const {result, rerender} = renderHook(() => useShopMinisQueryClient(), {
|
|
28
|
+
wrapper: MinisQueryProvider,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const firstClient = result.current
|
|
32
|
+
rerender()
|
|
33
|
+
const secondClient = result.current
|
|
34
|
+
|
|
35
|
+
// Same instance across re-renders
|
|
36
|
+
expect(firstClient).toBe(secondClient)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React, {useMemo} from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createShopMinisQueryClient,
|
|
5
|
+
ShopMinisQueryClientContext,
|
|
6
|
+
} from './queryClient'
|
|
7
|
+
|
|
8
|
+
export function MinisQueryProvider({children}: {children: React.ReactNode}) {
|
|
9
|
+
const queryClient = useMemo(() => createShopMinisQueryClient(), [])
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<ShopMinisQueryClientContext.Provider value={queryClient}>
|
|
13
|
+
{children}
|
|
14
|
+
</ShopMinisQueryClientContext.Provider>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ShopMinisQueryClientContext,
|
|
3
|
+
useShopMinisQueryClient,
|
|
4
|
+
createShopMinisQueryClient,
|
|
5
|
+
} from './queryClient'
|
|
6
|
+
export {MinisQueryProvider} from './MinisQueryProvider'
|
|
7
|
+
export {useShopActionQuery} from './useShopActionQuery'
|
|
8
|
+
export {useShopActionInfiniteQuery} from './useShopActionInfiniteQuery'
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import {QueryClient} from '@tanstack/react-query'
|
|
4
|
+
import {renderHook} from '@testing-library/react'
|
|
5
|
+
import {describe, expect, it} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createShopMinisQueryClient,
|
|
9
|
+
ShopMinisQueryClientContext,
|
|
10
|
+
useShopMinisQueryClient,
|
|
11
|
+
} from './queryClient'
|
|
12
|
+
|
|
13
|
+
describe('queryClient', () => {
|
|
14
|
+
describe('createShopMinisQueryClient', () => {
|
|
15
|
+
it('creates a QueryClient instance', () => {
|
|
16
|
+
const client = createShopMinisQueryClient()
|
|
17
|
+
|
|
18
|
+
expect(client).toBeInstanceOf(QueryClient)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('creates isolated QueryClient instances', () => {
|
|
22
|
+
const client1 = createShopMinisQueryClient()
|
|
23
|
+
const client2 = createShopMinisQueryClient()
|
|
24
|
+
|
|
25
|
+
// Each call creates a new instance
|
|
26
|
+
expect(client1).not.toBe(client2)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('has caching disabled by default', () => {
|
|
30
|
+
const client = createShopMinisQueryClient()
|
|
31
|
+
|
|
32
|
+
const defaults = client.getDefaultOptions()
|
|
33
|
+
|
|
34
|
+
expect(defaults.queries?.staleTime).toBe(0)
|
|
35
|
+
expect(defaults.queries?.gcTime).toBe(0)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('has retry and refetchOnWindowFocus configured', () => {
|
|
39
|
+
const client = createShopMinisQueryClient()
|
|
40
|
+
|
|
41
|
+
const defaults = client.getDefaultOptions()
|
|
42
|
+
|
|
43
|
+
expect(defaults.queries?.retry).toBe(1)
|
|
44
|
+
expect(defaults.queries?.refetchOnWindowFocus).toBe(false)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('useShopMinisQueryClient', () => {
|
|
49
|
+
it('throws error when used outside provider', () => {
|
|
50
|
+
expect(() => {
|
|
51
|
+
renderHook(() => useShopMinisQueryClient())
|
|
52
|
+
}).toThrow(
|
|
53
|
+
'Shop Minis hooks must be used within <MinisContainer> or <MinisQueryProvider>. ' +
|
|
54
|
+
'Wrap your component tree with one of these providers.'
|
|
55
|
+
)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('returns QueryClient when used within provider', () => {
|
|
59
|
+
const client = createShopMinisQueryClient()
|
|
60
|
+
const wrapper = ({children}: {children: React.ReactNode}) => (
|
|
61
|
+
<ShopMinisQueryClientContext.Provider value={client}>
|
|
62
|
+
{children}
|
|
63
|
+
</ShopMinisQueryClientContext.Provider>
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const {result} = renderHook(() => useShopMinisQueryClient(), {wrapper})
|
|
67
|
+
|
|
68
|
+
expect(result.current).toBe(client)
|
|
69
|
+
expect(result.current).toBeInstanceOf(QueryClient)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('returns the same client instance across re-renders', () => {
|
|
73
|
+
const client = createShopMinisQueryClient()
|
|
74
|
+
const wrapper = ({children}: {children: React.ReactNode}) => (
|
|
75
|
+
<ShopMinisQueryClientContext.Provider value={client}>
|
|
76
|
+
{children}
|
|
77
|
+
</ShopMinisQueryClientContext.Provider>
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
const {result, rerender} = renderHook(() => useShopMinisQueryClient(), {
|
|
81
|
+
wrapper,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const firstClient = result.current
|
|
85
|
+
rerender()
|
|
86
|
+
const secondClient = result.current
|
|
87
|
+
|
|
88
|
+
expect(firstClient).toBe(secondClient)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {createContext, useContext} from 'react'
|
|
2
|
+
|
|
3
|
+
import {QueryClient} from '@tanstack/react-query'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Custom QueryClient context for Shop Minis SDK
|
|
7
|
+
* This ensures our SDK always uses its own QueryClient,
|
|
8
|
+
* even if the parent app has their own QueryClientProvider
|
|
9
|
+
*/
|
|
10
|
+
export const ShopMinisQueryClientContext = createContext<QueryClient | null>(
|
|
11
|
+
null
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
export function useShopMinisQueryClient(): QueryClient {
|
|
15
|
+
const client = useContext(ShopMinisQueryClientContext)
|
|
16
|
+
if (!client) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'Shop Minis hooks must be used within <MinisContainer> or <MinisQueryProvider>. ' +
|
|
19
|
+
'Wrap your component tree with one of these providers.'
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
return client
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a QueryClient instance for Shop Minis SDK
|
|
27
|
+
* Isolated from any parent app's QueryClient
|
|
28
|
+
*
|
|
29
|
+
* Caching is disabled by default since Apollo provides the cache layer.
|
|
30
|
+
* React Query only provides request deduplication and state management.
|
|
31
|
+
*/
|
|
32
|
+
export function createShopMinisQueryClient() {
|
|
33
|
+
return new QueryClient({
|
|
34
|
+
defaultOptions: {
|
|
35
|
+
queries: {
|
|
36
|
+
staleTime: 0, // Data is immediately stale
|
|
37
|
+
gcTime: 0, // Don't keep in cache after component unmounts
|
|
38
|
+
retry: 1,
|
|
39
|
+
refetchOnWindowFocus: false,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
}
|