@liquidcommerce/elements-sdk 2.7.35 → 2.7.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/index.checkout.esm.js +8130 -7693
  2. package/dist/index.esm.js +13069 -12612
  3. package/dist/ssr-stub.esm.js +1 -0
  4. package/dist/types/clients/helpers.d.ts +2 -0
  5. package/dist/types/core/client/actions/client-checkout-action.service.d.ts +8 -2
  6. package/dist/types/core/pubsub/interfaces/cart.interface.d.ts +5 -0
  7. package/dist/types/core/pubsub/interfaces/core.interface.d.ts +2 -1
  8. package/dist/types/core/store/interfaces/checkout.interface.d.ts +1 -0
  9. package/dist/types/enums/core.enum.d.ts +1 -0
  10. package/dist/types/interfaces/configs/cart.interface.d.ts +1 -0
  11. package/dist/types/modules/cart/cart.commands.d.ts +5 -1
  12. package/dist/types/modules/cart/components/cart-fulfillment.component.d.ts +1 -1
  13. package/dist/types/modules/checkout/checkout.commands.d.ts +26 -1
  14. package/dist/types/modules/checkout/components/checkout-order-summary.component.d.ts +1 -0
  15. package/dist/types/modules/checkout/components/checkout-payment.component.d.ts +4 -0
  16. package/dist/types/modules/checkout/components/checkout-stripe-form.component.d.ts +3 -0
  17. package/dist/types/modules/checkout/customer-prefill.d.ts +2 -0
  18. package/dist/types/modules/product/components/product-retailers-popup.component.d.ts +1 -0
  19. package/dist/types/modules/product/product.commands.d.ts +1 -1
  20. package/dist/types/modules/product-list/product-list.commands.d.ts +1 -1
  21. package/docs/v1/api/actions/checkout-actions.md +58 -1
  22. package/docs/v1/api/configuration.md +2 -0
  23. package/docs/v1/api/typescript-types.md +57 -0
  24. package/docs/v1/examples/custom-theming.md +13 -0
  25. package/docs/v1/guides/accessibility.md +42 -8
  26. package/docs/v1/guides/cart-component.md +43 -0
  27. package/docs/v1/guides/checkout-component.md +57 -0
  28. package/docs/v1/guides/events.md +30 -0
  29. package/docs/v1/guides/theming.md +13 -0
  30. package/package.json +9 -9
