Skip to content

Commit

Permalink
PWA-3162-V3::Price filter change in sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
glo80771 committed Sep 15, 2023
1 parent 0ddf8f0 commit a10cebb
Show file tree
Hide file tree
Showing 13 changed files with 376 additions and 36 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"watch:venia": "yarn venia run watch"
},
"dependencies": {
"@material-ui/core": "~4.12.4",
"caniuse-lite": "~1.0.30001335"
},
"devDependencies": {
Expand Down Expand Up @@ -115,4 +116,4 @@
"maxSize": "100 kB"
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { useFilterBlock } from '../useFilterBlock';

const log = jest.fn();

jest.mock('react-router-dom', () => ({
useLocation: jest.fn(() => ({ search: '?a=b&c=d' }))
}));

let handleClickProp = null;
let inputValues = {};

Expand Down
19 changes: 16 additions & 3 deletions packages/peregrine/lib/talons/FilterModal/useFilterBlock.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { useCallback, useState, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom';

export const useFilterBlock = props => {
const { filterState, items, initialOpen } = props;
const { filterState, items, initialOpen, group } = props;
const location = useLocation();

const hasSelected = useMemo(() => {
const params = new URLSearchParams(location.search);
//expansion of price filter dropdown
if (group == 'price') {
return params.get('price[filter]') ? true : false;
}
return items.some(item => {
return filterState && filterState.has(item);
});
}, [filterState, items]);
}, [filterState, items, group, location.search]);

const [isExpanded, setExpanded] = useState(hasSelected || initialOpen);

Expand All @@ -16,8 +23,14 @@ export const useFilterBlock = props => {
}, [hasSelected, initialOpen]);

const handleClick = useCallback(() => {
const params = new URLSearchParams(location.search);
if (initialOpen == false && group == 'price') {
params.get('price[filter]')
? setExpanded(true)
: setExpanded(false);
}
setExpanded(value => !value);
}, [setExpanded]);
}, [setExpanded, group, initialOpen, location.search]);

return {
handleClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jest.mock('react-router-dom', () => ({
useHistory: jest.fn(() => ({ push: jest.fn() })),
useLocation: jest.fn(() => ({ pathname: '', search: '' }))
}));

const mockPush = jest.fn();
useHistory.mockImplementation(() => ({ push: mockPush }));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,15 @@ export const useFilterSidebar = props => {
}, [handleClose]);

const handleReset = useCallback(() => {
// preserve all existing params
const params = new URLSearchParams(search);
if (params.get('price[filter]')) {
params.delete('price[filter]');
}
history.replace({ search: params.toString() });
filterApi.clear();
setIsApplying(true);
}, [filterApi, setIsApplying]);
}, [filterApi, setIsApplying, history, search]);

const handleKeyDownActions = useCallback(
event => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const mockOnRemove = jest.fn();

jest.mock('../../../Trigger', () => props => <mock-Trigger {...props} />);

jest.mock('react-router-dom', () => ({
useLocation: jest.fn(() => ({ search: '?a=b&c=d' })),
useHistory: jest.fn()
}));

let inputProps = {};

const Component = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,28 @@ import { useStyle } from '../../../classify';
import Icon from '../../Icon';
import Trigger from '../../Trigger';
import defaultClasses from './currentFilter.module.css';
import { useHistory, useLocation } from 'react-router-dom';

const CurrentFilter = props => {
const { group, item, removeItem, onRemove } = props;
const classes = useStyle(defaultClasses, props.classes);
const { formatMessage } = useIntl();
const location = useLocation();
const history = useHistory();

const handleClick = useCallback(() => {
removeItem({ group, item });
if (typeof onRemove === 'function') {
onRemove(group, item);
}
}, [group, item, removeItem, onRemove]);

if (group == 'price') {
// preserve all existing params
const params = new URLSearchParams(location.search);
params.delete('price[filter]');
history.replace({ search: params.toString() });
}
}, [group, item, removeItem, onRemove, history, location.search]);

const ariaLabel = formatMessage(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ jest.mock('@magento/peregrine/lib/talons/FilterModal', () => ({
})
}));

jest.mock('react-router-dom', () => ({
useHistory: jest.fn(() => ({ push: jest.fn() })),
useLocation: jest.fn(() => ({ pathname: '', search: '' }))
}));

let inputProps = {};

