@liquidcommerce/elements-sdk 2.7.44 → 2.7.45

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 (54) hide show
  1. package/README.md +5 -3
  2. package/dist/index.checkout.esm.js +10918 -8932
  3. package/dist/index.esm.js +13321 -11337
  4. package/dist/ssr-stub.esm.js +3 -0
  5. package/dist/types/core/a11y/focus-manager.service.d.ts +1 -1
  6. package/dist/types/core/api/api-result.d.ts +3 -0
  7. package/dist/types/core/client/actions/client-analytics-action.service.d.ts +35 -0
  8. package/dist/types/core/client/actions/client-checkout-action.service.d.ts +6 -0
  9. package/dist/types/core/client/client-action.service.d.ts +1 -0
  10. package/dist/types/core/client/client-config.service.d.ts +2 -0
  11. package/dist/types/core/google-tag-manager.service.d.ts +3 -2
  12. package/dist/types/core/pubsub/interfaces/checkout.interface.d.ts +6 -0
  13. package/dist/types/core/pubsub/interfaces/core.interface.d.ts +3 -1
  14. package/dist/types/core/store/interfaces/checkout.interface.d.ts +4 -0
  15. package/dist/types/enums/core.enum.d.ts +3 -0
  16. package/dist/types/index.checkout.d.ts +4 -2
  17. package/dist/types/index.d.ts +1 -0
  18. package/dist/types/interfaces/analytics.interface.d.ts +41 -0
  19. package/dist/types/interfaces/api/cart.interface.d.ts +2 -0
  20. package/dist/types/interfaces/api/checkout.interface.d.ts +7 -1
  21. package/dist/types/interfaces/api/delivery.interface.d.ts +24 -0
  22. package/dist/types/interfaces/api/index.d.ts +1 -0
  23. package/dist/types/interfaces/api/product.interface.d.ts +2 -0
  24. package/dist/types/interfaces/client.interface.d.ts +5 -0
  25. package/dist/types/interfaces/config.interface.d.ts +1 -0
  26. package/dist/types/modules/cart/cart.commands.d.ts +3 -1
  27. package/dist/types/modules/cart/components/cart-item.component.d.ts +2 -0
  28. package/dist/types/modules/checkout/checkout.commands.d.ts +41 -0
  29. package/dist/types/modules/checkout/components/checkout-completed.component.d.ts +4 -0
  30. package/dist/types/modules/checkout/components/checkout-delivery-method.component.d.ts +2 -0
  31. package/dist/types/modules/checkout/components/checkout-delivery-scheduling.component.d.ts +53 -0
  32. package/dist/types/modules/checkout/components/checkout-express-checkout.component.d.ts +3 -0
  33. package/dist/types/modules/checkout/components/checkout-item.component.d.ts +2 -0
  34. package/dist/types/modules/checkout/components/checkout-payment.component.d.ts +3 -0
  35. package/dist/types/modules/checkout/components/index.d.ts +1 -0
  36. package/dist/types/modules/product-list/product-list.commands.d.ts +5 -0
  37. package/dist/types/ssr/stub.checkout.d.ts +3 -1
  38. package/dist/types/ssr/stub.d.ts +3 -1
  39. package/dist/types/utils/delivery-scheduling.d.ts +75 -0
  40. package/dist/types/utils/fulfillment-unavailable.d.ts +8 -0
  41. package/docs/v1/README.md +1 -0
  42. package/docs/v1/api/actions/analytics-actions.md +326 -0
  43. package/docs/v1/api/actions/checkout-actions.md +47 -2
  44. package/docs/v1/api/actions/product-actions.md +1 -0
  45. package/docs/v1/api/client.md +9 -2
  46. package/docs/v1/api/configuration.md +4 -0
  47. package/docs/v1/api/typescript-types.md +110 -1
  48. package/docs/v1/guides/accessibility.md +8 -1
  49. package/docs/v1/guides/cart-component.md +2 -0
  50. package/docs/v1/guides/checkout-component.md +64 -2
  51. package/docs/v1/guides/events.md +36 -2
  52. package/docs/v1/guides/product-component.md +6 -0
  53. package/docs/v1/reference/analytics.md +52 -9
  54. package/package.json +1 -1
@@ -155,6 +155,7 @@ interface IFulfillment {
155
155
  fee: number;
156
156
  timezone: string;
157
157
  hourStatus: { isOpen: boolean; openTime: string; isClosed: boolean; closeTime: string };
158
+ deliveryAvailability?: DeliveryAvailability; // same-day only, where the retailer schedules delivery
158
159
  }
159
160
 
