diff --git a/packages/peregrine/lib/talons/RootComponents/Category/__tests__/__snapshots__/useCategoryContent.spec.js.snap b/packages/peregrine/lib/talons/RootComponents/Category/__tests__/__snapshots__/useCategoryContent.spec.js.snap
index 8aa379def7..9ddebf6b30 100644
--- a/packages/peregrine/lib/talons/RootComponents/Category/__tests__/__snapshots__/useCategoryContent.spec.js.snap
+++ b/packages/peregrine/lib/talons/RootComponents/Category/__tests__/__snapshots__/useCategoryContent.spec.js.snap
@@ -5,6 +5,7 @@ Object {
"availableSortMethods": null,
"categoryDescription": "Jewelry category",
"categoryName": "Jewelry",
+ "filterOptions": undefined,
"filters": null,
"items": Array [
null,
@@ -17,6 +18,7 @@ Object {
null,
null,
],
+ "setFilterOptions": [Function],
"totalCount": null,
"totalPagesFromData": null,
}
@@ -32,6 +34,7 @@ Object {
],
"categoryDescription": "Jewelry category",
"categoryName": "Jewelry",
+ "filterOptions": undefined,
"filters": Array [
Object {
"label": "Label",
@@ -47,6 +50,7 @@ Object {
"name": "Necklace",
},
],
+ "setFilterOptions": [Function],
"totalCount": 2,
"totalPagesFromData": 1,
}
diff --git a/packages/peregrine/lib/talons/RootComponents/Category/__tests__/useCategoryContent.spec.js b/packages/peregrine/lib/talons/RootComponents/Category/__tests__/useCategoryContent.spec.js
index d22cfd685a..02f263a747 100644
--- a/packages/peregrine/lib/talons/RootComponents/Category/__tests__/useCategoryContent.spec.js
+++ b/packages/peregrine/lib/talons/RootComponents/Category/__tests__/useCategoryContent.spec.js
@@ -26,6 +26,18 @@ jest.mock('@apollo/client', () => {
};
});
+const mockGetFiltersAttributeCode = {
+ data: {
+ products: {
+ aggregations: [
+ {
+ label: 'Label'
+ }
+ ]
+ }
+ }
+};
+
const mockProductFiltersByCategoryData = {
data: {
products: {
@@ -91,6 +103,7 @@ const mockCategoryData = {
const mockGetSortMethods = jest.fn();
const mockGetFilters = jest.fn();
+const mockfilterData = jest.fn();
jest.mock('@magento/peregrine/lib/context/eventing', () => ({
useEventingContext: jest.fn().mockReturnValue([{}, { dispatch: jest.fn() }])
@@ -98,7 +111,6 @@ jest.mock('@magento/peregrine/lib/context/eventing', () => ({
const Component = props => {
const talonprops = useCategoryContent(props);
-
return ;
};
@@ -106,17 +118,18 @@ useQuery.mockReturnValue({ data: mockCategoryData });
describe('useCategoryContent tests', () => {
it('returns the proper shape', () => {
useLazyQuery
+ .mockReturnValueOnce([mockfilterData, mockGetFiltersAttributeCode])
.mockReturnValueOnce([
mockGetFilters,
mockProductFiltersByCategoryData
])
.mockReturnValueOnce([mockGetSortMethods, mockSortData]);
- const rendered = createTestInstance();
-
+ const testProps = Object.assign({}, mockProps, {
+ categoryId: 0
+ });
+ const rendered = createTestInstance();
const talonProps = rendered.root.findByType('i').props;
- expect(mockGetFilters).toHaveBeenCalled();
- expect(mockGetSortMethods).toHaveBeenCalled();
expect(useQuery).toHaveBeenCalled();
expect(useLazyQuery).toHaveBeenCalled();
expect(talonProps).toMatchSnapshot();
@@ -124,6 +137,7 @@ describe('useCategoryContent tests', () => {
it('handles default category id', () => {
useLazyQuery
+ .mockReturnValueOnce([mockfilterData, mockGetFiltersAttributeCode])
.mockReturnValueOnce([
mockGetFilters,
mockProductFiltersByCategoryData
diff --git a/packages/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js b/packages/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js
index 16e42557c4..eb7a7e36fa 100644
--- a/packages/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js
+++ b/packages/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js
@@ -1,10 +1,8 @@
import { gql } from '@apollo/client';
export const GET_PRODUCT_FILTERS_BY_CATEGORY = gql`
- query getProductFiltersByCategory(
- $categoryIdFilter: FilterEqualTypeInput!
- ) {
- products(filter: { category_uid: $categoryIdFilter }) {
+ query getProductFiltersByCategory($filters: ProductAttributeFilterInput!) {
+ products(filter: $filters) {
aggregations {
label
count
diff --git a/packages/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js b/packages/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js
index fde9369c5a..659fdb400a 100644
--- a/packages/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js
+++ b/packages/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js
@@ -1,4 +1,4 @@
-import { useEffect } from 'react';
+import { useEffect, useState, useMemo } from 'react';
import { useLazyQuery, useQuery } from '@apollo/client';
import mergeOperations from '../../../util/shallowMerge';
@@ -29,6 +29,92 @@ export const useCategoryContent = props => {
getCategoryAvailableSortMethodsQuery
} = operations;
+ const [
+ getFiltersAttributeCode,
+ { data: filterAttributeData }
+ ] = useLazyQuery(getProductFiltersByCategoryQuery, {
+ fetchPolicy: 'cache-and-network',
+ nextFetchPolicy: 'cache-first'
+ });
+
+ useEffect(() => {
+ if (categoryId) {
+ getFiltersAttributeCode({
+ variables: {
+ filters: {
+ category_uid: { eq: categoryId }
+ }
+ }
+ });
+ }
+ }, [categoryId, getFiltersAttributeCode]);
+
+ const availableFilterData = filterAttributeData
+ ? filterAttributeData.products?.aggregations
+ : null;
+ const availableFilters = availableFilterData
+ ?.map(eachitem => eachitem.attribute_code)
+ ?.sort();
+
+ const handlePriceFilter = priceFilter => {
+ if (priceFilter && priceFilter.size > 0) {
+ for (const price of priceFilter) {
+ const [from, to] = price.value.split('_');
+ return { price: { from, to } };
+ }
+ }
+ return {};
+ };
+
+ const [filterOptions, setFilterOptions] = useState();
+
+ const selectedFilters = useMemo(() => {
+ const filters = {};
+ if (filterOptions) {
+ for (const [group, items] of filterOptions.entries()) {
+ availableFilters?.map(eachitem => {
+ if (eachitem === group && group !== 'price') {
+ const sampleArray = [];
+ for (const item of items) {
+ sampleArray.push(item.value);
+ }
+ filters[group] = sampleArray;
+ }
+ });
+ }
+ }
+
+ if (filterOptions && filterOptions.has('price')) {
+ const priceFilter = filterOptions.get('price');
+ const priceRange = handlePriceFilter(priceFilter);
+ if (priceRange.price) {
+ filters.price = priceRange.price;
+ }
+ }
+
+ return filters;
+ }, [filterOptions, availableFilters]);
+
+ const dynamicQueryVariables = useMemo(() => {
+ const generateDynamicFiltersQuery = filterParams => {
+ let filterConditions = {
+ category_uid: { eq: categoryId }
+ };
+
+ Object.keys(filterParams).forEach(key => {
+ let filter = {};
+ if (key !== 'price') {
+ filter = { [key]: { in: filterParams[key] } };
+ }
+ filterConditions = { ...filterConditions, ...filter };
+ });
+
+ return filterConditions;
+ };
+
+ return generateDynamicFiltersQuery(selectedFilters);
+ }, [selectedFilters, categoryId]);
+
const placeholderItems = Array.from({ length: pageSize }).fill(null);
const [getFilters, { data: filterData }] = useLazyQuery(
@@ -60,18 +146,26 @@ export const useCategoryContent = props => {
);
const [, { dispatch }] = useEventingContext();
-
+ const [previousFilters, setPreviousFilters] = useState(null);
useEffect(() => {
- if (categoryId) {
+ if (
+ categoryId &&
+ JSON.stringify(selectedFilters) !== JSON.stringify(previousFilters)
+ ) {
getFilters({
variables: {
- categoryIdFilter: {
- eq: categoryId
- }
+ filters: dynamicQueryVariables
}
});
+ setPreviousFilters(selectedFilters);
}
- }, [categoryId, getFilters]);
+ }, [
+ categoryId,
+ selectedFilters,
+ dynamicQueryVariables,
+ previousFilters,
+ getFilters
+ ]);
useEffect(() => {
if (categoryId) {
@@ -85,7 +179,7 @@ export const useCategoryContent = props => {
}
}, [categoryId, getSortMethods]);
- const filters = filterData ? filterData.products.aggregations : null;
+ const filters = filterData ? filterData.products?.aggregations : null;
const items = data ? data.products.items : placeholderItems;
const totalPagesFromData = data
? data.products.page_info.total_pages
@@ -104,7 +198,7 @@ export const useCategoryContent = props => {
: null;
useEffect(() => {
- if (!categoryLoading && categoryData.categories.items.length > 0) {
+ if (!categoryLoading && categoryData?.categories.items.length > 0) {
dispatch({
type: 'CATEGORY_PAGE_VIEW',
payload: {
@@ -122,6 +216,8 @@ export const useCategoryContent = props => {
categoryName,
categoryDescription,
filters,
+ filterOptions,
+ setFilterOptions,
items,
totalCount,
totalPagesFromData
diff --git a/packages/venia-ui/lib/RootComponents/Category/categoryContent.js b/packages/venia-ui/lib/RootComponents/Category/categoryContent.js
index c3cbd4eb2e..d47c763499 100644
--- a/packages/venia-ui/lib/RootComponents/Category/categoryContent.js
+++ b/packages/venia-ui/lib/RootComponents/Category/categoryContent.js
@@ -50,6 +50,7 @@ const CategoryContent = props => {
categoryName,
categoryDescription,
filters,
+ setFilterOptions,
items,
totalCount,
totalPagesFromData
@@ -79,7 +80,7 @@ const CategoryContent = props => {
) : null;
const sidebar = shouldShowFilterButtons ? (
-
+
) : shouldShowFilterShimmer ? (
) : null;
diff --git a/packages/venia-ui/lib/components/FilterSidebar/__tests__/filterSidebar.spec.js b/packages/venia-ui/lib/components/FilterSidebar/__tests__/filterSidebar.spec.js
index 9fafd2bbf0..2c4e635dad 100644
--- a/packages/venia-ui/lib/components/FilterSidebar/__tests__/filterSidebar.spec.js
+++ b/packages/venia-ui/lib/components/FilterSidebar/__tests__/filterSidebar.spec.js
@@ -51,6 +51,8 @@ const mockHandleApply = jest.fn();
const mockScrollTo = jest.fn();
+const mockFilterOptions = jest.fn();
+
const mockGetBoundingClientRect = jest.fn();
let mockFilterState;
@@ -125,7 +127,8 @@ const Component = () => {
const givenDefaultValues = () => {
inputProps = {
- filters: []
+ filters: [],
+ setFilterOptions: mockFilterOptions
};
mockFilterState = new Map();
@@ -133,13 +136,15 @@ const givenDefaultValues = () => {
const givenFilters = () => {
inputProps = {
- filters: mockFilters
+ filters: mockFilters,
+ setFilterOptions: mockFilterOptions
};
};
const givenSelectedFilters = () => {
inputProps = {
- filters: mockFilters
+ filters: mockFilters,
+ setFilterOptions: mockFilterOptions
};
mockFilterState = new Map([['group', 'item']]);
@@ -148,7 +153,8 @@ const givenSelectedFilters = () => {
const givenFiltersAndAmountToShow = () => {
inputProps = {
filters: mockFilters,
- filterCountToOpen: mockFiltersOpenCount
+ filterCountToOpen: mockFiltersOpenCount,
+ setFilterOptions: mockFilterOptions
};
};
diff --git a/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js b/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js
index 4fa266eb8a..59a564a2d8 100644
--- a/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js
+++ b/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js
@@ -1,4 +1,4 @@
-import React, { useMemo, useCallback, useRef } from 'react';
+import React, { useMemo, useCallback, useRef, useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
import { array, arrayOf, shape, string, number } from 'prop-types';
import { useFilterSidebar } from '@magento/peregrine/lib/talons/FilterSidebar';
@@ -17,7 +17,7 @@ const SCROLL_OFFSET = 150;
* @param {Object} props.filters - filters to display
*/
const FilterSidebar = props => {
- const { filters, filterCountToOpen } = props;
+ const { filters, filterCountToOpen, setFilterOptions } = props;
const talonProps = useFilterSidebar({ filters });
const {
filterApi,
@@ -50,6 +50,12 @@ const FilterSidebar = props => {
[handleApply, filterRef]
);
+ useEffect(() => {
+ if (filterState) {
+ setFilterOptions(filterState);
+ }
+ }, [filterState, setFilterOptions]);
+
const filtersList = useMemo(
() =>
Array.from(filterItems, ([group, items], iteration) => {