Skip to content

Commit 39a2ee7

Browse files
authored
PWA-3389-Price slider implementation (#4386)
1 parent 071fc58 commit 39a2ee7

File tree

13 files changed

+423
-63
lines changed

13 files changed

+423
-63
lines changed

packages/peregrine/lib/talons/FilterModal/__tests__/useFilterBlock.spec.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect } from 'react';
22
import { act } from 'react-test-renderer';
3-
3+
import { MemoryRouter } from 'react-router-dom';
44
import { createTestInstance } from '@magento/peregrine';
55
import { useFilterBlock } from '../useFilterBlock';
66

@@ -56,7 +56,11 @@ describe('#useFilterBlock', () => {
5656
});
5757

5858
it('is closed by default', () => {
59-
createTestInstance(<Component />);
59+
createTestInstance(
60+
<MemoryRouter>
61+
<Component />
62+
</MemoryRouter>
63+
);
6064

6165
expect(log).toHaveBeenCalledWith({
6266
handleClick: expect.any(Function),
@@ -66,7 +70,11 @@ describe('#useFilterBlock', () => {
6670

6771
it('is open if passed initially open', () => {
6872
givenInitiallyOpen();
69-
createTestInstance(<Component />);
73+
createTestInstance(
74+
<MemoryRouter>
75+
<Component />
76+
</MemoryRouter>
77+
);
7078

7179
expect(log).toHaveBeenCalledWith({
7280
handleClick: expect.any(Function),
@@ -76,7 +84,11 @@ describe('#useFilterBlock', () => {
7684

7785
it('is open if items are selected', () => {
7886
givenSelectedItems();
79-
createTestInstance(<Component />);
87+
createTestInstance(
88+
<MemoryRouter>
89+
<Component />
90+
</MemoryRouter>
91+
);
8092

8193
expect(log).toHaveBeenCalledWith({
8294
handleClick: expect.any(Function),
@@ -85,7 +97,11 @@ describe('#useFilterBlock', () => {
8597
});
8698

8799
it('can toggle visibility', () => {
88-
createTestInstance(<Component />);
100+
createTestInstance(
101+
<MemoryRouter>
102+
<Component />
103+
</MemoryRouter>
104+
);
89105

90106
expect(typeof handleClickProp).toBe('function');
91107

packages/peregrine/lib/talons/FilterModal/helpers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const getStateFromSearch = (initialValue, filterKeys, filterItems) => {
5454

5555
if (existingFilter) {
5656
items.add(existingFilter);
57-
} else {
57+
} else if (group !== 'price') {
5858
console.warn(
5959
`Existing filter ${value} not found in possible filters`
6060
);

packages/peregrine/lib/talons/FilterModal/useFilterBlock.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import { useCallback, useState, useEffect, useMemo } from 'react';
2+
import { useLocation } from 'react-router-dom';
23

34
export const useFilterBlock = props => {
4-
const { filterState, items, initialOpen } = props;
5+
const { filterState, items, initialOpen, group } = props;
6+
const location = useLocation();
57

68
const hasSelected = useMemo(() => {
9+
const params = new URLSearchParams(location.search);
10+
//expansion of price filter dropdown
11+
if (group == 'price') {
12+
return params.get('price[filter]') ? true : false;
13+
}
714
return items.some(item => {
815
return filterState && filterState.has(item);
916
});
10-
}, [filterState, items]);
17+
}, [filterState, items, group, location.search]);
1118

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

packages/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,10 @@ export const useFilterSidebar = props => {
182182
}, [handleClose]);
183183

184184
const handleReset = useCallback(() => {
185-
filterApi.clear();
186-
setIsApplying(true);
187-
}, [filterApi, setIsApplying]);
185+
//filterApi.clear();
186+
//setIsApplying(true);
187+
history.replace({ search: 'page=1' });
188+
}, [history]);
188189

189190
const handleKeyDownActions = useCallback(
190191
event => {

packages/venia-ui/lib/components/FilterModal/CurrentFilters/__tests__/currentFilter.spec.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { act } from 'react-test-renderer';
3+
import { MemoryRouter } from 'react-router-dom';
34

45
import { createTestInstance } from '@magento/peregrine';
56

@@ -19,7 +20,11 @@ jest.mock('../../../Trigger', () => props => <mock-Trigger {...props} />);
1920
let inputProps = {};
2021

2122
const Component = () => {
22-
return <CurrentFilter {...inputProps} />;
23+
return (
24+
<MemoryRouter>
25+
<CurrentFilter {...inputProps} />
26+
</MemoryRouter>
27+
);
2328
};
2429

2530
const givenDefaultValues = () => {

packages/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilter.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
22
import { useIntl } from 'react-intl';
33
import { shape, string, func } from 'prop-types';
44
import { X as Remove } from 'react-feather';
5+
import { useHistory, useLocation } from 'react-router-dom';
56

67
import { useStyle } from '../../../classify';
78
import Icon from '../../Icon';
@@ -12,13 +13,22 @@ const CurrentFilter = props => {
1213
const { group, item, removeItem, onRemove } = props;
1314
const classes = useStyle(defaultClasses, props.classes);
1415
const { formatMessage } = useIntl();
16+
const location = useLocation();
17+
const history = useHistory();
1518

1619
const handleClick = useCallback(() => {
1720
removeItem({ group, item });
1821
if (typeof onRemove === 'function') {
1922
onRemove(group, item);
2023
}
21-
}, [group, item, removeItem, onRemove]);
24+
25+
if (group == 'price') {
26+
// preserve all existing params
27+
const params = new URLSearchParams(location.search);
28+
params.delete('price[filter]');
29+
history.replace({ search: params.toString() });
30+
}
31+
}, [group, item, removeItem, onRemove, history, location.search]);
2232

2333
const ariaLabel = formatMessage(
2434
{

packages/venia-ui/lib/components/FilterModal/FilterList/__tests__/filterList.spec.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { MemoryRouter } from 'react-router-dom';
23

34
import { createTestInstance } from '@magento/peregrine';
45

@@ -67,7 +68,11 @@ describe('#FilterList', () => {
6768
});
6869

6970
it('renders without show more button', () => {
70-
const { root } = createTestInstance(<Component />);
71+
const { root } = createTestInstance(
72+
<MemoryRouter>
73+
<Component />
74+
</MemoryRouter>
75+
);
7176

7277
expect(root.findAllByType(FilterItem)).toHaveLength(2);
7378
expect(() => root.findByType('button')).toThrow();
@@ -76,7 +81,11 @@ describe('#FilterList', () => {
7681
it('renders with show more button', () => {
7782
givenShowMore();
7883

79-
const { root } = createTestInstance(<Component />);
84+
const { root } = createTestInstance(
85+
<MemoryRouter>
86+
<Component />
87+
</MemoryRouter>
88+
);
8089

8190
expect(() => root.findByType('button')).not.toThrow();
8291
expect(root.findByType('button').children[0]).toBe(mockShowMore);
@@ -86,7 +95,11 @@ describe('#FilterList', () => {
8695
givenShowMore();
8796
givenExpanded();
8897

89-
const { root } = createTestInstance(<Component />);
98+
const { root } = createTestInstance(
99+
<MemoryRouter>
100+
<Component />
101+
</MemoryRouter>
102+
);
90103

91104
expect(() => root.findByType('button')).not.toThrow();
92105
expect(root.findByType('button').children[0]).toBe(mockShowLess);

packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Fragment, useMemo } from 'react';
1+
import React, { Fragment, useMemo, useCallback } from 'react';
22
import { array, func, number, shape, string } from 'prop-types';
33
import { useIntl } from 'react-intl';
44
import setValidator from '@magento/peregrine/lib/validators/set';
@@ -8,6 +8,8 @@ import { useStyle } from '../../../classify';
88
import FilterItem from './filterItem';
99
import defaultClasses from './filterList.module.css';
1010
import FilterItemRadioGroup from './filterItemRadioGroup';
11+
import RangeSlider from '../../RangeSlider/rangeSlider';
12+
import { useHistory, useLocation } from 'react-router-dom';
1113

1214
const labels = new WeakMap();
1315

@@ -22,13 +24,46 @@ const FilterList = props => {
2224
items,
2325
onApply
2426
} = props;
27+
const { pathname, search } = useLocation();
28+
const history = useHistory();
2529
const classes = useStyle(defaultClasses, props.classes);
2630
const talonProps = useFilterList({ filterState, items, itemCountToShow });
2731
const { isListExpanded, handleListToggle } = talonProps;
2832
const { formatMessage } = useIntl();
2933

30-
// memoize item creation
31-
// search value is not referenced, so this array is stable
34+
if (name === 'Price') {
35+
var minRange = Number(items[0].value.split('_')[0]);
36+
var maxRange = Number(items[items.length - 1].value.split('_')[1]);
37+
if (filterState !== undefined) {
38+
const filterArray = [...filterState];
39+
var currentMinVal = Number(filterArray[0].value.split('_')[0]);
40+
var currentMaxVal = Number(filterArray[0].value.split('_')[1]);
41+
}
42+
}
43+
44+
const handleChange = useCallback(
45+
newValue => {
46+
const test = String(search).split('&');
47+
const filters = test.filter(element => {
48+
return !element.includes('price');
49+
});
50+
const newSearch = filters.join('&');
51+
const nextParams = new URLSearchParams(newSearch);
52+
53+
const DELIMITER = ',';
54+
const title = String(newValue.min) + '-' + String(newValue.max);
55+
const value = String(newValue.min) + '_' + String(newValue.max);
56+
nextParams.append(
57+
`${group}[filter]`,
58+
`${title}${DELIMITER}${value}`
59+
);
60+
61+
history.push({ pathname, search: String(nextParams) });
62+
},
63+
[group, history, pathname, search]
64+
);
65+
66+
// Memoize item creation
3267
const itemElements = useMemo(() => {
3368
if (filterFrontendInput === 'boolean') {
3469
const key = `item-${group}`;
@@ -51,36 +86,46 @@ const FilterList = props => {
5186
);
5287
}
5388

54-
return items.map((item, index) => {
55-
const { title, value } = item;
56-
const key = `item-${group}-${value}`;
57-
58-
if (!isListExpanded && index >= itemCountToShow) {
59-
return null;
60-
}
61-
62-
// create an element for each item
63-
const element = (
64-
<li
65-
key={key}
66-
className={classes.item}
67-
data-cy="FilterList-item"
68-
>
69-
<FilterItem
70-
filterApi={filterApi}
71-
filterState={filterState}
72-
group={group}
73-
item={item}
74-
onApply={onApply}
89+
if (name === 'Price') {
90+
return (
91+
<div className={classes.root}>
92+
<RangeSlider
93+
min={minRange}
94+
max={maxRange}
95+
initialMin={currentMinVal}
96+
initialMax={currentMaxVal}
97+
onChange={handleChange}
7598
/>
76-
</li>
99+
</div>
77100
);
101+
} else {
102+
return items.map((item, index) => {
103+
const { title, value } = item;
104+
const key = `item-${group}-${value}`;
105+
106+
if (!isListExpanded && index >= itemCountToShow) {
107+
return null;
108+
}
78109

79-
// associate each element with its normalized title
80-
// titles are not unique, so use the element as the key
81-
labels.set(element, title.toUpperCase());
82-
return element;
83-
});
110+
const element = (
111+
<li
112+
key={key}
113+
className={classes.item}
114+
data-cy="FilterList-item"
115+
>
116+
<FilterItem
117+
filterApi={filterApi}
118+
filterState={filterState}
119+
group={group}
120+
item={item}
121+
onApply={onApply}
122+
/>
123+
</li>
124+
);
125+
labels.set(element, title.toUpperCase());
126+
return element;
127+
});
128+
}
84129
}, [
85130
classes,
86131
filterApi,
@@ -91,7 +136,12 @@ const FilterList = props => {
91136
items,
92137
isListExpanded,
93138
itemCountToShow,
94-
onApply
139+
onApply,
140+
minRange,
141+
maxRange,
142+
currentMinVal,
143+
currentMaxVal,
144+
handleChange
95145
]);
96146

97147
const showMoreLessItem = useMemo(() => {

packages/venia-ui/lib/components/FilterModal/filterBlock.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ const FilterBlock = props => {
2828
const talonProps = useFilterBlock({
2929
filterState,
3030
items,
31-
initialOpen
31+
initialOpen,
32+
group
3233
});
3334
const { handleClick, isExpanded } = talonProps;
3435
const iconSrc = isExpanded ? ArrowUp : ArrowDown;

0 commit comments

Comments
 (0)