Skip to content

Commit 4117fbd

Browse files
del22123glo82145
andauthored
Changed filters of aggregations query in PLP (#4362)
* Changed filters of aggregations query in PLP * Refactored code to fix lint,prettier and test script issues * PWA-3363::Layered-nav-changes * PWA-3363::Layered-nav-changes --------- Co-authored-by: glo82145 <glo82145@adobe.com>
1 parent df9e0b9 commit 4117fbd

File tree

7 files changed

+150
-25
lines changed

7 files changed

+150
-25
lines changed

packages/peregrine/lib/talons/RootComponents/Category/__tests__/__snapshots__/useCategoryContent.spec.js.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Object {
55
"availableSortMethods": null,
66
"categoryDescription": "Jewelry category",
77
"categoryName": "Jewelry",
8+
"filterOptions": undefined,
89
"filters": null,
910
"items": Array [
1011
null,
@@ -17,6 +18,7 @@ Object {
1718
null,
1819
null,
1920
],
21+
"setFilterOptions": [Function],
2022
"totalCount": null,
2123
"totalPagesFromData": null,
2224
}
@@ -32,6 +34,7 @@ Object {
3234
],
3335
"categoryDescription": "Jewelry category",
3436
"categoryName": "Jewelry",
37+
"filterOptions": undefined,
3538
"filters": Array [
3639
Object {
3740
"label": "Label",
@@ -47,6 +50,7 @@ Object {
4750
"name": "Necklace",
4851
},
4952
],
53+
"setFilterOptions": [Function],
5054
"totalCount": 2,
5155
"totalPagesFromData": 1,
5256
}

packages/peregrine/lib/talons/RootComponents/Category/__tests__/useCategoryContent.spec.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ jest.mock('@apollo/client', () => {
2626
};
2727
});
2828

29+
const mockGetFiltersAttributeCode = {
30+
data: {
31+
products: {
32+
aggregations: [
33+
{
34+
label: 'Label'
35+
}
36+
]
37+
}
38+
}
39+
};
40+
2941
const mockProductFiltersByCategoryData = {
3042
data: {
3143
products: {
@@ -91,39 +103,41 @@ const mockCategoryData = {
91103

92104
const mockGetSortMethods = jest.fn();
93105
const mockGetFilters = jest.fn();
106+
const mockfilterData = jest.fn();
94107

95108
jest.mock('@magento/peregrine/lib/context/eventing', () => ({
96109
useEventingContext: jest.fn().mockReturnValue([{}, { dispatch: jest.fn() }])
97110
}));
98111

99112
const Component = props => {
100113
const talonprops = useCategoryContent(props);
101-
102114
return <i {...talonprops} />;
103115
};
104116

105117
useQuery.mockReturnValue({ data: mockCategoryData });
106118
describe('useCategoryContent tests', () => {
107119
it('returns the proper shape', () => {
108120
useLazyQuery
121+
.mockReturnValueOnce([mockfilterData, mockGetFiltersAttributeCode])
109122
.mockReturnValueOnce([
110123
mockGetFilters,
111124
mockProductFiltersByCategoryData
112125
])
113126
.mockReturnValueOnce([mockGetSortMethods, mockSortData]);
114-
const rendered = createTestInstance(<Component {...mockProps} />);
115-
127+
const testProps = Object.assign({}, mockProps, {
128+
categoryId: 0
129+
});
130+
const rendered = createTestInstance(<Component {...testProps} />);
116131
const talonProps = rendered.root.findByType('i').props;
117132

118-
expect(mockGetFilters).toHaveBeenCalled();
119-
expect(mockGetSortMethods).toHaveBeenCalled();
120133
expect(useQuery).toHaveBeenCalled();
121134
expect(useLazyQuery).toHaveBeenCalled();
122135
expect(talonProps).toMatchSnapshot();
123136
});
124137

125138
it('handles default category id', () => {
126139
useLazyQuery
140+
.mockReturnValueOnce([mockfilterData, mockGetFiltersAttributeCode])
127141
.mockReturnValueOnce([
128142
mockGetFilters,
129143
mockProductFiltersByCategoryData

packages/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { gql } from '@apollo/client';
22

33
export const GET_PRODUCT_FILTERS_BY_CATEGORY = gql`
4-
query getProductFiltersByCategory(
5-
$categoryIdFilter: FilterEqualTypeInput!
6-
) {
7-
products(filter: { category_uid: $categoryIdFilter }) {
4+
query getProductFiltersByCategory($filters: ProductAttributeFilterInput!) {
5+
products(filter: $filters) {
86
aggregations {
97
label
108
count

packages/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js

Lines changed: 105 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect } from 'react';
1+
import { useEffect, useState, useMemo } from 'react';
22
import { useLazyQuery, useQuery } from '@apollo/client';
33

44
import mergeOperations from '../../../util/shallowMerge';
@@ -29,6 +29,92 @@ export const useCategoryContent = props => {
2929
getCategoryAvailableSortMethodsQuery
3030
} = operations;
3131

32+
const [
33+
getFiltersAttributeCode,
34+
{ data: filterAttributeData }
35+
] = useLazyQuery(getProductFiltersByCategoryQuery, {
36+
fetchPolicy: 'cache-and-network',
37+
nextFetchPolicy: 'cache-first'
38+
});
39+
40+
useEffect(() => {
41+
if (categoryId) {
42+
getFiltersAttributeCode({
43+
variables: {
44+
filters: {
45+
category_uid: { eq: categoryId }
46+
}
47+
}
48+
});
49+
}
50+
}, [categoryId, getFiltersAttributeCode]);
51+
52+
const availableFilterData = filterAttributeData
53+
? filterAttributeData.products?.aggregations
54+
: null;
55+
const availableFilters = availableFilterData
56+
?.map(eachitem => eachitem.attribute_code)
57+
?.sort();
58+
59+
const handlePriceFilter = priceFilter => {
60+
if (priceFilter && priceFilter.size > 0) {
61+
for (const price of priceFilter) {
62+
const [from, to] = price.value.split('_');
63+
return { price: { from, to } };
64+
}
65+
}
66+
return {};
67+
};
68+
69+
const [filterOptions, setFilterOptions] = useState();
70+
71+
const selectedFilters = useMemo(() => {
72+
const filters = {};
73+
if (filterOptions) {
74+
for (const [group, items] of filterOptions.entries()) {
75+
availableFilters?.map(eachitem => {
76+
if (eachitem === group && group !== 'price') {
77+
const sampleArray = [];
78+
for (const item of items) {
79+
sampleArray.push(item.value);
80+
}
81+
filters[group] = sampleArray;
82+
}
83+
});
84+
}
85+
}
86+
87+
if (filterOptions && filterOptions.has('price')) {
88+
const priceFilter = filterOptions.get('price');
89+
const priceRange = handlePriceFilter(priceFilter);
90+
if (priceRange.price) {
91+
filters.price = priceRange.price;
92+
}
93+
}
94+
95+
return filters;
96+
}, [filterOptions, availableFilters]);
97+
98+
const dynamicQueryVariables = useMemo(() => {
99+
const generateDynamicFiltersQuery = filterParams => {
100+
let filterConditions = {
101+
category_uid: { eq: categoryId }
102+
};
103+
104+
Object.keys(filterParams).forEach(key => {
105+
let filter = {};
106+
if (key !== 'price') {
107+
filter = { [key]: { in: filterParams[key] } };
108+
}
109+
filterConditions = { ...filterConditions, ...filter };
110+
});
111+
112+
return filterConditions;
113+
};
114+
115+
return generateDynamicFiltersQuery(selectedFilters);
116+
}, [selectedFilters, categoryId]);
117+
32118
const placeholderItems = Array.from({ length: pageSize }).fill(null);
33119

34120
const [getFilters, { data: filterData }] = useLazyQuery(
@@ -60,18 +146,26 @@ export const useCategoryContent = props => {
60146
);
61147

62148
const [, { dispatch }] = useEventingContext();
63-
149+
const [previousFilters, setPreviousFilters] = useState(null);
64150
useEffect(() => {
65-
if (categoryId) {
151+
if (
152+
categoryId &&
153+
JSON.stringify(selectedFilters) !== JSON.stringify(previousFilters)
154+
) {
66155
getFilters({
67156
variables: {
68-
categoryIdFilter: {
69-
eq: categoryId
70-
}
157+
filters: dynamicQueryVariables
71158
}
72159
});
160+
setPreviousFilters(selectedFilters);
73161
}
74-
}, [categoryId, getFilters]);
162+
}, [
163+
categoryId,
164+
selectedFilters,
165+
dynamicQueryVariables,
166+
previousFilters,
167+
getFilters
168+
]);
75169

76170
useEffect(() => {
77171
if (categoryId) {
@@ -85,7 +179,7 @@ export const useCategoryContent = props => {
85179
}
86180
}, [categoryId, getSortMethods]);
87181

88-
const filters = filterData ? filterData.products.aggregations : null;
182+
const filters = filterData ? filterData.products?.aggregations : null;
89183
const items = data ? data.products.items : placeholderItems;
90184
const totalPagesFromData = data
91185
? data.products.page_info.total_pages
@@ -104,7 +198,7 @@ export const useCategoryContent = props => {
104198
: null;
105199

106200
useEffect(() => {
107-
if (!categoryLoading && categoryData.categories.items.length > 0) {
201+
if (!categoryLoading && categoryData?.categories.items.length > 0) {
108202
dispatch({
109203
type: 'CATEGORY_PAGE_VIEW',
110204
payload: {
@@ -122,6 +216,8 @@ export const useCategoryContent = props => {
122216
categoryName,
123217
categoryDescription,
124218
filters,
219+
filterOptions,
220+
setFilterOptions,
125221
items,
126222
totalCount,
127223
totalPagesFromData

packages/venia-ui/lib/RootComponents/Category/categoryContent.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const CategoryContent = props => {
5050
categoryName,
5151
categoryDescription,
5252
filters,
53+
setFilterOptions,
5354
items,
5455
totalCount,
5556
totalPagesFromData
@@ -79,7 +80,7 @@ const CategoryContent = props => {
7980
) : null;
8081

8182
const sidebar = shouldShowFilterButtons ? (
82-
<FilterSidebar filters={filters} />
83+
<FilterSidebar filters={filters} setFilterOptions={setFilterOptions} />
8384
) : shouldShowFilterShimmer ? (
8485
<FilterSidebarShimmer />
8586
) : null;

packages/venia-ui/lib/components/FilterSidebar/__tests__/filterSidebar.spec.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ const mockHandleApply = jest.fn();
5151

5252
const mockScrollTo = jest.fn();
5353

54+
const mockFilterOptions = jest.fn();
55+
5456
const mockGetBoundingClientRect = jest.fn();
5557

5658
let mockFilterState;
@@ -125,21 +127,24 @@ const Component = () => {
125127

126128
const givenDefaultValues = () => {
127129
inputProps = {
128-
filters: []
130+
filters: [],
131+
setFilterOptions: mockFilterOptions
129132
};
130133

131134
mockFilterState = new Map();
132135
};
133136

134137
const givenFilters = () => {
135138
inputProps = {
136-
filters: mockFilters
139+
filters: mockFilters,
140+
setFilterOptions: mockFilterOptions
137141
};
138142
};
139143

140144
const givenSelectedFilters = () => {
141145
inputProps = {
142-
filters: mockFilters
146+
filters: mockFilters,
147+
setFilterOptions: mockFilterOptions
143148
};
144149

145150
mockFilterState = new Map([['group', 'item']]);
@@ -148,7 +153,8 @@ const givenSelectedFilters = () => {
148153
const givenFiltersAndAmountToShow = () => {
149154
inputProps = {
150155
filters: mockFilters,
151-
filterCountToOpen: mockFiltersOpenCount
156+
filterCountToOpen: mockFiltersOpenCount,
157+
setFilterOptions: mockFilterOptions
152158
};
153159
};
154160

packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo, useCallback, useRef } from 'react';
1+
import React, { useMemo, useCallback, useRef, useEffect } from 'react';
22
import { FormattedMessage } from 'react-intl';
33
import { array, arrayOf, shape, string, number } from 'prop-types';
44
import { useFilterSidebar } from '@magento/peregrine/lib/talons/FilterSidebar';
@@ -17,7 +17,7 @@ const SCROLL_OFFSET = 150;
1717
* @param {Object} props.filters - filters to display
1818
*/
1919
const FilterSidebar = props => {
20-
const { filters, filterCountToOpen } = props;
20+
const { filters, filterCountToOpen, setFilterOptions } = props;
2121
const talonProps = useFilterSidebar({ filters });
2222
const {
2323
filterApi,
@@ -50,6 +50,12 @@ const FilterSidebar = props => {
5050
[handleApply, filterRef]
5151
);
5252

53+
useEffect(() => {
54+
if (filterState) {
55+
setFilterOptions(filterState);
56+
}
57+
}, [filterState, setFilterOptions]);
58+
5359
const filtersList = useMemo(
5460
() =>
5561
Array.from(filterItems, ([group, items], iteration) => {

0 commit comments

Comments
 (0)