@shopify/shop-minis-react 0.0.34 → 0.0.35

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 (64) hide show
  1. package/dist/_virtual/index4.js +2 -2
  2. package/dist/_virtual/index5.js +2 -3
  3. package/dist/_virtual/index5.js.map +1 -1
  4. package/dist/_virtual/index6.js +2 -2
  5. package/dist/_virtual/index7.js +3 -2
  6. package/dist/_virtual/index7.js.map +1 -1
  7. package/dist/components/atoms/alert-dialog.js.map +1 -1
  8. package/dist/components/atoms/icon-button.js +12 -12
  9. package/dist/components/atoms/icon-button.js.map +1 -1
  10. package/dist/components/atoms/text-input.js +22 -0
  11. package/dist/components/atoms/text-input.js.map +1 -0
  12. package/dist/components/commerce/merchant-card.js +1 -0
  13. package/dist/components/commerce/merchant-card.js.map +1 -1
  14. package/dist/components/ui/input.js +15 -9
  15. package/dist/components/ui/input.js.map +1 -1
  16. package/dist/hooks/util/useKeyboardAvoidingView.js +23 -0
  17. package/dist/hooks/util/useKeyboardAvoidingView.js.map +1 -0
  18. package/dist/index.js +226 -222
  19. package/dist/index.js.map +1 -1
  20. package/dist/mocks.js +4 -1
  21. package/dist/mocks.js.map +1 -1
  22. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +1 -1
  23. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js +1 -1
  24. package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js +1 -1
  25. package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
  26. package/dist/utils/image.js +5 -4
  27. package/dist/utils/image.js.map +1 -1
  28. package/package.json +21 -4
  29. package/src/components/atoms/alert-dialog.test.tsx +67 -0
  30. package/src/components/atoms/alert-dialog.tsx +13 -11
  31. package/src/components/atoms/favorite-button.test.tsx +56 -0
  32. package/src/components/atoms/icon-button.tsx +1 -1
  33. package/src/components/atoms/image.test.tsx +108 -0
  34. package/src/components/atoms/product-variant-price.test.tsx +128 -0
  35. package/src/components/atoms/text-input.test.tsx +104 -0
  36. package/src/components/atoms/text-input.tsx +31 -0
  37. package/src/components/commerce/merchant-card.test.tsx +261 -0
  38. package/src/components/commerce/merchant-card.tsx +2 -0
  39. package/src/components/commerce/product-card.test.tsx +364 -0
  40. package/src/components/commerce/product-link.test.tsx +483 -0
  41. package/src/components/commerce/quantity-selector.test.tsx +382 -0
  42. package/src/components/commerce/search.test.tsx +487 -0
  43. package/src/components/content/image-content-wrapper.test.tsx +92 -0
  44. package/src/components/index.ts +1 -0
  45. package/src/components/navigation/transition-link.test.tsx +155 -0
  46. package/src/components/ui/input.test.tsx +21 -0
  47. package/src/components/ui/input.tsx +10 -1
  48. package/src/hooks/content/useCreateImageContent.test.ts +352 -0
  49. package/src/hooks/index.ts +1 -0
  50. package/src/hooks/navigation/useNavigateWithTransition.test.ts +371 -0
  51. package/src/hooks/navigation/useViewTransitions.test.ts +469 -0
  52. package/src/hooks/product/useProductSearch.test.ts +470 -0
  53. package/src/hooks/storage/useAsyncStorage.test.ts +225 -0
  54. package/src/hooks/storage/useImageUpload.test.ts +322 -0
  55. package/src/hooks/util/useKeyboardAvoidingView.ts +37 -0
  56. package/src/internal/useHandleAction.test.ts +265 -0
  57. package/src/internal/useShopActionsDataFetching.test.ts +465 -0
  58. package/src/mocks.ts +3 -1
  59. package/src/providers/ImagePickerProvider.test.tsx +467 -0
  60. package/src/stories/ProductCard.stories.tsx +2 -2
  61. package/src/stories/TextInput.stories.tsx +26 -0
  62. package/src/test-setup.ts +34 -0
  63. package/src/test-utils.tsx +167 -0
  64. package/src/utils/image.ts +1 -0