const Component = () => {
Expand Down
110 changes: 84 additions & 26 deletions packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { useStyle } from '../../../classify';
import FilterItem from './filterItem';
import defaultClasses from './filterList.module.css';
import FilterItemRadioGroup from './filterItemRadioGroup';
import Slider from '@material-ui/core/slider';
import { useHistory, useLocation } from 'react-router-dom';

const labels = new WeakMap();

Expand All @@ -22,11 +24,22 @@ const FilterList = props => {
items,
onApply
} = props;
const { pathname, search } = useLocation();
const history = useHistory();
const classes = useStyle(defaultClasses, props.classes);
const talonProps = useFilterList({ filterState, items, itemCountToShow });
const { isListExpanded, handleListToggle } = talonProps;
const { formatMessage } = useIntl();

if (name == 'Price') {
var minRange = Number(items[0].value.split('_')[0]);
var maxRange = Number(items[items.length - 1].value.split('_')[1]);
}
const [value, setValue] = React.useState([
minRange ? minRange : null,
maxRange ? maxRange : null
]);

// memoize item creation
// search value is not referenced, so this array is stable
const itemElements = useMemo(() => {
Expand All @@ -51,36 +64,75 @@ const FilterList = props => {
);
}

return items.map((item, index) => {
const { title, value } = item;
const key = `item-${group}-${value}`;
const handleChange = (event, newValue) => {
//removing the previous price filter from the url
const test = String(search).split('&');
const filters = test.filter(element => {
return !element.includes('price');
});
const newSearch = filters.join('&');
const nextParams = new URLSearchParams(newSearch);

if (!isListExpanded && index >= itemCountToShow) {
return null;
}
//appending the new price filter range in the url
const DELIMITER = ',';
const title = String(newValue[0]) + '-' + String(newValue[1]);
const value = String(newValue[0]) + '_' + String(newValue[1]);
nextParams.append(
`${group}[filter]`,
`${title}${DELIMITER}${value}`
);

// create an element for each item
const element = (
<li
key={key}
className={classes.item}
data-cy="FilterList-item"
>
<FilterItem
filterApi={filterApi}
filterState={filterState}
group={group}
item={item}
onApply={onApply}
// write price filter state to history
history.push({ pathname, search: String(nextParams) });

//setting new value to the slider on change
setValue(newValue);
};

if (name == 'Price') {
return (
<div className={classes.root}>
<Slider
value={value}
onChange={handleChange}
valueLabelDisplay="auto"
aria-labelledby="range-slider"
min={minRange}
max={maxRange}
/>
</li>
</div>
);
} else {
return items.map((item, index) => {
const { title, value } = item;
const key = `item-${group}-${value}`;

if (!isListExpanded && index >= itemCountToShow) {
return null;
}

// associate each element with its normalized title
// titles are not unique, so use the element as the key
labels.set(element, title.toUpperCase());
return element;
});
// create an element for each item
const element = (
<li
key={key}
className={classes.item}
data-cy="FilterList-item"
>
<FilterItem
filterApi={filterApi}
filterState={filterState}
group={group}
item={item}
onApply={onApply}
/>
</li>
);
// associate each element with its normalized title
// titles are not unique, so use the element as the key
labels.set(element, title.toUpperCase());
return element;
});
}
}, [
classes,
filterApi,
Expand All @@ -91,7 +143,13 @@ const FilterList = props => {
items,
isListExpanded,
itemCountToShow,
onApply
onApply,
history,
minRange,
maxRange,
pathname,
search,
value
]);

const showMoreLessItem = useMemo(() => {
Expand Down
3 changes: 2 additions & 1 deletion packages/venia-ui/lib/components/FilterModal/filterBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ const FilterBlock = props => {
const talonProps = useFilterBlock({
filterState,
items,
initialOpen
initialOpen,
group
});
const { handleClick, isExpanded } = talonProps;
const iconSrc = isExpanded ? ArrowUp : ArrowDown;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ let mockFilterState;

jest.mock('../../LinkButton', () => props => <mock-LinkButton {...props} />);

jest.mock('react-router-dom', () => ({
useLocation: jest.fn(() => ({ search: '?a=b&c=d' }))
}));

jest.mock('@magento/peregrine/lib/talons/FilterSidebar', () => ({
useFilterSidebar: jest.fn(({ filters }) => {
const names = new Map();
Expand Down
28 changes: 28 additions & 0 deletions packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import LinkButton from '../LinkButton';
import CurrentFilters from '../FilterModal/CurrentFilters';
import FilterBlock from '../FilterModal/filterBlock';
import defaultClasses from './filterSidebar.module.css';
import { useLocation } from 'react-router-dom';

const SCROLL_OFFSET = 150;

Expand All @@ -31,6 +32,32 @@ const FilterSidebar = props => {

const filterRef = useRef();
const classes = useStyle(defaultClasses, props.classes);
const location = useLocation();

//adding the price filter values to the filterstate
const priceFilters = Array.from(filterItems, ([group]) => {
if (group == 'price') {
// preserve all existing params
const params = new URLSearchParams(location.search);
const uniqueKeys = new Set(params.keys());
// iterate over existing param keys
for (const key of uniqueKeys) {
// if a key matches a known filter, add its items to the next state
if (key == 'price[filter]') {
const value = params.get('price[filter]');
const item = {
title: value.split(',')[0],
value: value.split(',')[1]
};
const filterVar = new Set();
filterVar.add(item);

//to display the price filter value after selecting the filter
filterState.set('price', new Set(filterVar));
}
}
}
});

const handleApplyFilter = useCallback(
(...args) => {
Expand Down Expand Up @@ -115,6 +142,7 @@ const FilterSidebar = props => {
/>
</h2>
</div>
{priceFilters}
<CurrentFilters
filterApi={filterApi}
filterNames={filterNames}
Expand Down
Loading

0 comments on commit a10cebb

Please sign in to comment.