diff --git a/.changeset/violet-flowers-deny.md b/.changeset/violet-flowers-deny.md new file mode 100644 index 0000000000..5d52c8063f --- /dev/null +++ b/.changeset/violet-flowers-deny.md @@ -0,0 +1,8 @@ +--- +'@shopify/ui-extensions-react': minor +'@shopify/ui-extensions': minor +--- + +- Adds `oneTimeUse` to `ShippingAddress` to denote whether the address can be saved in checkout. +- Adds `sku` to `ProductVariant` in checkout. +- Adds `bullet` icon in checkout. diff --git a/packages/ui-extensions-react/src/surfaces/checkout/context.ts b/packages/ui-extensions-react/src/surfaces/checkout/context.ts index 31990ac5dc..2d82200cc1 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/context.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/context.ts @@ -1,8 +1,8 @@ import {createContext} from 'react'; import type { - ApiForRenderExtension, + ApiForExtension, RenderExtensionTarget, } from '@shopify/ui-extensions/checkout'; export const ExtensionApiContext = - createContext | null>(null); + createContext | null>(null); diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/api.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/api.ts index dc171e9a36..73e8a49084 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/api.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/api.ts @@ -1,7 +1,7 @@ import {useContext} from 'react'; import type { RenderExtensionTarget, - ApiForRenderExtension, + ApiForExtension, } from '@shopify/ui-extensions/checkout'; import {CheckoutUIExtensionError} from '../errors'; @@ -19,7 +19,7 @@ import {ExtensionApiContext} from '../context'; */ export function useApi< Target extends RenderExtensionTarget = RenderExtensionTarget, ->(_target?: Target): ApiForRenderExtension { +>(_target?: Target): ApiForExtension { const api = useContext(ExtensionApiContext); if (api == null) { @@ -27,8 +27,7 @@ export function useApi< 'You can only call this hook when running as a checkout UI extension.', ); } - - return api as ApiForRenderExtension; + return api as ApiForExtension; } /** @@ -47,6 +46,6 @@ export function useApi< */ export function useExtensionApi< Target extends RenderExtensionTarget = RenderExtensionTarget, ->(): ApiForRenderExtension { +>(): ApiForExtension { return useApi(); } diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/shipping-address.ts b/packages/ui-extensions-react/src/surfaces/checkout/hooks/shipping-address.ts index 289656beb0..c4e28eb3c2 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/shipping-address.ts +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/shipping-address.ts @@ -1,5 +1,5 @@ import type { - MailingAddress, + ShippingAddress, RenderExtensionTarget, ShippingAddressChange, ShippingAddressChangeResult, @@ -15,7 +15,7 @@ import {useSubscription} from './subscription'; */ export function useShippingAddress< Target extends RenderExtensionTarget = RenderExtensionTarget, ->(): MailingAddress | undefined { +>(): ShippingAddress | undefined { const shippingAddress = useApi().shippingAddress; if (!shippingAddress) { diff --git a/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/mount.tsx b/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/mount.tsx index 66694e73fd..6141eb04cc 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/mount.tsx +++ b/packages/ui-extensions-react/src/surfaces/checkout/hooks/tests/mount.tsx @@ -1,7 +1,7 @@ import React from 'react'; import {createRender} from '@quilted/react-testing'; import type { - ApiForRenderExtension, + ApiForExtension, RenderExtensionTarget, } from '@shopify/ui-extensions/checkout'; import type {StatefulRemoteSubscribable} from '@remote-ui/async-subscription'; @@ -27,7 +27,7 @@ type DeepPartial = { }; export type PartialExtensionApi = DeepPartial< - ApiForRenderExtension + ApiForExtension >; interface Options { diff --git a/packages/ui-extensions-react/src/surfaces/checkout/render.tsx b/packages/ui-extensions-react/src/surfaces/checkout/render.tsx index 48b96efa15..8046d9ed85 100644 --- a/packages/ui-extensions-react/src/surfaces/checkout/render.tsx +++ b/packages/ui-extensions-react/src/surfaces/checkout/render.tsx @@ -5,7 +5,7 @@ import {extension} from '@shopify/ui-extensions/checkout'; import type { ExtensionTargets, RenderExtensionTarget, - ApiForRenderExtension, + ApiForExtension, } from '@shopify/ui-extensions/checkout'; import {ExtensionApiContext} from './context'; @@ -29,7 +29,7 @@ import {ExtensionApiContext} from './context'; export function reactExtension( target: Target, render: ( - api: ApiForRenderExtension, + api: ApiForExtension, ) => ReactElement | Promise>, ): ExtensionTargets[Target] { // TypeScript can’t infer the type of the callback because it’s a big union @@ -39,7 +39,7 @@ export function reactExtension( return extension<'purchase.checkout.block.render'>( target as any, async (root, api) => { - const element = await render(api as ApiForRenderExtension); + const element = await render(api as ApiForExtension); await new Promise((resolve, reject) => { try { @@ -84,7 +84,7 @@ export function reactExtension( export function render( target: Target, - render: (api: ApiForRenderExtension) => ReactElement, + render: (api: ApiForExtension) => ReactElement, ): ExtensionTargets[Target] { return reactExtension(target, render); } diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/order-confirmation/default.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/order-confirmation/default.example.tsx index 188821d8b5..a29e03693d 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/order-confirmation/default.example.tsx +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/order-confirmation/default.example.tsx @@ -2,6 +2,7 @@ import { reactExtension, Banner, useApi, + useSubscription, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( @@ -11,13 +12,13 @@ export default reactExtension( function Extension() { const {orderConfirmation} = useApi(); + const {id} = useSubscription(orderConfirmation); - if (orderConfirmation) { + if (id) { return ( Please include your order confirmation ID - ({orderConfirmation.id}) in support - requests + ({id}) in support requests ); } diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.ts index a6f3ca5624..c4df27bac3 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.ts @@ -7,39 +7,50 @@ import { TextBlock, } from '@shopify/ui-extensions/checkout'; -export default extension('purchase.checkout.block.render', (root) => { - const label = 'Queue process'; +export default extension( + 'purchase.checkout.block.render', + (root) => { + const label = 'Queue process'; - const progress = root.createComponent(BlockStack, null, [ - root.createComponent( - Grid, - { - columns: ['fill', 'auto'], - }, + const progress = root.createComponent( + BlockStack, + null, [ - root.createComponent(Text, null, label), root.createComponent( - Text, + Grid, + { + columns: ['fill', 'auto'], + }, + [ + root.createComponent( + Text, + null, + label, + ), + root.createComponent( + Text, + { + appearance: 'subdued', + }, + '45% completed', + ), + ], + ), + root.createComponent(Progress, { + value: 45, + max: 100, + accessibilityLabel: label, + }), + root.createComponent( + TextBlock, { appearance: 'subdued', }, - '45% completed', + 'Estimated wait time: 4 minutes', ), ], - ), - root.createComponent(Progress, { - value: 45, - max: 100, - accessibilityLabel: label, - }), - root.createComponent( - TextBlock, - { - appearance: 'subdued', - }, - 'Estimated wait time: 4 minutes', - ), - ]); + ); - root.appendChild(progress); -}); + root.appendChild(progress); + }, +); diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.tsx index ec93e080fb..ecbe6d3d29 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.tsx +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/ui-components/progress-determinate-state.example.tsx @@ -7,9 +7,10 @@ import { TextBlock, } from '@shopify/ui-extensions-react/checkout'; -export default reactExtension('purchase.checkout.footer.render-after', () => ( - -)); +export default reactExtension( + 'purchase.checkout.footer.render-after', + () => , +); function Extension() { const label = 'Queue process'; @@ -17,10 +18,18 @@ function Extension() { {label} - 45% completed + + 45% completed + - - Estimated wait time: 4 minutes + + + Estimated wait time: 4 minutes + ); } diff --git a/packages/ui-extensions/docs/surfaces/checkout/staticPages/extension-overview.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/staticPages/extension-overview.doc.ts index cf4641dc4c..6b4439bf5b 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/staticPages/extension-overview.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/staticPages/extension-overview.doc.ts @@ -185,7 +185,8 @@ Review [all **Order status** page extensions targets](/docs/api/checkout-ui-exte type: 'Generic', anchorLink: 'block-extension-targets', title: 'Block extension targets', - sectionContent: `Block extension targets render between core checkout features. Merchants can use the [checkout and accounts editor](/apps/checkout/test-ui-extensions#test-the-extension-in-the-checkout-editor) to place the extension in the [checkout](/docs/api/checkout-ui-extensions/extension-targets-overview#supported-locations), [Thank you](/docs/api/checkout-ui-extensions/extension-targets-overview#supported-typ-locations), or [Order status](/docs/api/checkout-ui-extensions/extension-targets-overview#supported-osp-locations) pages.\n\nBlock extensions are always rendered, regardless of what other elements of the checkout are present. For example, an extension placed above the shipping address will still render even for digital products which do not require a shipping address. Choose block extension targets when your content and functionality works independently of a core checkout feature. This is useful for custom content, like a field to capture order notes from the customer.\n\nBlock extension targets always support multiple placements. Each placement has an associated placement reference that represents its location on the page. For example, the block extension target \`purchase.checkout.block.render\` supports fourteen placements. The placement references include \`INFORMATION1\`, \`DELIVERY1\`, \`PAYMENT1\`, and more.\n\nYou can use placement references as URL parameters to [test block extensions](/docs/apps/build/checkout/test-checkout-ui-extensions#block-targets) in all supported placements on a page. You can also use placement references to [define the default placement](/docs/apps/build/app-extensions/configure-app-extensions#checkout-ui-extensions) of an extension for merchants.`, + sectionContent: `Block extension targets render between core checkout features. Merchants can use the [checkout editor](/apps/checkout/test-ui-extensions#test-the-extension-in-the-checkout-editor) to place the extension in the [checkout](/docs/api/checkout-ui-extensions/extension-targets-overview#supported-locations), [**Thank you**](/docs/api/checkout-ui-extensions/extension-targets-overview#supported-typ-locations), or [**Order status**](/docs/api/checkout-ui-extensions/extension-targets-overview#supported-osp-locations) pages. + \n\nBlock extensions are always rendered, regardless of what other elements of the checkout are present. For example, an extension placed above the shipping address will still render even for digital products which do not require a shipping address.\n\nChoose block extension targets when your content and functionality works independently of a core checkout feature. This is useful for custom content, like a field to capture order notes from the customer.`, image: 'block-extension-targets.png', sectionCard: [ { @@ -194,12 +195,6 @@ Review [all **Order status** page extensions targets](/docs/api/checkout-ui-exte url: '/docs/api/checkout-ui-extensions/targets', type: 'blocks', }, - { - name: 'Placement references', - subtitle: 'Learn more', - url: '/docs/apps/build/checkout/test-checkout-ui-extensions#placement-references', - type: 'resource', - }, ], }, ], diff --git a/packages/ui-extensions/src/surfaces/checkout.ts b/packages/ui-extensions/src/surfaces/checkout.ts index 7328ca2d69..45673ba802 100644 --- a/packages/ui-extensions/src/surfaces/checkout.ts +++ b/packages/ui-extensions/src/surfaces/checkout.ts @@ -75,6 +75,7 @@ export type { SellingPlan, ValidationError, MailingAddress, + ShippingAddress, } from './checkout/api/shared'; export type { diff --git a/packages/ui-extensions/src/surfaces/checkout/api/checkout/checkout.ts b/packages/ui-extensions/src/surfaces/checkout/api/checkout/checkout.ts index aa5b1cc38e..9a84947dec 100644 --- a/packages/ui-extensions/src/surfaces/checkout/api/checkout/checkout.ts +++ b/packages/ui-extensions/src/surfaces/checkout/api/checkout/checkout.ts @@ -1,4 +1,9 @@ -import type {Attribute, SellingPlan, MailingAddress} from '../shared'; +import type { + Attribute, + SellingPlan, + MailingAddress, + ShippingAddress, +} from '../shared'; /** * Removes a note @@ -452,7 +457,7 @@ export interface ShippingAddressUpdateChange { * values for the fields you want to update — any fields you do not list * will keep their current values. */ - address: Partial; + address: Partial; } export type ShippingAddressChange = ShippingAddressUpdateChange; diff --git a/packages/ui-extensions/src/surfaces/checkout/api/shared.ts b/packages/ui-extensions/src/surfaces/checkout/api/shared.ts index 0b553edb8a..576bd1e1c9 100644 --- a/packages/ui-extensions/src/surfaces/checkout/api/shared.ts +++ b/packages/ui-extensions/src/surfaces/checkout/api/shared.ts @@ -136,3 +136,7 @@ export interface MailingAddress { */ phone?: string; } + +export interface ShippingAddress extends MailingAddress { + oneTimeUse?: boolean; +} 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 f660e39402..f563a17d1e 100644 --- a/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts +++ b/packages/ui-extensions/src/surfaces/checkout/api/standard/standard.ts @@ -5,6 +5,7 @@ import type { SellingPlan, Attribute, MailingAddress, + ShippingAddress, } from '../shared'; import type {ExtensionTarget} from '../../targets'; import type { @@ -664,7 +665,7 @@ export interface StandardApi { * * {% include /apps/checkout/privacy-icon.md %} Requires access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data). */ - shippingAddress?: StatefulRemoteSubscribable; + shippingAddress?: StatefulRemoteSubscribable; /** * The proposed customer billing address. The address updates when the field is @@ -1047,6 +1048,11 @@ export interface ProductVariant extends BaseMerchandise { * The selling plan associated with the merchandise. */ sellingPlan?: SellingPlan; + + /** + * The product variant's sku. + */ + sku?: string; } export interface Product { diff --git a/packages/ui-extensions/src/surfaces/checkout/components/Icon/Icon.ts b/packages/ui-extensions/src/surfaces/checkout/components/Icon/Icon.ts index a931e69a22..849967c248 100644 --- a/packages/ui-extensions/src/surfaces/checkout/components/Icon/Icon.ts +++ b/packages/ui-extensions/src/surfaces/checkout/components/Icon/Icon.ts @@ -9,6 +9,7 @@ export type IconSource = | 'arrowUpRight' | 'arrowDown' | 'bag' + | 'bullet' | 'calendar' | 'camera' | 'caretDown' diff --git a/packages/ui-extensions/src/surfaces/checkout/targets.ts b/packages/ui-extensions/src/surfaces/checkout/targets.ts index 4e33654197..f8930c5bf2 100644 --- a/packages/ui-extensions/src/surfaces/checkout/targets.ts +++ b/packages/ui-extensions/src/surfaces/checkout/targets.ts @@ -780,12 +780,6 @@ export type ArgumentsForExtension = /** * For a given extension target, returns the type of the API that the * extension will receive at runtime. - * - * For RenderExtensionTargets, this API type is the second argument to - * the callback for that extension target. - * - * For RunnableExtensionTargets, this API type is the only argument to - * the callback for that extension target. */ export type ApiForExtension = ExtractedApiFromExtensionDefinition;