diff --git a/packages/template-retail-react-app/app/components/_app-config/index.jsx b/packages/template-retail-react-app/app/components/_app-config/index.jsx
index 0f018b613c..7bfd0a8cd7 100644
--- a/packages/template-retail-react-app/app/components/_app-config/index.jsx
+++ b/packages/template-retail-react-app/app/components/_app-config/index.jsx
@@ -19,9 +19,11 @@ import {
CustomerProductListsProvider,
CustomerProvider
} from '../../commerce-api/contexts'
+import {MultiSiteProvider} from '../../contexts'
import {resolveSiteFromUrl} from '../../utils/site-utils'
import {resolveLocaleFromUrl} from '../../utils/utils'
import {getConfig} from 'pwa-kit-runtime/utils/ssr-config'
+import {createUrlTemplate} from '../../utils/url'
/**
* Use the AppConfig component to inject extra arguments into the getProps
@@ -36,15 +38,17 @@ const AppConfig = ({children, locals = {}}) => {
const [customer, setCustomer] = useState(null)
return (
-
-
-
-
- {children}
-
-
-
-
+
+
+
+
+
+ {children}
+
+
+
+
+
)
}
@@ -66,13 +70,19 @@ AppConfig.restore = (locals = {}) => {
apiConfig.parameters.siteId = site.id
locals.api = new CommerceAPI({...apiConfig, locale: locale.id, currency})
+ locals.buildUrl = createUrlTemplate(appConfig, site.alias || site.id, locale.id)
+ locals.site = site
+ locals.locale = locale.id
}
AppConfig.freeze = () => undefined
AppConfig.extraGetPropsArgs = (locals = {}) => {
return {
- api: locals.api
+ api: locals.api,
+ buildUrl: locals.buildUrl,
+ site: locals.site,
+ locale: locals.locale
}
}
diff --git a/packages/template-retail-react-app/app/components/_app/index.jsx b/packages/template-retail-react-app/app/components/_app/index.jsx
index 24d14d7282..2fdf569943 100644
--- a/packages/template-retail-react-app/app/components/_app/index.jsx
+++ b/packages/template-retail-react-app/app/components/_app/index.jsx
@@ -35,8 +35,6 @@ import useShopper from '../../commerce-api/hooks/useShopper'
import useCustomer from '../../commerce-api/hooks/useCustomer'
import {AuthModal, useAuthModal} from '../../hooks/use-auth-modal'
import {AddToCartModalProvider} from '../../hooks/use-add-to-cart-modal'
-import useSite from '../../hooks/use-site'
-import useLocale from '../../hooks/use-locale'
import useWishlist from '../../hooks/use-wishlist'
// Localization
@@ -44,12 +42,12 @@ import {IntlProvider} from 'react-intl'
// Others
import {watchOnlineStatus, flatten} from '../../utils/utils'
-import {homeUrlBuilder, getPathWithLocale, buildPathWithUrlConfig} from '../../utils/url'
import {getTargetLocale, fetchTranslations} from '../../utils/locale'
import {DEFAULT_SITE_TITLE, HOME_HREF, THEME_COLOR} from '../../constants'
import Seo from '../seo'
import {resolveSiteFromUrl} from '../../utils/site-utils'
+import useMultiSite from '../../hooks/use-multi-site'
const DEFAULT_NAV_DEPTH = 3
const DEFAULT_ROOT_CATEGORY = 'root'
@@ -63,18 +61,11 @@ const App = (props) => {
const location = useLocation()
const authModal = useAuthModal()
const customer = useCustomer()
-
- const site = useSite()
- const locale = useLocale()
+ const {site, locale, buildUrl} = useMultiSite()
const [isOnline, setIsOnline] = useState(true)
const styles = useStyleConfig('App')
- const configValues = {
- locale: locale.alias || locale.id,
- site: site.alias || site.id
- }
-
const {isOpen, onOpen, onClose} = useDisclosure()
// Used to conditionally render header/footer for checkout page
@@ -115,7 +106,8 @@ const App = (props) => {
const onLogoClick = () => {
// Goto the home page.
- const path = homeUrlBuilder(HOME_HREF, {locale, site})
+ const path = buildUrl(HOME_HREF)
+
history.push(path)
// Close the drawer.
@@ -123,7 +115,7 @@ const App = (props) => {
}
const onCartClick = () => {
- const path = buildPathWithUrlConfig('/cart', configValues)
+ const path = buildUrl('/cart')
history.push(path)
// Close the drawer.
@@ -133,7 +125,7 @@ const App = (props) => {
const onAccountClick = () => {
// Link to account page for registered customer, open auth modal otherwise
if (customer.isRegistered) {
- const path = buildPathWithUrlConfig('/account', configValues)
+ const path = buildUrl('/account')
history.push(path)
} else {
// if they already are at the login page, do not show login modal
@@ -143,7 +135,7 @@ const App = (props) => {
}
const onWishlistClick = () => {
- const path = buildPathWithUrlConfig('/account/wishlist', configValues)
+ const path = buildUrl('/account/wishlist')
history.push(path)
}
@@ -184,9 +176,7 @@ const App = (props) => {
))}
@@ -194,9 +184,7 @@ const App = (props) => {
{/* A wider fallback for user locales that the app does not support */}
@@ -222,11 +210,15 @@ const App = (props) => {
onClose={onClose}
onLogoClick={onLogoClick}
root={allCategories[DEFAULT_ROOT_CATEGORY]}
+ locale={locale}
/>
-
+
) : (
diff --git a/packages/template-retail-react-app/app/components/_app/index.test.js b/packages/template-retail-react-app/app/components/_app/index.test.js
index 5d09111d6a..3536e5f8ca 100644
--- a/packages/template-retail-react-app/app/components/_app/index.test.js
+++ b/packages/template-retail-react-app/app/components/_app/index.test.js
@@ -11,10 +11,10 @@ import {Helmet} from 'react-helmet'
import App from './index.jsx'
import {renderWithProviders} from '../../utils/test-utils'
import {DEFAULT_LOCALE} from '../../utils/test-utils'
-import useSite from '../../hooks/use-site'
+import useMultiSite from '../../hooks/use-multi-site'
import messages from '../../translations/compiled/en-GB.json'
import mockConfig from '../../../config/mocks/default'
-jest.mock('../../hooks/use-site', () => jest.fn())
+jest.mock('../../hooks/use-multi-site', () => jest.fn())
let windowSpy
beforeAll(() => {
jest.spyOn(console, 'log').mockImplementation(jest.fn())
@@ -40,8 +40,21 @@ describe('App', () => {
...mockConfig.app.sites[0],
alias: 'uk'
}
+
+ const locale = DEFAULT_LOCALE
+
+ const buildUrl = jest.fn().mockImplementation((href, site, locale) => {
+ return `${site ? `/${site}` : ''}${locale ? `/${locale}` : ''}${href}`
+ })
+
+ const resultUseMultiSite = {
+ site,
+ locale,
+ buildUrl
+ }
+
test('App component is rendered appropriately', () => {
- useSite.mockImplementation(() => site)
+ useMultiSite.mockImplementation(() => resultUseMultiSite)
renderWithProviders(
Any children here
@@ -66,7 +79,7 @@ describe('App', () => {
})
test('The localized hreflang links exist in the html head', () => {
- useSite.mockImplementation(() => site)
+ useMultiSite.mockImplementation(() => resultUseMultiSite)
renderWithProviders(
)
@@ -77,7 +90,7 @@ describe('App', () => {
const hasGeneralLocale = ({hrefLang}) => hrefLang === DEFAULT_LOCALE.slice(0, 2)
// `length + 2` because one for a general locale and the other with x-default value
- expect(hreflangLinks.length).toBe(site.l10n.supportedLocales.length + 2)
+ expect(hreflangLinks.length).toBe(resultUseMultiSite.site.l10n.supportedLocales.length + 2)
expect(hreflangLinks.some((link) => hasGeneralLocale(link))).toBe(true)
expect(hreflangLinks.some((link) => link.hrefLang === 'x-default')).toBe(true)
diff --git a/packages/template-retail-react-app/app/components/drawer-menu/index.jsx b/packages/template-retail-react-app/app/components/drawer-menu/index.jsx
index cc60045281..4e272ba73c 100644
--- a/packages/template-retail-react-app/app/components/drawer-menu/index.jsx
+++ b/packages/template-retail-react-app/app/components/drawer-menu/index.jsx
@@ -52,7 +52,7 @@ import useCustomer from '../../commerce-api/hooks/useCustomer'
import LoadingSpinner from '../loading-spinner'
import useNavigation from '../../hooks/use-navigation'
-import useSite from '../../hooks/use-site'
+import useMultiSite from '../../hooks/use-multi-site'
// The FONT_SIZES and FONT_WEIGHTS constants are used to control the styling for
// the accordion buttons as their current depth. In the below definition we assign
@@ -84,7 +84,7 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => {
const styles = useMultiStyleConfig('DrawerMenu')
const drawerSize = useBreakpointValue({sm: PHONE_DRAWER_SIZE, md: TABLET_DRAWER_SIZE})
const socialIconVariant = useBreakpointValue({base: 'flex', md: 'flex-start'})
- const site = useSite()
+ const {site, buildUrl} = useMultiSite()
const {l10n} = site
const [showLoading, setShowLoading] = useState(false)
const onSignoutClick = async () => {
@@ -272,7 +272,7 @@ const DrawerMenu = ({isOpen, onClose = noop, onLogoClick = noop, root}) => {
locales={supportedLocaleIds}
onSelect={(newLocale) => {
// Update the `locale` in the URL.
- const newUrl = getPathWithLocale(newLocale, {
+ const newUrl = getPathWithLocale(newLocale, buildUrl, {
disallowParams: ['refine']
})
window.location = newUrl
diff --git a/packages/template-retail-react-app/app/components/footer/index.jsx b/packages/template-retail-react-app/app/components/footer/index.jsx
index c59efb62a9..a63c43202c 100644
--- a/packages/template-retail-react-app/app/components/footer/index.jsx
+++ b/packages/template-retail-react-app/app/components/footer/index.jsx
@@ -29,14 +29,15 @@ import SocialIcons from '../social-icons'
import {HideOnDesktop, HideOnMobile} from '../responsive'
import {getPathWithLocale} from '../../utils/url'
import LocaleText from '../locale-text'
-import useSite from '../../hooks/use-site'
+import useMultiSite from '../../hooks/use-multi-site'
const Footer = ({...otherProps}) => {
const styles = useMultiStyleConfig('Footer')
const intl = useIntl()
const [locale, setLocale] = useState(intl.locale)
- const site = useSite()
+ const {site, buildUrl} = useMultiSite()
const {l10n} = site
+
const supportedLocaleIds = l10n?.supportedLocales.map((locale) => locale.id)
const showLocaleSelector = supportedLocaleIds?.length > 1
@@ -136,7 +137,7 @@ const Footer = ({...otherProps}) => {
setLocale(target.value)
// Update the `locale` in the URL.
- const newUrl = getPathWithLocale(target.value, {
+ const newUrl = getPathWithLocale(target.value, buildUrl, {
disallowParams: ['refine']
})
diff --git a/packages/template-retail-react-app/app/components/link/index.jsx b/packages/template-retail-react-app/app/components/link/index.jsx
index be8d2b4660..74b61b14c2 100644
--- a/packages/template-retail-react-app/app/components/link/index.jsx
+++ b/packages/template-retail-react-app/app/components/link/index.jsx
@@ -8,20 +8,12 @@ import React from 'react'
import PropTypes from 'prop-types'
import {Link as ChakraLink} from '@chakra-ui/react'
import {Link as SPALink, NavLink as NavSPALink} from 'react-router-dom'
-import {buildPathWithUrlConfig} from '../../utils/url'
-import useSite from '../../hooks/use-site'
-import useLocale from '../../hooks/use-locale'
+import useMultiSite from '../../hooks/use-multi-site'
const Link = React.forwardRef(({href, to, useNavLink = false, ...props}, ref) => {
const _href = to || href
- const site = useSite()
- const locale = useLocale()
-
- // if alias is not defined, use site id
- const updatedHref = buildPathWithUrlConfig(_href, {
- locale: locale.alias || locale.id,
- site: site.alias || site.id
- })
+ const {buildUrl} = useMultiSite()
+ const updatedHref = buildUrl(_href)
return (
{
delete window.location
window.location = new URL('/us/en-US', 'https://www.example.com')
const {getByText} = renderWithProviders(My Page, {
- wrapperProps: {locale: 'en-US'}
+ wrapperProps: {locale: 'en-US', siteAlias: 'us', appConfig: mockConfig.app}
})
expect(getByText(/My Page/i)).toHaveAttribute('href', '/us/en-US/mypage')
})
@@ -49,7 +49,7 @@ test('renders a link with locale and site as query param', () => {
delete window.location
window.location = new URL('https://www.example.com/women/dresses?site=us&locale=en-US')
const {getByText} = renderWithProviders(My Page, {
- wrapperProps: {locale: 'en-US'}
+ wrapperProps: {locale: 'en-US', siteAlias: 'us', appConfig: newConfig.app}
})
expect(getByText(/My Page/i)).toHaveAttribute('href', `/mypage?site=us&locale=en-US`)
@@ -60,7 +60,7 @@ test('accepts `to` prop as well', () => {
delete window.location
window.location = new URL('us/en-US', 'https://www.example.com')
const {getByText} = renderWithProviders(My Page, {
- wrapperProps: {locale: 'en-US'}
+ wrapperProps: {locale: 'en-US', siteAlias: 'us', appConfig: mockConfig.app}
})
expect(getByText(/My Page/i)).toHaveAttribute('href', '/us/en-US/mypage')
})
diff --git a/packages/template-retail-react-app/app/components/search/index.test.js b/packages/template-retail-react-app/app/components/search/index.test.js
index fed930fc70..bac41a35a4 100644
--- a/packages/template-retail-react-app/app/components/search/index.test.js
+++ b/packages/template-retail-react-app/app/components/search/index.test.js
@@ -12,6 +12,7 @@ import SearchInput from './index'
import Suggestions from './partials/suggestions'
import {noop} from '../../utils/utils'
import mockSearchResults from '../../commerce-api/mocks/searchResults'
+import mockConfig from '../../../config/mocks/default'
jest.mock('../../commerce-api/utils', () => {
const originalModule = jest.requireActual('../../commerce-api/utils')
@@ -53,7 +54,9 @@ test('renders Popover if recent searches populated', async () => {
})
test('changes url when enter is pressed', async () => {
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
const searchInput = document.querySelector('input[type="search"]')
await user.type(searchInput, 'Dresses{enter}')
await waitFor(() => expect(window.location.pathname).toEqual(createPathWithDefaults('/search')))
diff --git a/packages/template-retail-react-app/app/contexts/index.js b/packages/template-retail-react-app/app/contexts/index.js
index cf0d25cf82..af739bb574 100644
--- a/packages/template-retail-react-app/app/contexts/index.js
+++ b/packages/template-retail-react-app/app/contexts/index.js
@@ -46,6 +46,51 @@ CategoriesProvider.propTypes = {
categories: PropTypes.object
}
+/**
+ * This is the global state for the multiples sites and locales supported in the App.
+ *
+ * To use the context simply import them into the component requiring context
+ * like the below example:
+ *
+ * import React, {useContext} from 'react'
+ * import {MultiSiteContext} from './contexts'
+ *
+ * export const RootCurrencyLabel = () => {
+ * const {site,locale,urlTemplate} = useContext(MultiSiteContext)
+ * return {site} {locale}
+ * }
+ *
+ * Alternatively you can use the hook provided by us:
+ *
+ * import {useMultiSite} from './hooks'
+ *
+ * const {site, locale, buildUrl} = useMultiSite()
+ * @type {React.Context}
+ */
+export const MultiSiteContext = React.createContext()
+export const MultiSiteProvider = ({
+ site: initialSite = {},
+ locale: initialLocale = {},
+ buildUrl,
+ children
+}) => {
+ const [site, setSite] = useState(initialSite)
+ const [locale, setLocale] = useState(initialLocale)
+
+ return (
+
+ {children}
+
+ )
+}
+
+MultiSiteProvider.propTypes = {
+ children: PropTypes.node.isRequired,
+ buildUrl: PropTypes.func,
+ site: PropTypes.object,
+ locale: PropTypes.string
+}
+
/**
* This is the global state for currency, we use this throughout the site. For example, on
* the product-list, product-detail and cart and basket pages..
diff --git a/packages/template-retail-react-app/app/hooks/use-categories.js b/packages/template-retail-react-app/app/hooks/use-categories.js
index 7da1452542..4b6dd59ca7 100644
--- a/packages/template-retail-react-app/app/hooks/use-categories.js
+++ b/packages/template-retail-react-app/app/hooks/use-categories.js
@@ -11,4 +11,10 @@ import {CategoriesContext} from '../contexts'
* Custom React hook to get the categories
* @returns {{categories: Object, setCategories: function}[]}
*/
-export const useCategories = () => useContext(CategoriesContext)
+export const useCategories = () => {
+ const context = useContext(CategoriesContext)
+ if (context === undefined) {
+ throw new Error('useCategories must be used within CategoriesProvider')
+ }
+ return context
+}
diff --git a/packages/template-retail-react-app/app/hooks/use-currency.js b/packages/template-retail-react-app/app/hooks/use-currency.js
index ec17d475e6..5527d8c6f2 100644
--- a/packages/template-retail-react-app/app/hooks/use-currency.js
+++ b/packages/template-retail-react-app/app/hooks/use-currency.js
@@ -11,4 +11,10 @@ import {CurrencyContext} from '../contexts'
* Custom React hook to get the currency for the active locale and to change it to a different currency
* @returns {{currency: string, setCurrency: function}[]}
*/
-export const useCurrency = () => useContext(CurrencyContext)
+export const useCurrency = () => {
+ const context = useContext(CurrencyContext)
+ if (context === undefined) {
+ throw new Error('useCurrency must be used within CurrencyProvider')
+ }
+ return context
+}
diff --git a/packages/template-retail-react-app/app/hooks/use-locale.js b/packages/template-retail-react-app/app/hooks/use-locale.js
deleted file mode 100644
index 179dd31f73..0000000000
--- a/packages/template-retail-react-app/app/hooks/use-locale.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2021, salesforce.com, inc.
- * All rights reserved.
- * SPDX-License-Identifier: BSD-3-Clause
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
- */
-
-import useSite from './use-site'
-import {useMemo} from 'react'
-import {resolveLocaleFromUrl} from '../utils/utils'
-import {useLocation} from 'react-router-dom'
-
-/**
- * This hook returns the locale object based on current location
- * @return {object} locale
- */
-const useLocale = () => {
- const {pathname, search} = useLocation()
- const site = useSite()
- const locale = useMemo(() => {
- return resolveLocaleFromUrl(`${pathname}${search}`)
- }, [pathname, search, site])
-
- return locale
-}
-
-export default useLocale
diff --git a/packages/template-retail-react-app/app/hooks/use-locale.test.js b/packages/template-retail-react-app/app/hooks/use-locale.test.js
deleted file mode 100644
index cb768d11d5..0000000000
--- a/packages/template-retail-react-app/app/hooks/use-locale.test.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2021, salesforce.com, inc.
- * All rights reserved.
- * SPDX-License-Identifier: BSD-3-Clause
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
- */
-
-import {createMemoryHistory} from 'history'
-import {screen} from '@testing-library/react'
-import {Router} from 'react-router'
-import React from 'react'
-
-import useLocale from './use-locale'
-import {renderWithReactIntl} from '../utils/test-utils'
-
-const MockComponent = () => {
- const locale = useLocale()
- return {JSON.stringify(locale)}
-}
-
-describe('useLocale', function() {
- test('return the default locale', () => {
- const history = createMemoryHistory()
- history.push('/test/path')
- renderWithReactIntl(
-
-
-
- )
- expect(screen.getByTestId('locale')).toHaveTextContent(
- '{"id":"en-GB","preferredCurrency":"GBP"}'
- )
- })
-
- test('return the locale object that matches the site (from the url) and the locale from intl', () => {
- const history = createMemoryHistory()
- history.push('/us/en-CA/test/path')
- renderWithReactIntl(
-
-
- ,
- 'en-CA'
- )
- expect(screen.getByTestId('locale')).toHaveTextContent(
- '{"id":"en-CA","preferredCurrency":"USD"}'
- )
- })
-})
diff --git a/packages/template-retail-react-app/app/hooks/use-multi-site.js b/packages/template-retail-react-app/app/hooks/use-multi-site.js
new file mode 100644
index 0000000000..9e053ef1fa
--- /dev/null
+++ b/packages/template-retail-react-app/app/hooks/use-multi-site.js
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, salesforce.com, inc.
+ * All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
+ */
+
+import {useCallback, useContext} from 'react'
+import {MultiSiteContext} from '../contexts'
+
+/**
+ * Custom React hook to get the function that returns usefule multi-site values, the site, the locale and
+ * the funtion used to build URLs following the App configuration.
+ * @returns {{site, locale, buildUrl: (function(*, *, *): *)}}
+ */
+const useMultiSite = () => {
+ const context = useContext(MultiSiteContext)
+ if (context === undefined) {
+ throw new Error('useMultiSite must be used within MultiSiteProvider')
+ }
+ const {buildUrl: originalFn, site, locale} = context
+
+ const buildUrl = useCallback(
+ (path, siteRef, localeRef) => {
+ return originalFn(
+ path,
+ siteRef ? siteRef : site?.alias || site?.id,
+ localeRef ? localeRef : locale
+ )
+ },
+ [originalFn, site, locale]
+ )
+ return {site, locale, buildUrl}
+}
+
+export default useMultiSite
diff --git a/packages/template-retail-react-app/app/hooks/use-multi-site.test.js b/packages/template-retail-react-app/app/hooks/use-multi-site.test.js
new file mode 100644
index 0000000000..c7d4c6c3ff
--- /dev/null
+++ b/packages/template-retail-react-app/app/hooks/use-multi-site.test.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021, salesforce.com, inc.
+ * All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
+ */
+
+import React from 'react'
+import {renderHook} from '@testing-library/react-hooks'
+import useMultiSite from './use-multi-site'
+import {MultiSiteProvider} from '../contexts'
+import mockConfig from '../../config/mocks/default'
+import {DEFAULT_LOCALE} from '../utils/test-utils'
+
+const wrapper = ({children}) => {children}
+
+let resultuseMultiSite = {}
+
+beforeEach(() => {
+ resultuseMultiSite = {}
+})
+
+const site = {
+ ...mockConfig.app.sites[0],
+ alias: 'uk'
+}
+
+const locale = DEFAULT_LOCALE
+
+const buildUrl = jest.fn().mockImplementation((href, site, locale) => {
+ return `${site ? `/${site}` : ''}${locale ? `/${locale}` : ''}${href}`
+})
+
+const mockResultuseMultiSite = {
+ site,
+ locale,
+ buildUrl
+}
+
+const mockUseContext = jest.fn().mockImplementation(() => mockResultuseMultiSite)
+
+React.useContext = mockUseContext
+describe('useMultiSite', () => {
+ it('should set initial values', () => {
+ expect(resultuseMultiSite).toMatchObject({})
+
+ const {result} = renderHook(() => useMultiSite(), {wrapper})
+
+ expect(mockUseContext).toHaveBeenCalled()
+ expect(result.current).toHaveProperty('site')
+ expect(result.current).toHaveProperty('locale')
+ })
+})
diff --git a/packages/template-retail-react-app/app/hooks/use-navigation.js b/packages/template-retail-react-app/app/hooks/use-navigation.js
index 9b02532fde..81eaab6f09 100644
--- a/packages/template-retail-react-app/app/hooks/use-navigation.js
+++ b/packages/template-retail-react-app/app/hooks/use-navigation.js
@@ -6,10 +6,7 @@
*/
import {useCallback} from 'react'
import {useHistory} from 'react-router'
-import {useIntl} from 'react-intl'
-import {buildPathWithUrlConfig} from '../utils/url'
-import useSite from './use-site'
-import {getLocaleByReference} from '../utils/utils'
+import useMultiSite from './use-multi-site'
/**
* A convenience hook for programmatic navigation uses history's `push` or `replace`. The proper locale
@@ -19,8 +16,8 @@ import {getLocaleByReference} from '../utils/utils'
const useNavigation = () => {
const history = useHistory()
- const {locale: localeShortCode} = useIntl()
- const site = useSite()
+ const {site, locale: localeShortCode, buildUrl} = useMultiSite()
+
return useCallback(
/**
*
@@ -29,12 +26,7 @@ const useNavigation = () => {
* @param {...any} args - additional args passed to `.push` or `.replace`
*/
(path, action = 'push', ...args) => {
- const locale = getLocaleByReference(site, localeShortCode)
-
- const updatedHref = buildPathWithUrlConfig(path, {
- locale: locale.alias || locale.id,
- site: site.alias || site.id
- })
+ const updatedHref = buildUrl(path)
history[action](path === '/' ? '/' : updatedHref, ...args)
},
[localeShortCode, site]
diff --git a/packages/template-retail-react-app/app/hooks/use-navigation.test.js b/packages/template-retail-react-app/app/hooks/use-navigation.test.js
index 2a31873fd6..cb4ca6db65 100644
--- a/packages/template-retail-react-app/app/hooks/use-navigation.test.js
+++ b/packages/template-retail-react-app/app/hooks/use-navigation.test.js
@@ -56,7 +56,9 @@ const TestComponent = () => {
test('prepends locale and site and calls history.push', () => {
getConfig.mockImplementation(() => mockConfig)
- const {getByTestId} = renderWithProviders()
+ const {getByTestId} = renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
user.click(getByTestId('page1-link'))
expect(mockHistoryPush).toHaveBeenCalledWith('/uk/en-GB/page1')
})
@@ -74,7 +76,9 @@ test('append locale as path and site as query and calls history.push', () => {
}
}
getConfig.mockImplementation(() => newConfig)
- const {getByTestId} = renderWithProviders()
+ const {getByTestId} = renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: newConfig.app}
+ })
user.click(getByTestId('page1-link'))
expect(mockHistoryPush).toHaveBeenCalledWith('/en-GB/page1?site=uk')
})
@@ -82,7 +86,9 @@ test('append locale as path and site as query and calls history.push', () => {
test('works for any history method and args', () => {
getConfig.mockImplementation(() => mockConfig)
- const {getByTestId} = renderWithProviders()
+ const {getByTestId} = renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
user.click(getByTestId('page2-link'))
expect(mockHistoryReplace).toHaveBeenCalledWith('/uk/en-GB/page2', {})
@@ -91,7 +97,9 @@ test('works for any history method and args', () => {
test('if given the path to root or homepage, will not prepend the locale', () => {
getConfig.mockImplementation(() => mockConfig)
- const {getByTestId} = renderWithProviders()
+ const {getByTestId} = renderWithProviders(, {
+ wrapperProps: {siteAlias: 'us', locale: 'en-US'}
+ })
user.click(getByTestId('page4-link'))
expect(mockHistoryPush).toHaveBeenCalledWith('/')
})
diff --git a/packages/template-retail-react-app/app/hooks/use-site.js b/packages/template-retail-react-app/app/hooks/use-site.js
deleted file mode 100644
index af3b9168f5..0000000000
--- a/packages/template-retail-react-app/app/hooks/use-site.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2021, salesforce.com, inc.
- * All rights reserved.
- * SPDX-License-Identifier: BSD-3-Clause
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
- */
-
-import {useLocation} from 'react-router-dom'
-import {resolveSiteFromUrl} from '../utils/site-utils'
-import {useMemo} from 'react'
-
-/**
- * This hook returns the current site based on current location
- *
- * @returns {Object} - current site
- */
-const useSite = () => {
- const {pathname, search} = useLocation()
- const site = useMemo(() => {
- return resolveSiteFromUrl(`${pathname}${search}`)
- }, [pathname, search])
- return site
-}
-
-export default useSite
diff --git a/packages/template-retail-react-app/app/hooks/use-site.test.js b/packages/template-retail-react-app/app/hooks/use-site.test.js
deleted file mode 100644
index 5f52ef354b..0000000000
--- a/packages/template-retail-react-app/app/hooks/use-site.test.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2021, salesforce.com, inc.
- * All rights reserved.
- * SPDX-License-Identifier: BSD-3-Clause
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
- */
-
-import {createMemoryHistory} from 'history'
-import {render, screen} from '@testing-library/react'
-import {Router} from 'react-router'
-import React from 'react'
-import useSite from './use-site'
-
-afterEach(() => {
- jest.clearAllMocks()
-})
-
-const MockComponent = () => {
- const site = useSite()
- return {JSON.stringify(site)}
-}
-
-describe('useSite', function() {
- test('returns the default site when there is no ref in the url ', () => {
- const history = createMemoryHistory()
- history.push('/test/path')
- render(
-
-
-
- )
- expect(screen.getByTestId('site')).toHaveTextContent(
- '{"id":"site-1","l10n":{"defaultLocale":"en-GB","supportedLocales":[{"id":"en-GB","preferredCurrency":"GBP"},{"id":"fr-FR","alias":"fr","preferredCurrency":"EUR"},{"id":"it-IT","preferredCurrency":"EUR"}]},"alias":"uk"}'
- )
- })
-
- test('returns site-2 as the result ', () => {
- const history = createMemoryHistory()
- history.push('/us/test/path')
- render(
-
-
-
- )
- expect(screen.getByTestId('site')).toHaveTextContent(
- '{"id":"site-2","l10n":{"defaultLocale":"en-US","supportedLocales":[{"id":"en-US","preferredCurrency":"USD"},{"id":"en-CA","preferredCurrency":"USD"}]},"alias":"us"}'
- )
- })
-})
diff --git a/packages/template-retail-react-app/app/pages/account/index.jsx b/packages/template-retail-react-app/app/pages/account/index.jsx
index 77976ad57d..a2ef39a2cc 100644
--- a/packages/template-retail-react-app/app/pages/account/index.jsx
+++ b/packages/template-retail-react-app/app/pages/account/index.jsx
@@ -37,22 +37,20 @@ import {useLocation} from 'react-router-dom'
import {messages, navLinks} from './constant'
import useNavigation from '../../hooks/use-navigation'
import LoadingSpinner from '../../components/loading-spinner'
-import {buildPathWithUrlConfig} from '../../utils/url'
-import useLocale from '../../hooks/use-locale'
-import useSite from '../../hooks/use-site'
+import useMultiSite from '../../hooks/use-multi-site'
const Account = () => {
const {path} = useRouteMatch()
const {formatMessage} = useIntl()
const customer = useCustomer()
- const locale = useLocale()
const location = useLocation()
- const site = useSite()
const navigate = useNavigation()
const [mobileNavIndex, setMobileNavIndex] = useState(-1)
const [showLoading, setShowLoading] = useState(false)
+ const {buildUrl} = useMultiSite()
+
const onSignoutClick = async () => {
setShowLoading(true)
await customer.logout()
@@ -91,10 +89,7 @@ const Account = () => {
// Using Redirect allows us to store the directed page to location
// so we can direct users back after they are successfully log in
if (customer.authType != null && !customer.isRegistered) {
- const path = buildPathWithUrlConfig('/login', {
- locale: locale.alias || locale.id,
- site: site.alias || site.id
- })
+ const path = buildUrl('/login')
return
}
diff --git a/packages/template-retail-react-app/app/pages/account/index.test.js b/packages/template-retail-react-app/app/pages/account/index.test.js
index 51f1f8b8ae..eefb1212ca 100644
--- a/packages/template-retail-react-app/app/pages/account/index.test.js
+++ b/packages/template-retail-react-app/app/pages/account/index.test.js
@@ -18,6 +18,7 @@ import {
} from '../../commerce-api/mock-data'
import useCustomer from '../../commerce-api/hooks/useCustomer'
import Account from './index'
+import mockConfig from '../../../config/mocks/default'
jest.mock('../../commerce-api/utils', () => {
const originalModule = jest.requireActual('../../commerce-api/utils')
@@ -75,12 +76,16 @@ test('Redirects to login page if the customer is not logged in', async () => {
return res(ctx.delay(0), ctx.status(200), ctx.json(mockedGuestCustomer))
})
)
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
await waitFor(() => expect(window.location.pathname).toEqual(`${expectedBasePath}/login`))
})
test('Provides navigation for subpages', async () => {
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
expect(await screen.findByTestId('account-page')).toBeInTheDocument()
const nav = within(screen.getByTestId('account-detail-nav'))
@@ -108,7 +113,9 @@ test('Renders account detail page by default for logged-in customer', async () =
})
test('Allows customer to sign out', async () => {
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
expect(await screen.findByTestId('account-detail-page')).toBeInTheDocument()
user.click(screen.getAllByText(/Log Out/)[0])
await waitFor(() => {
diff --git a/packages/template-retail-react-app/app/pages/account/orders.test.js b/packages/template-retail-react-app/app/pages/account/orders.test.js
index 1fc3c942cb..8e33706165 100644
--- a/packages/template-retail-react-app/app/pages/account/orders.test.js
+++ b/packages/template-retail-react-app/app/pages/account/orders.test.js
@@ -13,6 +13,7 @@ import {renderWithProviders, createPathWithDefaults, setupMockServer} from '../.
import {mockOrderHistory, mockOrderProducts} from '../../commerce-api/mock-data'
import useCustomer from '../../commerce-api/hooks/useCustomer'
import Orders from './orders'
+import mockConfig from '../../../config/mocks/default'
jest.mock('../../commerce-api/utils', () => {
const originalModule = jest.requireActual('../../commerce-api/utils')
@@ -66,7 +67,9 @@ afterEach(() => {
afterAll(() => server.close())
test('Renders order history and details', async () => {
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
expect(await screen.findByTestId('account-order-history-page')).toBeInTheDocument()
expect(await screen.findAllByText(/Ordered: /i)).toHaveLength(3)
expect(
diff --git a/packages/template-retail-react-app/app/pages/checkout/confirmation.test.js b/packages/template-retail-react-app/app/pages/checkout/confirmation.test.js
index 5b88995c08..411e3e92c0 100644
--- a/packages/template-retail-react-app/app/pages/checkout/confirmation.test.js
+++ b/packages/template-retail-react-app/app/pages/checkout/confirmation.test.js
@@ -211,7 +211,9 @@ afterEach(() => {
afterAll(() => server.close())
test('Navigates to homepage when no order present', async () => {
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB'}
+ })
expect(screen.queryByTestId('sf-checkout-confirmation-container')).not.toBeInTheDocument()
await waitFor(() => {
expect(window.location.pathname).toEqual('/')
diff --git a/packages/template-retail-react-app/app/pages/checkout/index.test.js b/packages/template-retail-react-app/app/pages/checkout/index.test.js
index ee8039f728..9c128cba5d 100644
--- a/packages/template-retail-react-app/app/pages/checkout/index.test.js
+++ b/packages/template-retail-react-app/app/pages/checkout/index.test.js
@@ -23,6 +23,7 @@ import {
mockedCustomerProductLists,
productsResponse
} from '../../commerce-api/mock-data'
+import mockConfig from '../../../config/mocks/default'
jest.setTimeout(60000)
@@ -238,7 +239,9 @@ test('Can proceed through checkout steps as guest', async () => {
// Set the initial browser router path and render our component tree.
window.history.pushState({}, 'Checkout', createPathWithDefaults('/checkout'))
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
// Wait for checkout to load and display first step
await screen.findByText(/checkout as guest/i)
@@ -446,7 +449,9 @@ test('Can proceed through checkout as registered customer', async () => {
// Set the initial browser router path and render our component tree.
window.history.pushState({}, 'Checkout', createPathWithDefaults('/checkout'))
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB', appConfig: mockConfig.app}
+ })
// Switch to login
const haveAccountButton = await screen.findByText(/already have an account/i)
@@ -580,7 +585,9 @@ test('Can edit address during checkout as a registered customer', async () => {
// Set the initial browser router path and render our component tree.
window.history.pushState({}, 'Checkout', createPathWithDefaults('/checkout'))
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB'}
+ })
// Switch to login
const haveAccountButton = await screen.findByText(/already have an account/i)
@@ -685,7 +692,9 @@ test('Can add address during checkout as a registered customer', async () => {
// Set the initial browser router path and render our component tree.
window.history.pushState({}, 'Checkout', createPathWithDefaults('/checkout'))
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'us', locale: 'en-US'}
+ })
// Switch to login
const haveAccountButton = await screen.findByText(/already have an account/i)
diff --git a/packages/template-retail-react-app/app/pages/login/index.test.js b/packages/template-retail-react-app/app/pages/login/index.test.js
index ccdab311d9..9bae5373fa 100644
--- a/packages/template-retail-react-app/app/pages/login/index.test.js
+++ b/packages/template-retail-react-app/app/pages/login/index.test.js
@@ -14,6 +14,7 @@ import {BrowserRouter as Router, Route} from 'react-router-dom'
import Account from '../account'
import Registration from '../registration'
import ResetPassword from '../reset-password'
+import mockConfig from '../../../config/mocks/default'
jest.setTimeout(60000)
@@ -139,7 +140,9 @@ test('Allows customer to sign in to their account', async () => {
})
)
// render our test component
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB', appConfig: mockConfig.app}
+ })
// enter credentials and submit
user.type(screen.getByLabelText('Email'), 'darek@test.com')
@@ -160,7 +163,9 @@ test('Renders error when given incorrect log in credentials', async () => {
)
// render our test component
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB', appConfig: mockConfig.app}
+ })
// enter credentials and submit
user.type(screen.getByLabelText('Email'), 'foo@test.com')
@@ -179,7 +184,9 @@ test('Renders error when given incorrect log in credentials', async () => {
test('should navigate to sign in page when the user clicks Create Account', async () => {
// render our test component
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB', appConfig: mockConfig.app}
+ })
user.click(screen.getByText(/Create Account/i))
// wait for sign up page to appear
@@ -188,7 +195,9 @@ test('should navigate to sign in page when the user clicks Create Account', asyn
test('should navigate to reset password page when the user clicks Forgot Password', async () => {
// render our test component
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB', appConfig: mockConfig.app}
+ })
user.click(screen.getByText(/forgot password/i))
// wait for sign up page to appear
diff --git a/packages/template-retail-react-app/app/pages/product-list/index.test.js b/packages/template-retail-react-app/app/pages/product-list/index.test.js
index 4ecf686551..ebdd925be9 100644
--- a/packages/template-retail-react-app/app/pages/product-list/index.test.js
+++ b/packages/template-retail-react-app/app/pages/product-list/index.test.js
@@ -166,7 +166,9 @@ test('show login modal when an unauthenticated user tries to add an item to wish
})
test('clicking a filter will change url', async () => {
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB'}
+ })
user.click(screen.getByText(/Beige/i))
await waitFor(() =>
expect(window.location.search).toEqual(
@@ -181,7 +183,9 @@ test('click on filter All should clear out all the filter in search params', asy
'ProductList',
'uk/en-GB/category/mens-clothing-jackets?limit=25&refine=c_refinementColor%3DBeige&sort=best-matches'
)
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', locale: 'en-GB'}
+ })
const clearAllButton = await screen.findAllByText(/Clear All/i)
user.click(clearAllButton[0])
await waitFor(() => expect(window.location.search).toEqual(''))
diff --git a/packages/template-retail-react-app/app/pages/registration/index.test.jsx b/packages/template-retail-react-app/app/pages/registration/index.test.jsx
index e24db4d010..f55f938629 100644
--- a/packages/template-retail-react-app/app/pages/registration/index.test.jsx
+++ b/packages/template-retail-react-app/app/pages/registration/index.test.jsx
@@ -11,6 +11,7 @@ import {renderWithProviders} from '../../utils/test-utils'
import Registration from '.'
import {BrowserRouter as Router, Route} from 'react-router-dom'
import Account from '../account'
+import mockConfig from '../../../config/mocks/default'
jest.setTimeout(60000)
@@ -147,7 +148,9 @@ test('Allows customer to create an account', async () => {
})
// render our test component
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
// fill out form and submit
const withinForm = within(screen.getByTestId('sf-auth-modal-form'))
diff --git a/packages/template-retail-react-app/app/pages/reset-password/index.test.jsx b/packages/template-retail-react-app/app/pages/reset-password/index.test.jsx
index 08962ffe6b..297f45eff3 100644
--- a/packages/template-retail-react-app/app/pages/reset-password/index.test.jsx
+++ b/packages/template-retail-react-app/app/pages/reset-password/index.test.jsx
@@ -10,6 +10,7 @@ import user from '@testing-library/user-event'
import {rest} from 'msw'
import {createPathWithDefaults, renderWithProviders, setupMockServer} from '../../utils/test-utils'
import ResetPassword from '.'
+import mockConfig from '../../../config/mocks/default'
jest.setTimeout(60000)
@@ -87,7 +88,9 @@ afterAll(() => server.close())
test('Allows customer to go to sign in page', async () => {
// render our test component
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
user.click(screen.getByText('Sign in'))
await waitFor(() => {
@@ -112,7 +115,9 @@ test('Allows customer to generate password token', async () => {
)
// render our test component
- renderWithProviders()
+ renderWithProviders(, {
+ wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
+ })
// enter credentials and submit
user.type(screen.getByLabelText('Email'), 'foo@test.com')
diff --git a/packages/template-retail-react-app/app/utils/site-utils.test.js b/packages/template-retail-react-app/app/utils/site-utils.test.js
index 67b5711fac..eee6633487 100644
--- a/packages/template-retail-react-app/app/utils/site-utils.test.js
+++ b/packages/template-retail-react-app/app/utils/site-utils.test.js
@@ -124,6 +124,7 @@ describe('getDefaultSite', function() {
id: 'site-2',
l10n: {
defaultLocale: 'en-US',
+ defaultCurrency: 'USD',
supportedLocales: [
{
id: 'en-US',
@@ -152,6 +153,7 @@ describe('getSites', function() {
alias: 'uk',
l10n: {
defaultLocale: 'en-GB',
+ defaultCurrency: 'GBP',
supportedLocales: [
{
id: 'en-GB',
@@ -174,6 +176,7 @@ describe('getSites', function() {
alias: 'us',
l10n: {
defaultLocale: 'en-US',
+ defaultCurrency: 'USD',
supportedLocales: [
{
id: 'en-US',
diff --git a/packages/template-retail-react-app/app/utils/test-utils.js b/packages/template-retail-react-app/app/utils/test-utils.js
index 7a2086d62b..fc4f684957 100644
--- a/packages/template-retail-react-app/app/utils/test-utils.js
+++ b/packages/template-retail-react-app/app/utils/test-utils.js
@@ -21,7 +21,7 @@ import {
CustomerProductListsProvider
} from '../commerce-api/contexts'
import {AddToCartModalContext} from '../hooks/use-add-to-cart-modal'
-import {app as appConfig} from '../../config/default'
+import {app as appDefaultConfig} from '../../config/default'
import {IntlProvider} from 'react-intl'
import {
mockCategories as initialMockCategories,
@@ -43,9 +43,12 @@ export const SUPPORTED_LOCALES = [
preferredCurrency: 'EUR'
}
]
+export const DEFAULT_SITE = 'global'
// Contexts
-import {CategoriesProvider, CurrencyProvider} from '../contexts'
-import {buildPathWithUrlConfig} from './url'
+import {CategoriesProvider, CurrencyProvider, MultiSiteProvider} from '../contexts'
+
+import {createUrlTemplate} from './url'
+import {getDefaultSite, getSites} from './site-utils'
export const renderWithReactIntl = (node, locale = DEFAULT_LOCALE) => {
return render(
@@ -59,8 +62,8 @@ export const renderWithRouter = (node) => renderWithReactIntl({node} {
const api = new CommerceAPI({
- ...appConfig.commerceAPI,
- einsteinConfig: appConfig.einsteinAPI,
+ ...appDefaultConfig.commerceAPI,
+ einsteinConfig: appDefaultConfig.einsteinAPI,
proxy: undefined
})
return renderWithReactIntl(
@@ -81,7 +84,9 @@ export const TestProviders = ({
initialCustomer = null,
initialCategories = initialMockCategories,
locale = DEFAULT_LOCALE,
- messages = fallbackMessages
+ messages = fallbackMessages,
+ appConfig = appDefaultConfig,
+ siteAlias = DEFAULT_SITE
}) => {
const mounted = useRef()
// We use this to track mounted state.
@@ -121,27 +126,39 @@ export const TestProviders = ({
onClose: () => {}
}
+ const sites = getSites()
+ const site =
+ sites.find((site) => {
+ return site.alias === siteAlias || site.id === appConfig['site']
+ }) || getDefaultSite()
+
+ const buildUrl = createUrlTemplate(appConfig, site.alias || site.id, locale)
+
return (
-
-
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+
+
+
)
}
@@ -153,7 +170,9 @@ TestProviders.propTypes = {
initialCategories: PropTypes.element,
initialProductLists: PropTypes.object,
messages: PropTypes.object,
- locale: PropTypes.string
+ locale: PropTypes.string,
+ appConfig: PropTypes.object,
+ siteAlias: PropTypes.string
}
/**
@@ -185,10 +204,9 @@ export const createPathWithDefaults = (path) => {
const siteAlias = app.siteAliases[defaultSite.id]
const defaultLocale = defaultSite.l10n.defaultLocale
- const updatedPath = buildPathWithUrlConfig(path, {
- site: siteAlias || defaultSite.id,
- locale: defaultLocale
- })
+ const buildUrl = createUrlTemplate(app, siteAlias || defaultSite, defaultLocale)
+
+ const updatedPath = buildUrl(path, siteAlias || defaultSite.id, defaultLocale)
return updatedPath
}
diff --git a/packages/template-retail-react-app/app/utils/url.js b/packages/template-retail-react-app/app/utils/url.js
index 468f5851c9..4e8c9371d1 100644
--- a/packages/template-retail-react-app/app/utils/url.js
+++ b/packages/template-retail-react-app/app/utils/url.js
@@ -6,7 +6,7 @@
*/
import {getAppOrigin} from 'pwa-kit-react-sdk/utils/url'
-import {getLocaleByReference, getParamsFromPath, getUrlConfig} from './utils'
+import {getLocaleByReference, getParamsFromPath} from './utils'
import {getDefaultSite, getSites} from './site-utils'
import {HOME_HREF, urlPartPositions} from '../constants'
@@ -110,7 +110,7 @@ export const categoryUrlBuilder = (category) => encodeURI(`/category/${category.
export const productUrlBuilder = (product) => encodeURI(`/product/${product.id}`)
/**
- * Given a search term, contructs a search url.
+ * Given a search term, constructs a search url.
*
* @param {string} searchTerm
* @returns {string}
@@ -122,11 +122,12 @@ export const searchUrlBuilder = (searchTerm) => `/search?q=${searchTerm}`
* Based on your app configuration, this function will replace your current locale shortCode with a new one
*
* @param {String} shortCode - The locale short code.
- * @param {Object} [opts] - Options, if there's any.
+ * @param {function(*, *, *, *=): string} - Generates a site URL from the provided path, site and locale.
+ * @param {string[]} opts.disallowParams - URL parameters to remove
* @param {Object} opts.location - location object to replace the default `window.location`
* @returns {String} url - The relative URL for the specific locale.
*/
-export const getPathWithLocale = (shortCode, opts = {}) => {
+export const getPathWithLocale = (shortCode, buildUrl, opts = {}) => {
const location = opts.location ? opts.location : window.location
let {siteRef, localeRef} = getParamsFromPath(`${location.pathname}${location.search}`)
let {pathname, search} = location
@@ -145,50 +146,92 @@ export const getPathWithLocale = (shortCode, opts = {}) => {
search = search.replace(/&$/, '')
const defaultSite = getDefaultSite()
- const isHomeRef = pathname === HOME_HREF
- const isDefaultLocaleOfDefaultSite = shortCode === defaultSite.l10n.defaultLocale
- const isDefaultSite = siteRef === defaultSite.alias || siteRef === defaultSite.id
+ // Remove query parameters
+ const {disallowParams = []} = opts
+
+ let queryString = new URLSearchParams(`${search}`)
+
+ if (disallowParams.length) {
+ disallowParams.forEach((param) => {
+ queryString.delete(param)
+ })
+ }
+
// rebuild the url with new locale,
- const newUrl = buildPathWithUrlConfig(
- `${pathname}${search}`,
- {
- // By default, as for home page, when the values of site and locale belongs to the default site,
- // they will be not shown in the url just
- site:
- isDefaultLocaleOfDefaultSite && isDefaultSite && isHomeRef
- ? ''
- : siteRef || defaultSite.alias || defaultSite.id,
- locale: isDefaultLocaleOfDefaultSite && isDefaultSite && isHomeRef ? '' : shortCode
- },
- opts
+ const newUrl = buildUrl(
+ `${pathname}${Array.from(queryString).length !== 0 ? `?${queryString}` : ''}`,
+ // By default, as for home page, when the values of site and locale belongs to the default site,
+ // they will be not shown in the url just
+ defaultSite.alias || defaultSite.id,
+ shortCode
)
return newUrl
}
/**
- * Builds the Home page URL for a given locale and site.
- * By default, when the values of site and locale belongs to the default site,
- * they will be not shown in the url.
+ * Generates the URL Template literal (Template string) used to build URLs in the App according
+ * the current selected site/locale and the default App URL configuration.
*
- * Adjust the logic here to fit your cases
- *
- * @param homeHref
- * @param options
- * @returns {string}
+ * @param appConfig Application default configuration.
+ * @param siteRef Current selected Site reference. The value can be the Site id or alias.
+ * @param localeRef Current selected Locale reference. The value can be the Locale id or alias.
+ * @returns {function(*, *, *): string} function providing: path, site and locale generates a URL.
*/
-export const homeUrlBuilder = (homeHref, options = {}) => {
- const {locale, site} = options
+export const createUrlTemplate = (appConfig, siteRef, localeRef) => {
+ const {site: siteConfig, locale: localeConfig, showDefaults: showDefaultsConfig} = appConfig.url
const defaultSite = getDefaultSite()
- const isDefaultLocaleOfDefaultSite =
- locale.alias === defaultSite.l10n.defaultLocale ||
- locale.id === defaultSite.l10n.defaultLocale
- const isDefaultSite = site.id === defaultSite.id || site.alias === defaultSite.alias
- const updatedUrl = buildPathWithUrlConfig(homeHref, {
- locale: isDefaultLocaleOfDefaultSite && isDefaultSite ? '' : locale.alias || locale.id,
- site: isDefaultLocaleOfDefaultSite && isDefaultSite ? '' : site.alias || site.id
- })
- return encodeURI(updatedUrl)
+ const sites = getSites()
+ const siteAliasOrIdRef =
+ sites.find((site) => {
+ return site.alias === siteRef || site.id === siteRef
+ }) || defaultSite
+ const defaultLocale = getLocaleByReference(
+ siteAliasOrIdRef,
+ siteAliasOrIdRef.l10n.defaultLocale
+ )
+
+ const isDefaultSite =
+ defaultSite.id === siteRef || (defaultSite.alias && defaultSite.alias === siteRef)
+ const isDefaultLocale =
+ defaultLocale.id === localeRef || (defaultLocale.alias && defaultLocale.alias === localeRef)
+
+ const querySite =
+ (siteConfig === urlPartPositions.QUERY_PARAM && showDefaultsConfig) ||
+ (siteConfig === urlPartPositions.QUERY_PARAM && !showDefaultsConfig && !isDefaultSite)
+ const queryLocale =
+ (localeConfig === urlPartPositions.QUERY_PARAM && showDefaultsConfig) ||
+ (localeConfig === urlPartPositions.QUERY_PARAM && !showDefaultsConfig && !isDefaultLocale)
+
+ const isQuery = querySite || queryLocale
+
+ const pathSite =
+ (siteConfig === urlPartPositions.PATH && showDefaultsConfig) ||
+ (siteConfig === urlPartPositions.PATH && !showDefaultsConfig && !isDefaultSite)
+ const pathLocale =
+ (localeConfig === urlPartPositions.PATH && showDefaultsConfig) ||
+ (localeConfig === urlPartPositions.PATH && !showDefaultsConfig && !isDefaultLocale)
+
+ return (path, site, locale) => {
+ const isHomeWithDefaultSiteAndLocale =
+ path === HOME_HREF &&
+ (defaultSite.id === site || (defaultSite.alias && defaultSite.alias === site)) &&
+ (defaultLocale.id === locale || (defaultLocale.alias && defaultLocale.alias === locale))
+
+ const sitePath = pathSite && site && !isHomeWithDefaultSiteAndLocale ? `/${site}` : ''
+ const localePath =
+ pathLocale && locale && !isHomeWithDefaultSiteAndLocale ? `/${locale}` : ''
+
+ const hasQuery = isQuery && (site || locale) && !isHomeWithDefaultSiteAndLocale
+ let queryString = ''
+ if (hasQuery) {
+ const searchParams = new URLSearchParams()
+ querySite && site && searchParams.append('site', site)
+ queryLocale && locale && searchParams.append('locale', locale)
+ queryString = `?${searchParams.toString()}`
+ }
+ return `${sitePath}${localePath}${path}${queryString}`
+ }
}
/*
@@ -224,98 +267,3 @@ export const removeQueryParamsFromPath = (path, keys) => {
return `${pathname}${paramStr && '?'}${paramStr}`
}
-
-/**
- * Rebuild the path with locale/site values with a given url
- * The position of those values will based on the url config of your current app configuration.
- *
- * @param {string} relativeUrl - the base relative Url to be reconstructed on
- * @param {object} configValues - object that contains values of url config
- * @param {Object} [opts] - Options, if there's any.
- * @param {string[]} opts.disallowParams - URL parameters to remove
- * @return {string} - an output path that has locale and site
- *
- * @example
- * // configuration
- * url {
- * locale: "query_param",
- * site: "path",
- * showDefaults: true
- * }
- *
- *
- * const site = {
- * id: 'RefArch',
- * alias: 'global'
- * l10n: {
- * defaultLocale: 'en-GB'
- * supportedLocales: [
- * {id: 'en-GB', preferCurrency: 'GBP'}
- * ]
- * // other props
- * }
- * }
- * buildPathWithUrlConfig('/women/dresses', {locale: 'en-GB', site: 'global'})
- * => /global/women/dresses?locale=en-GB
- *
- */
-export const buildPathWithUrlConfig = (relativeUrl, configValues = {}, opts = {}) => {
- const urlConfig = getUrlConfig()
- const sites = getSites()
- const defaultSite = getDefaultSite()
- const site =
- sites.find((site) => {
- return site.alias === configValues['site'] || site.id === configValues['site']
- }) || defaultSite
- const defaultLocale = getLocaleByReference(site, site.l10n.defaultLocale)
- const defaultLocaleRefs = [defaultLocale.alias, defaultLocale.id].filter(Boolean)
- const {disallowParams = []} = opts
- if (!Object.values(configValues).length) return relativeUrl
- const [pathname, search] = relativeUrl.split('?')
-
- const params = new URLSearchParams(search)
- // Remove any disallowed params.
- if (disallowParams.length) {
- disallowParams.forEach((param) => {
- params.delete(param)
- })
- }
-
- const queryParams = {...Object.fromEntries(params)}
- let basePathSegments = []
-
- // get the default values for site and locale
- const showDefaults = urlConfig.showDefaults
-
- const defaultSiteRefs = [defaultSite.id, defaultSite.alias]
- const defaultValues = [...defaultSiteRefs, ...defaultLocaleRefs]
-
- const options = ['site', 'locale']
- options.forEach((option) => {
- const position = urlConfig[option] || urlPartPositions.NONE
- const val = configValues[option]
- if (position === urlPartPositions.PATH) {
- // if showDefaults is false, the default value will not be show in the url
- if (!showDefaults && defaultValues.includes(val)) {
- return
- }
- basePathSegments.push(val)
- } else if (position === urlPartPositions.QUERY_PARAM) {
- // if showDefaults is false, the default value will not be show in the url
- if (!showDefaults && defaultValues.includes(val)) {
- return
- }
- queryParams[option] = val
- }
- })
- // filter out falsy (empty string, undefined, null, etc) values in the array
- basePathSegments = basePathSegments.filter(Boolean)
- let updatedPath = `${
- basePathSegments.length ? `/${basePathSegments.join('/')}` : ''
- }${pathname}`
- // append the query param to pathname
- if (Object.keys(queryParams).length) {
- updatedPath = rebuildPathWithParams(updatedPath, queryParams)
- }
- return updatedPath
-}
diff --git a/packages/template-retail-react-app/app/utils/url.test.js b/packages/template-retail-react-app/app/utils/url.test.js
index 6b476bdbd2..c938511e8d 100644
--- a/packages/template-retail-react-app/app/utils/url.test.js
+++ b/packages/template-retail-react-app/app/utils/url.test.js
@@ -11,11 +11,10 @@ import {
productUrlBuilder,
searchUrlBuilder,
getPathWithLocale,
- homeUrlBuilder,
rebuildPathWithParams,
removeQueryParamsFromPath,
- buildPathWithUrlConfig,
- absoluteUrl
+ absoluteUrl,
+ createUrlTemplate
} from './url'
import {getUrlConfig} from './utils'
import mockConfig from '../../config/mocks/default'
@@ -126,17 +125,20 @@ describe('url builder test', () => {
describe('getPathWithLocale', () => {
getUrlConfig.mockImplementation(() => mockConfig.app.url)
+
test('getPathWithLocale returns expected for PLP', () => {
const location = new URL('http://localhost:3000/uk/it-IT/category/newarrivals-womens')
+ const buildUrl = createUrlTemplate(mockConfig.app, 'uk', 'it-IT')
- const relativeUrl = getPathWithLocale('fr-FR', {location})
+ const relativeUrl = getPathWithLocale('fr-FR', buildUrl, {location})
expect(relativeUrl).toEqual(`/uk/fr-FR/category/newarrivals-womens`)
})
test('getPathWithLocale uses default site for siteRef when it is no defined in the url', () => {
const location = new URL('http://localhost:3000/category/newarrivals-womens')
+ const buildUrl = createUrlTemplate(mockConfig.app, 'uk', 'it-IT')
- const relativeUrl = getPathWithLocale('fr-FR', {location})
+ const relativeUrl = getPathWithLocale('fr-FR', buildUrl, {location})
expect(relativeUrl).toEqual(`/uk/fr-FR/category/newarrivals-womens`)
})
@@ -144,8 +146,9 @@ describe('getPathWithLocale', () => {
const location = new URL(
'http://localhost:3000/uk/it-IT/category/newarrivals-womens?limit=25&refine=c_refinementColor%3DBianco&sort=best-matches&offset=25'
)
+ const buildUrl = createUrlTemplate(mockConfig.app, 'uk', 'it-IT')
- const relativeUrl = getPathWithLocale('fr-FR', {
+ const relativeUrl = getPathWithLocale('fr-FR', buildUrl, {
disallowParams: ['refine'],
location
})
@@ -156,20 +159,22 @@ describe('getPathWithLocale', () => {
test('getPathWithLocale returns expected for Homepage', () => {
const location = new URL('http://localhost:3000/uk/it-IT/')
+ const buildUrl = createUrlTemplate(mockConfig.app, 'uk', 'it-IT')
- const relativeUrl = getPathWithLocale('fr-FR', {location})
+ const relativeUrl = getPathWithLocale('fr-FR', buildUrl, {location})
expect(relativeUrl).toEqual(`/uk/fr-FR/`)
})
test('getPathWithLocale returns / when both site and locale are default', () => {
- const location = new URL('http://localhost:3000/uk/it-IT/')
+ const location = new URL('http://localhost:3000/')
+ const buildUrl = createUrlTemplate(mockConfig.app, 'uk', 'en-GB')
- const relativeUrl = getPathWithLocale('en-GB', {location})
+ const relativeUrl = getPathWithLocale('en-GB', buildUrl, {location})
expect(relativeUrl).toEqual(`/`)
})
})
-describe('homeUrlBuilder', () => {
+describe('createUrlTemplate tests', () => {
const defaultSite = mockConfig.app.sites[0]
const defaultAlias = mockConfig.app.siteAliases[defaultSite.id]
const defaultSiteMock = {...defaultSite, alias: defaultAlias}
@@ -177,139 +182,162 @@ describe('homeUrlBuilder', () => {
const nonDefaultSite = mockConfig.app.sites[1]
const nonDefaultAlias = mockConfig.app.siteAliases[nonDefaultSite.id]
const nonDefaultSiteMock = {...nonDefaultSite, alias: nonDefaultAlias}
- const cases = [
- {
- urlConfig: {
- locale: 'path',
- site: 'path',
- showDefaults: true
- },
- site: defaultSiteMock,
- locale: {id: 'en-GB'},
- expectedRes: '/'
- },
- {
- urlConfig: {
- locale: 'query_param',
- site: 'query_param',
- showDefaults: true
- },
- site: defaultSiteMock,
- locale: {id: 'en-GB'},
- expectedRes: '/'
- },
- {
- urlConfig: {
- locale: 'path',
- site: 'path',
- showDefaults: false
- },
- site: defaultSiteMock,
- locale: {id: 'en-GB'},
- expectedRes: '/'
- },
- {
- urlConfig: {
- locale: 'query_param',
- site: 'query_param',
- showDefaults: false
- },
- site: defaultSiteMock,
- locale: {id: 'en-GB'},
- expectedRes: '/'
- },
- {
- urlConfig: {
- locale: 'path',
- site: 'path',
- showDefaults: true
- },
- site: defaultSiteMock,
- locale: {id: 'fr-FR'},
- expectedRes: '/uk/fr-FR/'
- },
- {
- urlConfig: {
- locale: 'path',
- site: 'path',
- showDefaults: false
- },
- site: defaultSiteMock,
- locale: {id: 'fr-FR'},
- expectedRes: '/fr-FR/'
- },
- {
- urlConfig: {
- locale: 'query_param',
- site: 'query_param',
- showDefaults: true
- },
- site: defaultSiteMock,
- locale: {id: 'fr-FR'},
- expectedRes: '/?site=uk&locale=fr-FR'
- },
- {
- urlConfig: {
- locale: 'path',
- site: 'path',
- showDefaults: true
- },
- site: nonDefaultSiteMock,
- locale: {id: 'en-US'},
- expectedRes: '/us/en-US/'
- },
- {
- urlConfig: {
- locale: 'query_param',
- site: 'path',
- showDefaults: true
- },
- site: nonDefaultSiteMock,
- locale: {id: 'en-US'},
- expectedRes: '/us/?locale=en-US'
- },
- {
- urlConfig: {
- locale: 'path',
- site: 'path',
- showDefaults: false
- },
- site: nonDefaultSiteMock,
- locale: {id: 'en-US'}, // default locale of the nonDefault Site
- expectedRes: '/us/'
- },
- {
- urlConfig: {
- locale: 'query_param',
- site: 'path',
- showDefaults: false
- },
- site: nonDefaultSiteMock,
- locale: {id: 'en-US'}, // default locale of the nonDefault Site
- expectedRes: '/us/'
- },
- {
- urlConfig: {
- locale: 'query_param',
- site: 'query_param',
- showDefaults: true
- },
- site: nonDefaultSiteMock,
- locale: {id: 'en-US'}, // default locale of the nonDefault Site
- expectedRes: '/?site=us&locale=en-US'
+
+ const configValues = ['path', 'query_param', 'none']
+
+ let cases = []
+ for (let i = 0; i < configValues.length; i++) {
+ for (let j = 0; j < configValues.length; j++) {
+ for (let showDefaultsValues = 0; showDefaultsValues < 2; showDefaultsValues++) {
+ if (showDefaultsValues === 0) {
+ cases.push({
+ urlConfig: {
+ locale: configValues[i],
+ site: configValues[j],
+ showDefaults: true
+ },
+ site: defaultSiteMock,
+ locale: {id: 'en-GB'}
+ })
+ } else {
+ for (let isDefaultSite = 0; isDefaultSite < 2; isDefaultSite++) {
+ for (let isDefaultLocale = 0; isDefaultLocale < 2; isDefaultLocale++) {
+ if (isDefaultSite === 0) {
+ cases.push({
+ urlConfig: {
+ locale: configValues[i],
+ site: configValues[j],
+ showDefaults: false
+ },
+ site: defaultSiteMock,
+ locale: isDefaultLocale === 0 ? {id: 'en-GB'} : {id: 'fr-FR'}
+ })
+ } else {
+ cases.push({
+ urlConfig: {
+ locale: configValues[i],
+ site: configValues[j],
+ showDefaults: false
+ },
+ site: nonDefaultSiteMock,
+ locale: isDefaultLocale === 0 ? {id: 'en-US'} : {id: 'fr-FR'}
+ })
+ }
+ }
+ }
+ }
+ }
}
- ]
-
- cases.forEach(({urlConfig, site, locale, expectedRes}) => {
- test(`return expected URL with site ${site.alias}, locale ${
- locale.id
- } and urlConfig as ${JSON.stringify(urlConfig)}`, () => {
- getUrlConfig.mockImplementation(() => urlConfig)
- const homeUrl = homeUrlBuilder('/', {
- site,
- locale
+ }
+
+ const paths = ['/testpath', '/']
+ const expectedResults = (path) => {
+ return path !== '/'
+ ? [
+ `/uk/en-GB${path}`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `/us${path}`,
+ `/us/fr-FR${path}`,
+ `/en-GB${path}?site=uk`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `${path}?site=us`,
+ `/fr-FR${path}?site=us`,
+ `/en-GB${path}`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `/uk${path}?locale=en-GB`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `/us${path}`,
+ `/us${path}?locale=fr-FR`,
+ `${path}?site=uk&locale=en-GB`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `${path}?site=us`,
+ `${path}?site=us&locale=fr-FR`,
+ `${path}?locale=en-GB`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `/uk${path}`,
+ `${path}`,
+ `${path}`,
+ `/us${path}`,
+ `/us${path}`,
+ `${path}?site=uk`,
+ `${path}`,
+ `${path}`,
+ `${path}?site=us`,
+ `${path}?site=us`,
+ `${path}`,
+ `${path}`,
+ `${path}`,
+ `${path}`,
+ `${path}`
+ ]
+ : [
+ `${path}`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `/us${path}`,
+ `/us/fr-FR${path}`,
+ `${path}`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `${path}?site=us`,
+ `/fr-FR${path}?site=us`,
+ `${path}`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `${path}`,
+ `/fr-FR${path}`,
+ `${path}`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `/us${path}`,
+ `/us${path}?locale=fr-FR`,
+ `${path}`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `${path}?site=us`,
+ `${path}?site=us&locale=fr-FR`,
+ `${path}`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `${path}`,
+ `${path}?locale=fr-FR`,
+ `${path}`,
+ `${path}`,
+ `${path}`,
+ `/us${path}`,
+ `/us${path}`,
+ `${path}`,
+ `${path}`,
+ `${path}`,
+ `${path}?site=us`,
+ `${path}?site=us`,
+ `${path}`,
+ `${path}`,
+ `${path}`,
+ `${path}`,
+ `${path}`
+ ]
+ }
+ paths.forEach((path) => {
+ cases.forEach(({urlConfig, site, locale}, index) => {
+ test(`URL template path:${path}, site:${site.alias}, locale:${
+ locale.id
+ } and urlConfig:${JSON.stringify(urlConfig)}`, () => {
+ const buildUrl = createUrlTemplate({url: urlConfig}, site.id, locale.id)
+ const resultUrl = buildUrl(path, mockConfig.app.siteAliases[site.id], locale.id)
+
+ expect(resultUrl).toEqual(expectedResults(path)[index])
})
- expect(homeUrl).toEqual(expectedRes)
})
})
})
@@ -330,94 +358,6 @@ describe('removeQueryParamsFromPath test', () => {
})
})
-describe('buildPathWithUrlConfig', () => {
- test('return a new url with locale and site a part of path', () => {
- getUrlConfig.mockImplementation(() => ({
- locale: 'path',
- site: 'path',
- showDefaults: true
- }))
- const url = buildPathWithUrlConfig('/women/dresses', {locale: 'en-GB', site: 'uk'})
- expect(url).toEqual('/uk/en-GB/women/dresses')
- })
-
- test('return an expected url with no site, no locale for default values when the showDefaults is off', () => {
- getUrlConfig.mockImplementation(() => ({
- locale: 'path',
- site: 'path',
- showDefaults: false
- }))
- const url = buildPathWithUrlConfig('/women/dresses', {locale: 'en-GB', site: 'uk'})
- expect(url).toEqual('/women/dresses')
- })
-
- test('return a new url with locale value as a query param and site in the path', () => {
- getUrlConfig.mockImplementation(() => ({
- locale: 'query_param',
- site: 'path',
- showDefaults: true
- }))
- const url = buildPathWithUrlConfig('/women/dresses', {locale: 'en-GB', site: 'uk'})
- expect(url).toEqual('/uk/women/dresses?locale=en-GB')
- })
-
- test('return a new url with locale value as a path, site as query_param', () => {
- getUrlConfig.mockImplementation(() => ({
- locale: 'path',
- site: 'query_param',
- showDefaults: true
- }))
- const url = buildPathWithUrlConfig('/women/dresses', {locale: 'en-GB', site: 'uk'})
- expect(url).toEqual('/en-GB/women/dresses?site=uk')
- })
-
- test('return a new url with locale value as a path, site as query_param when showDefault is off', () => {
- getUrlConfig.mockImplementation(() => ({
- locale: 'path',
- site: 'query_param',
- showDefaults: false
- }))
- const url = buildPathWithUrlConfig('/women/dresses', {locale: 'en-GB', site: 'uk'})
- expect(url).toEqual('/women/dresses')
- })
-
- test('return a new url without a disallow param but respect other params', () => {
- getUrlConfig.mockImplementation(() => ({
- locale: 'query_param',
- site: 'path',
- showDefaults: true
- }))
- const url = buildPathWithUrlConfig(
- '/women/dresses?something=else&refine=c_color',
- {locale: 'en-GB', site: 'uk'},
- {disallowParams: ['refine']}
- )
- expect(url).toEqual('/uk/women/dresses?something=else&locale=en-GB')
- })
-
- test('return a new url as configured when the values are not defaults and showDefault is off', () => {
- getUrlConfig.mockImplementation(() => ({
- locale: 'query_param',
- site: 'path',
- showDefaults: false
- }))
- const url = buildPathWithUrlConfig(
- '/women/dresses?something=else&refine=c_color',
- {locale: 'en-CA', site: 'us'},
- {disallowParams: ['refine']}
- )
- expect(url).toEqual('/us/women/dresses?something=else&locale=en-CA')
- })
-
- test('throw an error when url config is not defined', () => {
- getUrlConfig.mockImplementation(() => undefined)
-
- expect(() => {
- buildPathWithUrlConfig('/women/dresses', {locale: 'en-GB', site: 'uk'})
- }).toThrow()
- })
-})
-
describe('absoluteUrl', function() {
test('return expected when path is a relative url', () => {
const url = absoluteUrl('/uk/en/women/dresses')
diff --git a/packages/template-retail-react-app/config/mocks/default.js b/packages/template-retail-react-app/config/mocks/default.js
index 2a52309110..e160c5734a 100644
--- a/packages/template-retail-react-app/config/mocks/default.js
+++ b/packages/template-retail-react-app/config/mocks/default.js
@@ -29,6 +29,7 @@ module.exports = {
id: 'site-1',
l10n: {
defaultLocale: 'en-GB',
+ defaultCurrency: 'GBP',
supportedLocales: [
{
id: 'en-GB',
@@ -50,6 +51,7 @@ module.exports = {
id: 'site-2',
l10n: {
defaultLocale: 'en-US',
+ defaultCurrency: 'USD',
supportedLocales: [
{
id: 'en-US',