@constructor-io/constructorio-ui-plp 1.6.4 → 1.6.6
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/dist/constructorio-ui-plp-bundled.js +13 -13
- package/lib/cjs/components/ProductCard/ProductCard.js +8 -5
- package/lib/cjs/components/ProductSwatch/ProductSwatch.js +5 -2
- package/lib/cjs/constants.js +0 -1
- package/lib/cjs/hooks/useCioPlp.js +3 -2
- package/lib/cjs/hooks/useProduct.js +13 -9
- package/lib/cjs/utils/dataAttributeHelpers.js +77 -17
- package/lib/cjs/utils/itemFieldGetters.js +5 -1
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/ProductCard/ProductCard.js +9 -6
- package/lib/mjs/components/ProductSwatch/ProductSwatch.js +6 -3
- package/lib/mjs/constants.js +0 -1
- package/lib/mjs/hooks/useCioPlp.js +3 -2
- package/lib/mjs/hooks/useProduct.js +7 -2
- package/lib/mjs/utils/dataAttributeHelpers.js +73 -16
- package/lib/mjs/utils/itemFieldGetters.js +3 -0
- package/lib/mjs/version.js +1 -1
- package/lib/types/hooks/useCioPlp.d.ts +1 -1
- package/lib/types/types.d.ts +3 -2
- package/lib/types/utils/dataAttributeHelpers.d.ts +37 -3
- package/lib/types/utils/itemFieldGetters.d.ts +1 -0
- package/lib/types/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -30,7 +30,10 @@ function ProductCard(props) {
|
|
|
30
30
|
const onAddToCart = (0, callbacks_1.useOnAddToCart)(client, state.callbacks.onAddToCart);
|
|
31
31
|
const { formatPrice } = state.formatters;
|
|
32
32
|
const onClick = (0, callbacks_1.useOnProductCardClick)(client, state.callbacks.onProductCardClick);
|
|
33
|
-
const
|
|
33
|
+
const cnstrcDataAttrs = (0, utils_1.getProductCardCnstrcDataAttributes)(productInfo, {
|
|
34
|
+
labels: item.labels,
|
|
35
|
+
});
|
|
36
|
+
const addToCartBtnAttrs = (0, utils_1.getConversionButtonCnstrcDataAttributes)('add_to_cart');
|
|
34
37
|
const handleRolloverImageState = (isShown) => {
|
|
35
38
|
var _a;
|
|
36
39
|
setIsRolloverImageShown(isShown);
|
|
@@ -63,9 +66,9 @@ function ProductCard(props) {
|
|
|
63
66
|
onMouseEnter,
|
|
64
67
|
onMouseLeave,
|
|
65
68
|
isRolloverImageShown,
|
|
66
|
-
productCardCnstrcDataAttributes:
|
|
67
|
-
}, override: children, htmlOverride: (_a = state.renderOverrides.productCard) === null || _a === void 0 ? void 0 : _a.renderHtml, topLevelAttributes: Object.assign(Object.assign({},
|
|
68
|
-
react_1.default.createElement("a", Object.assign({},
|
|
69
|
+
productCardCnstrcDataAttributes: cnstrcDataAttrs,
|
|
70
|
+
}, override: children, htmlOverride: (_a = state.renderOverrides.productCard) === null || _a === void 0 ? void 0 : _a.renderHtml, topLevelAttributes: Object.assign(Object.assign({}, cnstrcDataAttrs), { className: 'cio-product-card' }) },
|
|
71
|
+
react_1.default.createElement("a", Object.assign({}, cnstrcDataAttrs, { className: 'cio-product-card', href: itemUrl, ref: cardRef, onClick: (e) => { var _a; return onClick(e, item, (_a = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _a === void 0 ? void 0 : _a.variationId); } }),
|
|
69
72
|
react_1.default.createElement("div", { className: 'cio-image-container', onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave },
|
|
70
73
|
react_1.default.createElement("img", { alt: itemName, src: itemImageUrl, className: 'cio-image' }),
|
|
71
74
|
rolloverImage && (react_1.default.createElement("img", { alt: `${itemName} rollover`, src: rolloverImage, loading: 'lazy', className: (0, utils_1.concatStyles)('cio-image cio-rollover-image', isRolloverImageShown && 'is-active') }))),
|
|
@@ -75,6 +78,6 @@ function ProductCard(props) {
|
|
|
75
78
|
Number(itemPrice) >= 0 && (react_1.default.createElement("div", { className: (0, utils_1.concatStyles)('cio-item-price', hasSalePrice && 'cio-item-price-strikethrough') }, formatPrice(itemPrice)))),
|
|
76
79
|
react_1.default.createElement("div", { className: 'cio-item-name' }, itemName),
|
|
77
80
|
productSwatch && react_1.default.createElement(ProductSwatch_1.default, { swatchObject: productSwatch })),
|
|
78
|
-
react_1.default.createElement("button", { className: 'cio-add-to-cart-button', type: 'button', onClick: (e) => { var _a; return onAddToCart(e, item, itemPrice, (_a = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _a === void 0 ? void 0 : _a.variationId); } }, "Add to Cart"))));
|
|
81
|
+
react_1.default.createElement("button", Object.assign({}, addToCartBtnAttrs, { className: 'cio-add-to-cart-button', type: 'button', onClick: (e) => { var _a; return onAddToCart(e, item, itemPrice, (_a = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _a === void 0 ? void 0 : _a.variationId); } }), "Add to Cart"))));
|
|
79
82
|
}
|
|
80
83
|
exports.default = ProductCard;
|
|
@@ -34,9 +34,12 @@ function ProductSwatch(props) {
|
|
|
34
34
|
const color = (0, utils_1.isHexColor)(swatch === null || swatch === void 0 ? void 0 : swatch.swatchPreview)
|
|
35
35
|
? swatch === null || swatch === void 0 ? void 0 : swatch.swatchPreview
|
|
36
36
|
: `url(${swatch === null || swatch === void 0 ? void 0 : swatch.swatchPreview})`;
|
|
37
|
-
|
|
37
|
+
const variationIdAttr = {
|
|
38
|
+
[utils_1.cnstrcDataAttrs.common.variationId]: swatch.variationId,
|
|
39
|
+
};
|
|
40
|
+
return (react_1.default.createElement("button", Object.assign({ type: 'button', key: swatch.variationId, "data-testid": `cio-swatch-${swatch.variationId}`, className: 'cio-swatch-button cio-swatch-item', onClick: (e) => swatchClickHandler(e, swatch), style: {
|
|
38
41
|
background: color,
|
|
39
|
-
} }, isSelected && (react_1.default.createElement("div", {
|
|
42
|
+
} }, variationIdAttr), isSelected && (react_1.default.createElement("div", Object.assign({ className: 'cio-swatch-selected', style: { outline: `3px solid ${color}` } }, variationIdAttr)))));
|
|
40
43
|
}))))));
|
|
41
44
|
}
|
|
42
45
|
exports.default = ProductSwatch;
|
package/lib/cjs/constants.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EMITTED_EVENTS = exports.DEMO_API_KEY = void 0;
|
|
4
|
-
// eslint-disable-next-line import/prefer-default-export
|
|
5
4
|
exports.DEMO_API_KEY = 'key_M57QS8SMPdLdLx4x';
|
|
6
5
|
exports.EMITTED_EVENTS = {
|
|
7
6
|
PRODUCT_CARD_IMAGE_ROLLOVER: 'cio.ui-plp.productCardImageRollover',
|
|
@@ -69,14 +69,15 @@ function useCioPlp(props = {}) {
|
|
|
69
69
|
const pagination = (0, usePagination_1.default)(Object.assign({ totalNumResults }, paginationConfigs));
|
|
70
70
|
const groups = (0, useGroups_1.default)(Object.assign({ groups: rawGroups }, groupsConfigs));
|
|
71
71
|
const data = isSearchPage ? search.data : browse.data;
|
|
72
|
-
const
|
|
72
|
+
const status = isSearchPage ? search.status : browse.status;
|
|
73
|
+
const plpContainerCnstrcDataAttributes = (0, utils_1.getPlpContainerCnstrcDataAttributes)(data, requestConfigs, status === 'fetching');
|
|
73
74
|
return {
|
|
74
75
|
isSearchPage,
|
|
75
76
|
isBrowsePage,
|
|
76
77
|
searchQuery: search.query,
|
|
77
78
|
browseFilterName: browse.filterName,
|
|
78
79
|
browseFilterValue: browse.filterValue,
|
|
79
|
-
status
|
|
80
|
+
status,
|
|
80
81
|
data,
|
|
81
82
|
refetch,
|
|
82
83
|
filters,
|
|
@@ -6,7 +6,7 @@ const useCioPlpContext_1 = require("./useCioPlpContext");
|
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
const itemFieldGetters_1 = require("../utils/itemFieldGetters");
|
|
8
8
|
const useProductInfo = ({ item }) => {
|
|
9
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
9
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
10
10
|
const state = (0, useCioPlpContext_1.useCioPlpContext)();
|
|
11
11
|
const productSwatch = (0, useProductSwatch_1.default)({ item });
|
|
12
12
|
if (!item.data || !item.itemId || !item.itemName) {
|
|
@@ -15,18 +15,22 @@ const useProductInfo = ({ item }) => {
|
|
|
15
15
|
const getPrice = (0, utils_1.tryCatchify)(((_a = state === null || state === void 0 ? void 0 : state.itemFieldGetters) === null || _a === void 0 ? void 0 : _a.getPrice) || itemFieldGetters_1.getPrice);
|
|
16
16
|
const getSalePrice = (0, utils_1.tryCatchify)(((_b = state === null || state === void 0 ? void 0 : state.itemFieldGetters) === null || _b === void 0 ? void 0 : _b.getSalePrice) || itemFieldGetters_1.getSalePrice);
|
|
17
17
|
const getRolloverImage = (0, utils_1.tryCatchify)(((_c = state === null || state === void 0 ? void 0 : state.itemFieldGetters) === null || _c === void 0 ? void 0 : _c.getRolloverImage) || itemFieldGetters_1.getRolloverImage);
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const getItemUrl = (0, utils_1.tryCatchify)(((_d = state === null || state === void 0 ? void 0 : state.itemFieldGetters) === null || _d === void 0 ? void 0 : _d.getItemUrl) || itemFieldGetters_1.getItemUrl);
|
|
19
|
+
const itemName = ((_e = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _e === void 0 ? void 0 : _e.itemName) || item.itemName;
|
|
20
|
+
const itemPrice = ((_f = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _f === void 0 ? void 0 : _f.price) || getPrice(item);
|
|
21
|
+
const itemImageUrl = ((_g = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _g === void 0 ? void 0 : _g.imageUrl) || item.imageUrl;
|
|
22
|
+
// Get href - merge variation URL into item if variation is selected
|
|
23
|
+
const itemWithVariationUrl = (productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation)
|
|
24
|
+
? Object.assign(Object.assign({}, item), { url: productSwatch.selectedVariation.url }) : item;
|
|
25
|
+
const itemUrl = getItemUrl(itemWithVariationUrl) || ((_h = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _h === void 0 ? void 0 : _h.url);
|
|
26
|
+
const variationId = (_j = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _j === void 0 ? void 0 : _j.variationId;
|
|
27
|
+
let rolloverImage = (_k = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _k === void 0 ? void 0 : _k.rolloverImage;
|
|
24
28
|
// Fallback to item's rollover image if all variations don't have a rollover image
|
|
25
|
-
if (!rolloverImage && ((
|
|
29
|
+
if (!rolloverImage && ((_l = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.swatchList) === null || _l === void 0 ? void 0 : _l.every((swatch) => !swatch.rolloverImage))) {
|
|
26
30
|
rolloverImage = getRolloverImage(item);
|
|
27
31
|
}
|
|
28
32
|
const { itemId } = item;
|
|
29
|
-
let salePrice = ((
|
|
33
|
+
let salePrice = ((_m = productSwatch === null || productSwatch === void 0 ? void 0 : productSwatch.selectedVariation) === null || _m === void 0 ? void 0 : _m.salePrice) || getSalePrice(item);
|
|
30
34
|
let hasSalePrice = true;
|
|
31
35
|
if (!(0, utils_1.isValidSalePrice)(salePrice, itemPrice)) {
|
|
32
36
|
salePrice = undefined;
|
|
@@ -1,42 +1,85 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPlpContainerCnstrcDataAttributes = exports.getProductCardCnstrcDataAttributes = void 0;
|
|
3
|
+
exports.getConversionButtonCnstrcDataAttributes = exports.getPlpContainerCnstrcDataAttributes = exports.getProductCardCnstrcDataAttributes = exports.cnstrcDataAttrs = void 0;
|
|
4
4
|
const typeHelpers_1 = require("./typeHelpers");
|
|
5
5
|
const requestConfigsHelpers_1 = require("./requestConfigsHelpers");
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
exports.cnstrcDataAttrs = {
|
|
7
|
+
common: {
|
|
8
|
+
itemId: 'data-cnstrc-item-id',
|
|
9
|
+
itemName: 'data-cnstrc-item-name',
|
|
10
|
+
itemPrice: 'data-cnstrc-item-price',
|
|
11
|
+
variationId: 'data-cnstrc-item-variation-id',
|
|
12
|
+
numResults: 'data-cnstrc-num-results',
|
|
13
|
+
conversionBtn: 'data-cnstrc-btn',
|
|
14
|
+
resultId: 'data-cnstrc-result-id',
|
|
15
|
+
section: 'data-cnstrc-section',
|
|
16
|
+
zeroResults: 'data-cnstrc-zero-result',
|
|
17
|
+
slCampaignId: 'data-cnstrc-sl-campaign-id',
|
|
18
|
+
slCampaignOwner: 'data-cnstrc-sl-campaign-owner',
|
|
19
|
+
},
|
|
20
|
+
search: {
|
|
21
|
+
searchContainer: 'data-cnstrc-search',
|
|
22
|
+
searchTerm: 'data-cnstrc-search-term',
|
|
23
|
+
},
|
|
24
|
+
browse: {
|
|
25
|
+
browseContainer: 'data-cnstrc-browse',
|
|
26
|
+
filterName: 'data-cnstrc-filter-name',
|
|
27
|
+
filterValue: 'data-cnstrc-filter-value',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
function getProductCardCnstrcDataAttributes(productInfo, options) {
|
|
31
|
+
var _a, _b;
|
|
8
32
|
const { itemId, itemPrice, itemName, variationId } = productInfo;
|
|
9
|
-
dataCnstrc = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
'data-cnstrc-item-price': itemPrice,
|
|
13
|
-
'data-cnstrc-item-variation-id': variationId,
|
|
33
|
+
const dataCnstrc = {
|
|
34
|
+
[exports.cnstrcDataAttrs.common.itemId]: itemId,
|
|
35
|
+
[exports.cnstrcDataAttrs.common.itemName]: itemName,
|
|
14
36
|
};
|
|
37
|
+
// Only include variation ID if it exists
|
|
38
|
+
if (variationId) {
|
|
39
|
+
dataCnstrc[exports.cnstrcDataAttrs.common.variationId] = variationId;
|
|
40
|
+
}
|
|
41
|
+
// Only include price if it exists
|
|
42
|
+
if (itemPrice !== undefined && itemPrice !== null) {
|
|
43
|
+
dataCnstrc[exports.cnstrcDataAttrs.common.itemPrice] = itemPrice;
|
|
44
|
+
}
|
|
45
|
+
// Add sponsored listing data if available
|
|
46
|
+
if ((_a = options === null || options === void 0 ? void 0 : options.labels) === null || _a === void 0 ? void 0 : _a.sl_campaign_id) {
|
|
47
|
+
dataCnstrc[exports.cnstrcDataAttrs.common.slCampaignId] = String(options.labels.sl_campaign_id);
|
|
48
|
+
}
|
|
49
|
+
if ((_b = options === null || options === void 0 ? void 0 : options.labels) === null || _b === void 0 ? void 0 : _b.sl_campaign_owner) {
|
|
50
|
+
dataCnstrc[exports.cnstrcDataAttrs.common.slCampaignOwner] = String(options.labels.sl_campaign_owner);
|
|
51
|
+
}
|
|
15
52
|
return dataCnstrc;
|
|
16
53
|
}
|
|
17
54
|
exports.getProductCardCnstrcDataAttributes = getProductCardCnstrcDataAttributes;
|
|
18
|
-
function getPlpContainerCnstrcDataAttributes(data, requestConfigs) {
|
|
55
|
+
function getPlpContainerCnstrcDataAttributes(data, requestConfigs, isLoading = false) {
|
|
56
|
+
var _a, _b;
|
|
19
57
|
if (!data || (!(0, typeHelpers_1.isPlpSearchDataResults)(data) && !(0, typeHelpers_1.isPlpBrowseDataResults)(data)))
|
|
20
58
|
return {};
|
|
21
59
|
const { filterName, filterValue } = requestConfigs;
|
|
22
60
|
const pageType = (0, requestConfigsHelpers_1.getPageType)(requestConfigs);
|
|
61
|
+
const isZeroResults = data.response.totalNumResults === 0;
|
|
23
62
|
let dataCnstrc = {};
|
|
24
63
|
switch (pageType) {
|
|
25
64
|
case 'browse':
|
|
26
65
|
dataCnstrc = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
66
|
+
[exports.cnstrcDataAttrs.browse.browseContainer]: true,
|
|
67
|
+
[exports.cnstrcDataAttrs.common.numResults]: data.response.totalNumResults,
|
|
68
|
+
[exports.cnstrcDataAttrs.common.resultId]: data.resultId,
|
|
69
|
+
[exports.cnstrcDataAttrs.browse.filterName]: filterName,
|
|
70
|
+
[exports.cnstrcDataAttrs.browse.filterValue]: filterValue,
|
|
32
71
|
};
|
|
33
72
|
break;
|
|
34
73
|
case 'search':
|
|
35
74
|
dataCnstrc = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
75
|
+
[exports.cnstrcDataAttrs.search.searchContainer]: true,
|
|
76
|
+
[exports.cnstrcDataAttrs.common.resultId]: data.resultId,
|
|
77
|
+
[exports.cnstrcDataAttrs.common.numResults]: data.response.totalNumResults,
|
|
39
78
|
};
|
|
79
|
+
// Add search term
|
|
80
|
+
if ((_a = data.request) === null || _a === void 0 ? void 0 : _a.term) {
|
|
81
|
+
dataCnstrc[exports.cnstrcDataAttrs.search.searchTerm] = data.request.term;
|
|
82
|
+
}
|
|
40
83
|
break;
|
|
41
84
|
case 'unknown':
|
|
42
85
|
dataCnstrc = {};
|
|
@@ -44,6 +87,23 @@ function getPlpContainerCnstrcDataAttributes(data, requestConfigs) {
|
|
|
44
87
|
default:
|
|
45
88
|
break;
|
|
46
89
|
}
|
|
90
|
+
// Add common conditional attributes (applies to both search and browse)
|
|
91
|
+
if (pageType === 'search' || pageType === 'browse') {
|
|
92
|
+
// Add zero-result attribute only if not loading and there are no results
|
|
93
|
+
if (isZeroResults && !isLoading) {
|
|
94
|
+
dataCnstrc[exports.cnstrcDataAttrs.common.zeroResults] = true;
|
|
95
|
+
}
|
|
96
|
+
// Add section if it's not "Products"
|
|
97
|
+
if (((_b = data.request) === null || _b === void 0 ? void 0 : _b.section) && data.request.section !== 'Products') {
|
|
98
|
+
dataCnstrc[exports.cnstrcDataAttrs.common.section] = data.request.section;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
47
101
|
return dataCnstrc;
|
|
48
102
|
}
|
|
49
103
|
exports.getPlpContainerCnstrcDataAttributes = getPlpContainerCnstrcDataAttributes;
|
|
104
|
+
function getConversionButtonCnstrcDataAttributes(conversionType) {
|
|
105
|
+
return {
|
|
106
|
+
[exports.cnstrcDataAttrs.common.conversionBtn]: conversionType,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
exports.getConversionButtonCnstrcDataAttributes = getConversionButtonCnstrcDataAttributes;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getIsHiddenGroupField = exports.getSwatchPreview = exports.getSwatches = exports.getRolloverImage = exports.getSalePrice = exports.getPrice = void 0;
|
|
3
|
+
exports.getItemUrl = exports.getIsHiddenGroupField = exports.getSwatchPreview = exports.getSwatches = exports.getRolloverImage = exports.getSalePrice = exports.getPrice = void 0;
|
|
4
4
|
// eslint-disable-next-line import/prefer-default-export
|
|
5
5
|
function getPrice(item) {
|
|
6
6
|
return item.data.price;
|
|
@@ -45,3 +45,7 @@ function getIsHiddenGroupField(group) {
|
|
|
45
45
|
return (_a = group === null || group === void 0 ? void 0 : group.data) === null || _a === void 0 ? void 0 : _a.cio_plp_hidden;
|
|
46
46
|
}
|
|
47
47
|
exports.getIsHiddenGroupField = getIsHiddenGroupField;
|
|
48
|
+
function getItemUrl(item) {
|
|
49
|
+
return item === null || item === void 0 ? void 0 : item.url;
|
|
50
|
+
}
|
|
51
|
+
exports.getItemUrl = getItemUrl;
|
package/lib/cjs/version.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useCioPlpContext } from '../../hooks/useCioPlpContext';
|
|
|
3
3
|
import { useOnAddToCart, useOnProductCardClick } from '../../hooks/callbacks';
|
|
4
4
|
import ProductSwatch from '../ProductSwatch';
|
|
5
5
|
import useProductInfo from '../../hooks/useProduct';
|
|
6
|
-
import { concatStyles, getProductCardCnstrcDataAttributes } from '../../utils';
|
|
6
|
+
import { concatStyles, getProductCardCnstrcDataAttributes, getConversionButtonCnstrcDataAttributes, } from '../../utils';
|
|
7
7
|
import RenderPropsWrapper from '../RenderPropsWrapper/RenderPropsWrapper';
|
|
8
8
|
import { EMITTED_EVENTS } from '../../constants';
|
|
9
9
|
/**
|
|
@@ -26,7 +26,10 @@ export default function ProductCard(props) {
|
|
|
26
26
|
const onAddToCart = useOnAddToCart(client, state.callbacks.onAddToCart);
|
|
27
27
|
const { formatPrice } = state.formatters;
|
|
28
28
|
const onClick = useOnProductCardClick(client, state.callbacks.onProductCardClick);
|
|
29
|
-
const
|
|
29
|
+
const cnstrcDataAttrs = getProductCardCnstrcDataAttributes(productInfo, {
|
|
30
|
+
labels: item.labels,
|
|
31
|
+
});
|
|
32
|
+
const addToCartBtnAttrs = getConversionButtonCnstrcDataAttributes('add_to_cart');
|
|
30
33
|
const handleRolloverImageState = (isShown) => {
|
|
31
34
|
setIsRolloverImageShown(isShown);
|
|
32
35
|
if (isShown && rolloverImage) {
|
|
@@ -58,9 +61,9 @@ export default function ProductCard(props) {
|
|
|
58
61
|
onMouseEnter,
|
|
59
62
|
onMouseLeave,
|
|
60
63
|
isRolloverImageShown,
|
|
61
|
-
productCardCnstrcDataAttributes:
|
|
62
|
-
}, override: children, htmlOverride: state.renderOverrides.productCard?.renderHtml, topLevelAttributes: { ...
|
|
63
|
-
React.createElement("a", { ...
|
|
64
|
+
productCardCnstrcDataAttributes: cnstrcDataAttrs,
|
|
65
|
+
}, override: children, htmlOverride: state.renderOverrides.productCard?.renderHtml, topLevelAttributes: { ...cnstrcDataAttrs, className: 'cio-product-card' } },
|
|
66
|
+
React.createElement("a", { ...cnstrcDataAttrs, className: 'cio-product-card', href: itemUrl, ref: cardRef, onClick: (e) => onClick(e, item, productSwatch?.selectedVariation?.variationId) },
|
|
64
67
|
React.createElement("div", { className: 'cio-image-container', onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave },
|
|
65
68
|
React.createElement("img", { alt: itemName, src: itemImageUrl, className: 'cio-image' }),
|
|
66
69
|
rolloverImage && (React.createElement("img", { alt: `${itemName} rollover`, src: rolloverImage, loading: 'lazy', className: concatStyles('cio-image cio-rollover-image', isRolloverImageShown && 'is-active') }))),
|
|
@@ -70,5 +73,5 @@ export default function ProductCard(props) {
|
|
|
70
73
|
Number(itemPrice) >= 0 && (React.createElement("div", { className: concatStyles('cio-item-price', hasSalePrice && 'cio-item-price-strikethrough') }, formatPrice(itemPrice)))),
|
|
71
74
|
React.createElement("div", { className: 'cio-item-name' }, itemName),
|
|
72
75
|
productSwatch && React.createElement(ProductSwatch, { swatchObject: productSwatch })),
|
|
73
|
-
React.createElement("button", { className: 'cio-add-to-cart-button', type: 'button', onClick: (e) => onAddToCart(e, item, itemPrice, productSwatch?.selectedVariation?.variationId) }, "Add to Cart"))));
|
|
76
|
+
React.createElement("button", { ...addToCartBtnAttrs, className: 'cio-add-to-cart-button', type: 'button', onClick: (e) => onAddToCart(e, item, itemPrice, productSwatch?.selectedVariation?.variationId) }, "Add to Cart"))));
|
|
74
77
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { useCioPlpContext } from '../../hooks/useCioPlpContext';
|
|
4
|
-
import { isHexColor } from '../../utils';
|
|
4
|
+
import { isHexColor, cnstrcDataAttrs } from '../../utils';
|
|
5
5
|
export default function ProductSwatch(props) {
|
|
6
6
|
const context = useCioPlpContext();
|
|
7
7
|
const { swatchObject, children } = props;
|
|
@@ -31,8 +31,11 @@ export default function ProductSwatch(props) {
|
|
|
31
31
|
const color = isHexColor(swatch?.swatchPreview)
|
|
32
32
|
? swatch?.swatchPreview
|
|
33
33
|
: `url(${swatch?.swatchPreview})`;
|
|
34
|
-
|
|
34
|
+
const variationIdAttr = {
|
|
35
|
+
[cnstrcDataAttrs.common.variationId]: swatch.variationId,
|
|
36
|
+
};
|
|
37
|
+
return (React.createElement("button", { type: 'button', key: swatch.variationId, "data-testid": `cio-swatch-${swatch.variationId}`, className: 'cio-swatch-button cio-swatch-item', onClick: (e) => swatchClickHandler(e, swatch), style: {
|
|
35
38
|
background: color,
|
|
36
|
-
} }, isSelected && (React.createElement("div", {
|
|
39
|
+
}, ...variationIdAttr }, isSelected && (React.createElement("div", { className: 'cio-swatch-selected', style: { outline: `3px solid ${color}` }, ...variationIdAttr }))));
|
|
37
40
|
}))))));
|
|
38
41
|
}
|
package/lib/mjs/constants.js
CHANGED
|
@@ -65,14 +65,15 @@ export default function useCioPlp(props = {}) {
|
|
|
65
65
|
const pagination = usePagination({ totalNumResults, ...paginationConfigs });
|
|
66
66
|
const groups = useGroups({ groups: rawGroups, ...groupsConfigs });
|
|
67
67
|
const data = isSearchPage ? search.data : browse.data;
|
|
68
|
-
const
|
|
68
|
+
const status = isSearchPage ? search.status : browse.status;
|
|
69
|
+
const plpContainerCnstrcDataAttributes = getPlpContainerCnstrcDataAttributes(data, requestConfigs, status === 'fetching');
|
|
69
70
|
return {
|
|
70
71
|
isSearchPage,
|
|
71
72
|
isBrowsePage,
|
|
72
73
|
searchQuery: search.query,
|
|
73
74
|
browseFilterName: browse.filterName,
|
|
74
75
|
browseFilterValue: browse.filterValue,
|
|
75
|
-
status
|
|
76
|
+
status,
|
|
76
77
|
data,
|
|
77
78
|
refetch,
|
|
78
79
|
filters,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import useProductSwatch from './useProductSwatch';
|
|
2
2
|
import { useCioPlpContext } from './useCioPlpContext';
|
|
3
3
|
import { tryCatchify, isValidSalePrice } from '../utils';
|
|
4
|
-
import { getPrice as defaultGetPrice, getSalePrice as defaultGetSalePrice, getRolloverImage as defaultGetRolloverImage, } from '../utils/itemFieldGetters';
|
|
4
|
+
import { getPrice as defaultGetPrice, getSalePrice as defaultGetSalePrice, getRolloverImage as defaultGetRolloverImage, getItemUrl as defaultGetItemUrl, } from '../utils/itemFieldGetters';
|
|
5
5
|
const useProductInfo = ({ item }) => {
|
|
6
6
|
const state = useCioPlpContext();
|
|
7
7
|
const productSwatch = useProductSwatch({ item });
|
|
@@ -11,10 +11,15 @@ const useProductInfo = ({ item }) => {
|
|
|
11
11
|
const getPrice = tryCatchify(state?.itemFieldGetters?.getPrice || defaultGetPrice);
|
|
12
12
|
const getSalePrice = tryCatchify(state?.itemFieldGetters?.getSalePrice || defaultGetSalePrice);
|
|
13
13
|
const getRolloverImage = tryCatchify(state?.itemFieldGetters?.getRolloverImage || defaultGetRolloverImage);
|
|
14
|
+
const getItemUrl = tryCatchify(state?.itemFieldGetters?.getItemUrl || defaultGetItemUrl);
|
|
14
15
|
const itemName = productSwatch?.selectedVariation?.itemName || item.itemName;
|
|
15
16
|
const itemPrice = productSwatch?.selectedVariation?.price || getPrice(item);
|
|
16
17
|
const itemImageUrl = productSwatch?.selectedVariation?.imageUrl || item.imageUrl;
|
|
17
|
-
|
|
18
|
+
// Get href - merge variation URL into item if variation is selected
|
|
19
|
+
const itemWithVariationUrl = productSwatch?.selectedVariation
|
|
20
|
+
? { ...item, url: productSwatch.selectedVariation.url }
|
|
21
|
+
: item;
|
|
22
|
+
const itemUrl = getItemUrl(itemWithVariationUrl) || productSwatch?.selectedVariation?.url;
|
|
18
23
|
const variationId = productSwatch?.selectedVariation?.variationId;
|
|
19
24
|
let rolloverImage = productSwatch?.selectedVariation?.rolloverImage;
|
|
20
25
|
// Fallback to item's rollover image if all variations don't have a rollover image
|
|
@@ -1,38 +1,79 @@
|
|
|
1
1
|
import { isPlpBrowseDataResults, isPlpSearchDataResults } from './typeHelpers';
|
|
2
2
|
import { getPageType } from './requestConfigsHelpers';
|
|
3
|
-
export
|
|
4
|
-
|
|
3
|
+
export const cnstrcDataAttrs = {
|
|
4
|
+
common: {
|
|
5
|
+
itemId: 'data-cnstrc-item-id',
|
|
6
|
+
itemName: 'data-cnstrc-item-name',
|
|
7
|
+
itemPrice: 'data-cnstrc-item-price',
|
|
8
|
+
variationId: 'data-cnstrc-item-variation-id',
|
|
9
|
+
numResults: 'data-cnstrc-num-results',
|
|
10
|
+
conversionBtn: 'data-cnstrc-btn',
|
|
11
|
+
resultId: 'data-cnstrc-result-id',
|
|
12
|
+
section: 'data-cnstrc-section',
|
|
13
|
+
zeroResults: 'data-cnstrc-zero-result',
|
|
14
|
+
slCampaignId: 'data-cnstrc-sl-campaign-id',
|
|
15
|
+
slCampaignOwner: 'data-cnstrc-sl-campaign-owner',
|
|
16
|
+
},
|
|
17
|
+
search: {
|
|
18
|
+
searchContainer: 'data-cnstrc-search',
|
|
19
|
+
searchTerm: 'data-cnstrc-search-term',
|
|
20
|
+
},
|
|
21
|
+
browse: {
|
|
22
|
+
browseContainer: 'data-cnstrc-browse',
|
|
23
|
+
filterName: 'data-cnstrc-filter-name',
|
|
24
|
+
filterValue: 'data-cnstrc-filter-value',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export function getProductCardCnstrcDataAttributes(productInfo, options) {
|
|
5
28
|
const { itemId, itemPrice, itemName, variationId } = productInfo;
|
|
6
|
-
dataCnstrc = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'data-cnstrc-item-price': itemPrice,
|
|
10
|
-
'data-cnstrc-item-variation-id': variationId,
|
|
29
|
+
const dataCnstrc = {
|
|
30
|
+
[cnstrcDataAttrs.common.itemId]: itemId,
|
|
31
|
+
[cnstrcDataAttrs.common.itemName]: itemName,
|
|
11
32
|
};
|
|
33
|
+
// Only include variation ID if it exists
|
|
34
|
+
if (variationId) {
|
|
35
|
+
dataCnstrc[cnstrcDataAttrs.common.variationId] = variationId;
|
|
36
|
+
}
|
|
37
|
+
// Only include price if it exists
|
|
38
|
+
if (itemPrice !== undefined && itemPrice !== null) {
|
|
39
|
+
dataCnstrc[cnstrcDataAttrs.common.itemPrice] = itemPrice;
|
|
40
|
+
}
|
|
41
|
+
// Add sponsored listing data if available
|
|
42
|
+
if (options?.labels?.sl_campaign_id) {
|
|
43
|
+
dataCnstrc[cnstrcDataAttrs.common.slCampaignId] = String(options.labels.sl_campaign_id);
|
|
44
|
+
}
|
|
45
|
+
if (options?.labels?.sl_campaign_owner) {
|
|
46
|
+
dataCnstrc[cnstrcDataAttrs.common.slCampaignOwner] = String(options.labels.sl_campaign_owner);
|
|
47
|
+
}
|
|
12
48
|
return dataCnstrc;
|
|
13
49
|
}
|
|
14
|
-
export function getPlpContainerCnstrcDataAttributes(data, requestConfigs) {
|
|
50
|
+
export function getPlpContainerCnstrcDataAttributes(data, requestConfigs, isLoading = false) {
|
|
15
51
|
if (!data || (!isPlpSearchDataResults(data) && !isPlpBrowseDataResults(data)))
|
|
16
52
|
return {};
|
|
17
53
|
const { filterName, filterValue } = requestConfigs;
|
|
18
54
|
const pageType = getPageType(requestConfigs);
|
|
55
|
+
const isZeroResults = data.response.totalNumResults === 0;
|
|
19
56
|
let dataCnstrc = {};
|
|
20
57
|
switch (pageType) {
|
|
21
58
|
case 'browse':
|
|
22
59
|
dataCnstrc = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
60
|
+
[cnstrcDataAttrs.browse.browseContainer]: true,
|
|
61
|
+
[cnstrcDataAttrs.common.numResults]: data.response.totalNumResults,
|
|
62
|
+
[cnstrcDataAttrs.common.resultId]: data.resultId,
|
|
63
|
+
[cnstrcDataAttrs.browse.filterName]: filterName,
|
|
64
|
+
[cnstrcDataAttrs.browse.filterValue]: filterValue,
|
|
28
65
|
};
|
|
29
66
|
break;
|
|
30
67
|
case 'search':
|
|
31
68
|
dataCnstrc = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
69
|
+
[cnstrcDataAttrs.search.searchContainer]: true,
|
|
70
|
+
[cnstrcDataAttrs.common.resultId]: data.resultId,
|
|
71
|
+
[cnstrcDataAttrs.common.numResults]: data.response.totalNumResults,
|
|
35
72
|
};
|
|
73
|
+
// Add search term
|
|
74
|
+
if (data.request?.term) {
|
|
75
|
+
dataCnstrc[cnstrcDataAttrs.search.searchTerm] = data.request.term;
|
|
76
|
+
}
|
|
36
77
|
break;
|
|
37
78
|
case 'unknown':
|
|
38
79
|
dataCnstrc = {};
|
|
@@ -40,5 +81,21 @@ export function getPlpContainerCnstrcDataAttributes(data, requestConfigs) {
|
|
|
40
81
|
default:
|
|
41
82
|
break;
|
|
42
83
|
}
|
|
84
|
+
// Add common conditional attributes (applies to both search and browse)
|
|
85
|
+
if (pageType === 'search' || pageType === 'browse') {
|
|
86
|
+
// Add zero-result attribute only if not loading and there are no results
|
|
87
|
+
if (isZeroResults && !isLoading) {
|
|
88
|
+
dataCnstrc[cnstrcDataAttrs.common.zeroResults] = true;
|
|
89
|
+
}
|
|
90
|
+
// Add section if it's not "Products"
|
|
91
|
+
if (data.request?.section && data.request.section !== 'Products') {
|
|
92
|
+
dataCnstrc[cnstrcDataAttrs.common.section] = data.request.section;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
43
95
|
return dataCnstrc;
|
|
44
96
|
}
|
|
97
|
+
export function getConversionButtonCnstrcDataAttributes(conversionType) {
|
|
98
|
+
return {
|
|
99
|
+
[cnstrcDataAttrs.common.conversionBtn]: conversionType,
|
|
100
|
+
};
|
|
101
|
+
}
|
package/lib/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.6.
|
|
1
|
+
export default '1.6.6';
|
|
@@ -59,5 +59,5 @@ export default function useCioPlp(props?: UseCioPlpProps): {
|
|
|
59
59
|
optionsToRender: PlpItemGroup[];
|
|
60
60
|
setOptionsToRender: import("react").Dispatch<import("react").SetStateAction<PlpItemGroup[]>>;
|
|
61
61
|
};
|
|
62
|
-
plpContainerCnstrcDataAttributes:
|
|
62
|
+
plpContainerCnstrcDataAttributes: import("../types").CnstrcDataAttrs;
|
|
63
63
|
};
|
package/lib/types/types.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface ItemFieldGetters {
|
|
|
17
17
|
getSwatchPreview: (variation: Variation) => string;
|
|
18
18
|
getSwatches: (item: Item, retrievePrice: ItemFieldGetters['getPrice'], retrieveSwatchPreview: ItemFieldGetters['getSwatchPreview'], retrieveSalePrice: ItemFieldGetters['getSalePrice'], retrieveRolloverImage: ItemFieldGetters['getRolloverImage']) => SwatchItem[] | undefined;
|
|
19
19
|
getIsHiddenGroupField: (group: PlpItemGroup) => boolean | undefined;
|
|
20
|
+
getItemUrl: (item: Item) => string | undefined;
|
|
20
21
|
}
|
|
21
22
|
export interface Formatters {
|
|
22
23
|
formatPrice: (price?: number) => string;
|
|
@@ -75,7 +76,7 @@ export interface ProductCardRenderProps extends ProductCardProps {
|
|
|
75
76
|
/**
|
|
76
77
|
* Data Attributes to surface on parent div of product card.
|
|
77
78
|
*/
|
|
78
|
-
productCardCnstrcDataAttributes:
|
|
79
|
+
productCardCnstrcDataAttributes: CnstrcDataAttrs;
|
|
79
80
|
}
|
|
80
81
|
export type ProductCardProps = IncludeRenderProps<ProductCardBaseProps, ProductCardRenderProps>;
|
|
81
82
|
export interface RenderOverrides {
|
|
@@ -295,7 +296,7 @@ export interface PlpItemGroup {
|
|
|
295
296
|
children: Array<PlpItemGroup>;
|
|
296
297
|
parents: Pick<PlpItemGroup, 'groupId' | 'displayName'>[];
|
|
297
298
|
}
|
|
298
|
-
export type
|
|
299
|
+
export type CnstrcDataAttrs = Record<`data-cnstrc-${string}` | string, string | number | boolean>;
|
|
299
300
|
export type PropsWithChildren<P> = P & {
|
|
300
301
|
children?: ReactNode;
|
|
301
302
|
};
|
|
@@ -1,3 +1,37 @@
|
|
|
1
|
-
import { RequestConfigs, PlpSearchDataResults, PlpSearchDataRedirect, PlpBrowseData,
|
|
2
|
-
export
|
|
3
|
-
|
|
1
|
+
import { RequestConfigs, PlpSearchDataResults, PlpSearchDataRedirect, PlpBrowseData, CnstrcDataAttrs, ProductInfoObject } from '../types';
|
|
2
|
+
export interface ProductCardDataAttributeOptions {
|
|
3
|
+
labels?: {
|
|
4
|
+
sl_campaign_id?: string | number;
|
|
5
|
+
sl_campaign_owner?: string | number;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export declare const cnstrcDataAttrs: {
|
|
9
|
+
common: {
|
|
10
|
+
itemId: string;
|
|
11
|
+
itemName: string;
|
|
12
|
+
itemPrice: string;
|
|
13
|
+
variationId: string;
|
|
14
|
+
numResults: string;
|
|
15
|
+
conversionBtn: string;
|
|
16
|
+
resultId: string;
|
|
17
|
+
section: string;
|
|
18
|
+
zeroResults: string;
|
|
19
|
+
slCampaignId: string;
|
|
20
|
+
slCampaignOwner: string;
|
|
21
|
+
};
|
|
22
|
+
search: {
|
|
23
|
+
searchContainer: string;
|
|
24
|
+
searchTerm: string;
|
|
25
|
+
};
|
|
26
|
+
browse: {
|
|
27
|
+
browseContainer: string;
|
|
28
|
+
filterName: string;
|
|
29
|
+
filterValue: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare function getProductCardCnstrcDataAttributes(productInfo: ProductInfoObject, options?: ProductCardDataAttributeOptions): CnstrcDataAttrs;
|
|
33
|
+
export declare function getPlpContainerCnstrcDataAttributes(data: PlpSearchDataResults | PlpSearchDataRedirect | PlpBrowseData | null, requestConfigs: RequestConfigs, isLoading?: boolean): CnstrcDataAttrs;
|
|
34
|
+
export type ConversionType = 'add_to_cart' | 'add_to_wishlist' | 'like' | 'message' | 'make_offer' | 'read' | string;
|
|
35
|
+
export declare function getConversionButtonCnstrcDataAttributes(conversionType: ConversionType): {
|
|
36
|
+
[x: string]: string;
|
|
37
|
+
};
|
|
@@ -5,3 +5,4 @@ export declare function getRolloverImage(item: Item | Variation): string | undef
|
|
|
5
5
|
export declare function getSwatches(item: Item, retrievePrice: ItemFieldGetters['getPrice'], retrieveSwatchPreview: ItemFieldGetters['getSwatchPreview'], retrieveSalePrice: ItemFieldGetters['getSalePrice'], retrieveRolloverImage: ItemFieldGetters['getRolloverImage']): SwatchItem[] | undefined;
|
|
6
6
|
export declare function getSwatchPreview(variation: Variation): string;
|
|
7
7
|
export declare function getIsHiddenGroupField(group: PlpItemGroup): any;
|
|
8
|
+
export declare function getItemUrl(item: Item): string | undefined;
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "1.6.
|
|
1
|
+
declare const _default: "1.6.6";
|
|
2
2
|
export default _default;
|