@shopify/shop-minis-react 0.0.33 → 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 (86) hide show
  1. package/dist/_virtual/index10.js +2 -2
  2. package/dist/_virtual/index2.js +4 -4
  3. package/dist/_virtual/index3.js +4 -4
  4. package/dist/_virtual/index8.js +2 -2
  5. package/dist/_virtual/index9.js +2 -2
  6. package/dist/components/atoms/alert-dialog.js.map +1 -1
  7. package/dist/components/atoms/icon-button.js +12 -12
  8. package/dist/components/atoms/icon-button.js.map +1 -1
  9. package/dist/components/atoms/image.js +52 -0
  10. package/dist/components/atoms/image.js.map +1 -0
  11. package/dist/components/atoms/text-input.js +22 -0
  12. package/dist/components/atoms/text-input.js.map +1 -0
  13. package/dist/components/commerce/merchant-card.js +2 -1
  14. package/dist/components/commerce/merchant-card.js.map +1 -1
  15. package/dist/components/commerce/product-card.js +11 -11
  16. package/dist/components/commerce/product-card.js.map +1 -1
  17. package/dist/components/content/image-content-wrapper.js +29 -22
  18. package/dist/components/content/image-content-wrapper.js.map +1 -1
  19. package/dist/components/ui/input.js +15 -9
  20. package/dist/components/ui/input.js.map +1 -1
  21. package/dist/hooks/content/useCreateImageContent.js +16 -22
  22. package/dist/hooks/content/useCreateImageContent.js.map +1 -1
  23. package/dist/hooks/storage/useImageUpload.js +36 -37
  24. package/dist/hooks/storage/useImageUpload.js.map +1 -1
  25. package/dist/hooks/util/useKeyboardAvoidingView.js +23 -0
  26. package/dist/hooks/util/useKeyboardAvoidingView.js.map +1 -0
  27. package/dist/index.js +218 -212
  28. package/dist/index.js.map +1 -1
  29. package/dist/mocks.js +4 -1
  30. package/dist/mocks.js.map +1 -1
  31. package/dist/shop-minis-platform/src/types/content.js.map +1 -1
  32. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +1 -1
  33. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js +1 -1
  34. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
  35. package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +1 -1
  36. package/dist/utils/colors.js +1 -1
  37. package/dist/utils/image.js +46 -9
  38. package/dist/utils/image.js.map +1 -1
  39. package/package.json +21 -4
  40. package/src/components/atoms/alert-dialog.test.tsx +67 -0
  41. package/src/components/atoms/alert-dialog.tsx +13 -11
  42. package/src/components/atoms/favorite-button.test.tsx +56 -0
  43. package/src/components/atoms/icon-button.tsx +1 -1
  44. package/src/components/atoms/image.test.tsx +108 -0
  45. package/src/components/atoms/{thumbhash-image.tsx → image.tsx} +14 -14
  46. package/src/components/atoms/product-variant-price.test.tsx +128 -0
  47. package/src/components/atoms/text-input.test.tsx +104 -0
  48. package/src/components/atoms/text-input.tsx +31 -0
  49. package/src/components/commerce/merchant-card.test.tsx +261 -0
  50. package/src/components/commerce/merchant-card.tsx +4 -2
  51. package/src/components/commerce/product-card.test.tsx +364 -0
  52. package/src/components/commerce/product-card.tsx +2 -2
  53. package/src/components/commerce/product-link.test.tsx +483 -0
  54. package/src/components/commerce/quantity-selector.test.tsx +382 -0
  55. package/src/components/commerce/search.test.tsx +487 -0
  56. package/src/components/content/image-content-wrapper.test.tsx +92 -0
  57. package/src/components/content/image-content-wrapper.tsx +9 -2
  58. package/src/components/index.ts +2 -1
  59. package/src/components/navigation/transition-link.test.tsx +155 -0
  60. package/src/components/ui/input.test.tsx +21 -0
  61. package/src/components/ui/input.tsx +10 -1
  62. package/src/hooks/content/useCreateImageContent.test.ts +352 -0
  63. package/src/hooks/content/useCreateImageContent.ts +1 -7
  64. package/src/hooks/index.ts +1 -0
  65. package/src/hooks/navigation/useNavigateWithTransition.test.ts +371 -0
  66. package/src/hooks/navigation/useViewTransitions.test.ts +469 -0
  67. package/src/hooks/product/useProductSearch.test.ts +470 -0
  68. package/src/hooks/storage/useAsyncStorage.test.ts +225 -0
  69. package/src/hooks/storage/useImageUpload.test.ts +322 -0
  70. package/src/hooks/storage/useImageUpload.ts +22 -20
  71. package/src/hooks/util/useKeyboardAvoidingView.ts +37 -0
  72. package/src/internal/useHandleAction.test.ts +265 -0
  73. package/src/internal/useShopActionsDataFetching.test.ts +465 -0
  74. package/src/mocks.ts +3 -1
  75. package/src/providers/ImagePickerProvider.test.tsx +467 -0
  76. package/src/stories/ProductCard.stories.tsx +2 -2
  77. package/src/stories/TextInput.stories.tsx +26 -0
  78. package/src/test-setup.ts +34 -0
  79. package/src/test-utils.tsx +167 -0
  80. package/src/utils/image.ts +73 -0
  81. package/src/utils/index.ts +1 -1
  82. package/dist/components/atoms/thumbhash-image.js +0 -54
  83. package/dist/components/atoms/thumbhash-image.js.map +0 -1
  84. package/dist/utils/imageToDataUri.js +0 -10
  85. package/dist/utils/imageToDataUri.js.map +0 -1
  86. package/src/utils/imageToDataUri.ts +0 -8
