Skip to content

Commit da5245b

Browse files
@W-20276592: Address autocompletion feature (#3484)
* @W-20276592: Address autocompletion feature - Implemented address autocomplete dropdown using mock addresses - Auto-fill address on choosing suggestion from dropdown - Added Google API integration - Sourced GCP API Key from Configuration API - Added ShopperConfigurations hook for API key management - Fixed accessibility, cleanup, and introduced caching by component - Added translations and various fixes * @W-20276592: clean up Signed-off-by: d.phan <d.phan@salesforce.com> * update API key rules Signed-off-by: d.phan <d.phan@salesforce.com> * quick fix Signed-off-by: d.phan <d.phan@salesforce.com> --------- Signed-off-by: d.phan <d.phan@salesforce.com>
1 parent 7880214 commit da5245b

39 files changed

+3079
-16
lines changed

packages/commerce-sdk-react/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## v4.3.0-dev (Nov 05, 2025)
22

3+
- Upgrade to commerce-sdk-isomorphic v4.2.0 and introduce Shopper Configurations SCAPI integration [#3071](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3071)
4+
35
## v4.2.0 (Nov 04, 2025)
46

57
- Upgrade to commerce-sdk-isomorphic v4.0.1 [#3449](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3449)

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

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

packages/commerce-sdk-react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"version": "node ./scripts/version.js"
4141
},
4242
"dependencies": {
43-
"commerce-sdk-isomorphic": "^4.0.1",
43+
"commerce-sdk-isomorphic": "4.2.0",
4444
"js-cookie": "^3.0.1",
4545
"jwt-decode": "^4.0.0"
4646
},

packages/commerce-sdk-react/src/auth/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ const DATA_MAP: AuthDataMap = {
174174
storageType: 'local',
175175
key: 'idp_access_token'
176176
},
177+
idp_refresh_token: {
178+
storageType: 'local',
179+
key: 'idp_refresh_token'
180+
},
181+
dnt: {
182+
storageType: 'local',
183+
key: 'dnt'
184+
},
177185
token_type: {
178186
storageType: 'local',
179187
key: 'token_type'

packages/commerce-sdk-react/src/constant.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,6 @@ export const CLIENT_KEYS = {
5656
SHOPPER_PROMOTIONS: 'shopperPromotions',
5757
SHOPPER_SEARCH: 'shopperSearch',
5858
SHOPPER_SEO: 'shopperSeo',
59-
SHOPPER_STORES: 'shopperStores'
59+
SHOPPER_STORES: 'shopperStores',
60+
SHOPPER_CONFIGURATIONS: 'shopperConfigurations'
6061
} as const
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2023, 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 {CLIENT_KEYS} from '../../constant'
8+
import {ApiClients, CacheUpdateMatrix} from '../types'
9+
10+
const CLIENT_KEY = CLIENT_KEYS.SHOPPER_CONFIGURATIONS
11+
type Client = NonNullable<ApiClients[typeof CLIENT_KEY]>
12+
13+
// ShopperConfigurations API is primarily for reading configuration data
14+
// No mutations are currently supported
15+
export const cacheUpdateMatrix: CacheUpdateMatrix<Client> = {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) 2023, 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 {useConfigurations} from './query'
8+
9+
describe('ShopperConfigurations', () => {
10+
describe('useConfigurations', () => {
11+
it('should be defined', () => {
12+
expect(useConfigurations).toBeDefined()
13+
})
14+
})
15+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*
2+
* Copyright (c) 2023, 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+
export * from './query'
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2023, 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 nock from 'nock'
8+
import {
9+
mockQueryEndpoint,
10+
renderHookWithProviders,
11+
waitAndExpectError,
12+
waitAndExpectSuccess,
13+
createQueryClient
14+
} from '../../test-utils'
15+
16+
import {Argument} from '../types'
17+
import * as queries from './query'
18+
19+
jest.mock('../../auth/index.ts', () => {
20+
const {default: mockAuth} = jest.requireActual('../../auth/index.ts')
21+
mockAuth.prototype.ready = jest.fn().mockResolvedValue({access_token: 'access_token'})
22+
return mockAuth
23+
})
24+
25+
type Queries = typeof queries
26+
const configurationsEndpoint = '/organizations/'
27+
// Not all endpoints use all parameters, but unused parameters are safely discarded
28+
const OPTIONS: Argument<Queries[keyof Queries]> = {
29+
parameters: {organizationId: 'f_ecom_zzrmy_orgf_001'}
30+
}
31+
32+
// Mock data for configurations
33+
const mockConfigurationsData = {
34+
configurations: [
35+
{
36+
id: 'gcp',
37+
value: 'test-gcp-api-key'
38+
},
39+
{
40+
id: 'einstein',
41+
value: 'test-einstein-api-key'
42+
}
43+
]
44+
}
45+
46+
describe('Shopper Configurations query hooks', () => {
47+
beforeEach(() => nock.cleanAll())
48+
afterEach(() => {
49+
expect(nock.pendingMocks()).toHaveLength(0)
50+
})
51+
52+
test('`useConfigurations` has meta.displayName defined', async () => {
53+
mockQueryEndpoint(configurationsEndpoint, mockConfigurationsData)
54+
const queryClient = createQueryClient()
55+
const {result} = renderHookWithProviders(
56+
() => {
57+
return queries.useConfigurations(OPTIONS)
58+
},
59+
{queryClient}
60+
)
61+
await waitAndExpectSuccess(() => result.current)
62+
expect(queryClient.getQueryCache().getAll()[0].meta?.displayName).toBe('useConfigurations')
63+
})
64+
65+
test('`useConfigurations` returns data on success', async () => {
66+
mockQueryEndpoint(configurationsEndpoint, mockConfigurationsData)
67+
const {result} = renderHookWithProviders(() => {
68+
return queries.useConfigurations(OPTIONS)
69+
})
70+
await waitAndExpectSuccess(() => result.current)
71+
expect(result.current.data).toEqual(mockConfigurationsData)
72+
})
73+
74+
test('`useConfigurations` returns error on error', async () => {
75+
mockQueryEndpoint(configurationsEndpoint, {}, 400)
76+
const {result} = renderHookWithProviders(() => {
77+
return queries.useConfigurations(OPTIONS)
78+
})
79+
await waitAndExpectError(() => result.current)
80+
})
81+
82+
test('`useConfigurations` handles 500 server error', async () => {
83+
mockQueryEndpoint(configurationsEndpoint, {}, 500)
84+
const {result} = renderHookWithProviders(() => {
85+
return queries.useConfigurations(OPTIONS)
86+
})
87+
await waitAndExpectError(() => result.current)
88+
})
89+
})
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2023, 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 {UseQueryResult} from '@tanstack/react-query'
8+
import {ShopperConfigurations} from 'commerce-sdk-isomorphic'
9+
import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types'
10+
import {useQuery} from '../useQuery'
11+
import {mergeOptions, omitNullableParameters, pickValidParams} from '../utils'
12+
import * as queryKeyHelpers from './queryKeyHelpers'
13+
import {CLIENT_KEYS} from '../../constant'
14+
import useCommerceApi from '../useCommerceApi'
15+
16+
const CLIENT_KEY = CLIENT_KEYS.SHOPPER_CONFIGURATIONS
17+
type Client = NonNullable<ApiClients[typeof CLIENT_KEY]>
18+
19+
/**
20+
* Gets configuration information that encompasses toggles, preferences, and configuration that allow the application to be reactive to changes performed by the merchant, admin, or support engineer.
21+
*
22+
* @group ShopperConfigurations
23+
* @category Query
24+
* @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters.
25+
* @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set.
26+
* @returns A TanStack Query query hook with data from the Shopper Configurations `getConfigurations` endpoint.
27+
* @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-configurations?meta=getConfigurations| Salesforce Developer Center} for more information about the API endpoint.
28+
* @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperconfigurations.shopperconfigurations-1.html#getconfigurations | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type.
29+
* @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value.
30+
*/
31+
export const useConfigurations = (
32+
apiOptions: NullableParameters<Argument<Client['getConfigurations']>>,
33+
queryOptions: ApiQueryOptions<Client['getConfigurations']> = {}
34+
): UseQueryResult<DataType<Client['getConfigurations']>> => {
35+
type Options = Argument<Client['getConfigurations']>
36+
type Data = DataType<Client['getConfigurations']>
37+
const client = useCommerceApi(CLIENT_KEY)
38+
const methodName = 'getConfigurations'
39+
const requiredParameters = ShopperConfigurations.paramKeys[`${methodName}Required`]
40+
41+
// Parameters can be set in `apiOptions` or `client.clientConfig`
42+
// we must merge them in order to generate the correct query key.
43+
const netOptions = omitNullableParameters(mergeOptions(client, apiOptions || {}))
44+
const parameters = pickValidParams(
45+
netOptions.parameters,
46+
ShopperConfigurations.paramKeys[methodName]
47+
)
48+
const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters)
49+
// We don't use `netOptions` here because we manipulate the options in `useQuery`.
50+
const method = async (options: Options) => await client[methodName](options)
51+
52+
queryOptions.meta = {
53+
displayName: 'useConfigurations',
54+
...queryOptions.meta
55+
}
56+
57+
// For some reason, if we don't explicitly set these generic parameters, the inferred type for
58+
// `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why.
59+
return useQuery<Client, Options, Data>({...netOptions, parameters}, queryOptions, {
60+
method,
61+
queryKey,
62+
requiredParameters
63+
})
64+
}

0 commit comments

Comments
 (0)