@liquidcommerce/elements-sdk 2.7.21 → 2.7.23
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.
- package/README.md +1 -1
- package/dist/index.checkout.esm.js +7220 -7124
- package/dist/index.esm.js +11633 -11540
- package/dist/types/auto-initialize/shared-utils.d.ts +1 -0
- package/dist/types/clients/builder.d.ts +1 -1
- package/dist/types/clients/checkout.d.ts +1 -1
- package/dist/types/clients/main.d.ts +1 -1
- package/dist/types/core/pubsub/interfaces/address.interface.d.ts +3 -0
- package/dist/types/core/pubsub/interfaces/core.interface.d.ts +2 -2
- package/dist/types/utils/product.d.ts +5 -0
- package/docs/v1/api/actions/address-actions.md +20 -15
- package/docs/v1/api/actions/cart-actions.md +22 -23
- package/docs/v1/api/actions/checkout-actions.md +72 -25
- package/docs/v1/api/actions/product-actions.md +61 -15
- package/docs/v1/api/client.md +38 -14
- package/docs/v1/api/configuration.md +5 -1
- package/docs/v1/api/injection-methods.md +8 -4
- package/docs/v1/api/typescript-types.md +6 -0
- package/docs/v1/examples/advanced-patterns.md +7 -6
- package/docs/v1/examples/checkout-flow.md +1 -2
- package/docs/v1/getting-started/concepts.md +20 -25
- package/docs/v1/getting-started/installation.md +4 -4
- package/docs/v1/guides/address-component.md +12 -8
- package/docs/v1/guides/best-practices.md +5 -5
- package/docs/v1/guides/cart-component.md +27 -39
- package/docs/v1/guides/checkout-component.md +27 -29
- package/docs/v1/guides/events.md +4 -4
- package/docs/v1/guides/product-component.md +64 -19
- package/docs/v1/guides/product-list-component.md +8 -9
- package/docs/v1/guides/theming.md +6 -9
- package/docs/v1/integration/proxy-setup.md +22 -5
- package/docs/v1/reference/browser-support.md +2 -1
- package/docs/v1/reference/error-handling.md +12 -7
- package/docs/v1/reference/performance.md +1 -3
- package/docs/v1/reference/troubleshooting.md +3 -3
- package/package.json +9 -17
|
@@ -45,13 +45,12 @@ Configure checkout to load from URL parameter:
|
|
|
45
45
|
data-liquid-commerce-elements
|
|
46
46
|
data-token="YOUR_API_KEY"
|
|
47
47
|
data-env="production"
|
|
48
|
-
data-checkout-container="checkout"
|
|
49
48
|
data-checkout-param="lce_checkout"
|
|
50
49
|
type="text/javascript"
|
|
51
50
|
src="https://elements.reservebar-worker.workers.dev/all/elements.js"
|
|
52
51
|
></script>
|
|
53
52
|
|
|
54
|
-
<div
|
|
53
|
+
<div data-lce-checkout></div>
|
|
55
54
|
```
|
|
56
55
|
|
|
57
56
|
Visit `https://yoursite.com/checkout?lce_checkout=checkout_abc123` to load that checkout.
|
|
@@ -208,7 +207,7 @@ window.LiquidCommerce.elements.actions.checkout.updateCustomerInfo({
|
|
|
208
207
|
lastName: 'Doe',
|
|
209
208
|
email: 'john@example.com',
|
|
210
209
|
phone: '+15551234567',
|
|
211
|
-
birthDate: '
|
|
210
|
+
birthDate: '1990-01-15',
|
|
212
211
|
addressTwo: 'Apt 4',
|
|
213
212
|
company: 'Acme Corp'
|
|
214
213
|
});
|
|
@@ -274,7 +273,7 @@ console.log(checkoutData);
|
|
|
274
273
|
// {
|
|
275
274
|
// token: 'abc123xyz',
|
|
276
275
|
// cartId: 'cart_abc123',
|
|
277
|
-
// items:
|
|
276
|
+
// items: { 'cart_item_id': { ... } },
|
|
278
277
|
// amounts: {
|
|
279
278
|
// subtotal: 9998,
|
|
280
279
|
// tax: 800,
|
|
@@ -282,8 +281,6 @@ console.log(checkoutData);
|
|
|
282
281
|
// tip: 200,
|
|
283
282
|
// total: 11498
|
|
284
283
|
// },
|
|
285
|
-
// customer: { ... },
|
|
286
|
-
// shippingAddress: { ... },
|
|
287
284
|
// isGift: false,
|
|
288
285
|
// ...
|
|
289
286
|
// }
|
|
@@ -307,7 +304,7 @@ window.addEventListener('lce:actions.checkout_closed', (event) => {
|
|
|
307
304
|
});
|
|
308
305
|
|
|
309
306
|
window.addEventListener('lce:actions.checkout_failed', (event) => {
|
|
310
|
-
console.error('Checkout failed:', event.detail.data.
|
|
307
|
+
console.error('Checkout failed:', event.detail.data.message);
|
|
311
308
|
});
|
|
312
309
|
```
|
|
313
310
|
|
|
@@ -315,8 +312,8 @@ window.addEventListener('lce:actions.checkout_failed', (event) => {
|
|
|
315
312
|
|
|
316
313
|
```javascript
|
|
317
314
|
window.addEventListener('lce:actions.checkout_customer_information_updated', (event) => {
|
|
318
|
-
const {
|
|
319
|
-
console.log('Customer info updated:',
|
|
315
|
+
const { cartId } = event.detail.data;
|
|
316
|
+
console.log('Customer info updated for cart:', cartId);
|
|
320
317
|
});
|
|
321
318
|
```
|
|
322
319
|
|
|
@@ -324,8 +321,8 @@ window.addEventListener('lce:actions.checkout_customer_information_updated', (ev
|
|
|
324
321
|
|
|
325
322
|
```javascript
|
|
326
323
|
window.addEventListener('lce:actions.checkout_is_gift_toggled', (event) => {
|
|
327
|
-
const {
|
|
328
|
-
console.log('Gift mode:',
|
|
324
|
+
const { isActive } = event.detail.data;
|
|
325
|
+
console.log('Gift mode:', isActive ? 'enabled' : 'disabled');
|
|
329
326
|
});
|
|
330
327
|
|
|
331
328
|
window.addEventListener('lce:actions.checkout_gift_information_updated', (event) => {
|
|
@@ -337,8 +334,8 @@ window.addEventListener('lce:actions.checkout_gift_information_updated', (event)
|
|
|
337
334
|
|
|
338
335
|
```javascript
|
|
339
336
|
window.addEventListener('lce:actions.checkout_billing_same_as_shipping_toggled', (event) => {
|
|
340
|
-
const {
|
|
341
|
-
console.log('Billing same as shipping:',
|
|
337
|
+
const { isActive } = event.detail.data;
|
|
338
|
+
console.log('Billing same as shipping:', isActive);
|
|
342
339
|
});
|
|
343
340
|
|
|
344
341
|
window.addEventListener('lce:actions.checkout_billing_information_updated', (event) => {
|
|
@@ -350,8 +347,8 @@ window.addEventListener('lce:actions.checkout_billing_information_updated', (eve
|
|
|
350
347
|
|
|
351
348
|
```javascript
|
|
352
349
|
window.addEventListener('lce:actions.checkout_marketing_preferences_toggled', (event) => {
|
|
353
|
-
const {
|
|
354
|
-
console.log(`Marketing ${
|
|
350
|
+
const { fieldName, isActive } = event.detail.data;
|
|
351
|
+
console.log(`Marketing ${fieldName}:`, isActive);
|
|
355
352
|
});
|
|
356
353
|
```
|
|
357
354
|
|
|
@@ -359,18 +356,18 @@ window.addEventListener('lce:actions.checkout_marketing_preferences_toggled', (e
|
|
|
359
356
|
|
|
360
357
|
```javascript
|
|
361
358
|
window.addEventListener('lce:actions.checkout_item_removed', (event) => {
|
|
362
|
-
const {
|
|
363
|
-
console.log('Item removed:',
|
|
359
|
+
const { cartItemId } = event.detail.data;
|
|
360
|
+
console.log('Item removed:', cartItemId);
|
|
364
361
|
});
|
|
365
362
|
|
|
366
363
|
window.addEventListener('lce:actions.checkout_item_quantity_increase', (event) => {
|
|
367
|
-
const {
|
|
368
|
-
console.log(`Item ${
|
|
364
|
+
const { cartItemId, quantity } = event.detail.data;
|
|
365
|
+
console.log(`Item ${cartItemId} quantity: ${quantity}`);
|
|
369
366
|
});
|
|
370
367
|
|
|
371
368
|
window.addEventListener('lce:actions.checkout_item_quantity_decrease', (event) => {
|
|
372
|
-
const {
|
|
373
|
-
console.log(`Item ${
|
|
369
|
+
const { cartItemId, quantity } = event.detail.data;
|
|
370
|
+
console.log(`Item ${cartItemId} quantity: ${quantity}`);
|
|
374
371
|
});
|
|
375
372
|
```
|
|
376
373
|
|
|
@@ -378,8 +375,9 @@ window.addEventListener('lce:actions.checkout_item_quantity_decrease', (event) =
|
|
|
378
375
|
|
|
379
376
|
```javascript
|
|
380
377
|
window.addEventListener('lce:actions.checkout_tip_updated', (event) => {
|
|
381
|
-
const {
|
|
382
|
-
|
|
378
|
+
const { deliveryTips } = event.detail.data;
|
|
379
|
+
const total = deliveryTips.reduce((sum, t) => sum + t.tip, 0);
|
|
380
|
+
console.log('Tip amount:', total / 100);
|
|
383
381
|
});
|
|
384
382
|
```
|
|
385
383
|
|
|
@@ -392,14 +390,14 @@ window.addEventListener('lce:actions.checkout_submit_started', (event) => {
|
|
|
392
390
|
});
|
|
393
391
|
|
|
394
392
|
window.addEventListener('lce:actions.checkout_submit_completed', (event) => {
|
|
395
|
-
const {
|
|
396
|
-
console.log('Order completed:',
|
|
393
|
+
const { orderNumber } = event.detail.data;
|
|
394
|
+
console.log('Order completed:', orderNumber);
|
|
397
395
|
// Redirect to confirmation page
|
|
398
396
|
});
|
|
399
397
|
|
|
400
398
|
window.addEventListener('lce:actions.checkout_submit_failed', (event) => {
|
|
401
|
-
const {
|
|
402
|
-
console.error('Payment failed:',
|
|
399
|
+
const { message } = event.detail.data;
|
|
400
|
+
console.error('Payment failed:', message);
|
|
403
401
|
// Show error message
|
|
404
402
|
});
|
|
405
403
|
```
|
|
@@ -566,8 +564,8 @@ window.addEventListener('lce:actions.checkout_customer_information_updated', ()
|
|
|
566
564
|
|
|
567
565
|
window.addEventListener('lce:actions.checkout_submit_completed', (event) => {
|
|
568
566
|
gtag('event', 'purchase', {
|
|
569
|
-
transaction_id: event.detail.data.
|
|
570
|
-
value: event.detail.data.
|
|
567
|
+
transaction_id: event.detail.data.orderNumber,
|
|
568
|
+
value: event.detail.data.orderTotal / 100,
|
|
571
569
|
currency: 'USD'
|
|
572
570
|
});
|
|
573
571
|
});
|
package/docs/v1/guides/events.md
CHANGED
|
@@ -64,7 +64,7 @@ window.addEventListener('lce:actions.client_ready', (event) => {
|
|
|
64
64
|
|
|
65
65
|
Fires when a product component finishes loading and product data is available.
|
|
66
66
|
|
|
67
|
-
**Data includes:** `identifier`, `name`, `
|
|
67
|
+
**Data includes:** `identifier`, `name`, `priceInfo`, `selectedSizeId`, `selectedFulfillmentType`.
|
|
68
68
|
|
|
69
69
|
**Example**
|
|
70
70
|
|
|
@@ -819,7 +819,7 @@ window.addEventListener('lce:actions.checkout_gift_card_failed', (event) => {
|
|
|
819
819
|
|
|
820
820
|
Fires when products are added to checkout via the actions API.
|
|
821
821
|
|
|
822
|
-
**Data includes:** `cartId`, `itemsAdded`, `identifiers
|
|
822
|
+
**Data includes:** `cartId`, `itemsAdded`, `identifiers`.
|
|
823
823
|
|
|
824
824
|
**Example**
|
|
825
825
|
|
|
@@ -834,7 +834,7 @@ window.addEventListener('lce:actions.checkout_product_add_success', (event) => {
|
|
|
834
834
|
|
|
835
835
|
Fires when adding products to checkout via the actions API fails.
|
|
836
836
|
|
|
837
|
-
**Data includes:** `cartId`, `identifiers`, `error
|
|
837
|
+
**Data includes:** `cartId`, `identifiers`, `error`.
|
|
838
838
|
|
|
839
839
|
**Example**
|
|
840
840
|
|
|
@@ -904,7 +904,7 @@ window.addEventListener('lce:actions.product_loaded', (event) => {
|
|
|
904
904
|
items: [{
|
|
905
905
|
item_id: data.identifier,
|
|
906
906
|
item_name: data.name,
|
|
907
|
-
price: data.
|
|
907
|
+
price: data.priceInfo?.minimum / 100
|
|
908
908
|
}]
|
|
909
909
|
});
|
|
910
910
|
});
|
|
@@ -157,6 +157,28 @@ For products with multiple sizes:
|
|
|
157
157
|
- Availability checking per size
|
|
158
158
|
- Out-of-stock indication
|
|
159
159
|
|
|
160
|
+
#### Preselect a Size via URL
|
|
161
|
+
|
|
162
|
+
Use the `lce_size` query parameter to preselect a size when the product page loads:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
https://yoursite.com/products/buffalo-trace?lce_size=750ml
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- The value is compared against each size's full label as shown on the page.
|
|
169
|
+
- Matching is case-insensitive and ignores spacing and punctuation — only letters, digits, and decimal points are compared — so `750 ML`, `750ml`, and `750ML` all select a `750ml` size, while decimals are kept so `1.0L` and `1.75L` stay distinct.
|
|
170
|
+
- If the value matches no size, the default size selection is used.
|
|
171
|
+
|
|
172
|
+
**Pack sizes:** the label includes the pack description when present (e.g. `50 ML (12PK)`). To select that size, include the pack in the value:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
https://yoursite.com/products/mini-bottles?lce_size=50ml(12pk)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Both `50ml(12pk)` and `50ml12pk` match `50 ML (12PK)` (punctuation is ignored). A value of just `50ml` only matches a size whose label is `50 ML` with no pack.
|
|
179
|
+
|
|
180
|
+
`lce_size` is a fixed parameter name (not configured through a script attribute) and only applies to the product page.
|
|
181
|
+
|
|
160
182
|
### Fulfillment Types
|
|
161
183
|
|
|
162
184
|
Two fulfillment options:
|
|
@@ -183,7 +205,7 @@ For products with multiple retailers:
|
|
|
183
205
|
- Select with one tap
|
|
184
206
|
|
|
185
207
|
**Popup View**
|
|
186
|
-
- "
|
|
208
|
+
- "See Delivery Options" button (shows the available fulfillment count, e.g. "See Delivery Options (3)")
|
|
187
209
|
- Modal with full retailer list
|
|
188
210
|
- Filter and search capabilities
|
|
189
211
|
|
|
@@ -218,17 +240,38 @@ console.log(productData);
|
|
|
218
240
|
// {
|
|
219
241
|
// identifier: '00619947000020',
|
|
220
242
|
// name: 'Premium Whiskey',
|
|
221
|
-
//
|
|
222
|
-
//
|
|
243
|
+
// priceInfo: { currency: 'USD', minimum: 4999, average: 4999, maximum: 4999 },
|
|
244
|
+
// selectedSizeId: '750ml',
|
|
223
245
|
// selectedFulfillmentType: 'shipping',
|
|
224
|
-
//
|
|
225
|
-
//
|
|
246
|
+
// selectedFulfillmentId: 'fulfillment_123',
|
|
247
|
+
// productHasAvailability: true,
|
|
248
|
+
// fulfillmentHasAvailability: true,
|
|
249
|
+
// sizes: { '750ml': { ... } },
|
|
226
250
|
// ...
|
|
227
251
|
// }
|
|
228
252
|
```
|
|
229
253
|
|
|
230
254
|
**Note:** The product must be injected and loaded before calling `getDetails()`. If the product hasn't been loaded, an error is thrown.
|
|
231
255
|
|
|
256
|
+
### Get Product Availability by State
|
|
257
|
+
|
|
258
|
+
Check availability for one or more products in a given state. Returns a `Promise<IProductAvailabilityResponse>`:
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
const availability = await window.LiquidCommerce.elements.actions.product.getProductAvailabilityByState(
|
|
262
|
+
['00619947000020', '08504405135'],
|
|
263
|
+
'NY'
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
console.log(availability);
|
|
267
|
+
// {
|
|
268
|
+
// products: [...],
|
|
269
|
+
// retailers: { ... }
|
|
270
|
+
// }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The `state` argument is optional; at least one product identifier is required.
|
|
274
|
+
|
|
232
275
|
## Events
|
|
233
276
|
|
|
234
277
|
Listen for product-related events:
|
|
@@ -239,8 +282,8 @@ Fired when product data is successfully loaded:
|
|
|
239
282
|
|
|
240
283
|
```javascript
|
|
241
284
|
window.addEventListener('lce:actions.product_loaded', (event) => {
|
|
242
|
-
const { identifier, name,
|
|
243
|
-
console.log(`Product loaded: ${name} - $${
|
|
285
|
+
const { identifier, name, priceInfo } = event.detail.data;
|
|
286
|
+
console.log(`Product loaded: ${name} - $${priceInfo.minimum / 100}`);
|
|
244
287
|
});
|
|
245
288
|
```
|
|
246
289
|
|
|
@@ -250,8 +293,8 @@ Fired when user clicks "Add to Cart":
|
|
|
250
293
|
|
|
251
294
|
```javascript
|
|
252
295
|
window.addEventListener('lce:actions.product_add_to_cart', (event) => {
|
|
253
|
-
const { identifier, quantity,
|
|
254
|
-
console.log(`Adding ${quantity}x ${identifier} (${
|
|
296
|
+
const { identifier, quantity, fulfillmentId } = event.detail.data;
|
|
297
|
+
console.log(`Adding ${quantity}x ${identifier} (${fulfillmentId})`);
|
|
255
298
|
});
|
|
256
299
|
```
|
|
257
300
|
|
|
@@ -261,8 +304,8 @@ Fired when user selects a different size:
|
|
|
261
304
|
|
|
262
305
|
```javascript
|
|
263
306
|
window.addEventListener('lce:actions.product_size_changed', (event) => {
|
|
264
|
-
const { identifier,
|
|
265
|
-
console.log(`Size changed to ${selectedSize
|
|
307
|
+
const { identifier, selectedSizeId, selectedSize } = event.detail.data;
|
|
308
|
+
console.log(`Size changed to ${selectedSize} (${selectedSizeId})`);
|
|
266
309
|
});
|
|
267
310
|
```
|
|
268
311
|
|
|
@@ -272,8 +315,8 @@ Fired when user switches between shipping and on-demand:
|
|
|
272
315
|
|
|
273
316
|
```javascript
|
|
274
317
|
window.addEventListener('lce:actions.product_fulfillment_type_changed', (event) => {
|
|
275
|
-
const { identifier,
|
|
276
|
-
console.log(`Fulfillment type changed to: ${
|
|
318
|
+
const { identifier, selectedFulfillmentType } = event.detail.data;
|
|
319
|
+
console.log(`Fulfillment type changed to: ${selectedFulfillmentType}`);
|
|
277
320
|
});
|
|
278
321
|
```
|
|
279
322
|
|
|
@@ -283,8 +326,8 @@ Fired when user selects a different retailer:
|
|
|
283
326
|
|
|
284
327
|
```javascript
|
|
285
328
|
window.addEventListener('lce:actions.product_fulfillment_changed', (event) => {
|
|
286
|
-
const { identifier,
|
|
287
|
-
console.log(`
|
|
329
|
+
const { identifier, selectedFulfillmentId, selectedFulfillmentType } = event.detail.data;
|
|
330
|
+
console.log(`Fulfillment changed to ${selectedFulfillmentId} (${selectedFulfillmentType})`);
|
|
288
331
|
});
|
|
289
332
|
```
|
|
290
333
|
|
|
@@ -397,7 +440,8 @@ await window.LiquidCommerce.elements.actions.address.setAddressManually(
|
|
|
397
440
|
two: 'Apt 4',
|
|
398
441
|
city: 'New York',
|
|
399
442
|
state: 'NY',
|
|
400
|
-
zip: '10001'
|
|
443
|
+
zip: '10001',
|
|
444
|
+
country: 'US'
|
|
401
445
|
},
|
|
402
446
|
{
|
|
403
447
|
latitude: 40.7128,
|
|
@@ -458,8 +502,9 @@ console.log(container); // <div id="product-1">...</div>
|
|
|
458
502
|
If a product identifier doesn't exist:
|
|
459
503
|
|
|
460
504
|
```javascript
|
|
461
|
-
// An error view is shown in the container
|
|
462
|
-
//
|
|
505
|
+
// An error view is shown in the container, and the store entry's `error`
|
|
506
|
+
// is set to 'Product data not found'. In debug/logging mode the SDK warns:
|
|
507
|
+
// "No product data found for the provided product IDs."
|
|
463
508
|
```
|
|
464
509
|
|
|
465
510
|
### No Availability
|
|
@@ -535,7 +580,7 @@ window.addEventListener('lce:actions.product_loaded', (event) => {
|
|
|
535
580
|
items: [{
|
|
536
581
|
item_id: event.detail.data.identifier,
|
|
537
582
|
item_name: event.detail.data.name,
|
|
538
|
-
price: event.detail.data.
|
|
583
|
+
price: event.detail.data.priceInfo.minimum / 100
|
|
539
584
|
}]
|
|
540
585
|
});
|
|
541
586
|
});
|
|
@@ -39,7 +39,7 @@ Use data attributes to configure the product list:
|
|
|
39
39
|
|
|
40
40
|
**Attributes:**
|
|
41
41
|
- `data-liquid-commerce-elements-products-list`: Product list container; value is the collection slug
|
|
42
|
-
- `data-rows`: Number of rows to display (default:
|
|
42
|
+
- `data-rows`: Number of rows to display (default: 4)
|
|
43
43
|
- `data-columns`: Number of columns (default: 4)
|
|
44
44
|
- `data-filters`: Comma-separated filter types
|
|
45
45
|
- `data-product-url`: URL pattern for product detail pages (optional)
|
|
@@ -138,8 +138,8 @@ Only filter keys that are configured for the list are honored — anything else
|
|
|
138
138
|
|
|
139
139
|
1. `data-filters` on `<div data-liquid-commerce-elements-products-list>` (use this when the page does **not** mount a filters UI but you still want URL filtering — e.g. a curated category page).
|
|
140
140
|
2. `data-filters` on the matching `<... -products-list-filters>` container (the common case when a filters panel is mounted).
|
|
141
|
-
3. `
|
|
142
|
-
4. `
|
|
141
|
+
3. `filters` array passed to `injectProductList(...)` programmatically.
|
|
142
|
+
4. `availableFilters` from the theme config for the list slug (fallback only).
|
|
143
143
|
|
|
144
144
|
### Supported formats
|
|
145
145
|
|
|
@@ -190,10 +190,9 @@ The search component provides full-text search across:
|
|
|
190
190
|
|
|
191
191
|
### Search Behavior
|
|
192
192
|
|
|
193
|
-
- Real-time search as user types (
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
- Shows result count
|
|
193
|
+
- Real-time search as user types (500ms debounce; fires on any non-empty input — no minimum character count)
|
|
194
|
+
- Input is limited to 100 characters; allowed characters: letters, numbers, spaces, and `- _ ' . , & ( )`
|
|
195
|
+
- Server-side filtering by the search term
|
|
197
196
|
- "Clear search" button appears when active
|
|
198
197
|
|
|
199
198
|
### Programmatic Search
|
|
@@ -262,7 +261,7 @@ Each product card shows:
|
|
|
262
261
|
- Brand
|
|
263
262
|
- Price (or price range for multiple sizes)
|
|
264
263
|
- Rating (if available)
|
|
265
|
-
-
|
|
264
|
+
- Clickable image/card linking to the product detail page (when `productUrl` is configured)
|
|
266
265
|
- "Add to Cart" button (optional)
|
|
267
266
|
- Availability indicator
|
|
268
267
|
|
|
@@ -619,7 +618,7 @@ await client.injectProductList({
|
|
|
619
618
|
|
|
620
619
|
### Search Not Finding Products
|
|
621
620
|
|
|
622
|
-
1. Verify
|
|
621
|
+
1. Verify the input uses allowed characters and is under the 100-character limit
|
|
623
622
|
2. Check search is not case-sensitive (it shouldn't be)
|
|
624
623
|
3. Ensure products have searchable text fields
|
|
625
624
|
4. Look for API errors in network tab
|
|
@@ -77,8 +77,7 @@ customTheme: {
|
|
|
77
77
|
personalizationCardStyle: 'outlined', // or 'filled'
|
|
78
78
|
allowPromoCodes: true,
|
|
79
79
|
inputFieldStyle: 'outlined', // or 'filled'
|
|
80
|
-
|
|
81
|
-
poweredByMode: 'light' // or 'dark'
|
|
80
|
+
poweredByMode: 'light' // or 'dark' (note: showPoweredBy is controlled server-side and cannot be overridden via customTheme)
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
}
|
|
@@ -152,16 +151,15 @@ customTheme: {
|
|
|
152
151
|
text: 'Receive SMS updates about your order and exclusive deals.'
|
|
153
152
|
},
|
|
154
153
|
allowGiftCards: true,
|
|
155
|
-
legalMessage: 'By placing your order, you agree to our Terms of Service and Privacy Policy.',
|
|
154
|
+
legalMessage: { show: true, text: 'By placing your order, you agree to our Terms of Service and Privacy Policy.' },
|
|
156
155
|
continueShoppingUrl: 'https://yoursite.com/shop',
|
|
157
156
|
exitUrl: 'https://yoursite.com',
|
|
158
157
|
thankYouButtonText: 'Continue Shopping',
|
|
159
158
|
drawerHeaderText: 'Checkout',
|
|
160
159
|
placeOrderButtonText: 'Place Order',
|
|
161
160
|
checkoutCompleted: {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
161
|
+
customLogo: 'https://yoursite.com/logo.png',
|
|
162
|
+
customText: 'Thank you for your purchase! Your order has been received.'
|
|
165
163
|
}
|
|
166
164
|
}
|
|
167
165
|
}
|
|
@@ -191,8 +189,7 @@ const client = await Elements('YOUR_API_KEY', {
|
|
|
191
189
|
},
|
|
192
190
|
layout: {
|
|
193
191
|
allowPromoCodes: true,
|
|
194
|
-
inputFieldStyle: 'outlined'
|
|
195
|
-
showPoweredBy: false
|
|
192
|
+
inputFieldStyle: 'outlined'
|
|
196
193
|
}
|
|
197
194
|
},
|
|
198
195
|
product: {
|
|
@@ -203,7 +200,7 @@ const client = await Elements('YOUR_API_KEY', {
|
|
|
203
200
|
},
|
|
204
201
|
cart: {
|
|
205
202
|
layout: {
|
|
206
|
-
|
|
203
|
+
goToCheckoutButtonText: 'Checkout Now'
|
|
207
204
|
}
|
|
208
205
|
}
|
|
209
206
|
}
|
|
@@ -12,7 +12,12 @@ import { Elements } from '@liquidcommerce/elements-sdk';
|
|
|
12
12
|
const client = await Elements('YOUR_API_KEY', {
|
|
13
13
|
env: 'production',
|
|
14
14
|
proxy: {
|
|
15
|
-
|
|
15
|
+
// Must be an ABSOLUTE URL ending with a trailing slash — the SDK resolves
|
|
16
|
+
// each request as `new URL('api' + path, baseUrl)`, so a missing trailing
|
|
17
|
+
// slash drops the last path segment.
|
|
18
|
+
baseUrl: 'https://yoursite.com/api/elements-proxy/',
|
|
19
|
+
// Optional: extra headers merged into every proxied request (e.g. credentials)
|
|
20
|
+
headers: { 'X-My-Auth': '...' }
|
|
16
21
|
}
|
|
17
22
|
});
|
|
18
23
|
```
|
|
@@ -30,7 +35,8 @@ export default function ProductPage() {
|
|
|
30
35
|
(async () => {
|
|
31
36
|
const client = await Elements('YOUR_API_KEY', {
|
|
32
37
|
env: 'production',
|
|
33
|
-
|
|
38
|
+
// Absolute URL with a trailing slash (see note above)
|
|
39
|
+
proxy: { baseUrl: 'https://yoursite.com/api/elements-proxy/' }
|
|
34
40
|
});
|
|
35
41
|
|
|
36
42
|
await client.injectProductElement([
|
|
@@ -47,6 +53,8 @@ export default function ProductPage() {
|
|
|
47
53
|
|
|
48
54
|
Your endpoint should forward requests to the LiquidCommerce Elements API, preserving method, headers, and body.
|
|
49
55
|
|
|
56
|
+
The SDK does not hardcode the upstream host in the proxy path. Instead, it sends the correct environment-specific upstream base URL in the `X-Liquid-Proxy-Target` request header (along with `X-Liquid-Proxy: true`). Forward to that header value rather than to a literal host — this keeps the proxy environment-agnostic. The SDK calls your proxy at `<baseUrl>/api/<endpoint>`, so the path captured after your proxy mount is exactly the `api/<endpoint>` to append to the target.
|
|
57
|
+
|
|
50
58
|
### Minimal Express Example
|
|
51
59
|
|
|
52
60
|
```javascript
|
|
@@ -56,15 +64,24 @@ import fetch from 'node-fetch';
|
|
|
56
64
|
const app = express();
|
|
57
65
|
app.use(express.json());
|
|
58
66
|
|
|
67
|
+
// Proxy is mounted at the path of your proxy.baseUrl ('/api/elements-proxy/').
|
|
59
68
|
app.all('/api/elements-proxy/*', async (req, res) => {
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
// Upstream base URL the SDK wants this request forwarded to
|
|
70
|
+
// (e.g. https://elements-services-production-948630220003.us-central1.run.app)
|
|
71
|
+
const upstream = req.headers['x-liquid-proxy-target'];
|
|
72
|
+
if (!upstream) {
|
|
73
|
+
return res.status(400).send('Missing X-Liquid-Proxy-Target header');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Everything after the proxy mount, e.g. 'api/auth/authenticate'
|
|
77
|
+
const forwardedPath = req.params[0];
|
|
78
|
+
const targetUrl = `${upstream}/${forwardedPath}`;
|
|
62
79
|
|
|
63
80
|
const response = await fetch(targetUrl, {
|
|
64
81
|
method: req.method,
|
|
65
82
|
headers: {
|
|
66
83
|
...req.headers,
|
|
67
|
-
host:
|
|
84
|
+
host: new URL(upstream).host
|
|
68
85
|
},
|
|
69
86
|
body: ['GET', 'HEAD'].includes(req.method) ? undefined : JSON.stringify(req.body)
|
|
70
87
|
});
|
|
@@ -15,7 +15,8 @@ The Elements SDK requires modern browser features (Web Components + Shadow DOM).
|
|
|
15
15
|
- Shadow DOM
|
|
16
16
|
- ES2018 JavaScript
|
|
17
17
|
- Fetch API
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
**LocalStorage (optional, recommended):** used as a fast path for session persistence (cart/address). When unavailable (incognito, Safari/Firefox private mode, in-app webviews, cross-origin iframes), the SDK falls back to a generated device fingerprint plus server-side session persistence, so functionality is preserved.
|
|
19
20
|
|
|
20
21
|
## Server-Side Rendering (SSR)
|
|
21
22
|
|
|
@@ -19,9 +19,9 @@ class SDKError extends Error {
|
|
|
19
19
|
|
|
20
20
|
```javascript
|
|
21
21
|
try {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
]);
|
|
22
|
+
// Structural input errors throw a catchable SDKError — e.g. a non-array
|
|
23
|
+
// argument or an empty array:
|
|
24
|
+
await window.LiquidCommerce.elements.injectProductElement([]);
|
|
25
25
|
} catch (error) {
|
|
26
26
|
if (error.name === 'SDKError') {
|
|
27
27
|
console.error('SDK Error:', error);
|
|
@@ -29,13 +29,18 @@ try {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
> A well-formed entry with an invalid product identifier does **not** throw — it renders an error view inside the component and sets an error in the store. `try/catch` here only catches structural input errors (a non-array argument or an empty array).
|
|
33
|
+
|
|
32
34
|
## Error Isolation
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
Component failures are contained — a component that fails to load renders an error view and logs to the console without crashing your page. Action methods (like `cart.addProduct`) emit a `*_FAILED` event on failure, and re-throw only on an unexpected error; several soft-failure paths (e.g. no product found, no items added) emit the failure event but still resolve. Wrap calls in try/catch **and** listen for the corresponding `*_failed` event to reliably detect failures:
|
|
35
37
|
|
|
36
38
|
```javascript
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
try {
|
|
40
|
+
await window.LiquidCommerce.elements.actions.cart.addProduct([/* invalid */]);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.log('Handled add-to-cart failure');
|
|
43
|
+
}
|
|
39
44
|
```
|
|
40
45
|
|
|
41
46
|
## Error Events
|
|
@@ -55,7 +60,7 @@ window.addEventListener('lce:actions.address_failed', (event) => {
|
|
|
55
60
|
|
|
56
61
|
// Checkout submit failed
|
|
57
62
|
window.addEventListener('lce:actions.checkout_submit_failed', (event) => {
|
|
58
|
-
console.error('Checkout failed:', event.detail.data.
|
|
63
|
+
console.error('Checkout failed:', event.detail.data.message);
|
|
59
64
|
});
|
|
60
65
|
```
|
|
61
66
|
|
|
@@ -44,9 +44,7 @@ setTimeout(async () => {
|
|
|
44
44
|
## 5) Let the SDK Handle Media Optimization
|
|
45
45
|
|
|
46
46
|
The SDK automatically:
|
|
47
|
-
- Lazy loads images
|
|
48
|
-
- Uses responsive image sizes
|
|
49
|
-
- Virtualizes carousel images
|
|
47
|
+
- Lazy loads carousel/thumbnail images (native `loading="lazy"`)
|
|
50
48
|
|
|
51
49
|
## Related Docs
|
|
52
50
|
|
|
@@ -60,7 +60,7 @@ Common setup issues and how to resolve them.
|
|
|
60
60
|
|
|
61
61
|
**Symptoms:** Console warning "This SDK is designed for the browser. Calls made during SSR return null."
|
|
62
62
|
|
|
63
|
-
This is expected behavior. The SDK ships
|
|
63
|
+
This is expected behavior. The SDK ships SSR stubs that are automatically resolved when bundled for Node.js (via the `node` export condition in `package.json`). There are two: the main build's stub (the `.` export) provides `Elements` and `ElementsBuilder` and warns with the `[LiquidCommerce Elements]` prefix; the checkout build's stub (the `./checkout` export) provides `ElementsCheckout` and warns with the `[LiquidCommerce Checkout]` prefix. Both re-export their types and enums for TypeScript compatibility and return `null` from the factory functions.
|
|
64
64
|
|
|
65
65
|
**No action required** — initialize the SDK in a client-only lifecycle hook (`useEffect`, `onMounted`, etc.) and the real client will activate in the browser.
|
|
66
66
|
|
|
@@ -72,8 +72,8 @@ This is expected behavior. The SDK ships an SSR stub that is automatically resol
|
|
|
72
72
|
|
|
73
73
|
**Fixes:**
|
|
74
74
|
- Use `customTheme` in the client configuration to style components -- external CSS cannot penetrate Shadow DOM.
|
|
75
|
-
- For debugging, enable `development.openShadowDom: true` to disable
|
|
76
|
-
- Use the debug panel (`debugMode: 'panel'`) to inspect component state.
|
|
75
|
+
- For debugging, enable `development.openShadowDom: true` to make the shadow root open (`mode: 'open'`) so you can inspect component internals in DevTools (via `element.shadowRoot`). This does **not** disable style isolation — external CSS still cannot reach the components; use `customTheme` to style them. (Forced off in production.)
|
|
76
|
+
- Use the debug panel (`debugMode: 'panel'`) to inspect SDK logs, events, and GTM activity in real time — it surfaces a live stream of logger output and pubsub/GTM events (not component internal state), and auto-enables only in non-production environments.
|
|
77
77
|
|
|
78
78
|
## Component Not Updating After Data Changes
|
|
79
79
|
|