160
161
  interface IProduct {
@@ -64,8 +64,10 @@ const client = await ElementsCheckout('YOUR_API_KEY', {
64
64
  });
65
65
  ```
66
66
 
67
- The checkout-only client exposes `injectCheckout()`, `injectExpressCheckout()`, `actions.checkout`
68
- and `destroy()`.
67
+ The checkout-only client exposes `injectCheckout()`, `injectExpressCheckout()`, `actions.checkout`,
68
+ `actions.analytics` and `destroy()`. `actions.analytics` reports the steps your own UI renders —
69
+ product views, address, availability, starting checkout — to the same GTM stream the checkout
70
+ reports to; see [Analytics Actions](./actions/analytics-actions.md).
69
71
 
70
72
  #### injectExpressCheckout()
71
73
 
@@ -73,6 +75,9 @@ Injects only the Apple Pay and Google Pay wallet buttons, anywhere on the page,
73
75
  the checkout UI. Confirming the wallet sheet places the order outright — the wallet's contact and
74
76
  billing details are sent to the checkout, so the shopper never fills a checkout form.
75
77
 
78
+ Express checkout is off by default: pass `development.enableExpressCheckout: true` in the client config to show it. Without it this
79
+ method renders nothing and resolves to `null`.
80
+
76
81
  ```typescript
77
82
  injectExpressCheckout(params: IInjectExpressCheckoutParams): Promise<IInjectedComponent | null>
78
83
  ```
@@ -591,6 +596,8 @@ import type {
591
596
  } from '@liquidcommerce/elements-sdk';
592
597
  ```
593
598
 
599
+ Both package entries, including their Node/SSR stubs, export `DeliverySlot`, `DeliveryAvailability`, `CheckoutDeliveryScheduling` and `CheckoutDeliverySelection`. Both clients expose `actions.checkout.loadDeliverySlots(fulfillmentId)` and `actions.checkout.chooseDeliverySlot(fulfillmentId, slotId)`.
600
+
594
601
  See [TypeScript Types](./typescript-types.md) for complete type reference.
595
602
 
596
603
  ## Best Practices
@@ -563,6 +563,7 @@ interface ILiquidCommerceElementsDevelopmentConfig {
563
563
  customApiUrl?: string;
564
564
  openShadowDom?: boolean;
565
565
  mockMode?: boolean;
566
+ enableExpressCheckout?: boolean;
566
567
  }
567
568
  ```
568
569
 
@@ -571,6 +572,9 @@ interface ILiquidCommerceElementsDevelopmentConfig {
571
572
  | `customApiUrl` | `string` | Custom API URL for local or custom backend testing |
572
573
  | `openShadowDom` | `boolean` | Use an OPEN (inspectable) Shadow DOM instead of the default closed mode, for debugging. Shadow DOM is always attached; forced off in production (default: `false`) |
573
574
  | `mockMode` | `boolean` | Enable mock mode to receive mock data from the API; sends an `X-Liquid-Api-Mock-Mode` header with all requests (default: `false`) |
575
+ | `enableExpressCheckout` | `boolean` | Show the Apple Pay / Google Pay express checkout and the checkout's delivery-method switch (same-day vs shipping) — the express block and delivery-method section in the checkout element, and `injectExpressCheckoutElement()` / `injectExpressCheckout()`, which otherwise render nothing and resolve to `null`. Honoured in every environment, production included (default: `false`) |
576
+
577
+ > **Temporary:** this flag exists only while express checkout and the delivery-method switch are being verified. Once they are confirmed, the flag will be removed and both will always be on.
574
578
 
575
579
  > To pre-select a payment method and skip the card form (in any environment), use [`actions.checkout.setSavedPaymentMethod`](./actions/checkout-actions.md#actionscheckoutsetsavedpaymentmethod) instead.
576
580
 
@@ -21,6 +21,8 @@ import { ElementsCheckout } from '@liquidcommerce/elements-sdk/checkout';
21
21
  import type {
22
22
  IElementsCheckoutClient,
23
23
  IElementsCheckoutActions,
24
+ IAnalyticsActions,
25
+ IAnalyticsItem,
24
26
  ILiquidCommerceElementsCheckoutClientConfig,
25
27
  IInjectedComponent
26
28
  } from '@liquidcommerce/elements-sdk/checkout';
@@ -81,7 +83,7 @@ Checkout-only client interface returned by `ElementsCheckout()`.
81
83
  interface IElementsCheckoutClient {
82
84
  injectCheckout(params: IInjectCheckoutParams): Promise<IInjectedComponent | null>;
83
85
  injectExpressCheckout(params: IInjectExpressCheckoutParams): Promise<IInjectedComponent | null>;
84
- actions: { checkout: IElementsCheckoutActions };
86
+ actions: { checkout: IElementsCheckoutActions; analytics: IAnalyticsActions };
85
87
  destroy(): void;
86
88
  }
87
89
 
@@ -158,6 +160,7 @@ interface ILiquidCommerceElementsDevelopmentConfig {
158
160
  customApiUrl?: string;
159
161
  openShadowDom?: boolean;
160
162
  mockMode?: boolean; // when true, sends an 'X-Liquid-Api-Mock-Mode' header to receive mock data
163
+ enableExpressCheckout?: boolean; // show express checkout and the delivery-method switch; off by default, honoured in production
161
164
  }
162
165
  ```
163
166
 
@@ -309,6 +312,7 @@ interface ILiquidCommerceElementsActions {
309
312
  address: IAddressActions;
310
313
  cart: ICartActions;
311
314
  checkout: ICheckoutActions;
315
+ analytics: IAnalyticsActions;
312
316
  }
313
317
  ```
314
318
 
@@ -320,6 +324,74 @@ Checkout actions available in the checkout-only client. This is `ICheckoutAction
320
324
  interface IElementsCheckoutActions extends Omit<ICheckoutActions, 'openCheckout' | 'closeCheckout' | 'toggleCheckout'> {}
321
325
  ```
322
326
 
327
+ ### IAnalyticsActions
328
+
329
+ Reports steps a host renders with its own UI to the SDK's GTM / GA4 stream. On both the full client
330
+ and the checkout-only client. See [Analytics Actions](./actions/analytics-actions.md).
331
+
332
+ ```typescript
333
+ interface IAnalyticsActions {
334
+ search(searchTerm: string): void;
335
+ viewItem(item: IAnalyticsItem): void;
336
+ viewItemList(items: IAnalyticsItem[], list?: IAnalyticsItemList): void;
337
+ selectItem(item: IAnalyticsItem, list?: IAnalyticsItemList): void;
338
+ addToCart(item: IAnalyticsItem): void;
339
+ removeFromCart(item: IAnalyticsItem): void;
340
+ viewCart(items: IAnalyticsItem[]): void;
341
+ beginCheckout(items: IAnalyticsItem[], options?: { coupon?: string }): void;
342
+ addressUpdated(address: IAnalyticsAddress): void;
343
+ addressFailed(failure: IAnalyticsAddressFailure): void;
344
+ productNoAvailability(details: IAnalyticsAvailability): void;
345
+ productSizeNoAvailability(details: IAnalyticsAvailability): void;
346
+ productFulfillmentNoAvailability(details: IAnalyticsFulfillmentAvailability): void;
347
+ }
348
+
349
+ interface IAnalyticsItem {
350
+ id: string; // the selected size's id
351
+ name: string;
352
+ brand?: string;
353
+ size?: string;
354
+ categoryPath?: string; // product catPath
355
+ price?: number; // cents
356
+ quantity?: number;
357
+ type?: string;
358
+ subType?: string;
359
+ image?: string;
360
+ upc?: string;
361
+ groupingId?: string;
362
+ index?: number;
363
+ }
364
+
365
+ interface IAnalyticsItemList {
366
+ id?: string;
367
+ name?: string;
368
+ }
369
+
370
+ interface IAnalyticsAddress {
371
+ placesId?: string;
372
+ formattedAddress?: string;
373
+ address: Partial<IAddressAddress>;
374
+ coordinates?: IAddressCoordinates;
375
+ }
376
+
377
+ interface IAnalyticsAddressFailure {
378
+ placesId?: string;
379
+ formattedAddress?: string;
380
+ address?: Partial<IAddressAddress>;
381
+ error: string;
382
+ }
383
+
384
+ interface IAnalyticsAvailability {
385
+ item: IAnalyticsItem;
386
+ reason: string;
387
+ address?: Partial<IAddressAddress>;
388
+ }
389
+
390
+ interface IAnalyticsFulfillmentAvailability extends IAnalyticsAvailability {
391
+ fulfillmentType: FulfillmentType;
392
+ }
393
+ ```
394
+
323
395
  See [Actions API](./actions/) for detailed method signatures.
324
396
 
325
397
  ---
@@ -353,6 +425,7 @@ type ComponentType =
353
425
  | 'drawer' | 'input' | 'engraving-form' | 'engraving-view'
354
426
  | 'buttons-cart-open' | 'powered-by' | 'lce-element'
355
427
  | 'purchase-min-alert' | 'alert' | 'promo-code-ticker'
428
+ | 'checkout-delivery-scheduling' // Internal child of checkout
356
429
  // ...and additional internal component types
357
430
  ```
358
431
 
@@ -499,3 +572,39 @@ ownership and for reloading checkout when identity changes.
499
572
  Order completion waits for queued preference work. It cannot race a late saved
500
573
  opt-out or proceed with an unconfirmed saved choice. If applying a known choice
501
574
  fails, the shopper can retry or explicitly choose the visible channel preference.
575
+
576
+ ## Delivery scheduling types
577
+
578
+ The main and checkout-only entries, and their Node/SSR stubs, export these types.
579
+
580
+ ```typescript
581
+ export interface DeliverySlot {
582
+ id: string; // server-issued, `<fulfillmentOptionId>:<window start, Unix seconds>`
583
+ start: string; // ISO-8601 UTC
584
+ end: string; // ISO-8601 UTC
585
+ cutoff: string; // ISO-8601 UTC, latest time to order for this window ("Order by")
586
+ }
587
+
588
+ export interface DeliveryAvailability {
589
+ status: 'available' | 'unavailable'; // available when the store can deliver now or offers windows
590
+ canDeliverNow: boolean;
591
+ canSchedule: boolean;
592
+ timezone: string | null; // store IANA time zone; null when the server has none. The SDK shows times in the viewer's browser time zone, not this one
593
+ nextSlot: DeliverySlot | null; // null on a product fulfillment of a closed store: checkout gives the windows
594
+ checkedAt: string;
595
+ }
596
+
597
+ export interface CheckoutDeliveryScheduling {
598
+ availability: DeliveryAvailability;
599
+ slots: DeliverySlot[]; // only in answer to deliverySlotsFulfillmentId; empty otherwise
600
+ selectedSlotId: string | null; // null = as soon as possible / no window
601
+ selectedSlot?: DeliverySlot | null; // the accepted window with its real end and cutoff; null when none
602
+ }
603
+
604
+ export interface CheckoutDeliverySelection {
605
+ fulfillmentId: string;
606
+ slotId: string | null;
607
+ }
608
+ ```
609
+
610
+ `ICheckoutFulfillment.deliveryScheduling` is optional. `ICheckoutPrepareParams` takes `deliverySelections?: CheckoutDeliverySelection[]` and `deliverySlotsFulfillmentId?: string`. `actions.checkout` adds `loadDeliverySlots(fulfillmentId: string): Promise<DeliverySlot[]>` and `chooseDeliverySlot(fulfillmentId: string, slotId: string | null): Promise<void>` on both clients, and `ICheckoutDetailsEventData` adds `deliveryScheduling: Record<string, CheckoutDeliveryScheduling>`.
@@ -152,7 +152,10 @@ on render, and a live region recreated with its text already in place is not ann
152
152
  - An option in a single-select group carries its **whole** visible content in its accessible name.
153
153
  `option` and `radio` have presentational children, so the name is the only channel: a delivery
154
154
  option names the retailer, the price, the timing, the delivery fee and — for same-day — the
155
- store address and whether it is currently closed.
155
+ store address and whether it is currently closed. A closed store that schedules delivery leads
156
+ with that instead ("Columbus Avenue Wines, closed now, schedule delivery"), followed by
157
+ "next arrival Thu, 1 PM – 2 PM" only when the server sends a next window; its card
158
+ stays enabled.
156
159
  - The California Proposition 65 warning on the product is a native `<details>` / `<summary>`
157
160
  disclosure: the summary is a button that exposes its expanded state, and `Enter` / `Space` toggle
158
161
  it with no scripting involved. Its `+` / `−` indicator is decorative and hidden. In checkout it is
@@ -289,3 +292,7 @@ from the accessibility tree; unknown choices retain their named, keyboard-operab
289
292
  checkboxes. Clearing account preferences reveals the controls and synchronizes
290
293
  `aria-checked` with the current guest defaults. A shopper's explicit checkbox edit
291
294
  keeps that channel visible when account data arrives later.
295
+
296
+ ### Delivery date and arrival windows
297
+
298
+ The delivery picker is a named native modal dialog opened from each delivery's Schedule delivery or Edit control. Dates and arrival windows are radio groups with one tab stop each and arrow-key selection; the date strip scrolls inside the dialog and unavailable dates are disabled. While windows load, the dialog is marked busy and shows a loading line; Cancel and Close stay available. Changes are drafts until Save; Cancel, Close and Escape discard them, and closing returns focus to the opening control. The dialog stays the same modal while it is open: a date or window change updates its contents in place, so it is not announced again. Save errors keep the dialog open and move focus to the error. Success is announced politely; errors and an expired or unavailable window are announced assertively, each with its own message. No time zone is shown above the windows; the ASAP window's accessible name is "As soon as possible", and the first window's accessible name also carries its "Order by" time. The window list scrolls inside the dialog, and the chosen or focused window is kept in view as the picker opens, the date changes and arrow keys move.
@@ -14,6 +14,8 @@ The Cart component automatically:
14
14
  - Persists cart across sessions and tabs
15
15
  - Provides checkout navigation
16
16
 
17
+ A same-day line from a store that is closed now but takes orders for later (its fulfillment's `deliveryAvailability` says it cannot deliver now and can schedule) shows "Schedule delivery" in place of the delivery estimate. The shopper chooses the delivery window in checkout.
18
+
17
19
  ## Basic Usage
18
20
 
19
21
  The cart is automatically available when using the SDK - no explicit injection needed for the drawer. However, you need a way for users to open it.
@@ -286,6 +286,31 @@ console.log(checkoutData);
286
286
  // }
287
287
  ```
288
288
 
289
+ ### Load Delivery Slots
290
+
291
+ Load the delivery windows for one fulfillment that has scheduled delivery. `getDetails().deliveryScheduling` is keyed by fulfillment ID:
292
+
293
+ ```javascript
294
+ const actions = window.LiquidCommerce.elements.actions.checkout;
295
+ const [fulfillmentId] = Object.keys(actions.getDetails().deliveryScheduling);
296
+
297
+ const slots = await actions.loadDeliverySlots(fulfillmentId);
298
+ // [{ id: 'option_1:1791475200', start: '2026-10-08T16:00:00.000Z', end: '2026-10-08T17:00:00.000Z', cutoff: '2026-10-08T15:00:00.000Z' }, ...]
299
+ ```
300
+
301
+ ### Choose Delivery Slot
302
+
303
+ Save a window from the last loaded list, or `null` for as soon as possible:
304
+
305
+ ```javascript
306
+ await window.LiquidCommerce.elements.actions.checkout.chooseDeliverySlot(fulfillmentId, slots[0].id);
307
+
308
+ // As soon as possible (only when availability.canDeliverNow is true)
309
+ await window.LiquidCommerce.elements.actions.checkout.chooseDeliverySlot(fulfillmentId, null);
310
+ ```
311
+
312
+ The promise rejects when the window was not offered, the fulfillment is unknown or another checkout update is in flight. See the [checkout actions reference](../api/actions/checkout-actions.md#delivery-scheduling-actions).
313
+
289
314
  ## Events
290
315
 
291
316
  ### Checkout Lifecycle
@@ -373,7 +398,9 @@ window.addEventListener('lce:actions.checkout_item_quantity_decrease', (event) =
373
398
 
374
399
  ### Delivery Method
375
400
 
376
- Fires when the shopper switches a single-item checkout between same-day delivery and shipping.
401
+ Fires when the shopper switches a single-item checkout between same-day delivery and shipping. The
402
+ switch is shown only with `development.enableExpressCheckout: true`, the same opt-in as express
403
+ checkout.
377
404
 
378
405
  ```javascript
379
406
  window.addEventListener('lce:actions.checkout_delivery_method_updated', (event) => {
@@ -382,6 +409,17 @@ window.addEventListener('lce:actions.checkout_delivery_method_updated', (event)
382
409
  });
383
410
  ```
384
411
 
412
+ ### Delivery Time
413
+
414
+ Fires once after checkout accepts a delivery window or as soon as possible, from the built-in picker or from `chooseDeliverySlot`.
415
+
416
+ ```javascript
417
+ window.addEventListener('lce:actions.checkout_delivery_selection_updated', (event) => {
418
+ const { deliveryScheduling } = event.detail.data;
419
+ console.log('Delivery windows by fulfillment:', deliveryScheduling);
420
+ });
421
+ ```
422
+
385
423
  ### Tips
386
424
 
387
425
  ```javascript
@@ -427,6 +465,11 @@ window.addEventListener('lce:actions.checkout_express_checkout_availability', (e
427
465
  const { data } = event.detail;
428
466
  console.log(data.available ? `Wallets: ${data.wallets.join(', ')}` : 'No wallets available');
429
467
  });
468
+
469
+ // The shopper tapped a wallet button and its sheet opened
470
+ window.addEventListener('lce:actions.checkout_express_checkout_started', (event) => {
471
+ console.log(`Wallet sheet opened: ${event.detail.data.wallet}`);
472
+ });
430
473
  ```
431
474
 
432
475
  ### Promo Codes
@@ -563,7 +606,12 @@ Apple Pay and Google Pay are offered as express checkout buttons at the top of t
563
606
  the gift toggle. Tapping one collects the shopper's contact and billing details from the wallet and
564
607
  places the order without them completing the checkout forms.
565
608
 
566
- Express checkout is offered to every shopper whose platform can show a wallet. Each wallet is
609
+ Express checkout is off by default: pass `development.enableExpressCheckout: true` in the client config to show it. Without it the checkout element renders
610
+ no express block, and the injection methods below render nothing and resolve to `null`.
611
+
612
+ > **Temporary:** this flag exists only while express checkout and the delivery-method switch are being verified. Once they are confirmed, the flag will be removed and both will always be on.
613
+
614
+ Once enabled, express checkout is offered to every shopper whose platform can show a wallet. Each wallet is
567
615
  requested with Stripe's `always` policy, so the button renders wherever the device supports it rather
568
616
  than only where Stripe judges it good for conversion. The block hides itself entirely — heading,
569
617
  buttons and divider — in four cases: the device or browser offers no wallet, the order is a
@@ -856,3 +904,17 @@ ownership and for reloading checkout when identity changes.
856
904
  Order completion waits for queued preference work. It cannot race a late saved
857
905
  opt-out or proceed with an unconfirmed saved choice. If applying a known choice
858
906
  fails, the shopper can retry or explicitly choose the visible channel preference.
907
+
908
+ ## Scheduled delivery
909
+
910
+ When an AccelPay retailer has scheduled delivery turned on, checkout shows a **Delivery time** row for that delivery, between the delivery details and payment. It reads "ASAP" while the store can deliver now, or asks for a window when it cannot. The row's button reads **Schedule** while the row is on as soon as possible, **Edit** after a window is saved, and **Schedule delivery** when the shopper must choose a window. It opens a dialog that loads the windows for that delivery: date cards for today and the next 7 days, then arrival windows. Days and times are in the shopper's own time zone (the browser's), so "Today" is the shopper's today; no time zone is shown. The window list scrolls inside the dialog and keeps the chosen window in view. The first window shows "Order by" with its cutoff, and **ASAP** sits next to it when the store can deliver now (screen readers hear "As soon as possible"). Choices are drafts until **Save**; Cancel, Close and Escape discard them.
911
+
912
+ The accepted window shows in the row and on the items in the order summary ("Scheduled for Thu, 1 PM – 2 PM"), and on the thank-you page as "Delivery time" with "Thu, 1 PM – 2 PM". An ASAP order adds no line there. If the window's cutoff passes before the order is placed, the SDK clears it, shows "Your delivery window has passed. Choose a new delivery time.", reopens the picker and keeps Place Order disabled until a new choice is saved. If the store no longer offers the window, the same happens with "Your delivery window is no longer available. Choose a new delivery time." Everything else the shopper entered stays.
913
+
914
+ A store that was added to the cart while closed cannot deliver now, so checkout asks for a window and offers no ASAP. If a store closes after the shopper chose ASAP, Place Order answers `delivery_window_required`: the SDK shows "The store cannot deliver now. Choose a delivery time.", opens the picker and keeps Place Order disabled until a window is saved.
915
+
916
+ In a checkout with more than one fulfillment group (another store, or shipping), each same-day group with scheduled delivery gets its own **Delivery time** row and its own picker, which loads that group's windows. Its button reads **Schedule this store's delivery** (**Edit** after a window is saved), and the picker is named after the store ("Schedule delivery from Columbus Avenue Wines"). Every prepare sends each group's current choice, so saving one group keeps the other's window. A group whose store can deliver now stays on as soon as possible; a group whose store cannot needs its own window before payment and Place Order. When a window lapses, only that group's window is cleared and only its picker reopens: the SDK clears the window whose cutoff passed on the page, and for `delivery_window_required` marks the groups that have no window. When an error names its group (`fulfillmentId` on the error entry), only that group is cleared. A name that is not a scheduling group of this checkout counts as no name; when the error names no group and the SDK cannot place it, every saved window is cleared (a refused window save clears nothing and asks for another time). When the save of one store's window is refused because the window of another store lapsed, the SDK clears that other window and names its store: "Choose a delivery window for Columbus Avenue Wines before continuing." Until each store that cannot deliver now has a window, payment says which: "Choose a delivery window for Amsterdam Liquors before continuing." In an order with more than one fulfillment group, the thank-you page has one line per group, named after its store: the accepted window for a scheduled group ("Columbus Avenue Wines" with "Thu, 1 PM – 2 PM"), and the group's own expectation for the others ("Harbor Spirits" with "Ships in 3-5 days"). A scheduled group never shows the as-soon-as-possible expectation. While a store that cannot deliver now has no accepted window, the express wallet buttons stay off, like the card form. When checkout refuses a store that has scheduled delivery and also another delivery method (`multiple_fulfillment_options_per_store`, code 5516), the SDK shows the refusal as the checkout error and keeps the saved windows: "Scheduled delivery cannot be combined with another delivery method from the same store. Choose ASAP delivery, or use one delivery method for all items from this store." When that store is closed now, the text leaves out ASAP: "This store is closed now. Use one delivery method for all items from this store." When the refusal says whether the store is closed now (`closedNow` on the error entry), the SDK uses that. Otherwise, when Place Order is refused, the SDK reads the cart again and decides "closed now" from its `deliveryAvailability`, because a store can close after the last prepare. Place Order and the express wallet stay off until a prepare succeeds after a change to the cart or to a delivery choice.
917
+
918
+ In the delivery-method switch, a same-day method whose `deliveryAvailability` says the store cannot deliver now but can schedule shows "Schedule delivery" in place of its delivery estimate. Without `deliveryAvailability` the card shows the estimate as before.
919
+
920
+ Checkouts without scheduled delivery are unchanged. For a custom UI, see `actions.checkout.loadDeliverySlots` and `actions.checkout.chooseDeliverySlot` in the checkout actions reference, and the `checkout_delivery_selection_updated` event.
@@ -502,7 +502,7 @@ window.addEventListener('lce:actions.cart_product_add_failed', (event) => {
502
502
 
503
503
  Fires when checkout data loads.
504
504
 
505
- **Data includes:** `cartId`, `amounts`, `itemCount`, `isGift`, `billingSameAsShipping`.
505
+ **Data includes:** `cartId`, `amounts`, `itemCount`, `isGift`, `billingSameAsShipping`, `deliveryScheduling` (keyed by fulfillment ID).
506
506
 
507
507
  **Example**
508
508
 
@@ -709,7 +709,8 @@ window.addEventListener('lce:actions.checkout_item_engraving_updated', (event) =
709
709
  ### `lce:actions.checkout_delivery_method_updated`
710
710
 
711
711
  Fires when the shopper moves a single-item checkout between same-day delivery and shipping. The order
712
- is re-priced, so the totals change with it.
712
+ is re-priced, so the totals change with it. The switch is shown only with
713
+ `development.enableExpressCheckout: true`.
713
714
 
714
715
  **Data includes:** `cartId`, `fulfillmentType`, `previousFulfillmentType`, `fulfillmentId`.
715
716
 
@@ -893,6 +894,26 @@ window.addEventListener('lce:actions.checkout_express_checkout_availability', (e
893
894
  });
894
895
  ```
895
896
 
897
+ ### `lce:actions.checkout_express_checkout_started`
898
+
899
+ Fires when the shopper taps a wallet button and the SDK opens the wallet sheet, inside the checkout
900
+ or on a block placed with `injectExpressCheckout` / `injectExpressCheckoutElement`. It fires for every
901
+ tap, and not for a tap the block refuses (a total that moved, an unmet order minimum, a second tap
902
+ while an order is being placed). The tap happens inside Stripe's frame, so this is the only way a host
903
+ page learns of it — for instance, to report `add_to_cart` for a product-page wallet with
904
+ [`actions.analytics`](../api/actions/analytics-actions.md).
905
+
906
+ **Data includes:** `cartId`, `wallet` (Stripe's name for it: `'apple_pay'`, `'google_pay'`, …).
907
+
908
+ **Example**
909
+
910
+ ```javascript
911
+ window.addEventListener('lce:actions.checkout_express_checkout_started', (event) => {
912
+ const { wallet } = event.detail.data;
913
+ console.log(`Wallet sheet opened: ${wallet}`);
914
+ });
915
+ ```
916
+
896
917
  ### `lce:actions.checkout_payment_form_failed`
897
918
 
898
919
  Fires when the card form cannot load Stripe.js, so the card fields cannot appear. The shopper sees a
@@ -946,6 +967,19 @@ window.addEventListener('lce:actions.checkout_product_add_failed', (event) => {
946
967
  });
947
968
  ```
948
969
 
970
+ ### `lce:actions.checkout_delivery_selection_updated`
971
+
972
+ Emitted once after checkout prepare accepts a delivery window or ASAP choice, from the built-in picker or from `actions.checkout.chooseDeliverySlot`. Draft choices in the picker and failed saves do not emit it.
973
+
974
+ **Data:** `ICheckoutDetailsEventData`, including `deliveryScheduling: Record<string, CheckoutDeliveryScheduling>` keyed by fulfillment ID, with each accepted `selectedSlotId` and `selectedSlot`.
975
+
976
+ ```javascript
977
+ window.addEventListener('lce:actions.checkout_delivery_selection_updated', (event) => {
978
+ const scheduling = event.detail.data.deliveryScheduling;
979
+ console.log('Delivery windows:', scheduling);
980
+ });
981
+ ```
982
+
949
983
  ## Form Events
950
984
 
951
985
  ### `lce:forms.customer`
@@ -480,6 +480,12 @@ await window.LiquidCommerce.elements.actions.address.setAddressManually(
480
480
  );
481
481
  ```
482
482
 
483
+ ## Buying when a retailer is closed
484
+
485
+ A same-day retailer whose store schedules delivery can take orders while it is closed. Its fulfillment then carries `deliveryAvailability`. When that says the store cannot deliver now but can schedule (`status: 'available'`, `canSchedule: true`), the retailer card stays on and shows "Schedule delivery". The product page shows no time: the server sends `nextSlot: null` for a closed store, and checkout asks the shopper to choose a delivery window. Add to Cart stays on. Screen readers hear "Columbus Avenue Wines, closed now, schedule delivery" in place of "Currently closed". When the server does send a `nextSlot`, the card adds it on a second line, in the shopper's own time zone ("Schedule delivery", then "Thu, 1 PM – 2 PM") and screen readers hear "…, next arrival Thu, 1 PM – 2 PM". The SDK selects a retailer that can deliver now first, then one that schedules delivery, and goes to shipping only when neither is available.
486
+
487
+ A closed store without scheduled delivery is blocked as before. An unknown or unavailable `deliveryAvailability`, or a `nextSlot` whose order cutoff has passed, never turns purchase on.
488
+
483
489
  ## Presale Products
484
490
 
485
491
  Products in presale mode:
@@ -10,6 +10,11 @@ This runs entirely inside the SDK. In most cases you do not need to configure or
10
10
 
11
11
  It coexists with a host site's own GTM — see [Coexisting With Your Own GTM](#coexisting-with-your-own-gtm) below.
12
12
 
13
+ The one case that needs code: if **your own UI** renders a step Elements would otherwise render — a
14
+ product page, an address picker, a cart, or a "Proceed to checkout" in front of the checkout-only
15
+ client — Elements never sees it. Report it with `actions.analytics` — see
16
+ [Reporting Steps Your Own UI Renders](#reporting-steps-your-own-ui-renders).
17
+
13
18
  ## Supported Environments
14
19
 
15
20
  GTM initialization and every event push are enabled in production and staging. When the SDK is running in Builder mode, no container is loaded and no events are sent.
@@ -93,22 +98,23 @@ Every event automatically carries source-tracking fields identifying the tenant
93
98
 
94
99
  ## Events
95
100
 
96
- All events are GA4-shaped and follow the standard ecommerce schema (`items[]`, `value`, `currency` — the SDK uses `USD`, `coupon`, etc.).
101
+ All events are GA4-shaped and follow the standard ecommerce schema (`items[]`, `value`, `currency` — the SDK uses `USD`, `coupon`, etc.). `search` is the exception: it carries only `search_term`.
97
102
 
98
103
  ### Standard GA4 ecommerce events
99
104
 
100
105
  | Event | Fired when |
101
106
  | --- | --- |
102
- | `view_item` | A shopper views a product |
103
- | `view_item_list` | A shopper views a list of products |
104
- | `select_item` | A shopper selects a product from a list |
105
- | `add_to_cart` | A shopper adds an item to the cart |
106
- | `view_cart` | A shopper views the cart |
107
- | `remove_from_cart` | A shopper removes an item from the cart |
108
- | `begin_checkout` | A shopper begins checkout |
109
- | `add_shipping_info` | Shipping info is added during checkout |
107
+ | `view_item` | A shopper views a product, and again on each size they switch to |
108
+ | `view_item_list` | A product list loads products: its first load, a filtered or searched list, each "load more" |
109
+ | `select_item` | A shopper opens a product from a product list card |
110
+ | `add_to_cart` | A shopper adds an item: from the product component or a list card, `actions.cart.addProduct()` / `actions.checkout.addProduct()`, a cart or checkout quantity increase, or a presale added straight to checkout |
111
+ | `view_cart` | The cart drawer opens with items in it — the cart button, `openCart()` / `toggleCart()`, the checkout's back button — or an add that opens it lands |
112
+ | `remove_from_cart` | A shopper lowers an item's quantity or removes it, in the cart or in the checkout |
113
+ | `begin_checkout` | A shopper begins checkout: the cart's Checkout button (also before its redirect to `checkout.pageUrl`), `actions.checkout.addProduct()`, `openCheckout()` / `toggleCheckout()` opening the drawer, a presale added straight to checkout, or the first wallet tap on express buttons placed on their own |
114
+ | `add_shipping_info` | Shipping info is added during checkout: the customer or gift recipient form is saved, or a wallet (Apple Pay / Google Pay) order hands over the wallet's contact details |
110
115
  | `add_payment_info` | Payment info is added during checkout |
111
116
  | `purchase` | A purchase completes |
117
+ | `search` | A search typed into the product list's search box runs |
112
118
 
113
119
  ### Custom events
114
120
 
@@ -126,8 +132,45 @@ All events are GA4-shaped and follow the standard ecommerce schema (`items[]`, `
126
132
  | `product_size_no_availability` | A selected size has no availability |
127
133
  | `product_fulfillment_no_availability` | A selected fulfillment type has no availability |
128
134
 
135
+ ## Reporting Steps Your Own UI Renders
136
+
137
+ Elements reports the steps its components render. A host that renders some steps itself reports
138
+ them through `actions.analytics`, on the full client and on the checkout-only client:
139
+
140
+ | Action | Event |
141
+ | --- | --- |
142
+ | `search(searchTerm)` | `search` |
143
+ | `viewItem(item)` | `view_item` |
144
+ | `viewItemList(items, list?)` | `view_item_list` |
145
+ | `selectItem(item, list?)` | `select_item` |
146
+ | `addToCart(item)` | `add_to_cart` |
147
+ | `removeFromCart(item)` | `remove_from_cart` |
148
+ | `viewCart(items)` | `view_cart` |
149
+ | `beginCheckout(items, { coupon? })` | `begin_checkout` |
150
+ | `addressUpdated(address)` | `address_updated` |
151
+ | `addressFailed(failure)` | `address_failed` |
152
+ | `productNoAvailability(details)` | `product_no_availability` |
153
+ | `productSizeNoAvailability(details)` | `product_size_no_availability` |
154
+ | `productFulfillmentNoAvailability(details)` | `product_fulfillment_no_availability` |
155
+
156
+ They go through the same pipeline as the SDK's own events — same shape, `tenant_*` fields,
157
+ `send_to` targets, environment gating and queueing — so a report built on Elements events covers
158
+ both. There are no actions for `add_shipping_info`, `add_payment_info`, `purchase` or the promo code
159
+ and gift card events: the Elements checkout reports those itself, and a host reporting them too
160
+ would count every order twice. For the same reason, don't report a step an Elements component
161
+ renders — including `begin_checkout`, which Elements reports from the cart's Checkout button,
162
+ `actions.checkout.addProduct()`, `openCheckout()` / `toggleCheckout()`, a presale, and the first
163
+ wallet tap on express buttons placed on their own.
164
+
165
+ Malformed input never throws: the event is skipped and a warning logged, so reporting can't break
166
+ the host's page.
167
+
168
+ Items take prices in cents, as product responses carry them, and send them in dollars. Full
169
+ signatures and examples: [Analytics Actions](../api/actions/analytics-actions.md).
170
+
129
171
  ## Related Docs
130
172
 
131
173
  - [Events Guide](../guides/events.md)
174
+ - [Analytics Actions](../api/actions/analytics-actions.md)
132
175
  - [Client API](../api/client.md)
133
176
  - [Troubleshooting](./troubleshooting.md)
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "LiquidCommerce Elements SDK",
4
4
  "license": "UNLICENSED",
5
5
  "author": "LiquidCommerce Team",
6
- "version": "2.7.44",
6
+ "version": "2.7.45",
7
7
  "homepage": "https://docs.liquidcommerce.co/elements-sdk",
8
8
  "repository": {
9
9
  "type": "git",