diff --git a/src/content/docs/dropins/checkout/tutorials/add-payment-method.mdx b/src/content/docs/dropins/checkout/tutorials/add-payment-method.mdx
index 0415b2ed..54561f9d 100644
--- a/src/content/docs/dropins/checkout/tutorials/add-payment-method.mdx
+++ b/src/content/docs/dropins/checkout/tutorials/add-payment-method.mdx
@@ -154,4 +154,8 @@ CheckoutProvider.render(PlaceOrder, {
```
-
\ No newline at end of file
+
+
+## Example
+
+See [`blocks/commerce-checkout-braintree`](https://github.com/hlxsites/aem-boilerplate-commerce/tree/demos/blocks/commerce-checkout-braintree) in the `demos` branch of the boilerplate repository for complete JS and CSS code for the Braintree payment method checkout flow.
\ No newline at end of file
diff --git a/src/content/docs/dropins/checkout/tutorials/buy-online-pickup-in-store.mdx b/src/content/docs/dropins/checkout/tutorials/buy-online-pickup-in-store.mdx
index 589839f8..1df5f2a1 100644
--- a/src/content/docs/dropins/checkout/tutorials/buy-online-pickup-in-store.mdx
+++ b/src/content/docs/dropins/checkout/tutorials/buy-online-pickup-in-store.mdx
@@ -185,400 +185,4 @@ After a user selects **In-store pickup** and chooses a location, the pickup form
## Example
-The following files show the complete JS and CSS code for the BOPIS checkout flow:
-
-
-
-
- ```js title="commerce-checkout-bopis.js" ins={"2":32} ins={"2":67} ins={"3":149} ins={"4":162} ins={"4":220} ins={"5":123} ins={"6":239}
- // Dropin Tools
- import { debounce } from '@dropins/tools/lib.js';
- import { events } from '@dropins/tools/event-bus.js';
- import {
- RadioButton,
- ToggleButton,
- provider as UI,
- } from '@dropins/tools/components.js';
-
- // Cart Dropin Modules
- import CartSummaryList from '@dropins/storefront-cart/containers/CartSummaryList.js';
- import { OrderSummary } from '@dropins/storefront-cart/containers/OrderSummary.js';
- import { render as cartProvider } from '@dropins/storefront-cart/render.js';
-
- // Checkout Dropin Modules
- import * as checkoutApi from '@dropins/storefront-checkout/api.js';
- import LoginForm from '@dropins/storefront-checkout/containers/LoginForm.js';
- import PaymentMethods from '@dropins/storefront-checkout/containers/PaymentMethods.js';
- import PlaceOrder from '@dropins/storefront-checkout/containers/PlaceOrder.js';
- import ShippingMethods from '@dropins/storefront-checkout/containers/ShippingMethods.js';
- import { render as checkoutProvider } from '@dropins/storefront-checkout/render.js';
-
- // Account Dropin Modules
- import AddressForm from '@dropins/storefront-account/containers/AddressForm.js';
- import { render as accountProvider } from '@dropins/storefront-account/render.js';
-
- const DEBOUNCE_TIME = 1000;
- const LOGIN_FORM_NAME = 'login-form';
- const SHIPPING_FORM_NAME = 'selectedShippingAddress';
- const BILLING_FORM_NAME = 'selectedBillingAddress';
-
- // Step 2: Update content fragment
- const fragment = document.createRange().createContextualFragment(`
-
- `);
-
- export const $root = fragment.querySelector('.checkout__content');
- export const $heading = fragment.querySelector('.checkout__heading');
- export const $headingTitle = fragment.querySelector('.checkout__heading-title');
- export const $main = fragment.querySelector('.checkout__main');
- export const $login = fragment.querySelector('.checkout__login');
- // Step 2: Update content fragment
- export const $deliveryButton = fragment.querySelector('.checkout-delivery-method__delivery-button');
- export const $inStorePickupButton = fragment.querySelector('.checkout-delivery-method__in-store-pickup-button');
- export const $inStorePickup = fragment.querySelector('.checkout__in-store-pickup');
- export const $shippingForm = fragment.querySelector('.checkout__shipping-form');
- export const $billToShippingAddress = fragment.querySelector(
- '.checkout__bill-to-shipping-address',
- );
- export const $shippingMethods = fragment.querySelector(
- '.checkout__shipping-methods',
- );
- export const $paymentMethods = fragment.querySelector(
- '.checkout__payment-methods',
- );
- export const $billingForm = fragment.querySelector('.checkout__billing-form');
- export const $aside = fragment.querySelector('.checkout__aside');
- export const $orderSummary = fragment.querySelector('.checkout__order-summary');
- export const $cartSummaryList = fragment.querySelector('.cart-summary-list');
- export const $placeOrder = fragment.querySelector('.checkout__place-order');
-
- function setAddressOnCart(values, setAddressApi) {
- const { data, isDataValid } = values;
- const isNewAddress = !data?.id;
-
- if (!isDataValid) return;
-
- const customAttributes = data.customAttributes
- ? Object.entries(data.customAttributes).map(([code, value]) => ({
- code,
- value: value.toString(),
- }))
- : [];
-
- const address = !isNewAddress
- ? { customerAddressId: data.id }
- : {
- saveInAddressBook: data.saveAddressBook,
- address: {
- city: data.city,
- company: data?.company,
- countryCode: data.countryCode,
- customAttributes,
- firstName: data.firstName,
- lastName: data.lastName,
- postcode: data.postcode,
- region: data?.region?.regionCode,
- regionId: data?.region?.regionId,
- street: data.street,
- telephone: data.telephone,
- vatId: data.vatId,
- },
- };
-
- setAddressApi(address);
- }
-
- // Step 5: Fetch pickup locations
- async function fetchPickupLocations() {
- return checkoutApi
- .fetchGraphQl(
- `query pickupLocations {
- pickupLocations {
- items {
- name
- pickup_location_code
- }
- total_count
- }
- }`,
- { method: 'GET', cache: 'no-cache' },
- )
- .then((res) => res.data.pickupLocations.items);
- }
-
- export default async function decorate(block) {
- // Initializers
- import('../../scripts/initializers/account.js');
- import('../../scripts/initializers/checkout.js');
-
- events.on('checkout/initialized', async (checkoutData) => {
- const [
- _login,
- // Step 3: Add toggle buttons
- deliveryButton,
- inStorePickupButton,
- _shippingForm,
- _billingForm,
- _shippingMethods,
- _paymentMethods,
- _orderSummary,
- _cartSummary,
- _placeOrder,
- ] = await Promise.all([
- checkoutProvider.render(LoginForm, { name: LOGIN_FORM_NAME })($login),
-
- // Step 3: Add toggle buttons
- UI.render(ToggleButton, {
- label: 'Delivery',
- onChange: () => onToggle('delivery'),
- })($deliveryButton),
-
- UI.render(ToggleButton, {
- label: 'In-store Pickup',
- onChange: () => onToggle('in-store-pickup'),
- })($inStorePickupButton),
-
- accountProvider.render(AddressForm, {
- addressesFormTitle: 'Shipping address',
- className: 'checkout-shipping-form__address-form',
- formName: SHIPPING_FORM_NAME,
- hideActionFormButtons: true,
- isOpen: true,
- showBillingCheckBox: false,
- showShippingCheckBox: false,
- onChange: debounce((values) => {
- setAddressOnCart(values, checkoutApi.setShippingAddress);
-
- const hasCartShippingAddress = Boolean(
- checkoutData.shippingAddresses?.[0],
- );
- const { data, isDataValid } = values;
-
- if (hasCartShippingAddress || isDataValid) return;
-
- const criteria = {
- country_code: data.countryCode,
- region_name: String(data.region.regionCode || ''),
- region_id: String(data.region.regionId || ''),
- };
- checkoutApi.estimateShippingMethods({ criteria });
- }, DEBOUNCE_TIME),
- })($shippingForm),
-
- accountProvider.render(AddressForm, {
- addressesFormTitle: 'Billing address',
- className: 'checkout-billing-form__address-form',
- formName: BILLING_FORM_NAME,
- hideActionFormButtons: true,
- isOpen: true,
- showBillingCheckBox: false,
- showShippingCheckBox: false,
- onChange: debounce((values) => {
- setAddressOnCart(values, checkoutApi.setBillingAddress);
- }, DEBOUNCE_TIME),
- })($billingForm),
-
- checkoutProvider.render(ShippingMethods)($shippingMethods),
- checkoutProvider.render(PaymentMethods)($paymentMethods),
- cartProvider.render(OrderSummary)($orderSummary),
- cartProvider.render(CartSummaryList)($cartSummaryList),
- checkoutProvider.render(PlaceOrder)($placeOrder),
- ]);
-
- // Step 4: Toggle functionality
- async function onToggle(type) {
- if (type === 'delivery') {
- deliveryButton.setProps((prev) => ({ ...prev, selected: true }));
- inStorePickupButton.setProps((prev) => ({ ...prev, selected: false }));
- $shippingForm.removeAttribute('hidden');
- $shippingMethods.removeAttribute('hidden');
- $inStorePickup.setAttribute('hidden', '');
- } else {
- inStorePickupButton.setProps((prev) => ({ ...prev, selected: true }));
- deliveryButton.setProps((prev) => ({ ...prev, selected: false }));
- $shippingForm.setAttribute('hidden', '');
- $shippingMethods.setAttribute('hidden', '');
- $inStorePickup.removeAttribute('hidden');
- }
- }
-
- onToggle('delivery');
-
- // Step 6: Render pickup options
- const pickupLocations = await fetchPickupLocations();
-
- pickupLocations.forEach((location) => {
- const { name, pickup_location_code } = location;
- const locationRadiobutton = document.createElement('div');
-
- UI.render(RadioButton, {
- label: name,
- name: 'pickup-location',
- value: name,
- onChange: () => {
- checkoutApi.setShippingAddress({
- pickupLocationCode: pickup_location_code,
- });
- },
- })(locationRadiobutton);
-
- $inStorePickup.appendChild(locationRadiobutton);
- });
-
- $root.style.display = 'grid';
- });
-
- block.appendChild(fragment);
- }
- ```
-
-
-
- ```css title="commerce-checkout-bopis.css"
- .checkout__content {
- display: grid;
- align-items: start;
- grid-template-columns: repeat(var(--grid-4-columns), 1fr);
- gap: var(--spacing-big) var(--grid-4-gutters);
- }
-
- .checkout__heading-title {
- font: var(--type-headline-1-font);
- letter-spacing: var(--type-headline-1-letter-spacing);
- color: var(--color-neutral-800);
- }
-
- .checkout__shipping-form:empty,
- .checkout__shipping-methods:has(> div:empty),
- .checkout__bill-to-shipping-address:has(> div:empty) {
- display: none;
- }
-
- .checkout__main {
- display: grid;
- grid-column: 1 / span 7;
- row-gap: var(--spacing-xbig);
- }
-
- .checkout__aside {
- display: grid;
- grid-column: 9 / span 4;
- gap: var(--spacing-xbig);
- }
-
- .checkout__place-order {
- display: grid;
- grid-column: 1 / span 12;
- justify-items: center;
- }
-
- /* To add into the cart dropin **/
- .cart-order-summary__taxes.dropin-accordion .dropin-divider {
- margin: var(--spacing-medium) auto;
- }
-
- @media only screen and (width >= 320px) and (width <= 768px) {
- .checkout__content {
- grid-template-columns: 1fr;
- gap: var(--spacing-big) 0;
- }
- }
-
- /* CartSummaryList */
-
- .cart-summary-list {
- padding: var(--spacing-medium);
- background-color: var(--color-neutral-200);
- }
-
- .cart-summary-list__heading {
- display: flex;
- justify-content: space-between;
- }
-
- .cart-summary-list__heading-text {
- font: var(--type-headline-2-strong-font);
- letter-spacing: var(--type-headline-2-strong-letter-spacing);
- color: var(--color-neutral-800);
- }
-
- .cart-cart-summary-list__heading {
- row-gap: var(--spacing-small);
- padding-top: 0;
- }
-
- .cart-cart-summary-list__heading-text {
- font: var(--type-headline-2-strong-font);
- letter-spacing: var(--type-headline-2-strong-letter-spacing);
- color: var(--color-neutral-800);
- }
-
- .cart-summary-list__edit {
- font: var(--type-body-2-strong-font);
- letter-spacing: var(--type-body-2-strong-letter-spacing);
- }
-
- /* BOPIS specific styles */
-
- .checkout__in-store-pickup {
- display: flex;
- flex-direction: column;
- gap: var(--spacing-small);
- }
-
- .checkout__in-store-pickup[hidden] {
- display:none;
- }
-
- .checkout-delivery-method__title {
- color: var(--color-neutral-800);
- font: var(--type-headline-2-default-font);
- letter-spacing: var(--type-headline-2-default-letter-spacing);
- margin: 0 0 var(--spacing-medium) 0;
- }
-
- .checkout-delivery-method__toogle-buttons {
- display: flex;
- gap: var(--spacing-big);
- max-width: 600px;
- }
-
- .checkout-delivery-method__delivery-button {
- flex: 1;
- }
-
- .checkout-delivery-method__in-store-pickup-button {
- flex: 1;
- }
-
- .checkout__content {
- display: none;
- }
- ```
-
-
\ No newline at end of file
+See [`blocks/commerce-checkout-bopis`](https://github.com/hlxsites/aem-boilerplate-commerce/tree/demos/blocks/commerce-checkout-bopis) in the `demos` branch of the boilerplate repository for complete JS and CSS code for the BOPIS checkout flow.
\ No newline at end of file
diff --git a/src/content/docs/dropins/checkout/tutorials/multi-step.mdx b/src/content/docs/dropins/checkout/tutorials/multi-step.mdx
index 5e1756ce..4b369865 100644
--- a/src/content/docs/dropins/checkout/tutorials/multi-step.mdx
+++ b/src/content/docs/dropins/checkout/tutorials/multi-step.mdx
@@ -735,670 +735,4 @@ The last step to complete the multi-step checkout process is to create an order
## Example
-The following files show the complete JS and CSS code for the multi-step checkout flow:
-
-```js title="commerce-checkout-multi-step.js"
-// Dropin Tools
-import { events } from '@dropins/tools/event-bus.js';
-import { initializers } from '@dropins/tools/initializer.js';
-
-// Dropin Components
-import { Button, Header, provider as UI } from '@dropins/tools/components.js';
-
-// Auth Dropin
-import SignUp from '@dropins/storefront-auth/containers/SignUp.js';
-import { render as AuthProvider } from '@dropins/storefront-auth/render.js';
-
-// Account Dropin
-import AddressForm from '@dropins/storefront-account/containers/AddressForm.js';
-import { render as AccountProvider } from '@dropins/storefront-account/render.js';
-
-// Cart Dropin
-import * as cartApi from '@dropins/storefront-cart/api.js';
-import { CartSummaryList } from '@dropins/storefront-cart/containers/CartSummaryList.js';
-import EmptyCart from '@dropins/storefront-cart/containers/EmptyCart.js';
-import { OrderSummary } from '@dropins/storefront-cart/containers/OrderSummary.js';
-import { render as CartProvider } from '@dropins/storefront-cart/render.js';
-
-// Checkout Dropin
-import * as checkoutApi from '@dropins/storefront-checkout/api.js';
-import BillToShippingAddress from '@dropins/storefront-checkout/containers/BillToShippingAddress.js';
-import EstimateShipping from '@dropins/storefront-checkout/containers/EstimateShipping.js';
-import LoginForm from '@dropins/storefront-checkout/containers/LoginForm.js';
-import OrderConfirmationHeader from '@dropins/storefront-checkout/containers/OrderConfirmationHeader.js';
-import PaymentMethods from '@dropins/storefront-checkout/containers/PaymentMethods.js';
-import PlaceOrder from '@dropins/storefront-checkout/containers/PlaceOrder.js';
-import ShippingMethods from '@dropins/storefront-checkout/containers/ShippingMethods.js';
-
-import { render as CheckoutProvider } from '@dropins/storefront-checkout/render.js';
-
-// Order Dropin Modules
-import * as orderApi from '@dropins/storefront-order/api.js';
-import CustomerDetails from '@dropins/storefront-order/containers/CustomerDetails.js';
-import OrderCostSummary from '@dropins/storefront-order/containers/OrderCostSummary.js';
-import OrderProductList from '@dropins/storefront-order/containers/OrderProductList.js';
-import OrderStatus from '@dropins/storefront-order/containers/OrderStatus.js';
-import ShippingStatus from '@dropins/storefront-order/containers/ShippingStatus.js';
-import { render as OrderProvider } from '@dropins/storefront-order/render.js';
-import { getUserTokenCookie } from '../../scripts/initializers/index.js';
-
-// Block-level
-import {
- getCartAddress,
- getCartDeliveryMethod,
- setAddressOnCart,
-} from '../../scripts/checkout.js';
-import createModal from '../modal/modal.js';
-
-export default async function decorate(block) {
- // Initializers
- import('../../scripts/initializers/auth.js');
- import('../../scripts/initializers/checkout.js');
-
- const DEBOUNCE_TIME = 1000;
- const LOGIN_FORM_NAME = 'login-form';
- const SHIPPING_FORM_NAME = 'selectedShippingAddress';
- const BILLING_FORM_NAME = 'selectedBillingAddress';
- const SHIPPING_ADDRESS_DATA_KEY = `${SHIPPING_FORM_NAME}_addressData`;
- const BILLING_ADDRESS_DATA_KEY = `${BILLING_FORM_NAME}_addressData`;
-
- // Pre-fetch checkout store configuration
- const storeConfig = await checkoutApi.getStoreConfig();
-
- // Define the Layout for the Checkout
- const fragment = document.createRange().createContextualFragment(`
-
- `);
-
- const $heading = fragment.querySelector('.checkout__heading');
- const $emptyCart = fragment.querySelector('.checkout__empty-cart');
- const $content = fragment.querySelector('.checkout__content');
-
- const $orderSummary = fragment.querySelector('.checkout__order-summary');
- const $cartSummary = fragment.querySelector('.checkout__cart-summary');
-
- const $shippingTitle = fragment.querySelector('.checkout__shipping-title');
- const $login = fragment.querySelector('.checkout__login');
- const $shippingForm = fragment.querySelector('.checkout__shipping-form');
- const $continueToDeliveryBtn = fragment.querySelector(
- '.checkout__continue-to-delivery',
- );
-
- const $deliveryTitle = fragment.querySelector('.checkout__delivery-title');
- const $deliveryMethods = fragment.querySelector(
- '.checkout__delivery-methods',
- );
- const $continueToPaymentBtn = fragment.querySelector(
- '.checkout__continue-to-payment',
- );
-
- const $paymentTitle = fragment.querySelector('.checkout__payment-title');
- const $billToShipping = fragment.querySelector('.checkout__bill-to-shipping');
- const $billingForm = fragment.querySelector('.checkout__billing-form');
- const $paymentMethods = fragment.querySelector('.checkout__payment-methods');
- const $placeOrder = fragment.querySelector('.checkout__place-order');
-
- block.replaceChildren(fragment);
-
- // Render the initial containers
- const [
- heading,
- _shippingInfoHeading,
- __shippingMethodHeading,
- _paymentHeading,
- _loginForm,
- shippingFormSkeleton,
- continueToDeliveryBtn,
- orderSummary,
- _cartSummary,
- ] = await Promise.all([
- UI.render(Header, {
- title: 'Guest Checkout',
- size: 'large',
- divider: false,
- })($heading),
-
- UI.render(Header, {
- title: '1. SHIPPING INFORMATION',
- size: 'medium',
- divider: false,
- })($shippingTitle),
-
- UI.render(Header, {
- title: '2. SHIPPING METHOD',
- size: 'medium',
- divider: false,
- })($deliveryTitle),
-
- UI.render(Header, {
- title: '3. PAYMENT INFORMATION',
- size: 'medium',
- divider: false,
- })($paymentTitle),
-
- // render the initial containers
- CheckoutProvider.render(LoginForm, {
- name: LOGIN_FORM_NAME,
- })($login),
-
- AccountProvider.render(AddressForm, {
- isOpen: true,
- showFormLoader: true,
- })($shippingForm),
-
- UI.render(Button, {
- children: 'CONTINUE TO SHIPPING METHOD',
- disabled: true,
- onClick: async () => {
- await continueToDelivery();
- },
- })($continueToDeliveryBtn),
-
- CartProvider.render(OrderSummary)($orderSummary),
-
- CartProvider.render(CartSummaryList, {
- variant: 'secondary',
- slots: {
- Heading: (headingCtx) => {
- const title = 'Your Cart ({count})';
-
- const cartSummaryListHeading = document.createElement('div');
- cartSummaryListHeading.classList.add('cart-summary-list__heading');
-
- const cartSummaryListHeadingText = document.createElement('div');
- cartSummaryListHeadingText.classList.add(
- 'cart-summary-list__heading-text',
- );
-
- cartSummaryListHeadingText.innerText = title.replace(
- '({count})',
- headingCtx.count ? `(${headingCtx.count})` : '',
- );
- const editCartLink = document.createElement('a');
- editCartLink.classList.add('cart-summary-list__edit');
- editCartLink.href = '/cart';
- editCartLink.rel = 'noreferrer';
- editCartLink.innerText = 'Edit';
-
- cartSummaryListHeading.appendChild(cartSummaryListHeadingText);
- cartSummaryListHeading.appendChild(editCartLink);
- headingCtx.appendChild(cartSummaryListHeading);
-
- headingCtx.onChange((nextHeadingCtx) => {
- cartSummaryListHeadingText.innerText = title.replace(
- '({count})',
- nextHeadingCtx.count ? `(${nextHeadingCtx.count})` : '',
- );
- });
- },
- },
- })($cartSummary),
- ]);
-
- // Dynamic containers and components
-
- let modal;
-
- const showModal = async (content) => {
- modal = await createModal([content]);
- modal.showModal();
- };
-
- let emptyCart;
-
- const displayEmptyCart = async () => {
- if (emptyCart) return;
-
- heading.setProps((prev) => ({
- ...prev,
- title: 'Empty Cart',
- }));
-
- emptyCart = await CartProvider.render(EmptyCart, {
- routeCTA: () => '/',
- })($emptyCart);
-
- $content.classList.add('checkout__content--empty');
- };
-
- const removeEmptyCart = () => {
- if (!emptyCart) return;
-
- emptyCart.remove();
- emptyCart = null;
- $emptyCart.innerHTML = '';
-
- heading.setProps((prev) => ({
- ...prev,
- title: 'Guest Checkout',
- }));
-
- $content.classList.remove('checkout__content--empty');
- };
-
- let shippingForm;
-
- const continueToShipping = async (initialData = null) => {
- if (shippingForm) return;
-
- // cleanup
- shippingFormSkeleton.remove();
- $shippingForm.innerHTML = '';
-
- shippingForm = await AccountProvider.render(AddressForm, {
- addressesFormTitle: 'Shipping address',
- className: 'checkout-shipping-form__address-form',
- formName: SHIPPING_FORM_NAME,
- hideActionFormButtons: true,
- inputsDefaultValueSet: initialData ?? {
- countryCode: storeConfig.defaultCountry,
- },
- isOpen: true,
- onChange: setAddressOnCart({
- api: checkoutApi.setShippingAddress,
- debounceMs: DEBOUNCE_TIME,
- placeOrderBtn: placeOrder,
- }),
- showBillingCheckBox: false,
- showShippingCheckBox: false,
- })($shippingForm);
- };
-
- let deliveryMethods;
- let continueToPaymentBtn;
-
- const continueToDelivery = async () => {
- if (deliveryMethods) return;
-
- deliveryMethods = await CheckoutProvider.render(ShippingMethods, {
- hideOnVirtualCart: true,
- onCheckoutDataUpdate: () => {
- cartApi.refreshCart().catch(console.error);
- },
- })($deliveryMethods);
-
- orderSummary.setProps((prev) => ({
- ...prev,
- slots: {
- EstimateShipping: (esCtx) => {
- const estimateShippingForm = document.createElement('div');
- CheckoutProvider.render(EstimateShipping)(estimateShippingForm);
- esCtx.appendChild(estimateShippingForm);
- },
- },
- }));
-
- continueToDeliveryBtn.remove();
- $continueToDeliveryBtn.remove();
-
- continueToPaymentBtn = await UI.render(Button, {
- children: 'CONTINUE TO PAYMENT INFORMATION',
- disabled: true,
- onClick: async () => {
- await continueToPayment();
- },
- })($continueToPaymentBtn);
- };
-
- let billToShipping;
- let billingForm;
- let paymentMethods;
- let placeOrder;
-
- const continueToPayment = async () => {
- if (!billToShipping) {
- billToShipping = await CheckoutProvider.render(BillToShippingAddress, {
- hideOnVirtualCart: true,
- onChange: (checked) => {
- $billingForm.style.display = checked ? 'none' : 'block';
- },
- })($billToShipping);
- }
-
- if (!billingForm) {
- billingForm = await AccountProvider.render(AddressForm, {
- addressesFormTitle: 'Billing address',
- className: 'checkout-billing-form__address-form',
- formName: BILLING_FORM_NAME,
- hideActionFormButtons: true,
- isOpen: true,
- onChange: setAddressOnCart({
- api: checkoutApi.setBillingAddress,
- debounceMs: DEBOUNCE_TIME,
- placeOrderBtn: placeOrder,
- }),
- showBillingCheckBox: false,
- showShippingCheckBox: false,
- })($billingForm);
- }
-
- if (!paymentMethods) {
- paymentMethods = await CheckoutProvider.render(PaymentMethods)($paymentMethods);
- }
-
- if (!placeOrder) {
- placeOrder = await CheckoutProvider.render(PlaceOrder)($placeOrder);
- }
-
- continueToPaymentBtn.remove();
- $continueToPaymentBtn.remove();
- };
-
- const displayOrderConfirmation = async (orderData) => {
- // Define the Layout for the Order Confirmation
- const orderConfirmationFragment = document.createRange()
- .createContextualFragment(`
-
- `);
-
- // Order confirmation elements
- const $orderConfirmationHeader = orderConfirmationFragment.querySelector(
- '.order-confirmation__header',
- );
- const $orderStatus = orderConfirmationFragment.querySelector(
- '.order-confirmation__order-status',
- );
- const $shippingStatus = orderConfirmationFragment.querySelector(
- '.order-confirmation__shipping-status',
- );
- const $customerDetails = orderConfirmationFragment.querySelector(
- '.order-confirmation__customer-details',
- );
- const $orderCostSummary = orderConfirmationFragment.querySelector(
- '.order-confirmation__order-cost-summary',
- );
- const $orderProductList = orderConfirmationFragment.querySelector(
- '.order-confirmation__order-product-list',
- );
- const $orderConfirmationFooter = orderConfirmationFragment.querySelector(
- '.order-confirmation__footer',
- );
-
- await initializers.mountImmediately(orderApi.initialize, { orderData });
-
- block.replaceChildren(orderConfirmationFragment);
-
- const onSignUpClick = async ({ inputsDefaultValueSet, addressesData }) => {
- const signUpForm = document.createElement('div');
- AuthProvider.render(SignUp, {
- routeSignIn: () => '/customer/login',
- routeRedirectOnEmailConfirmationClose: () => '/customer/account',
- inputsDefaultValueSet,
- addressesData,
- })(signUpForm);
-
- showModal(signUpForm);
- };
-
- CheckoutProvider.render(OrderConfirmationHeader, {
- orderData,
- onSignUpClick,
- })($orderConfirmationHeader);
-
- OrderProvider.render(OrderStatus, { slots: { OrderActions: () => null } })(
- $orderStatus,
- );
- OrderProvider.render(ShippingStatus)($shippingStatus);
- OrderProvider.render(CustomerDetails)($customerDetails);
- OrderProvider.render(OrderCostSummary)($orderCostSummary);
- OrderProvider.render(OrderProductList)($orderProductList);
-
- $orderConfirmationFooter.innerHTML = `
-
-
- `;
-
- const $orderConfirmationFooterContinueBtn = $orderConfirmationFooter.querySelector(
- '.order-confirmation-footer__continue-button',
- );
-
- UI.render(Button, {
- children: 'Continue shopping',
- 'data-testid': 'order-confirmation-footer__continue-button',
- className: 'order-confirmation-footer__continue-button',
- size: 'medium',
- variant: 'primary',
- type: 'submit',
- href: '/',
- })($orderConfirmationFooterContinueBtn);
- };
-
- // Define checkout event handlers and shared utilities
- const isEmptyCart = (data) => data === null || data.isEmpty;
-
- async function handleCheckoutInitialized(data) {
- if (isEmptyCart(data)) {
- await displayEmptyCart();
- return;
- }
-
- // continue to shipping
- const cartShippingAddress = getCartAddress(data, 'shipping');
- await continueToShipping(cartShippingAddress);
-
- // continue to delivery
- if (!cartShippingAddress) return;
- await continueToDelivery();
-
- // continue to payment
- const deliveryMethod = getCartDeliveryMethod(data);
- if (!deliveryMethod) return;
- await continueToPayment();
- }
-
- async function handleCheckoutUpdated(data) {
- if (isEmptyCart(data)) {
- await displayEmptyCart();
- return;
- }
-
- removeEmptyCart();
-
- // continue to shipping
- const cartShippingAddress = getCartAddress(data, 'shipping');
- await continueToShipping(cartShippingAddress);
-
- if (!cartShippingAddress) return;
-
- continueToDeliveryBtn.setProps((prev) => ({
- ...prev,
- disabled: false,
- }));
-
- const deliveryMethod = getCartDeliveryMethod(data);
- if (!deliveryMethod) return;
-
- continueToPaymentBtn.setProps((prev) => ({
- ...prev,
- disabled: false,
- }));
- }
-
- async function handleCheckoutOrder(orderData) {
- // clear address form data
- sessionStorage.removeItem(SHIPPING_ADDRESS_DATA_KEY);
- sessionStorage.removeItem(BILLING_ADDRESS_DATA_KEY);
-
- const token = getUserTokenCookie();
- const orderRef = token ? orderData.number : orderData.token;
- const encodedOrderRef = encodeURIComponent(orderRef);
-
- window.history.pushState(
- {},
- '',
- `/order-details?orderRef=${encodedOrderRef}`,
- );
-
- // TODO cleanup checkout containers
- await displayOrderConfirmation(orderData);
- }
-
- events.on('checkout/initialized', handleCheckoutInitialized, { eager: true });
- events.on('checkout/order', handleCheckoutOrder);
- events.on('checkout/updated', handleCheckoutUpdated);
-}
-```
-
-```css title="commerce-checkout-multi-step.css"
-.checkout__wrapper {
- padding-left: 3rem;
- padding-right: 3rem;
-}
-
-.checkout__banners {
- padding-top: 1.5rem;
-}
-
-.checkout__content {
- display: grid;
- align-items: start;
- grid-template-columns: repeat(var(--grid-4-columns), 1fr);
- gap: var(--spacing-big);
- padding-top: 1.5rem;
-}
-
-.checkout__content--empty {
- display: none;
-}
-
-.checkout__empty-cart {
- padding-top: 1.5rem;
-}
-
-.checkout__main {
- display: grid;
- grid-column: 1 / span 7;
- row-gap: var(--spacing-xbig);
-}
-
-.checkout__aside {
- display: grid;
- grid-column: 9 / span 4;
- row-gap: var(--spacing-xbig);
-}
-
-.checkout__shipping {
- display: flex;
- flex-direction: column;
- gap: var(--spacing-big);
- padding: 0;
-}
-
-.checkout__shipping:has(> div:empty:not(.checkout__continue-to-delivery)) {
- gap: 0;
- padding-top: var(--spacing-small);
- padding-bottom: var(--spacing-small);
- border-top: var(--shape-border-width-3) solid var(--color-neutral-400);
- border-bottom: var(--shape-border-width-3) solid var(--color-neutral-400);
-}
-
-.checkout__delivery {
- display: flex;
- flex-direction: column;
- gap: var(--spacing-big);
- padding: 0;
-}
-
-.checkout__delivery:has(> div:empty:not(.checkout__continue-to-payment)) {
- gap: 0;
- padding-top: var(--spacing-small);
- padding-bottom: var(--spacing-small);
- border-top: var(--shape-border-width-3) solid var(--color-neutral-400);
- border-bottom: var(--shape-border-width-3) solid var(--color-neutral-400);
-}
-
-.checkout__payment {
- display: flex;
- flex-direction: column;
- gap: var(--spacing-big);
- padding: 0;
-}
-
-.checkout__payment:has(> div:empty) {
- gap: 0;
- padding-top: var(--spacing-small);
- padding-bottom: var(--spacing-small);
- border-top: var(--shape-border-width-3) solid var(--color-neutral-400);
- border-bottom: var(--shape-border-width-3) solid var(--color-neutral-400);
-}
-
-/* temporary fix to hide the default cart heading */
-[data-testid='default-cart-heading'] {
- display: none;
-}
-
-/* Responsive adjustments */
-@media only screen and (width <= 768px) {
- .checkout__wrapper {
- padding-left: 1.5rem;
- padding-right: 1.5rem;
- }
-
- .checkout__content {
- grid-template-columns: 1fr;
- gap: var(--spacing-big) 0;
- }
-
- .checkout__main,
- .checkout__aside {
- grid-column: auto;
- }
-
- .checkout__aside {
- order: -1;
- }
-}
-```
\ No newline at end of file
+See [`blocks/commerce-checkout-multi-step`](https://github.com/hlxsites/aem-boilerplate-commerce/tree/demos/blocks/commerce-checkout-multi-step) in the `demos` branch of the boilerplate repository for complete JS and CSS code for the multi-step checkout flow.
\ No newline at end of file