import { debounce } from '@dropins/tools/lib.js';
import { events } from '@dropins/tools/event-bus.js';
} from '@dropins/tools/components.js';
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(`
<div class="checkout__content">
<div class="checkout__heading">
<h1 class="checkout__heading-title">Checkout</h1>
<div class="checkout__heading-divider"></div>
<div class="checkout__main">
<div class="checkout__login"></div>
<div class="checkout__delivery-method">
<h2 class="checkout-delivery-method__title">Delivery Method</h2>
<div class="checkout-delivery-method__toogle-buttons">
<div class="checkout-delivery-method__delivery-button"></div>
<div class="checkout-delivery-method__in-store-pickup-button"></div>
<div class="checkout__in-store-pickup"></div>
<div class="checkout__shipping-form"></div>
<div class="checkout__shipping-methods"></div>
<div class="checkout__payment-methods"></div>
<div class="checkout__billing-form"></div>
<div class="checkout__aside">
<div class="checkout__order-summary"></div>
<div class="cart-summary-list"></div>
<div class="checkout__place-order"></div>
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]) => ({
const address = !isNewAddress
? { customerAddressId: data.id }
saveInAddressBook: data.saveAddressBook,
countryCode: data.countryCode,
firstName: data.firstName,
region: data?.region?.regionCode,
regionId: data?.region?.regionId,
telephone: data.telephone,
// Step 5: Fetch pickup locations
async function fetchPickupLocations() {
{ method: 'GET', cache: 'no-cache' },
.then((res) => res.data.pickupLocations.items);
export default async function decorate(block) {
import('../../scripts/initializers/account.js');
import('../../scripts/initializers/checkout.js');
events.on('checkout/initialized', async (checkoutData) => {
// Step 3: Add toggle buttons
checkoutProvider.render(LoginForm, { name: LOGIN_FORM_NAME })($login),
// Step 3: Add toggle buttons
UI.render(ToggleButton, {
onChange: () => onToggle('delivery'),
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,
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;
country_code: data.countryCode,
region_name: String(data.region.regionCode || ''),
region_id: String(data.region.regionId || ''),
checkoutApi.estimateShippingMethods({ criteria });
accountProvider.render(AddressForm, {
addressesFormTitle: 'Billing address',
className: 'checkout-billing-form__address-form',
formName: BILLING_FORM_NAME,
hideActionFormButtons: true,
showBillingCheckBox: false,
showShippingCheckBox: false,
onChange: debounce((values) => {
setAddressOnCart(values, checkoutApi.setBillingAddress);
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', '');
inStorePickupButton.setProps((prev) => ({ ...prev, selected: true }));
deliveryButton.setProps((prev) => ({ ...prev, selected: false }));
$shippingForm.setAttribute('hidden', '');
$shippingMethods.setAttribute('hidden', '');
$inStorePickup.removeAttribute('hidden');
// Step 6: Render pickup options
const pickupLocations = await fetchPickupLocations();
pickupLocations.forEach((location) => {
const { name, pickup_location_code } = location;
const locationRadiobutton = document.createElement('div');
checkoutApi.setShippingAddress({
pickupLocationCode: pickup_location_code,
$inStorePickup.appendChild(locationRadiobutton);
$root.style.display = 'grid';
block.appendChild(fragment);