@shopify/shop-minis-react 0.4.13 → 0.4.16
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 +3 -2
- package/dist/_virtual/index6.js.map +1 -1
- package/dist/components/atoms/product-variant-price.js +1 -1
- package/dist/components/atoms/product-variant-price.js.map +1 -1
- package/dist/components/commerce/product-card.js +1 -1
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/components/commerce/product-link.js +1 -1
- package/dist/components/commerce/product-link.js.map +1 -1
- package/dist/hooks/storage/useImageUpload.js +32 -24
- package/dist/hooks/storage/useImageUpload.js.map +1 -1
- package/dist/index.js +48 -46
- package/dist/index.js.map +1 -1
- package/dist/mocks.js +70 -50
- package/dist/mocks.js.map +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/formatMoney.js.map +1 -0
- package/eslint/rules/validate-manifest.cjs +91 -41
- package/package.json +2 -2
- package/src/components/atoms/product-variant-price.test.tsx +1 -1
- package/src/components/atoms/product-variant-price.tsx +1 -1
- package/src/components/commerce/product-card.test.tsx +1 -1
- package/src/components/commerce/product-card.tsx +1 -1
- package/src/components/commerce/product-link.test.tsx +1 -1
- package/src/components/commerce/product-link.tsx +1 -1
- package/src/hooks/storage/useImageUpload.ts +13 -0
- package/src/mocks.ts +48 -19
- package/src/utils/index.ts +1 -0
- package/dist/lib/formatMoney.js.map +0 -1
- /package/dist/{lib → utils}/formatMoney.js +0 -0
- /package/src/{lib → utils}/formatMoney.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatMoney.js","sources":["../../src/utils/formatMoney.ts"],"sourcesContent":["/**\n * Formats money amount with appropriate currency symbol\n * Uses browser's Intl.NumberFormat for consistent currency formatting\n */\nexport function formatMoney(amount: string, currencyCode: string): string {\n try {\n // Use en-US specifically for USD to get $ instead of US$\n // For other currencies, use browser locale but fallback to en-US\n const locale =\n currencyCode === 'USD' ? 'en-US' : navigator.language || 'en-US'\n\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: currencyCode,\n }).format(Number(amount))\n } catch (error) {\n // Fallback if currency code is invalid or not supported\n console.warn(`Invalid currency code: ${currencyCode}`, error)\n return `${currencyCode} ${amount}`\n }\n}\n"],"names":["formatMoney","amount","currencyCode","locale","error"],"mappings":"AAIgB,SAAAA,EAAYC,GAAgBC,GAA8B;AACpE,MAAA;AAGF,UAAMC,IACJD,MAAiB,QAAQ,UAAU,UAAU,YAAY;AAEpD,WAAA,IAAI,KAAK,aAAaC,GAAQ;AAAA,MACnC,OAAO;AAAA,MACP,UAAUD;AAAA,IACX,CAAA,EAAE,OAAO,OAAOD,CAAM,CAAC;AAAA,WACjBG,GAAO;AAEd,mBAAQ,KAAK,0BAA0BF,CAAY,IAAIE,CAAK,GACrD,GAAGF,CAAY,IAAID,CAAM;AAAA,EAAA;AAEpC;"}
|
|
@@ -31,8 +31,8 @@ const hookPermissionsMap = {
|
|
|
31
31
|
useImagePicker: ['CAMERA'],
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// Extract domain from URL (returns
|
|
35
|
-
function
|
|
34
|
+
// Extract domain and path from URL (returns {hostname, pathname} for CSP matching)
|
|
35
|
+
function extractDomainAndPath(url) {
|
|
36
36
|
if (!url || typeof url !== 'string') return null
|
|
37
37
|
|
|
38
38
|
try {
|
|
@@ -48,25 +48,65 @@ function extractDomain(url) {
|
|
|
48
48
|
|
|
49
49
|
// Parse URL
|
|
50
50
|
const urlObj = new URL(url)
|
|
51
|
-
return
|
|
51
|
+
return {
|
|
52
|
+
hostname: urlObj.hostname,
|
|
53
|
+
pathname: urlObj.pathname,
|
|
54
|
+
}
|
|
52
55
|
} catch (err) {
|
|
53
56
|
// If URL parsing fails, might be a relative path
|
|
54
57
|
return null
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
// Check if
|
|
59
|
-
|
|
61
|
+
// Check if a URL matches a trusted_domain pattern
|
|
62
|
+
// Supports: wildcards (*.example.com), paths with CSP semantics
|
|
63
|
+
// CSP path matching: path with trailing slash matches directory, without matches exact or prefix
|
|
64
|
+
function urlMatchesPattern(urlInfo, pattern) {
|
|
60
65
|
if (pattern === '*') return true
|
|
61
|
-
if (pattern === domain) return true
|
|
62
66
|
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
// Parse the pattern to extract hostname and optional path
|
|
68
|
+
let patternHostname = pattern
|
|
69
|
+
let patternPath = null
|
|
70
|
+
|
|
71
|
+
// Check if pattern contains a path (has / after the domain)
|
|
72
|
+
const slashIndex = pattern.indexOf('/')
|
|
73
|
+
if (slashIndex !== -1) {
|
|
74
|
+
patternHostname = pattern.slice(0, slashIndex)
|
|
75
|
+
patternPath = pattern.slice(slashIndex)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// First check hostname match
|
|
79
|
+
let hostnameMatches = false
|
|
80
|
+
|
|
81
|
+
if (patternHostname === urlInfo.hostname) {
|
|
82
|
+
hostnameMatches = true
|
|
83
|
+
} else if (patternHostname.startsWith('*.')) {
|
|
84
|
+
// Handle wildcard subdomains (*.example.com)
|
|
85
|
+
const basePattern = patternHostname.slice(2)
|
|
86
|
+
hostnameMatches =
|
|
87
|
+
urlInfo.hostname === basePattern ||
|
|
88
|
+
urlInfo.hostname.endsWith(`.${basePattern}`)
|
|
67
89
|
}
|
|
68
90
|
|
|
69
|
-
|
|
91
|
+
if (!hostnameMatches) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// If no path in pattern, hostname match is sufficient
|
|
96
|
+
if (!patternPath) {
|
|
97
|
+
return true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// CSP path matching semantics:
|
|
101
|
+
// - Path ending with / matches that directory and everything under it (prefix match)
|
|
102
|
+
// - Path without trailing / matches exact path only
|
|
103
|
+
if (patternPath.endsWith('/')) {
|
|
104
|
+
// Directory match: URL path must start with pattern path
|
|
105
|
+
return urlInfo.pathname.startsWith(patternPath)
|
|
106
|
+
} else {
|
|
107
|
+
// Exact match only
|
|
108
|
+
return urlInfo.pathname === patternPath
|
|
109
|
+
}
|
|
70
110
|
}
|
|
71
111
|
|
|
72
112
|
module.exports = {
|
|
@@ -308,10 +348,11 @@ module.exports = {
|
|
|
308
348
|
// Check for fetch() calls
|
|
309
349
|
if (node.callee.name === 'fetch' && firstArg.type === 'Literal') {
|
|
310
350
|
const url = firstArg.value
|
|
311
|
-
const
|
|
312
|
-
if (
|
|
351
|
+
const urlInfo = extractDomainAndPath(url)
|
|
352
|
+
if (urlInfo) {
|
|
313
353
|
requiredDomains.add({
|
|
314
|
-
|
|
354
|
+
urlInfo,
|
|
355
|
+
url,
|
|
315
356
|
reason: 'fetch() call',
|
|
316
357
|
node,
|
|
317
358
|
})
|
|
@@ -326,10 +367,11 @@ module.exports = {
|
|
|
326
367
|
node.arguments[1].type === 'Literal'
|
|
327
368
|
) {
|
|
328
369
|
const url = node.arguments[1].value
|
|
329
|
-
const
|
|
330
|
-
if (
|
|
370
|
+
const urlInfo = extractDomainAndPath(url)
|
|
371
|
+
if (urlInfo) {
|
|
331
372
|
requiredDomains.add({
|
|
332
|
-
|
|
373
|
+
urlInfo,
|
|
374
|
+
url,
|
|
333
375
|
reason: 'XMLHttpRequest.open() call',
|
|
334
376
|
node,
|
|
335
377
|
})
|
|
@@ -340,11 +382,12 @@ module.exports = {
|
|
|
340
382
|
if (node.callee.name === 'WebSocket' && firstArg.type === 'Literal') {
|
|
341
383
|
const url = firstArg.value
|
|
342
384
|
// WebSocket URLs use ws:// or wss://
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
if (
|
|
385
|
+
const normalizedUrl = url.replace(/^wss?:\/\//, 'https://')
|
|
386
|
+
const urlInfo = extractDomainAndPath(normalizedUrl)
|
|
387
|
+
if (urlInfo) {
|
|
346
388
|
requiredDomains.add({
|
|
347
|
-
|
|
389
|
+
urlInfo,
|
|
390
|
+
url,
|
|
348
391
|
reason: 'WebSocket connection',
|
|
349
392
|
node,
|
|
350
393
|
})
|
|
@@ -354,10 +397,11 @@ module.exports = {
|
|
|
354
397
|
// Check for EventSource
|
|
355
398
|
if (node.callee.name === 'EventSource' && firstArg.type === 'Literal') {
|
|
356
399
|
const url = firstArg.value
|
|
357
|
-
const
|
|
358
|
-
if (
|
|
400
|
+
const urlInfo = extractDomainAndPath(url)
|
|
401
|
+
if (urlInfo) {
|
|
359
402
|
requiredDomains.add({
|
|
360
|
-
|
|
403
|
+
urlInfo,
|
|
404
|
+
url,
|
|
361
405
|
reason: 'EventSource connection',
|
|
362
406
|
node,
|
|
363
407
|
})
|
|
@@ -371,10 +415,11 @@ module.exports = {
|
|
|
371
415
|
firstArg.type === 'Literal'
|
|
372
416
|
) {
|
|
373
417
|
const url = firstArg.value
|
|
374
|
-
const
|
|
375
|
-
if (
|
|
418
|
+
const urlInfo = extractDomainAndPath(url)
|
|
419
|
+
if (urlInfo) {
|
|
376
420
|
requiredDomains.add({
|
|
377
|
-
|
|
421
|
+
urlInfo,
|
|
422
|
+
url,
|
|
378
423
|
reason: 'navigator.sendBeacon() call',
|
|
379
424
|
node,
|
|
380
425
|
})
|
|
@@ -389,10 +434,11 @@ module.exports = {
|
|
|
389
434
|
firstArg.type === 'Literal'
|
|
390
435
|
) {
|
|
391
436
|
const url = firstArg.value
|
|
392
|
-
const
|
|
393
|
-
if (
|
|
437
|
+
const urlInfo = extractDomainAndPath(url)
|
|
438
|
+
if (urlInfo) {
|
|
394
439
|
requiredDomains.add({
|
|
395
|
-
|
|
440
|
+
urlInfo,
|
|
441
|
+
url,
|
|
396
442
|
reason: 'window.open() call',
|
|
397
443
|
node,
|
|
398
444
|
})
|
|
@@ -499,9 +545,9 @@ module.exports = {
|
|
|
499
545
|
|
|
500
546
|
const attrName = node.name.name
|
|
501
547
|
const url = node.value.value
|
|
502
|
-
const
|
|
548
|
+
const urlInfo = extractDomainAndPath(url)
|
|
503
549
|
|
|
504
|
-
if (!
|
|
550
|
+
if (!urlInfo) {
|
|
505
551
|
return
|
|
506
552
|
}
|
|
507
553
|
|
|
@@ -522,7 +568,8 @@ module.exports = {
|
|
|
522
568
|
]
|
|
523
569
|
if (supportedElements.includes(elementName)) {
|
|
524
570
|
requiredDomains.add({
|
|
525
|
-
|
|
571
|
+
urlInfo,
|
|
572
|
+
url,
|
|
526
573
|
reason: `<${elementName}> src attribute`,
|
|
527
574
|
node,
|
|
528
575
|
})
|
|
@@ -531,7 +578,8 @@ module.exports = {
|
|
|
531
578
|
// poster attribute (video)
|
|
532
579
|
else if (attrName === 'poster') {
|
|
533
580
|
requiredDomains.add({
|
|
534
|
-
|
|
581
|
+
urlInfo,
|
|
582
|
+
url,
|
|
535
583
|
reason: `<${elementName}> poster attribute`,
|
|
536
584
|
node,
|
|
537
585
|
})
|
|
@@ -539,7 +587,8 @@ module.exports = {
|
|
|
539
587
|
// data attribute (object)
|
|
540
588
|
else if (attrName === 'data') {
|
|
541
589
|
requiredDomains.add({
|
|
542
|
-
|
|
590
|
+
urlInfo,
|
|
591
|
+
url,
|
|
543
592
|
reason: `<${elementName}> data attribute`,
|
|
544
593
|
node,
|
|
545
594
|
})
|
|
@@ -547,7 +596,8 @@ module.exports = {
|
|
|
547
596
|
// action attribute (form)
|
|
548
597
|
else if (attrName === 'action') {
|
|
549
598
|
requiredDomains.add({
|
|
550
|
-
|
|
599
|
+
urlInfo,
|
|
600
|
+
url,
|
|
551
601
|
reason: `<${elementName}> action attribute`,
|
|
552
602
|
node,
|
|
553
603
|
})
|
|
@@ -692,26 +742,26 @@ module.exports = {
|
|
|
692
742
|
})
|
|
693
743
|
|
|
694
744
|
// Check trusted domains
|
|
695
|
-
requiredDomains.forEach(({
|
|
745
|
+
requiredDomains.forEach(({urlInfo, url, reason, node}) => {
|
|
696
746
|
if (!manifest) {
|
|
697
747
|
issues.push({
|
|
698
748
|
messageId: 'manifestNotFound',
|
|
699
|
-
data: {reason, domain},
|
|
749
|
+
data: {reason, domain: urlInfo.hostname},
|
|
700
750
|
})
|
|
701
751
|
return
|
|
702
752
|
}
|
|
703
753
|
|
|
704
754
|
const trustedDomains = manifest.trusted_domains || []
|
|
705
755
|
|
|
706
|
-
// Check if
|
|
756
|
+
// Check if URL is trusted (supports wildcards and paths)
|
|
707
757
|
const isTrusted = trustedDomains.some(pattern =>
|
|
708
|
-
|
|
758
|
+
urlMatchesPattern(urlInfo, pattern)
|
|
709
759
|
)
|
|
710
760
|
|
|
711
761
|
if (!isTrusted) {
|
|
712
762
|
issues.push({
|
|
713
763
|
type: 'domain',
|
|
714
|
-
domain,
|
|
764
|
+
domain: urlInfo.hostname,
|
|
715
765
|
reason,
|
|
716
766
|
node,
|
|
717
767
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopify/shop-minis-react",
|
|
3
3
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.16",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typescript": ">=5.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@shopify/shop-minis-platform": "0.8.
|
|
49
|
+
"@shopify/shop-minis-platform": "0.8.3",
|
|
50
50
|
"@tailwindcss/vite": "4.1.8",
|
|
51
51
|
"@tanstack/react-query": "5.86.0",
|
|
52
52
|
"@types/color": "3.0.6",
|
|
@@ -5,7 +5,7 @@ import {render, screen} from '../../test-utils'
|
|
|
5
5
|
import {ProductVariantPrice} from './product-variant-price'
|
|
6
6
|
|
|
7
7
|
// Mock formatMoney function
|
|
8
|
-
vi.mock('../../
|
|
8
|
+
vi.mock('../../utils/formatMoney', () => ({
|
|
9
9
|
formatMoney: vi.fn((amount: string, currencyCode: string) => {
|
|
10
10
|
const numAmount = parseFloat(amount)
|
|
11
11
|
return currencyCode === 'USD'
|
|
@@ -40,7 +40,7 @@ vi.mock('../../hooks/user/useSavedProductsActions', () => ({
|
|
|
40
40
|
}))
|
|
41
41
|
|
|
42
42
|
// Mock formatMoney
|
|
43
|
-
vi.mock('../../
|
|
43
|
+
vi.mock('../../utils/formatMoney', () => ({
|
|
44
44
|
formatMoney: vi.fn((amount: string, currencyCode: string) => {
|
|
45
45
|
const numAmount = parseFloat(amount)
|
|
46
46
|
return currencyCode === 'USD'
|
|
@@ -6,8 +6,8 @@ import {type Product, type ProductVariant} from '@shopify/shop-minis-platform'
|
|
|
6
6
|
import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
|
|
7
7
|
import {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'
|
|
8
8
|
import {ProductReviewStars} from '../../internal/components/product-review-stars'
|
|
9
|
-
import {formatMoney} from '../../lib/formatMoney'
|
|
10
9
|
import {cn} from '../../lib/utils'
|
|
10
|
+
import {formatMoney} from '../../utils/formatMoney'
|
|
11
11
|
import {Image} from '../atoms/image'
|
|
12
12
|
import {ProductVariantPrice} from '../atoms/product-variant-price'
|
|
13
13
|
import {Touchable} from '../atoms/touchable'
|
|
@@ -28,7 +28,7 @@ vi.mock('../../hooks/user/useSavedProductsActions', () => ({
|
|
|
28
28
|
}))
|
|
29
29
|
|
|
30
30
|
// Mock formatMoney
|
|
31
|
-
vi.mock('../../
|
|
31
|
+
vi.mock('../../utils/formatMoney', () => ({
|
|
32
32
|
formatMoney: vi.fn((amount: string, currencyCode: string) => {
|
|
33
33
|
const numAmount = parseFloat(amount)
|
|
34
34
|
return currencyCode === 'USD'
|
|
@@ -7,8 +7,8 @@ import {Slot as SlotPrimitive} from 'radix-ui'
|
|
|
7
7
|
import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
|
|
8
8
|
import {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'
|
|
9
9
|
import {ProductReviewStars} from '../../internal/components/product-review-stars'
|
|
10
|
-
import {formatMoney} from '../../lib/formatMoney'
|
|
11
10
|
import {cn} from '../../lib/utils'
|
|
11
|
+
import {formatMoney} from '../../utils/formatMoney'
|
|
12
12
|
import {Touchable} from '../atoms/touchable'
|
|
13
13
|
import {Card, CardContent, CardAction} from '../ui/card'
|
|
14
14
|
|
|
@@ -107,6 +107,19 @@ export const useImageUpload = (): UseImageUploadReturns => {
|
|
|
107
107
|
throw new Error(links.error.message)
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
if (links.mocked) {
|
|
111
|
+
// Skip upload and return mock data
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
id: 'uploaded-image-id',
|
|
115
|
+
imageUrl:
|
|
116
|
+
'https://cdn.shopify.com/s/files/1/0621/0463/3599/files/Mr._Bean_2007_800x800.jpg?v=1763126175',
|
|
117
|
+
resourceUrl:
|
|
118
|
+
'https://cdn.shopify.com/s/files/1/0621/0463/3599/files/Mr._Bean_2007_800x800.jpg?v=1763126175',
|
|
119
|
+
},
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
|
|
110
123
|
// Upload single file to GCS
|
|
111
124
|
const {error: uploadError} = await uploadFileToGCS(
|
|
112
125
|
processedImageParams,
|
package/src/mocks.ts
CHANGED
|
@@ -6,28 +6,50 @@ import {
|
|
|
6
6
|
} from '@shopify/shop-minis-platform'
|
|
7
7
|
import {ShopActions} from '@shopify/shop-minis-platform/actions'
|
|
8
8
|
|
|
9
|
+
const SAMPLE_IMAGE_NAMES = [
|
|
10
|
+
'garnished.jpeg',
|
|
11
|
+
'bath.jpeg',
|
|
12
|
+
'teapot.jpg',
|
|
13
|
+
'shoes.jpeg',
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
// Simple hash function to get a deterministic index from a string
|
|
17
|
+
const hashString = (str: string): number => {
|
|
18
|
+
let hash = 0
|
|
19
|
+
for (let i = 0; i < str.length; i++) {
|
|
20
|
+
hash = (hash << 5) - hash + str.charCodeAt(i)
|
|
21
|
+
hash |= 0
|
|
22
|
+
}
|
|
23
|
+
return Math.abs(hash)
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
// Helper functions for common data structures
|
|
10
27
|
export const createProduct = (
|
|
11
28
|
id: string,
|
|
12
29
|
title: string,
|
|
13
30
|
price = '99.99',
|
|
14
31
|
compareAtPrice?: string
|
|
15
|
-
): Product =>
|
|
16
|
-
id
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
): Product => {
|
|
33
|
+
const imageIndex = hashString(id) % SAMPLE_IMAGE_NAMES.length
|
|
34
|
+
const imageName = SAMPLE_IMAGE_NAMES[imageIndex]
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
id,
|
|
38
|
+
title,
|
|
39
|
+
price: {amount: price, currencyCode: 'USD'},
|
|
40
|
+
...(compareAtPrice && {
|
|
41
|
+
compareAtPrice: {amount: compareAtPrice, currencyCode: 'USD'},
|
|
42
|
+
}),
|
|
43
|
+
reviewAnalytics: {averageRating: 4.5, reviewCount: 10},
|
|
44
|
+
shop: createShop('shop1', 'Mock Shop'),
|
|
45
|
+
defaultVariantId: `variant-${id}`,
|
|
46
|
+
isFavorited: false,
|
|
47
|
+
featuredImage: {
|
|
48
|
+
url: `https://cdn.shopify.com/static/sample-images/${imageName}`,
|
|
49
|
+
altText: title,
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
|
31
53
|
|
|
32
54
|
export const createShop = (
|
|
33
55
|
id: string,
|
|
@@ -202,7 +224,11 @@ function makeMockMethod<K extends keyof ShopActions>(
|
|
|
202
224
|
): ShopActions[K] {
|
|
203
225
|
return ((params: Parameters<ShopActions[K]>[0]) => {
|
|
204
226
|
logMockAction(String(key), params)
|
|
205
|
-
return Promise.resolve({
|
|
227
|
+
return Promise.resolve({
|
|
228
|
+
ok: true as const,
|
|
229
|
+
data: result,
|
|
230
|
+
mocked: true,
|
|
231
|
+
})
|
|
206
232
|
}) as ShopActions[K]
|
|
207
233
|
}
|
|
208
234
|
|
|
@@ -249,6 +275,7 @@ export function makeMockActions(): ShopActions {
|
|
|
249
275
|
navigateToOrder: undefined,
|
|
250
276
|
navigateToCheckout: undefined,
|
|
251
277
|
createImageUploadLink: {
|
|
278
|
+
// This action is mocked in the actual hook. See `useImageUpload` for more details.
|
|
252
279
|
targets: [
|
|
253
280
|
{
|
|
254
281
|
url: 'https://example.com/upload',
|
|
@@ -262,7 +289,9 @@ export function makeMockActions(): ShopActions {
|
|
|
262
289
|
{
|
|
263
290
|
id: 'file-123',
|
|
264
291
|
fileStatus: 'READY',
|
|
265
|
-
image: {
|
|
292
|
+
image: {
|
|
293
|
+
url: 'https://example.com/image.jpg',
|
|
294
|
+
},
|
|
266
295
|
},
|
|
267
296
|
],
|
|
268
297
|
},
|
|
@@ -556,7 +585,7 @@ export const injectMocks = ({force}: {force?: boolean} = {}) => {
|
|
|
556
585
|
window.minisSDK = makeMockActions()
|
|
557
586
|
window.minisParams = {
|
|
558
587
|
handle: 'mock-handle',
|
|
559
|
-
initialUrl: '/
|
|
588
|
+
initialUrl: '/',
|
|
560
589
|
platform: 'web',
|
|
561
590
|
}
|
|
562
591
|
}
|
package/src/utils/index.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"formatMoney.js","sources":["../../src/lib/formatMoney.ts"],"sourcesContent":["/**\n * Formats money amount with appropriate currency symbol\n * Uses browser's Intl.NumberFormat for consistent currency formatting\n */\nexport function formatMoney(amount: string, currencyCode: string): string {\n try {\n // Use en-US specifically for USD to get $ instead of US$\n // For other currencies, use browser locale but fallback to en-US\n const locale =\n currencyCode === 'USD' ? 'en-US' : navigator.language || 'en-US'\n\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: currencyCode,\n }).format(Number(amount))\n } catch (error) {\n // Fallback if currency code is invalid or not supported\n console.warn(`Invalid currency code: ${currencyCode}`, error)\n return `${currencyCode} ${amount}`\n }\n}\n"],"names":["formatMoney","amount","currencyCode","locale","error"],"mappings":"AAIgB,SAAAA,EAAYC,GAAgBC,GAA8B;AACpE,MAAA;AAGF,UAAMC,IACJD,MAAiB,QAAQ,UAAU,UAAU,YAAY;AAEpD,WAAA,IAAI,KAAK,aAAaC,GAAQ;AAAA,MACnC,OAAO;AAAA,MACP,UAAUD;AAAA,IACX,CAAA,EAAE,OAAO,OAAOD,CAAM,CAAC;AAAA,WACjBG,GAAO;AAEd,mBAAQ,KAAK,0BAA0BF,CAAY,IAAIE,CAAK,GACrD,GAAGF,CAAY,IAAID,CAAM;AAAA,EAAA;AAEpC;"}
|
|
File without changes
|
|
File without changes
|