-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2547 from Shopify/rc/unstable-release
[Checkout UI extensions] Add Localized Fields API to `Unstable`
- Loading branch information
Showing
13 changed files
with
308 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@shopify/ui-extensions-react': minor | ||
'@shopify/ui-extensions': minor | ||
--- | ||
|
||
Adds the Localized Fields API and `useLocalizedFields` hook. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
packages/ui-extensions-react/src/surfaces/checkout/hooks/localized-fields.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import type { | ||
LocalizedField, | ||
LocalizedFieldKey, | ||
RenderExtensionTarget, | ||
} from '@shopify/ui-extensions/checkout'; | ||
|
||
import {ScopeNotGrantedError} from '../errors'; | ||
|
||
import {useApi} from './api'; | ||
import {useSubscription} from './subscription'; | ||
|
||
/** | ||
* Returns the current localized fields and | ||
* re-renders your component if the values change. | ||
*/ | ||
export function useLocalizedFields< | ||
Target extends RenderExtensionTarget = RenderExtensionTarget, | ||
>(keys: LocalizedFieldKey[]): LocalizedField[] | undefined { | ||
const {localizedFields} = useApi<Target>(); | ||
|
||
if (!localizedFields) { | ||
throw new ScopeNotGrantedError( | ||
'Using localized fields requires having personal customer data permissions granted to your app.', | ||
); | ||
} | ||
|
||
return useSubscription(localizedFields)?.filter(({key}) => | ||
keys.includes(key), | ||
); | ||
} |
78 changes: 78 additions & 0 deletions
78
packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/localized-fields.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import type {LocalizedField} from '@shopify/ui-extensions/checkout'; | ||
|
||
import {useLocalizedFields} from '../localized-fields'; | ||
|
||
import {mount, createMockStatefulRemoteSubscribable} from './mount'; | ||
|
||
describe('useLocalizedFields', () => { | ||
it('throws an error if localized fields are not available', () => { | ||
const extensionApi = { | ||
localizedFields: undefined, | ||
}; | ||
|
||
expect(() => { | ||
mount.hook(() => useLocalizedFields(['TAX_CREDENTIAL_BR']), { | ||
extensionApi, | ||
}); | ||
}).toThrow( | ||
'Using localized fields requires having personal customer data permissions granted to your app.', | ||
); | ||
}); | ||
|
||
it('returns empty if no localized fields match the passed keys', () => { | ||
const localizedFields: LocalizedField[] = [ | ||
{ | ||
key: 'TAX_CREDENTIAL_BR', | ||
title: 'CPF/CNPJ', | ||
value: 'test-value', | ||
}, | ||
{ | ||
key: 'SHIPPING_CREDENTIAL_BR', | ||
title: 'CPF/CNPJ', | ||
value: 'test-value', | ||
}, | ||
]; | ||
|
||
const extensionApi = { | ||
localizedFields: createMockStatefulRemoteSubscribable(localizedFields), | ||
}; | ||
|
||
const {value} = mount.hook( | ||
() => useLocalizedFields(['TAX_CREDENTIAL_ES']), | ||
{extensionApi}, | ||
); | ||
|
||
expect(value).toStrictEqual([]); | ||
}); | ||
|
||
it('returns an array of localized fields that match the passsed keys', () => { | ||
const localizedFields: LocalizedField[] = [ | ||
{ | ||
key: 'TAX_CREDENTIAL_MX', | ||
title: 'Tax credential MX', | ||
value: 'test-value', | ||
}, | ||
{ | ||
key: 'SHIPPING_CREDENTIAL_MX', | ||
title: 'Shipping credential MX', | ||
value: 'test-value', | ||
}, | ||
{ | ||
key: 'TAX_CREDENTIAL_USE_MX', | ||
title: 'Tax credential use MX', | ||
value: 'test-value', | ||
}, | ||
]; | ||
|
||
const extensionApi = { | ||
localizedFields: createMockStatefulRemoteSubscribable(localizedFields), | ||
}; | ||
|
||
const {value} = mount.hook( | ||
() => useLocalizedFields(['TAX_CREDENTIAL_MX', 'TAX_CREDENTIAL_USE_MX']), | ||
{extensionApi}, | ||
); | ||
|
||
expect(value).toMatchObject([localizedFields[0], localizedFields[2]]); | ||
}); | ||
}); |
34 changes: 34 additions & 0 deletions
34
packages/ui-extensions/docs/surfaces/checkout/reference/apis/localized-fields.doc.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; | ||
|
||
import { | ||
getExample, | ||
getLinksByTag, | ||
STANDARD_API_PROPERTIES_DESCRIPTION, | ||
REQUIRES_PROTECTED_CUSTOMER_DATA, | ||
} from '../helper.docs'; | ||
|
||
const data: ReferenceEntityTemplateSchema = { | ||
name: 'Localized Fields', | ||
description: 'The API for interacting with localized fields.', | ||
isVisualComponent: false, | ||
requires: REQUIRES_PROTECTED_CUSTOMER_DATA, | ||
category: 'APIs', | ||
type: 'API', | ||
definitions: [ | ||
{ | ||
title: 'StandardApi', | ||
description: STANDARD_API_PROPERTIES_DESCRIPTION, | ||
type: 'Docs_Standard_LocalizedFieldsApi', | ||
}, | ||
{ | ||
title: 'useLocalizedFields', | ||
description: | ||
'Returns the current localized fields and re-renders your component if the values change.', | ||
type: 'UseLocalizedFieldsGeneratedType', | ||
}, | ||
], | ||
defaultExample: getExample('localized-fields/default', ['jsx', 'js']), | ||
related: getLinksByTag('apis'), | ||
}; | ||
|
||
export default data; |
45 changes: 45 additions & 0 deletions
45
...-extensions/docs/surfaces/checkout/reference/examples/localized-fields/default.example.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import {extension} from '@shopify/ui-extensions/checkout'; | ||
|
||
// 1. Choose an extension target | ||
export default extension( | ||
'purchase.checkout.block.render', | ||
(root, {buyerJourney, localizedFields}) => { | ||
let taxIdField; | ||
|
||
localizedFields?.subscribe( | ||
(localizedFields) => { | ||
// 2. Access localized field values | ||
taxIdField = localizedFields?.find( | ||
({key}) => | ||
key.startsWith('TAX_CREDENTIAL'), | ||
); | ||
}, | ||
); | ||
|
||
// 3. Validate localized field values | ||
buyerJourney.intercept( | ||
({canBlockProgress}) => { | ||
return canBlockProgress && | ||
taxIdField && | ||
(!taxIdField.value || | ||
taxIdField.value.length > 10) | ||
? { | ||
behavior: 'block', | ||
reason: 'Invalid tax ID', | ||
errors: [ | ||
{ | ||
message: `${taxIdField.title} is required and | ||
cannot exceed 10 characters in length`, | ||
// Show an error under the localized field or | ||
// exclude target to show at the top of the page | ||
target: `$.cart.localizedField.${taxIdField.key}`, | ||
}, | ||
], | ||
} | ||
: { | ||
behavior: 'allow', | ||
}; | ||
}, | ||
); | ||
}, | ||
); |
46 changes: 46 additions & 0 deletions
46
...extensions/docs/surfaces/checkout/reference/examples/localized-fields/default.example.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { | ||
reactExtension, | ||
useBuyerJourneyIntercept, | ||
useLocalizedFields, | ||
} from '@shopify/ui-extensions-react/checkout'; | ||
|
||
export default reactExtension( | ||
'purchase.checkout.block.render', | ||
() => <Extension />, | ||
); | ||
|
||
function Extension() { | ||
// 1. Access localized field values | ||
const localizedFields = useLocalizedFields([ | ||
'TAX_CREDENTIAL_BR', | ||
]); | ||
|
||
// 2. Access localized field values | ||
const taxIdField = localizedFields?.[0]; | ||
|
||
// 3. Validate localized field values | ||
useBuyerJourneyIntercept( | ||
({canBlockProgress}) => { | ||
return canBlockProgress && | ||
taxIdField && | ||
(!taxIdField.value || | ||
taxIdField.value.length > 10) | ||
? { | ||
behavior: 'block', | ||
reason: 'Invalid tax ID', | ||
errors: [ | ||
{ | ||
message: `${taxIdField.title} is required and | ||
cannot exceed 10 characters in length`, | ||
// Show an error under the localized field or | ||
// exclude target to show at the top of the page | ||
target: `$.cart.localizedField.${taxIdField.key}`, | ||
}, | ||
], | ||
} | ||
: { | ||
behavior: 'allow', | ||
}; | ||
}, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters