Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Price slider implementation #4376

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import { act } from 'react-test-renderer';

import { MemoryRouter } from 'react-router-dom';
import { createTestInstance } from '@magento/peregrine';
import { useFilterBlock } from '../useFilterBlock';

Expand Down Expand Up @@ -56,7 +56,11 @@ describe('#useFilterBlock', () => {
});

it('is closed by default', () => {
createTestInstance(<Component />);
createTestInstance(
<MemoryRouter>
<Component />
</MemoryRouter>
);

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

it('is open if passed initially open', () => {
givenInitiallyOpen();
createTestInstance(<Component />);
createTestInstance(
<MemoryRouter>
<Component />
</MemoryRouter>
);

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

it('is open if items are selected', () => {
givenSelectedItems();
createTestInstance(<Component />);
createTestInstance(
<MemoryRouter>
<Component />
</MemoryRouter>
);

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

it('can toggle visibility', () => {
createTestInstance(<Component />);
createTestInstance(
<MemoryRouter>
<Component />
</MemoryRouter>
);

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

Expand Down
2 changes: 1 addition & 1 deletion packages/peregrine/lib/talons/FilterModal/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const getStateFromSearch = (initialValue, filterKeys, filterItems) => {

if (existingFilter) {
items.add(existingFilter);
} else {
} else if (group !== 'price') {
console.warn(
`Existing filter ${value} not found in possible filters`
);
Expand Down
11 changes: 9 additions & 2 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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,10 @@ export const useFilterSidebar = props => {
}, [handleClose]);

const handleReset = useCallback(() => {
filterApi.clear();
setIsApplying(true);
}, [filterApi, setIsApplying]);
//filterApi.clear();
//setIsApplying(true);
history.replace({ search: 'page=1' });
}, [history]);

const handleKeyDownActions = useCallback(
event => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`returns concatenated error message when allowErrorMessages 1`] = `"GraphQL Error 1, GraphQL Error 2"`;
exports[`returns concatenated error message when allowErrorMessages 1`] = `"formError.responseError"`;

exports[`returns general error message 1`] = `"formError.errorMessage, Generic Error"`;
exports[`returns general error message 1`] = `"formError.responseError, Generic Error"`;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { act } from 'react-test-renderer';
import { MemoryRouter } from 'react-router-dom';

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

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

const Component = () => {
return <CurrentFilter {...inputProps} />;
return (
<MemoryRouter>
<CurrentFilter {...inputProps} />
</MemoryRouter>
);
};

const givenDefaultValues = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
import { useIntl } from 'react-intl';
import { shape, string, func } from 'prop-types';
import { X as Remove } from 'react-feather';
import { useHistory, useLocation } from 'react-router-dom';

import { useStyle } from '../../../classify';
import Icon from '../../Icon';
Expand All @@ -12,13 +13,22 @@ 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
@@ -1,4 +1,5 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';

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

Expand Down Expand Up @@ -67,7 +68,11 @@ describe('#FilterList', () => {
});

it('renders without show more button', () => {
const { root } = createTestInstance(<Component />);
const { root } = createTestInstance(
<MemoryRouter>
<Component />
</MemoryRouter>
);

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

const { root } = createTestInstance(<Component />);
const { root } = createTestInstance(
<MemoryRouter>
<Component />
</MemoryRouter>
);

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

const { root } = createTestInstance(<Component />);
const { root } = createTestInstance(
<MemoryRouter>
<Component />
</MemoryRouter>
);

expect(() => root.findByType('button')).not.toThrow();
expect(root.findByType('button').children[0]).toBe(mockShowLess);
Expand Down
103 changes: 72 additions & 31 deletions packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment, useMemo } from 'react';
import React, { Fragment, useMemo, useCallback } from 'react';
import { array, func, number, shape, string } from 'prop-types';
import { useIntl } from 'react-intl';
import setValidator from '@magento/peregrine/lib/validators/set';
Expand All @@ -8,6 +8,8 @@ import { useStyle } from '../../../classify';
import FilterItem from './filterItem';
import defaultClasses from './filterList.module.css';
import FilterItemRadioGroup from './filterItemRadioGroup';
import RangeSlider from '../../RangeSlider/rangeSlider';
import { useHistory, useLocation } from 'react-router-dom';

const labels = new WeakMap();

Expand All @@ -22,13 +24,41 @@ 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();

// memoize item creation
// search value is not referenced, so this array is stable
if (name === 'Price') {
var minRange = Number(items[0].value.split('_')[0]);
var maxRange = Number(items[items.length - 1].value.split('_')[1]);
}

const handleChange = useCallback(
newValue => {
const test = String(search).split('&');
const filters = test.filter(element => {
return !element.includes('price');
});
const newSearch = filters.join('&');
const nextParams = new URLSearchParams(newSearch);

const DELIMITER = ',';
const title = String(newValue.min) + '-' + String(newValue.max);
const value = String(newValue.min) + '_' + String(newValue.max);
nextParams.append(
`${group}[filter]`,
`${title}${DELIMITER}${value}`
);

history.push({ pathname, search: String(nextParams) });
},
[group, history, pathname, search]
);

// Memoize item creation
const itemElements = useMemo(() => {
if (filterFrontendInput === 'boolean') {
const key = `item-${group}`;
Expand All @@ -51,36 +81,44 @@ const FilterList = props => {
);
}

return items.map((item, index) => {
const { title, value } = item;
const key = `item-${group}-${value}`;

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

// 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}
if (name === 'Price') {
return (
<div className={classes.root}>
<RangeSlider
min={minRange}
max={maxRange}
onChange={handleChange}
/>
</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;
});
const element = (
<li
key={key}
className={classes.item}
data-cy="FilterList-item"
>
<FilterItem
filterApi={filterApi}
filterState={filterState}
group={group}
item={item}
onApply={onApply}
/>
</li>
);
labels.set(element, title.toUpperCase());
return element;
});
}
}, [
classes,
filterApi,
Expand All @@ -91,7 +129,10 @@ const FilterList = props => {
items,
isListExpanded,
itemCountToShow,
onApply
onApply,
minRange,
maxRange,
handleChange
]);

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
Loading