@riosst100/pwa-marketplace 1.5.5 → 1.5.7
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/package.json +1 -1
- package/src/componentOverrideMapping.js +1 -0
- package/src/components/CollectibleGameSets/collectibleGameSets.js +38 -21
- package/src/components/CustomSortBy/customSortBy.js +198 -0
- package/src/components/CustomSortBy/customSortBy.module.css +68 -0
- package/src/components/CustomSortBy/customSortItem.js +57 -0
- package/src/components/CustomSortBy/customSortItem.module.css +23 -0
- package/src/components/CustomSortBy/index.js +1 -0
- package/src/components/FilterTop/CustomFilters/customFilters.module.css +0 -1
- package/src/components/ProductListTab/productListTab.js +1 -1
- package/src/components/SubCategory/customSubCategory.js +35 -0
- package/src/components/SubCategory/customSubCategory.module.css +22 -0
- package/src/components/SubCategory/subCategory.js +24 -6
- package/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js +252 -0
- package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js +7 -1
- package/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js +34 -5
- package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +33 -9
- package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +30 -5
- package/src/overwrites/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilters.js +12 -10
- package/src/overwrites/venia-ui/lib/components/FilterModal/FilterList/filterList.js +1 -1
- package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js +22 -14
- package/src/talons/CollectibleGameSets/collectibleGameSets.gql.js +1 -0
- package/src/talons/CollectibleGameSets/useCollectibleGameSets.js +2 -6
- package/src/talons/SubCategory/useCustomSubCategory.js +49 -0
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@ module.exports = componentOverrideMapping = {
|
|
|
24
24
|
[`@magento/peregrine/lib/talons/RootComponents/Category/useCategory.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategory.js',
|
|
25
25
|
[`@magento/peregrine/lib/talons/Breadcrumbs/useBreadcrumbs.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/Breadcrumbs/useBreadcrumbs.js',
|
|
26
26
|
[`@magento/peregrine/lib/talons/FilterModal/useFilterList.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/FilterModal/useFilterList.js',
|
|
27
|
+
[`@magento/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js`]: '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js',
|
|
27
28
|
[`@magento/venia-ui/lib/components/FilterSidebar/filterSidebar.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js',
|
|
28
29
|
[`@magento/venia-ui/lib/components/TextInput/index.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/TextInput/index.js',
|
|
29
30
|
[`@magento/venia-ui/lib/components/Pagination/index.js`]: '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/Pagination/index.js',
|
|
@@ -11,16 +11,20 @@ import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
|
11
11
|
import cn from 'classnames';
|
|
12
12
|
import Divider from '@riosst100/pwa-marketplace/src/components/Divider';
|
|
13
13
|
import { CollectibleGameSetsShimmer } from '@riosst100/pwa-marketplace/src/components/CollectibleGameSets';
|
|
14
|
+
import ProductSort from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/ProductSort';
|
|
15
|
+
import CustomSortBy from '@riosst100/pwa-marketplace/src/components/CustomSortBy';
|
|
14
16
|
|
|
15
17
|
const CollectibleGameSets = props => {
|
|
16
|
-
|
|
17
|
-
const { product_type } = props;
|
|
18
|
-
|
|
19
18
|
const [active, setActive] = useState('all');
|
|
20
19
|
|
|
20
|
+
const [sortBy, setSortBy] = useState({
|
|
21
|
+
sortText: 'All (A-Z)',
|
|
22
|
+
value: 'all'
|
|
23
|
+
});
|
|
24
|
+
|
|
21
25
|
const classes = useStyle(defaultClasses);
|
|
22
26
|
|
|
23
|
-
const talonProps = useCollectibleGameSets(
|
|
27
|
+
const talonProps = useCollectibleGameSets();
|
|
24
28
|
|
|
25
29
|
const { error, loading, collectibleGameSets, categoryUrlSuffix, categoryUrlKey, productType } = talonProps;
|
|
26
30
|
|
|
@@ -28,20 +32,12 @@ const CollectibleGameSets = props => {
|
|
|
28
32
|
return <CollectibleGameSetsShimmer />;
|
|
29
33
|
if (error && !collectibleGameSets) return <ErrorView />;
|
|
30
34
|
|
|
31
|
-
if (!collectibleGameSets) {
|
|
32
|
-
return
|
|
33
|
-
<h1>
|
|
34
|
-
<FormattedMessage
|
|
35
|
-
id={'seller.notFound'}
|
|
36
|
-
defaultMessage={
|
|
37
|
-
'Seller Not Found.'
|
|
38
|
-
}
|
|
39
|
-
/>
|
|
40
|
-
</h1>
|
|
41
|
-
);
|
|
35
|
+
if (!collectibleGameSets && !loading && !error) {
|
|
36
|
+
return <CollectibleGameSetsShimmer />;
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
const setsLengthArr = [];
|
|
40
|
+
const groupByYear = [];
|
|
45
41
|
|
|
46
42
|
const setRelases = collectibleGameSets.map((setRelease, index) => {
|
|
47
43
|
const { release_type, sets } = setRelease;
|
|
@@ -52,7 +48,9 @@ const CollectibleGameSets = props => {
|
|
|
52
48
|
|
|
53
49
|
if (sets.length) {
|
|
54
50
|
sets.map((set, index) => {
|
|
55
|
-
const { set_name, option_id, set_abbreviation } = set;
|
|
51
|
+
const { set_name, option_id, set_abbreviation, release_year } = set;
|
|
52
|
+
|
|
53
|
+
groupByYear[release_year] = set;
|
|
56
54
|
|
|
57
55
|
const categoryUrl = resourceUrl(
|
|
58
56
|
`/games/collectible-game/${categoryUrlKey}${categoryUrlSuffix || ''}?card_set[filter]=${set_name},${option_id}`
|
|
@@ -79,20 +77,39 @@ const CollectibleGameSets = props => {
|
|
|
79
77
|
);
|
|
80
78
|
});
|
|
81
79
|
|
|
80
|
+
console.log(groupByYear)
|
|
81
|
+
|
|
82
82
|
const handleActive = (val) => {
|
|
83
83
|
setActive(val);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const alpha = ['#', 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
|
|
87
87
|
|
|
88
|
+
const availableSortBy = [
|
|
89
|
+
{
|
|
90
|
+
'label': 'All (A-Z)',
|
|
91
|
+
'value': 'all'
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
'label': 'Newest',
|
|
95
|
+
'value': 'newest'
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
'label': 'All (Date)',
|
|
99
|
+
'value': 'all-date'
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
|
|
88
103
|
return (
|
|
89
|
-
|
|
104
|
+
<Fragment>
|
|
105
|
+
<StoreTitle>{"Magic: The Gathering" + " | " + (productType == "expansion-sets" ? "Expansion Sets" : "All Sets")}</StoreTitle>
|
|
90
106
|
<h1 className='mx-auto relative block text-xl font-bold text-center pt-10 pb-4'>
|
|
91
|
-
{productType == "
|
|
107
|
+
{productType == "expansion-sets" ? "Expansion Sets" : "All Sets"}
|
|
92
108
|
</h1>
|
|
93
109
|
<div className='border border-gray-100 px-6'>
|
|
94
|
-
{productType != "
|
|
110
|
+
{productType != "expansion-sets" ? (
|
|
95
111
|
<>
|
|
112
|
+
<CustomSortBy sortProps={[sortBy, setSortBy]} availableSortMethods={availableSortBy} />
|
|
96
113
|
<section className='single_list-indexing-container relative m-auto py-10'>
|
|
97
114
|
<ul className='flex gap-2 justify-center flex-wrap'>
|
|
98
115
|
<li>
|
|
@@ -136,8 +153,8 @@ const CollectibleGameSets = props => {
|
|
|
136
153
|
</div>
|
|
137
154
|
</section>
|
|
138
155
|
</div>
|
|
139
|
-
|
|
156
|
+
</Fragment>
|
|
140
157
|
);
|
|
141
|
-
}
|
|
158
|
+
}
|
|
142
159
|
|
|
143
160
|
export default CollectibleGameSets;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import { ChevronDown as ArrowDown } from 'react-feather';
|
|
3
|
+
import { FormattedMessage, useIntl } from 'react-intl';
|
|
4
|
+
import { array, arrayOf, shape, string } from 'prop-types';
|
|
5
|
+
import { useDropdown } from '@magento/peregrine/lib/hooks/useDropdown';
|
|
6
|
+
|
|
7
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
8
|
+
import SortItem from './customSortItem';
|
|
9
|
+
import defaultClasses from './customSortBy.module.css';
|
|
10
|
+
import Button from '@magento/venia-ui/lib/components/Button';
|
|
11
|
+
// import Icon from '@magento/venia-ui/lib/components/Icon';
|
|
12
|
+
import { ArrowUp2 } from 'iconsax-react';
|
|
13
|
+
import cn from 'classnames';
|
|
14
|
+
|
|
15
|
+
const CustomSortBy = props => {
|
|
16
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
17
|
+
const { availableSortMethods, sortProps } = props;
|
|
18
|
+
const [currentSort, setSort] = sortProps;
|
|
19
|
+
const { elementRef, expanded, setExpanded } = useDropdown();
|
|
20
|
+
const { formatMessage, locale } = useIntl();
|
|
21
|
+
|
|
22
|
+
const orderSortingList = useCallback(
|
|
23
|
+
list => {
|
|
24
|
+
return list.sort((a, b) => {
|
|
25
|
+
return a.text.localeCompare(b.text, locale, {
|
|
26
|
+
sensitivity: 'base'
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
[locale]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const sortMethodsFromConfig = availableSortMethods
|
|
34
|
+
? availableSortMethods
|
|
35
|
+
.map(method => {
|
|
36
|
+
const { value, label } = method;
|
|
37
|
+
if (value !== 'price' && value !== 'position') {
|
|
38
|
+
return {
|
|
39
|
+
id: `sortItem.${value}`,
|
|
40
|
+
text: label,
|
|
41
|
+
attribute: value,
|
|
42
|
+
sortDirection: 'ASC'
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
.filter(method => !!method)
|
|
47
|
+
: null;
|
|
48
|
+
|
|
49
|
+
// click event for menu items
|
|
50
|
+
const handleItemClick = useCallback(
|
|
51
|
+
sortAttribute => {
|
|
52
|
+
setSort(prevSort => {
|
|
53
|
+
return {
|
|
54
|
+
sortText: sortAttribute.text,
|
|
55
|
+
value: sortAttribute.attribute,
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
setExpanded(false);
|
|
59
|
+
},
|
|
60
|
+
[setExpanded, setSort]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const sortElements = useMemo(() => {
|
|
64
|
+
// should be not render item in collapsed mode.
|
|
65
|
+
if (!expanded) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const defaultSortMethods = [
|
|
70
|
+
{
|
|
71
|
+
id: 'sortItem.relevance',
|
|
72
|
+
text: formatMessage({
|
|
73
|
+
id: 'sortItem.relevance',
|
|
74
|
+
defaultMessage: 'Best Match'
|
|
75
|
+
}),
|
|
76
|
+
attribute: 'relevance',
|
|
77
|
+
sortDirection: 'DESC'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'sortItem.priceDesc',
|
|
81
|
+
text: formatMessage({
|
|
82
|
+
id: 'sortItem.priceDesc',
|
|
83
|
+
defaultMessage: 'Price: High to Low'
|
|
84
|
+
}),
|
|
85
|
+
attribute: 'price',
|
|
86
|
+
sortDirection: 'DESC'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'sortItem.priceAsc',
|
|
90
|
+
text: formatMessage({
|
|
91
|
+
id: 'sortItem.priceAsc',
|
|
92
|
+
defaultMessage: 'Price: Low to High'
|
|
93
|
+
}),
|
|
94
|
+
attribute: 'price',
|
|
95
|
+
sortDirection: 'ASC'
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const allSortingMethods = sortMethodsFromConfig
|
|
100
|
+
? sortMethodsFromConfig
|
|
101
|
+
: defaultSortMethods;
|
|
102
|
+
|
|
103
|
+
const itemElements = Array.from(allSortingMethods, sortItem => {
|
|
104
|
+
const { attribute, sortDirection } = sortItem;
|
|
105
|
+
const isActive =
|
|
106
|
+
currentSort.value === attribute;
|
|
107
|
+
|
|
108
|
+
const key = `${attribute}--${sortDirection}`;
|
|
109
|
+
return (
|
|
110
|
+
<li key={key} className={classes.menuItem}>
|
|
111
|
+
<SortItem
|
|
112
|
+
sortItem={sortItem}
|
|
113
|
+
active={isActive}
|
|
114
|
+
onClick={handleItemClick}
|
|
115
|
+
/>
|
|
116
|
+
</li>
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div className={classes.menu}>
|
|
122
|
+
<ul className='flex flex-col gap-2'>{itemElements}</ul>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}, [
|
|
126
|
+
classes.menu,
|
|
127
|
+
classes.menuItem,
|
|
128
|
+
currentSort.sortAttribute,
|
|
129
|
+
currentSort.sortDirection,
|
|
130
|
+
currentSort.sortFromSearch,
|
|
131
|
+
expanded,
|
|
132
|
+
formatMessage,
|
|
133
|
+
handleItemClick,
|
|
134
|
+
orderSortingList,
|
|
135
|
+
sortMethodsFromConfig
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
// expand or collapse on click
|
|
139
|
+
const handleSortClick = () => {
|
|
140
|
+
setExpanded(!expanded);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const handleKeypress = e => {
|
|
144
|
+
if (e.code == 'Enter') {
|
|
145
|
+
setExpanded(expanded);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const result = expanded
|
|
149
|
+
? formatMessage({
|
|
150
|
+
id: 'productSort.sortButtonExpanded',
|
|
151
|
+
defaultMessage: 'Sort Button Expanded'
|
|
152
|
+
})
|
|
153
|
+
: formatMessage({
|
|
154
|
+
id: 'productSort.sortButtonCollapsed',
|
|
155
|
+
defaultMessage: 'Sort Button Collapsed'
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div
|
|
160
|
+
ref={elementRef}
|
|
161
|
+
className={classes.root}
|
|
162
|
+
data-cy="CustomSortBy-root"
|
|
163
|
+
aria-busy="false"
|
|
164
|
+
>
|
|
165
|
+
<Button
|
|
166
|
+
priority={'low'}
|
|
167
|
+
classes={{
|
|
168
|
+
root_lowPriority: classes.sortButton
|
|
169
|
+
}}
|
|
170
|
+
onClick={handleSortClick}
|
|
171
|
+
data-cy="CustomSortBy-sortButton"
|
|
172
|
+
onKeyDown={handleKeypress}
|
|
173
|
+
aria-label={result}
|
|
174
|
+
className='border border-gray-100 border-solid rounded-[5px] p-2.5 flex gap-x-[15px]'
|
|
175
|
+
>
|
|
176
|
+
<span className={classes.mobileText}>
|
|
177
|
+
<FormattedMessage
|
|
178
|
+
id={'productSort.sortButton'}
|
|
179
|
+
defaultMessage={'Sort'}
|
|
180
|
+
/>
|
|
181
|
+
</span>
|
|
182
|
+
<span className={cn(classes.desktopText, 'flex items-center gap-[15px]')}>
|
|
183
|
+
<span className={classes.sortText}>
|
|
184
|
+
<FormattedMessage
|
|
185
|
+
id={'productSort.sortByButton'}
|
|
186
|
+
defaultMessage={'Sort by'}
|
|
187
|
+
/>
|
|
188
|
+
{currentSort.sortText}
|
|
189
|
+
</span>
|
|
190
|
+
<ArrowUp2 size={15} className={cn('text-gray-900 transition-all stroke-[#292D32]', expanded ? 'rotate-0' : 'rotate-180')} />
|
|
191
|
+
</span>
|
|
192
|
+
</Button>
|
|
193
|
+
{sortElements}
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export default CustomSortBy;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
composes: relative from global;
|
|
3
|
+
composes: ml-2xs from global;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: row-reverse;
|
|
6
|
+
margin-top: 20px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.menu {
|
|
10
|
+
composes: absolute from global;
|
|
11
|
+
composes: bg-clip-padding from global;
|
|
12
|
+
composes: bg-white from global;
|
|
13
|
+
composes: border from global;
|
|
14
|
+
composes: border-solid from global;
|
|
15
|
+
composes: border-gray-100 from global;
|
|
16
|
+
composes: list-none from global;
|
|
17
|
+
composes: mb-0 from global;
|
|
18
|
+
composes: min-w-[10rem] from global;
|
|
19
|
+
composes: mt-0.5 from global;
|
|
20
|
+
composes: mx-0 from global;
|
|
21
|
+
composes: right-0 from global;
|
|
22
|
+
composes: rounded from global;
|
|
23
|
+
composes: shadow-menu from global;
|
|
24
|
+
composes: text-colorDefault from global;
|
|
25
|
+
composes: text-left from global;
|
|
26
|
+
composes: top-[110%] from global;
|
|
27
|
+
composes: z-menu from global;
|
|
28
|
+
composes: p-2.5 from global;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.menuItem {
|
|
32
|
+
composes: hover_bg-gray-100 from global;
|
|
33
|
+
composes: rounded-[5px] from global;
|
|
34
|
+
composes: relative from global;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.sortButton {
|
|
38
|
+
composes: rounded-[5px] from global;
|
|
39
|
+
composes: p-2.5 from global;
|
|
40
|
+
|
|
41
|
+
composes: min-w-[154px] from global;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.desktopText {
|
|
45
|
+
composes: hidden from global;
|
|
46
|
+
|
|
47
|
+
composes: lg_inline-flex from global;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.sortText {
|
|
51
|
+
composes: leading-normal from global;
|
|
52
|
+
composes: text-colorDefault from global;
|
|
53
|
+
composes: text-base from global;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.desktopIconWrapper {
|
|
57
|
+
transform: translateX(10px);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.desktopIcon {
|
|
61
|
+
/* composes: icon from '../Icon/icon.module.css'; */
|
|
62
|
+
|
|
63
|
+
composes: stroke-gray-500 from global;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.mobileText {
|
|
67
|
+
composes: lg_hidden from global;
|
|
68
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { Check } from 'react-feather';
|
|
3
|
+
import { bool, func, shape, string } from 'prop-types';
|
|
4
|
+
|
|
5
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
6
|
+
import defaultClasses from './customSortItem.module.css';
|
|
7
|
+
import cn from 'classnames';
|
|
8
|
+
|
|
9
|
+
const CustomSortItem = props => {
|
|
10
|
+
const { active, onClick, sortItem } = props;
|
|
11
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
12
|
+
|
|
13
|
+
const handleClick = useCallback(
|
|
14
|
+
e => {
|
|
15
|
+
// use only left click for selection
|
|
16
|
+
if (e.button === 0) {
|
|
17
|
+
onClick(sortItem);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
[sortItem, onClick]
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const handleKeyDown = useCallback(
|
|
24
|
+
e => {
|
|
25
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
onClick(sortItem);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
[onClick, sortItem]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<button
|
|
35
|
+
className={cn(classes.root, active ? `before_block before_h-[100%] before_w-[3px] before_bg-blue-700 before_left-[-10px] before_absolute` : '')}
|
|
36
|
+
data-cy={active ? 'CustomSortItem-activeButton' : 'CustomSortItem-button'}
|
|
37
|
+
onMouseDown={handleClick}
|
|
38
|
+
onKeyDown={handleKeyDown}
|
|
39
|
+
>
|
|
40
|
+
<span className={classes.content}>
|
|
41
|
+
<span className={classes.text}>{sortItem.text}</span>
|
|
42
|
+
</span>
|
|
43
|
+
</button>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
CustomSortItem.propTypes = {
|
|
48
|
+
active: bool,
|
|
49
|
+
classes: shape({
|
|
50
|
+
content: string,
|
|
51
|
+
root: string,
|
|
52
|
+
text: string
|
|
53
|
+
}),
|
|
54
|
+
onClick: func
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default CustomSortItem;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
composes: flex from global;
|
|
3
|
+
composes: items-center from global;
|
|
4
|
+
composes: w-full from global;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.content {
|
|
8
|
+
composes: items-center from global;
|
|
9
|
+
composes: grid from global;
|
|
10
|
+
composes: grid-cols-[1fr] from global;
|
|
11
|
+
composes: grid-flow-col from global;
|
|
12
|
+
composes: gap-3 from global;
|
|
13
|
+
composes: h-[30px] from global;
|
|
14
|
+
composes: px-3 from global;
|
|
15
|
+
composes: py-0 from global;
|
|
16
|
+
composes: w-full from global;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.text {
|
|
20
|
+
composes: text-left from global;
|
|
21
|
+
composes: whitespace-nowrap from global;
|
|
22
|
+
composes: text-base from global;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './customSortBy';
|
|
@@ -29,7 +29,7 @@ const ProductListTab = props => {
|
|
|
29
29
|
</span> */}
|
|
30
30
|
<nav>
|
|
31
31
|
<ul className={classes.buttonContainer}>
|
|
32
|
-
<li><button className={
|
|
32
|
+
<li><button className={classes.activeFirstButton}>All Products</button></li>
|
|
33
33
|
<li><button className={classes.middleButton}>Pre Order</button></li>
|
|
34
34
|
<li><button className={classes.lastButton}>Auctions</button></li>
|
|
35
35
|
</ul>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useCustomSubCategory } from '@riosst100/pwa-marketplace/src/talons/SubCategory/useCustomSubCategory';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { Link } from 'react-router-dom';
|
|
4
|
+
import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
|
|
5
|
+
import defaultClasses from './customSubCategory.module.css';
|
|
6
|
+
import { useStyle } from '@magento/venia-ui/lib/classify';
|
|
7
|
+
|
|
8
|
+
const CustomSubCategory = props => {
|
|
9
|
+
const { customSubCategory } = props;
|
|
10
|
+
|
|
11
|
+
const talonProps = useCustomSubCategory({ customSubCategory });
|
|
12
|
+
|
|
13
|
+
const classes = useStyle(defaultClasses, props.classes);
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
normalizedData
|
|
17
|
+
} = talonProps;
|
|
18
|
+
|
|
19
|
+
const subCategory = [];
|
|
20
|
+
|
|
21
|
+
normalizedData && normalizedData.map(({ text, path }, index) => {
|
|
22
|
+
subCategory.push(
|
|
23
|
+
<Link
|
|
24
|
+
key={index}
|
|
25
|
+
to={resourceUrl(path)}
|
|
26
|
+
>
|
|
27
|
+
<li className={classes.item}>{text}</li>
|
|
28
|
+
</Link>
|
|
29
|
+
)
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return subCategory.length ? <ul className={classes.root}>{subCategory}</ul> : '';
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default CustomSubCategory;
|
|
@@ -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: 10px;
|
|
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-base 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
|
+
}
|
|
@@ -8,21 +8,39 @@ import { useFilterSidebar } from '@magento/peregrine/lib/talons/FilterSidebar';
|
|
|
8
8
|
import CollectibleGameSets from '@riosst100/pwa-marketplace/src/components/CollectibleGameSets/collectibleGameSets';
|
|
9
9
|
|
|
10
10
|
const SubCategory = props => {
|
|
11
|
-
|
|
11
|
+
const { children } = props;
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
const talonProps = useSubCategory({ children });
|
|
14
14
|
|
|
15
15
|
const classes = useStyle(defaultClasses, props.classes);
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const {
|
|
18
|
+
normalizedData
|
|
19
|
+
} = talonProps;
|
|
20
|
+
|
|
21
|
+
const subCategory = [];
|
|
22
|
+
|
|
23
|
+
normalizedData && normalizedData.map(({ text, path }, index) => {
|
|
24
|
+
subCategory.push(
|
|
25
|
+
<Link
|
|
26
|
+
key={index}
|
|
27
|
+
to={resourceUrl(path)}
|
|
28
|
+
>
|
|
29
|
+
<li className={classes.item}>{text}</li>
|
|
30
|
+
</Link>
|
|
31
|
+
)
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return subCategory.length ? <ul className={classes.root}>{subCategory}</ul> : '';
|
|
35
|
+
|
|
36
|
+
// const [activeTab, setActiveTab] = useState('singles')
|
|
18
37
|
|
|
19
38
|
return (
|
|
20
39
|
<>
|
|
21
40
|
<ul className={classes.root}>
|
|
22
|
-
<li className={classes.item}
|
|
23
|
-
<li className={classes.item}
|
|
41
|
+
<li className={classes.item}>All Sets</li>
|
|
42
|
+
<li className={classes.item}>Expansion Sets</li>
|
|
24
43
|
</ul>
|
|
25
|
-
{activeTab == "singles" ? <CollectibleGameSets product_type={"singles"} /> : <CollectibleGameSets product_type={"sealed-products"} />}
|
|
26
44
|
</>
|
|
27
45
|
)
|
|
28
46
|
};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useQuery } from '@apollo/client';
|
|
3
|
+
import { useHistory, useLocation } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
import { useAppContext } from '@magento/peregrine/lib/context/app';
|
|
6
|
+
|
|
7
|
+
import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
|
|
8
|
+
import { useFilterState } from '@magento/peregrine/lib/talons/FilterModal';
|
|
9
|
+
import {
|
|
10
|
+
getSearchFromState,
|
|
11
|
+
getStateFromSearch,
|
|
12
|
+
sortFiltersArray,
|
|
13
|
+
stripHtml
|
|
14
|
+
} from '@magento/peregrine/lib/talons/FilterModal/helpers';
|
|
15
|
+
|
|
16
|
+
import DEFAULT_OPERATIONS from '@magento/peregrine/lib/talons/FilterModal/filterModal.gql';
|
|
17
|
+
|
|
18
|
+
const DRAWER_NAME = 'filter';
|
|
19
|
+
|
|
20
|
+
export const useFilterSidebar = props => {
|
|
21
|
+
const { filters } = props;
|
|
22
|
+
|
|
23
|
+
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
24
|
+
const { getFilterInputsQuery } = operations;
|
|
25
|
+
|
|
26
|
+
const [isApplying, setIsApplying] = useState(false);
|
|
27
|
+
const [{ drawer }, { toggleDrawer, closeDrawer }] = useAppContext();
|
|
28
|
+
const [filterState, filterApi] = useFilterState();
|
|
29
|
+
const prevDrawer = useRef(null);
|
|
30
|
+
const isOpen = drawer === DRAWER_NAME;
|
|
31
|
+
|
|
32
|
+
const history = useHistory();
|
|
33
|
+
const { pathname, search } = useLocation();
|
|
34
|
+
|
|
35
|
+
const { data: introspectionData } = useQuery(getFilterInputsQuery);
|
|
36
|
+
|
|
37
|
+
const attributeCodes = useMemo(
|
|
38
|
+
() => filters.map(({ attribute_code }) => attribute_code),
|
|
39
|
+
[filters]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Create a set of disabled filters.
|
|
43
|
+
const DISABLED_FILTERS = useMemo(() => {
|
|
44
|
+
const disabled = new Set();
|
|
45
|
+
// Disable category filtering when not on a search page.
|
|
46
|
+
if (pathname !== '/search.html') {
|
|
47
|
+
// disabled.add('category_id');
|
|
48
|
+
// disabled.add('category_uid');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return disabled;
|
|
52
|
+
}, [pathname]);
|
|
53
|
+
|
|
54
|
+
// Get "allowed" filters by intersection of filter attribute codes and
|
|
55
|
+
// schema input field types. This restricts the displayed filters to those
|
|
56
|
+
// that the api will understand.
|
|
57
|
+
const possibleFilters = useMemo(() => {
|
|
58
|
+
const nextFilters = new Set();
|
|
59
|
+
const inputFields = introspectionData
|
|
60
|
+
? introspectionData.__type.inputFields
|
|
61
|
+
: [];
|
|
62
|
+
|
|
63
|
+
// perform mapping and filtering in the same cycle
|
|
64
|
+
for (const { name } of inputFields) {
|
|
65
|
+
const isValid = attributeCodes.includes(name);
|
|
66
|
+
const isEnabled = !DISABLED_FILTERS.has(name);
|
|
67
|
+
|
|
68
|
+
if (isValid && isEnabled) {
|
|
69
|
+
nextFilters.add(name);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return nextFilters;
|
|
74
|
+
}, [DISABLED_FILTERS, attributeCodes, introspectionData]);
|
|
75
|
+
|
|
76
|
+
const isBooleanFilter = options => {
|
|
77
|
+
const optionsString = JSON.stringify(options);
|
|
78
|
+
return (
|
|
79
|
+
options.length <= 2 &&
|
|
80
|
+
(optionsString.includes(
|
|
81
|
+
JSON.stringify({
|
|
82
|
+
__typename: 'AggregationOption',
|
|
83
|
+
label: '0',
|
|
84
|
+
value: '0'
|
|
85
|
+
})
|
|
86
|
+
) ||
|
|
87
|
+
optionsString.includes(
|
|
88
|
+
JSON.stringify({
|
|
89
|
+
__typename: 'AggregationOption',
|
|
90
|
+
label: '1',
|
|
91
|
+
value: '1'
|
|
92
|
+
})
|
|
93
|
+
))
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// iterate over filters once to set up all the collections we need
|
|
98
|
+
const [
|
|
99
|
+
filterNames,
|
|
100
|
+
filterKeys,
|
|
101
|
+
filterItems,
|
|
102
|
+
filterFrontendInput
|
|
103
|
+
] = useMemo(() => {
|
|
104
|
+
const names = new Map();
|
|
105
|
+
const keys = new Set();
|
|
106
|
+
const frontendInput = new Map();
|
|
107
|
+
const itemsByGroup = new Map();
|
|
108
|
+
|
|
109
|
+
const sortedFilters = sortFiltersArray([...filters]);
|
|
110
|
+
|
|
111
|
+
for (const filter of sortedFilters) {
|
|
112
|
+
const { options, label: name, attribute_code: group } = filter;
|
|
113
|
+
|
|
114
|
+
// If this aggregation is not a possible filter, just back out.
|
|
115
|
+
if (possibleFilters.has(group)) {
|
|
116
|
+
const items = [];
|
|
117
|
+
|
|
118
|
+
// add filter name
|
|
119
|
+
names.set(group, name);
|
|
120
|
+
|
|
121
|
+
// add filter key permutations
|
|
122
|
+
keys.add(`${group}[filter]`);
|
|
123
|
+
|
|
124
|
+
// TODO: Get all frontend input type from gql if other filter input types are needed
|
|
125
|
+
// See https://github.com/magento-commerce/magento2-pwa/pull/26
|
|
126
|
+
if (isBooleanFilter(options)) {
|
|
127
|
+
frontendInput.set(group, 'boolean');
|
|
128
|
+
// add items
|
|
129
|
+
items.push({
|
|
130
|
+
title: 'No',
|
|
131
|
+
value: '0',
|
|
132
|
+
label: name + ':' + 'No'
|
|
133
|
+
});
|
|
134
|
+
items.push({
|
|
135
|
+
title: 'Yes',
|
|
136
|
+
value: '1',
|
|
137
|
+
label: name + ':' + 'Yes'
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
// Add frontend input type
|
|
141
|
+
frontendInput.set(group, null);
|
|
142
|
+
// add items
|
|
143
|
+
for (const { label, value } of options) {
|
|
144
|
+
items.push({ title: stripHtml(label), value });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
itemsByGroup.set(group, items);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return [names, keys, itemsByGroup, frontendInput];
|
|
152
|
+
}, [filters, possibleFilters]);
|
|
153
|
+
|
|
154
|
+
// on apply, write filter state to location
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (isApplying) {
|
|
157
|
+
const nextSearch = getSearchFromState(
|
|
158
|
+
search,
|
|
159
|
+
filterKeys,
|
|
160
|
+
filterState
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// write filter state to history
|
|
164
|
+
history.push({ pathname, search: nextSearch });
|
|
165
|
+
|
|
166
|
+
// mark the operation as complete
|
|
167
|
+
setIsApplying(false);
|
|
168
|
+
}
|
|
169
|
+
}, [filterKeys, filterState, history, isApplying, pathname, search]);
|
|
170
|
+
|
|
171
|
+
const handleOpen = useCallback(() => {
|
|
172
|
+
toggleDrawer(DRAWER_NAME);
|
|
173
|
+
}, [toggleDrawer]);
|
|
174
|
+
|
|
175
|
+
const handleClose = useCallback(() => {
|
|
176
|
+
closeDrawer();
|
|
177
|
+
}, [closeDrawer]);
|
|
178
|
+
|
|
179
|
+
const handleApply = useCallback(() => {
|
|
180
|
+
setIsApplying(true);
|
|
181
|
+
handleClose();
|
|
182
|
+
}, [handleClose]);
|
|
183
|
+
|
|
184
|
+
const handleReset = useCallback(() => {
|
|
185
|
+
filterApi.clear();
|
|
186
|
+
setIsApplying(true);
|
|
187
|
+
}, [filterApi, setIsApplying]);
|
|
188
|
+
|
|
189
|
+
const handleKeyDownActions = useCallback(
|
|
190
|
+
event => {
|
|
191
|
+
// do not handle keyboard actions when the modal is closed
|
|
192
|
+
if (!isOpen) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
switch (event.keyCode) {
|
|
197
|
+
// when "Esc" key fired -> close the modal
|
|
198
|
+
case 27:
|
|
199
|
+
handleClose();
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
[isOpen, handleClose]
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
const justOpened =
|
|
208
|
+
prevDrawer.current === null && drawer === DRAWER_NAME;
|
|
209
|
+
const justClosed =
|
|
210
|
+
prevDrawer.current === DRAWER_NAME && drawer === null;
|
|
211
|
+
|
|
212
|
+
// on drawer toggle, read filter state from location
|
|
213
|
+
if (justOpened || justClosed) {
|
|
214
|
+
const nextState = getStateFromSearch(
|
|
215
|
+
search,
|
|
216
|
+
filterKeys,
|
|
217
|
+
filterItems
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
filterApi.setItems(nextState);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// on drawer close, update the modal visibility state
|
|
224
|
+
if (justClosed) {
|
|
225
|
+
handleClose();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
prevDrawer.current = drawer;
|
|
229
|
+
}, [drawer, filterApi, filterItems, filterKeys, search, handleClose]);
|
|
230
|
+
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
const nextState = getStateFromSearch(search, filterKeys, filterItems);
|
|
233
|
+
|
|
234
|
+
filterApi.setItems(nextState);
|
|
235
|
+
}, [filterApi, filterItems, filterKeys, search]);
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
filterApi,
|
|
239
|
+
filterItems,
|
|
240
|
+
filterKeys,
|
|
241
|
+
filterNames,
|
|
242
|
+
filterFrontendInput,
|
|
243
|
+
filterState,
|
|
244
|
+
handleApply,
|
|
245
|
+
handleClose,
|
|
246
|
+
handleKeyDownActions,
|
|
247
|
+
handleOpen,
|
|
248
|
+
handleReset,
|
|
249
|
+
isApplying,
|
|
250
|
+
isOpen
|
|
251
|
+
};
|
|
252
|
+
};
|
|
@@ -85,12 +85,44 @@ export const useCategoryContent = props => {
|
|
|
85
85
|
}
|
|
86
86
|
}, [categoryId, getSortMethods]);
|
|
87
87
|
|
|
88
|
-
const
|
|
88
|
+
const rawFilters = filterData ? filterData.products.aggregations : null;
|
|
89
89
|
const items = data ? data.products.items : placeholderItems;
|
|
90
|
+
const category =
|
|
91
|
+
categoryData && categoryData.categories.items.length
|
|
92
|
+
? categoryData.categories.items[0]
|
|
93
|
+
: null;
|
|
90
94
|
const children =
|
|
91
95
|
categoryData && categoryData.categories.items.length
|
|
92
96
|
? categoryData.categories.items[0].children
|
|
93
97
|
: null;
|
|
98
|
+
|
|
99
|
+
const filters = [];
|
|
100
|
+
|
|
101
|
+
rawFilters && rawFilters.map((filter, index) => {
|
|
102
|
+
|
|
103
|
+
const filterOptions = [];
|
|
104
|
+
let label = filter.label;
|
|
105
|
+
if (filter.attribute_code == "category_uid") {
|
|
106
|
+
children && children.map((category, index) => {
|
|
107
|
+
filterOptions.push({
|
|
108
|
+
'label': category.name,
|
|
109
|
+
'value': category.uid
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
label = category ? category.name : label;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const newFilter = {
|
|
117
|
+
attribute_code: filter.attribute_code,
|
|
118
|
+
count: filter.count,
|
|
119
|
+
label: label,
|
|
120
|
+
options: filterOptions.length ? filterOptions : filter.options
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
filters.push(newFilter);
|
|
124
|
+
});
|
|
125
|
+
|
|
94
126
|
const attributesBlock = categoryData && categoryData.categories.items.length
|
|
95
127
|
? categoryData.categories.items[0].attributes_block
|
|
96
128
|
: null;
|
|
@@ -102,10 +134,7 @@ export const useCategoryContent = props => {
|
|
|
102
134
|
? data.products.page_info.total_pages
|
|
103
135
|
: null;
|
|
104
136
|
const totalCount = data ? data.products.total_count : null;
|
|
105
|
-
|
|
106
|
-
categoryData && categoryData.categories.items.length
|
|
107
|
-
? categoryData.categories.items[0]
|
|
108
|
-
: null;
|
|
137
|
+
|
|
109
138
|
const categoryName =
|
|
110
139
|
categoryData && categoryData.categories.items.length
|
|
111
140
|
? categoryData.categories.items[0].name
|
|
@@ -31,6 +31,7 @@ import BrandSlider from '@riosst100/pwa-marketplace/src/components/BrandSlider';
|
|
|
31
31
|
import CollectibleGameSets from '@riosst100/pwa-marketplace/src/components/CollectibleGameSets/collectibleGameSets';
|
|
32
32
|
import { useLocation } from 'react-router-dom';
|
|
33
33
|
import { getFiltersFromSearch } from '@magento/peregrine/lib/talons/FilterModal/helpers';
|
|
34
|
+
import CustomSubCategory from '@riosst100/pwa-marketplace/src/components/SubCategory/customSubCategory';
|
|
34
35
|
|
|
35
36
|
const FilterModal = React.lazy(() => import('@magento/venia-ui/lib/components/FilterModal'));
|
|
36
37
|
const FilterSidebar = React.lazy(() =>
|
|
@@ -94,7 +95,7 @@ const CategoryContent = props => {
|
|
|
94
95
|
) : null;
|
|
95
96
|
|
|
96
97
|
const sidebar = shouldShowFilterButtons ? (
|
|
97
|
-
<FilterSidebar filters={filters} />
|
|
98
|
+
<FilterSidebar children={children} filters={filters} allowedFilters={category ? category.allowed_filters : []} />
|
|
98
99
|
) : shouldShowFilterShimmer ? (
|
|
99
100
|
<FilterSidebarShimmer />
|
|
100
101
|
) : null;
|
|
@@ -174,12 +175,34 @@ const CategoryContent = props => {
|
|
|
174
175
|
const categoryTitle = categoryName ? categoryName : <Shimmer width={5} />;
|
|
175
176
|
|
|
176
177
|
const { search } = useLocation();
|
|
177
|
-
const
|
|
178
|
+
const allActiveFilters = getFiltersFromSearch(search);
|
|
179
|
+
|
|
180
|
+
const activeFilters = [];
|
|
181
|
+
|
|
182
|
+
if (allActiveFilters && allActiveFilters.size > 0) {
|
|
183
|
+
allActiveFilters.forEach((value, key) => {
|
|
184
|
+
value.forEach((value) => {
|
|
185
|
+
const filterArr = value.split(',');
|
|
186
|
+
|
|
187
|
+
const label = filterArr[0];
|
|
188
|
+
const optionId = filterArr[1];
|
|
189
|
+
if (key == "card_set") {
|
|
190
|
+
activeFilters.push(
|
|
191
|
+
{
|
|
192
|
+
'label': label
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const currentFilter = activeFilters && activeFilters.length ? activeFilters[activeFilters.length - 1].label : '';
|
|
178
201
|
|
|
179
202
|
return (
|
|
180
203
|
<Fragment>
|
|
181
|
-
<Breadcrumbs categoryId={categoryId} />
|
|
182
|
-
<StoreTitle>{categoryName}</StoreTitle>
|
|
204
|
+
<Breadcrumbs categoryId={categoryId} currentFilter={activeFilters} />
|
|
205
|
+
<StoreTitle>{currentFilter ? currentFilter : categoryName}</StoreTitle>
|
|
183
206
|
<article className={classes.root} data-cy="CategoryContent-root">
|
|
184
207
|
<div className={classes.categoryHeader}>
|
|
185
208
|
<h1 aria-live="polite" className={classes.title}>
|
|
@@ -187,16 +210,17 @@ const CategoryContent = props => {
|
|
|
187
210
|
className={classes.categoryTitle}
|
|
188
211
|
data-cy="CategoryContent-categoryTitle"
|
|
189
212
|
>
|
|
190
|
-
{categoryTitle}
|
|
213
|
+
{currentFilter ? currentFilter : categoryTitle}
|
|
191
214
|
</span>
|
|
192
215
|
</h1>
|
|
193
216
|
{categoryDescriptionElement}
|
|
194
217
|
</div>
|
|
195
|
-
{activeFilters.size <= 0 && category && category.custom_landing_page ? (
|
|
218
|
+
{/* {activeFilters.size <= 0 && category && category.custom_landing_page ? ( */}
|
|
196
219
|
<>
|
|
197
|
-
<SubCategory
|
|
220
|
+
<SubCategory children={children} />
|
|
221
|
+
<CustomSubCategory customSubCategory={category ? category.custom_subcategory : null} />
|
|
198
222
|
</>
|
|
199
|
-
) : (
|
|
223
|
+
{/* ) : ( */}
|
|
200
224
|
<>
|
|
201
225
|
{/* <SubCategory filters={filters} children={children} /> */}
|
|
202
226
|
<FilterTop filters={filters} category={category} />
|
|
@@ -232,7 +256,7 @@ const CategoryContent = props => {
|
|
|
232
256
|
</div>
|
|
233
257
|
</div>
|
|
234
258
|
</>
|
|
235
|
-
)}
|
|
259
|
+
{/* )} */}
|
|
236
260
|
</article>
|
|
237
261
|
</Fragment>
|
|
238
262
|
);
|
|
@@ -10,7 +10,8 @@ import Shimmer from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/comp
|
|
|
10
10
|
import defaultClasses from '@riosst100/pwa-marketplace/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.module.css';
|
|
11
11
|
import { ArrowRight2 } from 'iconsax-react';
|
|
12
12
|
import cn from 'classnames';
|
|
13
|
-
|
|
13
|
+
import { getFiltersFromSearch } from '@magento/peregrine/lib/talons/FilterModal/helpers';
|
|
14
|
+
import { useLocation } from 'react-router-dom';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Breadcrumbs! Generates a sorted display of category links.
|
|
@@ -21,7 +22,7 @@ import cn from 'classnames';
|
|
|
21
22
|
const Breadcrumbs = props => {
|
|
22
23
|
const classes = useStyle(defaultClasses, props.classes);
|
|
23
24
|
|
|
24
|
-
const { categoryId, currentProduct } = props;
|
|
25
|
+
const { categoryId, currentProduct, currentFilter } = props;
|
|
25
26
|
|
|
26
27
|
const talonProps = useBreadcrumbs({ categoryId });
|
|
27
28
|
|
|
@@ -69,13 +70,36 @@ const Breadcrumbs = props => {
|
|
|
69
70
|
);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
const filterBreadcrumbsElement = [];
|
|
74
|
+
|
|
75
|
+
currentFilter && currentFilter.length && currentFilter.map((filter, index) => {
|
|
76
|
+
currentProduct ? (
|
|
77
|
+
filterBreadcrumbsElement.push(
|
|
78
|
+
<Link
|
|
79
|
+
className={classes.link}
|
|
80
|
+
to={resourceUrl(currentCategoryPath)}
|
|
81
|
+
onClick={handleClick}
|
|
82
|
+
>
|
|
83
|
+
{filter.label}
|
|
84
|
+
</Link>
|
|
85
|
+
)
|
|
86
|
+
) : (
|
|
87
|
+
filterBreadcrumbsElement.push(
|
|
88
|
+
<>
|
|
89
|
+
<span className={classes.divider}>{DELIMITER}</span>
|
|
90
|
+
<span className={cn(classes.currentCategory, 'text-blue-700 font-medium')}>{filter.label}</span>
|
|
91
|
+
</>
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
72
96
|
// If we have a "currentProduct" it means we're on a PDP so we want the last
|
|
73
97
|
// category text to be a link. If we don't have a "currentProduct" we're on
|
|
74
98
|
// a category page so it should be regular text.
|
|
75
|
-
const currentCategoryLink = currentProduct ? (
|
|
99
|
+
const currentCategoryLink = currentProduct || currentFilter && currentFilter.length ? (
|
|
76
100
|
<Link
|
|
77
101
|
className={classes.link}
|
|
78
|
-
to={resourceUrl(currentCategoryPath)}
|
|
102
|
+
to={'/'+resourceUrl(currentCategoryPath)}
|
|
79
103
|
onClick={handleClick}
|
|
80
104
|
>
|
|
81
105
|
{currentCategory}
|
|
@@ -90,7 +114,7 @@ const Breadcrumbs = props => {
|
|
|
90
114
|
<span className={classes.text}>{currentProduct}</span>
|
|
91
115
|
</Fragment>
|
|
92
116
|
) : null;
|
|
93
|
-
|
|
117
|
+
|
|
94
118
|
return (
|
|
95
119
|
<div className={classes.root} aria-live="polite" aria-busy="false">
|
|
96
120
|
<Link className={classes.link} to="/">
|
|
@@ -99,6 +123,7 @@ const Breadcrumbs = props => {
|
|
|
99
123
|
{links}
|
|
100
124
|
<span className={classes.divider}>{DELIMITER}</span>
|
|
101
125
|
{currentCategoryLink}
|
|
126
|
+
{filterBreadcrumbsElement}
|
|
102
127
|
{currentProductNode}
|
|
103
128
|
</div>
|
|
104
129
|
);
|
|
@@ -20,16 +20,18 @@ const CurrentFilters = props => {
|
|
|
20
20
|
const { title, value } = item || {};
|
|
21
21
|
const key = `${group}::${title}_${value}`;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
if (group != "card_set") {
|
|
24
|
+
elements.push(
|
|
25
|
+
<li key={key} className={classes.item}>
|
|
26
|
+
<CurrentFilter
|
|
27
|
+
group={group}
|
|
28
|
+
item={item}
|
|
29
|
+
removeItem={removeItem}
|
|
30
|
+
onRemove={onRemove}
|
|
31
|
+
/>
|
|
32
|
+
</li>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -19,7 +19,7 @@ const SCROLL_OFFSET = 150;
|
|
|
19
19
|
* @param {Object} props.filters - filters to display
|
|
20
20
|
*/
|
|
21
21
|
const FilterSidebar = props => {
|
|
22
|
-
const { filters, filterCountToOpen } = props;
|
|
22
|
+
const { filters, filterCountToOpen, allowedFilters } = props;
|
|
23
23
|
const talonProps = useFilterSidebar({ filters });
|
|
24
24
|
const {
|
|
25
25
|
filterApi,
|
|
@@ -52,25 +52,33 @@ const FilterSidebar = props => {
|
|
|
52
52
|
[handleApply, filterRef]
|
|
53
53
|
);
|
|
54
54
|
|
|
55
|
+
const allowedFiltersArr = [];
|
|
56
|
+
|
|
57
|
+
allowedFilters.length && allowedFilters.map((val, index) => {
|
|
58
|
+
allowedFiltersArr.push(val.code);
|
|
59
|
+
});
|
|
60
|
+
|
|
55
61
|
const filtersList = useMemo(
|
|
56
62
|
() =>
|
|
57
63
|
Array.from(filterItems, ([group, items], iteration) => {
|
|
58
64
|
const blockState = filterState.get(group);
|
|
59
65
|
const groupName = filterNames.get(group);
|
|
60
66
|
const frontendInput = filterFrontendInput.get(group);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
if (!allowedFiltersArr.length && group != "category_uid" || allowedFiltersArr.length && allowedFiltersArr.includes(group)) {
|
|
68
|
+
return (
|
|
69
|
+
<FilterBlock
|
|
70
|
+
key={group}
|
|
71
|
+
filterApi={filterApi}
|
|
72
|
+
filterState={blockState}
|
|
73
|
+
filterFrontendInput={frontendInput}
|
|
74
|
+
group={group}
|
|
75
|
+
items={items}
|
|
76
|
+
name={groupName}
|
|
77
|
+
onApply={handleApplyFilter}
|
|
78
|
+
initialOpen={iteration < filterCountToOpen}
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
74
82
|
}),
|
|
75
83
|
[
|
|
76
84
|
filterApi,
|
|
@@ -8,8 +8,6 @@ import DEFAULT_OPERATIONS from './collectibleGameSets.gql';
|
|
|
8
8
|
|
|
9
9
|
export const useCollectibleGameSets = props => {
|
|
10
10
|
|
|
11
|
-
const { product_type } = props
|
|
12
|
-
|
|
13
11
|
const operations = mergeOperations(DEFAULT_OPERATIONS, null);
|
|
14
12
|
const { getStoreConfigData, getCollectibleGameQuery } = operations;
|
|
15
13
|
const { pathname } = useLocation();
|
|
@@ -27,8 +25,8 @@ export const useCollectibleGameSets = props => {
|
|
|
27
25
|
|
|
28
26
|
const pathnameArr = pathname.split('/');
|
|
29
27
|
|
|
30
|
-
const categoryUrlKey = pathnameArr[pathnameArr.length -
|
|
31
|
-
const productType =
|
|
28
|
+
const categoryUrlKey = pathnameArr[pathnameArr.length - 2].replace('.html','');
|
|
29
|
+
const productType = pathnameArr[pathnameArr.length - 1].replace('.html','');
|
|
32
30
|
|
|
33
31
|
const categoryUrlSuffix = storeConfigData?.storeConfig?.category_url_suffix;
|
|
34
32
|
|
|
@@ -50,7 +48,6 @@ export const useCollectibleGameSets = props => {
|
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
const collectibleGameSets = data.collectibleGameSets;
|
|
53
|
-
|
|
54
51
|
if (!collectibleGameSets) {
|
|
55
52
|
return null;
|
|
56
53
|
}
|
|
@@ -58,7 +55,6 @@ export const useCollectibleGameSets = props => {
|
|
|
58
55
|
return collectibleGameSets;
|
|
59
56
|
}, [data, categoryUrlKey]);
|
|
60
57
|
|
|
61
|
-
// Update the page indicator if the GraphQL query is in flight.
|
|
62
58
|
useEffect(() => {
|
|
63
59
|
setPageLoading(isBackgroundLoading);
|
|
64
60
|
}, [isBackgroundLoading, setPageLoading]);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useQuery } from '@apollo/client';
|
|
3
|
+
|
|
4
|
+
import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
|
|
5
|
+
|
|
6
|
+
import DEFAULT_OPERATIONS from './subCategory.gql';
|
|
7
|
+
|
|
8
|
+
const getPath = (path, suffix) => {
|
|
9
|
+
if (path) {
|
|
10
|
+
return `/${path}${suffix || ''}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// If there is no path this is just a dead link.
|
|
14
|
+
return '#';
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const useCustomSubCategory = props => {
|
|
18
|
+
const { customSubCategory } = props;
|
|
19
|
+
|
|
20
|
+
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
21
|
+
const { getStoreConfigQuery } = operations;
|
|
22
|
+
|
|
23
|
+
const { data: storeConfigData } = useQuery(getStoreConfigQuery, {
|
|
24
|
+
fetchPolicy: 'cache-and-network'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const categoryUrlSuffix = useMemo(() => {
|
|
28
|
+
if (storeConfigData) {
|
|
29
|
+
return storeConfigData.storeConfig.category_url_suffix;
|
|
30
|
+
}
|
|
31
|
+
}, [storeConfigData]);
|
|
32
|
+
|
|
33
|
+
const normalizedData = useMemo(() => {
|
|
34
|
+
if (customSubCategory) {
|
|
35
|
+
return (
|
|
36
|
+
customSubCategory.length &&
|
|
37
|
+
customSubCategory
|
|
38
|
+
.map(category => ({
|
|
39
|
+
text: category.name,
|
|
40
|
+
path: `/${category.path}`
|
|
41
|
+
}))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}, [categoryUrlSuffix, customSubCategory]);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
normalizedData: normalizedData || []
|
|
48
|
+
};
|
|
49
|
+
};
|