diff --git a/.changeset/rude-suns-type.md b/.changeset/rude-suns-type.md new file mode 100644 index 0000000000..f6ad53ce2a --- /dev/null +++ b/.changeset/rude-suns-type.md @@ -0,0 +1,6 @@ +--- +'@shopify/ui-extensions-react': minor +'@shopify/ui-extensions': minor +--- + +Removed customer account targets as valid targets for checkout UI extensions. Use [customer account UI extensions](https://shopify.dev/docs/api/customer-account-ui-extensions) instead. diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/buyer-journey.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/buyer-journey.ts index 4ad30a1a29..b7dca165dc 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/buyer-journey.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/buyer-journey.ts @@ -19,14 +19,7 @@ export function useBuyerJourney< >(): BuyerJourney { const api = useApi(); - if ('buyerJourney' in api) { - return api.buyerJourney; - } - - throw new ExtensionHasNoMethodError( - 'applyAttributeChange', - api.extension.target, - ); + return api.buyerJourney; } /** @@ -38,12 +31,7 @@ export function useBuyerJourneyCompleted< Target extends RenderExtensionTarget = RenderExtensionTarget, >(): boolean { const api = useApi(); - - if ('buyerJourney' in api) { - return useSubscription(api.buyerJourney.completed); - } - - throw new ExtensionHasNoMethodError('buyerJourney', api.extension.target); + return useSubscription(api.buyerJourney.completed); } /** @@ -60,10 +48,6 @@ export function useBuyerJourneyIntercept< >(interceptor: Interceptor): void { const api = useApi(); - if (!('buyerJourney' in api)) { - throw new ExtensionHasNoMethodError('buyerJourney', api.extension.target); - } - const interceptorRef = useRef(interceptor); interceptorRef.current = interceptor; diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/cart-line-target.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/cart-line-target.ts index 7100fde564..ae4fae9554 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/cart-line-target.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/cart-line-target.ts @@ -11,14 +11,12 @@ import {useSubscription} from './subscription'; * - `purchase.cart-line-item.line-components.render` * - `purchase.checkout.cart-line-item.render-after` * - `purchase.thank-you.cart-line-item.render-after` - * - 'customer-account.order-status.cart-line-item.render-after' */ export function useCartLineTarget(): CartLine { const api = useApi< | 'purchase.cart-line-item.line-components.render' | 'purchase.checkout.cart-line-item.render-after' | 'purchase.thank-you.cart-line-item.render-after' - | 'customer-account.order-status.cart-line-item.render-after' >(); if (!api.target) { throw new ExtensionHasNoTargetError( diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/delivery-groups.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/delivery-groups.ts index f7465b5b64..cede535209 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/delivery-groups.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/delivery-groups.ts @@ -3,8 +3,6 @@ import type { RenderExtensionTarget, } from '@shopify/ui-extensions/checkout'; -import {ExtensionHasNoMethodError} from '../errors'; - import {useApi} from './api'; import {useSubscription} from './subscription'; @@ -16,10 +14,5 @@ export function useDeliveryGroups< Target extends RenderExtensionTarget = RenderExtensionTarget, >(): DeliveryGroup[] { const api = useApi(); - - if (!('deliveryGroups' in api)) { - throw new ExtensionHasNoMethodError('deliveryGroups', api.extension.target); - } - return useSubscription(api.deliveryGroups); } diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/payment-options.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/payment-options.ts index bea0176b4a..b50763e139 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/payment-options.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/payment-options.ts @@ -4,8 +4,6 @@ import type { SelectedPaymentOption, } from '@shopify/ui-extensions/checkout'; -import {ExtensionHasNoMethodError} from '../errors'; - import {useApi} from './api'; import {useSubscription} from './subscription'; @@ -16,15 +14,7 @@ export function useAvailablePaymentOptions< Target extends RenderExtensionTarget = RenderExtensionTarget, >(): PaymentOption[] { const api = useApi(); - - if ('availablePaymentOptions' in api) { - return useSubscription(api.availablePaymentOptions); - } - - throw new ExtensionHasNoMethodError( - 'availablePaymentOptions', - api.extension.target, - ); + return useSubscription(api.availablePaymentOptions); } /** @@ -34,13 +24,5 @@ export function useSelectedPaymentOptions< Target extends RenderExtensionTarget = RenderExtensionTarget, >(): SelectedPaymentOption[] { const api = useApi(); - - if ('selectedPaymentOptions' in api) { - return useSubscription(api.selectedPaymentOptions); - } - - throw new ExtensionHasNoMethodError( - 'selectedPaymentOptions', - api.extension.target, - ); + return useSubscription(api.selectedPaymentOptions); } diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/target.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/target.ts index c6995fcfe5..6f7f1d5791 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/target.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/target.ts @@ -15,8 +15,8 @@ class ExtensionHasNoTargetError extends Error { /** * Returns the cart line the extension is attached to. This hook can only be used by extensions in the - * `purchase.cart-line-item.line-components.render`, `purchase.checkout.cart-line-item.render-after`, - * `purchase.thank-you.cart-line-item.render-after`, and `customer-account.order-status.cart-line-item.render-after` + * `purchase.cart-line-item.line-components.render`, `purchase.checkout.cart-line-item.render-after`, and + * `purchase.thank-you.cart-line-item.render-after` * extension targets. Until version `2023-04`, this hook returned a `PresentmentCartLine` object. * * > Caution: Deprecated as of version `2023-10`, use `useCartLineTarget()` instead. @@ -28,7 +28,6 @@ export function useTarget(): CartLine { | 'purchase.cart-line-item.line-components.render' | 'purchase.checkout.cart-line-item.render-after' | 'purchase.thank-you.cart-line-item.render-after' - | 'customer-account.order-status.cart-line-item.render-after' >(); if (!api.target) { throw new ExtensionHasNoTargetError(api.extension.target); diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/buyer-journey.test.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/buyer-journey.test.ts index 6907b18a96..aeb4f70e7c 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/buyer-journey.test.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/buyer-journey.test.ts @@ -151,18 +151,6 @@ describe('buyerJourney Hooks', () => { }); describe('useBuyerJourney()', () => { - it('raises an exception when buyerJourney api is not available', () => { - expect(() => { - mount.hook(() => useBuyerJourney(), { - extensionApi: { - extension: { - target: 'purchase.checkout.header.render-after', - }, - }, - }); - }).toThrow(ExtensionHasNoMethodError); - }); - it('returns the buyer journey when the api is available', () => { const hook = mount.hook(() => useBuyerJourney(), { extensionApi: { @@ -178,18 +166,6 @@ describe('buyerJourney Hooks', () => { }); describe('useBuyerJourneyCompleted()', () => { - it('raises an exception when buyerJourney api is not available', () => { - expect(() => { - mount.hook(() => useBuyerJourneyCompleted(), { - extensionApi: { - extension: { - target: 'purchase.checkout.header.render-after', - }, - }, - }); - }).toThrow(ExtensionHasNoMethodError); - }); - it.each([true, false])( 'returns the buyer journey completed value: %s', (completed) => { @@ -210,24 +186,6 @@ describe('buyerJourney Hooks', () => { }); describe('useBuyerJourneyIntercept()', () => { - it('raises an exception when buyerJourney api is not available', () => { - expect(() => { - mount.hook( - () => - useBuyerJourneyIntercept(() => ({ - behavior: 'allow', - })), - { - extensionApi: { - extension: { - target: 'purchase.checkout.header.render-after', - }, - }, - }, - ); - }).toThrow(ExtensionHasNoMethodError); - }); - it('calls the interceptor function', () => { const mockIntercept = jest.fn(() => Promise.resolve({behavior: 'allow'} as const), diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.ts index f132cdaa13..d7cf051b0e 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.ts @@ -6,8 +6,7 @@ import { export default extension( 'purchase.checkout.block.render', - (root, {shop}) => { - const apiVersion = 'unstable'; + (root) => { const getProductsQuery = { query: `query ($first: Int!) { products(first: $first) { @@ -20,16 +19,13 @@ export default extension( variables: {first: 5}, }; - fetch( - `${shop.storefrontUrl}api/${apiVersion}/graphql.json`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(getProductsQuery), + fetch('shopify:storefront/api/graphql.json', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', }, - ) + body: JSON.stringify(getProductsQuery), + }) .then((response) => response.json()) .then(({data}) => { const listItems = diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.tsx index 0a8dc709a9..95649e8134 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.tsx +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/query-fetch.example.tsx @@ -28,22 +28,17 @@ function Extension() { variables: {first: 5}, }; - const apiVersion = 'unstable'; - - fetch( - `${shop.storefrontUrl}api/${apiVersion}/graphql.json`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(getProductsQuery), + fetch('shopify:storefront/api/graphql.json', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', }, - ) + body: JSON.stringify(getProductsQuery), + }) .then((response) => response.json()) .then(({data, errors}) => setData(data)) .catch(console.error); - }, [shop]); + }); return ( diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/helper.docs.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/helper.docs.ts index 4283fc4ecd..139443db98 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/helper.docs.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/helper.docs.ts @@ -339,6 +339,12 @@ Ensure your extension can use this API by [enabling the \`api_access\` capabilit description: ` You can access the [Storefront GraphQL API](/docs/api/storefront) using global \`fetch()\`. Ensure your extension can access the Storefront API via the [\`api_access\` capability](/docs/api/checkout-ui-extensions/configuration#api-access). + +The \`shopify:storefront\` protocol will automatically infer your Storefront URL and API version declared in your extension config. + +By omitting the API version (recommended), Shopify will use your API version configured in \`shopify.extension.toml\`. To change the API version, simply add it to the URL like \`shopify:storefront/api/2024-04/graphql.json\`. + +See [Storefront GraphQL API endpoints](/docs/api/storefront#endpoints) for more information. `, codeblock: { title: 'Accessing the Storefront API with fetch()', diff --git a/packages/ui-extensions/docs/surfaces/checkout/staticPages/configuration.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/staticPages/configuration.doc.ts index 7dc858fdbe..bbf3615685 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/staticPages/configuration.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/staticPages/configuration.doc.ts @@ -155,7 +155,7 @@ Defines the [capabilities](/docs/api/checkout-ui-extensions/apis/standardapi#pro { name: 'API access examples', subtitle: 'See', - url: '/docs/api/checkout-ui-extensions/apis/standardapi#example-storefront-api-access', + url: '/docs/api/checkout-ui-extensions/apis/storefront-api#examples', type: 'blocks', }, ], @@ -170,7 +170,7 @@ Defines the [capabilities](/docs/api/checkout-ui-extensions/apis/standardapi#pro }, { title: 'Methods for accessing the Storefront API', - sectionContent: `Enabling the \`api_access\` capability allows you to use the Standard API [\`query\`](/docs/api/checkout-ui-extensions/apis/standardapi#properties-propertydetail-query) method and the global \`fetch\` to retrieve data from the [Storefront API](/api/storefront) without manually managing token aquisition and refresh. + sectionContent: `Enabling the \`api_access\` capability allows you to use the Standard API [\`query\`](/docs/api/checkout-ui-extensions/apis/storefront-api) method and the global \`fetch\` to retrieve data from the [Storefront API](/api/storefront) without manually managing token aquisition and refresh. \`query\` lets you request a single GraphQL response from the Storefront API. @@ -194,6 +194,12 @@ Your extensions will have the following unauthenticated access scopes to the Sto - unauthenticated_read_metaobjects `, }, + { + title: 'Protocol Links', + sectionContent: ` +Protocol links are an easy way for Shopify to infer the type of request you are trying to make. If you would like to make a request to the [Storefront GraphQL API](/docs/api/storefront), you can use our [Storefront Protocol](/docs/api/checkout-ui-extensions/unstable/apis/storefront-api#examples) to infer your Storefront URL and API version. + `, + }, ], }, { @@ -434,7 +440,7 @@ You retrieve these metafields in your extension by reading [\`appMetafields\`](/ { title: 'Validation options', sectionContent: - 'Each setting can include validation options. Validation options enable you to apply additional constraints to the data that a setting can store, such as a minimum or maximum value, or a regular expression. The setting\'s `type` determines the available validation options. \n\n You can include a validation option for a setting using the validation `name` and a corresponding `value`. The appropriate value depends on the setting type to which the validation applies.\n\n The following table outlines the available validation options with supported types for applying constraints to a setting:\n\n | Validation option | Description | Supported types | Example |\n|---|---|---|---|\n| Minimum length | The minimum length of a text value. |
  • single_line_text_field
  • multi_line_text_field
|
[[extensions.settings.fields.validations]]
name = "min"
value = "8"
|\n| Maximum length | The maximum length of a text value. |
  • single_line_text_field
  • multi_line_text_field
|
[[extensions.settings.fields.validations]]
name = "max"
value = "25"
|\n| Regular expression | A regular expression. Shopify supports [RE2](https://github.com/google/re2/wiki/Syntax). |
  • single_line_text_field
  • multi_line_text_field
|
[[extensions.settings.fields.validations]]
name = "regex"
value = "(@)(.+)$"
|\n| Choices | A list of up to 128 predefined options that limits the values allowed for the metafield. | `single_line_text_field` |
[[extensions.settings.fields.validations]]
name = "choices"
value = "[\"red\", \"green\", \"blue\"]"
|\n| Minimum date | The minimum date in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date` |
[[extensions.settings.fields.validations]]
name = "min"
value = "2022-01-01"
|\n| Maximum date | The maximum date in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date` |
[[extensions.settings.fields.validations]]
name = "max"
value = "2022-03-03"
|\n| Minimum datetime | The minimum date and time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date_time` |
[[extensions.settings.fields.validations]]
name = "min"
value = "2022-03-03T16:30:00"
|\n| Maximum datetime | The maximum date and time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date_time` |
[[extensions.settings.fields.validations]]
name = "max"
value = "2022-03-03T17:30:00"
|\n| Minimum integer | The minimum integer number. | `number_integer` |
[[extensions.settings.fields.validations]]
name = "min"
value = "9"
|\n| Maximum integer | The maximum integer number. | `number_integer` |
[[extensions.settings.fields.validations]]
name = "max"
value = "15"
|\n| Minimum decimal | The minimum decimal number. | `number_decimal` |
[[extensions.settings.fields.validations]]
name = "min"
value = "0.5"
|\n| Maximum decimal | The maximum decimal number. | `number_decimal` |
[[extensions.settings.fields.validations]]
name = "max"
value = "1.99"
|\n| Maximum precision | The maximum number of decimal places to store for a decimal number. | `number_decimal` |
[[extensions.settings.fields.validations]]
name = "max_precision"
value = "2"
|', + 'Each setting can include validation options. Validation options enable you to apply additional constraints to the data that a setting can store, such as a minimum or maximum value, or a regular expression. The setting\'s `type` determines the available validation options. \n\n You can include a validation option for a setting using the validation `name` and a corresponding `value`. The appropriate value depends on the setting type to which the validation applies.\n\n The following table outlines the available validation options with supported types for applying constraints to a setting:\n\n | Validation option | Description | Supported types | Example |\n|---|---|---|---|\n| Minimum length | The minimum length of a text value. |
  • single_line_text_field
  • multi_line_text_field
|
[[extensions.settings.fields.validations]]
name = "min"
value = "8"
|\n| Maximum length | The maximum length of a text value. |
  • single_line_text_field
  • multi_line_text_field
|
[[extensions.settings.fields.validations]]
name = "max"
value = "25"
|\n| Regular expression | A regular expression. Shopify supports [RE2](https://github.com/google/re2/wiki/Syntax). |
  • single_line_text_field
  • multi_line_text_field
|
[[extensions.settings.fields.validations]]
name = "regex"
value = "(@)(.+)$"
|\n| Choices | A list of up to 128 predefined options that limits the values allowed for the metafield. | `single_line_text_field` |
[[extensions.settings.fields.validations]]
name = "choices"
value = "["red", "green", "blue"]"
|\n| Minimum date | The minimum date in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date` |
[[extensions.settings.fields.validations]]
name = "min"
value = "2022-01-01"
|\n| Maximum date | The maximum date in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date` |
[[extensions.settings.fields.validations]]
name = "max"
value = "2022-03-03"
|\n| Minimum datetime | The minimum date and time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date_time` |
[[extensions.settings.fields.validations]]
name = "min"
value = "2022-03-03T16:30:00"
|\n| Maximum datetime | The maximum date and time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. | `date_time` |
[[extensions.settings.fields.validations]]
name = "max"
value = "2022-03-03T17:30:00"
|\n| Minimum integer | The minimum integer number. | `number_integer` |
[[extensions.settings.fields.validations]]
name = "min"
value = "9"
|\n| Maximum integer | The maximum integer number. | `number_integer` |
[[extensions.settings.fields.validations]]
name = "max"
value = "15"
|\n| Minimum decimal | The minimum decimal number. | `number_decimal` |
[[extensions.settings.fields.validations]]
name = "min"
value = "0.5"
|\n| Maximum decimal | The maximum decimal number. | `number_decimal` |
[[extensions.settings.fields.validations]]
name = "max"
value = "1.99"
|\n| Maximum precision | The maximum number of decimal places to store for a decimal number. | `number_decimal` |
[[extensions.settings.fields.validations]]
name = "max_precision"
value = "2"
|', }, ], }, @@ -473,6 +479,7 @@ For specific targets, you must provide the URL of assets or pages loaded by UI c The \`chat\` property specifies the URL for the iframe used in this extension target. The URL can be absolute or relative. Relative URLs are resolved against the app URL defined in the app configuration. For example, + * if the app URL is \`https://example.com\` and \`chat = "/my-chat-application"\`, the resolved URL will be \`https://example.com/my-chat-application\`. * if \`chat = "https://my-chat-application.com"\`, the resolved URL will be \`https://my-chat-application.com\`. `, diff --git a/packages/ui-extensions/docs/surfaces/checkout/staticPages/instructions-update.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/staticPages/instructions-update.doc.ts index 71a36f2786..cd737e5148 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/staticPages/instructions-update.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/staticPages/instructions-update.doc.ts @@ -3,7 +3,7 @@ import type {LandingTemplateSchema} from '@shopify/generate-docs'; const exampleCodePath = '../reference/examples/cart-instructions'; const data: LandingTemplateSchema = { - title: 'Updating to 2024-10', + title: 'Updating to 2025-01', description: ` Some checkouts may be created with [cart instructions](/docs/api/checkout-ui-extensions/apis/cart-instructions) that prevent buyers from making certain changes to their checkout. @@ -38,7 +38,7 @@ As of version \`2024-07\`, UI extensions must check for instructions before call sectionContent: ` You will need to check for cart instructions before calling the following APIs: -| Extension API | As of July 2024 | +| Extension API | As of January 2025 | | ---- | ----- | | applyAttributeChange() | Attributes cannot be modified on draft order checkouts. | | applyShippingAddressChange() | Buyers cannot change the address on a draft order checkout if it has fixed shipping rates. | diff --git a/packages/ui-extensions/src/shared.ts b/packages/ui-extensions/src/shared.ts index b8178f8af0..e6bfbe7946 100644 --- a/packages/ui-extensions/src/shared.ts +++ b/packages/ui-extensions/src/shared.ts @@ -23,6 +23,7 @@ export type ApiVersion = | '2024-04' | '2024-07' | '2024-10' + | '2025-01' | 'unstable'; /** @@ -38,7 +39,7 @@ export type ApiVersion = * * * [`collect_buyer_consent.customer_privacy`](https://shopify.dev/docs/api/checkout-ui-extensions/configuration#collect-buyer-consent): the extension can register buyer consent decisions that will be honored on Shopify-managed services. * - * * `iframe.sources`: the extension can embed an external URL in an iframe. + * * [`iframe.sources`](https://shopify.dev/docs/api/checkout-ui-extensions/configuration#iframe): the extension can embed an external URL in an iframe. */ export type Capability = @@ -915,6 +916,7 @@ export type StorefrontApiVersion = | '2024-04' | '2024-07' | '2024-10' + | '2025-01' | 'unstable'; /** diff --git a/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts b/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts index 41be950399..179a683474 100644 --- a/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts +++ b/packages/ui-extensions/src/surfaces/checkout/api/address-autocomplete/standard.ts @@ -194,7 +194,7 @@ interface Extension< * * * [`collect_buyer_consent.customer_privacy`](https://shopify.dev/docs/api/checkout-ui-extensions/configuration#collect-buyer-consent): the extension can register customer consent decisions that will be honored on Shopify-managed services. * - * * `iframe.sources`: the extension can embed an external URL in an iframe. + * * [`iframe.sources`](https://shopify.dev/docs/api/checkout-ui-extensions/configuration#iframe): the extension can embed an external URL in an iframe. */ capabilities: Capability[]; diff --git a/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts b/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts index 64a82d312b..b32fef18a8 100644 --- a/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts +++ b/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts @@ -62,7 +62,7 @@ export interface Extension { /** * The API version that was set in the extension config file. * - * @example '2023-10', '2024-01', '2024-04', '2024-07', '2024-10', 'unstable' + * @example '2024-04', '2024-07', '2024-10', '2025-01', 'unstable' */ apiVersion: ApiVersion; @@ -80,7 +80,7 @@ export interface Extension { * * * [`collect_buyer_consent.customer_privacy`](https://shopify.dev/docs/api/checkout-ui-extensions/configuration#collect-buyer-consent): the extension can register customer consent decisions that will be honored on Shopify-managed services. * - * * `iframe.sources`: the extension can embed an external URL in an iframe. + * * [`iframe.sources`](https://shopify.dev/docs/api/checkout-ui-extensions/configuration#iframe): the extension can embed an external URL in an iframe. */ capabilities: StatefulRemoteSubscribable; @@ -1371,6 +1371,16 @@ export interface Customer { * {% include /apps/checkout/privacy-icon.md %} Requires level 1 access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data). */ image: ImageDetails; + /** + * Defines if the customer email accepts marketing activities. + * + * {% include /apps/checkout/privacy-icon.md %} Requires level 1 access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data). + * + * > Caution: This field is deprecated and will be removed in a future version. Use `acceptsEmailMarketing` or `acceptsSmsMarketing` instead. + * + * @deprecated Use `acceptsEmailMarketing` or `acceptsSmsMarketing` instead. + */ + acceptsMarketing: boolean; /** * Defines if the customer accepts email marketing activities. * diff --git a/packages/ui-extensions/src/surfaces/checkout/components/Badge/Badge.ts b/packages/ui-extensions/src/surfaces/checkout/components/Badge/Badge.ts index a2bd251c4d..a8ed4c6b92 100644 --- a/packages/ui-extensions/src/surfaces/checkout/components/Badge/Badge.ts +++ b/packages/ui-extensions/src/surfaces/checkout/components/Badge/Badge.ts @@ -21,7 +21,7 @@ export interface BadgeProps extends VisibilityProps { size?: Extract; /** * A label that describes the purpose or contents of the element. When set, - * it will be passed as `aria-label` to underlying element and announced to buyers using assistive technologies. + * it will announced to buyers using assistive technologies. */ accessibilityLabel?: string; /** diff --git a/packages/ui-extensions/src/surfaces/checkout/components/Chat/AppBridge.ts b/packages/ui-extensions/src/surfaces/checkout/components/Chat/AppBridge.ts index 216e21e7f5..6dcf9c5c45 100644 --- a/packages/ui-extensions/src/surfaces/checkout/components/Chat/AppBridge.ts +++ b/packages/ui-extensions/src/surfaces/checkout/components/Chat/AppBridge.ts @@ -20,7 +20,7 @@ interface AppBridge { /** * The ID token providing a set of claims as a signed [JSON Web Token (JWT)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken%5C) - * with a TTL of 5 minutes. It can be used can be used to ensure that requests came from a Shopify authenticated user. + * with a TTL of 5 minutes. It can be used to ensure that requests came from a Shopify authenticated user. * See the [ID Token documentation](https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens) for more information. * * @see https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/session-token @@ -85,8 +85,8 @@ interface Extension { interface Visitor { /** - * The unique token of a given user across all surfaces in a shop, present - * if a user has opted-in to tracking. + * The unique token of a given user across all surfaces in a shop, + * present if processing permission is provided. * * @ignore this maps to the _shopify_y cookie which Trekkie refers to as a * uniqToken. diff --git a/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.doc.ts b/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.doc.ts index 7cfd7d8788..af19d63a4b 100644 --- a/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.doc.ts +++ b/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.doc.ts @@ -23,7 +23,7 @@ Use the Chat component to create real-time chat applications. description: ` The App Bridge script for checkout provides APIs that enables a secure communication channel between the Shopify checkout and the embedded application within the Chat iframe. It also offers convenient methods to perform common actions like resizing the iframe from within the application. -After App Bridge is set up in your app, you have access to the \`shopify\` global variable. This variable exposes the following App Bridge functionalities and configuration information: +After App Bridge is [set up](#about-app-bridge) in your app, you have access to the \`shopify\` global variable. This variable exposes the following App Bridge functionalities and configuration information: `, type: 'AppBridge', }, @@ -57,7 +57,7 @@ After App Bridge is set up in your app, you have access to the \`shopify\` globa The \`src\` of the iframe rendered by Chat is provided by the \`preloads\` \`chat\` key in the extension configuration file. Shopify automatically appends query parameters to the URL which allows developers to verify the authenticity of the request and the identity of the merchant. We guarantee these tokens are valid and signed by Shopify. #### id_token -The ID token providing a set of claims as a signed [JSON Web Token (JWT)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken%5C) with a TTL of 5 minutes. It can be used can be used to retrieve merchants information on the backend as well as ensure that requests came from a Shopify authenticated source. See the [ID Token documentation](https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens) for more information. +The ID token providing a set of claims as a signed [JSON Web Token (JWT)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken%5C) with a TTL of 5 minutes. It can be used to retrieve merchants information on the backend as well as ensure that requests came from a Shopify authenticated source. See the [ID Token documentation](https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens) for more information. #### locale The locale of the shop that’s embedding the app, i.e. \`en-CA\`. This information is also available in the \`shopify\` global variable under \`config\`. diff --git a/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.ts b/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.ts index 1919a155bb..0ec0fce420 100644 --- a/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.ts +++ b/packages/ui-extensions/src/surfaces/checkout/components/Chat/Chat.ts @@ -6,7 +6,7 @@ export interface ChatProps extends IdProps { /** * Adjust the inline size. * - * Checkout imposes sizing restrictions for the component, therefore the size set + * Checkout imposes [sizing restrictions](https://shopify.dev/docs/apps/build/checkout/chat#component-states) for the component, therefore the size set * may not be the actual size rendered. * * `number`: size in pixels. @@ -18,7 +18,7 @@ export interface ChatProps extends IdProps { /** * Adjust the block size. * - * Checkout imposes sizing restrictions for the component, therefore the size set + * Checkout imposes [sizing restrictions](https://shopify.dev/docs/apps/build/checkout/chat#component-states) for the component, therefore the size set * may not be the actual size rendered. * * `number`: size in pixels. diff --git a/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-communication.example.js b/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-communication.example.js index 876067b0aa..d4330692b7 100644 --- a/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-communication.example.js +++ b/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-communication.example.js @@ -8,7 +8,7 @@ shopify.extension.port.onMessage = async (event) => { if (event.data.action === 'ping') { buyerFirstName = event.data.buyer.firstName; - await shopify.extension.messagePort.postMessage({ + await shopify.extension.port.postMessage({ action: 'pong', }); } diff --git a/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-resize.example.html b/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-resize.example.html index 5278fb830d..34b5cfe1de 100644 --- a/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-resize.example.html +++ b/packages/ui-extensions/src/surfaces/checkout/components/Chat/examples/app-bridge-resize.example.html @@ -33,13 +33,13 @@ // - resize the iframe to the button's size if (dialog.classList.contains('visible')) { dialog.classList.remove('visible'); - shopify !== undefined && window.shopify.resizeTo(150, 50); + shopify !== undefined && window.resizeTo(150, 50); // if the dialog is not visible, // - resize the iframe to the desired dialog's size // - then show the dialog } else { - shopify !== undefined && window.shopify.resizeTo(415, 700); + shopify !== undefined && window.resizeTo(415, 700); dialog.classList.add('visible'); } } diff --git a/packages/ui-extensions/src/surfaces/checkout/targets.ts b/packages/ui-extensions/src/surfaces/checkout/targets.ts index be274480fc..efaf9c785f 100644 --- a/packages/ui-extensions/src/surfaces/checkout/targets.ts +++ b/packages/ui-extensions/src/surfaces/checkout/targets.ts @@ -279,100 +279,6 @@ export interface RenderExtensionTargets { StandardApi<'Checkout::ThankYou::CustomerInformation::RenderAfter'>, AnyComponent >; - /** - * A [block extension target](https://shopify.dev/docs/api/checkout-ui-extensions/extension-targets-overview#block-extension-targets) that renders exclusively on the **Order status** page. - * Unlike static extension targets, block extension targets render where the merchant - * sets them using the [checkout editor](https://shopify.dev/apps/checkout/test-ui-extensions#test-the-extension-in-the-checkout-editor). - * - * The [supported locations](https://shopify.dev/docs/api/checkout-ui-extensions/extension-targets-overview#supported-locations) for block extension targets can be previewed during development - * by [using a URL parameter](https://shopify.dev/docs/apps/checkout/best-practices/testing-ui-extensions#block-extension-targets). - * - * @deprecated Use `customer-account.order-status.block.render` from `@shopify/ui-extension/customer-account` instead. - */ - 'customer-account.order-status.block.render': RenderExtension< - OrderStatusApi & - CustomerAccountStandardApi<'customer-account.order-status.block.render'>, - AnyComponent - >; - /** - * A [block extension target](https://shopify.dev/docs/api/checkout-ui-extensions/extension-targets-overview#block-extension-targets) that renders exclusively on the **Order status** page. - * Unlike static extension targets, block extension targets render where the merchant - * sets them using the [checkout editor](https://shopify.dev/apps/checkout/test-ui-extensions#test-the-extension-in-the-checkout-editor). - * - * The [supported locations](https://shopify.dev/docs/api/checkout-ui-extensions/extension-targets-overview#supported-locations) for block extension targets can be previewed during development - * by [using a URL parameter](https://shopify.dev/docs/apps/checkout/best-practices/testing-ui-extensions#block-extension-targets). - * - * @deprecated Use `customer-account.order-status.block.render` from `@shopify/ui-extension/customer-account` instead. - */ - 'Checkout::OrderStatus::Dynamic::Render': RenderExtension< - OrderStatusApi & - CustomerAccountStandardApi<'Checkout::OrderStatus::Dynamic::Render'>, - AnyComponent - >; - /** - * A static extension target that renders on every line item, inside the details - * under the line item properties element on the **Order status** page. - * - * @deprecated Use `customer-account.order-status.cart-line-item.render-after` from `@shopify/ui-extension/customer-account` instead. - */ - 'customer-account.order-status.cart-line-item.render-after': RenderExtension< - CartLineItemApi & - OrderStatusApi & - CustomerAccountStandardApi<'customer-account.order-status.cart-line-item.render-after'>, - AnyComponent - >; - /** - * A static extension target that renders on every line item, inside the details - * under the line item properties element on the **Order status** page. - * - * @deprecated Use `customer-account.order-status.cart-line-item.render-after` instead. - */ - 'Checkout::OrderStatus::CartLineDetails::RenderAfter': RenderExtension< - CartLineItemApi & - OrderStatusApi & - CustomerAccountStandardApi<'Checkout::OrderStatus::CartLineDetails::RenderAfter'>, - AnyComponent - >; - /** - * A static extension target that is rendered after all line items on the **Order status** page. - * - * @deprecated Use `customer-account.order-status.cart-line-list.render-after` from `@shopify/ui-extension/customer-account` instead. - */ - 'customer-account.order-status.cart-line-list.render-after': RenderExtension< - OrderStatusApi & - CustomerAccountStandardApi<'customer-account.order-status.cart-line-list.render-after'>, - AnyComponent - >; - /** - * A static extension target that is rendered after all line items on the **Order status** page. - * - * @deprecated Use `customer-account.order-status.cart-line-list.render-after` from `@shopify/ui-extension/customer-account` instead. - */ - 'Checkout::OrderStatus::CartLines::RenderAfter': RenderExtension< - OrderStatusApi & - CustomerAccountStandardApi<'Checkout::OrderStatus::CartLines::RenderAfter'>, - AnyComponent - >; - /** - * A static extension target that is rendered after a purchase below the customer information on the **Order status** page. - * - * @deprecated Use `customer-account.order-status.customer-information.render-after` from `@shopify/ui-extension/customer-account` instead. - */ - 'customer-account.order-status.customer-information.render-after': RenderExtension< - OrderStatusApi & - CustomerAccountStandardApi<'customer-account.order-status.customer-information.render-after'>, - AnyComponent - >; - /** - * A static extension target that is rendered after a purchase below the customer information on the **Order status** page. - * - * @deprecated Use `customer-account.order-status.customer-information.render-after` from `@shopify/ui-extension/customer-account` instead. - */ - 'Checkout::OrderStatus::CustomerInformation::RenderAfter': RenderExtension< - OrderStatusApi & - CustomerAccountStandardApi<'Checkout::OrderStatus::CustomerInformation::RenderAfter'>, - AnyComponent - >; /** * A static extension target that renders the gift card entry form fields after * the buyer ticks a box to use a gift card. This does not replace the @@ -861,43 +767,3 @@ export type RunnableExtensionTarget = keyof RunnableExtensionTargets; export type RunnableExtensions = { [Target in RunnableExtensionTarget]: RunnableExtensionTargets[Target]; }; - -/** - * The part of the standard API implemented for customer-account targets. Must - * match the types defined in the `surfaces/customer-account` section of this package. - */ -export interface CustomerAccountStandardApi< - Target extends keyof ExtensionTargets, -> extends Pick< - StandardApi, - | 'analytics' - | 'appliedGiftCards' - | 'appMetafields' - | 'attributes' - | 'buyerIdentity' - | 'checkoutSettings' - | 'checkoutToken' - | 'cost' - | 'discountCodes' - | 'discountAllocations' - | 'extension' - | 'extensionPoint' - | 'i18n' - | 'instructions' - | 'lines' - | 'localization' - | 'metafields' - | 'note' - | 'query' - | 'sessionToken' - | 'settings' - | 'shippingAddress' - | 'billingAddress' - | 'shop' - | 'storage' - | 'ui' - | 'version' - | 'customerPrivacy' - | 'applyTrackingConsentChange' - | 'localizedFields' - > {}