@riosst100/pwa-marketplace 1.3.4 → 1.3.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.
Files changed (22) hide show
  1. package/i18n/en_US.json +1 -0
  2. package/i18n/id_ID.json +1 -0
  3. package/package.json +1 -1
  4. package/src/componentOverrideMapping.js +1 -0
  5. package/src/components/MegaMenu/customMenuItems.js +138 -0
  6. package/src/components/MegaMenu/customMenuItems.module.css +31 -0
  7. package/src/components/ProductListTab/index.js +4 -0
  8. package/src/components/ProductListTab/productListTab.js +41 -0
  9. package/src/components/ProductListTab/productListTab.module.css +65 -0
  10. package/src/components/ProductListTab/productListTab.shimmer.js +24 -0
  11. package/src/components/SubCategory/subCategory.js +6 -2
  12. package/src/components/SubCategory/subCategory.module.css +22 -0
  13. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/category.gql.js +49 -0
  14. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryFragments.gql.js +51 -0
  15. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategory.js +227 -0
  16. package/src/overwrites/venia-ui/lib/RootComponents/Category/category.module.css +1 -1
  17. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +21 -6
  18. package/src/overwrites/venia-ui/lib/components/FilterModal/FilterList/filterList.js +6 -6
  19. package/src/overwrites/venia-ui/lib/components/FilterModal/FilterList/filterList.module.css +8 -0
  20. package/src/overwrites/venia-ui/lib/components/FilterModal/filterModal.module.css +2 -1
  21. package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.module.css +3 -2
  22. package/src/overwrites/venia-ui/lib/components/Header/header.js +1 -1
package/i18n/en_US.json CHANGED
@@ -172,6 +172,7 @@
172
172
  "field.optional": "Optional",
173
173
  "filterFooter.results": "See Results",
174
174
  "filterList.showMore": "Show More",
175
+ "filterList.viewAll": "View All",
175
176
  "filterList.showLess": "Show Less",
176
177
  "filterModal.action": "Clear all",
177
178
  "filterModal.action.clearAll.ariaLabel": "Clear all applied filters",
package/i18n/id_ID.json CHANGED
@@ -172,6 +172,7 @@
172
172
  "field.optional": "Optional",
173
173
  "filterFooter.results": "See Results",
174
174
  "filterList.showMore": "Show More",
175
+ "filterList.viewAll": "Lihat Selengkapnya",
175
176
  "filterList.showLess": "Show Less",
176
177
  "filterModal.action": "Clear all",
177
178
  "filterModal.action.clearAll.ariaLabel": "Clear all applied filters",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@riosst100/pwa-marketplace",
3
3
  "author": "riosst100@gmail.com",
4
- "version": "1.3.4",
4
+ "version": "1.3.6",
5
5
  "main": "src/index.js",
