diff --git a/packages/peregrine/lib/talons/FilterModal/helpers.js b/packages/peregrine/lib/talons/FilterModal/helpers.js index 5d3a7b163c..cd958399c2 100644 --- a/packages/peregrine/lib/talons/FilterModal/helpers.js +++ b/packages/peregrine/lib/talons/FilterModal/helpers.js @@ -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` ); diff --git a/packages/peregrine/lib/talons/FilterModal/useFilterBlock.js b/packages/peregrine/lib/talons/FilterModal/useFilterBlock.js index 8691516d95..e6542aa943 100644 --- a/packages/peregrine/lib/talons/FilterModal/useFilterBlock.js +++ b/packages/peregrine/lib/talons/FilterModal/useFilterBlock.js @@ -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); diff --git a/packages/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js b/packages/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js index fbcc2bce3f..688e957db3 100644 --- a/packages/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js +++ b/packages/peregrine/lib/talons/FilterSidebar/useFilterSidebar.js @@ -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 => { diff --git a/packages/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilter.js b/packages/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilter.js index 8578c5546d..91a42d7950 100644 --- a/packages/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilter.js +++ b/packages/venia-ui/lib/components/FilterModal/CurrentFilters/currentFilter.js @@ -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'; @@ -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( { diff --git a/packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js b/packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js index 0acf3d07c1..67462df866 100644 --- a/packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js +++ b/packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js @@ -1,4 +1,4 @@ -import React, { Fragment, useMemo } from 'react'; +import React, { Fragment, useMemo, useState, useRef } from 'react'; import { array, func, number, shape, string } from 'prop-types'; import { useIntl } from 'react-intl'; import setValidator from '@magento/peregrine/lib/validators/set'; @@ -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(); @@ -22,13 +24,65 @@ 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') { + debugger; + var minRange = Number(items[0].value.split('_')[0]); + var maxRange = Number(items[items.length - 1].value.split('_')[1]); + } + + const [value, setValue] = useState([ + minRange ? minRange : null, + maxRange ? maxRange : null + ]); + + const [isSliding, setIsSliding] = useState(false); // Track whether the user is sliding + const sliderTimeoutRef = useRef(null); + + const handleSliderStart = () => { + setIsSliding(true); // User started sliding + if (sliderTimeoutRef.current) { + clearTimeout(sliderTimeoutRef.current); + } + }; + + const handleSliderEnd = newValue => { + setIsSliding(false); // User stopped sliding + // Call the actual onChange only after a brief delay (debounce) + sliderTimeoutRef.current = setTimeout(() => { + handleChange(newValue); + }, 300); // Delay of 300ms after the user stops interacting with the slider + }; + + const handleChange = newValue => { + // Remove 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); + + // Append the new price filter range in the URL + 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}`); + + // Write price filter state to history + history.push({ pathname, search: String(nextParams) }); + + // Set new value to the slider when the slider stops + setValue(newValue); + }; + + // Memoize item creation const itemElements = useMemo(() => { if (filterFrontendInput === 'boolean') { const key = `item-${group}`; @@ -51,36 +105,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 = ( -