@wix/headless-stores 0.0.104 → 0.0.106
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/cjs/dist/react/Option.d.ts +1 -0
- package/cjs/dist/react/Product.d.ts +51 -4
- package/cjs/dist/react/Product.js +95 -15
- package/cjs/dist/react/ProductList.d.ts +2 -3
- package/cjs/dist/react/core/Product.d.ts +8 -8
- package/cjs/dist/react/core/ProductModifiers.d.ts +11 -9
- package/cjs/dist/react/core/ProductModifiers.js +6 -3
- package/cjs/dist/react/core/ProductVariantSelector.d.ts +13 -9
- package/cjs/dist/react/core/ProductVariantSelector.js +14 -8
- package/cjs/dist/services/buy-now-service.js +2 -2
- package/cjs/dist/services/product-modifiers-service.d.ts +1 -1
- package/cjs/dist/services/product-modifiers-service.js +1 -1
- package/cjs/dist/services/product-service.d.ts +17 -1
- package/cjs/dist/services/product-service.js +151 -1
- package/cjs/dist/services/selected-variant-service.d.ts +6 -6
- package/cjs/dist/services/selected-variant-service.js +15 -16
- package/dist/react/Option.d.ts +1 -0
- package/dist/react/Product.d.ts +51 -4
- package/dist/react/Product.js +95 -15
- package/dist/react/ProductList.d.ts +2 -3
- package/dist/react/core/Product.d.ts +8 -8
- package/dist/react/core/ProductModifiers.d.ts +11 -9
- package/dist/react/core/ProductModifiers.js +6 -3
- package/dist/react/core/ProductVariantSelector.d.ts +13 -9
- package/dist/react/core/ProductVariantSelector.js +14 -8
- package/dist/services/buy-now-service.js +2 -2
- package/dist/services/product-modifiers-service.d.ts +1 -1
- package/dist/services/product-modifiers-service.js +1 -1
- package/dist/services/product-service.d.ts +17 -1
- package/dist/services/product-service.js +151 -1
- package/dist/services/selected-variant-service.d.ts +6 -6
- package/dist/services/selected-variant-service.js +15 -16
- package/package.json +8 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { productsV3 } from '@wix/stores';
|
|
1
2
|
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
2
|
-
import * as productsV3 from '@wix/auto_sdk_stores_products-v-3';
|
|
3
3
|
/**
|
|
4
4
|
* API interface for the Product service, providing reactive product data management.
|
|
5
5
|
* This service handles loading and managing a single product's data, loading state, and errors.
|
|
@@ -15,6 +15,22 @@ export interface ProductServiceAPI {
|
|
|
15
15
|
error: Signal<string | null>;
|
|
16
16
|
/** Function to load a product by its slug */
|
|
17
17
|
loadProduct: (slug: string) => Promise<void>;
|
|
18
|
+
/** Reports a ViewContent analytics event for the current product */
|
|
19
|
+
reportViewContentTrackEvent: () => void;
|
|
20
|
+
/** Reports an AddToCart analytics event for the current product */
|
|
21
|
+
reportAddToCartTrackEvent: (options?: {
|
|
22
|
+
quantity?: number;
|
|
23
|
+
variant?: string;
|
|
24
|
+
origin?: string;
|
|
25
|
+
position?: string;
|
|
26
|
+
}) => void;
|
|
27
|
+
/** Reports a ClickProduct analytics event for the current product */
|
|
28
|
+
reportClickProductTrackEvent: (options?: {
|
|
29
|
+
variant?: string;
|
|
30
|
+
list?: string;
|
|
31
|
+
position?: string;
|
|
32
|
+
origin?: string;
|
|
33
|
+
}) => void;
|
|
18
34
|
}
|
|
19
35
|
/**
|
|
20
36
|
* Service definition for the Product service.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { productsV3 } from '@wix/stores';
|
|
1
2
|
import { defineService, implementService } from '@wix/services-definitions';
|
|
2
3
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
3
|
-
import
|
|
4
|
+
import { analytics } from '@wix/site';
|
|
4
5
|
/**
|
|
5
6
|
* Service definition for the Product service.
|
|
6
7
|
* This defines the contract that the ProductService must implement.
|
|
@@ -61,11 +62,160 @@ export const ProductService = implementService.withConfig()(ProductServiceDefini
|
|
|
61
62
|
if (config.productSlug) {
|
|
62
63
|
loadProduct(config.productSlug);
|
|
63
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Reports a ViewContent analytics event for product page tracking.
|
|
67
|
+
* This function automatically formats and sends a standardized ViewContent event
|
|
68
|
+
* to the Wix Analytics service when a user views a product page. It extracts
|
|
69
|
+
* relevant product information (brand, category, price, SKU) and sends it in
|
|
70
|
+
* the proper format for analytics tracking.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* // Track when a product page loads
|
|
75
|
+
* import { useService } from '@wix/services-manager-react';
|
|
76
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
77
|
+
*
|
|
78
|
+
* function ProductPage() {
|
|
79
|
+
* const productService = useService(ProductServiceDefinition);
|
|
80
|
+
*
|
|
81
|
+
* useEffect(() => {
|
|
82
|
+
* productService.reportViewContentTrackEvent();
|
|
83
|
+
* }, []);
|
|
84
|
+
*
|
|
85
|
+
* return <div>Product Page</div>;
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
const reportViewContentTrackEvent = () => {
|
|
90
|
+
const currentProduct = product.get();
|
|
91
|
+
if (!currentProduct)
|
|
92
|
+
return;
|
|
93
|
+
const eventData = {
|
|
94
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
95
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
96
|
+
currency: currentProduct.currency ?? undefined,
|
|
97
|
+
id: currentProduct._id,
|
|
98
|
+
name: currentProduct.name || '',
|
|
99
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
100
|
+
?.amount ?? '0'),
|
|
101
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
102
|
+
};
|
|
103
|
+
analytics.trackEvent('ViewContent', eventData);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Reports an AddToCart analytics event for cart tracking.
|
|
107
|
+
* This function automatically formats and sends a standardized AddToCart event
|
|
108
|
+
* to the Wix Analytics service when a user adds a product to their cart. It extracts
|
|
109
|
+
* relevant product information (brand, category, price, SKU, variant, quantity) and sends
|
|
110
|
+
* it in the proper format for analytics tracking.
|
|
111
|
+
*
|
|
112
|
+
* @param options Additional options for the AddToCart event
|
|
113
|
+
* @param options.quantity The quantity being added to cart (defaults to 1)
|
|
114
|
+
* @param options.variant The selected product variant (e.g., "Large", "Green")
|
|
115
|
+
* @param options.origin Event origin (e.g., "Product Page", "Quick View")
|
|
116
|
+
* @param options.position List or collection position (e.g., "Product Gallery", "Search Results")
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```tsx
|
|
120
|
+
* // Track add to cart from product page
|
|
121
|
+
* import { useService } from '@wix/services-manager-react';
|
|
122
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
123
|
+
*
|
|
124
|
+
* function AddToCartButton() {
|
|
125
|
+
* const productService = useService(ProductServiceDefinition);
|
|
126
|
+
*
|
|
127
|
+
* const handleAddToCart = () => {
|
|
128
|
+
* productService.reportAddToCartTrackEvent({
|
|
129
|
+
* quantity: 1,
|
|
130
|
+
* variant: 'Large Blue'
|
|
131
|
+
* });
|
|
132
|
+
* };
|
|
133
|
+
*
|
|
134
|
+
* return <button onClick={handleAddToCart}>Add to Cart</button>;
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
const reportAddToCartTrackEvent = (options) => {
|
|
139
|
+
const currentProduct = product.get();
|
|
140
|
+
if (!currentProduct)
|
|
141
|
+
return;
|
|
142
|
+
const eventData = {
|
|
143
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
144
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
145
|
+
currency: currentProduct.currency ?? undefined,
|
|
146
|
+
id: currentProduct._id,
|
|
147
|
+
name: currentProduct.name || '',
|
|
148
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
149
|
+
?.amount ?? '0'),
|
|
150
|
+
quantity: options?.quantity ?? 1,
|
|
151
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
152
|
+
variant: options?.variant,
|
|
153
|
+
origin: options?.origin,
|
|
154
|
+
position: options?.position,
|
|
155
|
+
};
|
|
156
|
+
analytics.trackEvent('AddToCart', eventData);
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Reports a ClickProduct analytics event for product click tracking.
|
|
160
|
+
* This function automatically formats and sends a standardized ClickProduct event
|
|
161
|
+
* to the Wix Analytics service when a user clicks on a product link. It extracts
|
|
162
|
+
* relevant product information (brand, category, price, SKU, variant) and sends
|
|
163
|
+
* it in the proper format for analytics tracking.
|
|
164
|
+
*
|
|
165
|
+
* @param options Additional options for the ClickProduct event
|
|
166
|
+
* @param options.variant The selected product variant (e.g., "Large", "Green")
|
|
167
|
+
* @param options.list List or collection the product is part of (e.g., "Category: T-Shirts")
|
|
168
|
+
* @param options.position Position of the product within a list or collection (e.g., "3")
|
|
169
|
+
* @param options.origin Event origin (e.g., "Search Results", "Related Products")
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```tsx
|
|
173
|
+
* // Track product click from category page
|
|
174
|
+
* import { useService } from '@wix/services-manager-react';
|
|
175
|
+
* import { ProductServiceDefinition } from '@wix/stores/services';
|
|
176
|
+
*
|
|
177
|
+
* function ProductLink() {
|
|
178
|
+
* const productService = useService(ProductServiceDefinition);
|
|
179
|
+
*
|
|
180
|
+
* const handleClick = () => {
|
|
181
|
+
* productService.reportClickProductTrackEvent({
|
|
182
|
+
* list: 'Category: T-Shirts',
|
|
183
|
+
* position: '3'
|
|
184
|
+
* });
|
|
185
|
+
* };
|
|
186
|
+
*
|
|
187
|
+
* return <a onClick={handleClick}>View Product</a>;
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
const reportClickProductTrackEvent = (options) => {
|
|
192
|
+
const currentProduct = product.get();
|
|
193
|
+
if (!currentProduct)
|
|
194
|
+
return;
|
|
195
|
+
const eventData = {
|
|
196
|
+
brand: currentProduct.brand?.name ?? undefined,
|
|
197
|
+
category: currentProduct.breadcrumbsInfo?.breadcrumbs?.[0]?.categoryName,
|
|
198
|
+
currency: currentProduct.currency ?? undefined,
|
|
199
|
+
id: currentProduct._id,
|
|
200
|
+
name: currentProduct.name || '',
|
|
201
|
+
price: parseFloat(currentProduct.variantsInfo?.variants?.[0]?.price?.actualPrice
|
|
202
|
+
?.amount ?? '0'),
|
|
203
|
+
sku: currentProduct.variantsInfo?.variants?.[0]?.sku ?? undefined,
|
|
204
|
+
variant: options?.variant,
|
|
205
|
+
list: options?.list,
|
|
206
|
+
position: options?.position,
|
|
207
|
+
origin: options?.origin,
|
|
208
|
+
};
|
|
209
|
+
analytics.trackEvent('ClickProduct', eventData);
|
|
210
|
+
};
|
|
64
211
|
return {
|
|
65
212
|
product,
|
|
66
213
|
isLoading,
|
|
67
214
|
error,
|
|
68
215
|
loadProduct,
|
|
216
|
+
reportViewContentTrackEvent,
|
|
217
|
+
reportAddToCartTrackEvent,
|
|
218
|
+
reportClickProductTrackEvent,
|
|
69
219
|
};
|
|
70
220
|
});
|
|
71
221
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Signal, type ReadOnlySignal } from '@wix/services-definitions/core-services/signals';
|
|
2
|
-
import
|
|
2
|
+
import { productsV3 } from '@wix/stores';
|
|
3
3
|
export interface SelectedVariantServiceAPI {
|
|
4
4
|
selectedQuantity: Signal<number>;
|
|
5
5
|
selectedChoices: Signal<Record<string, string>>;
|
|
@@ -39,15 +39,15 @@ export interface SelectedVariantServiceAPI {
|
|
|
39
39
|
decrementQuantity: () => void;
|
|
40
40
|
selectVariantById: (id: string) => void;
|
|
41
41
|
resetSelections: () => void;
|
|
42
|
-
getAvailableChoicesForOption: (
|
|
43
|
-
getChoiceInfo: (
|
|
42
|
+
getAvailableChoicesForOption: (optionKey: string) => string[];
|
|
43
|
+
getChoiceInfo: (optionKey: string, choiceValue: string) => {
|
|
44
44
|
isAvailable: boolean;
|
|
45
45
|
isInStock: boolean;
|
|
46
46
|
isPreOrderEnabled: boolean;
|
|
47
47
|
};
|
|
48
|
-
isChoiceAvailable: (
|
|
49
|
-
isChoiceInStock: (
|
|
50
|
-
isChoicePreOrderEnabled: (
|
|
48
|
+
isChoiceAvailable: (optionKey: string, choiceValue: string) => boolean;
|
|
49
|
+
isChoiceInStock: (optionKey: string, choiceValue: string) => boolean;
|
|
50
|
+
isChoicePreOrderEnabled: (optionKey: string, choiceValue: string) => boolean;
|
|
51
51
|
hasAnySelections: () => boolean;
|
|
52
52
|
IsAllVariantsAreOutOfStock: () => boolean;
|
|
53
53
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { defineService, implementService } from '@wix/services-definitions';
|
|
2
2
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
3
|
-
import
|
|
4
|
-
import * as inventoryItemsV3 from '@wix/auto_sdk_stores_inventory-items-v-3';
|
|
3
|
+
import { inventoryItemsV3, productsV3 } from '@wix/stores';
|
|
5
4
|
import { CurrentCartServiceDefinition } from '@wix/headless-ecom/services';
|
|
6
5
|
import { MediaGalleryServiceDefinition } from '@wix/headless-media/services';
|
|
7
6
|
import { ProductServiceDefinition } from './product-service.js';
|
|
@@ -202,8 +201,8 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
|
|
|
202
201
|
if (Object.keys(choices).length !==
|
|
203
202
|
Object.keys(variantChoices).length)
|
|
204
203
|
return false;
|
|
205
|
-
return Object.entries(choices).every(([
|
|
206
|
-
return variantChoices[
|
|
204
|
+
return Object.entries(choices).every(([optionKey, optionValue]) => {
|
|
205
|
+
return variantChoices[optionKey] === optionValue;
|
|
207
206
|
});
|
|
208
207
|
}) || null);
|
|
209
208
|
}));
|
|
@@ -410,7 +409,7 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
|
|
|
410
409
|
selectedQuantity.set(newQuantity);
|
|
411
410
|
};
|
|
412
411
|
// New methods for smart variant selection
|
|
413
|
-
const getAvailableChoicesForOption = (
|
|
412
|
+
const getAvailableChoicesForOption = (optionKey) => {
|
|
414
413
|
const currentChoices = selectedChoices.get();
|
|
415
414
|
const variantsList = variants.get();
|
|
416
415
|
// Get all possible choices for this option that result in valid variants
|
|
@@ -419,21 +418,21 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
|
|
|
419
418
|
const variantChoices = processVariantChoices(variant);
|
|
420
419
|
// Check if this variant matches all currently selected choices (except for the option we're checking)
|
|
421
420
|
const matchesOtherChoices = Object.entries(currentChoices)
|
|
422
|
-
.filter(([key]) => key !==
|
|
421
|
+
.filter(([key]) => key !== optionKey)
|
|
423
422
|
.every(([key, value]) => variantChoices[key] === value);
|
|
424
|
-
if (matchesOtherChoices && variantChoices[
|
|
425
|
-
availableChoices.add(variantChoices[
|
|
423
|
+
if (matchesOtherChoices && variantChoices[optionKey]) {
|
|
424
|
+
availableChoices.add(variantChoices[optionKey]);
|
|
426
425
|
}
|
|
427
426
|
});
|
|
428
427
|
return Array.from(availableChoices);
|
|
429
428
|
};
|
|
430
429
|
// Core method that provides both availability and stock info efficiently
|
|
431
|
-
const getChoiceInfo = (
|
|
430
|
+
const getChoiceInfo = (optionKey, choiceValue) => {
|
|
432
431
|
// Create hypothetical choices with this choice selected
|
|
433
432
|
const currentChoices = selectedChoices.get();
|
|
434
433
|
const hypotheticalChoices = {
|
|
435
434
|
...currentChoices,
|
|
436
|
-
[
|
|
435
|
+
[optionKey]: choiceValue,
|
|
437
436
|
};
|
|
438
437
|
// Get all variants and find one that matches these choices
|
|
439
438
|
const variantsList = variants.get();
|
|
@@ -459,14 +458,14 @@ export const SelectedVariantService = implementService.withConfig()(SelectedVari
|
|
|
459
458
|
return { isAvailable, isInStock, isPreOrderEnabled };
|
|
460
459
|
};
|
|
461
460
|
// Simplified methods using the core getChoiceInfo
|
|
462
|
-
const isChoiceAvailable = (
|
|
463
|
-
return getChoiceInfo(
|
|
461
|
+
const isChoiceAvailable = (optionKey, choiceValue) => {
|
|
462
|
+
return getChoiceInfo(optionKey, choiceValue).isAvailable;
|
|
464
463
|
};
|
|
465
|
-
const isChoiceInStock = (
|
|
466
|
-
return getChoiceInfo(
|
|
464
|
+
const isChoiceInStock = (optionKey, choiceValue) => {
|
|
465
|
+
return getChoiceInfo(optionKey, choiceValue).isInStock;
|
|
467
466
|
};
|
|
468
|
-
const isChoicePreOrderEnabled = (
|
|
469
|
-
return getChoiceInfo(
|
|
467
|
+
const isChoicePreOrderEnabled = (optionKey, choiceValue) => {
|
|
468
|
+
return getChoiceInfo(optionKey, choiceValue).isPreOrderEnabled;
|
|
470
469
|
};
|
|
471
470
|
const hasAnySelections = () => {
|
|
472
471
|
const currentChoices = selectedChoices.get();
|
package/dist/react/Option.d.ts
CHANGED
package/dist/react/Product.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import React from 'react';
|
|
1
|
+
import { productsV3 } from '@wix/stores';
|
|
3
2
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
3
|
+
import React from 'react';
|
|
4
4
|
import { AsContent } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Context for sharing variant options state between components
|
|
@@ -37,7 +37,7 @@ export type StockStatus = BasicStockStatus | 'can-pre-order';
|
|
|
37
37
|
*/
|
|
38
38
|
export interface ProductRootProps {
|
|
39
39
|
children: React.ReactNode;
|
|
40
|
-
product: V3Product;
|
|
40
|
+
product: productsV3.V3Product;
|
|
41
41
|
selectedVariant?: any;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
@@ -268,7 +268,7 @@ export interface RawProps {
|
|
|
268
268
|
asChild?: boolean;
|
|
269
269
|
/** Custom render function when using asChild */
|
|
270
270
|
children?: AsChildChildren<{
|
|
271
|
-
product: V3Product;
|
|
271
|
+
product: productsV3.V3Product;
|
|
272
272
|
}>;
|
|
273
273
|
/** CSS classes to apply to the default element */
|
|
274
274
|
className?: string;
|
|
@@ -885,6 +885,53 @@ export declare const Action: {
|
|
|
885
885
|
/** Pre-order action button */
|
|
886
886
|
readonly PreOrder: React.ForwardRefExoticComponent<ProductActionProps & React.RefAttributes<HTMLButtonElement>>;
|
|
887
887
|
};
|
|
888
|
+
/**
|
|
889
|
+
* Props for Product Link component
|
|
890
|
+
*/
|
|
891
|
+
export interface ProductLinkProps {
|
|
892
|
+
/** Whether to render as a child component */
|
|
893
|
+
asChild?: boolean;
|
|
894
|
+
/** Custom render function when using asChild */
|
|
895
|
+
children?: React.ReactNode;
|
|
896
|
+
/** CSS classes to apply to the link */
|
|
897
|
+
className?: string;
|
|
898
|
+
/** List or collection the product is part of (e.g., "Category: T-Shirts") */
|
|
899
|
+
list?: string;
|
|
900
|
+
/** Position of the product within a list or collection */
|
|
901
|
+
position?: string;
|
|
902
|
+
/** Event origin (e.g., "Search Results", "Related Products") */
|
|
903
|
+
origin?: string;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Product link component that automatically tracks ClickProduct events.
|
|
907
|
+
* Wraps a link to the product page and fires analytics when clicked.
|
|
908
|
+
*
|
|
909
|
+
* @component
|
|
910
|
+
* @example
|
|
911
|
+
* ```tsx
|
|
912
|
+
* // Simple usage
|
|
913
|
+
* <Product.Link className="btn-secondary">
|
|
914
|
+
* View Product
|
|
915
|
+
* </Product.Link>
|
|
916
|
+
*
|
|
917
|
+
* // With tracking parameters
|
|
918
|
+
* <Product.Link
|
|
919
|
+
* list="Category: T-Shirts"
|
|
920
|
+
* position="3"
|
|
921
|
+
* className="btn-secondary"
|
|
922
|
+
* >
|
|
923
|
+
* View Product
|
|
924
|
+
* </Product.Link>
|
|
925
|
+
*
|
|
926
|
+
* // asChild with custom link
|
|
927
|
+
* <Product.Link asChild list="Search Results" position="5">
|
|
928
|
+
* <a href="/custom-url" className="custom-link">
|
|
929
|
+
* <Product.Name />
|
|
930
|
+
* </a>
|
|
931
|
+
* </Product.Link>
|
|
932
|
+
* ```
|
|
933
|
+
*/
|
|
934
|
+
export declare const Link: React.ForwardRefExoticComponent<ProductLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
888
935
|
/**
|
|
889
936
|
* Props for ProductVariantStock component
|
|
890
937
|
*/
|
package/dist/react/Product.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { productsV3 } from '@wix/stores';
|
|
3
|
+
import { Quantity as QuantityComponent } from '@wix/headless-components/react';
|
|
4
4
|
import { Commerce } from '@wix/headless-ecom/react';
|
|
5
|
-
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
6
5
|
import { MediaGallery } from '@wix/headless-media/react';
|
|
7
|
-
import {
|
|
6
|
+
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
7
|
+
import { useService } from '@wix/services-manager-react';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { DataComponentTags } from '../data-component-tags.js';
|
|
10
|
+
import { ProductServiceDefinition, SelectedVariantServiceDefinition, } from '../services/index.js';
|
|
8
11
|
import * as CoreProduct from './core/Product.js';
|
|
9
|
-
import * as CoreProductVariantSelector from './core/ProductVariantSelector.js';
|
|
10
12
|
import * as CoreProductModifiers from './core/ProductModifiers.js';
|
|
13
|
+
import * as CoreProductVariantSelector from './core/ProductVariantSelector.js';
|
|
11
14
|
import * as CoreSelectedVariant from './core/SelectedVariant.js';
|
|
12
15
|
import * as Option from './Option.js';
|
|
13
16
|
import { AsContent } from './types.js';
|
|
14
|
-
import { DataComponentTags } from '../data-component-tags.js';
|
|
15
17
|
const VariantsContext = React.createContext(null);
|
|
16
18
|
/**
|
|
17
19
|
* Hook to access variants context
|
|
@@ -61,6 +63,7 @@ var TestIds;
|
|
|
61
63
|
TestIds["productActionAddToCart"] = "product-action-add-to-cart";
|
|
62
64
|
TestIds["productActionBuyNow"] = "product-action-buy-now";
|
|
63
65
|
TestIds["productActionPreOrder"] = "product-action-can-pre-order";
|
|
66
|
+
TestIds["productLink"] = "product-link";
|
|
64
67
|
TestIds["productQuantity"] = "product-quantity";
|
|
65
68
|
TestIds["productQuantityDecrement"] = "product-quantity-decrement";
|
|
66
69
|
TestIds["productQuantityInput"] = "product-quantity-input";
|
|
@@ -403,15 +406,15 @@ export const Stock = React.forwardRef((props, ref) => {
|
|
|
403
406
|
let status;
|
|
404
407
|
let label;
|
|
405
408
|
switch (availabilityStatus) {
|
|
406
|
-
case InventoryAvailabilityStatus.IN_STOCK:
|
|
409
|
+
case productsV3.InventoryAvailabilityStatus.IN_STOCK:
|
|
407
410
|
status = 'in-stock';
|
|
408
411
|
label = finalLabels.inStock;
|
|
409
412
|
break;
|
|
410
|
-
case InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
|
|
413
|
+
case productsV3.InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
|
|
411
414
|
status = 'limited-stock';
|
|
412
415
|
label = finalLabels.limitedStock;
|
|
413
416
|
break;
|
|
414
|
-
case InventoryAvailabilityStatus.OUT_OF_STOCK:
|
|
417
|
+
case productsV3.InventoryAvailabilityStatus.OUT_OF_STOCK:
|
|
415
418
|
default:
|
|
416
419
|
status = 'out-of-stock';
|
|
417
420
|
label = finalLabels.outOfStock;
|
|
@@ -879,6 +882,19 @@ export const ProductVariantSelectorReset = React.forwardRef((props, ref) => {
|
|
|
879
882
|
*/
|
|
880
883
|
export const ProductActionAddToCart = React.forwardRef((props, ref) => {
|
|
881
884
|
const { asChild, children, className, label, loadingState } = props;
|
|
885
|
+
// Get services for tracking
|
|
886
|
+
const productService = useService(ProductServiceDefinition);
|
|
887
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
888
|
+
// Track after successful add to cart
|
|
889
|
+
const handleSuccess = async () => {
|
|
890
|
+
const choices = variantService.selectedChoices.get();
|
|
891
|
+
const quantity = variantService.selectedQuantity.get();
|
|
892
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
893
|
+
productService.reportAddToCartTrackEvent({
|
|
894
|
+
quantity,
|
|
895
|
+
variant: variantString,
|
|
896
|
+
});
|
|
897
|
+
};
|
|
882
898
|
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, canAddToCart, isLoading, addToCart, isPreOrderEnabled, }) => {
|
|
883
899
|
if (isPreOrderEnabled) {
|
|
884
900
|
return null;
|
|
@@ -886,13 +902,13 @@ export const ProductActionAddToCart = React.forwardRef((props, ref) => {
|
|
|
886
902
|
const onClick = addToCart;
|
|
887
903
|
const disabled = !canAddToCart || isLoading;
|
|
888
904
|
if (asChild && children) {
|
|
889
|
-
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
905
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, onSuccess: handleSuccess, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
890
906
|
disabled,
|
|
891
907
|
isLoading,
|
|
892
908
|
onClick,
|
|
893
909
|
} }) }));
|
|
894
910
|
}
|
|
895
|
-
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading }));
|
|
911
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, onSuccess: handleSuccess, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading }));
|
|
896
912
|
} }));
|
|
897
913
|
});
|
|
898
914
|
/**
|
|
@@ -923,6 +939,19 @@ export const ProductActionBuyNow = React.forwardRef((props, ref) => {
|
|
|
923
939
|
*/
|
|
924
940
|
export const ProductActionPreOrder = React.forwardRef((props, ref) => {
|
|
925
941
|
const { asChild, children, className, label, loadingState } = props;
|
|
942
|
+
// Get services for tracking
|
|
943
|
+
const productService = useService(ProductServiceDefinition);
|
|
944
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
945
|
+
// Track after successful pre-order
|
|
946
|
+
const handleSuccess = async () => {
|
|
947
|
+
const choices = variantService.selectedChoices.get();
|
|
948
|
+
const quantity = variantService.selectedQuantity.get();
|
|
949
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
950
|
+
productService.reportAddToCartTrackEvent({
|
|
951
|
+
quantity,
|
|
952
|
+
variant: variantString,
|
|
953
|
+
});
|
|
954
|
+
};
|
|
926
955
|
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, isLoading, addToCart, isPreOrderEnabled }) => {
|
|
927
956
|
if (!isPreOrderEnabled) {
|
|
928
957
|
return null;
|
|
@@ -931,13 +960,13 @@ export const ProductActionPreOrder = React.forwardRef((props, ref) => {
|
|
|
931
960
|
const onClick = addToCart;
|
|
932
961
|
const disabled = !canPreOrder;
|
|
933
962
|
if (asChild && children) {
|
|
934
|
-
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
963
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, onSuccess: handleSuccess, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
935
964
|
disabled,
|
|
936
965
|
isLoading,
|
|
937
966
|
onClick,
|
|
938
967
|
} }) }));
|
|
939
968
|
}
|
|
940
|
-
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading }));
|
|
969
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, onSuccess: handleSuccess, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading }));
|
|
941
970
|
} }));
|
|
942
971
|
});
|
|
943
972
|
/**
|
|
@@ -952,6 +981,57 @@ export const Action = {
|
|
|
952
981
|
/** Pre-order action button */
|
|
953
982
|
PreOrder: ProductActionPreOrder,
|
|
954
983
|
};
|
|
984
|
+
/**
|
|
985
|
+
* Product link component that automatically tracks ClickProduct events.
|
|
986
|
+
* Wraps a link to the product page and fires analytics when clicked.
|
|
987
|
+
*
|
|
988
|
+
* @component
|
|
989
|
+
* @example
|
|
990
|
+
* ```tsx
|
|
991
|
+
* // Simple usage
|
|
992
|
+
* <Product.Link className="btn-secondary">
|
|
993
|
+
* View Product
|
|
994
|
+
* </Product.Link>
|
|
995
|
+
*
|
|
996
|
+
* // With tracking parameters
|
|
997
|
+
* <Product.Link
|
|
998
|
+
* list="Category: T-Shirts"
|
|
999
|
+
* position="3"
|
|
1000
|
+
* className="btn-secondary"
|
|
1001
|
+
* >
|
|
1002
|
+
* View Product
|
|
1003
|
+
* </Product.Link>
|
|
1004
|
+
*
|
|
1005
|
+
* // asChild with custom link
|
|
1006
|
+
* <Product.Link asChild list="Search Results" position="5">
|
|
1007
|
+
* <a href="/custom-url" className="custom-link">
|
|
1008
|
+
* <Product.Name />
|
|
1009
|
+
* </a>
|
|
1010
|
+
* </Product.Link>
|
|
1011
|
+
* ```
|
|
1012
|
+
*/
|
|
1013
|
+
export const Link = React.forwardRef((props, ref) => {
|
|
1014
|
+
const { asChild, children, className, list, position, origin } = props;
|
|
1015
|
+
// Get services for tracking
|
|
1016
|
+
const productService = useService(ProductServiceDefinition);
|
|
1017
|
+
const variantService = useService(SelectedVariantServiceDefinition);
|
|
1018
|
+
return (_jsx(CoreProduct.Slug, { children: ({ slug }) => {
|
|
1019
|
+
const handleClick = () => {
|
|
1020
|
+
const choices = variantService.selectedChoices.get();
|
|
1021
|
+
const variantString = Object.values(choices).join(' ') || undefined;
|
|
1022
|
+
productService.reportClickProductTrackEvent({
|
|
1023
|
+
variant: variantString,
|
|
1024
|
+
list,
|
|
1025
|
+
position,
|
|
1026
|
+
origin,
|
|
1027
|
+
});
|
|
1028
|
+
};
|
|
1029
|
+
if (asChild && children) {
|
|
1030
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productLink, customElement: children, customElementProps: { onClick: handleClick } }));
|
|
1031
|
+
}
|
|
1032
|
+
return (_jsx("a", { ref: ref, href: `/${slug}`, onClick: handleClick, className: className, "data-testid": TestIds.productLink, children: children }));
|
|
1033
|
+
} }));
|
|
1034
|
+
});
|
|
955
1035
|
/**
|
|
956
1036
|
* Helper function to determine stock status and label based on availability and can-pre-order settings
|
|
957
1037
|
*/
|
|
@@ -966,12 +1046,12 @@ function getStockStatusMessage(inStock, isPreOrderEnabled, availabilityStatus, l
|
|
|
966
1046
|
// Handle stock status based on availability
|
|
967
1047
|
if (inStock) {
|
|
968
1048
|
switch (availabilityStatus) {
|
|
969
|
-
case InventoryAvailabilityStatus.IN_STOCK:
|
|
1049
|
+
case productsV3.InventoryAvailabilityStatus.IN_STOCK:
|
|
970
1050
|
return {
|
|
971
1051
|
status: 'in-stock',
|
|
972
1052
|
label: labels.inStock,
|
|
973
1053
|
};
|
|
974
|
-
case InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
|
|
1054
|
+
case productsV3.InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
|
|
975
1055
|
return {
|
|
976
1056
|
status: 'limited-stock',
|
|
977
1057
|
label: labels.limitedStock,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { V3Product } from '@wix/auto_sdk_stores_products-v-3';
|
|
2
1
|
import { Sort as SortPrimitive, GenericListTotalsRenderProps, GenericListLoadMoreRenderProps, ListVariant, GenericListRepeaterRenderProps } from '@wix/headless-components/react';
|
|
3
2
|
import React from 'react';
|
|
4
3
|
import type { ProductsListServiceConfig } from '../services/products-list-service.js';
|
|
@@ -9,7 +8,7 @@ import { AsChildChildren } from '@wix/headless-utils/react';
|
|
|
9
8
|
*/
|
|
10
9
|
export interface ProductListRootProps {
|
|
11
10
|
children: React.ReactNode;
|
|
12
|
-
products?: V3Product[];
|
|
11
|
+
products?: productsV3.V3Product[];
|
|
13
12
|
productsListConfig?: ProductsListServiceConfig;
|
|
14
13
|
className?: string;
|
|
15
14
|
variant?: ListVariant;
|
|
@@ -95,7 +94,7 @@ export declare const Products: React.ForwardRefExoticComponent<ProductsProps & R
|
|
|
95
94
|
/**
|
|
96
95
|
* Render props for ProductRepeater asChild pattern
|
|
97
96
|
*/
|
|
98
|
-
export type ProductRepeaterRenderProps = GenericListRepeaterRenderProps<V3Product>;
|
|
97
|
+
export type ProductRepeaterRenderProps = GenericListRepeaterRenderProps<productsV3.V3Product>;
|
|
99
98
|
/**
|
|
100
99
|
* Props for ProductList ProductRepeater component
|
|
101
100
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProductServiceConfig } from '../../services/product-service.js';
|
|
2
|
-
import
|
|
2
|
+
import { productsV3 } from '@wix/stores';
|
|
3
3
|
export interface RootProps {
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
productServiceConfig: ProductServiceConfig;
|
|
@@ -81,18 +81,18 @@ export interface ProductDescriptionProps {
|
|
|
81
81
|
*/
|
|
82
82
|
export interface ProductDescriptionRenderProps {
|
|
83
83
|
/** Product description using the RICOS (Rich Content Object) format. See https://dev.wix.com/docs/ricos/api-reference/ricos-document */
|
|
84
|
-
description: NonNullable<V3Product['description']>;
|
|
84
|
+
description: NonNullable<productsV3.V3Product['description']>;
|
|
85
85
|
/** Product description with plain html */
|
|
86
|
-
plainDescription: NonNullable<V3Product['plainDescription']>;
|
|
86
|
+
plainDescription: NonNullable<productsV3.V3Product['plainDescription']>;
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
89
|
* Render props for ProductDescription component
|
|
90
90
|
*/
|
|
91
91
|
export interface ProductDescriptionRenderProps {
|
|
92
92
|
/** Product description using the RICOS (Rich Content Object) format. See https://dev.wix.com/docs/ricos/api-reference/ricos-document */
|
|
93
|
-
description: NonNullable<V3Product['description']>;
|
|
93
|
+
description: NonNullable<productsV3.V3Product['description']>;
|
|
94
94
|
/** Product description with plain html */
|
|
95
|
-
plainDescription: NonNullable<V3Product['plainDescription']>;
|
|
95
|
+
plainDescription: NonNullable<productsV3.V3Product['plainDescription']>;
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
98
98
|
* Headless component for product description display
|
|
@@ -129,15 +129,15 @@ export interface ProductMediaProps {
|
|
|
129
129
|
children: (props: ProductMediaRenderProps) => React.ReactNode;
|
|
130
130
|
}
|
|
131
131
|
export interface ProductMediaRenderProps {
|
|
132
|
-
mediaItems: ProductMedia[];
|
|
133
|
-
mainMedia?: ProductMedia;
|
|
132
|
+
mediaItems: productsV3.ProductMedia[];
|
|
133
|
+
mainMedia?: productsV3.ProductMedia;
|
|
134
134
|
}
|
|
135
135
|
export declare function Media(props: ProductMediaProps): import("react").ReactNode;
|
|
136
136
|
export interface ProductProps {
|
|
137
137
|
children: (props: ProductRenderProps) => React.ReactNode;
|
|
138
138
|
}
|
|
139
139
|
export interface ProductRenderProps {
|
|
140
|
-
product: V3Product;
|
|
140
|
+
product: productsV3.V3Product;
|
|
141
141
|
}
|
|
142
142
|
export declare function Content(props: ProductProps): import("react").ReactNode;
|
|
143
143
|
export interface LoadingProps {
|