diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/address.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/address.doc.ts index 213a5b3674..ad4ac1d797 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/address.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/address.doc.ts @@ -2,6 +2,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; import { getLinksByTag, + getExample, STANDARD_API_PROPERTIES_DESCRIPTION, CHECKOUT_API_PROPERTIES_DESCRIPTION, REQUIRES_PROTECTED_CUSTOMER_DATA_LEVEL_2, @@ -45,6 +46,11 @@ const data: ReferenceEntityTemplateSchema = { type: 'UseApplyShippingAddressChangeGeneratedType', }, ], + defaultExample: getExample('address/shipping-address', ['jsx']), + examples: { + description: '', + examples: [getExample('address/billing-address', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/buyer-identity.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/buyer-identity.doc.ts index 6bf23cc51b..133bfcf2b6 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/buyer-identity.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/buyer-identity.doc.ts @@ -3,6 +3,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; import { STANDARD_API_PROPERTIES_DESCRIPTION, REQUIRES_PROTECTED_CUSTOMER_DATA_LEVEL_2, + getExample, getLinksByTag, } from '../helper.docs'; @@ -45,6 +46,11 @@ const data: ReferenceEntityTemplateSchema = { type: 'UsePurchasingCompanyGeneratedType', }, ], + defaultExample: getExample('buyer-identity/customer-info', ['jsx']), + examples: { + description: '', + examples: [getExample('buyer-identity/purchasing-company', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cart-lines.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cart-lines.doc.ts index 8c4186036d..55bb437d46 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cart-lines.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cart-lines.doc.ts @@ -57,6 +57,10 @@ const data: ReferenceEntityTemplateSchema = { }, ], defaultExample: getExample('cart-line-item/default', ['jsx']), + examples: { + description: '', + examples: [getExample('cart-lines/summary', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/checkout-token.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/checkout-token.doc.ts index b5d7d260b7..f55c7eff05 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/checkout-token.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/checkout-token.doc.ts @@ -2,6 +2,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; import { getLinksByTag, + getExample, STANDARD_API_PROPERTIES_DESCRIPTION, } from '../helper.docs'; @@ -24,6 +25,11 @@ const data: ReferenceEntityTemplateSchema = { type: 'UseCheckoutTokenGeneratedType', }, ], + defaultExample: getExample('checkout-token/default', ['jsx']), + examples: { + description: '', + examples: [getExample('checkout-token/support-message', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cost.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cost.doc.ts index 19ec36bd62..bdb24c96a0 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cost.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/cost.doc.ts @@ -2,6 +2,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; import { getLinksByTag, + getExample, STANDARD_API_PROPERTIES_DESCRIPTION, } from '../helper.docs'; @@ -43,6 +44,11 @@ const data: ReferenceEntityTemplateSchema = { type: 'UseTotalAmountGeneratedType', }, ], + defaultExample: getExample('cost/order-summary', ['jsx']), + examples: { + description: '', + examples: [getExample('cost/free-shipping-goal', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/customer-privacy.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/customer-privacy.doc.ts index 265a9db438..21491dffee 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/customer-privacy.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/customer-privacy.doc.ts @@ -32,6 +32,10 @@ const data: ReferenceEntityTemplateSchema = { }, ], defaultExample: getExample('customer-privacy/default', ['jsx']), + examples: { + description: '', + examples: [getExample('customer-privacy/consent-banner', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/discounts.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/discounts.doc.ts index aead1ea837..aa974fbdbd 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/discounts.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/discounts.doc.ts @@ -2,6 +2,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; import { CHECKOUT_API_PROPERTIES_DESCRIPTION, + getExample, getLinksByTag, STANDARD_API_PROPERTIES_DESCRIPTION, } from '../helper.docs'; @@ -41,6 +42,11 @@ const data: ReferenceEntityTemplateSchema = { type: 'UseDiscountCodesGeneratedType', }, ], + defaultExample: getExample('discounts/discount-codes', ['jsx']), + examples: { + description: '', + examples: [getExample('discounts/apply-discount', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/gift-cards.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/gift-cards.doc.ts index 778671d4e6..9ef0ee9686 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/gift-cards.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/gift-cards.doc.ts @@ -2,6 +2,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; import { CHECKOUT_API_PROPERTIES_DESCRIPTION, + getExample, getLinksByTag, STANDARD_API_PROPERTIES_DESCRIPTION, } from '../helper.docs'; @@ -35,6 +36,11 @@ const data: ReferenceEntityTemplateSchema = { type: 'UseApplyGiftCardChangeGeneratedType', }, ], + defaultExample: getExample('gift-cards/applied-gift-cards', ['jsx']), + examples: { + description: '', + examples: [getExample('gift-cards/apply-gift-card', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/note.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/note.doc.ts index 1f2af6f39f..2a8fc760f5 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/note.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/note.doc.ts @@ -2,6 +2,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; import { CHECKOUT_API_PROPERTIES_DESCRIPTION, + getExample, getLinksByTag, STANDARD_API_PROPERTIES_DESCRIPTION, } from '../helper.docs'; @@ -36,6 +37,11 @@ const data: ReferenceEntityTemplateSchema = { type: 'UseApplyNoteChangeGeneratedType', }, ], + defaultExample: getExample('note/display-note', ['jsx']), + examples: { + description: '', + examples: [getExample('note/update-note', ['jsx'])], + }, related: getLinksByTag('apis'), }; diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/order.doc.ts b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/order.doc.ts index 03d95ba829..886f0d7fa9 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/apis/order.doc.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/apis/order.doc.ts @@ -24,6 +24,10 @@ const data: ReferenceEntityTemplateSchema = { subCategory: 'Checkout APIs', type: 'API', defaultExample: getExample('order-confirmation/default', ['jsx']), + examples: { + description: '', + examples: [getExample('order-confirmation/first-order', ['jsx'])], + }, definitions: [ { title: 'OrderConfirmationApi', diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/address/billing-address.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/address/billing-address.example.tsx new file mode 100644 index 0000000000..6cdd4d5237 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/address/billing-address.example.tsx @@ -0,0 +1,31 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useBillingAddress} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const billingAddress = useBillingAddress(); + + if (!billingAddress) { + return null; + } + + const formattedAddress = [ + billingAddress.address1, + billingAddress.city, + billingAddress.provinceCode, + billingAddress.countryCode, + ] + .filter(Boolean) + .join(', '); + + return ( + + {formattedAddress} + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/address/shipping-address.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/address/shipping-address.example.tsx new file mode 100644 index 0000000000..d5ae21ad72 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/address/shipping-address.example.tsx @@ -0,0 +1,30 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useShippingAddress} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const shippingAddress = useShippingAddress(); + + if (!shippingAddress) { + return ( + + No shipping address has been entered yet. + + ); + } + + return ( + + + Shipping to: {shippingAddress.city},{' '} + {shippingAddress.provinceCode},{' '} + {shippingAddress.countryCode} + + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/buyer-identity/customer-info.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/buyer-identity/customer-info.example.tsx new file mode 100644 index 0000000000..38501a9b1f --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/buyer-identity/customer-info.example.tsx @@ -0,0 +1,30 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useCustomer} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const customer = useCustomer(); + + if (!customer) { + return null; + } + + const name = + customer.fullName ?? 'Customer'; + + return ( + + {name}, you have {customer.ordersCount}{' '} + previous{' '} + {customer.ordersCount === 1 + ? 'order' + : 'orders'}{' '} + with us. + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/buyer-identity/purchasing-company.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/buyer-identity/purchasing-company.example.tsx new file mode 100644 index 0000000000..508b048e28 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/buyer-identity/purchasing-company.example.tsx @@ -0,0 +1,25 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {usePurchasingCompany} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const purchasingCompany = usePurchasingCompany(); + + if (!purchasingCompany) { + return null; + } + + return ( + + Purchasing on behalf of{' '} + {purchasingCompany.company.name},{' '} + location:{' '} + {purchasingCompany.location.name} + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cart-lines/summary.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cart-lines/summary.example.tsx new file mode 100644 index 0000000000..ab7ff92637 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cart-lines/summary.example.tsx @@ -0,0 +1,42 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useCartLines} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const cartLines = useCartLines(); + + const totalQuantity = cartLines.reduce( + (sum, line) => sum + line.quantity, + 0, + ); + + return ( + + + {totalQuantity}{' '} + {totalQuantity === 1 ? 'item' : 'items'}{' '} + in your cart + + {cartLines.map((line) => ( + + + {line.merchandise.title} × {line.quantity} + + + {line.cost.totalAmount.amount}{' '} + {line.cost.totalAmount.currencyCode} + + + ))} + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/checkout-token/default.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/checkout-token/default.example.tsx new file mode 100644 index 0000000000..49ec70c168 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/checkout-token/default.example.tsx @@ -0,0 +1,22 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useCheckoutToken} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const checkoutToken = useCheckoutToken(); + + if (!checkoutToken) { + return null; + } + + return ( + + Checkout reference: {checkoutToken} + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/checkout-token/support-message.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/checkout-token/support-message.example.tsx new file mode 100644 index 0000000000..2905655c22 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/checkout-token/support-message.example.tsx @@ -0,0 +1,24 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useCheckoutToken} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const checkoutToken = useCheckoutToken(); + + if (!checkoutToken) { + return null; + } + + return ( + + Please include reference ID{' '} + {checkoutToken}{' '} + when contacting support. + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cost/free-shipping-goal.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cost/free-shipping-goal.example.tsx new file mode 100644 index 0000000000..a58ab439dc --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cost/free-shipping-goal.example.tsx @@ -0,0 +1,32 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useSubtotalAmount} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +const FREE_SHIPPING_THRESHOLD = 100; + +function Extension() { + const subtotal = useSubtotalAmount(); + const remaining = + FREE_SHIPPING_THRESHOLD - subtotal.amount; + + if (remaining <= 0) { + return ( + + You qualify for free shipping! + + ); + } + + return ( + + Add {remaining.toFixed(2)}{' '} + {subtotal.currencyCode} more to get free + shipping + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cost/order-summary.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cost/order-summary.example.tsx new file mode 100644 index 0000000000..4fae71028b --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/cost/order-summary.example.tsx @@ -0,0 +1,44 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import { + useSubtotalAmount, + useTotalAmount, + useTotalShippingAmount, + useTotalTaxAmount, +} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const subtotal = useSubtotalAmount(); + const totalShipping = useTotalShippingAmount(); + const totalTax = useTotalTaxAmount(); + const total = useTotalAmount(); + + return ( + + + Subtotal: {subtotal.amount}{' '} + {subtotal.currencyCode} + + {totalShipping && ( + + Shipping: {totalShipping.amount}{' '} + {totalShipping.currencyCode} + + )} + {totalTax && ( + + Tax: {totalTax.amount}{' '} + {totalTax.currencyCode} + + )} + + Total: {total.amount} {total.currencyCode} + + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/customer-privacy/consent-banner.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/customer-privacy/consent-banner.example.tsx new file mode 100644 index 0000000000..73f0c11751 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/customer-privacy/consent-banner.example.tsx @@ -0,0 +1,30 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useCustomerPrivacy} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const customerPrivacy = useCustomerPrivacy(); + + const {analytics, marketing} = + customerPrivacy.visitorConsent; + + if ( + analytics === undefined || + marketing === undefined + ) { + return ( + + We use cookies to improve your experience. + Update your preferences in your browser + settings. + + ); + } + + return null; +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/discounts/apply-discount.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/discounts/apply-discount.example.tsx new file mode 100644 index 0000000000..4baf026f06 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/discounts/apply-discount.example.tsx @@ -0,0 +1,50 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; +import {useState} from 'preact/hooks'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const [code, setCode] = useState(''); + + const canUpdate = + shopify.instructions.value.discounts + .canUpdateDiscountCodes; + + async function applyDiscount() { + if (!code) return; + + const result = + await shopify.applyDiscountCodeChange({ + type: 'addDiscountCode', + code, + }); + + if (result.type === 'error') { + console.error(result.message); + } else { + setCode(''); + } + } + + if (!canUpdate) { + return null; + } + + return ( + + + setCode(e.currentTarget.value) + } + /> + + Apply discount + + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/discounts/discount-codes.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/discounts/discount-codes.example.tsx new file mode 100644 index 0000000000..2cecdc4547 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/discounts/discount-codes.example.tsx @@ -0,0 +1,29 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useDiscountCodes} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const discountCodes = useDiscountCodes(); + + if (discountCodes.length === 0) { + return null; + } + + return ( + + + Applied discount codes: + + {discountCodes.map((discount) => ( + + {discount.code} + + ))} + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/gift-cards/applied-gift-cards.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/gift-cards/applied-gift-cards.example.tsx new file mode 100644 index 0000000000..cf430c4a64 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/gift-cards/applied-gift-cards.example.tsx @@ -0,0 +1,39 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useAppliedGiftCards} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const giftCards = useAppliedGiftCards(); + + if (giftCards.length === 0) { + return null; + } + + return ( + + + Applied gift cards: + + {giftCards.map((card) => ( + + + •••• {card.lastCharacters} + + + -{card.amountUsed.amount}{' '} + {card.amountUsed.currencyCode} + + + ))} + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/gift-cards/apply-gift-card.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/gift-cards/apply-gift-card.example.tsx new file mode 100644 index 0000000000..1b68c802df --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/gift-cards/apply-gift-card.example.tsx @@ -0,0 +1,42 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; +import {useState} from 'preact/hooks'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const [code, setCode] = useState(''); + + async function applyGiftCard() { + if (!code) return; + + const result = + await shopify.applyGiftCardChange({ + type: 'addGiftCard', + code, + }); + + if (result.type === 'error') { + console.error(result.message); + } else { + setCode(''); + } + } + + return ( + + + setCode(e.currentTarget.value) + } + /> + + Apply gift card + + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/note/display-note.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/note/display-note.example.tsx new file mode 100644 index 0000000000..0294cefc0c --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/note/display-note.example.tsx @@ -0,0 +1,22 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +import {useNote} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const note = useNote(); + + if (!note) { + return null; + } + + return ( + + {note} + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/note/update-note.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/note/update-note.example.tsx new file mode 100644 index 0000000000..345558d63d --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/note/update-note.example.tsx @@ -0,0 +1,54 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; +import {useState} from 'preact/hooks'; + +import {useNote} from '@shopify/ui-extensions/checkout/preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const currentNote = useNote(); + const [note, setNote] = useState( + currentNote ?? '', + ); + + const canUpdate = + shopify.instructions.value.notes + .canUpdateNote; + + async function saveNote() { + const result = note + ? await shopify.applyNoteChange({ + type: 'updateNote', + note, + }) + : await shopify.applyNoteChange({ + type: 'removeNote', + }); + + if (result.type === 'error') { + console.error(result.message); + } + } + + if (!canUpdate) { + return null; + } + + return ( + + + setNote(e.currentTarget.value) + } + /> + + Save note + + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/checkout/reference/examples/order-confirmation/first-order.example.tsx b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/order-confirmation/first-order.example.tsx new file mode 100644 index 0000000000..f3afa383d7 --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/examples/order-confirmation/first-order.example.tsx @@ -0,0 +1,27 @@ +import '@shopify/ui-extensions/preact'; +import {render} from 'preact'; + +export default function extension() { + render(, document.body); +} + +function Extension() { + const {order, isFirstOrder} = + shopify.orderConfirmation.value; + + if (isFirstOrder) { + return ( + + Thanks for your first order! Use code + WELCOME10 for 10% off your next purchase. + + ); + } + + return ( + + Order {order.id} confirmed. Thank you for + shopping with us! + + ); +} 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 e00ced41ae..12d0026a37 100644 --- a/packages/ui-extensions/docs/surfaces/checkout/reference/helper.docs.ts +++ b/packages/ui-extensions/docs/surfaces/checkout/reference/helper.docs.ts @@ -154,7 +154,7 @@ export function getExamples( 'shipping-option-item/default': { description: '', codeblock: { - title: 'Reading the selected shipping option', + title: 'Read the selected shipping option', tabs: getExtensionCodeTabs('shipping-option-item/default'), }, }, @@ -168,7 +168,7 @@ export function getExamples( 'purchase.checkout.pickup-location-option-item.render-after/default': { description: '', codeblock: { - title: 'Reading the selected pickup location option', + title: 'Read the selected pickup location option', tabs: getExtensionCodeTabs( 'purchase.checkout.pickup-location-option-item.render-after/default', ), @@ -177,7 +177,7 @@ export function getExamples( 'order-confirmation/default': { description: '', codeblock: { - title: 'Order confirmation', + title: 'Display order confirmation details', tabs: getExtensionCodeTabs('order-confirmation/default'), }, }, @@ -352,14 +352,14 @@ See [Storefront GraphQL API endpoints](/docs/api/storefront#endpoints) for more 'payments/use-available-payment-options': { description: '', codeblock: { - title: 'Available payment options', + title: 'Read available payment options', tabs: getExtensionCodeTabs('payments/use-available-payment-options'), }, }, 'payments/use-selected-payment-options': { description: '', codeblock: { - title: 'Selected payment options', + title: 'Read selected payment options', tabs: getExtensionCodeTabs('payments/use-selected-payment-options'), }, }, @@ -435,21 +435,21 @@ The contents of the token are signed using your shared app secret. The optional 'delivery-groups': { description: '', codeblock: { - title: 'Delivery groups', + title: 'Read delivery groups', tabs: getExtensionCodeTabs('delivery-groups'), }, }, 'delivery-group': { description: '', codeblock: { - title: 'Delivery group', + title: 'Read a delivery group', tabs: getExtensionCodeTabs('delivery-group'), }, }, 'attribute-values': { description: '', codeblock: { - title: 'Attribute values', + title: 'Read attribute values', tabs: getExtensionCodeTabs('attribute-values'), }, }, @@ -457,7 +457,7 @@ The contents of the token are signed using your shared app secret. The optional description: 'You can add or remove cart and checkout attributes by using the `applyAttributeChange` API.', codeblock: { - title: 'Applying changes to attributes', + title: 'Apply changes to attributes', tabs: getExtensionCodeTabs('attributes/attribute-change'), }, }, @@ -502,6 +502,91 @@ The contents of the token are signed using your shared app secret. The optional description: 'Use the `$app` format to request metafields that are owned by your app in your extension configuration file. Your app exclusively controls structure, data, permissions and optional features for this type of metafield. See [app owned metafields](/docs/apps/build/custom-data/ownership#reserved-prefixes) for more information.', }), + ...createExample('address/shipping-address', { + title: 'Read the shipping address', + description: + "Use the `useShippingAddress` hook to access the buyer's shipping destination. The city, province, and country code are displayed, with a fallback banner when no address has been entered yet.", + }), + ...createExample('address/billing-address', { + title: 'Read the billing address', + description: + 'Use the `useBillingAddress` hook to access the billing address. Optional fields like address line, city, province, and country are filtered and joined into a single formatted string.', + }), + ...createExample('buyer-identity/customer-info', { + title: 'Display customer information', + description: + "Use the `useCustomer` hook to greet returning buyers by name. The customer's order count is included in a welcome banner, with a safe fallback when `fullName` is unavailable.", + }), + ...createExample('buyer-identity/purchasing-company', { + title: 'Display B2B purchasing company details', + description: + 'Use the `usePurchasingCompany` hook to identify business buyers during checkout. The company name and location are displayed in a banner, and the extension renders nothing for non-B2B sessions.', + }), + ...createExample('checkout-token/default', { + title: 'Access the checkout token', + description: + 'Use the `useCheckoutToken` hook to retrieve the stable identifier for the current checkout session. The token renders as inline text and can be sent to backend systems for event correlation.', + }), + ...createExample('checkout-token/support-message', { + title: 'Display a checkout reference for support', + description: + 'Use the `useCheckoutToken` hook to present a reference ID in a help banner. Buyers can share this token with customer support to speed up issue resolution.', + }), + ...createExample('cost/order-summary', { + title: 'Display an order cost summary', + description: + 'Use `useSubtotalAmount`, `useTotalShippingAmount`, `useTotalTaxAmount`, and `useTotalAmount` to render a full cost breakdown. Shipping and tax are conditionally rendered since they may be unavailable on earlier checkout steps.', + }), + ...createExample('cost/free-shipping-goal', { + title: 'Show progress toward free shipping', + description: + 'Use the `useSubtotalAmount` hook to compare the cart total against a threshold. The remaining amount is calculated and displayed, switching to a success banner once the buyer qualifies.', + }), + ...createExample('discounts/discount-codes', { + title: 'Display applied discount codes', + description: + 'Use the `useDiscountCodes` hook to list all active codes on the checkout. Each code is rendered in a stack, and the extension returns nothing when no codes are applied.', + }), + ...createExample('discounts/apply-discount', { + title: 'Apply and remove discount codes', + description: + 'Use the `applyDiscountCodeChange` API to submit a discount code entered by the buyer. The extension checks [`instructions.discounts.canUpdateDiscountCodes`](/docs/api/checkout-ui-extensions/apis/cart-instructions) before rendering, and clears the input on success.', + }), + ...createExample('gift-cards/applied-gift-cards', { + title: 'Display applied gift cards', + description: + 'Use the `useAppliedGiftCards` hook to list each gift card on the checkout. The last four characters and amount deducted are displayed side by side for each card.', + }), + ...createExample('gift-cards/apply-gift-card', { + title: 'Apply a gift card', + description: + 'Use the `applyGiftCardChange` API to submit a gift card code entered by the buyer. The input is cleared on success, and errors are logged when the code is invalid.', + }), + ...createExample('note/display-note', { + title: 'Display the order note', + description: + 'Use the `useNote` hook to read the note attached to the checkout. The note is displayed in a banner only when one exists, and the extension renders nothing otherwise.', + }), + ...createExample('note/update-note', { + title: 'Add or update an order note', + description: + 'Use the `applyNoteChange` API to let buyers write delivery or gift instructions. The extension checks [`instructions.notes.canUpdateNote`](/docs/api/checkout-ui-extensions/apis/cart-instructions) before rendering, and supports both updating and removing the note.', + }), + ...createExample('cart-lines/summary', { + title: 'Display cart line item details', + description: + 'Use the `useCartLines` hook to render a breakdown of all items in the cart. Each line shows its merchandise title, quantity, and total price, with a summary count at the top.', + }), + ...createExample('customer-privacy/consent-banner', { + title: 'Display a consent banner', + description: + 'Use the `useCustomerPrivacy` hook to check whether the buyer has responded to analytics and marketing consent. A banner is shown when either preference is still undefined. See the [`customer_privacy` capability](/docs/api/checkout-ui-extensions/configuration#collect-buyer-consent) for configuration.', + }), + ...createExample('order-confirmation/first-order', { + title: 'Display a first-time buyer offer', + description: + "Use `shopify.orderConfirmation` to check whether this is the buyer's first order. A success banner with a discount code is shown for new customers, and a standard confirmation message is shown for returning buyers.", + }), }; }