Skip to content

Commit 9d9cd97

Browse files
authored
Merge pull request #8 from FoxComm/feature/reviews-on-pdp
Add reviews props
2 parents fbd26ef + 79b8bd6 commit 9d9cd97

7 files changed

Lines changed: 255 additions & 2 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@foxcomm/storefront-react",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "FoxCommerce Storefront React UI Library",
55
"main": "lib/index.js",
66
"scripts": {
@@ -74,6 +74,7 @@
7474
"dom-scroll-into-view": "^1.2.1",
7575
"hoist-non-react-statics": "^1.2.0",
7676
"lodash": "^4.17.4",
77+
"moment": "^2.18.1",
7778
"postcss-assets": "^4.1.0",
7879
"postcss-css-variables": "^0.7.0",
7980
"postcss-import": "^10.0.0",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
export default from './action-link';

src/components/pdp/pdp.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Props = DefaultProps & {
3838
fetchError?: mixed,
3939
notFound?: boolean,
4040
relatedProductsList?: Element<any>,
41+
reviewsList?: Element<any>,
4142
shareImage?: Element<any>,
4243
onAddLineItem?: (skuCode: string, quantity: number, attributes: Object) => Promise<mixed>,
4344
};
@@ -355,6 +356,7 @@ export default class Pdp extends Component<DefaultProps, Props, State> {
355356
{this.props.shareImage}
356357
</div>
357358
{this.props.relatedProductsList}
359+
{this.props.reviewsList}
358360
</div>
359361
);
360362
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
@import "variables.css";
2+
@import "typography.css";
3+
@import "media-queries.css";
4+
@import "colors.css";
5+
6+
.product-reviews-list-wrapper {
7+
position: relative;
8+
}
9+
10+
.product-reviews-title {
11+
composes: title-medium;
12+
display: block;
13+
margin-top: 90px;
14+
text-align: center;
15+
width: 100%;
16+
}
17+
18+
.product-reviews-subtitle {
19+
text-transform: uppercase;
20+
margin-top: 20px;
21+
text-align: center;
22+
}
23+
24+
.loader {
25+
position: fixed;
26+
width: 100%;
27+
top: 120px;
28+
}
29+
30+
.product-review-container {
31+
border: 1px solid #DDDDDD;
32+
border-radius: 4px;
33+
padding: 12px 15px 12px 15px;
34+
margin-top: 32px;
35+
}
36+
37+
.product-review-content {
38+
margin-top: 4px;
39+
}
40+
41+
.product-review-title {
42+
font-size: 22px;
43+
font-weight: 900;
44+
color: #222222;
45+
}
46+
47+
.product-review-name-date {
48+
margin-top: 16px;
49+
}
50+
51+
.product-review-variant {
52+
margin-top: 8px;
53+
}
54+
55+
.product-review-body {
56+
margin-top: 28px;
57+
}
58+
59+
.product-review-flag {
60+
margin-top: 28px;
61+
}
62+
63+
.product-review-report-offense {
64+
color: var(--link-color);
65+
cursor: pointer;
66+
}
67+
68+
.product-review-load-more {
69+
margin-top: 28px;
70+
display: block;
71+
text-align: center;
72+
color: var(--link-color);
73+
cursor: pointer;
74+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/* @flow weak */
2+
3+
// libs
4+
import React, { Component, Element } from 'react';
5+
import _ from 'lodash';
6+
import moment from 'moment';
7+
8+
// components
9+
import WaitAnimation from 'components/core/wait-animation/wait-animation';
10+
import ActionLink from 'components/core/action-link';
11+
12+
// styles
13+
import styles from './product-reviews-list.css';
14+
15+
// types
16+
type ReviewAttributes = {
17+
title: {t:'string', v: string},
18+
body: {t:'string', v: string},
19+
status: {t: 'string', v: string},
20+
}
21+
22+
type ReviewItem = {
23+
sku: string,
24+
id: number,
25+
scope: string,
26+
createdAt: string,
27+
updatedAt: string,
28+
archivedAt: ?string,
29+
userName: string,
30+
attributes: ReviewAttributes,
31+
}
32+
33+
type Props = {
34+
isLoading: ?boolean,
35+
listItems: ?Array<ReviewItem>,
36+
title: string,
37+
emptyContentTitle: string,
38+
paginationSize: number,
39+
onLoadMoreReviews: Function,
40+
showLoadMore: ?boolean
41+
};
42+
43+
type State = {
44+
page: number,
45+
}
46+
47+
const ReviewBody = (props): Element<any> => {
48+
const { title, userName, updatedAt, sku, body } = props;
49+
const updatedAtFormatted = moment(updatedAt).format('MMM Do YYYY');
50+
51+
return (
52+
<div styleName="product-review-container">
53+
<div styleName="product-review-content">
54+
<div styleName="product-review-title">
55+
{title}
56+
</div>
57+
<div styleName="product-review-name-date">
58+
From: {userName} on {updatedAtFormatted}
59+
</div>
60+
<div styleName="product-review-variant">
61+
SKU: {sku}
62+
</div>
63+
<div styleName="product-review-body">
64+
{body}
65+
</div>
66+
<div styleName="product-review-flag">
67+
<ActionLink
68+
action={_.noop}
69+
title="Report Offensive Review"
70+
styleName="product-review-report-offense"
71+
/>
72+
</div>
73+
</div>
74+
</div>
75+
);
76+
};
77+
78+
function incrementPage(nextPage) {
79+
return { page: nextPage };
80+
}
81+
82+
class ProductReviewsList extends Component {
83+
84+
props: Props;
85+
state: State = {
86+
page: 0,
87+
};
88+
89+
get reviewsEmptyContentTitle(): ?Element<any> {
90+
const { listItems, emptyContentTitle } = this.props;
91+
92+
if (_.isEmpty(listItems)) {
93+
return (
94+
<div styleName="product-reviews-subtitle">
95+
{emptyContentTitle}
96+
</div>
97+
);
98+
}
99+
return null;
100+
}
101+
102+
get displayReviews(): ?Element<any> {
103+
const { listItems, showLoadMore } = this.props;
104+
const activeReviews = _.filter(listItems, (review) => {
105+
return review.attributes.status.v == 'submitted';
106+
});
107+
if (!_.isEmpty(activeReviews)) {
108+
const reviews = _.map(listItems, (review) => {
109+
return (
110+
<ReviewBody
111+
key={review.id}
112+
title={review.attributes.title.v}
113+
userName={review.userName}
114+
updatedAt={review.updatedAt}
115+
sku={review.sku}
116+
body={review.attributes.body.v}
117+
/>
118+
);
119+
});
120+
121+
const loadMoreActionLink = (showLoadMore)
122+
? (
123+
<ActionLink
124+
action={this.handleLoadMoreReviews}
125+
title="LOAD MORE REVIEWS"
126+
styleName="product-review-load-more"
127+
/>
128+
)
129+
: null;
130+
131+
return (
132+
<div>
133+
{reviews}
134+
{loadMoreActionLink}
135+
</div>
136+
);
137+
}
138+
return null;
139+
}
140+
141+
handleLoadMoreReviews = () => {
142+
const { onLoadMoreReviews, paginationSize } = this.props;
143+
const { page } = this.state;
144+
145+
const nextPage = page + 1;
146+
this.setState(incrementPage(nextPage));
147+
onLoadMoreReviews(paginationSize * nextPage);
148+
}
149+
150+
render(): Element<any> {
151+
const { title, isLoading } = this.props;
152+
153+
if (isLoading) {
154+
return <WaitAnimation />;
155+
}
156+
157+
return (
158+
<div styleName="product-reviews-list-wrapper">
159+
<div styleName="product-reviews-title">
160+
{title}
161+
</div>
162+
{this.reviewsEmptyContentTitle}
163+
{this.displayReviews}
164+
</div>
165+
);
166+
}
167+
}
168+
169+
export default ProductReviewsList;

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ export WrapToLines from './components/core/wrap-to-lines/wrap-to-lines';
3232
export Pdp from './components/pdp/pdp';
3333
export ProductImage from './components/product-image/product-image';
3434
export ImagePlaceholder from './components/product-image/image-placeholder';
35-
export RelatedProductList from './components/related-products-list/related-products-list';
35+
export RelatedProductList from './components/related-products-list/related-products-list';
36+
export ProductReviewsList from './components/product-reviews-list/product-reviews-list';

yarn.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4187,6 +4187,10 @@ minimist@^1.1.0, minimist@^1.2.0:
41874187
dependencies:
41884188
minimist "0.0.8"
41894189

4190+
moment@^2.18.1:
4191+
version "2.18.1"
4192+
resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
4193+
41904194
ms@0.7.1:
41914195
version "0.7.1"
41924196
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"

0 commit comments

Comments
 (0)