@@ -0,0 +1,382 @@
1
+ import {describe, expect, it, vi, beforeEach} from 'vitest'
2
+
3
+ import {render, screen, userEvent, waitFor} from '../../test-utils'
4
+
5
+ import {QuantitySelector} from './quantity-selector'
6
+
7
+ describe('QuantitySelector', () => {
8
+ const defaultProps = {
9
+ quantity: 1,
10
+ onQuantityChange: vi.fn(),
11
+ maxQuantity: 10,
12
+ }
13
+
14
+ beforeEach(() => {
15
+ vi.clearAllMocks()
16
+ })
17
+
18
+ describe('Rendering', () => {
19
+ it('renders with initial quantity', () => {
20
+ render(<QuantitySelector {...defaultProps} quantity={3} />)
21
+
22
+ expect(screen.getByText('3')).toBeInTheDocument()
23
+ })
24
+
25
+ it('renders increment and decrement buttons', () => {
26
+ render(<QuantitySelector {...defaultProps} />)
27
+
28
+ const buttons = screen.getAllByRole('button')
29
+ expect(buttons).toHaveLength(2)
30
+ })
31
+
32
+ it('displays correct quantity value', () => {
33
+ render(<QuantitySelector {...defaultProps} quantity={5} />)
34
+
35
+ expect(screen.getByText('5')).toBeInTheDocument()
36
+ })
37
+ })
38
+
39
+ describe('Increment Functionality', () => {
40
+ it('increments quantity on plus button click', async () => {
41
+ const onQuantityChange = vi.fn()
42
+ const user = userEvent.setup()
43
+
44
+ render(
45
+ <QuantitySelector
46
+ {...defaultProps}
47
+ quantity={1}
48
+ onQuantityChange={onQuantityChange}
49
+ />
50
+ )
51
+
52
+ const buttons = screen.getAllByRole('button')
53
+ const incrementButton = buttons[1] // Plus button is second
54
+
55
+ await user.click(incrementButton)
56
+
57
+ await waitFor(() => {
58
+ expect(onQuantityChange).toHaveBeenCalledWith(2)
59
+ })
60
+ })
61
+
62
+ it('does not increment beyond maxQuantity', async () => {
63
+ const onQuantityChange = vi.fn()
64
+ const user = userEvent.setup()
65
+
66
+ render(
67
+ <QuantitySelector
68
+ {...defaultProps}
69
+ quantity={10}
70
+ maxQuantity={10}
71
+ onQuantityChange={onQuantityChange}
72
+ />
73
+ )
74
+
75
+ const buttons = screen.getAllByRole('button')
76
+ const incrementButton = buttons[1]
77
+
78
+ await user.click(incrementButton)
79
+
80
+ // Should not call onQuantityChange with value > maxQuantity
81
+ await waitFor(() => {
82
+ expect(onQuantityChange).toHaveBeenCalledWith(10)
83
+ })
84
+ })
85
+
86
+ it('disables increment button at maxQuantity', async () => {
87
+ const onQuantityChange = vi.fn()
88
+ const user = userEvent.setup()
89
+
90
+ render(
91
+ <QuantitySelector
92
+ {...defaultProps}
93
+ quantity={10}
94
+ maxQuantity={10}
95
+ onQuantityChange={onQuantityChange}
96
+ />
97
+ )
98
+
99
+ const buttons = screen.getAllByRole('button')
100
+ const incrementButton = buttons[1]
101
+
102
+ // Clear previous calls
103
+ onQuantityChange.mockClear()
104
+
105
+ // Try to increment at max - quantity should not change
106
+ await user.click(incrementButton)
107
+
108
+ // Should not call onQuantityChange with a value greater than max
109
+ expect(onQuantityChange).not.toHaveBeenCalledWith(11)
110
+ expect(screen.getByText('10')).toBeInTheDocument()
111
+ })
112
+ })
113
+
114
+ describe('Decrement Functionality', () => {
115
+ it('decrements quantity on minus button click', async () => {
116
+ const onQuantityChange = vi.fn()
117
+ const user = userEvent.setup()
118
+
119
+ render(
120
+ <QuantitySelector
121
+ {...defaultProps}
122
+ quantity={3}
123
+ onQuantityChange={onQuantityChange}
124
+ />
125
+ )
126
+
127
+ const buttons = screen.getAllByRole('button')
128
+ const decrementButton = buttons[0] // Minus button is first
129
+
130
+ await user.click(decrementButton)
131
+
132
+ await waitFor(() => {
133
+ expect(onQuantityChange).toHaveBeenCalledWith(2)
134
+ })
135
+ })
136
+
137
+ it('does not decrement below minQuantity', async () => {
138
+ const onQuantityChange = vi.fn()
139
+ const user = userEvent.setup()
140
+
141
+ render(
142
+ <QuantitySelector
143
+ {...defaultProps}
144
+ quantity={1}
145
+ minQuantity={1}
146
+ onQuantityChange={onQuantityChange}
147
+ />
148
+ )
149
+
150
+ const buttons = screen.getAllByRole('button')
151
+ const decrementButton = buttons[0]
152
+
153
+ await user.click(decrementButton)
154
+
155
+ // Should not call onQuantityChange with value < minQuantity
156
+ await waitFor(() => {
157
+ expect(onQuantityChange).toHaveBeenCalledWith(1)
158
+ })
159
+ })
160
+
161
+ it('respects custom minQuantity', async () => {
162
+ const onQuantityChange = vi.fn()
163
+ const user = userEvent.setup()
164
+
165
+ render(
166
+ <QuantitySelector
167
+ {...defaultProps}
168
+ quantity={5}
169
+ minQuantity={3}
170
+ onQuantityChange={onQuantityChange}
171
+ />
172
+ )
173
+
174
+ const buttons = screen.getAllByRole('button')
175
+ const decrementButton = buttons[0]
176
+
177
+ // Click multiple times to try to go below minQuantity
178
+ await user.click(decrementButton)
179
+ await user.click(decrementButton)
180
+ await user.click(decrementButton)
181
+
182
+ await waitFor(() => {
183
+ // Should not go below 3
184
+ expect(onQuantityChange).toHaveBeenLastCalledWith(3)
185
+ })
186
+ })
187
+
188
+ it('disables decrement button at minQuantity', async () => {
189
+ const onQuantityChange = vi.fn()
190
+ const user = userEvent.setup()
191
+
192
+ render(
193
+ <QuantitySelector
194
+ {...defaultProps}
195
+ quantity={1}
196
+ minQuantity={1}
197
+ onQuantityChange={onQuantityChange}
198
+ />
199
+ )
200
+
201
+ const buttons = screen.getAllByRole('button')
202
+ const decrementButton = buttons[0]
203
+
204
+ // Clear previous calls
205
+ onQuantityChange.mockClear()
206
+
207
+ // Try to decrement at min - quantity should not change
208
+ await user.click(decrementButton)
209
+
210
+ // Should not call onQuantityChange with a value less than min
211
+ expect(onQuantityChange).not.toHaveBeenCalledWith(0)
212
+ expect(screen.getByText('1')).toBeInTheDocument()
213
+ })
214
+ })
215
+
216
+ describe('Disabled State', () => {
217
+ it('disables both buttons when disabled prop is true', async () => {
218
+ const onQuantityChange = vi.fn()
219
+ const user = userEvent.setup()
220
+
221
+ render(
222
+ <QuantitySelector
223
+ {...defaultProps}
224
+ quantity={5}
225
+ disabled
226
+ onQuantityChange={onQuantityChange}
227
+ />
228
+ )
229
+
230
+ const buttons = screen.getAllByRole('button')
231
+
232
+ await user.click(buttons[0]) // Try decrement
233
+ await user.click(buttons[1]) // Try increment
234
+
235
+ // onQuantityChange should still be called due to useEffect
236
+ // but the buttons shouldn't change the internal state
237
+ expect(screen.getByText('5')).toBeInTheDocument()
238
+ })
239
+ })
240
+
241
+ describe('Props Updates', () => {
242
+ it('updates quantity when prop changes', () => {
243
+ const {rerender} = render(
244
+ <QuantitySelector {...defaultProps} quantity={1} />
245
+ )
246
+
247
+ expect(screen.getByText('1')).toBeInTheDocument()
248
+
249
+ rerender(<QuantitySelector {...defaultProps} quantity={5} />)
250
+
251
+ expect(screen.getByText('5')).toBeInTheDocument()
252
+ })
253
+
254
+ it('calls onQuantityChange when quantity changes', async () => {
255
+ const onQuantityChange = vi.fn()
256
+
257
+ const {rerender} = render(
258
+ <QuantitySelector
259
+ {...defaultProps}
260
+ quantity={1}
261
+ onQuantityChange={onQuantityChange}
262
+ />
263
+ )
264
+
265
+ // Initial render calls onQuantityChange
266
+ expect(onQuantityChange).toHaveBeenCalledWith(1)
267
+
268
+ rerender(
269
+ <QuantitySelector
270
+ {...defaultProps}
271
+ quantity={3}
272
+ onQuantityChange={onQuantityChange}
273
+ />
274
+ )
275
+
276
+ await waitFor(() => {
277
+ expect(onQuantityChange).toHaveBeenCalledWith(3)
278
+ })
279
+ })
280
+ })
281
+
282
+ describe('Edge Cases', () => {
283
+ it('handles maxQuantity of 0', () => {
284
+ render(
285
+ <QuantitySelector
286
+ {...defaultProps}
287
+ quantity={0}
288
+ maxQuantity={0}
289
+ minQuantity={0}
290
+ />
291
+ )
292
+
293
+ expect(screen.getByText('0')).toBeInTheDocument()
294
+
295
+ // Both buttons should be disabled at 0
296
+ // Verify the quantity stays at 0
297
+ expect(screen.getByText('0')).toBeInTheDocument()
298
+ })
299
+
300
+ it('handles rapid clicking', async () => {
301
+ const onQuantityChange = vi.fn()
302
+ const user = userEvent.setup()
303
+
304
+ render(
305
+ <QuantitySelector
306
+ {...defaultProps}
307
+ quantity={5}
308
+ onQuantityChange={onQuantityChange}
309
+ />
310
+ )
311
+
312
+ const buttons = screen.getAllByRole('button')
313
+ const incrementButton = buttons[1]
314
+
315
+ // Rapid clicks
316
+ await user.click(incrementButton)
317
+ await user.click(incrementButton)
318
+ await user.click(incrementButton)
319
+
320
+ // Should handle all clicks properly
321
+ await waitFor(() => {
322
+ expect(onQuantityChange).toHaveBeenCalled()
323
+ })
324
+ })
325
+
326
+ it('maintains bounds when quantity prop exceeds maxQuantity', () => {
327
+ render(
328
+ <QuantitySelector {...defaultProps} quantity={15} maxQuantity={10} />
329
+ )
330
+
331
+ // Should display the exceeded value
332
+ expect(screen.getByText('15')).toBeInTheDocument()
333
+
334
+ // Increment should be disabled when exceeding max
335
+ // Verify the quantity is above max
336
+ expect(screen.getByText('15')).toBeInTheDocument()
337
+ })
338
+
339
+ it('maintains bounds when quantity prop is below minQuantity', () => {
340
+ render(
341
+ <QuantitySelector {...defaultProps} quantity={0} minQuantity={1} />
342
+ )
343
+
344
+ // Should display the value below minimum
345
+ expect(screen.getByText('0')).toBeInTheDocument()
346
+
347
+ // Decrement should be disabled when below min
348
+ // Verify the quantity is below min
349
+ expect(screen.getByText('0')).toBeInTheDocument()
350
+ })
351
+ })
352
+
353
+ describe('Visual Feedback', () => {
354
+ it('applies correct styles to container', () => {
355
+ render(<QuantitySelector {...defaultProps} />)
356
+
357
+ const container = screen.getByTestId('QuantitySelector')
358
+ expect(container).toHaveClass(
359
+ 'inline-flex',
360
+ 'bg-tertiary',
361
+ 'rounded-[8px]'
362
+ )
363
+ })
364
+
365
+ it('shows different icon styles for enabled/disabled states', () => {
366
+ const {rerender} = render(
367
+ <QuantitySelector {...defaultProps} quantity={1} minQuantity={1} />
368
+ )
369
+
370
+ // At minimum, quantity is 1
371
+ expect(screen.getByText('1')).toBeInTheDocument()
372
+
373
+ // Change quantity to enable decrement
374
+ rerender(
375
+ <QuantitySelector {...defaultProps} quantity={2} minQuantity={1} />
376
+ )
377
+
378
+ // Should now show quantity 2
379
+ expect(screen.getByText('2')).toBeInTheDocument()
380
+ })
381
+ })
382
+ })