6
6
  "pwa-studio": {
7
7
  "targets": {
@@ -19,6 +19,7 @@ module.exports = componentOverrideMapping = {
19
19
  [`@magento/venia-ui/lib/components/FilterModal/FilterList/filterDefault.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/FilterModal/FilterList/filterDefault.js',
20
20
  [`@magento/venia-ui/lib/RootComponents/Category/categoryContent.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js',
21
21
  [`@magento/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js',
22
+ [`@magento/peregrine/lib/talons/RootComponents/Category/useCategory.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategory.js',
22
23
  [`@magento/peregrine/lib/talons/Breadcrumbs/useBreadcrumbs.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/Breadcrumbs/useBreadcrumbs.js',
23
24
  [`@magento/peregrine/lib/talons/FilterModal/useFilterList.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/FilterModal/useFilterList.js',
24
25
  [`@magento/venia-ui/lib/components/FilterSidebar/filterSidebar.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js',
@@ -0,0 +1,138 @@
1
+ import React, { useMemo } from 'react';
2
+ import { ChevronDown as ArrowDown } from 'react-feather';
3
+ import { Link } from 'react-router-dom';
4
+ import PropTypes from 'prop-types';
5
+
6
+ import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
7
+ import { useCustomMenuItems } from '@magento/peregrine/lib/talons/MegaMenu/useCustomMenuItems';
8
+
9
+ import { useStyle } from '@magento/venia-ui/lib/classify';
10
+ import defaultClasses from './megaMenuItem.module.css';
11
+ import Submenu from './submenu';
12
+ import { ArrowDown2 } from 'iconsax-react';
13
+ import { textPrimary } from '@riosst100/pwa-marketplace/src/theme/vars';
14
+ import cn from 'classnames';
15
+
16
+ /**
17
+ * The CustomMenuItems component displays mega menu item
18
+ *
19
+ * @param {MegaMenuCategory} props.category
20
+ * @param {String} props.activeCategoryId - uid of active category
21
+ * @param {int} props.mainNavWidth - width of the main nav. It's used for setting min-width of the submenu
22
+ * @param {function} props.onNavigate - function called when clicking on Link
23
+ */
24
+ const CustomMenuItems = props => {
25
+ const {
26
+ activeCategoryId,
27
+ category,
28
+ mainNavWidth,
29
+ categoryUrlSuffix,
30
+ subMenuState,
31
+ disableFocus,
32
+ onNavigate,
33
+ handleSubMenuFocus,
34
+ handleClickOutside,
35
+ megaMenuItemClassname,
36
+ titleClassName,
37
+ } = props;
38
+
39
+ const classes = useStyle(defaultClasses, props.classes);
40
+ const categoryUrl = resourceUrl(
41
+ `/${category.url_path}${categoryUrlSuffix || ''}`
42
+ );
43
+
44
+ const talonProps = useCustomMenuItems({
45
+ category,
46
+ activeCategoryId,
47
+ subMenuState,
48
+ disableFocus
49
+ });
50
+
51
+ const {
52
+ isFocused,
53
+ isActive,
54
+ handleMenuItemFocus,
55
+ handleCloseSubMenu,
56
+ isMenuActive,
57
+ handleKeyDown
58
+ } = talonProps;
59
+
60
+ const itemClassName = isMenuActive
61
+ ? classes.megaMenuItem_active
62
+ : classes.megaMenuItem;
63
+
64
+ const children = useMemo(() => {
65
+ return category.children.length ? (
66
+ <Submenu
67
+ isFocused={isFocused}
68
+ subMenuState={subMenuState}
69
+ items={category.children}
70
+ shopByItems={category.shop_by}
71
+ mainNavWidth={mainNavWidth}
72
+ handleCloseSubMenu={handleCloseSubMenu}
73
+ categoryUrlSuffix={categoryUrlSuffix}
74
+ onNavigate={onNavigate}
75
+ />
76
+ ) : null;
77
+ }, [
78
+ category,
79
+ isFocused,
80
+ mainNavWidth,
81
+ subMenuState,
82
+ handleCloseSubMenu,
83
+ categoryUrlSuffix,
84
+ onNavigate
85
+ ]);
86
+
87
+ const maybeDownArrowIcon = category.children.length ? (
88
+ <ArrowDown2 className='ml-2 stroke-current' size="16" color={textPrimary} variant="Outline" />
89
+ ) : null;
90
+
91
+ const linkAttributes = category.children.length
92
+ ? {
93
+ 'aria-label': `Category: ${category.name}. ${category.children.length
94
+ } sub-categories`
95
+ }
96
+ : {};
97
+
98
+ return (
99
+ <div
100
+ className={cn(itemClassName, megaMenuItemClassname)}
101
+ data-cy="MegaMenu-CustomMenuItems-megaMenuItem"
102
+ onMouseEnter={() => {
103
+ handleSubMenuFocus();
104
+ handleMenuItemFocus();
105
+ }}
106
+ onFocus={() => {
107
+ handleSubMenuFocus();
108
+ handleMenuItemFocus();
109
+ }}
110
+ onTouchStart={() => {
111
+ handleSubMenuFocus();
112
+ handleMenuItemFocus();
113
+ }}
114
+ onMouseLeave={e => {
115
+ handleClickOutside(e);
116
+ handleCloseSubMenu();
117
+ }}
118
+ >
119
+ <Link
120
+ {...linkAttributes}
121
+ onKeyDown={handleKeyDown}
122
+ className={cn(
123
+ isActive ? classes.megaMenuLinkActive : classes.megaMenuLink,
124
+ titleClassName
125
+ )}
126
+ data-cy="MegaMenu-CustomMenuItems-link"
127
+ to={categoryUrl}
128
+ onClick={onNavigate}
129
+ >
130
+ {category.name}
131
+ {maybeDownArrowIcon}
132
+ </Link>
133
+ {children}
134
+ </div>
135
+ );
136
+ };
137
+
138
+ export default CustomMenuItems;
@@ -0,0 +1,31 @@
1
+ .megaMenuItem {
2
+ /* composes: px-3 from global;
3
+ composes: py-0 from global; */
4
+ }
5
+
6
+ .megaMenuLink {
7
+ composes: items-center from global;
8
+ composes: inline-flex from global;
9
+ /* min-height: 5rem; */
10
+ }
11
+
12
+ .megaMenuLinkActive {
13
+ composes: megaMenuLink;
14
+ composes: underline from global;
15
+ }
16
+
17
+ .megaMenuItem_active {
18
+ composes: megaMenuItem;
19
+ composes: flex from global;
20
+ }
21
+
22
+ .arrowDown {
23
+ composes: leading-[0] from global;
24
+ composes: pl-2xs from global;
25
+ }
26
+
27
+ /* TODO @TW: cannot compose */
28
+ .megaMenuItem:hover .megaMenuLink,
29
+ .megaMenuItem:focus .megaMenuLink {
30
+ text-decoration: underline;
31
+ }
@@ -0,0 +1,4 @@
1
+ export { default } from './productListTab';
2
+ export {
3
+ default as ProductListTabShimmer
4
+ } from './productListTab.shimmer';
@@ -0,0 +1,41 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import { useStyle } from '@magento/venia-ui/lib/classify';
5
+ import defaultClasses from './productListTab.module.css';
6
+
7
+ const ProductListTab = props => {
8
+
9
+ const classes = useStyle(defaultClasses, props.classes);
10
+
11
+ const [activeTab, setActiveTab] = useState(null);
12
+
13
+ let allProductClass = classes.firstButton;
14
+ if (!activeTab) {
15
+ allProductClass = classes.activeFirstButton;
16
+ }
17
+
18
+ return (
19
+ <div className={classes.root} aria-busy="true">
20
+ {/* <FormattedMessage
21
+ id={'searchPage.sortContainer'}
22
+ defaultMessage={'Items sorted by '}
23
+ />
24
+ <span className={classes.sortText}>
25
+ <FormattedMessage
26
+ id={currentSort.sortId}
27
+ defaultMessage={currentSort.sortText}
28
+ />
29
+ </span> */}
30
+ <nav>
31
+ <ul className={classes.buttonContainer}>
32
+ <li><button className={allProductClass}>All Products</button></li>
33
+ <li><button className={classes.middleButton}>Pre Order</button></li>
34
+ <li><button className={classes.lastButton}>Auctions</button></li>
35
+ </ul>
36
+ </nav>
37
+ </div>
38
+ );
39
+ };
40
+
41
+ export default ProductListTab;
@@ -0,0 +1,65 @@
1
+ .root {
2
+ composes: bg-white from global;
3
+ composes: flex from global;
4
+ composes: items-center from global;
5
+ composes: flex-wrap from global;
6
+ width: inherit;
7
+ }
8
+
9
+ .buttonContainer {
10
+ composes: inline-flex from global;
11
+ align-items: center;
12
+ }
13
+
14
+ .button {
15
+ composes: flex from global;
16
+ composes: px-4 from global;
17
+ composes: py-2 from global;
18
+ composes: transition-colors from global;
19
+ composes: duration-150 from global;
20
+ composes: border from global;
21
+ composes: border-solid from global;
22
+ composes: leading-normal from global;
23
+ composes: text-[14px] from global;
24
+ }
25
+
26
+ .activeButton {
27
+ composes: button;
28
+ color: white;
29
+ background-color: #4E31DB;
30
+ }
31
+
32
+ .activeFirstButton {
33
+ composes: activeButton;
34
+ composes: border-r-0 from global;
35
+ composes: rounded-l-lg from global;
36
+ border-radius: 6px 0px 0px 6px;
37
+ }
38
+
39
+ .middleButton {
40
+ composes: button;
41
+ composes: text-colorDefault from global;
42
+ composes: bg-white from global;
43
+ composes: border-l-0 from global;
44
+ composes: border-gray-100 from global;
45
+ }
46
+
47
+ .lastButton {
48
+ composes: button;
49
+ composes: text-colorDefault from global;
50
+ composes: bg-white from global;
51
+ composes: border-l-0 from global;
52
+ composes: border-gray-100 from global;
53
+ composes: rounded-r-lg from global;
54
+ border-radius: 0px 6px 6px 0px;
55
+ }
56
+
57
+ .firstButton {
58
+ composes: button;
59
+ composes: text-colorDefault from global;
60
+ composes: bg-white from global;
61
+ composes: border-r-0 from global;
62
+ composes: border-gray-100 from global;
63
+ composes: rounded-l-lg from global;
64
+ border-radius: 6px 0px 0px 6px;
65
+ }
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { shape, string } from 'prop-types';
3
+ import { useStyle } from '@magento/venia-ui/lib/classify';
4
+
5
+ import Shimmer from '@magento/venia-ui/lib/components/Shimmer';
6
+ import defaultClasses from './productListTab.module.css';
7
+
8
+ const ProductListTabShimmer = 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={10} />
14
+ </div>
15
+ );
16
+ };
17
+
18
+ ProductListTabShimmer.propTypes = {
19
+ classes: shape({
20
+ root: string
21
+ })
22
+ };
23
+
24
+ export default ProductListTabShimmer;
@@ -2,12 +2,16 @@ import { useSubCategory } from '@riosst100/pwa-marketplace/src/talons/SubCategor
2
2
  import React from 'react';
3
3
  import { Link } from 'react-router-dom';
4
4
  import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
5
+ import defaultClasses from './subCategory.module.css';
6
+ import { useStyle } from '@magento/venia-ui/lib/classify';
5
7
 
6
8
  const SubCategory = props => {
7
9
  const { children } = props;
8
10
 
9
11
  const talonProps = useSubCategory({ children });
10
12
 
13
+ const classes = useStyle(defaultClasses, props.classes);
14
+
11
15
  const {
12
16
  normalizedData
13
17
  } = talonProps;
@@ -20,12 +24,12 @@ const SubCategory = props => {
20
24
  key={index}
21
25
  to={resourceUrl(path)}
22
26
  >
23
- <span className="filterModalOpenButton-filterButton-qRo button-root_lowPriority-Qoh button-root-MFn border-2 border-solid cursor-pointer font-bold inline-flex items-center justify-center leading-tight max-w-full min-w-[10rem] outline-none pointer-events-auto px-sm rounded-full text-center text-sm uppercase disabled_bg-gray-400 disabled_border-gray-400 disabled_opacity-50 disabled_pointer-events-none disabled_text-white focus_shadow-inputFocus bg-transparent border-gray-700 text-gray-700 active_border-gray-800 active_text-gray-800 hover_border-gray-800 hover_text-gray-800 min-w-[6.25rem]" style={{"margin":"10px"}}>{text}</span>
27
+ <li className={classes.item}>{text}</li>
24
28
  </Link>
25
29
  )
26
30
  });
27
31
 
28
- return subCategory;
32
+ return <ul className={classes.root}>{subCategory}</ul>;
29
33
  };
30
34
 
31
35
  export default SubCategory;
@@ -0,0 +1,22 @@
1
+ .root {
2
+ composes: flex from global;
3
+ composes: flex-wrap from global;
4
+ composes: mt-3 from global;
5
+ composes: gap-[15px] from global;
6
+ margin-bottom: 30px;
7
+ }
8
+
9
+ .item {
10
+ composes: px-4 from global;
11
+ composes: py-2 from global;
12
+ composes: transition-colors from global;
13
+ composes: duration-150 from global;
14
+ composes: border from global;
15
+ composes: border-solid from global;
16
+ composes: leading-normal from global;
17
+ composes: text-[14px] from global;
18
+ composes: text-colorDefault from global;
19
+ composes: bg-white from global;
20
+ composes: border-gray-100 from global;
21
+ border-radius: 5px;
22
+ }
@@ -0,0 +1,49 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ import { CategoryFragment, ProductsFragment } from './categoryFragments.gql';
4
+
5
+ export const GET_CATEGORY = gql`
6
+ query GetCategories(
7
+ $id: String!
8
+ $pageSize: Int!
9
+ $currentPage: Int!
10
+ $filters: ProductAttributeFilterInput!
11
+ $sort: ProductAttributeSortInput
12
+ ) {
13
+ categories(filters: { category_uid: { in: [$id] } }) {
14
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
15
+ items {
16
+ uid
17
+ ...CategoryFragment
18
+ }
19
+ }
20
+ products(
21
+ pageSize: $pageSize
22
+ currentPage: $currentPage
23
+ filter: $filters
24
+ sort: $sort
25
+ ) {
26
+ ...ProductsFragment
27
+ }
28
+ }
29
+ ${CategoryFragment}
30
+ ${ProductsFragment}
31
+ `;
32
+
33
+ export const GET_FILTER_INPUTS = gql`
34
+ query GetFilterInputsForCategory {
35
+ __type(name: "ProductAttributeFilterInput") {
36
+ inputFields {
37
+ name
38
+ type {
39
+ name
40
+ }
41
+ }
42
+ }
43
+ }
44
+ `;
45
+
46
+ export default {
47
+ getCategoryQuery: GET_CATEGORY,
48
+ getFilterInputsQuery: GET_FILTER_INPUTS
49
+ };
@@ -0,0 +1,51 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ export const CategoryFragment = gql`
4
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
5
+ fragment CategoryFragment on CategoryTree {
6
+ uid
7
+ meta_title
8
+ meta_keywords
9
+ meta_description
10
+ }
11
+ `;
12
+
13
+ export const ProductsFragment = gql`
14
+ fragment ProductsFragment on Products {
15
+ items {
16
+ id
17
+ uid
18
+ name
19
+ seller {
20
+ name
21
+ }
22
+ price_range {
23
+ maximum_price {
24
+ final_price {
25
+ currency
26
+ value
27
+ }
28
+ regular_price {
29
+ currency
30
+ value
31
+ }
32
+ discount {
33
+ amount_off
34
+ }
35
+ }
36
+ }
37
+ sku
38
+ small_image {
39
+ url
40
+ }
41
+ stock_status
42
+ rating_summary
43
+ __typename
44
+ url_key
45
+ }
46
+ page_info {
47
+ total_pages
48
+ }
49
+ total_count
50
+ }
51
+ `;
@@ -0,0 +1,227 @@
1
+ import { useEffect, useMemo, useRef } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+ import { useLazyQuery, useQuery } from '@apollo/client';
4
+
5
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
6
+ import { useAppContext } from '@magento/peregrine/lib/context/app';
7
+ import { usePagination } from '@magento/peregrine/lib/hooks/usePagination';
8
+ import { useScrollTopOnChange } from '@magento/peregrine/lib/hooks/useScrollTopOnChange';
9
+ import { useSort } from '@magento/peregrine/lib/hooks/useSort';
10
+ import {
11
+ getFiltersFromSearch,
12
+ getFilterInput
13
+ } from '@magento/peregrine/lib/talons/FilterModal/helpers';
14
+
15
+ import DEFAULT_OPERATIONS from './category.gql';
16
+
17
+ /**
18
+ * A [React Hook]{@link https://reactjs.org/docs/hooks-intro.html} that
19
+ * controls the logic for the Category Root Component.
20
+ *
21
+ * @kind function
22
+ *
23
+ * @param {object} props
24
+ * @param {String} props.id - Category uid.
25
+ * @param {GraphQLAST} props.operations.getCategoryQuery - Fetches category using a server query
26
+ * @param {GraphQLAST} props.operations.getFilterInputsQuery - Fetches "allowed" filters using a server query
27
+ * @param {GraphQLAST} props.queries.getStoreConfig - Fetches store configuration using a server query
28
+ *
29
+ * @returns {object} result
30
+ * @returns {object} result.error - Indicates a network error occurred.
31
+ * @returns {object} result.categoryData - Category data.
32
+ * @returns {bool} result.isLoading - Category data loading.
33
+ * @returns {string} result.metaDescription - Category meta description.
34
+ * @returns {object} result.pageControl - Category pagination state.
35
+ * @returns {array} result.sortProps - Category sorting parameters.
36
+ * @returns {number} result.pageSize - Category total pages.
37
+ */
38
+ export const useCategory = props => {
39
+ const {
40
+ id,
41
+ queries: { getPageSize }
42
+ } = props;
43
+
44
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
45
+ const { getCategoryQuery, getFilterInputsQuery } = operations;
46
+
47
+ const { data: pageSizeData } = useQuery(getPageSize, {
48
+ fetchPolicy: 'cache-and-network',
49
+ nextFetchPolicy: 'cache-first'
50
+ });
51
+ const pageSize = pageSizeData && pageSizeData.storeConfig.grid_per_page;
52
+
53
+ const [paginationValues, paginationApi] = usePagination();
54
+ const { currentPage, totalPages } = paginationValues;
55
+ const { setCurrentPage, setTotalPages } = paginationApi;
56
+
57
+ const sortProps = useSort({ sortFromSearch: false });
58
+ const [currentSort] = sortProps;
59
+
60
+ // Keep track of the sort criteria so we can tell when they change.
61
+ const previousSort = useRef(currentSort);
62
+
63
+ const pageControl = {
64
+ currentPage,
65
+ setPage: setCurrentPage,
66
+ totalPages
67
+ };
68
+
69
+ const [
70
+ ,
71
+ {
72
+ actions: { setPageLoading }
73
+ }
74
+ ] = useAppContext();
75
+
76
+ const [runQuery, queryResponse] = useLazyQuery(getCategoryQuery, {
77
+ fetchPolicy: 'cache-and-network',
78
+ nextFetchPolicy: 'cache-first'
79
+ });
80
+ const {
81
+ called: categoryCalled,
82
+ loading: categoryLoading,
83
+ error,
84
+ data
85
+ } = queryResponse;
86
+ const { search } = useLocation();
87
+
88
+ const isBackgroundLoading = !!data && categoryLoading;
89
+
90
+ // Update the page indicator if the GraphQL query is in flight.
91
+ useEffect(() => {
92
+ setPageLoading(isBackgroundLoading);
93
+ }, [isBackgroundLoading, setPageLoading]);
94
+
95
+ // Keep track of the search terms so we can tell when they change.
96
+ const previousSearch = useRef(search);
97
+
98
+ // Get "allowed" filters by intersection of schema and aggregations
99
+ const {
100
+ called: introspectionCalled,
101
+ data: introspectionData,
102
+ loading: introspectionLoading
103
+ } = useQuery(getFilterInputsQuery);
104
+
105
+ // Create a type map we can reference later to ensure we pass valid args
106
+ // to the graphql query.
107
+ // For example: { category_id: 'FilterEqualTypeInput', price: 'FilterRangeTypeInput' }
108
+ const filterTypeMap = useMemo(() => {
109
+ const typeMap = new Map();
110
+ if (introspectionData) {
111
+ introspectionData.__type.inputFields.forEach(({ name, type }) => {
112
+ typeMap.set(name, type.name);
113
+ });
114
+ }
115
+ return typeMap;
116
+ }, [introspectionData]);
117
+
118
+ // Run the category query immediately and whenever its variable values change.
119
+ useEffect(() => {
120
+ // Wait until we have the type map to fetch product data.
121
+ if (!filterTypeMap.size || !pageSize) {
122
+ return;
123
+ }
124
+
125
+ const filters = getFiltersFromSearch(search);
126
+
127
+ // Construct the filter arg object.
128
+ const newFilters = {};
129
+ filters.forEach((values, key) => {
130
+ newFilters[key] = getFilterInput(values, filterTypeMap.get(key));
131
+ });
132
+
133
+ // Use the category uid for the current category page regardless of the
134
+ // applied filters. Follow-up in PWA-404.
135
+ newFilters['category_uid'] = { eq: id };
136
+
137
+ runQuery({
138
+ variables: {
139
+ currentPage: Number(currentPage),
140
+ id: id,
141
+ filters: newFilters,
142
+ pageSize: Number(pageSize),
143
+ sort: { [currentSort.sortAttribute]: currentSort.sortDirection }
144
+ }
145
+ });
146
+ }, [
147
+ currentPage,
148
+ currentSort,
149
+ filterTypeMap,
150
+ id,
151
+ pageSize,
152
+ runQuery,
153
+ search
154
+ ]);
155
+
156
+ const totalPagesFromData = data
157
+ ? data.products.page_info.total_pages
158
+ : null;
159
+
160
+ useEffect(() => {
161
+ setTotalPages(totalPagesFromData);
162
+ return () => {
163
+ setTotalPages(null);
164
+ };
165
+ }, [setTotalPages, totalPagesFromData]);
166
+
167
+ // If we get an error after loading we should try to reset to page 1.
168
+ // If we continue to have errors after that, render an error message.
169
+ useEffect(() => {
170
+ if (error && !categoryLoading && !data && currentPage !== 1) {
171
+ setCurrentPage(1);
172
+ }
173
+ }, [currentPage, error, categoryLoading, setCurrentPage, data]);
174
+
175
+ // Reset the current page back to one (1) when the search string, filters
176
+ // or sort criteria change.
177
+ useEffect(() => {
178
+ // We don't want to compare page value.
179
+ const prevSearch = new URLSearchParams(previousSearch.current);
180
+ const nextSearch = new URLSearchParams(search);
181
+ prevSearch.delete('page');
182
+ nextSearch.delete('page');
183
+
184
+ if (
185
+ prevSearch.toString() !== nextSearch.toString() ||
186
+ previousSort.current.sortAttribute.toString() !==
187
+ currentSort.sortAttribute.toString() ||
188
+ previousSort.current.sortDirection.toString() !==
189
+ currentSort.sortDirection.toString()
190
+ ) {
191
+ // The search term changed.
192
+ setCurrentPage(1, true);
193
+ // And update the ref.
194
+ previousSearch.current = search;
195
+ previousSort.current = currentSort;
196
+ }
197
+ }, [currentSort, previousSearch, search, setCurrentPage]);
198
+
199
+ const categoryData = categoryLoading && !data ? null : data;
200
+ const categoryNotFound =
201
+ !categoryLoading && data && data.categories.items.length === 0;
202
+ const metaDescription =
203
+ data &&
204
+ data.categories.items[0] &&
205
+ data.categories.items[0].meta_description
206
+ ? data.categories.items[0].meta_description
207
+ : '';
208
+
209
+ // When only categoryLoading is involved, noProductsFound component flashes for a moment
210
+ const loading =
211
+ (introspectionCalled && !categoryCalled) ||
212
+ (categoryLoading && !data) ||
213
+ introspectionLoading;
214
+
215
+ useScrollTopOnChange(currentPage);
216
+
217
+ return {
218
+ error,
219
+ categoryData,
220
+ loading,
221
+ metaDescription,
222
+ pageControl,
223
+ sortProps,
224
+ pageSize,
225
+ categoryNotFound
226
+ };
227
+ };
@@ -49,6 +49,7 @@
49
49
  composes: text-[14px] from global;
50
50
  composes: lg_m-0 from global;
51
51
  composes: lg_text-left from global;
52
+ composes: py-4 from global;
52
53
  }
53
54
 
54
55
  .headerButtons {
@@ -73,5 +74,4 @@
73
74
 
74
75
  composes: lg_flex from global;
75
76
  composes: lg_self-start from global;
76
- width: 500px;
77
77
  }
@@ -23,6 +23,8 @@ import SortedByContainer, {
23
23
  import defaultClasses from './category.module.css';
24
24
  import NoProductsFound from './NoProductsFound';
25
25
  import cn from 'classnames';
26
+ import ProductListTab, { ProductListTabShimmer } from '@riosst100/pwa-marketplace/src/components/ProductListTab';
27
+ import SubCategory from '@riosst100/pwa-marketplace/src/components/SubCategory/subCategory';
26
28
 
27
29
  const FilterModal = React.lazy(() => import('@magento/venia-ui/lib/components/FilterModal'));
28
30
  const FilterSidebar = React.lazy(() =>
@@ -52,6 +54,7 @@ const CategoryContent = props => {
52
54
  categoryDescription,
53
55
  filters,
54
56
  items,
57
+ children,
55
58
  totalCount,
56
59
  totalPagesFromData
57
60
  } = talonProps;
@@ -94,12 +97,21 @@ const CategoryContent = props => {
94
97
  <ProductSortShimmer />
95
98
  ) : null;
96
99
 
100
+ const shouldShowProductListTab = totalPagesFromData;
101
+ const shouldShowProductListTabShimmer = !totalPagesFromData && isLoading;
102
+
97
103
  const maybeSortContainer = shouldShowSortButtons ? (
98
104
  <SortedByContainer currentSort={currentSort} />
99
105
  ) : shouldShowSortShimmer ? (
100
106
  <SortedByContainerShimmer />
101
107
  ) : null;
102
108
 
109
+ const maybeProductsTabContainer = shouldShowProductListTab ? (
110
+ <ProductListTab />
111
+ ) : shouldShowProductListTabShimmer ? (
112
+ <ProductListTabShimmer />
113
+ ) : null;
114
+
103
115
  const categoryResultsHeading =
104
116
  totalCount > 0 ? (
105
117
  <FormattedMessage
@@ -153,7 +165,6 @@ const CategoryContent = props => {
153
165
  return (
154
166
  <Fragment>
155
167
  <Breadcrumbs categoryId={categoryId} />
156
- {/* <SubCategory children={children} /> */}
157
168
  <StoreTitle>{categoryName}</StoreTitle>
158
169
  <article className={classes.root} data-cy="CategoryContent-root">
159
170
  <div className={classes.categoryHeader}>
@@ -167,6 +178,7 @@ const CategoryContent = props => {
167
178
  </h1>
168
179
  {categoryDescriptionElement}
169
180
  </div>
181
+ <SubCategory children={children} />
170
182
  <div className={classes.contentWrapper}>
171
183
  <div ref={sidebarRef} className={classes.sidebar}>
172
184
  <Suspense fallback={<FilterSidebarShimmer />}>
@@ -174,6 +186,14 @@ const CategoryContent = props => {
174
186
  </Suspense>
175
187
  </div>
176
188
  <div className={classes.categoryContent}>
189
+ <div className={cn(classes.heading)}>
190
+ {maybeProductsTabContainer}
191
+ <div className={classes.headerButtons}>
192
+ {maybeFilterButtons}
193
+ {maybeSortButton}
194
+ </div>
195
+ {maybeSortContainer}
196
+ </div>
177
197
  <div className={cn(classes.heading)}>
178
198
  <div
179
199
  data-cy="CategoryContent-categoryInfo"
@@ -181,11 +201,6 @@ const CategoryContent = props => {
181
201
  >
182
202
  {categoryResultsHeading}
183
203
  </div>
184
- <div className={classes.headerButtons}>
185
- {maybeFilterButtons}
186
- {maybeSortButton}
187
- </div>
188
- {maybeSortContainer}
189
204
  </div>
190
205
  {content}
191
206
  <Suspense fallback={null}>{filtersModal}</Suspense>
@@ -62,7 +62,7 @@ const FilterList = props => {
62
62
  elements1.push(
63
63
  <li
64
64
  key={key}
65
- className={classes.item}
65
+ className={classes.popupFilterItem}
66
66
  data-cy="FilterList-item"
67
67
  >
68
68
  <FilterItem
@@ -82,7 +82,7 @@ const FilterList = props => {
82
82
  // return element2;
83
83
  });
84
84
 
85
- const element2 = <ul><div><b>{grouping}</b></div>{elements1}</ul>;
85
+ const element2 = <><ul><div><b>{grouping}</b></div>{elements1}</ul><br /></>;
86
86
 
87
87
  return element2;
88
88
  });
@@ -177,8 +177,8 @@ const FilterList = props => {
177
177
  defaultMessage: 'Show Less'
178
178
  })
179
179
  : formatMessage({
180
- id: 'filterList.showMore',
181
- defaultMessage: 'Show More'
180
+ id: 'filterList.showAll',
181
+ defaultMessage: 'Show All'
182
182
  });
183
183
 
184
184
  return (
@@ -207,8 +207,8 @@ const FilterList = props => {
207
207
  }
208
208
 
209
209
  const label = formatMessage({
210
- id: 'filterList.showMore',
211
- defaultMessage: 'Show More'
210
+ id: 'filterList.viewAll',
211
+ defaultMessage: 'View All'
212
212
  });
213
213
 
214
214
  return (
@@ -17,4 +17,12 @@
17
17
  composes: underline from global;
18
18
 
19
19
  composes: hover_no-underline from global;
20
+ }
21
+
22
+ .popupFilterItem {
23
+ border: 1px solid #e1e1e1;
24
+ padding: 10px;
25
+ border-radius: 5px;
26
+ width: 100%;
27
+ margin-top: 10px;
20
28
  }
@@ -4,7 +4,8 @@
4
4
  composes: grid from global;
5
5
  composes: h-full from global;
6
6
  composes: left-0 from global;
7
- composes: max-w-modal from global;
7
+ /* composes: max-w-modal from global;
8
+ */
8
9
  composes: opacity-0 from global;
9
10
  composes: overflow-hidden from global;
10
11
  composes: fixed from global;
@@ -2,8 +2,9 @@
2
2
  composes: bg-white from global;
3
3
  composes: bottom-0 from global;
4
4
  composes: hidden from global;
5
- composes: max-w-modal from global;
6
- composes: w-full from global;
5
+ /* composes: max-w-modal from global; */
6
+ width: 220px;
7
+ /* composes: w-full from global; */
7
8
  composes: z-foreground from global;
8
9
  grid-template-rows: 1fr 7rem;
9
10
 
@@ -82,7 +82,7 @@ const Header = props => {
82
82
  data-cy="Header-logoContainer"
83
83
  >
84
84
  <div><b>TCG Collective</b></div>
85
- <small>Toys | Card | Games</small>
85
+ <small>Bringing Collectors Together</small>
86
86
  </Link>
87
87
  </div>
88
88
  <div className='flex flex-auto max-w-[700px]'>