@@ -97,6 +97,7 @@ const ELEMENTS_ACTIONS_EVENT = {
97
97
  PRODUCT_FULFILLMENT_TYPE_CHANGED: 'product_fulfillment_type_changed',
98
98
  PRODUCT_FULFILLMENT_CHANGED: 'product_fulfillment_changed',
99
99
  CART_LOADED: 'cart_loaded',
100
+ CART_SHOP_RETAILER: 'cart_shop_retailer',
100
101
  CART_CLOSED: 'cart_closed',
101
102
  CART_OPENED: 'cart_opened',
102
103
  CART_UPDATED: 'cart_updated',
@@ -1,5 +1,7 @@
1
1
  import { type ComponentType } from '@/enums';
2
+ import type { ICartComponent } from '@/interfaces/configs';
2
3
  export declare function normalizeProductListLists<T extends Record<string, any>>(data: T): T;
4
+ export declare function withCartLayoutDefaults(cart: ICartComponent): ICartComponent;
3
5
  export declare function deepMergeConfigs<T extends Record<string, any>>(target: T, source: Partial<T>, currentPath?: string): T;
4
6
  export declare const layoutFieldToComponentTypes: Map<string, ComponentType[]>;
5
7
  export declare function getComponentTypesForLayoutFields(parentPath: string, layoutFields: Record<string, any>): ComponentType[];
@@ -1,5 +1,5 @@
1
1
  import type { ICheckoutDetailsEventData } from '@/core/pubsub/interfaces/checkout.interface';
2
- import type { IAnonymousCheckoutAddProductParams, IAnonymousCheckoutAddProductResponse } from '@/interfaces/api/checkout.interface';
2
+ import type { IAnonymousCheckoutAddProductParams, IAnonymousCheckoutAddProductResponse, ICheckoutMarketingPreferences } from '@/interfaces/api/checkout.interface';
3
3
  import type { IProductAvailabilityResponse } from '@/interfaces/api/product.interface';
4
4
  import type { BillingFieldName, CustomerFieldName, GiftFieldName } from '@/modules/checkout/constant';
5
5
  import { BaseActionService } from './base-action.service';
@@ -20,6 +20,8 @@ export interface ICheckoutActions {
20
20
  exitCheckout: () => void;
21
21
  setSavedPaymentMethod: (params: ISavedPaymentMethodInput) => void;
22
22
  clearSavedPaymentMethod: () => void;
23
+ setSavedMarketingPreferences: (preferences: Partial<ICheckoutMarketingPreferences>) => Promise<void>;
24
+ clearSavedMarketingPreferences: () => void;
23
25
  addProduct: (params: IAddProductParams[], openCheckout?: boolean) => Promise<void>;
24
26
  applyPromoCode: (promoCode: string) => Promise<void>;
25
27
  removePromoCode: () => Promise<void>;
@@ -28,7 +30,9 @@ export interface ICheckoutActions {
28
30
  toggleIsGift: (active?: boolean) => Promise<void>;
29
31
  toggleBillingSameAsShipping: (active?: boolean) => Promise<void>;
30
32
  toggleMarketingPreferences: (field: 'canEmail' | 'canSms', active: boolean) => Promise<void>;
31
- updateCustomerInfo: (params: Record<CustomerFieldName, string>) => void;
33
+ updateCustomerInfo: (params: Record<CustomerFieldName, string>, options?: {
34
+ saveIfValid?: boolean;
35
+ }) => Promise<void>;
32
36
  updateBillingInfo: (params: Record<BillingFieldName, string>) => void;
33
37
  updateGiftInfo: (params: Record<GiftFieldName, string>) => void;
34
38
  getDetails: () => ICheckoutDetailsEventData;
@@ -59,6 +63,8 @@ export declare class ClientCheckoutActionService extends BaseActionService {
59
63
  private removeGiftCard;
60
64
  private toggleIsGift;
61
65
  private toggleBillingSameAsShipping;
66
+ private setSavedMarketingPreferences;
67
+ private clearSavedMarketingPreferences;
62
68
  private toggleMarketingPreferences;
63
69
  private updateCustomerInfo;
64
70
  private updateBillingInfo;
@@ -67,3 +67,8 @@ export interface ICartProductAddFailedEventData {
67
67
  identifiers: string[];
68
68
  error: string;
69
69
  }
70
+ export interface ICartShopRetailerEventData {
71
+ retailerId: string;
72
+ fulfillmentId: string;
73
+ fulfillmentType: 'onDemand' | 'shipping';
74
+ }
@@ -1,5 +1,5 @@
1
1
  import type { IAddressActionEventData, IAddressFailedEventData } from '@/core/pubsub/interfaces/address.interface';
2
- import type { ICartFailedEventData, ICartItemAddedEventData, ICartItemEngravingUpdatedEventData, ICartItemQuantityChangedEventData, ICartItemRemovedEventData, ICartLoadedEventData, ICartProductAddEventData, ICartProductAddFailedEventData, ICartPromoCodeEventData, ICartPromoCodeFailedEventData, ICartUpdatedEventData } from '@/core/pubsub/interfaces/cart.interface';
2
+ import type { ICartFailedEventData, ICartItemAddedEventData, ICartItemEngravingUpdatedEventData, ICartItemQuantityChangedEventData, ICartItemRemovedEventData, ICartLoadedEventData, ICartProductAddEventData, ICartProductAddFailedEventData, ICartPromoCodeEventData, ICartPromoCodeFailedEventData, ICartShopRetailerEventData, ICartUpdatedEventData } from '@/core/pubsub/interfaces/cart.interface';
3
3
  import type { ICheckoutBillingInformationUpdatedEventData, ICheckoutCustomerInformationUpdatedEventData, ICheckoutFailedEventData, ICheckoutGiftCardEventData, ICheckoutGiftCardFailedEventData, ICheckoutGiftInformationUpdatedEventData, ICheckoutItemEngravingUpdatedEventData, ICheckoutItemQuantityChangedEventData, ICheckoutItemRemovedEventData, ICheckoutLoadedEventData, ICheckoutMarketingPreferencesToggleEventData, ICheckoutProductAddEventData, ICheckoutProductAddFailedEventData, ICheckoutPromoCodeEventData, ICheckoutPromoCodeFailedEventData, ICheckoutSubmitCompletedEventData, ICheckoutSubmitFailedEventData, ICheckoutSubmitStartedEventData, ICheckoutTipUpdatedEventData, ICheckoutToggleEventData } from '@/core/pubsub/interfaces/checkout.interface';
4
4
  import type { IProductAddToCartEventData, IProductFulfillmentChangedEventData, IProductFulfillmentTypeChangedEventData, IProductLoadedEventData, IProductQuantityChangedEventData, IProductSizeChangedEventData } from '@/core/pubsub/interfaces/product.interface';
5
5
  import { ELEMENTS_ACTIONS_EVENT } from '@/enums';
@@ -37,6 +37,7 @@ export interface IElementsActionsEventsMap {
37
37
  [ELEMENTS_ACTIONS_EVENT.PRODUCT_SIZE_CHANGED]: IProductSizeChangedEventData;
38
38
  [ELEMENTS_ACTIONS_EVENT.PRODUCT_FULFILLMENT_TYPE_CHANGED]: IProductFulfillmentTypeChangedEventData;
39
39
  [ELEMENTS_ACTIONS_EVENT.PRODUCT_FULFILLMENT_CHANGED]: IProductFulfillmentChangedEventData;
40
+ [ELEMENTS_ACTIONS_EVENT.CART_SHOP_RETAILER]: ICartShopRetailerEventData;
40
41
  [ELEMENTS_ACTIONS_EVENT.CART_CLOSED]: boolean;
41
42
  [ELEMENTS_ACTIONS_EVENT.CART_OPENED]: boolean;
42
43
  [ELEMENTS_ACTIONS_EVENT.CART_UPDATED]: ICartUpdatedEventData;
@@ -36,6 +36,7 @@ export interface ICheckoutStore {
36
36
  presale: ICheckoutPresale | null;
37
37
  isGift: boolean;
38
38
  marketingPreferences: ICheckoutMarketingPreferences;
39
+ appliedSavedMarketingPreferences: Partial<ICheckoutMarketingPreferences>;
39
40
  customerForm: ICheckoutCustomerFormStore;
40
41
  giftRecipientForm: ICheckoutGiftRecipientFormStore;
41
42
  paymentForm: ICheckoutPaymentFormStore;
@@ -16,6 +16,7 @@ export declare const ELEMENTS_ACTIONS_EVENT: {
16
16
  readonly PRODUCT_FULFILLMENT_TYPE_CHANGED: "product_fulfillment_type_changed";
17
17
  readonly PRODUCT_FULFILLMENT_CHANGED: "product_fulfillment_changed";
18
18
  readonly CART_LOADED: "cart_loaded";
19
+ readonly CART_SHOP_RETAILER: "cart_shop_retailer";
19
20
  readonly CART_CLOSED: "cart_closed";
20
21
  readonly CART_OPENED: "cart_opened";
21
22
  readonly CART_UPDATED: "cart_updated";
@@ -3,6 +3,7 @@ export interface ICartTheme {
3
3
  backgroundColor: string;
4
4
  }
5
5
  export interface ICartLayout {
6
+ showShopRetailerButton?: boolean;
6
7
  showQuantityCounter: boolean;
7
8
  quantityCounterStyle: 'outlined' | 'ghost';
8
9
  drawerHeaderText: string;
@@ -1,5 +1,5 @@
1
1
  import { BaseCommand } from '@/core/command/base-command.service';
2
- import type { IBaseCartEventData } from '@/core/pubsub/interfaces/cart.interface';
2
+ import type { IBaseCartEventData, ICartShopRetailerEventData } from '@/core/pubsub/interfaces/cart.interface';
3
3
  export interface AddItemParams {
4
4
  fulfillmentId: string;
5
5
  partNumber: string;
@@ -10,6 +10,10 @@ export interface AddItemParams {
10
10
  export declare class CartCommands extends BaseCommand {
11
11
  constructor();
12
12
  static getInstance(): CartCommands;
13
+ getShopRetailerTarget(fulfillmentId: string): (ICartShopRetailerEventData & {
14
+ retailerName: string;
15
+ }) | null;
16
+ shopRetailer(fulfillmentId: string): void;
13
17
  updateItemQuantity(itemId: string, delta: number): Promise<void>;
14
18
  removeItem(itemId: string, isNotStandAlone?: boolean): Promise<void>;
15
19
  updateItemEngraving(productItemId: string, engravingLines: string[]): Promise<void>;
@@ -9,6 +9,7 @@ export declare class CartFulfillmentComponent extends BaseComponent<ICartFulfill
9
9
  private handleItemsUpdate;
10
10
  private setupItemElements;
11
11
  private setupMinimumPurchaseAlert;
12
+ private removeMinimumControl;
12
13
  private createLoadingOverlay;
13
14
  private hasLoadingOverlay;
14
15
  private showLoadingOverlay;
@@ -16,7 +17,6 @@ export declare class CartFulfillmentComponent extends BaseComponent<ICartFulfill
16
17
  private isAnyItemUpdating;
17
18
  private checkAndUpdateLoadingState;
18
19
  onStoreChanged(changes: IOnStoreChanged[]): boolean | undefined;
19
- private hasFulfillmentDataChanged;
20
20
  protected afterRender(): void;
21
21
  protected disconnected(): void;
22
22
  protected template(): HTMLElement[];
@@ -1,13 +1,23 @@
1
1
  import { BaseCommand } from '@/core/command/base-command.service';
2
2
  import type { ICheckoutDetailsEventData } from '@/core/pubsub/interfaces/checkout.interface';
3
3
  import type { ICheckoutPaymentMethodStore } from '@/core/store/interfaces/checkout.interface';
4
- import type { ICheckoutBilling, ICheckoutCustomer, ICheckoutGiftRecipient, ICheckoutPrepare } from '@/interfaces/api/checkout.interface';
4
+ import type { ICheckoutBilling, ICheckoutCustomer, ICheckoutGiftRecipient, ICheckoutMarketingPreferences, ICheckoutPrepare } from '@/interfaces/api/checkout.interface';
5
5
  import type { AddItemParams } from '@/modules/cart/cart.commands';
6
6
  import type { PresaleExpiredReason } from '@/modules/checkout/components';
7
7
  export declare class CheckoutCommands extends BaseCommand {
8
8
  private readonly uiCommands;
9
9
  private lastLoadParams;
10
10
  private paymentGeneration;
11
+ private savedMarketingPreferences;
12
+ private savedMarketingRevision;
13
+ private marketingNeedsPrepare;
14
+ private completingCheckout;
15
+ private savedMarketingError;
16
+ private marketingEdits;
17
+ private marketingQueue;
18
+ private readonly marketingFields;
19
+ private customerEditRevision;
20
+ private customerEditedGeneration;
11
21
  private confirmedPayment;
12
22
  assertPaymentReady(): void;
13
23
  retryLoadCheckout(): Promise<void>;
@@ -23,6 +33,17 @@ export declare class CheckoutCommands extends BaseCommand {
23
33
  loadCheckoutFromResponse(response: ICheckoutPrepare): void;
24
34
  toggleIsGift(active?: boolean): Promise<void>;
25
35
  toggleBillingSameAsShipping(active?: boolean): Promise<void>;
36
+ private loadedMarketingState;
37
+ private defaultMarketingPreferences;
38
+ private marketingForPrepare;
39
+ private confirmedSavedMarketing;
40
+ private queueMarketing;
41
+ setSavedMarketingPreferences(preferences: Partial<ICheckoutMarketingPreferences>): Promise<void>;
42
+ clearSavedMarketingPreferences(): void;
43
+ private recordSavedMarketing;
44
+ private assertMarketingRestored;
45
+ private canApplySavedMarketing;
46
+ private applySavedMarketingPreferences;
26
47
  toggleMarketingPreferences(fieldName: 'canEmail' | 'canSms', active?: boolean): Promise<boolean>;
27
48
  getPaymentSession(): Promise<void>;
28
49
  confirmPaymentSession(confirmationTokenId: string, setupIntentId: string): Promise<ICheckoutPaymentMethodStore>;
@@ -44,6 +65,9 @@ export declare class CheckoutCommands extends BaseCommand {
44
65
  updateFulfillmentTip(fulfillmentId: string, tipAmount: number): Promise<void>;
45
66
  removeFulfillmentTip(fulfillmentId: string): Promise<void>;
46
67
  clearCheckoutError(): Promise<void>;
68
+ markCustomerFormEdited(): void;
69
+ canPrefillCustomerForm(): boolean;
70
+ savePrefilledCustomerInfo(): Promise<void>;
47
71
  editCustomerForm(): Promise<void>;
48
72
  saveCustomerForm({ shippingAddressTwo, deliveryInstructions, customerData, }: {
49
73
  customerData: ICheckoutCustomer;
@@ -65,6 +89,7 @@ export declare class CheckoutCommands extends BaseCommand {
65
89
  setupIntentId: string;
66
90
  };
67
91
  }): Promise<void>;
92
+ private prepareMarketingForCompletion;
68
93
  checkoutComplete(): Promise<void>;
69
94
  private updateStoreFromCheckoutResponse;
70
95
  private syncFromItemUpdate;
@@ -10,6 +10,7 @@ export declare class CheckoutOrderSummaryComponent extends BaseComponent<null, I
10
10
  private toggleMarketingPreference;
11
11
  private handleEmailToggle;
12
12
  private handleSmsToggle;
13
+ private updateSavedMarketingPrompts;
13
14
  private marketingOptInsContainer;
14
15
  private hasOnDemandOrder;
15
16
  protected template(): HTMLElement[];
@@ -7,6 +7,7 @@ export declare class CheckoutPaymentComponent extends BaseComponent<ICheckoutSen
7
7
  private stripeHandlerElement;
8
8
  private saveButton;
9
9
  private errorMessageElement;
10
+ private walletInstructionsElement?;
10
11
  private billingFormData;
11
12
  private isValidBillingForm;
12
13
  private isBillingSameAsShipping;
@@ -21,6 +22,9 @@ export declare class CheckoutPaymentComponent extends BaseComponent<ICheckoutSen
21
22
  private handleBillingSameAsShippingChange;
22
23
  afterRender(): Promise<void>;
23
24
  private updateSaveButtonState;
25
+ private getSelectedWallet;
26
+ private getPaymentButtonText;
27
+ private updateWalletInstructions;
24
28
  private lastAnnouncedPaymentError;
25
29
  private updateErrorMessage;
26
30
  private showError;
@@ -18,6 +18,8 @@ export interface IStripeConfig {
18
18
  elementOptions?: Record<string, any>;
19
19
  }
20
20
  export interface IStripeFormStatus {
21
+ selectedPaymentType?: string;
22
+ hasPendingConfirmation?: boolean;
21
23
  isFormComplete: boolean;
22
24
  isProcessing: boolean;
23
25
  hasCompletedPayment: boolean;
@@ -31,6 +33,7 @@ export declare class CheckoutStripeFormComponent extends SafeHTMLElement {
31
33
  private _stripeElements;
32
34
  private _paymentElement;
33
35
  private _isProcessing;
36
+ private _selectedPaymentType;
34
37
  private _isFormComplete;
35
38
  private _hasUsedCurrentSession;
36
39
  private _pendingConfirmation;
@@ -0,0 +1,2 @@
1
+ import type { ICheckoutCustomer } from '@/interfaces/api/checkout.interface';
2
+ export declare function isValidCustomerPrefill(customer: ICheckoutCustomer, today?: Date): boolean;
@@ -7,5 +7,6 @@ export declare class ProductRetailersPopupComponent extends BaseComponent<IProdu
7
7
  get hostClasses(): string[];
8
8
  constructor();
9
9
  private getProductId;
10
+ private get fulfillmentLabelId();
10
11
  protected template(): HTMLElement[];
11
12
  }
@@ -22,7 +22,7 @@ export declare class ProductCommands extends BaseCommand {
22
22
  openProductDrawer(productId: string, contentType: IProductDrawerContentConfig['type'], data?: Record<string, any>, trigger?: HTMLElement | null): Promise<void>;
23
23
  closeProductDrawer(productId: string): Promise<void>;
24
24
  rerenderAllProducts(): Promise<void>;
25
- addToCart(productId: string, params: AddItemParams, trigger?: HTMLElement | null): Promise<void>;
25
+ addToCart(productId: string, params: AddItemParams, trigger?: HTMLElement | null, triggerFallback?: () => HTMLElement | null): Promise<void>;
26
26
  addPresaleToCart(productId: string, params: AddItemParams): Promise<void>;
27
27
  productDataForEventFromStore(identifier: string): IBaseProductEventData;
28
28
  }
@@ -22,7 +22,7 @@ export declare class ProductListCommands extends BaseCommand {
22
22
  getItemsPerPage(slug: string): number;
23
23
  updatePagination(navigation: INavigationResponse | undefined): IPagination;
24
24
  createInitialPagination(): IPagination;
25
- addToCart(slug: string, productId: string, params: AddItemParams, trigger?: HTMLElement | null): Promise<void>;
25
+ addToCart(slug: string, productId: string, params: AddItemParams, trigger?: HTMLElement | null, triggerFallback?: () => HTMLElement | null): Promise<void>;
26
26
  selectSize(slug: string, productId: string, sizeId: string): Promise<void>;
27
27
  changeFulfillmentType: (slug: string, productId: string, newFulfillmentType: FulfillmentType) => Promise<void>;
28
28
  selectFulfillment: (slug: string, productId: string, selectedFulfillmentId: string) => Promise<void>;
@@ -264,7 +264,7 @@ await window.LiquidCommerce.elements.actions.checkout.toggleMarketingPreferences
264
264
  ### actions.checkout.updateCustomerInfo()
265
265
 
266
266
  ```typescript
267
- updateCustomerInfo(params: Record<CustomerFieldName, string>): void
267
+ updateCustomerInfo(params: Record<CustomerFieldName, string>, options?: { saveIfValid?: boolean }): Promise<void>
268
268
  ```
269
269
 
270
270
  Pre-fill customer information.
@@ -525,3 +525,60 @@ Key events:
525
525
  - [Checkout Component Guide](../../guides/checkout-component.md)
526
526
  - [Checkout Events](../../guides/events.md#checkout-events)
527
527
  - [Cart Actions](./cart-actions.md)
528
+
529
+ ### Preparing complete account delivery details
530
+
531
+ After `lce:actions.checkout_loaded`, call
532
+ `await elements.actions.checkout.updateCustomerInfo(fields, { saveIfValid: true })`
533
+ to validate and prepare complete account details, then show their editable summary.
534
+ Supply names, email, a US phone number, and a real `birthDate` in `MM/DD/YYYY` format
535
+ (age 21+, year 1930 or later). A shipping street must already be selected.
536
+ Omitted apartment and delivery instructions retain their existing checkout values.
537
+ Partial or invalid details stay in the form; backend validation failures reject the
538
+ promise, show the checkout error, and keep details editable. The host should handle that rejection.
539
+
540
+ The option skips gifts, saved forms, in-flight saves, and customer forms the shopper
541
+ has edited during the current checkout. It does not overwrite those forms. Edits or
542
+ address/checkout changes during preparation prevent a stale response from closing
543
+ the form. Without the option, this action only prefills fields, as before.
544
+
545
+ Apple Pay and Google Pay selection displays **Continue with Apple Pay** or
546
+ **Continue with Google Pay**. This opens the wallet to save a payment method; the
547
+ shopper still reviews and places the order afterward. It is not express checkout.
548
+
549
+ After wallet cancellation, the same Continue action remains available. If authorization
550
+ succeeded but saving the method failed, **Retry saving Apple Pay/Google Pay** reuses
551
+ the existing authorization rather than opening the wallet again.
552
+
553
+ ### Saved account marketing preferences
554
+
555
+ ```ts
556
+ setSavedMarketingPreferences(preferences: { canEmail?: boolean; canSms?: boolean }): Promise<void>
557
+ clearSavedMarketingPreferences(): void
558
+ ```
559
+
560
+ Call `actions.checkout.setSavedMarketingPreferences()` with known recorded account
561
+ choices, including `false`, before checkout loads. Only boolean values are used;
562
+ missing/null/unrecognized values remain unknown. The first checkout prepare uses
563
+ saved values instead of tenant defaults. Each channel's prompt is hidden only
564
+ after the server confirms that saved choice. Unknown channels retain their normal
565
+ checkout prompts. The actions are available in both full and checkout-only clients.
566
+
567
+ Calling the setter during an active checkout prepares untouched channels and
568
+ preserves shopper checkbox edits. Backend failure rejects the promise, displays
569
+ an error, and leaves prompts available; the host should catch the rejection.
570
+ The same call can retry. Applying values does not call a subscription service.
571
+ It supplies the account's recorded choices to this checkout.
572
+
573
+ Saved choices persist across cart resets in the same SDK session. Call
574
+ `clearSavedMarketingPreferences()` on sign-out, account change, and host cleanup.
575
+ Clearing reveals the prompts and restores configured guest defaults. It invalidates
576
+ stale responses while draining outstanding preference requests; checkout completion
577
+ prepares the current choices before submitting the checkout token. A load already
578
+ in progress still finishes and reconciles the current account's values. Bind a new
579
+ account only to its own checkout; the host remains responsible for account/cart
580
+ ownership and for reloading checkout when identity changes.
581
+
582
+ Order completion waits for queued preference work. It cannot race a late saved
583
+ opt-out or proceed with an unconfirmed saved choice. If applying a known choice
584
+ fails, the shopper can retry or explicitly choose the visible channel preference.
@@ -271,6 +271,7 @@ interface ICartTheme {
271
271
 
272
272
  ```typescript
273
273
  interface ICartLayout {
274
+ showShopRetailerButton?: boolean;
274
275
  showQuantityCounter: boolean;
275
276
  quantityCounterStyle: 'outlined' | 'ghost';
276
277
  drawerHeaderText: string;
@@ -280,6 +281,7 @@ interface ICartLayout {
280
281
 
281
282
  | Property | Type | Description |
282
283
  |----------|------|-------------|
284
+ | `showShopRetailerButton` | `boolean` | Default `false`, including older server themes. Shows the retailer shopping button for eligible unmet minimums; requires a `cart_shop_retailer` event handler. |
283
285
  | `showQuantityCounter` | `boolean` | Show quantity counter in cart items |
284
286
  | `quantityCounterStyle` | `'outlined' \| 'ghost'` | Visual style for quantity counter |
285
287
  | `drawerHeaderText` | `string` | Header text for the cart drawer |
@@ -422,3 +422,60 @@ type DeepPartial<T> = {
422
422
  - [Client API](./client.md) - Client initialization and usage
423
423
  - [Configuration Reference](./configuration.md) - Detailed config options
424
424
  - [Injection Methods](./injection-methods.md) - Component injection API
425
+
426
+ ### Preparing complete account delivery details
427
+
428
+ After `lce:actions.checkout_loaded`, call
429
+ `await elements.actions.checkout.updateCustomerInfo(fields, { saveIfValid: true })`
430
+ to validate and prepare complete account details, then show their editable summary.
431
+ Supply names, email, a US phone number, and a real `birthDate` in `MM/DD/YYYY` format
432
+ (age 21+, year 1930 or later). A shipping street must already be selected.
433
+ Omitted apartment and delivery instructions retain their existing checkout values.
434
+ Partial or invalid details stay in the form; backend validation failures reject the
435
+ promise, show the checkout error, and keep details editable. The host should handle that rejection.
436
+
437
+ The option skips gifts, saved forms, in-flight saves, and customer forms the shopper
438
+ has edited during the current checkout. It does not overwrite those forms. Edits or
439
+ address/checkout changes during preparation prevent a stale response from closing
440
+ the form. Without the option, this action only prefills fields, as before.
441
+
442
+ Apple Pay and Google Pay selection displays **Continue with Apple Pay** or
443
+ **Continue with Google Pay**. This opens the wallet to save a payment method; the
444
+ shopper still reviews and places the order afterward. It is not express checkout.
445
+
446
+ After wallet cancellation, the same Continue action remains available. If authorization
447
+ succeeded but saving the method failed, **Retry saving Apple Pay/Google Pay** reuses
448
+ the existing authorization rather than opening the wallet again.
449
+
450
+ ### Saved account marketing preferences
451
+
452
+ ```ts
453
+ setSavedMarketingPreferences(preferences: { canEmail?: boolean; canSms?: boolean }): Promise<void>
454
+ clearSavedMarketingPreferences(): void
455
+ ```
456
+
457
+ Call `actions.checkout.setSavedMarketingPreferences()` with known recorded account
458
+ choices, including `false`, before checkout loads. Only boolean values are used;
459
+ missing/null/unrecognized values remain unknown. The first checkout prepare uses
460
+ saved values instead of tenant defaults. Each channel's prompt is hidden only
461
+ after the server confirms that saved choice. Unknown channels retain their normal
462
+ checkout prompts. The actions are available in both full and checkout-only clients.
463
+
464
+ Calling the setter during an active checkout prepares untouched channels and
465
+ preserves shopper checkbox edits. Backend failure rejects the promise, displays
466
+ an error, and leaves prompts available; the host should catch the rejection.
467
+ The same call can retry. Applying values does not call a subscription service.
468
+ It supplies the account's recorded choices to this checkout.
469
+
470
+ Saved choices persist across cart resets in the same SDK session. Call
471
+ `clearSavedMarketingPreferences()` on sign-out, account change, and host cleanup.
472
+ Clearing reveals the prompts and restores configured guest defaults. It invalidates
473
+ stale responses while draining outstanding preference requests; checkout completion
474
+ prepares the current choices before submitting the checkout token. A load already
475
+ in progress still finishes and reconciles the current account's values. Bind a new
476
+ account only to its own checkout; the host remains responsible for account/cart
477
+ ownership and for reloading checkout when identity changes.
478
+
479
+ Order completion waits for queued preference work. It cannot race a late saved
480
+ opt-out or proceed with an unconfirmed saved choice. If applying a known choice
481
+ fails, the shopper can retry or explicitly choose the visible channel preference.
@@ -41,6 +41,7 @@ const client = await Elements('YOUR_API_KEY', {
41
41
  },
42
42
  cart: {
43
43
  layout: {
44
+ showShopRetailerButton: false, // Enable only with a cart_shop_retailer event handler
44
45
  drawerHeaderText: 'Your Cart',
45
46
  goToCheckoutButtonText: 'Checkout Now'
46
47
  }
@@ -57,6 +58,18 @@ const client = await Elements('YOUR_API_KEY', {
57
58
 
58
59
  > If you use custom fonts, make sure the font is available on the page.
59
60
 
61
+ ### Shop the same retailer
62
+
63
+ Set `customTheme.cart.layout.showShopRetailerButton: true` to show a native **Shop {retailer}**
64
+ button beside each unmet order-minimum warning (below it on mobile). The default is `false`,
65
+ including when the server theme predates this option. It only appears for a current, nonempty
66
+ `onDemand` or `shipping` fulfillment with a positive amount remaining and valid retailer ownership.
67
+
68
+ Handle `ELEMENTS_ACTIONS_EVENT.CART_SHOP_RETAILER` (`lce:actions.cart_shop_retailer`) to close the
69
+ cart and open your retailer-filtered catalog. The SDK emits identifiers only and performs no
70
+ navigation. Revalidate them against `client.actions.cart.getDetails()` before navigating; see
71
+ [Events](../guides/events.md) for the payload.
72
+
60
73
  ## Related Docs
61
74
 
62
75
  - [Theming Guide](../guides/theming.md)
@@ -44,7 +44,12 @@ retailer closes the panel — arrow keys move focus and `Enter` commits.
44
44
 
45
45
  ### Focus management
46
46
 
47
- - **Opening an overlay** moves focus into it and remembers the control that opened it.
47
+ - **Opening an overlay** moves focus onto the first control inside it — the panel's close
48
+ button, the address field — and remembers the control that opened it. Focus lands on a
49
+ control rather than on the panel itself so the focus ring shows a sighted keyboard user
50
+ where they now are; the dialog's role and name are announced either way as focus crosses
51
+ into it. Views whose point is the text at the top of them (the order confirmation) focus
52
+ that heading instead.
48
53
  - **Closing** returns focus to that control, even when a re-render has replaced it.
49
54
  - **While an overlay is open** focus is trapped inside it and the rest of the page is `inert` and
50
55
  `aria-hidden`, so a screen reader cannot wander behind the dialog.
@@ -55,6 +60,18 @@ retailer closes the panel — arrow keys move focus and `Enter` commits.
55
60
  - **Nothing hidden is focusable.** Collapsed panels are removed from the tab order and the
56
61
  accessibility tree, and closed overlays have their content torn down.
57
62
 
63
+ ### Retailer shopping from the cart
64
+
65
+ The opt-in `cart.layout.showShopRetailerButton` uses a native **Shop {retailer}** button with
66
+ normal Tab, Enter and Space behavior, a 44px minimum target height and a visible focus ring.
67
+ It appears beside the unmet-minimum warning on desktop and below it on mobile. Warning updates
68
+ retain the button node and its focus; if it disappears because the minimum is met or eligibility
69
+ changes, focus moves to the containing fulfillment before its item controls. The host handles
70
+ focus after closing the drawer and navigating in response to `cart_shop_retailer`.
71
+
72
+ The button text, border and focus ring use `global.theme.primaryColor`; check that color against
73
+ the cart background as part of your theme contrast review.
74
+
58
75
  ### Focus indicator
59
76
 
60
77
  A 2px ring in `--focus-ring-color`, offset 2px from the control. Controls that span their
@@ -129,14 +146,14 @@ on render, and a live region recreated with its text already in place is not ann
129
146
 
130
147
  ### Headings
131
148
 
132
- The product element emits a level-1 title with its sections (`Size`, `About this product:`) at
133
- level 2, and an overlay supplies its own heading at the top of its content, because the page behind
134
- it is out of the accessibility tree while it is open.
149
+ The product element emits a level-1 title, and every section caption below it sits at level 2 —
150
+ `Size`, `Delivers to:`, `Shipping From:` and `About this product:` — so the whole element is
151
+ reachable by heading navigation with no level skipped. An overlay supplies its own heading at the
152
+ top of its content, because the page behind it is out of the accessibility tree while it is open.
135
153
 
136
- Captions that label a single value — the `Delivers to:` and `Shipping From:` lines — are **not**
137
- headings, so they do not turn up in a heading list as headings that head nothing. `Shipping From:`
138
- names the card it captions with `aria-labelledby`; `Delivers to:` is hidden from assistive
139
- technology because the control beside it already announces the address.
154
+ A caption can do both jobs at once: `Shipping From:` is a heading *and* names the retailer card
155
+ beneath it through `aria-labelledby`. Heading ids are scoped to the product, so each product
156
+ element on a page labels its own sections.
140
157
 
141
158
  The checkout element has its own outline and is not covered by the above. Where any element's
142
159
  top-level heading sits in **your** page's outline is yours to place.
@@ -212,3 +229,20 @@ for (const region of document.querySelectorAll('[data-lce-live-region]')) {
212
229
  Checkout announces loading or initialization failure in a status region. If initialization fails, payment fields are withheld and a keyboard-operable **Retry checkout** button retries the same checkout. The button is disabled while loading.
213
230
 
214
231
  Save Payment Information remains disabled until checkout initializes. During payment save it stays busy until the server responds. Recoverable billing/prepare errors are announced while the mounted card fields and billing input remain available for correction and retry.
232
+
233
+ ### Wallet payment actions
234
+
235
+ The payment action's accessible name matches its visible label, including the
236
+ selected Apple Pay or Google Pay wallet and retry/processing states. Adjacent
237
+ instructions explain whether the action opens the wallet or retries saving already
238
+ authorized details. These actions save the payment method; order placement is a
239
+ separate step.
240
+
241
+ ### Recorded marketing choices
242
+
243
+ Checkout suppresses each email/SMS consent checkbox only after the corresponding
244
+ recorded account choice is confirmed. Suppressed controls are hidden visually and
245
+ from the accessibility tree; unknown choices retain their named, keyboard-operable
246
+ checkboxes. Clearing account preferences reveals the controls and synchronizes
247
+ `aria-checked` with the current guest defaults. A shopper's explicit checkbox edit
248
+ keeps that channel visible when account data arrives later.
@@ -277,6 +277,36 @@ Or using data attributes:
277
277
 
278
278
  ## Events
279
279
 
280
+ ### `lce:actions.cart_shop_retailer`
281
+
282
+ A shopper activated **Shop {retailer}** from an unmet cart fulfillment minimum. Opt in with
283
+ `customTheme.cart.layout.showShopRetailerButton: true` (default `false`). The cart command
284
+ revalidates the fulfillment, its current retailer and items, fulfillment type, and remaining
285
+ minimum before emitting. Unsupported, empty, stale or already-satisfied fulfillments emit nothing.
286
+
287
+ ```typescript
288
+ interface ICartShopRetailerEventData {
289
+ retailerId: string;
290
+ fulfillmentId: string;
291
+ fulfillmentType: 'onDemand' | 'shipping';
292
+ }
293
+ ```
294
+
295
+ Listen with the exported constant; the usual event envelope is `{ data, metadata }`:
296
+
297
+ ```javascript
298
+ window.addEventListener(`lce:actions.${ELEMENTS_ACTIONS_EVENT.CART_SHOP_RETAILER}`, (event) => {
299
+ const { retailerId, fulfillmentId, fulfillmentType } = event.detail.data;
300
+ // Revalidate against client.actions.cart.getDetails(), then close the drawer
301
+ // and route to your catalog scoped to this retailer and fulfillment type.
302
+ });
303
+ ```
304
+
305
+ The SDK leaves the drawer open and does not navigate. The host handles routing and derives
306
+ retailer names, totals and minimums from current cart details. The event is also sent to `lce:actions`.
307
+
308
+
309
+
280
310
  ### Cart Loaded
281
311
 
282
312
  Fired when cart data is loaded:
@@ -410,6 +440,7 @@ const client = await Elements('YOUR_API_KEY', {
410
440
  backgroundColor: '#ffffff'
411
441
  },
412
442
  layout: {
443
+ showShopRetailerButton: false, // Enable only with a cart_shop_retailer event handler
413
444
  showQuantityCounter: true,
414
445
  quantityCounterStyle: 'outlined', // or 'ghost'
415
446
  drawerHeaderText: 'My Bag',
@@ -420,6 +451,18 @@ const client = await Elements('YOUR_API_KEY', {
420
451
  });
421
452
  ```
422
453
 
454
+ ### Shop the same retailer
455
+
456
+ Set `customTheme.cart.layout.showShopRetailerButton: true` to show a native **Shop {retailer}**
457
+ button beside each unmet order-minimum warning (below it on mobile). The default is `false`,
458
+ including when the server theme predates this option. It only appears for a current, nonempty
459
+ `onDemand` or `shipping` fulfillment with a positive amount remaining and valid retailer ownership.
460
+
461
+ Handle `ELEMENTS_ACTIONS_EVENT.CART_SHOP_RETAILER` (`lce:actions.cart_shop_retailer`) to close the
462
+ cart and open your retailer-filtered catalog. The SDK emits identifiers only and performs no
463
+ navigation. Revalidate them against `client.actions.cart.getDetails()` before navigating; see
464
+ [Events](./events.md) for the payload.
465
+
423
466
  ## State Persistence
424
467
 
425
468
  ### Local Storage