Skip to content

Commit 9065726

Browse files
authored
PWA-3318::Prex Compatility (#4458)
* PWA-3318::Prex Compatility * PWA-3318 * PWA-3318-v1 * PWA-3318-v1
1 parent dded70d commit 9065726

File tree

23 files changed

+1033
-191
lines changed

23 files changed

+1033
-191
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React from 'react';
2+
import { string, shape, array } from 'prop-types';
3+
import { mergeClasses } from '@magento/venia-ui/lib/classify';
4+
import GalleryItem from '@magento/venia-ui/lib/components/Gallery/item';
5+
// inline loading of the css is janky, but the webpack loader gets blown out in local environment.
6+
import defaultGalleryClasses from '!!style-loader!css-loader?modules!./gallery.css';
7+
import defaultItemClasses from '!!style-loader!css-loader?modules!./item.css';
8+
9+
/**
10+
* Renders a Gallery of items. If items is an array of nulls Gallery will render
11+
* a placeholder item for each.
12+
*
13+
* @params {Array} props.items an array of items to render
14+
*/
15+
export const Gallery = props => {
16+
const galleryClasses = mergeClasses(
17+
defaultGalleryClasses,
18+
props.galleryClasses
19+
);
20+
const itemClasses = mergeClasses(defaultItemClasses, props.itemClasses);
21+
22+
const { items } = props;
23+
24+
const galleryItems = items.map((item, index) => {
25+
if (item === null) {
26+
return <GalleryItem key={index} />;
27+
}
28+
return <GalleryItem key={item.id} item={item} classes={itemClasses} />;
29+
});
30+
31+
return (
32+
<div className={galleryClasses.root}>
33+
<div className={galleryClasses.items}>{galleryItems}</div>
34+
</div>
35+
);
36+
};
37+
38+
Gallery.propTypes = {
39+
galleryClasses: shape({
40+
filters: string,
41+
items: string,
42+
root: string
43+
}),
44+
itemClasses: shape({
45+
image: string,
46+
imageContainer: string,
47+
imagePlaceholder: string,
48+
image_pending: string,
49+
images: string,
50+
images_pending: string,
51+
name: string,
52+
name_pending: string,
53+
price: string,
54+
price_pending: string,
55+
root: string,
56+
root_pending: string
57+
}),
58+
items: array.isRequired
59+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.root {
2+
display: grid;
3+
grid-template-areas:
4+
'actions'
5+
'items';
6+
grid-template-columns: 1fr;
7+
line-height: 1;
8+
}
9+
10+
.items {
11+
grid-template-columns: repeat(5, 1fr);
12+
margin-left: 2em;
13+
margin-right: 2em;
14+
margin-bottom: 60px;
15+
display: grid;
16+
grid-area: items;
17+
grid-gap: 1rem;
18+
}
19+
20+
@media (max-width: 640px) {
21+
.items {
22+
grid-template-columns: repeat(2, 1fr);
23+
}
24+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './Gallery';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.name {
2+
font-weight: bold;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.unitTitle {
2+
text-align: center;
3+
margin: 1.5em;
4+
font-weight: 700;
5+
font-size: 2.25rem;
6+
font-family: 'Source Serif Pro';
7+
}
8+
9+
.root a {
10+
text-decoration: none;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import React, { useRef } from 'react';
2+
import { string, shape } from 'prop-types';
3+
4+
import useRecsTrackingProps from '../../hooks/useRecsTrackingProps';
5+
import { Gallery } from '../Gallery/Gallery';
6+
import { mergeClasses } from '@magento/venia-ui/lib/classify';
7+
// inline loading of the css is janky, but the webpack loader gets blown out in local environment.
8+
import defaultClasses from '!!style-loader!css-loader?modules!./ProductRecommendations.css';
9+
import useObserver from '../../hooks/useObserver';
10+
import { mse } from '@magento/venia-data-collector';
11+
12+
export const VeniaProductRecommendations = props => {
13+
const rendered = useRef([]);
14+
const { units } = useRecsTrackingProps(props);
15+
const { observeUnit } = useObserver();
16+
17+
const classes = mergeClasses(defaultClasses, props.classes);
18+
const galleryClasses = mergeClasses(defaultClasses, props.galleryClasses);
19+
const itemClasses = mergeClasses(defaultClasses, props.itemClasses);
20+
21+
const galleryUnits = units.map(recommendationUnit => {
22+
if (recommendationUnit.totalProducts < 1) {
23+
return null;
24+
}
25+
26+
const items = recommendationUnit.products.map(shapeItem);
27+
return (
28+
<div
29+
key={recommendationUnit.unitId}
30+
data-unit-id={recommendationUnit.unitId}
31+
className={classes.root}
32+
ref={element => observeUnit(recommendationUnit, element)}
33+
>
34+
<div className={classes.unitTitle}>
35+
{recommendationUnit.storefrontLabel}
36+
</div>
37+
<Gallery
38+
galleryClasses={galleryClasses}
39+
itemClasses={itemClasses}
40+
items={items}
41+
/>
42+
</div>
43+
);
44+
});
45+
46+
if (units && units.length > 0) {
47+
units.forEach(recUnit => {
48+
if (
49+
recUnit.totalProducts > 0 &&
50+
!rendered.current.includes(recUnit.unitId)
51+
) {
52+
mse.publish.recsUnitRender(recUnit.unitId);
53+
rendered.current.push(recUnit.unitId);
54+
}
55+
});
56+
57+
return <div>{galleryUnits}</div>;
58+
} else {
59+
return null;
60+
}
61+
};
62+
63+
VeniaProductRecommendations.propTypes = {
64+
galleryClasses: shape({
65+
filters: string,
66+
items: string,
67+
root: string
68+
}),
69+
itemClasses: shape({
70+
image: string,
71+
imageContainer: string,
72+
imagePlaceholder: string,
73+
image_pending: string,
74+
images: string,
75+
images_pending: string,
76+
name: string,
77+
name_pending: string,
78+
price: string,
79+
price_pending: string,
80+
root: string,
81+
root_pending: string
82+
}),
83+
classes: shape({
84+
unitTitle: string,
85+
root: string
86+
}),
87+
pageType: string.isRequired
88+
};
89+
90+
// format data for GalleryItem, exported for testing
91+
export const shapeItem = item => {
92+
if (item) {
93+
const { url, image, prices, productId, currency, type } = item;
94+
95+
// derive the url_key and url_suffix from the url
96+
// example url --> https://magento.com/blah/blah/url_key.url_suffix
97+
const urlArray = String(url)
98+
.split('/')
99+
.splice(-1)[0]
100+
.split('.');
101+
const url_key = urlArray[0] + `.${urlArray[1]}`;
102+
const url_suffix = `.${urlArray[1]}`;
103+
104+
const price = {
105+
regularPrice: {
106+
amount: {
107+
value: prices.minimum.regular,
108+
currency
109+
}
110+
}
111+
};
112+
113+
return {
114+
...item,
115+
id: productId,
116+
small_image: image,
117+
url_key,
118+
url_suffix,
119+
price,
120+
// use inStock when the recs service provides it, use the commented out line below:
121+
// stock_status: inStock ? "IN_STOCK" : "OUT_OF_STOCK";
122+
stock_status: 'IN_STOCK',
123+
type_id: type
124+
};
125+
} else return null;
126+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './ProductRecommendations';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './localStorageConstants';
2+
export * from './pageTypes';
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const USER_VIEW_HISTORY_TIME_DECAY_KEY = 'ds-view-history-time-decay';
2+
export const USER_VIEW_HISTORY_KEY = 'ds-view-history';
3+
export const PURCHASE_HISTORY_KEY = 'ds-purchase-history';
4+
export const CART_CONTENTS_KEY = 'ds-cart';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const CMS = 'CMS';
2+
export const CART = 'Cart';
3+
export const CATEGORY = 'Category';
4+
export const PRODUCT = 'Product';
5+
export const CHECKOUT = 'Checkout';
6+
export const PAGEBUILDER = 'PageBuilder';
7+
8+
export const PageTypes = [CMS, CART, CATEGORY, CHECKOUT, PRODUCT, PAGEBUILDER];

0 commit comments

Comments
 (0)