@@ -0,0 +1,371 @@
1
+ import {renderHook, act} from '@testing-library/react'
2
+ import {describe, expect, it, vi, beforeEach, afterEach} from 'vitest'
3
+
4
+ import {useNavigateWithTransition} from './useNavigateWithTransition'
5
+
6
+ // Mock react-router
7
+ const mockNavigate = vi.fn()
8
+ const mockLocation = {pathname: '/current'}
9
+
10
+ vi.mock('react-router', () => ({
11
+ useNavigate: () => mockNavigate,
12
+ useLocation: () => mockLocation,
13
+ NavigateOptions: {},
14
+ }))
15
+
16
+ describe('useNavigateWithTransition', () => {
17
+ let originalStartViewTransition: any
18
+
19
+ beforeEach(() => {
20
+ vi.clearAllMocks()
21
+ originalStartViewTransition = document.startViewTransition
22
+ mockLocation.pathname = '/current'
23
+ })
24
+
25
+ afterEach(() => {
26
+ document.startViewTransition = originalStartViewTransition
27
+ document.documentElement.removeAttribute('data-navigation-type')
28
+ })
29
+
30
+ describe('Without View Transitions API', () => {
31
+ beforeEach(() => {
32
+ // Remove startViewTransition to simulate unsupported browser
33
+ ;(document as any).startViewTransition = undefined
34
+ })
35
+
36
+ it('navigates directly when view transitions not supported', () => {
37
+ const {result} = renderHook(() => useNavigateWithTransition())
38
+
39
+ act(() => {
40
+ result.current('/new-route')
41
+ })
42
+
43
+ expect(mockNavigate).toHaveBeenCalledWith('/new-route', undefined)
44
+ })
45
+
46
+ it('navigates with options when view transitions not supported', () => {
47
+ const {result} = renderHook(() => useNavigateWithTransition())
48
+
49
+ const options = {replace: true, state: {from: 'test'}}
50
+
51
+ act(() => {
52
+ result.current('/new-route', options)
53
+ })
54
+
55
+ expect(mockNavigate).toHaveBeenCalledWith('/new-route', options)
56
+ })
57
+
58
+ it('handles delta navigation without view transitions', () => {
59
+ const {result} = renderHook(() => useNavigateWithTransition())
60
+
61
+ act(() => {
62
+ result.current(-1)
63
+ })
64
+
65
+ expect(mockNavigate).toHaveBeenCalledWith(-1)
66
+ })
67
+ })
68
+
69
+ describe('With View Transitions API', () => {
70
+ let mockTransition: any
71
+
72
+ beforeEach(() => {
73
+ mockTransition = {
74
+ finished: Promise.resolve(),
75
+ ready: Promise.resolve(),
76
+ types: new Set<string>(),
77
+ updateCallbackDone: Promise.resolve(),
78
+ skipTransition: () => {},
79
+ }
80
+
81
+ // Mock startViewTransition
82
+ document.startViewTransition = vi.fn((callback: () => void) => {
83
+ callback()
84
+ return mockTransition
85
+ }) as any
86
+ })
87
+
88
+ it('uses view transition for path navigation', async () => {
89
+ const {result} = renderHook(() => useNavigateWithTransition())
90
+
91
+ await act(async () => {
92
+ result.current('/new-route')
93
+ })
94
+
95
+ expect(document.startViewTransition).toHaveBeenCalled()
96
+ expect(mockNavigate).toHaveBeenCalledWith('/new-route', {
97
+ preventScrollReset: true,
98
+ replace: false,
99
+ })
100
+ })
101
+
102
+ it('uses replace navigation for same route', async () => {
103
+ const {result} = renderHook(() => useNavigateWithTransition())
104
+
105
+ await act(async () => {
106
+ result.current('/current')
107
+ })
108
+
109
+ expect(document.startViewTransition).toHaveBeenCalled()
110
+ expect(mockNavigate).toHaveBeenCalledWith('/current', {
111
+ preventScrollReset: true,
112
+ replace: true,
113
+ })
114
+ })
115
+
116
+ it('merges custom options with defaults', async () => {
117
+ const {result} = renderHook(() => useNavigateWithTransition())
118
+
119
+ const customOptions = {state: {from: 'test'}, replace: true}
120
+
121
+ await act(async () => {
122
+ result.current('/new-route', customOptions)
123
+ })
124
+
125
+ expect(mockNavigate).toHaveBeenCalledWith('/new-route', {
126
+ preventScrollReset: true,
127
+ replace: true,
128
+ state: {from: 'test'},
129
+ })
130
+ })
131
+
132
+ it('removes navigation type attribute after transition completes', async () => {
133
+ const removeAttributeSpy = vi.spyOn(
134
+ document.documentElement,
135
+ 'removeAttribute'
136
+ )
137
+
138
+ const {result} = renderHook(() => useNavigateWithTransition())
139
+
140
+ await act(async () => {
141
+ result.current('/new-route')
142
+ await mockTransition.finished
143
+ })
144
+
145
+ expect(removeAttributeSpy).toHaveBeenCalledWith('data-navigation-type')
146
+ })
147
+
148
+ it('handles view transition errors gracefully', async () => {
149
+ const consoleErrorSpy = vi
150
+ .spyOn(console, 'error')
151
+ .mockImplementation(() => {})
152
+
153
+ const errorTransition = {
154
+ finished: Promise.reject(new Error('Transition failed')),
155
+ ready: Promise.resolve(),
156
+ types: new Set<string>(),
157
+ updateCallbackDone: Promise.resolve(),
158
+ skipTransition: () => {},
159
+ }
160
+
161
+ document.startViewTransition = vi.fn((callback: () => void) => {
162
+ callback()
163
+ return errorTransition
164
+ }) as any
165
+
166
+ const {result} = renderHook(() => useNavigateWithTransition())
167
+
168
+ await act(async () => {
169
+ result.current('/new-route')
170
+ try {
171
+ await errorTransition.finished
172
+ } catch {
173
+ // Expected error
174
+ }
175
+ })
176
+
177
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
178
+ 'View transition error:',
179
+ expect.any(Error)
180
+ )
181
+
182
+ consoleErrorSpy.mockRestore()
183
+ })
184
+
185
+ it('uses view transition for delta navigation', async () => {
186
+ const {result} = renderHook(() => useNavigateWithTransition())
187
+
188
+ await act(async () => {
189
+ result.current(-1)
190
+ })
191
+
192
+ expect(document.startViewTransition).toHaveBeenCalled()
193
+ expect(mockNavigate).toHaveBeenCalledWith(-1)
194
+ })
195
+
196
+ it('removes attribute after delta navigation transition', async () => {
197
+ const removeAttributeSpy = vi.spyOn(
198
+ document.documentElement,
199
+ 'removeAttribute'
200
+ )
201
+
202
+ const {result} = renderHook(() => useNavigateWithTransition())
203
+
204
+ await act(async () => {
205
+ result.current(-2)
206
+ await mockTransition.finished
207
+ })
208
+
209
+ expect(removeAttributeSpy).toHaveBeenCalledWith('data-navigation-type')
210
+ })
211
+
212
+ it('handles positive delta navigation', async () => {
213
+ const {result} = renderHook(() => useNavigateWithTransition())
214
+
215
+ await act(async () => {
216
+ result.current(1)
217
+ })
218
+
219
+ expect(document.startViewTransition).toHaveBeenCalled()
220
+ expect(mockNavigate).toHaveBeenCalledWith(1)
221
+ })
222
+ })
223
+
224
+ describe('Edge Cases', () => {
225
+ beforeEach(() => {
226
+ document.startViewTransition = vi.fn((callback: () => void) => {
227
+ callback()
228
+ return {
229
+ finished: Promise.resolve(),
230
+ ready: Promise.resolve(),
231
+ types: new Set<string>(),
232
+ updateCallbackDone: Promise.resolve(),
233
+ skipTransition: () => {},
234
+ }
235
+ }) as any
236
+ })
237
+
238
+ it('handles navigation to empty string', async () => {
239
+ const {result} = renderHook(() => useNavigateWithTransition())
240
+
241
+ await act(async () => {
242
+ result.current('')
243
+ })
244
+
245
+ expect(mockNavigate).toHaveBeenCalledWith('', {
246
+ preventScrollReset: true,
247
+ replace: false,
248
+ })
249
+ })
250
+
251
+ it('handles navigation to root path', async () => {
252
+ const {result} = renderHook(() => useNavigateWithTransition())
253
+
254
+ await act(async () => {
255
+ result.current('/')
256
+ })
257
+
258
+ expect(mockNavigate).toHaveBeenCalledWith('/', {
259
+ preventScrollReset: true,
260
+ replace: false,
261
+ })
262
+ })
263
+
264
+ it('handles navigation with query parameters', async () => {
265
+ const {result} = renderHook(() => useNavigateWithTransition())
266
+
267
+ await act(async () => {
268
+ result.current('/search?q=test&page=2')
269
+ })
270
+
271
+ expect(mockNavigate).toHaveBeenCalledWith('/search?q=test&page=2', {
272
+ preventScrollReset: true,
273
+ replace: false,
274
+ })
275
+ })
276
+
277
+ it('handles navigation with hash', async () => {
278
+ const {result} = renderHook(() => useNavigateWithTransition())
279
+
280
+ await act(async () => {
281
+ result.current('/page#section')
282
+ })
283
+
284
+ expect(mockNavigate).toHaveBeenCalledWith('/page#section', {
285
+ preventScrollReset: true,
286
+ replace: false,
287
+ })
288
+ })
289
+
290
+ it('handles zero delta navigation', async () => {
291
+ const {result} = renderHook(() => useNavigateWithTransition())
292
+
293
+ await act(async () => {
294
+ result.current(0)
295
+ })
296
+
297
+ expect(document.startViewTransition).toHaveBeenCalled()
298
+ expect(mockNavigate).toHaveBeenCalledWith(0)
299
+ })
300
+ })
301
+
302
+ describe('Multiple Navigations', () => {
303
+ beforeEach(() => {
304
+ const transitions: any[] = []
305
+
306
+ document.startViewTransition = vi.fn((callback: () => void) => {
307
+ callback()
308
+ const transition = {
309
+ finished: new Promise(resolve => setTimeout(resolve, 10)),
310
+ ready: Promise.resolve(),
311
+ types: new Set<string>(),
312
+ updateCallbackDone: Promise.resolve(),
313
+ skipTransition: () => {},
314
+ }
315
+ transitions.push(transition)
316
+ return transition
317
+ }) as any
318
+ })
319
+
320
+ it('handles multiple sequential navigations', async () => {
321
+ const {result} = renderHook(() => useNavigateWithTransition())
322
+
323
+ await act(async () => {
324
+ result.current('/route1')
325
+ result.current('/route2')
326
+ result.current('/route3')
327
+ })
328
+
329
+ expect(document.startViewTransition).toHaveBeenCalledTimes(3)
330
+ expect(mockNavigate).toHaveBeenCalledTimes(3)
331
+ expect(mockNavigate).toHaveBeenNthCalledWith(
332
+ 1,
333
+ '/route1',
334
+ expect.any(Object)
335
+ )
336
+ expect(mockNavigate).toHaveBeenNthCalledWith(
337
+ 2,
338
+ '/route2',
339
+ expect.any(Object)
340
+ )
341
+ expect(mockNavigate).toHaveBeenNthCalledWith(
342
+ 3,
343
+ '/route3',
344
+ expect.any(Object)
345
+ )
346
+ })
347
+
348
+ it('handles mixed path and delta navigations', async () => {
349
+ const {result} = renderHook(() => useNavigateWithTransition())
350
+
351
+ await act(async () => {
352
+ result.current('/forward')
353
+ result.current(-1)
354
+ result.current('/another')
355
+ })
356
+
357
+ expect(document.startViewTransition).toHaveBeenCalledTimes(3)
358
+ expect(mockNavigate).toHaveBeenNthCalledWith(
359
+ 1,
360
+ '/forward',
361
+ expect.any(Object)
362
+ )
363
+ expect(mockNavigate).toHaveBeenNthCalledWith(2, -1)
364
+ expect(mockNavigate).toHaveBeenNthCalledWith(
365
+ 3,
366
+ '/another',
367
+ expect.any(Object)
368
+ )
369
+ })
370
+ })
371
+ })