@liquidcommerce/elements-sdk 2.7.0 → 2.7.2
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 +83 -2750
- package/dist/index.checkout.esm.js +6898 -6837
- package/dist/index.esm.js +11463 -10993
- package/dist/types/auto-initialize/shared-utils.d.ts +5 -0
- package/dist/types/constants/core.constant.d.ts +0 -4
- package/dist/types/core/base-component.service.d.ts +2 -1
- package/dist/types/core/pubsub/interfaces/checkout.interface.d.ts +1 -0
- package/dist/types/enums/core.enum.d.ts +11 -0
- package/dist/types/interfaces/configs/product-list.interface.d.ts +2 -2
- package/dist/types/interfaces/core.interface.d.ts +5 -4
- package/dist/types/modules/address/address-input.component.d.ts +11 -0
- package/dist/types/modules/checkout/components/checkout-stripe-form.component.d.ts +2 -1
- package/dist/types/modules/product-list/components/card-components/index.d.ts +3 -0
- package/dist/types/modules/product-list/components/card-components/product-badge.d.ts +8 -0
- package/dist/types/modules/product-list/components/card-components/product-fulfillments.d.ts +2 -0
- package/dist/types/modules/product-list/components/card-components/product-price-and-personalization.d.ts +13 -0
- package/dist/types/modules/product-list/components/card-components/product-title.d.ts +6 -0
- package/dist/types/modules/product-list/components/index.d.ts +1 -0
- package/dist/types/modules/product-list/components/product-list-engraving.component.d.ts +5 -1
- package/dist/types/modules/product-list/components/product-list-product-pre-cart.component.d.ts +28 -0
- package/dist/types/modules/product-list/components/product-list-retailers.component.d.ts +10 -2
- package/dist/types/modules/product-list/product-list-card.component.d.ts +0 -5
- package/dist/types/modules/product-list/product-list.commands.d.ts +11 -2
- package/dist/types/modules/product-list/product-list.interface.d.ts +1 -0
- package/dist/types/modules/ui-components/engraving/engraving-form.component.d.ts +11 -2
- package/dist/types/modules/ui-components/lce-element/lce-element.component.d.ts +2 -1
- package/dist/types/utils/dom-compat.d.ts +2 -0
- package/docs/gitbook/actions.md +964 -0
- package/docs/gitbook/address.md +48 -0
- package/docs/gitbook/cart.md +65 -0
- package/docs/gitbook/checkout.md +131 -0
- package/docs/gitbook/events.md +1765 -0
- package/docs/gitbook/overview.md +166 -0
- package/docs/gitbook/product.md +64 -0
- package/docs/gitbook/quick-start-guide.md +393 -0
- package/docs/v1/README.md +210 -0
- package/docs/v1/api/actions/address-actions.md +281 -0
- package/docs/v1/api/actions/cart-actions.md +337 -0
- package/docs/v1/api/actions/checkout-actions.md +387 -0
- package/docs/v1/api/actions/product-actions.md +115 -0
- package/docs/v1/api/client.md +482 -0
- package/docs/v1/api/configuration.md +1 -0
- package/docs/v1/api/injection-methods.md +247 -0
- package/docs/v1/api/typescript-types.md +1 -0
- package/docs/v1/api/ui-helpers.md +200 -0
- package/docs/v1/examples/advanced-patterns.md +96 -0
- package/docs/v1/examples/checkout-flow.md +91 -0
- package/docs/v1/examples/custom-theming.md +63 -0
- package/docs/v1/examples/multi-product-page.md +90 -0
- package/docs/v1/examples/simple-product-page.md +89 -0
- package/docs/v1/getting-started/concepts.md +507 -0
- package/docs/v1/getting-started/installation.md +328 -0
- package/docs/v1/getting-started/quick-start.md +405 -0
- package/docs/v1/guides/address-component.md +431 -0
- package/docs/v1/guides/best-practices.md +324 -0
- package/docs/v1/guides/cart-component.md +737 -0
- package/docs/v1/guides/checkout-component.md +672 -0
- package/docs/v1/guides/events.md +926 -0
- package/docs/v1/guides/product-component.md +686 -0
- package/docs/v1/guides/product-list-component.md +598 -0
- package/docs/v1/guides/theming.md +216 -0
- package/docs/v1/integration/angular.md +39 -0
- package/docs/v1/integration/laravel.md +41 -0
- package/docs/v1/integration/nextjs.md +60 -0
- package/docs/v1/integration/proxy-setup.md +89 -0
- package/docs/v1/integration/react.md +64 -0
- package/docs/v1/integration/vanilla-js.md +84 -0
- package/docs/v1/integration/vue.md +34 -0
- package/docs/v1/reference/browser-support.md +44 -0
- package/docs/v1/reference/error-handling.md +70 -0
- package/docs/v1/reference/performance.md +54 -0
- package/docs/v1/reference/troubleshooting.md +64 -0
- package/package.json +1 -1
- package/docs/ACTIONS.md +0 -1301
- package/docs/BROWSER_SUPPORT.md +0 -279
- package/docs/CONFIGURATION.md +0 -997
- package/docs/DOCUMENTATION_INDEX.md +0 -319
- package/docs/EVENTS.md +0 -798
- package/docs/PROXY.md +0 -228
- package/docs/THEMING.md +0 -681
- package/docs/TROUBLESHOOTING.md +0 -793
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
# Product Component
|
|
2
|
+
|
|
3
|
+
The Product component displays product information with add-to-cart functionality, image carousel, size selection, and fulfillment options.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Product component automatically:
|
|
8
|
+
- Displays product images in a carousel
|
|
9
|
+
- Shows product name, description, and pricing
|
|
10
|
+
- Provides size selection
|
|
11
|
+
- Offers fulfillment type options (shipping/on-demand delivery)
|
|
12
|
+
- Handles retailer selection
|
|
13
|
+
- Includes add-to-cart functionality
|
|
14
|
+
- Supports product personalization/engraving
|
|
15
|
+
- Adjusts pricing based on delivery location
|
|
16
|
+
|
|
17
|
+
## Basic Usage
|
|
18
|
+
|
|
19
|
+
### Declarative Setup
|
|
20
|
+
|
|
21
|
+
The simplest way to add a product is using HTML data attributes:
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<script
|
|
25
|
+
defer
|
|
26
|
+
data-liquid-commerce-elements
|
|
27
|
+
data-token="YOUR_API_KEY"
|
|
28
|
+
data-env="production"
|
|
29
|
+
data-container-1="product-display"
|
|
30
|
+
data-product-1="00619947000020"
|
|
31
|
+
type="text/javascript"
|
|
32
|
+
src="https://assets-elements.liquidcommerce.us/all/elements.js"
|
|
33
|
+
></script>
|
|
34
|
+
|
|
35
|
+
<div id="product-display"></div>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Multiple products:**
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<script
|
|
42
|
+
defer
|
|
43
|
+
data-liquid-commerce-elements
|
|
44
|
+
data-token="YOUR_API_KEY"
|
|
45
|
+
data-env="production"
|
|
46
|
+
data-container-1="product-1"
|
|
47
|
+
data-product-1="00619947000020"
|
|
48
|
+
data-container-2="product-2"
|
|
49
|
+
data-product-2="08504405135"
|
|
50
|
+
type="text/javascript"
|
|
51
|
+
src="https://assets-elements.liquidcommerce.us/all/elements.js"
|
|
52
|
+
></script>
|
|
53
|
+
|
|
54
|
+
<div id="product-1"></div>
|
|
55
|
+
<div id="product-2"></div>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Alternative: Annotated Elements
|
|
59
|
+
|
|
60
|
+
Use `data-lce-product` on any div:
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<script
|
|
64
|
+
defer
|
|
65
|
+
data-liquid-commerce-elements
|
|
66
|
+
data-token="YOUR_API_KEY"
|
|
67
|
+
data-env="production"
|
|
68
|
+
type="text/javascript"
|
|
69
|
+
src="https://assets-elements.liquidcommerce.us/all/elements.js"
|
|
70
|
+
></script>
|
|
71
|
+
|
|
72
|
+
<div data-lce-product="00619947000020"></div>
|
|
73
|
+
<div data-lce-product="08504405135"></div>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The SDK automatically generates IDs and injects products into these elements.
|
|
77
|
+
|
|
78
|
+
### Alternative: JSON Configuration
|
|
79
|
+
|
|
80
|
+
For many products, use a JSON script tag:
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<script
|
|
84
|
+
defer
|
|
85
|
+
data-liquid-commerce-elements
|
|
86
|
+
data-token="YOUR_API_KEY"
|
|
87
|
+
data-env="production"
|
|
88
|
+
type="text/javascript"
|
|
89
|
+
src="https://assets-elements.liquidcommerce.us/all/elements.js"
|
|
90
|
+
></script>
|
|
91
|
+
|
|
92
|
+
<script data-liquid-commerce-elements-products type="application/json">
|
|
93
|
+
[
|
|
94
|
+
{ "containerId": "product-1", "identifier": "00619947000020" },
|
|
95
|
+
{ "containerId": "product-2", "identifier": "08504405135" },
|
|
96
|
+
{ "containerId": "product-3", "identifier": "08068660001" }
|
|
97
|
+
]
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<div id="product-1"></div>
|
|
101
|
+
<div id="product-2"></div>
|
|
102
|
+
<div id="product-3"></div>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Programmatic Setup
|
|
106
|
+
|
|
107
|
+
Use the JavaScript API for dynamic product injection:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
const client = await Elements('YOUR_API_KEY', {
|
|
111
|
+
env: 'production'
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await client.injectProductElement([
|
|
115
|
+
{ containerId: 'product-display', identifier: '00619947000020' }
|
|
116
|
+
]);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**With NPM:**
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
import { Elements } from '@liquidcommerce/elements-sdk';
|
|
123
|
+
|
|
124
|
+
const client = await Elements('YOUR_API_KEY', { env: 'production' });
|
|
125
|
+
|
|
126
|
+
await client.injectProductElement([
|
|
127
|
+
{ containerId: 'product-1', identifier: '00619947000020' },
|
|
128
|
+
{ containerId: 'product-2', identifier: '08504405135' }
|
|
129
|
+
]);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Product Identifiers
|
|
133
|
+
|
|
134
|
+
Products can be identified using:
|
|
135
|
+
|
|
136
|
+
- **UPC**: `00619947000020`
|
|
137
|
+
- **Salsify Grouping**: `GROUPING-12345`
|
|
138
|
+
|
|
139
|
+
The SDK automatically resolves any of these identifier types.
|
|
140
|
+
|
|
141
|
+
## Features
|
|
142
|
+
|
|
143
|
+
### Image Carousel
|
|
144
|
+
|
|
145
|
+
Products with multiple images display in an interactive carousel:
|
|
146
|
+
|
|
147
|
+
- Swipe/arrow navigation
|
|
148
|
+
- Thumbnail preview
|
|
149
|
+
- Lazy loading for performance
|
|
150
|
+
|
|
151
|
+
### Size Selection
|
|
152
|
+
|
|
153
|
+
For products with multiple sizes:
|
|
154
|
+
|
|
155
|
+
- Size selector
|
|
156
|
+
- Price updates per size
|
|
157
|
+
- Availability checking per size
|
|
158
|
+
- Out-of-stock indication
|
|
159
|
+
|
|
160
|
+
### Fulfillment Types
|
|
161
|
+
|
|
162
|
+
Two fulfillment options:
|
|
163
|
+
|
|
164
|
+
**Shipping**
|
|
165
|
+
- Standard delivery
|
|
166
|
+
- Nationwide availability
|
|
167
|
+
- Carrier-based shipping
|
|
168
|
+
|
|
169
|
+
**On-Demand Delivery**
|
|
170
|
+
- Same-day or scheduled delivery
|
|
171
|
+
- Local availability only
|
|
172
|
+
- Location-dependent pricing
|
|
173
|
+
|
|
174
|
+
The component shows only available fulfillment types based on the user's location.
|
|
175
|
+
|
|
176
|
+
### Retailer Selection
|
|
177
|
+
|
|
178
|
+
For products with multiple retailers:
|
|
179
|
+
|
|
180
|
+
**Carousel View** (default)
|
|
181
|
+
- Swipeable carousel of retailer cards
|
|
182
|
+
- Shows retailer address, shipping/delivery expectation time, and pricing
|
|
183
|
+
- Select with one tap
|
|
184
|
+
|
|
185
|
+
**Popup View**
|
|
186
|
+
- "Choose Retailer" button
|
|
187
|
+
- Modal with full retailer list
|
|
188
|
+
- Filter and search capabilities
|
|
189
|
+
|
|
190
|
+
### Personalization/Engraving
|
|
191
|
+
|
|
192
|
+
For products that support personalization:
|
|
193
|
+
|
|
194
|
+
- Engraving form appears automatically
|
|
195
|
+
- Character limits enforced
|
|
196
|
+
- Additional fees displayed
|
|
197
|
+
|
|
198
|
+
### Quantity Selection
|
|
199
|
+
|
|
200
|
+
Adjust product quantity before adding to cart:
|
|
201
|
+
|
|
202
|
+
- Increment/decrement buttons
|
|
203
|
+
- Direct input field
|
|
204
|
+
- Inventory limits enforced
|
|
205
|
+
|
|
206
|
+
## Actions API
|
|
207
|
+
|
|
208
|
+
Programmatically interact with products:
|
|
209
|
+
|
|
210
|
+
### Get Product Details
|
|
211
|
+
|
|
212
|
+
Retrieve product information:
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
const productData = window.elements.actions.product.getDetails('00619947000020');
|
|
216
|
+
|
|
217
|
+
console.log(productData);
|
|
218
|
+
// {
|
|
219
|
+
// identifier: '00619947000020',
|
|
220
|
+
// name: 'Premium Whiskey',
|
|
221
|
+
// price: 4999,
|
|
222
|
+
// selectedSize: { id: '750ml', upc: '00619947000020', ... },
|
|
223
|
+
// selectedFulfillmentType: 'shipping',
|
|
224
|
+
// selectedRetailer: { id: 'retailer_123', name: 'Spirits Shop', ... },
|
|
225
|
+
// quantity: 1,
|
|
226
|
+
// ...
|
|
227
|
+
// }
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Note:** The product must be injected and loaded before calling `getDetails()`. If the product hasn't been loaded, an error is thrown.
|
|
231
|
+
|
|
232
|
+
## Events
|
|
233
|
+
|
|
234
|
+
Listen for product-related events:
|
|
235
|
+
|
|
236
|
+
### Product Loaded
|
|
237
|
+
|
|
238
|
+
Fired when product data is successfully loaded:
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
window.addEventListener('lce:actions.product_loaded', (event) => {
|
|
242
|
+
const { identifier, name, price } = event.detail.data;
|
|
243
|
+
console.log(`Product loaded: ${name} - $${price / 100}`);
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Product Add to Cart
|
|
248
|
+
|
|
249
|
+
Fired when user clicks "Add to Cart":
|
|
250
|
+
|
|
251
|
+
```javascript
|
|
252
|
+
window.addEventListener('lce:actions.product_add_to_cart', (event) => {
|
|
253
|
+
const { identifier, quantity, fulfillmentType } = event.detail.data;
|
|
254
|
+
console.log(`Adding ${quantity}x ${identifier} (${fulfillmentType})`);
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Size Changed
|
|
259
|
+
|
|
260
|
+
Fired when user selects a different size:
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
window.addEventListener('lce:actions.product_size_changed', (event) => {
|
|
264
|
+
const { identifier, selectedSize, price } = event.detail.data;
|
|
265
|
+
console.log(`Size changed to ${selectedSize.name}: $${price / 100}`);
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Fulfillment Type Changed
|
|
270
|
+
|
|
271
|
+
Fired when user switches between shipping and on-demand:
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
window.addEventListener('lce:actions.product_fulfillment_type_changed', (event) => {
|
|
275
|
+
const { identifier, fulfillmentType } = event.detail.data;
|
|
276
|
+
console.log(`Fulfillment type changed to: ${fulfillmentType}`);
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Fulfillment (Retailer) Changed
|
|
281
|
+
|
|
282
|
+
Fired when user selects a different retailer:
|
|
283
|
+
|
|
284
|
+
```javascript
|
|
285
|
+
window.addEventListener('lce:actions.product_fulfillment_changed', (event) => {
|
|
286
|
+
const { identifier, selectedRetailer, price } = event.detail.data;
|
|
287
|
+
console.log(`Retailer changed to ${selectedRetailer.name}: $${price / 100}`);
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Quantity Increased/Decreased
|
|
292
|
+
|
|
293
|
+
Fired when user adjusts quantity:
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
window.addEventListener('lce:actions.product_quantity_increase', (event) => {
|
|
297
|
+
const { identifier, quantity } = event.detail.data;
|
|
298
|
+
console.log(`Quantity increased to: ${quantity}`);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
window.addEventListener('lce:actions.product_quantity_decrease', (event) => {
|
|
302
|
+
const { identifier, quantity } = event.detail.data;
|
|
303
|
+
console.log(`Quantity decreased to: ${quantity}`);
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Customization
|
|
308
|
+
|
|
309
|
+
### Theme Configuration
|
|
310
|
+
|
|
311
|
+
Customize product appearance globally:
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
const client = await Elements('YOUR_API_KEY', {
|
|
315
|
+
env: 'production',
|
|
316
|
+
customTheme: {
|
|
317
|
+
product: {
|
|
318
|
+
theme: {
|
|
319
|
+
backgroundColor: '#ffffff'
|
|
320
|
+
},
|
|
321
|
+
layout: {
|
|
322
|
+
showImages: true,
|
|
323
|
+
showOnlyMainImage: false, // Show all images or just the main one
|
|
324
|
+
showTitle: true,
|
|
325
|
+
showDescription: true,
|
|
326
|
+
showQuantityCounter: true,
|
|
327
|
+
showOffHours: true, // Show when retailer is closed
|
|
328
|
+
quantityCounterStyle: 'outlined', // or 'ghost'
|
|
329
|
+
fulfillmentDisplay: 'carousel', // or 'popup'
|
|
330
|
+
enableShippingFulfillment: true,
|
|
331
|
+
enableOnDemandFulfillment: true,
|
|
332
|
+
addToCartButtonText: 'Add to Cart',
|
|
333
|
+
addToCartButtonShowTotalPrice: true,
|
|
334
|
+
buyNowButtonText: 'Buy Now',
|
|
335
|
+
preSaleButtonText: 'Pre-Order',
|
|
336
|
+
noAvailabilityText: 'Not available in your area'
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Global Theme
|
|
344
|
+
|
|
345
|
+
Set colors, fonts, and styles that apply to all components:
|
|
346
|
+
|
|
347
|
+
```javascript
|
|
348
|
+
customTheme: {
|
|
349
|
+
global: {
|
|
350
|
+
theme: {
|
|
351
|
+
primaryColor: '#007bff',
|
|
352
|
+
accentColor: '#28a745',
|
|
353
|
+
buttonCornerRadius: '8px',
|
|
354
|
+
cardCornerRadius: '12px',
|
|
355
|
+
headingFont: {
|
|
356
|
+
name: 'Poppins',
|
|
357
|
+
weights: [400, 600, 700]
|
|
358
|
+
},
|
|
359
|
+
paragraphFont: {
|
|
360
|
+
name: 'Inter',
|
|
361
|
+
weights: [400, 500]
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
See [Theming Guide](./theming.md) for complete theming options.
|
|
369
|
+
|
|
370
|
+
## Address Requirement
|
|
371
|
+
|
|
372
|
+
Products require a delivery address for:
|
|
373
|
+
- Availability checking
|
|
374
|
+
- Accurate pricing
|
|
375
|
+
- Delivery options
|
|
376
|
+
|
|
377
|
+
### Automatic Address Collection
|
|
378
|
+
|
|
379
|
+
If no address is set, the SDK automatically:
|
|
380
|
+
1. Prompts for address when user clicks "Add to Cart"
|
|
381
|
+
2. Shows address input drawer
|
|
382
|
+
3. Validates and saves address
|
|
383
|
+
4. Completes the add-to-cart action
|
|
384
|
+
|
|
385
|
+
### Pre-set Address
|
|
386
|
+
|
|
387
|
+
Set address programmatically to skip prompting:
|
|
388
|
+
|
|
389
|
+
```javascript
|
|
390
|
+
// Using Google Places ID
|
|
391
|
+
await window.elements.actions.address.setAddressByPlacesId('ChIJ...');
|
|
392
|
+
|
|
393
|
+
// Or manually
|
|
394
|
+
await window.elements.actions.address.setAddressManually(
|
|
395
|
+
{
|
|
396
|
+
one: '123 Main St',
|
|
397
|
+
two: 'Apt 4',
|
|
398
|
+
city: 'New York',
|
|
399
|
+
state: 'NY',
|
|
400
|
+
zip: '10001'
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
latitude: 40.7128,
|
|
404
|
+
longitude: -74.0060
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Presale Products
|
|
410
|
+
|
|
411
|
+
Products in presale mode:
|
|
412
|
+
- Display "Pre-Order" button (customizable text)
|
|
413
|
+
- Cannot be added to cart, it will send the user to checkout directly
|
|
414
|
+
- Show presale countdown if configured
|
|
415
|
+
- Display expected availability date
|
|
416
|
+
|
|
417
|
+
Presale products are handled automatically; no special configuration needed.
|
|
418
|
+
|
|
419
|
+
## Component Management
|
|
420
|
+
|
|
421
|
+
### Rerender Product
|
|
422
|
+
|
|
423
|
+
Force a product to reload and rerender:
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
const components = window.elements.getInjectedComponents();
|
|
427
|
+
const productComponent = components.get('product-1');
|
|
428
|
+
|
|
429
|
+
if (productComponent) {
|
|
430
|
+
productComponent.rerender();
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Get Component Type
|
|
435
|
+
|
|
436
|
+
Check if a component is a product:
|
|
437
|
+
|
|
438
|
+
```javascript
|
|
439
|
+
const component = components.get('product-1');
|
|
440
|
+
const type = component.getType();
|
|
441
|
+
console.log(type); // 'product'
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Get Container Element
|
|
445
|
+
|
|
446
|
+
Access the container DOM element:
|
|
447
|
+
|
|
448
|
+
```javascript
|
|
449
|
+
const component = components.get('product-1');
|
|
450
|
+
const container = component.getElement();
|
|
451
|
+
console.log(container); // <div id="product-1">...</div>
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Error Handling
|
|
455
|
+
|
|
456
|
+
### Product Not Found
|
|
457
|
+
|
|
458
|
+
If a product identifier doesn't exist:
|
|
459
|
+
|
|
460
|
+
```javascript
|
|
461
|
+
// An error view is shown in the container
|
|
462
|
+
// Console logs: [LiquidCommerce Elements] Product not found: invalid_id
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### No Availability
|
|
466
|
+
|
|
467
|
+
If a product isn't available in the user's location:
|
|
468
|
+
|
|
469
|
+
```javascript
|
|
470
|
+
// Shows: "Not available in your area" (customizable)
|
|
471
|
+
// User cannot add to cart
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Loading Errors
|
|
475
|
+
|
|
476
|
+
If product data fails to load:
|
|
477
|
+
|
|
478
|
+
```javascript
|
|
479
|
+
// Shows error view with retry option
|
|
480
|
+
// Console logs detailed error information
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## Use Cases
|
|
484
|
+
|
|
485
|
+
### Basic Product Page
|
|
486
|
+
|
|
487
|
+
```html
|
|
488
|
+
<!DOCTYPE html>
|
|
489
|
+
<html>
|
|
490
|
+
<head>
|
|
491
|
+
<script
|
|
492
|
+
defer
|
|
493
|
+
data-liquid-commerce-elements
|
|
494
|
+
data-token="YOUR_API_KEY"
|
|
495
|
+
data-env="production"
|
|
496
|
+
data-container-1="product"
|
|
497
|
+
data-product-1="00619947000020"
|
|
498
|
+
type="text/javascript"
|
|
499
|
+
src="https://assets-elements.liquidcommerce.us/all/elements.js"
|
|
500
|
+
></script>
|
|
501
|
+
</head>
|
|
502
|
+
<body>
|
|
503
|
+
<div id="product"></div>
|
|
504
|
+
</body>
|
|
505
|
+
</html>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Dynamic Product Selection
|
|
509
|
+
|
|
510
|
+
```javascript
|
|
511
|
+
import { Elements } from '@liquidcommerce/elements-sdk';
|
|
512
|
+
|
|
513
|
+
const client = await Elements('YOUR_API_KEY', { env: 'production' });
|
|
514
|
+
|
|
515
|
+
// User selects product from dropdown
|
|
516
|
+
document.getElementById('product-selector').addEventListener('change', async (e) => {
|
|
517
|
+
const selectedId = e.target.value;
|
|
518
|
+
|
|
519
|
+
// Clear existing product
|
|
520
|
+
document.getElementById('product').innerHTML = '';
|
|
521
|
+
|
|
522
|
+
// Inject new product
|
|
523
|
+
await client.injectProductElement([
|
|
524
|
+
{ containerId: 'product', identifier: selectedId }
|
|
525
|
+
]);
|
|
526
|
+
});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Analytics Integration
|
|
530
|
+
|
|
531
|
+
```javascript
|
|
532
|
+
// Track product views
|
|
533
|
+
window.addEventListener('lce:actions.product_loaded', (event) => {
|
|
534
|
+
gtag('event', 'view_item', {
|
|
535
|
+
items: [{
|
|
536
|
+
item_id: event.detail.data.identifier,
|
|
537
|
+
item_name: event.detail.data.name,
|
|
538
|
+
price: event.detail.data.price / 100
|
|
539
|
+
}]
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// Track add to cart
|
|
544
|
+
window.addEventListener('lce:actions.product_add_to_cart', (event) => {
|
|
545
|
+
gtag('event', 'add_to_cart', {
|
|
546
|
+
items: [{
|
|
547
|
+
item_id: event.detail.data.identifier,
|
|
548
|
+
quantity: event.detail.data.quantity
|
|
549
|
+
}]
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Multi-Product Gallery
|
|
555
|
+
|
|
556
|
+
```javascript
|
|
557
|
+
const products = [
|
|
558
|
+
'00619947000020',
|
|
559
|
+
'08504405135',
|
|
560
|
+
'08068660001',
|
|
561
|
+
'07549900125'
|
|
562
|
+
];
|
|
563
|
+
|
|
564
|
+
const client = await Elements('YOUR_API_KEY', { env: 'production' });
|
|
565
|
+
|
|
566
|
+
// Create containers
|
|
567
|
+
const gallery = document.getElementById('product-gallery');
|
|
568
|
+
const productParams = products.map((id, index) => {
|
|
569
|
+
const container = document.createElement('div');
|
|
570
|
+
container.className = 'product-card';
|
|
571
|
+
container.id = `product-${index}`;
|
|
572
|
+
gallery.appendChild(container);
|
|
573
|
+
|
|
574
|
+
return { containerId: `product-${index}`, identifier: id };
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// Inject all products
|
|
578
|
+
await client.injectProductElement(productParams);
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
## Best Practices
|
|
582
|
+
|
|
583
|
+
### Container Sizing
|
|
584
|
+
|
|
585
|
+
Provide adequate space for the product component:
|
|
586
|
+
|
|
587
|
+
```css
|
|
588
|
+
#product-display {
|
|
589
|
+
min-height: 600px; /* Prevents layout shift */
|
|
590
|
+
max-width: 1200px;
|
|
591
|
+
margin: 0 auto;
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Loading States
|
|
596
|
+
|
|
597
|
+
Show a loading indicator while the product loads:
|
|
598
|
+
|
|
599
|
+
```javascript
|
|
600
|
+
// Show loader
|
|
601
|
+
document.getElementById('product').innerHTML = '<div class="loader">Loading...</div>';
|
|
602
|
+
|
|
603
|
+
await client.injectProductElement([
|
|
604
|
+
{ containerId: 'product', identifier: '00619947000020' }
|
|
605
|
+
]);
|
|
606
|
+
|
|
607
|
+
// Loader is automatically replaced when product loads
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Error Handling
|
|
611
|
+
|
|
612
|
+
Listen for errors and provide fallback:
|
|
613
|
+
|
|
614
|
+
```javascript
|
|
615
|
+
try {
|
|
616
|
+
await client.injectProductElement([
|
|
617
|
+
{ containerId: 'product', identifier: productId }
|
|
618
|
+
]);
|
|
619
|
+
} catch (error) {
|
|
620
|
+
console.error('Failed to load product:', error);
|
|
621
|
+
document.getElementById('product').innerHTML =
|
|
622
|
+
'<p>Unable to load product. Please try again later.</p>';
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Performance
|
|
627
|
+
|
|
628
|
+
For product listings, lazy load products as they come into view:
|
|
629
|
+
|
|
630
|
+
```javascript
|
|
631
|
+
const observer = new IntersectionObserver(async (entries) => {
|
|
632
|
+
for (const entry of entries) {
|
|
633
|
+
if (entry.isIntersecting) {
|
|
634
|
+
const container = entry.target;
|
|
635
|
+
const productId = container.dataset.productId;
|
|
636
|
+
|
|
637
|
+
await client.injectProductElement([
|
|
638
|
+
{ containerId: container.id, identifier: productId }
|
|
639
|
+
]);
|
|
640
|
+
|
|
641
|
+
observer.unobserve(container);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// Observe all product containers
|
|
647
|
+
document.querySelectorAll('.product-placeholder').forEach(el => {
|
|
648
|
+
observer.observe(el);
|
|
649
|
+
});
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
## Troubleshooting
|
|
653
|
+
|
|
654
|
+
### Product Not Displaying
|
|
655
|
+
|
|
656
|
+
1. Check browser console for errors
|
|
657
|
+
2. Verify container ID exists in the DOM
|
|
658
|
+
3. Confirm product identifier is valid
|
|
659
|
+
4. Check that SDK is initialized (`window.elements` exists)
|
|
660
|
+
|
|
661
|
+
### Wrong Pricing
|
|
662
|
+
|
|
663
|
+
- Ensure user's address is set correctly
|
|
664
|
+
- Verify product has availability in user's location
|
|
665
|
+
- Check that fulfillment type is supported
|
|
666
|
+
|
|
667
|
+
### Images Not Loading
|
|
668
|
+
|
|
669
|
+
- Check network tab for 404 errors
|
|
670
|
+
- Verify product has images in the catalog
|
|
671
|
+
- Ensure no ad blockers are interfering
|
|
672
|
+
|
|
673
|
+
### Size Selector Not Showing
|
|
674
|
+
|
|
675
|
+
- Product must have multiple sizes to show selector
|
|
676
|
+
- Check that sizes have availability
|
|
677
|
+
- Verify theme config hasn't hidden the selector
|
|
678
|
+
|
|
679
|
+
## See Also
|
|
680
|
+
|
|
681
|
+
- [Cart Component](./cart-component.md) - Shopping cart functionality
|
|
682
|
+
- [Address Component](./address-component.md) - Location management
|
|
683
|
+
- [Checkout Component](./checkout-component.md) - Complete purchase flow
|
|
684
|
+
- [Theming](./theming.md) - Customize appearance
|
|
685
|
+
- [Events](./events.md) - All available events
|
|
686
|
+
- [Actions API](../api/actions/product-actions.md) - Product actions reference
|