@liquidcommerce/elements-sdk 2.6.0-beta.75 → 2.6.0-beta.76

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.
@@ -28,11 +28,14 @@ export declare const SHARED_ATTR: {
28
28
  readonly JSON_SCRIPT: {
29
29
  readonly DEVELOPMENT: "data-liquid-commerce-elements-development";
30
30
  readonly CUSTOM_THEME: "data-liquid-commerce-elements-custom-theme";
31
+ readonly PRODUCT_URLS: "data-liquid-commerce-elements-product-urls";
31
32
  };
32
33
  };
34
+ export type ProductUrlsConfig = Record<string, Record<string, string>>;
33
35
  export declare function startsWithLcePrefix(value: string | null): boolean;
34
36
  export declare function getDevelopmentConfigs(): ILiquidCommerceElementsDevelopmentConfig | undefined;
35
37
  export declare function getCustomThemeConfig(): IClientCustomThemeConfig | undefined;
38
+ export declare function getProductUrlsConfig(): ProductUrlsConfig | undefined;
36
39
  export declare function triggerAutoInit(initFunction: () => Promise<void>, errorPrefix: string): void;
37
40
  export declare function setupCheckout(script: HTMLScriptElement, injectFn: (params: IInjectCheckoutParams) => Promise<IInjectedComponent | null>, exitCheckoutFn: () => void, options: {
38
41
  errorPrefix: string;
@@ -3,6 +3,7 @@ import type { ProductListFilterType } from '@/interfaces/injection.interface';
3
3
  export type PLCPresentationModeType = 'drawer' | 'modal';
4
4
  export type PLCListType = 'curated' | 'dynamic';
5
5
  export type PLCCardStyle = 'card' | 'ghost';
6
+ export type PLCProductUrl = string | Record<string, string>;
6
7
  export interface IPLCProductCard {
7
8
  style: PLCCardStyle;
8
9
  cornerRadius: string;
@@ -16,7 +17,7 @@ export interface IPLCProductCard {
16
17
  showQuantityCounter: boolean;
17
18
  enablePreCart: boolean;
18
19
  showCollectionTags: boolean;
19
- productUrl: string | null;
20
+ productUrl: PLCProductUrl | null;
20
21
  noAvailabilityText: string;
21
22
  }
22
23
  export interface IPLCList {
@@ -1,5 +1,6 @@
1
1
  import type { PRODUCT_LIST_FILTER_TYPES } from '@/constants';
2
2
  import type { ComponentType } from '@/enums';
3
+ import type { PLCProductUrl } from '@/interfaces/configs/product-list.interface';
3
4
  import type { IAddressOptions } from '@/modules/address/address.interface';
4
5
  export interface IInjectProductElement {
5
6
  containerId: string;
@@ -12,7 +13,7 @@ export interface IInjectProductListParams {
12
13
  rows?: number;
13
14
  columns?: number;
14
15
  filters?: ProductListFilterType[];
15
- productUrl?: string;
16
+ productUrl?: PLCProductUrl;
16
17
  }
17
18
  export interface IInjectProductListSearchParams {
18
19
  containerId: string;
@@ -1,9 +1,10 @@
1
1
  import { BaseComponent, type IOnStoreChanged } from '@/core/base-component.service';
2
2
  import type { IProductListComponent } from '@/interfaces/configs';
3
+ import type { PLCProductUrl } from '@/interfaces/configs/product-list.interface';
3
4
  export interface IProductListCardParams {
4
5
  slug: string;
5
6
  productId: string;
6
- productUrl?: string;
7
+ productUrl?: PLCProductUrl;
7
8
  }
8
9
  export declare class ProductListCardComponent extends BaseComponent<IProductListCardParams, IProductListComponent> {
9
10
  private imageElement;
@@ -1,12 +1,13 @@
1
1
  import { BaseComponent, type IOnStoreChanged } from '@/core/base-component.service';
2
2
  import type { IProductListComponent } from '@/interfaces/configs';
3
+ import type { PLCProductUrl } from '@/interfaces/configs/product-list.interface';
3
4
  import type { ProductListFilterType } from '@/interfaces/injection.interface';
4
5
  export interface IProductListComponentParams {
5
6
  slug: string;
6
7
  rows: number;
7
8
  columns: number;
8
9
  filters: ProductListFilterType[];
9
- productUrl?: string;
10
+ productUrl?: PLCProductUrl;
10
11
  }
11
12
  export declare class ProductListComponent extends BaseComponent<IProductListComponentParams, IProductListComponent> {
12
13
  private products;
@@ -130,11 +130,15 @@ interface IInjectProductListParams {
130
130
  rows?: number; // Default: 3
131
131
  columns?: number; // Default: 4
132
132
  filters?: ProductListFilterType[];
133
- productUrl?: string; // URL pattern with {identifier}
133
+ productUrl?: PLCProductUrl; // string template OR Record<identifier, url> map
134
134
  }
135
+
136
+ // String template: replace {upc} or {grouping} per product.
137
+ // Map: keys are product identifiers (UPC or salsifyGrouping ID); UPC checked first.
138
+ type PLCProductUrl = string | Record<string, string>;
135
139
  ```
136
140
 
137
- ### Example
141
+ ### Example — string template
138
142
 
139
143
  ```javascript
140
144
  await client.injectProductList({
@@ -143,7 +147,25 @@ await client.injectProductList({
143
147
  rows: 4,
144
148
  columns: 3,
145
149
  filters: ['price', 'brands', 'categories'],
146
- productUrl: '/product/{identifier}'
150
+ productUrl: '/product/{grouping}'
151
+ });
152
+ ```
153
+
154
+ ### Example — URL map (partner-owned dedicated PDPs)
155
+
156
+ Use this form when each product has a hand-curated URL that can't be derived
157
+ from a single `{upc}` or `{grouping}` token. See the
158
+ [Product List guide](../guides/product-list-component.md#product-url-map) for
159
+ the equivalent declarative `<script type="application/json">` pattern.
160
+
161
+ ```javascript
162
+ await client.injectProductList({
163
+ containerId: 'products',
164
+ slug: 'best-sellers',
165
+ productUrl: {
166
+ 'GROUPING-33277': '/wines/macallan-12-special-edition',
167
+ '00832889005513': '/spirits/cabernet-2018-club-only',
168
+ },
147
169
  });
148
170
  ```
149
171
 
@@ -190,10 +190,26 @@ interface IInjectProductListParams {
190
190
  rows?: number;
191
191
  columns?: number;
192
192
  filters?: ProductListFilterType[];
193
- productUrl?: string;
193
+ productUrl?: PLCProductUrl;
194
194
  }
195
195
  ```
196
196
 
197
+ ### PLCProductUrl
198
+
199
+ ```typescript
200
+ /**
201
+ * Product card link configuration.
202
+ *
203
+ * - String: template with `{upc}` (selected size's UPC) or `{grouping}`
204
+ * (product's salsifyGrouping ID). Replaced per product card.
205
+ * - Map: keyed by product identifier (UPC or salsifyGrouping ID — same
206
+ * identifier types accepted by `injectProductElement`). The card looks up
207
+ * UPC first, then grouping ID. Use this when partner PDPs have
208
+ * hand-curated URLs that aren't derivable from a single token.
209
+ */
210
+ type PLCProductUrl = string | Record<string, string>;
211
+ ```
212
+
197
213
  ### IInjectProductListSearchParams
198
214
 
199
215
  ```typescript
@@ -177,7 +177,7 @@ await client.injectProductList({
177
177
  slug: 'whiskey-collection',
178
178
  rows: 4,
179
179
  columns: 3,
180
- productUrl: '/products/{identifier}'
180
+ productUrl: '/products/{grouping}'
181
181
  });
182
182
  ```
183
183
 
@@ -33,7 +33,7 @@ Use data attributes to configure the product list:
33
33
  data-rows="3"
34
34
  data-columns="4"
35
35
  data-filters="price,brands,categories"
36
- data-product-url="/product/{identifier}"
36
+ data-product-url="/product/{grouping}"
37
37
  ></div>
38
38
  ```
39
39
 
@@ -44,7 +44,13 @@ Use data attributes to configure the product list:
44
44
  - `data-filters`: Comma-separated filter types
45
45
  - `data-product-url`: URL pattern for product detail pages (optional)
46
46
 
47
- The `{identifier}` placeholder in `data-product-url` is replaced with the product's identifier.
47
+ `data-product-url` accepts a string template with one of two placeholders:
48
+ - `{grouping}` — replaced with the product's salsifyGrouping ID
49
+ - `{upc}` — replaced with the selected size's UPC
50
+
51
+ If your PDP URLs aren't derivable from a single placeholder (e.g. each
52
+ product has a hand-curated marketing slug), use the
53
+ [Product URL Map](#product-url-map) instead.
48
54
 
49
55
  ### With Search and Filters
50
56
 
@@ -62,7 +68,7 @@ Separate containers for search and filters:
62
68
  data-liquid-commerce-elements-products-list="my-collection-slug"
63
69
  data-rows="4"
64
70
  data-columns="3"
65
- data-product-url="/products/{identifier}"
71
+ data-product-url="/products/{grouping}"
66
72
  ></div>
67
73
  ```
68
74
 
@@ -80,7 +86,7 @@ await client.injectProductList({
80
86
  rows: 3,
81
87
  columns: 4,
82
88
  filters: ['price', 'brands', 'categories', 'fulfillment'],
83
- productUrl: '/product/{identifier}'
89
+ productUrl: '/product/{grouping}'
84
90
  });
85
91
 
86
92
  // Inject search (optional)
@@ -213,7 +219,7 @@ Each product card shows:
213
219
 
214
220
  ### Card Interaction
215
221
 
216
- **Click on card:** Navigate to product detail page (if `productUrl` configured)
222
+ **Click on card:** Navigate to product detail page (if `productUrl` configured — see [Product URL Map](#product-url-map) for partner-owned PDP URLs that aren't derivable from a token).
217
223
 
218
224
  **Quick Add:** Add product to cart directly from list view (if enabled)
219
225
 
@@ -298,7 +304,7 @@ See [Configuration Reference](../api/configuration.md#product-list-theme) for th
298
304
  data-liquid-commerce-elements-products-list="whiskey-collection"
299
305
  data-rows="4"
300
306
  data-columns="3"
301
- data-product-url="/whiskey/{identifier}"
307
+ data-product-url="/whiskey/{grouping}"
302
308
  ></div>
303
309
  </main>
304
310
  </div>
@@ -339,13 +345,13 @@ const productType = getProductTypeFromData();
339
345
  let urlPattern;
340
346
  switch (productType) {
341
347
  case 'whiskey':
342
- urlPattern = '/spirits/whiskey/{identifier}';
348
+ urlPattern = '/spirits/whiskey/{grouping}';
343
349
  break;
344
350
  case 'wine':
345
- urlPattern = '/wine/{identifier}';
351
+ urlPattern = '/wine/{grouping}';
346
352
  break;
347
353
  default:
348
- urlPattern = '/products/{identifier}';
354
+ urlPattern = '/products/{grouping}';
349
355
  }
350
356
 
351
357
  await client.injectProductList({
@@ -357,6 +363,76 @@ await client.injectProductList({
357
363
  });
358
364
  ```
359
365
 
366
+ ### Product URL Map
367
+
368
+ For partners whose PDPs have hand-curated URLs that aren't derivable from a
369
+ single placeholder (e.g. dedicated marketing pages, Shopify handles, WordPress
370
+ slugs), pass a **map** instead of a string template. Keys are product
371
+ identifiers — either a UPC or a salsifyGrouping ID, the same identifier types
372
+ accepted by `injectProductElement`. The card looks up UPC first, then grouping
373
+ ID; products not in the map render without a link.
374
+
375
+ #### Declarative — JSON script tag
376
+
377
+ Drop a single `<script type="application/json">` tag anywhere on the page,
378
+ keyed by list slug → identifier → URL. Generate it server-side from your CMS.
379
+
380
+ ```html
381
+ <script data-liquid-commerce-elements-product-urls type="application/json">
382
+ {
383
+ "best-sellers": {
384
+ "GROUPING-33277": "/wines/macallan-12-special-edition",
385
+ "00832889005513": "/spirits/cabernet-2018-club-only"
386
+ },
387
+ "limited-releases": {
388
+ "GROUPING-78941": "/exclusive/pappy-23-allocation"
389
+ }
390
+ }
391
+ </script>
392
+
393
+ <div data-liquid-commerce-elements-products-list="best-sellers"></div>
394
+ ```
395
+
396
+ When both `data-product-url` and a slug entry in this script are present for
397
+ the same list, the **map wins** — it's the more specific intent.
398
+
399
+ ##### Shopify Liquid
400
+
401
+ ```liquid
402
+ <script data-liquid-commerce-elements-product-urls type="application/json">
403
+ {
404
+ "best-sellers": {
405
+ {% for p in collections.best-sellers.products %}
406
+ "{{ p.metafields.lc.grouping_id }}": "{{ p.url }}"{% unless forloop.last %},{% endunless %}
407
+ {% endfor %}
408
+ }
409
+ }
410
+ </script>
411
+ ```
412
+
413
+ ##### WordPress / WooCommerce (PHP)
414
+
415
+ ```php
416
+ <script data-liquid-commerce-elements-product-urls type="application/json">
417
+ <?= json_encode(['best-sellers' => $lc_identifier_to_pdp_url_map]) ?>
418
+ </script>
419
+ ```
420
+
421
+ #### Programmatic
422
+
423
+ ```javascript
424
+ await client.injectProductList({
425
+ containerId: 'products',
426
+ slug: 'best-sellers',
427
+ rows: 3,
428
+ columns: 4,
429
+ productUrl: {
430
+ 'GROUPING-33277': '/wines/macallan-12-special-edition',
431
+ '00832889005513': '/spirits/cabernet-2018-club-only',
432
+ },
433
+ });
434
+ ```
435
+
360
436
  ## Events
361
437
 
362
438
  While product list events are primarily internal, you can listen for cart events when users add products:
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.6.0-beta.75",
6
+ "version": "2.6.0-beta.76",
7
7
  "homepage": "https://docs.liquidcommerce.co/elements-sdk",
8
8
  "repository": {
9
9
  "type": "git",