@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.
- package/dist/_virtual/index4.js +2 -2
- package/dist/_virtual/index5.js +2 -3
- package/dist/_virtual/index5.js.map +1 -1
- package/dist/_virtual/index6.js +2 -2
- package/dist/_virtual/index7.js +3 -2
- package/dist/_virtual/index7.js.map +1 -1
- package/dist/components/atoms/alert-dialog.js.map +1 -1
- package/dist/components/atoms/icon-button.js +12 -12
- package/dist/components/atoms/icon-button.js.map +1 -1
- package/dist/components/atoms/text-input.js +22 -0
- package/dist/components/atoms/text-input.js.map +1 -0
- package/dist/components/commerce/merchant-card.js +1 -0
- package/dist/components/commerce/merchant-card.js.map +1 -1
- package/dist/components/ui/input.js +15 -9
- package/dist/components/ui/input.js.map +1 -1
- package/dist/hooks/util/useKeyboardAvoidingView.js +23 -0
- package/dist/hooks/util/useKeyboardAvoidingView.js.map +1 -0
- package/dist/index.js +226 -222
- package/dist/index.js.map +1 -1
- package/dist/mocks.js +4 -1
- package/dist/mocks.js.map +1 -1
- 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
- package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
- package/dist/utils/image.js +5 -4
- package/dist/utils/image.js.map +1 -1
- package/package.json +21 -4
- package/src/components/atoms/alert-dialog.test.tsx +67 -0
- package/src/components/atoms/alert-dialog.tsx +13 -11
- package/src/components/atoms/favorite-button.test.tsx +56 -0
- package/src/components/atoms/icon-button.tsx +1 -1
- package/src/components/atoms/image.test.tsx +108 -0
- package/src/components/atoms/product-variant-price.test.tsx +128 -0
- package/src/components/atoms/text-input.test.tsx +104 -0
- package/src/components/atoms/text-input.tsx +31 -0
- package/src/components/commerce/merchant-card.test.tsx +261 -0
- package/src/components/commerce/merchant-card.tsx +2 -0
- package/src/components/commerce/product-card.test.tsx +364 -0
- package/src/components/commerce/product-link.test.tsx +483 -0
- package/src/components/commerce/quantity-selector.test.tsx +382 -0
- package/src/components/commerce/search.test.tsx +487 -0
- package/src/components/content/image-content-wrapper.test.tsx +92 -0
- package/src/components/index.ts +1 -0
- package/src/components/navigation/transition-link.test.tsx +155 -0
- package/src/components/ui/input.test.tsx +21 -0
- package/src/components/ui/input.tsx +10 -1
- package/src/hooks/content/useCreateImageContent.test.ts +352 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/navigation/useNavigateWithTransition.test.ts +371 -0
- package/src/hooks/navigation/useViewTransitions.test.ts +469 -0
- package/src/hooks/product/useProductSearch.test.ts +470 -0
- package/src/hooks/storage/useAsyncStorage.test.ts +225 -0
- package/src/hooks/storage/useImageUpload.test.ts +322 -0
- package/src/hooks/util/useKeyboardAvoidingView.ts +37 -0
- package/src/internal/useHandleAction.test.ts +265 -0
- package/src/internal/useShopActionsDataFetching.test.ts +465 -0
- package/src/mocks.ts +3 -1
- package/src/providers/ImagePickerProvider.test.tsx +467 -0
- package/src/stories/ProductCard.stories.tsx +2 -2
- package/src/stories/TextInput.stories.tsx +26 -0
- package/src/test-setup.ts +34 -0
- package/src/test-utils.tsx +167 -0
- 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
|
+
})
|