@riosst100/pwa-marketplace 1.6.9 → 1.7.1

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.
Files changed (64) hide show
  1. package/package.json +1 -1
  2. package/src/componentOverrideMapping.js +4 -0
  3. package/src/components/CollectibleGameSets/collectibleGameSets.js +0 -4
  4. package/src/components/FilterTop/filterTop.js +0 -2
  5. package/src/components/FilterTopBackup/CustomFilters/customFilters.js +0 -1
  6. package/src/components/ProductLabel/index.js +47 -0
  7. package/src/components/ProductListTab/productListTab.js +51 -5
  8. package/src/components/ProductListTab/productListTab.module.css +1 -1
  9. package/src/components/RelatedProduct/relatedProduct.js +1 -1
  10. package/src/components/ShopBy/shopBy copy.js +172 -0
  11. package/src/components/ShopBy/shopBy.js +0 -1
  12. package/src/components/ShopBySets/shopBySets.js +0 -3
  13. package/src/components/SportCardsSets/index.js +2 -0
  14. package/src/components/SportCardsSets/sportCardsSets.js +372 -0
  15. package/src/components/SportCardsSets/sportCardsSets.module.css +76 -0
  16. package/src/components/SportCardsSets/sportCardsSets.shimmer.js +50 -0
  17. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/Carousel/__fixtures__/apolloMocks.js +33 -0
  18. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/Carousel/carousel.gql.ce.js +16 -0
  19. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/Carousel/carousel.gql.ee.js +17 -0
  20. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/Carousel/carousel.js +38 -0
  21. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/Carousel/index.js +1 -0
  22. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/Carousel/useCarousel.js +25 -0
  23. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/configAggregator.js +26 -0
  24. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/index.js +1 -0
  25. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/products.js +369 -0
  26. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/products.module.css +0 -0
  27. package/src/overwrites/pagebuilder/lib/ContentTypes/Slider/banner.module.css +103 -0
  28. package/src/overwrites/pagebuilder/lib/ContentTypes/Slider/configAggregator.js +17 -0
  29. package/src/overwrites/pagebuilder/lib/ContentTypes/Slider/index.js +2 -0
  30. package/src/overwrites/pagebuilder/lib/ContentTypes/Slider/slider.js +221 -0
  31. package/src/overwrites/pagebuilder/lib/ContentTypes/Slider/slider.module.css +231 -0
  32. package/src/overwrites/pagebuilder/lib/ContentTypes/Slider/slider.shimmer.js +126 -0
  33. package/src/overwrites/pagebuilder/lib/ContentTypes/Slider/slider.shimmer.module.css +5 -0
  34. package/src/overwrites/pagebuilder/lib/utils.js +224 -0
  35. package/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js +60 -0
  36. package/src/overwrites/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +4 -1
  37. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryFragments.gql.js +8 -0
  38. package/src/overwrites/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js +44 -0
  39. package/src/overwrites/venia-ui/lib/RootComponents/CMS/cms.js +73 -0
  40. package/src/overwrites/venia-ui/lib/RootComponents/CMS/cms.module.css +25 -0
  41. package/src/overwrites/venia-ui/lib/RootComponents/CMS/cms.shimmer.js +18 -0
  42. package/src/overwrites/venia-ui/lib/RootComponents/CMS/index.js +7 -0
  43. package/src/overwrites/venia-ui/lib/RootComponents/Category/category.js +12 -1
  44. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +3 -3
  45. package/src/overwrites/venia-ui/lib/components/App/app.js +131 -0
  46. package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.module.css +1 -0
  47. package/src/overwrites/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilters.js +1 -1
  48. package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js +4 -1
  49. package/src/overwrites/venia-ui/lib/components/Gallery/item.js +13 -3
  50. package/src/overwrites/venia-ui/lib/components/Gallery/item.module.css +1 -1
  51. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/auctionDetail.js +176 -138
  52. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/preOrderDetail.js +19 -10
  53. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +5 -26
  54. package/src/overwrites/venia-ui/lib/components/RichContent/richContent.module.css +3 -3
  55. package/src/overwrites/venia-ui/lib/components/SearchBar/searchBar.js +0 -1
  56. package/src/overwrites/venia-ui/lib/index.module.css +137 -0
  57. package/src/overwrites/venia-ui/lib/tokens.module.css +167 -0
  58. package/src/talons/CollectibleGameSets/collectibleGameSets.gql.js +15 -1
  59. package/src/talons/CollectibleGameSets/useCollectibleGameSets.js +0 -11
  60. package/src/talons/FilterTop/useFilterTop.js +0 -2
  61. package/src/talons/ShopBy/useShopBy.js +0 -22
  62. package/src/talons/SportCardsSets/sportCardsSets.gql.js +46 -0
  63. package/src/talons/SportCardsSets/useSportCardsSets.js +170 -0
  64. package/src/talons/SubCategoryPage/useSubCategoryPage.js +0 -3
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Retrieve background images from a master format node
3
+ *
4
+ * @param node
5
+ * @returns {{mobileImage: null, desktopImage: null}}
6
+ */
7
+ export function getBackgroundImages(node) {
8
+ const images = node.getAttribute('data-background-images');
9
+ const response = {
10
+ desktopImage: null,
11
+ mobileImage: null,
12
+ backgroundSize: node.style.backgroundSize,
13
+ backgroundPosition: node.style.backgroundPosition,
14
+ backgroundAttachment: node.style.backgroundAttachment,
15
+ backgroundRepeat: node.style.backgroundRepeat || 'repeat'
16
+ };
17
+
18
+ if (images) {
19
+ const imagesStructure = JSON.parse(images.replace(/\\"/g, '"'));
20
+ if (imagesStructure.desktop_image) {
21
+ response.desktopImage = imagesStructure.desktop_image;
22
+ }
23
+ if (imagesStructure.mobile_image) {
24
+ response.mobileImage = imagesStructure.mobile_image;
25
+ }
26
+ }
27
+
28
+ return response;
29
+ }
30
+
31
+ const alignmentToFlex = {
32
+ top: 'flex-start',
33
+ middle: 'center',
34
+ bottom: 'flex-end'
35
+ };
36
+
37
+ /**
38
+ * Retrieve vertical alignment from a master format node
39
+ *
40
+ * @param node
41
+ * @returns {{verticalAlignment: null}}
42
+ */
43
+ export function getVerticalAlignment(node) {
44
+ let verticalAlignment = null;
45
+ if (node.style.justifyContent) {
46
+ verticalAlignment = flexToVerticalAlignment(node.style.justifyContent);
47
+ }
48
+
49
+ return {
50
+ verticalAlignment
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Convert vertical alignment values to flex values
56
+ *
57
+ * @param alignment
58
+ * @returns {*}
59
+ */
60
+ export function verticalAlignmentToFlex(alignment) {
61
+ return alignmentToFlex[alignment];
62
+ }
63
+
64
+ /**
65
+ * Convert flex to vertical alignment values
66
+ *
67
+ * @param flex
68
+ * @returns {*}
69
+ */
70
+ export function flexToVerticalAlignment(flex) {
71
+ const flexToAlignment = Object.assign(
72
+ {},
73
+ ...Object.entries(alignmentToFlex).map(([a, b]) => ({ [b]: a }))
74
+ );
75
+ return flexToAlignment[flex];
76
+ }
77
+
78
+ /**
79
+ * Retrieve advanced props from content type node
80
+ *
81
+ * @param node
82
+ * @returns {{border: (string|string[]|string), marginRight: (*|string), borderColor: *, paddingBottom: (*|number|string), borderRadius: *, borderWidth: *, paddingRight: (*|number|string), marginBottom: (*|string), paddingTop: (*|string), paddingLeft: (*|string), marginTop: (*|string), marginLeft: (*|string|{get}|number)}}
83
+ */
84
+ export function getAdvanced(node) {
85
+ return {
86
+ ...getPadding(node),
87
+ ...getMargin(node),
88
+ ...getBorder(node),
89
+ ...getTextAlign(node),
90
+ ...getCssClasses(node),
91
+ ...getIsHidden(node)
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Retrieve the padding from a content type node
97
+ *
98
+ * @param node
99
+ * @returns {{paddingBottom: *, paddingRight: *, paddingTop: *, paddingLeft: *}}
100
+ */
101
+ export function getPadding(node) {
102
+ return {
103
+ paddingTop: node.style.paddingTop,
104
+ paddingRight: node.style.paddingRight,
105
+ paddingBottom: node.style.paddingBottom,
106
+ paddingLeft: node.style.paddingLeft
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Retrieve the margin from a content type node
112
+ *
113
+ * @param node
114
+ * @returns {{marginRight: *, marginBottom: *, marginTop: *, marginLeft: *}}
115
+ */
116
+ export function getMargin(node) {
117
+ return {
118
+ marginTop: node.style.marginTop,
119
+ marginRight: node.style.marginRight,
120
+ marginBottom: node.style.marginBottom,
121
+ marginLeft: node.style.marginLeft
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Retrieve the border from a content type node
127
+ *
128
+ * @param node
129
+ * @returns {{border: (string|string), borderColor: *, borderRadius: *, borderWidth: *}}
130
+ */
131
+ export function getBorder(node) {
132
+ return {
133
+ border: node.style.borderStyle,
134
+ borderColor: node.style.borderColor,
135
+ borderWidth: node.style.borderWidth,
136
+ borderRadius: node.style.borderRadius
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Retrieve the text align from a content type node
142
+ *
143
+ * @param node
144
+ * @returns {{textAlign: *}}
145
+ */
146
+ export function getTextAlign(node) {
147
+ return {
148
+ textAlign: node.style.textAlign
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Retrieve the CSS classes from a content type node
154
+ * @param node
155
+ * @returns {{cssClasses: any}}
156
+ */
157
+ export function getCssClasses(node) {
158
+ return {
159
+ cssClasses: node.getAttribute('class')
160
+ ? node.getAttribute('class').split(' ')
161
+ : []
162
+ };
163
+ }
164
+
165
+ /**
166
+ * Retrieve if CSS display property is set to none from a content type node
167
+ *
168
+ * @param node
169
+ * @returns {{isHidden: boolean}}
170
+ */
171
+ export function getIsHidden(node) {
172
+ return {
173
+ isHidden: node.style.display === 'none'
174
+ };
175
+ }
176
+
177
+ /**
178
+ * Converts a CSS string style into a JSX object inline style
179
+ *
180
+ * @param {String} style
181
+ * @returns {Object}
182
+ */
183
+ export function cssToJSXStyle(style) {
184
+ const toCamelCase = str => str.replace(/-(.)/g, (_, p) => p.toUpperCase());
185
+ const result = {};
186
+ style.split(';').forEach(el => {
187
+ const [prop, value] = el.split(':');
188
+ if (prop) {
189
+ result[toCamelCase(prop.trim())] = value.trim();
190
+ }
191
+ });
192
+
193
+ return result;
194
+ }
195
+
196
+ /**
197
+ * Retrieve media queries from a master format node
198
+ *
199
+ * @param node
200
+ * @param {Array} mediaQueries
201
+ *
202
+ * @returns {{mediaQueries: {media: string, style: string}}}
203
+ */
204
+ export function getMediaQueries(node) {
205
+ const response = [];
206
+ const dataset = Object.keys(node.dataset);
207
+
208
+ const medias = dataset
209
+ .filter(key => key.match(/media-/))
210
+ .map(key => node.dataset[key]);
211
+
212
+ const styles = dataset
213
+ .filter(key => key.match(/mediaStyle/))
214
+ .map(key => node.dataset[key]);
215
+
216
+ medias.forEach((media, i) => {
217
+ response.push({
218
+ media,
219
+ style: cssToJSXStyle(styles[i])
220
+ });
221
+ });
222
+
223
+ return { mediaQueries: response };
224
+ }
@@ -148,6 +148,66 @@ export const useFilterSidebar = props => {
148
148
  }
149
149
  }
150
150
 
151
+ const productTabs = [
152
+ {
153
+ 'label': 'All Products',
154
+ 'value': 'all',
155
+ 'path': '',
156
+ 'options': [
157
+ {'value':'0','label':'No','title':'No'},
158
+ {'value':'1','label':'Yes','title':'Yes'}
159
+ ]
160
+ },
161
+ {
162
+ 'label': 'Sale',
163
+ 'value': 'special_price',
164
+ 'path': '',
165
+ 'options': [
166
+ {'value':'0','label':'No','title':'No'},
167
+ {'value':'1','label':'Yes','title':'Yes'}
168
+ ]
169
+ },
170
+ {
171
+ 'label': 'Preorder',
172
+ 'value': 'lof_preorder',
173
+ 'path': '',
174
+ 'options': [
175
+ {'value':'0','label':'No','title':'No'},
176
+ {'value':'1','label':'Yes','title':'Yes'}
177
+ ]
178
+ },
179
+ {
180
+ 'label': 'Auction',
181
+ 'value': 'auction',
182
+ 'path': '',
183
+ 'options': [
184
+ {'value':'0','label':'No','title':'No'},
185
+ {'value':'1','label':'Yes','title':'Yes'}
186
+ ]
187
+ }
188
+ ];
189
+
190
+ if (productTabs) {
191
+ productTabs.map(({ value, options }, index) => {
192
+
193
+
194
+ const items = [];
195
+
196
+ names.set(value, '');
197
+ frontendInput.set(value, null);
198
+
199
+ // add filter key permutations
200
+ keys.add(`${value}[filter]`);
201
+
202
+ // add items
203
+ for (const { label, value, path } of options) {
204
+ items.push({ title: stripHtml(label), value, path });
205
+ }
206
+
207
+ itemsByGroup.set(value, items);
208
+ })
209
+ }
210
+
151
211
  return [names, keys, itemsByGroup, frontendInput];
152
212
  }, [filters, possibleFilters]);
153
213
 
@@ -552,10 +552,13 @@ export const useProductFullDetail = props => {
552
552
  shortDescription: product.short_description,
553
553
  name: product.name,
554
554
  price: productPrice?.final_price,
555
+ price_range: product?.price_range,
555
556
  sku: product.sku,
556
557
  term_and_conditions: product.term_and_conditions,
557
558
  shipping_policy: product.shipping_policy,
558
- return_policy: product.return_policy
559
+ return_policy: product.return_policy,
560
+ preorder: product.preorder,
561
+ auction_data: product.auction_data
559
562
  };
560
563
 
561
564
  const sellerDetails = {
@@ -16,6 +16,14 @@ export const ProductsFragment = gql`
16
16
  id
17
17
  uid
18
18
  name
19
+ preorder {
20
+ is_preorder
21
+ preorder_notes
22
+ preorder_availability
23
+ }
24
+ auction_data {
25
+ is_auction
26
+ }
19
27
  seller {
20
28
  name
21
29
  }
@@ -34,6 +34,9 @@ export const ProductDetailsFragment = gql`
34
34
  }
35
35
  meta_description
36
36
  name
37
+ special_price
38
+ special_from_date
39
+ special_to_date
37
40
  price {
38
41
  regularPrice {
39
42
  amount {
@@ -48,12 +51,53 @@ export const ProductDetailsFragment = gql`
48
51
  currency
49
52
  value
50
53
  }
54
+ regular_price {
55
+ currency
56
+ value
57
+ }
51
58
  discount {
52
59
  amount_off
53
60
  }
54
61
  }
55
62
  }
56
63
  sku
64
+ auction_data {
65
+ is_auction
66
+ status
67
+ customer_id
68
+ starting_price
69
+ reserve_price
70
+ days
71
+ min_qty
72
+ max_qty
73
+ start_auction_time
74
+ stop_auction_time
75
+ auction_status
76
+ increment_opt
77
+ increment_price
78
+ auto_auction_opt
79
+ featured_auction
80
+ buy_it_now
81
+ max_amount
82
+ min_amount
83
+ current_time_stamp
84
+ continue_time
85
+ limit_bids
86
+ time_zone
87
+ stop_auction_utc_time
88
+ start_auction_utc_time
89
+ today_time
90
+ start_auction_time_stamp
91
+ stop_auction_time_stamp
92
+ new_auction_start
93
+ customer_name
94
+ max_bids
95
+ }
96
+ preorder {
97
+ is_preorder
98
+ preorder_notes
99
+ preorder_availability
100
+ }
57
101
  term_and_conditions
58
102
  shipping_policy
59
103
  return_policy
@@ -0,0 +1,73 @@
1
+ import React, { Fragment } from 'react';
2
+ import { shape, string } from 'prop-types';
3
+
4
+ import CMSPageShimmer from './cms.shimmer';
5
+ import { useCmsPage } from '@magento/peregrine/lib/talons/Cms/useCmsPage';
6
+ import RichContent from '@magento/venia-ui/lib/components/RichContent';
7
+ import { Meta, StoreTitle } from '@magento/venia-ui/lib/components/Head';
8
+ import { useStyle } from '@magento/venia-ui/lib/classify';
9
+ import { toCamelCase } from '@magento/venia-ui/lib/util/toCamelCase';
10
+
11
+ import defaultClasses from './cms.module.css';
12
+
13
+ const CMSPage = props => {
14
+ const { identifier } = props;
15
+
16
+ const talonProps = useCmsPage({ identifier });
17
+ const { cmsPage, shouldShowLoadingIndicator } = talonProps;
18
+ const classes = useStyle(defaultClasses, props.classes);
19
+
20
+ if (shouldShowLoadingIndicator) {
21
+ return <CMSPageShimmer classes={classes} />;
22
+ }
23
+
24
+ const {
25
+ content_heading,
26
+ title,
27
+ meta_title,
28
+ meta_description,
29
+ page_layout,
30
+ content
31
+ } = cmsPage;
32
+
33
+ const headingElement =
34
+ content_heading !== '' ? (
35
+ <h1 data-cy="Cms-contentHeading" className={classes.heading}>
36
+ {content_heading}
37
+ </h1>
38
+ ) : null;
39
+
40
+ const pageTitle = meta_title || title;
41
+ const rootClassName = page_layout
42
+ ? classes[`root_${toCamelCase(page_layout)}`]
43
+ : classes.root;
44
+ return (
45
+ <Fragment>
46
+ <StoreTitle>{pageTitle}</StoreTitle>
47
+ <Meta name="title" content={pageTitle} />
48
+ <Meta name="description" content={meta_description} />
49
+ <article className={rootClassName}>
50
+ {headingElement}
51
+ <RichContent html={content} />
52
+ </article>
53
+ </Fragment>
54
+ );
55
+ };
56
+
57
+ CMSPage.propTypes = {
58
+ identifier: string,
59
+ classes: shape({
60
+ root: string,
61
+ heading: string,
62
+ root_empty: string,
63
+ root_1column: string,
64
+ root_2columnsLeft: string,
65
+ root_2columnsRight: string,
66
+ root_3columns: string,
67
+ root_cmsFullWidth: string,
68
+ root_categoryFullWidth: string,
69
+ root_productFullWidth: string
70
+ })
71
+ };
72
+
73
+ export default CMSPage;
@@ -0,0 +1,25 @@
1
+ .root {
2
+ composes: px-0 from global;
3
+ }
4
+
5
+ .heading {
6
+ composes: mb-2 from global;
7
+ composes: mt-4 from global;
8
+ composes: text-center from global;
9
+ composes: text-3xl from global;
10
+
11
+ /* Medium */
12
+ composes: md_mb-10 from global;
13
+ composes: md_text-4xl from global;
14
+ }
15
+
16
+ .root_empty,
17
+ .root_1column,
18
+ .root_2columnsLeft,
19
+ .root_2columnsRight,
20
+ .root_3columns,
21
+ .root_cmsFullWidth,
22
+ .root_categoryFullWidth,
23
+ .root_productFullWidth {
24
+ composes: root;
25
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+
3
+ import Shimmer from '@magento/venia-ui/lib/components/Shimmer/shimmer.js';
4
+ import { useStyle } from '@magento/venia-ui/lib/classify';
5
+
6
+ import defaultClasses from './cms.module.css';
7
+
8
+ const CMSPageShimmer = props => {
9
+ const classes = useStyle(defaultClasses, props.classes);
10
+
11
+ return (
12
+ <div className={classes.root} aria-live="polite" aria-busy="true">
13
+ <Shimmer width="100%" height="880px" key="banner" />
14
+ </div>
15
+ );
16
+ };
17
+
18
+ export default CMSPageShimmer;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @RootComponent
3
+ * description = 'Basic CMS Page'
4
+ * pageTypes = CMS_PAGE
5
+ */
6
+
7
+ export { default } from './cms';
@@ -12,6 +12,8 @@ import { useIntl } from 'react-intl';
12
12
  import ShopBy from '@riosst100/pwa-marketplace/src/components/ShopBy/shopBy';
13
13
  import SubCategoryPage from '@riosst100/pwa-marketplace/src/components/CustomSubCategoryPage/subCategoryPage';
14
14
  import ShopBySets from '@riosst100/pwa-marketplace/src/components/ShopBySets/shopBySets';
15
+ import { useLocation } from 'react-router-dom';
16
+ import SportCardsSets from '@riosst100/pwa-marketplace/src/components/SportCardsSets/sportCardsSets';
15
17
 
16
18
  const MESSAGES = new Map().set(
17
19
  'NOT_FOUND',
@@ -24,6 +26,12 @@ const Category = props => {
24
26
 
25
27
  const { location } = globalThis;
26
28
 
29
+ const { pathname } = useLocation();
30
+
31
+ const pathnameArr = pathname.split('/');
32
+
33
+ const parentCategoryUrlKey = pathnameArr[pathnameArr.length - 2].replace('.html','');
34
+
27
35
  const query = new URLSearchParams(location.search);
28
36
  const shopby = query.get('shopby') || null;
29
37
  const showSubcategory = query.get('show_subcategory') || null;
@@ -76,7 +84,10 @@ const Category = props => {
76
84
  categoryId={uid}
77
85
  isLoading={loading} />
78
86
  ) : (
79
- shopby == "sets" || shopby == "expansion_sets" || shopby == "release_year" ? <ShopBySets
87
+ shopby == "release" && parentCategoryUrlKey == "sport-cards" ? <SportCardsSets
88
+ categoryId={uid}
89
+ shopby={shopby}
90
+ isLoading={loading} /> : shopby == "sets" || shopby == "expansion_sets" || shopby == "release_year" ? <ShopBySets
80
91
  categoryId={uid}
81
92
  shopby={shopby}
82
93
  isLoading={loading} /> : (shopby ? (
@@ -112,7 +112,7 @@ const CategoryContent = props => {
112
112
  }
113
113
  }
114
114
  }),
115
- [items, activeLetter]
115
+ [items, activeLetter, activeTab]
116
116
  });
117
117
 
118
118
  const sidebarRef = useRef(null);
@@ -162,8 +162,8 @@ const CategoryContent = props => {
162
162
  <SortedByContainerShimmer />
163
163
  ) : null;
164
164
 
165
- const maybeProductsTabContainer = shouldShowProductListTab ? (
166
- <ProductListTab activeTab={activeTab} setActiveTab={setActiveTab} />
165
+ const maybeProductsTabContainer = !isLoading ? (
166
+ <ProductListTab filters={filters} activeTab={activeTab} setActiveTab={setActiveTab} />
167
167
  ) : shouldShowProductListTabShimmer ? (
168
168
  <ProductListTabShimmer />
169
169
  ) : null;
@@ -0,0 +1,131 @@
1
+ import React, { useCallback } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { array, func, shape, string } from 'prop-types';
4
+
5
+ import { useToasts } from '@magento/peregrine';
6
+ import { useDelayedTransition } from '@magento/peregrine/lib/hooks/useDelayedTransition';
7
+ import { useApp } from '@magento/peregrine/lib/talons/App/useApp';
8
+
9
+ import globalCSS from '../../index.module.css';
10
+ import { HeadProvider, StoreTitle } from '@magento/venia-ui/lib/components/Head';
11
+ import Main from '../Main';
12
+ import Mask from '@magento/venia-ui/lib/components/Mask';
13
+ import Navigation from '@magento/venia-ui/lib/components/Navigation';
14
+ import Routes from '@magento/venia-ui/lib/components/Routes';
15
+ import ToastContainer from '@magento/venia-ui/lib/components/ToastContainer';
16
+ import Icon from '@magento/venia-ui/lib/components/Icon';
17
+
18
+ import {
19
+ AlertCircle as AlertCircleIcon,
20
+ CloudOff as CloudOffIcon,
21
+ Wifi as WifiIcon
22
+ } from 'react-feather';
23
+
24
+ const OnlineIcon = <Icon src={WifiIcon} attrs={{ width: 18 }} />;
25
+ const OfflineIcon = <Icon src={CloudOffIcon} attrs={{ width: 18 }} />;
26
+ const ErrorIcon = <Icon src={AlertCircleIcon} attrs={{ width: 18 }} />;
27
+
28
+ const App = props => {
29
+ const { markErrorHandled, renderError, unhandledErrors } = props;
30
+ const { formatMessage } = useIntl();
31
+ const [, { addToast }] = useToasts();
32
+ useDelayedTransition();
33
+
34
+ const ERROR_MESSAGE = formatMessage({
35
+ id: 'app.errorUnexpected',
36
+ defaultMessage: 'Sorry! An unexpected error occurred.'
37
+ });
38
+
39
+ const handleIsOffline = useCallback(() => {
40
+ addToast({
41
+ type: 'error',
42
+ icon: OfflineIcon,
43
+ message: formatMessage({
44
+ id: 'app.errorOffline',
45
+ defaultMessage:
46
+ 'You are offline. Some features may be unavailable.'
47
+ }),
48
+ timeout: 3000
49
+ });
50
+ }, [addToast, formatMessage]);
51
+
52
+ const handleIsOnline = useCallback(() => {
53
+ addToast({
54
+ type: 'info',
55
+ icon: OnlineIcon,
56
+ message: formatMessage({
57
+ id: 'app.infoOnline',
58
+ defaultMessage: 'You are online.'
59
+ }),
60
+ timeout: 3000
61
+ });
62
+ }, [addToast, formatMessage]);
63
+
64
+ const handleError = useCallback(
65
+ (error, id, loc, handleDismissError) => {
66
+ const errorToastProps = {
67
+ icon: ErrorIcon,
68
+ message: `${ERROR_MESSAGE}\nDebug: ${id} ${loc}`,
69
+ onDismiss: remove => {
70
+ handleDismissError();
71
+ remove();
72
+ },
73
+ timeout: 15000,
74
+ type: 'error'
75
+ };
76
+
77
+ addToast(errorToastProps);
78
+ },
79
+ [ERROR_MESSAGE, addToast]
80
+ );
81
+
82
+ const talonProps = useApp({
83
+ handleError,
84
+ handleIsOffline,
85
+ handleIsOnline,
86
+ markErrorHandled,
87
+ renderError,
88
+ unhandledErrors
89
+ });
90
+
91
+ const { hasOverlay, handleCloseDrawer } = talonProps;
92
+
93
+ if (renderError) {
94
+ return (
95
+ <HeadProvider>
96
+ <StoreTitle />
97
+ <Main isMasked={true} />
98
+ <Mask isActive={true} />
99
+ <ToastContainer />
100
+ </HeadProvider>
101
+ );
102
+ }
103
+
104
+ return (
105
+ <HeadProvider>
106
+ <StoreTitle />
107
+ <Main isMasked={hasOverlay}>
108
+ <Routes />
109
+ </Main>
110
+ <Mask
111
+ isActive={hasOverlay}
112
+ dismiss={handleCloseDrawer}
113
+ data-cy="App-Mask-button"
114
+ />
115
+ <Navigation />
116
+ <ToastContainer />
117
+ </HeadProvider>
118
+ );
119
+ };
120
+
121
+ App.propTypes = {
122
+ markErrorHandled: func.isRequired,
123
+ renderError: shape({
124
+ stack: string
125
+ }),
126
+ unhandledErrors: array
127
+ };
128
+
129
+ App.globalCSS = globalCSS;
130
+
131
+ export default App;