Skip to content

Commit 240a9d5

Browse files
committed
Merge branch 'v3' into feature/template-extensibility
* v3: split ssr build on local (#1155) (#1156) [V3] Update Page Designer (#1128) [V3][Hooks Integration 🪝] Replace last of the `getProps` (#1149) Fix Page Designer ImageWithText Link component (#1092) # Conflicts: # packages/template-retail-react-app/app/components/_app-config/index.jsx
2 parents ef25c00 + 84ecf2f commit 240a9d5

File tree

26 files changed

+113
-497
lines changed

26 files changed

+113
-497
lines changed

packages/commerce-sdk-react/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"files": [
1818
"CHANGELOG.md",
1919
"LICENSE",
20-
"+(auth|hooks|scripts)/**/!(*.test*).{ts,js}",
20+
"+(auth|components|hooks|scripts)/**/!(*.test*).{ts,js}",
2121
"*.{js,d.ts}",
2222
"!*.test*.{js,d.ts}",
2323
"!test*.*",
@@ -75,6 +75,9 @@
7575
"react": "^17",
7676
"react-helmet": "6"
7777
},
78+
"optionalDependencies": {
79+
"prop-types": "^15.8.1"
80+
},
7881
"engines": {
7982
"node": "^16.0.0 || ^18.0.0",
8083
"npm": "^7.0.0 || ^8.0.0 || ^9.0.0"

packages/commerce-sdk-react/src/components/ShopperExperience/Region/index.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,18 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77
import React from 'react'
8-
import {Region as RegionType} from '../types'
8+
import PropTypes from 'prop-types'
99
import {Component} from '../Component'
10+
import {componentPropType} from '../prop-types'
11+
import {Region as RegionType} from '../types'
12+
13+
/**
14+
* This PropType represents a `region` object from the ShopperExperience API.
15+
*/
16+
export const propType = PropTypes.shape({
17+
id: PropTypes.string,
18+
components: PropTypes.arrayOf(componentPropType)
19+
})
1020

1121
interface RegionProps extends React.ComponentProps<'div'> {
1222
region: RegionType
@@ -36,4 +46,9 @@ export const Region = (props: RegionProps) => {
3646

3747
Region.displayName = 'Region'
3848

49+
Region.propTypes = {
50+
region: propType.isRequired,
51+
className: PropTypes.string
52+
}
53+
3954
export default Region

packages/commerce-sdk-react/src/components/ShopperExperience/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* SPDX-License-Identifier: BSD-3-Clause
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
7+
78
export * from './Component'
8-
export * from './Page'
99
export * from './Region'
10+
export * from './Page'
11+
export * from './prop-types'

packages/template-retail-react-app/app/page-designer/core/types.js packages/commerce-sdk-react/src/components/ShopperExperience/prop-types.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import PropTypes from 'prop-types'
99
/**
1010
* This PropType represents a `component` object from the ShopperExperience API.
1111
*/
12-
export const componentType = PropTypes.shape({
12+
export const componentPropType = PropTypes.shape({
1313
data: PropTypes.object,
1414
id: PropTypes.string,
1515
typeId: PropTypes.string
@@ -18,9 +18,9 @@ export const componentType = PropTypes.shape({
1818
/**
1919
* This PropType represents a `region` object from the ShopperExperience API.
2020
*/
21-
export const regionType = PropTypes.shape({
21+
export const regionPropType = PropTypes.shape({
2222
id: PropTypes.string,
23-
components: PropTypes.arrayOf(componentType)
23+
components: PropTypes.arrayOf(componentPropType)
2424
})
2525

2626
/**
@@ -31,6 +31,6 @@ export const pageType = PropTypes.shape({
3131
description: PropTypes.string,
3232
id: PropTypes.string,
3333
name: PropTypes.string,
34-
regions: PropTypes.arrayOf(regionType),
34+
regions: PropTypes.arrayOf(regionPropType),
3535
typeId: PropTypes.string
3636
})

packages/pwa-kit-dev/src/configs/webpack/config.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,11 @@ const renderer =
412412
devtool: mode === development ? 'eval-source-map' : false,
413413
output: {
414414
path: buildDir,
415-
filename: 'server-renderer.js',
415+
416+
// We want to split the build on local development to reduce memory usage.
417+
// It is required to have a single entry point for the remote server.
418+
// See pwa-kit-runtime/ssr/server/build-remote-server.js render method.
419+
filename: mode === development ? '[name].js' : 'server-renderer.js',
416420
libraryTarget: 'commonjs2'
417421
},
418422
plugins: [

packages/pwa-kit-react-sdk/src/ssr/universal/components/route-component/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ const withErrorHandling = (Wrapped) => {
5656
*/
5757
export const routeComponent = (Wrapped, isPage, locals) => {
5858
const AppConfig = getAppConfig()
59-
const extraArgs = AppConfig.extraGetPropsArgs(locals)
6059

6160
const hocs = AppConfig.getHOCsInUse()
6261
const getPropsEnabled = hocs.indexOf(withLegacyGetProps) >= 0
6362

63+
const extraArgs = getPropsEnabled ? AppConfig.extraGetPropsArgs(locals) : {}
64+
6465
/* istanbul ignore next */
6566
const wrappedComponentName = Wrapped.displayName || Wrapped.name
6667

packages/template-retail-react-app/app/components/_app-config/index.jsx

+1-10
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {getConfig} from 'pwa-kit-runtime/utils/ssr-config'
1818
import {createUrlTemplate} from '../../utils/url'
1919

2020
import {CommerceApiProvider} from 'commerce-sdk-react-preview'
21-
import {withLegacyGetProps} from 'pwa-kit-react-sdk/ssr/universal/components/with-legacy-get-props'
2221
import {withReactQuery} from 'pwa-kit-react-sdk/ssr/universal/components/with-react-query'
2322
import {useCorrelationId} from 'pwa-kit-react-sdk/ssr/universal/hooks'
2423
import {getAppOrigin} from 'pwa-kit-react-sdk/utils/url'
@@ -86,14 +85,6 @@ AppConfig.restore = (locals = {}) => {
8685

8786
AppConfig.freeze = () => undefined
8887

89-
AppConfig.extraGetPropsArgs = (locals = {}) => {
90-
return {
91-
buildUrl: locals.buildUrl,
92-
site: locals.site,
93-
locale: locals.locale,
94-
}
95-
}
96-
9788
AppConfig.propTypes = {
9889
children: PropTypes.node,
9990
locals: PropTypes.object,
@@ -119,4 +110,4 @@ const options = {
119110
}
120111
}
121112

122-
export default withReactQuery(withLegacyGetProps(AppConfig), options)
113+
export default withReactQuery(AppConfig, options)

packages/template-retail-react-app/app/components/_app/index.jsx

+33-40
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {useHistory, useLocation} from 'react-router-dom'
1111
import {getAssetUrl} from 'pwa-kit-react-sdk/ssr/universal/utils'
1212
import {getAppOrigin} from 'pwa-kit-react-sdk/utils/url'
1313
import {getConfig} from 'pwa-kit-runtime/utils/ssr-config'
14-
import {useQueries} from '@tanstack/react-query'
14+
import {useQuery, useQueries} from '@tanstack/react-query'
1515
import {
1616
useAccessToken,
1717
useCategory,
@@ -62,7 +62,6 @@ import {
6262
} from '../../constants'
6363

6464
import Seo from '../seo'
65-
import {resolveLocaleFromUrl, resolveSiteFromUrl} from '../../utils/site-utils'
6665

6766
const onClient = typeof window !== 'undefined'
6867

@@ -99,7 +98,7 @@ const useLazyLoadCategories = () => {
9998
}
10099

101100
const App = (props) => {
102-
const {children, targetLocale = DEFAULT_LOCALE, messages = {}} = props
101+
const {children} = props
103102
const {data: categoriesTree} = useLazyLoadCategories()
104103
const categories = flatten(categoriesTree || {}, 'categories')
105104

@@ -116,6 +115,31 @@ const App = (props) => {
116115

117116
const {isOpen, onOpen, onClose} = useDisclosure()
118117

118+
const targetLocale = getTargetLocale({
119+
getUserPreferredLocales: () => {
120+
// CONFIG: This function should return an array of preferred locales. They can be
121+
// derived from various sources. Below are some examples of those:
122+
//
123+
// - client side: window.navigator.languages
124+
// - the page URL they're on (example.com/en-GB/home)
125+
// - cookie (if their previous preference is saved there)
126+
//
127+
// If this function returns an empty array (e.g. there isn't locale in the page url),
128+
// then the app would use the default locale as the fallback.
129+
130+
// NOTE: Your implementation may differ, this is just what we did.
131+
return [locale?.id || DEFAULT_LOCALE]
132+
},
133+
l10nConfig: site.l10n
134+
})
135+
136+
// Fetch the translation message data using the target locale.
137+
const {data: messages} = useQuery({
138+
queryKey: ['app', 'translationas', 'messages', targetLocale],
139+
queryFn: () => fetchTranslations(targetLocale),
140+
enabled: isServer
141+
})
142+
119143
// Used to conditionally render header/footer for checkout page
120144
const isCheckout = /\/checkout$/.test(location?.pathname)
121145

@@ -201,6 +225,11 @@ const App = (props) => {
201225
<Box className="sf-app" {...styles.container}>
202226
<IntlProvider
203227
onError={(err) => {
228+
if (!messages) {
229+
// During the ssr prepass phase the messages object has not loaded, so we can suppress
230+
// errors during this time.
231+
return
232+
}
204233
if (err.code === 'MISSING_TRANSLATION') {
205234
// NOTE: Remove the console error for missing translations during development,
206235
// as we knew translations would be added later.
@@ -314,44 +343,8 @@ const App = (props) => {
314343
)
315344
}
316345

317-
App.shouldGetProps = () => {
318-
// In this case, we only want to fetch data for the app once, on the server.
319-
return typeof window === 'undefined'
320-
}
321-
322-
App.getProps = async ({res}) => {
323-
const site = resolveSiteFromUrl(res.locals.originalUrl)
324-
const locale = resolveLocaleFromUrl(res.locals.originalUrl)
325-
const l10nConfig = site.l10n
326-
const targetLocale = getTargetLocale({
327-
getUserPreferredLocales: () => {
328-
// CONFIG: This function should return an array of preferred locales. They can be
329-
// derived from various sources. Below are some examples of those:
330-
//
331-
// - client side: window.navigator.languages
332-
// - the page URL they're on (example.com/en-GB/home)
333-
// - cookie (if their previous preference is saved there)
334-
//
335-
// If this function returns an empty array (e.g. there isn't locale in the page url),
336-
// then the app would use the default locale as the fallback.
337-
338-
// NOTE: Your implementation may differ, this is just what we did.
339-
return [locale?.id]
340-
},
341-
l10nConfig
342-
})
343-
const messages = await fetchTranslations(targetLocale)
344-
345-
return {
346-
targetLocale,
347-
messages
348-
}
349-
}
350-
351346
App.propTypes = {
352-
children: PropTypes.node,
353-
targetLocale: PropTypes.string,
354-
messages: PropTypes.object
347+
children: PropTypes.node
355348
}
356349

357350
/**

packages/template-retail-react-app/app/components/_app/index.test.js

-13
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,6 @@ describe('App', () => {
6565
expect(screen.getByText('Any children here')).toBeInTheDocument()
6666
})
6767

68-
test('shouldGetProps returns true only server-side', () => {
69-
windowSpy.mockImplementation(() => undefined)
70-
71-
expect(App.shouldGetProps()).toBe(true)
72-
73-
windowSpy.mockImplementation(() => ({
74-
location: {
75-
origin: 'http://localhost:3000/'
76-
}
77-
}))
78-
expect(App.shouldGetProps()).toBe(false)
79-
})
80-
8168
test('The localized hreflang links exist in the html head', () => {
8269
useMultiSite.mockImplementation(() => resultUseMultiSite)
8370
renderWithProviders(

packages/template-retail-react-app/app/page-designer/README.md

+20-25
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77

88
---
99

10-
This folder contains React components and utilities that render pages from [Page Designer](https://documentation.b2c.commercecloud.salesforce.com/DOC2/topic/com.demandware.dochelp/content/b2c_commerce/topics/page_designer/b2c_creating_pd_pages.html).
10+
This folder contains the React components used when rendering the pages from [Page Designer](https://documentation.b2c.commercecloud.salesforce.com/DOC2/topic/com.demandware.dochelp/content/b2c_commerce/topics/page_designer/b2c_creating_pd_pages.html).
1111

1212
Use this folder to add React components that can render Page Designer components that have been serialized to JSON.
1313

14+
> NOTE: If you are creating components that do not already exist in Page Designer, follow [this](https://documentation.b2c.commercecloud.salesforce.com/DOC1/index.jsp) guide to first create your Page Designer components before creating their matching PWA-Kit React components.
15+
1416
This folder includes components for layout and visualization of images, grids, and carousels.
1517

1618
**By default, Page Designer integration is not enabled in the Retail React App.** Additionally, to utilize the `shopperExperience`
@@ -20,8 +22,7 @@ for more information on configuring your SLAS client.
2022

2123
## Folder Structure
2224

23-
- **`/core`** - Base components for rendering: `<Page>`, `<Region>`, and `<Component>`. Use `<Page>` to render Page Designer content. Use `<Region>` and `<Component>` for creating new assets.
24-
- **`/assets`** - Non-visual components used in Page Designer. Includes `<Image>` and `<ImageWithText>` as well as any other Page Designer assets that you want to use in your PWA-Kit app. If you need to visualize a component, add it here.
25+
- **`/assets`** - Visual components used in Page Designer. Includes `<Image>` and `<ImageWithText>` as well as any other Page Designer assets that you want to use in your PWA-Kit app. If you need to visualize a component, add it here.
2526
- **`/layouts`** - Components responsible for layout. Includes various grids and a `<Carousel>` component.
2627

2728
## Sample Usage
@@ -32,8 +33,10 @@ Create a new file called `app/pages/page-viewer/index.jsx`, and add the followin
3233
// app/pages/page-viewer/index.jsx
3334

3435
import React from 'react'
36+
import {useParams} from 'react-router-dom'
3537
import {Box} from '@chakra-ui/react'
36-
import {Page, pageType} from '../../page-designer'
38+
import {usePage} from 'commerce-sdk-react-preview'
39+
import {Page} from 'commerce-sdk-react-preview/components'
3740
import {ImageTile, ImageWithText} from '../../page-designer/assets'
3841
import {
3942
Carousel,
@@ -59,32 +62,24 @@ const PAGEDESIGNER_TO_COMPONENT = {
5962
'commerce_layouts.mobileGrid3r2c': MobileGrid3r2c
6063
}
6164

62-
const PageViewer = ({page}) => (
63-
<Box layerStyle={'page'}>
64-
<Page page={page} components={PAGEDESIGNER_TO_COMPONENT} />
65-
</Box>
66-
)
67-
68-
PageViewer.getProps = async ({api, params}) => {
69-
const {pageId} = params
70-
const page = await api.shopperExperience.getPage({
71-
parameters: {pageId}
72-
})
73-
74-
if (page.isError) {
75-
let ErrorClass = page.type?.endsWith('page-not-found') ? HTTPNotFound : HTTPError
76-
throw new ErrorClass(page.detail)
65+
const PageViewer = () => {
66+
const {pageId} = useParams()
67+
const {data: page, error}= usePage({parameters: {pageId}})
68+
69+
if (error) {
70+
let ErrorClass = error.response?.status === 404 ? HTTPNotFound : HTTPError
71+
throw new ErrorClass(error.response?.statusText)
7772
}
7873

79-
return {page}
74+
return (
75+
<Box layerStyle={'page'}>
76+
<Page page={page} components={PAGEDESIGNER_TO_COMPONENT} />
77+
</Box>
78+
)
8079
}
8180

8281
PageViewer.displayName = 'PageViewer'
8382

84-
PageViewer.propTypes = {
85-
page: pageType.isRequired
86-
}
87-
8883
export default PageViewer
8984
```
9085
@@ -104,4 +99,4 @@ Open `app/routes.jsx` and add a route for `<PageViewer>`:
10499
+ },
105100
```
106101
107-
Using the local development server, you can now see Page Designer pages rendered in React.js at `http://localhost:3000/page-viewer/:pageid` by providing their `pageid`.
102+
Using the local development server, you can now see Page Designer pages rendered in React.js at `http://localhost:3000/page-viewer/:pageid` by providing their `pageid` defined in Business Manager.

packages/template-retail-react-app/app/page-designer/core/index.js packages/template-retail-react-app/app/page-designer/assets/index.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,5 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77

8-
export * from './component'
9-
export * from './page'
10-
export * from './region'
11-
export * from './types'
8+
export * from './image-tile'
9+
export * from './image-with-text'

0 commit comments

Comments
 (0)