Skip to content

Commit beabe8a

Browse files
vcua-mobifynayanavishwaamittapallidannyphan2000
authored
@W-21566314@ - Bump version for 3.17.1-preview.0 (#3749)
* W-2129587: removed wrapperStyle, add position fixed for LoadingSpinner to have full screen overlay * bump version to preview.1 * @W-21414441@ Bugfix - Fix adding to cart from a master product in the wishlist (#3732) * Fix add to cart bug and add e2e test * Update CHANGELOG.md * W-21432256 fix redirect payment methods status value during preview t… (#3734) * W-21432256 fix redirect payment methods status value during preview testing * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration (#3741) * @W-21551291: [1CC][PWA Kit] Custom billing address lost after OTP registration Signed-off-by: d.phan <d.phan@salesforce.com> * add changelog Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> * lint fix Signed-off-by: d.phan <d.phan@salesforce.com> --------- Signed-off-by: d.phan <d.phan@salesforce.com> * clean up changelog * bump versions * Add missing overrides to extensible app (#3745) * Add missing overrides to extensible app * Update CHANGELOG.md * Bump versions * @W-20784635@ Productize base paths (#3614) * @W-20784722@ Move envBasePath into ssrParameters (#3590) * Move envBasePath into ssrParameters * Apply MRT restrictions on supported base paths * Update changelogs * one more changelog * Update express.test.js * Remove whitespace trimming * Remove test base path * Add comment * @W-20784635@ Support adding base paths to shopper facing URLs (#3615) * Add base path to react router routes * Handle basename for urls that bypass react router * Add a feature toggle for app base names * Add tests * Update config templates * Rename getBasename to getRouterBasePath * Update changelogs * Test fixes * Remove unused import and cleanup comments * Apply suggestions * Clean up redundant code * Fix issues from merge conflict * Update CHANGELOG.md * [Storefront Preview] Address base path duplication for manually entered urls (#3666) * Address base path duplication * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Apply suggestions * Fix unwanted basePath removal if base path is a substring * Lint * Add console warn * Update refresh component to avoid duplicate base paths * Fix merge issue * Add tests for coverage * Squash changelogs * Remove warning --------- Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> * Bump version to 3.17.1-preview.0 * Update CHANGELOG.md --------- Signed-off-by: nayanavishwa <nayana.vishwa@salesforce.com> Signed-off-by: d.phan <d.phan@salesforce.com> Signed-off-by: vcua-mobify <47404250+vcua-mobify@users.noreply.github.com> Co-authored-by: nayanavishwa <nayana.vishwa@salesforce.com> Co-authored-by: amittapalli <amittapalli@yahoo.com> Co-authored-by: Danny Phan <125327707+dannyphan2000@users.noreply.github.com>
1 parent 6d0cfd2 commit beabe8a

File tree

62 files changed

+811
-215
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+811
-215
lines changed

e2e/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

e2e/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "e2e-scripts",
3-
"version": "3.17.0",
3+
"version": "3.17.1-preview.0",
44
"description": "E2E test scripts for PWA Kit",
55
"main": "index.js",
66
"private": true,
@@ -15,7 +15,7 @@
1515
"@actions/core": "^1.11.1",
1616
"@aws-sdk/client-s3": "3.450.0",
1717
"@aws-sdk/client-sts": "3.450.0",
18-
"@salesforce/pwa-kit-dev": "3.17.0",
18+
"@salesforce/pwa-kit-dev": "3.17.1-preview.0",
1919
"jest": "^29.7.0"
2020
},
2121
"jest": {

lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "3.17.0",
2+
"version": "3.17.1-preview.0",
33
"packages": [
44
"packages/*",
55
"e2e"

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pwa-kit",
3-
"version": "3.17.0",
3+
"version": "3.17.1-preview.0",
44
"scripts": {
55
"bump-version": "node ./scripts/bump-version/index.js",
66
"bump-version:retail-react-app": "node ./scripts/bump-version/index.js --package=@salesforce/retail-react-app",

packages/commerce-sdk-react/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## v5.1.1-preview.0 (Mar 13, 2026)
2+
- Update storefront preview to support base paths [#3614](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3614)
3+
14
## v5.1.0 (Mar 12, 2026)
25
- Add Page Designer Support [#3727](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3727)
36
- Bump commerce-sdk-isomorphic to 5.1.0 [#3725](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3725)

packages/commerce-sdk-react/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/commerce-sdk-react/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@salesforce/commerce-sdk-react",
3-
"version": "5.1.0",
3+
"version": "5.1.1-preview.0",
44
"description": "A library that provides react hooks for fetching data from Commerce Cloud",
55
"homepage": "https://github.com/SalesforceCommerceCloud/pwa-kit/tree/develop/packages/ecom-react-hooks#readme",
66
"bugs": {
@@ -46,7 +46,7 @@
4646
"jwt-decode": "^4.0.0"
4747
},
4848
"devDependencies": {
49-
"@salesforce/pwa-kit-dev": "3.17.0",
49+
"@salesforce/pwa-kit-dev": "3.17.1-preview.0",
5050
"@tanstack/react-query": "^4.28.0",
5151
"@testing-library/jest-dom": "^5.16.5",
5252
"@testing-library/react": "^14.0.0",
@@ -61,7 +61,7 @@
6161
"@types/react-helmet": "~6.1.6",
6262
"@types/react-router-dom": "~5.3.3",
6363
"cross-env": "^5.2.1",
64-
"internal-lib-build": "3.17.0",
64+
"internal-lib-build": "3.17.1-preview.0",
6565
"jsonwebtoken": "^9.0.0",
6666
"nock": "^13.3.0",
6767
"nodemon": "^2.0.22",

packages/commerce-sdk-react/src/components/StorefrontPreview/storefront-preview.test.tsx

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,16 @@ import {useCommerceApi, useConfig} from '../../hooks'
1414

1515
declare global {
1616
interface Window {
17-
STOREFRONT_PREVIEW?: Record<string, unknown>
17+
STOREFRONT_PREVIEW?: {
18+
getToken?: () => string | undefined | Promise<string | undefined>
19+
onContextChange?: () => void | Promise<void>
20+
siteId?: string
21+
experimentalUnsafeNavigate?: (
22+
path: string | {pathname: string; search?: string; hash?: string; state?: unknown},
23+
action?: 'push' | 'replace',
24+
...args: unknown[]
25+
) => void
26+
}
1827
}
1928
}
2029

@@ -28,9 +37,24 @@ jest.mock('./utils', () => {
2837
jest.mock('../../auth/index.ts')
2938
jest.mock('../../hooks/useConfig', () => jest.fn())
3039

40+
const mockPush = jest.fn()
41+
const mockReplace = jest.fn()
42+
jest.mock('react-router-dom', () => {
43+
const actual = jest.requireActual('react-router-dom')
44+
return {
45+
...actual,
46+
useHistory: () => ({
47+
push: mockPush,
48+
replace: mockReplace
49+
})
50+
}
51+
})
52+
3153
describe('Storefront Preview Component', function () {
3254
beforeEach(() => {
3355
delete window.STOREFRONT_PREVIEW
56+
mockPush.mockClear()
57+
mockReplace.mockClear()
3458
;(useConfig as jest.Mock).mockReturnValue({siteId: 'site-id'})
3559
})
3660
afterEach(() => {
@@ -107,6 +131,88 @@ describe('Storefront Preview Component', function () {
107131
expect(window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate).toBeDefined()
108132
})
109133

134+
test('experimentalUnsafeNavigate removes base path from path when getBasePath is provided', () => {
135+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
136+
137+
render(
138+
<StorefrontPreview
139+
enabled={true}
140+
getToken={() => 'my-token'}
141+
getBasePath={() => '/mybase'}
142+
/>
143+
)
144+
145+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.('/mybase/product/123', 'push')
146+
expect(mockPush).toHaveBeenCalledWith('/product/123')
147+
148+
mockPush.mockClear()
149+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.('/mybase/account', 'replace')
150+
expect(mockReplace).toHaveBeenCalledWith('/account')
151+
})
152+
153+
test('experimentalUnsafeNavigate does not remove when path does not start with base path', () => {
154+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
155+
156+
render(
157+
<StorefrontPreview
158+
enabled={true}
159+
getToken={() => 'my-token'}
160+
getBasePath={() => '/mybase'}
161+
/>
162+
)
163+
164+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.('/other/product/123', 'push')
165+
expect(mockPush).toHaveBeenCalledWith('/other/product/123')
166+
})
167+
168+
test('experimentalUnsafeNavigate does not strip when path has basePath only as substring (e.g. /shop vs /shopping/cart)', () => {
169+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
170+
171+
render(
172+
<StorefrontPreview
173+
enabled={true}
174+
getToken={() => 'my-token'}
175+
getBasePath={() => '/shop'}
176+
/>
177+
)
178+
179+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.('/shopping/cart', 'push')
180+
expect(mockPush).toHaveBeenCalledWith('/shopping/cart')
181+
})
182+
183+
test('experimentalUnsafeNavigate strips to / when path exactly equals basePath', () => {
184+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
185+
186+
render(
187+
<StorefrontPreview
188+
enabled={true}
189+
getToken={() => 'my-token'}
190+
getBasePath={() => '/mybase'}
191+
/>
192+
)
193+
194+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.('/mybase', 'push')
195+
expect(mockPush).toHaveBeenCalledWith('/')
196+
})
197+
198+
test('experimentalUnsafeNavigate removes base path from location object when getBasePath is provided', () => {
199+
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
200+
201+
render(
202+
<StorefrontPreview
203+
enabled={true}
204+
getToken={() => 'my-token'}
205+
getBasePath={() => '/mybase'}
206+
/>
207+
)
208+
209+
window.STOREFRONT_PREVIEW?.experimentalUnsafeNavigate?.(
210+
{pathname: '/mybase/product/123', search: '?q=1'},
211+
'push'
212+
)
213+
expect(mockPush).toHaveBeenCalledWith({pathname: '/product/123', search: '?q=1'})
214+
})
215+
110216
test('cache breaker is added to the parameters of SCAPI requests, only if in storefront preview', () => {
111217
;(detectStorefrontPreview as jest.Mock).mockReturnValue(true)
112218
mockQueryEndpoint('baskets/123', {})

packages/commerce-sdk-react/src/components/StorefrontPreview/storefront-preview.tsx

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,58 @@ type GetToken = () => string | undefined | Promise<string | undefined>
1717
type ContextChangeHandler = () => void | Promise<void>
1818
type OptionalWhenDisabled<T> = ({enabled?: true} & T) | ({enabled: false} & Partial<T>)
1919

20+
/**
21+
* Remove the base path from a path string.
22+
* Only strips when path equals basePath or path starts with basePath + '/'.
23+
*/
24+
function removeBasePathFromPath(path: string, basePath: string): string {
25+
const matches =
26+
path.startsWith(basePath + '/') || path === basePath
27+
return matches ? path.slice(basePath.length) || '/' : path
28+
}
29+
30+
/**
31+
* Strip the base path from a path
32+
*
33+
* React Router history re-adds the base path to the path, so we
34+
* remove it here to avoid base path duplication.
35+
*/
36+
function removeBasePathFromLocation<T>(
37+
pathOrLocation: LocationDescriptor<T>,
38+
basePath: string
39+
): LocationDescriptor<T> {
40+
if (!basePath) return pathOrLocation
41+
if (typeof pathOrLocation === 'string') {
42+
return removeBasePathFromPath(pathOrLocation, basePath) as LocationDescriptor<T>
43+
}
44+
const pathname = pathOrLocation.pathname ?? '/'
45+
return {
46+
...pathOrLocation,
47+
pathname: removeBasePathFromPath(pathname, basePath)
48+
}
49+
}
50+
2051
/**
2152
*
2253
* @param enabled - flag to turn on/off Storefront Preview feature. By default, it is set to true.
2354
* This flag only applies if storefront is running in a Runtime Admin iframe.
2455
* @param getToken - A method that returns the access token for the current user
56+
* @param getBasePath - A method that returns the router base path of the app. Requird if using
57+
* base path for router routes (showBasePath is true in url config).
2558
*/
2659
export const StorefrontPreview = ({
2760
children,
2861
enabled = true,
2962
getToken,
30-
onContextChange
63+
onContextChange,
64+
getBasePath
3165
}: React.PropsWithChildren<
3266
// Props are only required when Storefront Preview is enabled
33-
OptionalWhenDisabled<{getToken: GetToken; onContextChange?: ContextChangeHandler}>
67+
OptionalWhenDisabled<{
68+
getToken: GetToken
69+
onContextChange?: ContextChangeHandler
70+
getBasePath?: () => string
71+
}>
3472
>) => {
3573
const history = useHistory()
3674
const isHostTrusted = detectStorefrontPreview()
@@ -49,11 +87,13 @@ export const StorefrontPreview = ({
4987
action: 'push' | 'replace' = 'push',
5088
...args: unknown[]
5189
) => {
52-
history[action](path, ...args)
90+
const basePath = getBasePath?.() ?? ''
91+
const pathWithoutBase = removeBasePathFromLocation(path, basePath)
92+
history[action](pathWithoutBase, ...args)
5393
}
5494
}
5595
}
56-
}, [enabled, getToken, onContextChange, siteId])
96+
}, [enabled, getToken, onContextChange, siteId, getBasePath])
5797

5898
useEffect(() => {
5999
if (enabled && isHostTrusted) {
@@ -99,7 +139,8 @@ StorefrontPreview.propTypes = {
99139
// to get to a place where both these props are simply optional and we will provide default implementations.
100140
// This would make the API simpler to use.
101141
getToken: CustomPropTypes.requiredFunctionWhenEnabled,
102-
onContextChange: PropTypes.func
142+
onContextChange: PropTypes.func,
143+
getBasePath: PropTypes.func
103144
}
104145

105146
export default StorefrontPreview

0 commit comments

Comments
 (0)