@salesforce/retail-react-app 8.3.0-nightly-20251209080224 → 8.3.0-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,12 +11,14 @@ import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-cur
11
11
  import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket'
12
12
  import {STORE_LOCATOR_IS_ENABLED} from '@salesforce/retail-react-app/app/constants'
13
13
  import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
14
+ import {useConfigurations} from '@salesforce/commerce-sdk-react'
14
15
 
15
16
  const CheckoutContext = React.createContext()
16
17
 
17
18
  export const CheckoutProvider = ({children}) => {
18
19
  const {data: customer} = useCurrentCustomer()
19
20
  const {data: basket, derivedData, isLoading: isBasketLoading} = useCurrentBasket()
21
+ const {data: configurations} = useConfigurations()
20
22
  const einstein = useEinstein()
21
23
  const [step, setStep] = useState()
22
24
  const storeLocatorEnabled = getConfig()?.app?.storeLocatorEnabled ?? STORE_LOCATOR_IS_ENABLED
@@ -105,7 +107,8 @@ export const CheckoutProvider = ({children}) => {
105
107
  step,
106
108
  STEPS,
107
109
  goToNextStep,
108
- goToStep
110
+ goToStep,
111
+ configurations
109
112
  }
110
113
 
111
114
  return <CheckoutContext.Provider value={value}>{children}</CheckoutContext.Provider>
@@ -0,0 +1,45 @@
1
+ /*
2
+ * Copyright (c) 2025, Salesforce, Inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ import React from 'react'
8
+ import PropTypes from 'prop-types'
9
+ import {APIProvider} from '@vis.gl/react-google-maps'
10
+ import {useCheckout} from '@salesforce/retail-react-app/app/pages/checkout/util/checkout-context'
11
+ import {getConfig} from '@salesforce/pwa-kit-runtime/utils/ssr-config'
12
+
13
+ /**
14
+ * Resolve the Google Cloud API key from the configurations
15
+ * User-defined environment variable (GOOGLE_CLOUD_API_KEY) always take precedence over the platform provided key
16
+ * If no custom key is set, the platform provided key is used (only if feature toggle is enabled in production)
17
+ */
18
+ function resolveGoogleCloudAPIKey(configurations) {
19
+ const customKey = getConfig()?.app?.googleCloudAPI?.apiKey
20
+ if (customKey) {
21
+ return customKey
22
+ }
23
+
24
+ const platformProvidedKey = configurations?.configurations?.find(
25
+ (config) => config.id === 'gcp'
26
+ )?.value
27
+
28
+ return platformProvidedKey
29
+ }
30
+
31
+ export const GoogleAPIProvider = ({children}) => {
32
+ const {configurations} = useCheckout()
33
+ const googleCloudAPIKey = resolveGoogleCloudAPIKey(configurations)
34
+
35
+ return googleCloudAPIKey ? (
36
+ <APIProvider apiKey={googleCloudAPIKey}>{children}</APIProvider>
37
+ ) : (
38
+ children
39
+ )
40
+ }
41
+
42
+ GoogleAPIProvider.propTypes = {
43
+ googleCloudAPIKey: PropTypes.string,
44
+ children: PropTypes.node.isRequired
45
+ }
@@ -0,0 +1,395 @@
1
+ /*
2
+ * Copyright (c) 2025, Salesforce, Inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ import React from 'react'
9
+ import {render} from '@testing-library/react'
10
+ import {GoogleAPIProvider} from '@salesforce/retail-react-app/app/pages/checkout/util/google-api-provider'
11
+
12
+ // Mock the dependencies
13
+ const mockUseCheckout = jest.fn()
14
+ const mockGetConfig = jest.fn()
15
+
16
+ jest.mock('@salesforce/retail-react-app/app/pages/checkout/util/checkout-context', () => ({
17
+ useCheckout: () => mockUseCheckout()
18
+ }))
19
+
20
+ jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-config', () => ({
21
+ getConfig: () => mockGetConfig()
22
+ }))
23
+
24
+ jest.mock('@vis.gl/react-google-maps', () => ({
25
+ // eslint-disable-next-line react/prop-types
26
+ APIProvider: ({children, apiKey}) => (
27
+ <div data-testid="api-provider" data-api-key={apiKey}>
28
+ {children}
29
+ </div>
30
+ )
31
+ }))
32
+
33
+ describe('GoogleAPIProvider', () => {
34
+ beforeEach(() => {
35
+ jest.clearAllMocks()
36
+ })
37
+
38
+ afterEach(() => {
39
+ jest.resetModules()
40
+ })
41
+
42
+ test('should render children directly when platform provided key is not present and no custom key', () => {
43
+ mockUseCheckout.mockReturnValue({
44
+ configurations: {
45
+ configurations: []
46
+ }
47
+ })
48
+
49
+ mockGetConfig.mockReturnValue({
50
+ app: {}
51
+ })
52
+
53
+ const {getByTestId, queryByTestId} = render(
54
+ <GoogleAPIProvider>
55
+ <div data-testid="test-child">Test Child</div>
56
+ </GoogleAPIProvider>
57
+ )
58
+
59
+ expect(getByTestId('test-child')).toBeInTheDocument()
60
+ expect(queryByTestId('api-provider')).not.toBeInTheDocument()
61
+ })
62
+
63
+ test('should render children directly when configurations is undefined and no custom key', () => {
64
+ mockUseCheckout.mockReturnValue({
65
+ configurations: undefined
66
+ })
67
+
68
+ mockGetConfig.mockReturnValue({
69
+ app: {}
70
+ })
71
+
72
+ const {getByTestId, queryByTestId} = render(
73
+ <GoogleAPIProvider>
74
+ <div data-testid="test-child">Test Child</div>
75
+ </GoogleAPIProvider>
76
+ )
77
+
78
+ expect(getByTestId('test-child')).toBeInTheDocument()
79
+ expect(queryByTestId('api-provider')).not.toBeInTheDocument()
80
+ })
81
+
82
+ test('should render children directly when configurations is null and no custom key', () => {
83
+ mockUseCheckout.mockReturnValue({
84
+ configurations: null
85
+ })
86
+
87
+ mockGetConfig.mockReturnValue({
88
+ app: {}
89
+ })
90
+
91
+ const {getByTestId, queryByTestId} = render(
92
+ <GoogleAPIProvider>
93
+ <div data-testid="test-child">Test Child</div>
94
+ </GoogleAPIProvider>
95
+ )
96
+
97
+ expect(getByTestId('test-child')).toBeInTheDocument()
98
+ expect(queryByTestId('api-provider')).not.toBeInTheDocument()
99
+ })
100
+
101
+ test('should wrap children in APIProvider when platform provided key is available and no custom key is configured', () => {
102
+ mockUseCheckout.mockReturnValue({
103
+ configurations: {
104
+ configurations: [
105
+ {
106
+ id: 'gcp',
107
+ value: 'platform-provided-key'
108
+ }
109
+ ]
110
+ }
111
+ })
112
+
113
+ mockGetConfig.mockReturnValue({
114
+ app: {}
115
+ })
116
+
117
+ const {getByTestId} = render(
118
+ <GoogleAPIProvider>
119
+ <div data-testid="test-child">Test Child</div>
120
+ </GoogleAPIProvider>
121
+ )
122
+
123
+ const apiProvider = getByTestId('api-provider')
124
+ expect(apiProvider).toBeInTheDocument()
125
+ expect(apiProvider).toHaveAttribute('data-api-key', 'platform-provided-key')
126
+ expect(getByTestId('test-child')).toBeInTheDocument()
127
+ })
128
+
129
+ test('should wrap children in APIProvider when platform provided key is available and custom key is not configured', () => {
130
+ mockUseCheckout.mockReturnValue({
131
+ configurations: {
132
+ configurations: [
133
+ {
134
+ id: 'gcp',
135
+ value: 'platform-provided-key'
136
+ }
137
+ ]
138
+ }
139
+ })
140
+
141
+ mockGetConfig.mockReturnValue({
142
+ app: {}
143
+ })
144
+
145
+ const {getByTestId} = render(
146
+ <GoogleAPIProvider>
147
+ <div data-testid="test-child">Test Child</div>
148
+ </GoogleAPIProvider>
149
+ )
150
+
151
+ const apiProvider = getByTestId('api-provider')
152
+ expect(apiProvider).toBeInTheDocument()
153
+ expect(apiProvider).toHaveAttribute('data-api-key', 'platform-provided-key')
154
+ expect(getByTestId('test-child')).toBeInTheDocument()
155
+ })
156
+
157
+ test('should wrap children in APIProvider with platform key when custom key is empty string', () => {
158
+ mockUseCheckout.mockReturnValue({
159
+ configurations: {
160
+ configurations: [
161
+ {
162
+ id: 'gcp',
163
+ value: 'platform-provided-key'
164
+ }
165
+ ]
166
+ }
167
+ })
168
+
169
+ mockGetConfig.mockReturnValue({
170
+ app: {
171
+ googleCloudAPI: {
172
+ apiKey: ''
173
+ }
174
+ }
175
+ })
176
+
177
+ const {getByTestId} = render(
178
+ <GoogleAPIProvider>
179
+ <div data-testid="test-child">Test Child</div>
180
+ </GoogleAPIProvider>
181
+ )
182
+
183
+ const apiProvider = getByTestId('api-provider')
184
+ expect(apiProvider).toBeInTheDocument()
185
+ expect(apiProvider).toHaveAttribute('data-api-key', 'platform-provided-key')
186
+ expect(getByTestId('test-child')).toBeInTheDocument()
187
+ })
188
+
189
+ test('should wrap children in APIProvider with platform key when custom key is null', () => {
190
+ mockUseCheckout.mockReturnValue({
191
+ configurations: {
192
+ configurations: [
193
+ {
194
+ id: 'gcp',
195
+ value: 'platform-provided-key'
196
+ }
197
+ ]
198
+ }
199
+ })
200
+
201
+ mockGetConfig.mockReturnValue({
202
+ app: {
203
+ googleCloudAPI: {
204
+ apiKey: null
205
+ }
206
+ }
207
+ })
208
+
209
+ const {getByTestId} = render(
210
+ <GoogleAPIProvider>
211
+ <div data-testid="test-child">Test Child</div>
212
+ </GoogleAPIProvider>
213
+ )
214
+
215
+ const apiProvider = getByTestId('api-provider')
216
+ expect(apiProvider).toBeInTheDocument()
217
+ expect(apiProvider).toHaveAttribute('data-api-key', 'platform-provided-key')
218
+ expect(getByTestId('test-child')).toBeInTheDocument()
219
+ })
220
+
221
+ test('should wrap children in APIProvider with platform key when custom key is undefined', () => {
222
+ mockUseCheckout.mockReturnValue({
223
+ configurations: {
224
+ configurations: [
225
+ {
226
+ id: 'gcp',
227
+ value: 'platform-provided-key'
228
+ }
229
+ ]
230
+ }
231
+ })
232
+
233
+ mockGetConfig.mockReturnValue({
234
+ app: {
235
+ googleCloudAPI: {}
236
+ }
237
+ })
238
+
239
+ const {getByTestId} = render(
240
+ <GoogleAPIProvider>
241
+ <div data-testid="test-child">Test Child</div>
242
+ </GoogleAPIProvider>
243
+ )
244
+
245
+ const apiProvider = getByTestId('api-provider')
246
+ expect(apiProvider).toBeInTheDocument()
247
+ expect(apiProvider).toHaveAttribute('data-api-key', 'platform-provided-key')
248
+ expect(getByTestId('test-child')).toBeInTheDocument()
249
+ })
250
+
251
+ test('should wrap children in APIProvider with custom key when custom key is configured', () => {
252
+ mockUseCheckout.mockReturnValue({
253
+ configurations: {
254
+ configurations: [
255
+ {
256
+ id: 'gcp',
257
+ value: 'platform-provided-key'
258
+ }
259
+ ]
260
+ }
261
+ })
262
+
263
+ mockGetConfig.mockReturnValue({
264
+ app: {
265
+ googleCloudAPI: {
266
+ apiKey: 'custom-api-key'
267
+ }
268
+ }
269
+ })
270
+
271
+ const {getByTestId} = render(
272
+ <GoogleAPIProvider>
273
+ <div data-testid="test-child">Test Child</div>
274
+ </GoogleAPIProvider>
275
+ )
276
+
277
+ const apiProvider = getByTestId('api-provider')
278
+ expect(apiProvider).toBeInTheDocument()
279
+ expect(apiProvider).toHaveAttribute('data-api-key', 'custom-api-key')
280
+ expect(getByTestId('test-child')).toBeInTheDocument()
281
+ })
282
+
283
+ test('should wrap children in APIProvider with custom key when custom key is configured and platform key is not present', () => {
284
+ mockUseCheckout.mockReturnValue({
285
+ configurations: {
286
+ configurations: []
287
+ }
288
+ })
289
+
290
+ mockGetConfig.mockReturnValue({
291
+ app: {
292
+ googleCloudAPI: {
293
+ apiKey: 'custom-api-key'
294
+ }
295
+ }
296
+ })
297
+
298
+ const {getByTestId} = render(
299
+ <GoogleAPIProvider>
300
+ <div data-testid="test-child">Test Child</div>
301
+ </GoogleAPIProvider>
302
+ )
303
+
304
+ const apiProvider = getByTestId('api-provider')
305
+ expect(apiProvider).toBeInTheDocument()
306
+ expect(apiProvider).toHaveAttribute('data-api-key', 'custom-api-key')
307
+ expect(getByTestId('test-child')).toBeInTheDocument()
308
+ })
309
+
310
+ test('should wrap children in APIProvider with custom key when custom key is configured and configurations is undefined', () => {
311
+ mockUseCheckout.mockReturnValue({
312
+ configurations: undefined
313
+ })
314
+
315
+ mockGetConfig.mockReturnValue({
316
+ app: {
317
+ googleCloudAPI: {
318
+ apiKey: 'custom-api-key'
319
+ }
320
+ }
321
+ })
322
+
323
+ const {getByTestId} = render(
324
+ <GoogleAPIProvider>
325
+ <div data-testid="test-child">Test Child</div>
326
+ </GoogleAPIProvider>
327
+ )
328
+
329
+ const apiProvider = getByTestId('api-provider')
330
+ expect(apiProvider).toBeInTheDocument()
331
+ expect(apiProvider).toHaveAttribute('data-api-key', 'custom-api-key')
332
+ expect(getByTestId('test-child')).toBeInTheDocument()
333
+ })
334
+
335
+ test('should wrap children in APIProvider with custom key when custom key is configured and configurations is null', () => {
336
+ mockUseCheckout.mockReturnValue({
337
+ configurations: null
338
+ })
339
+
340
+ mockGetConfig.mockReturnValue({
341
+ app: {
342
+ googleCloudAPI: {
343
+ apiKey: 'custom-api-key'
344
+ }
345
+ }
346
+ })
347
+
348
+ const {getByTestId} = render(
349
+ <GoogleAPIProvider>
350
+ <div data-testid="test-child">Test Child</div>
351
+ </GoogleAPIProvider>
352
+ )
353
+
354
+ const apiProvider = getByTestId('api-provider')
355
+ expect(apiProvider).toBeInTheDocument()
356
+ expect(apiProvider).toHaveAttribute('data-api-key', 'custom-api-key')
357
+ expect(getByTestId('test-child')).toBeInTheDocument()
358
+ })
359
+
360
+ test('should handle multiple configurations and find the correct gcp one', () => {
361
+ mockUseCheckout.mockReturnValue({
362
+ configurations: {
363
+ configurations: [
364
+ {
365
+ id: 'other-config',
366
+ value: 'other-value'
367
+ },
368
+ {
369
+ id: 'gcp',
370
+ value: 'platform-provided-key'
371
+ },
372
+ {
373
+ id: 'another-config',
374
+ value: 'another-value'
375
+ }
376
+ ]
377
+ }
378
+ })
379
+
380
+ mockGetConfig.mockReturnValue({
381
+ app: {}
382
+ })
383
+
384
+ const {getByTestId} = render(
385
+ <GoogleAPIProvider>
386
+ <div data-testid="test-child">Test Child</div>
387
+ </GoogleAPIProvider>
388
+ )
389
+
390
+ const apiProvider = getByTestId('api-provider')
391
+ expect(apiProvider).toBeInTheDocument()
392
+ expect(apiProvider).toHaveAttribute('data-api-key', 'platform-provided-key')
393
+ expect(getByTestId('test-child')).toBeInTheDocument()
394
+ })
395
+ })
package/app/ssr.js CHANGED
@@ -359,13 +359,17 @@ const {handler} = runtime.createHandler(options, (app) => {
359
359
  ],
360
360
  'script-src': [
361
361
  // Used by the service worker in /worker/main.js
362
- 'storage.googleapis.com'
362
+ 'storage.googleapis.com',
363
+ 'maps.googleapis.com',
364
+ 'places.googleapis.com'
363
365
  ],
364
366
  'connect-src': [
365
367
  // Connect to Einstein APIs
366
368
  'api.cquotient.com',
367
369
  // Connect to DataCloud APIs
368
370
  '*.c360a.salesforce.com',
371
+ 'maps.googleapis.com',
372
+ 'places.googleapis.com',
369
373
  // Connect to SCRT2 URLs
370
374
  '*.salesforce-scrt.com'
371
375
  ],
@@ -407,6 +407,12 @@
407
407
  "value": "You Might Also Like"
408
408
  }
409
409
  ],
410
+ "addressSuggestionDropdown.suggested": [
411
+ {
412
+ "type": 0,
413
+ "value": "SUGGESTED"
414
+ }
415
+ ],
410
416
  "auth_modal.button.close.assistive_msg": [
411
417
  {
412
418
  "type": 0,
@@ -407,6 +407,12 @@
407
407
  "value": "You Might Also Like"
408
408
  }
409
409
  ],
410
+ "addressSuggestionDropdown.suggested": [
411
+ {
412
+ "type": 0,
413
+ "value": "SUGGESTED"
414
+ }
415
+ ],
410
416
  "auth_modal.button.close.assistive_msg": [
411
417
  {
412
418
  "type": 0,
@@ -839,6 +839,20 @@
839
839
  "value": "]"
840
840
  }
841
841
  ],
842
+ "addressSuggestionDropdown.suggested": [
843
+ {
844
+ "type": 0,
845
+ "value": "["
846
+ },
847
+ {
848
+ "type": 0,
849
+ "value": "ŞŬƓƓḖŞŦḖḒ"
850
+ },
851
+ {
852
+ "type": 0,
853
+ "value": "]"
854
+ }
855
+ ],
842
856
  "auth_modal.button.close.assistive_msg": [
843
857
  {
844
858
  "type": 0,
@@ -10499,4 +10513,4 @@
10499
10513
  "value": "]"
10500
10514
  }
10501
10515
  ]
10502
- }
10516
+ }
@@ -41,7 +41,7 @@ export default {
41
41
  marginRight: 2,
42
42
  marginLeft: 0,
43
43
  marginBottom: 2,
44
- color: `${props.selected ? 'black' : 'gray.200'}`,
44
+ color: `${props.selected ? 'black' : 'gray.600'}`,
45
45
  border: `${props.selected ? '1px' : '0'}`,
46
46
  _hover: {
47
47
  borderColor: `${props.selected ? 'black' : 'gray.200'}`,