From 0f73ad48fb097bbb10a2bc6731c3be38a367df24 Mon Sep 17 00:00:00 2001 From: Bharathidasan Elangovan Date: Fri, 17 Jan 2025 12:16:05 +0530 Subject: [PATCH 1/5] Price slider implementation --- .../lib/talons/FilterModal/helpers.js | 2 +- .../lib/talons/FilterModal/useFilterBlock.js | 11 +- .../talons/FilterSidebar/useFilterSidebar.js | 7 +- .../CurrentFilters/currentFilter.js | 12 +- .../FilterModal/FilterList/filterList.js | 131 +++++++++--- .../lib/components/FilterModal/filterBlock.js | 3 +- .../components/FilterSidebar/filterSidebar.js | 28 +++ .../lib/components/RangeSlider/rangeSlider.js | 197 ++++++++++++++++++ .../RangeSlider/rangeSlider.module.css | 76 +++++++ 9 files changed, 428 insertions(+), 39 deletions(-) create mode 100644 packages/venia-ui/lib/components/RangeSlider/rangeSlider.js create mode 100644 packages/venia-ui/lib/components/RangeSlider/rangeSlider.module.css 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 = ( -
  • - + -
  • + ); + } else { + return items.map((item, index) => { + const { title, value } = item; + const key = `item-${group}-${value}`; - // 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; - }); + if (!isListExpanded && index >= itemCountToShow) { + return null; + } + + const element = ( +
  • + +
  • + ); + labels.set(element, title.toUpperCase()); + return element; + }); + } }, [ classes, filterApi, @@ -91,7 +153,14 @@ const FilterList = props => { items, isListExpanded, itemCountToShow, - onApply + onApply, + history, + minRange, + maxRange, + pathname, + search, + value, + isSliding ]); const showMoreLessItem = useMemo(() => { diff --git a/packages/venia-ui/lib/components/FilterModal/filterBlock.js b/packages/venia-ui/lib/components/FilterModal/filterBlock.js index d667d98810..b961c0a3ac 100644 --- a/packages/venia-ui/lib/components/FilterModal/filterBlock.js +++ b/packages/venia-ui/lib/components/FilterModal/filterBlock.js @@ -28,7 +28,8 @@ const FilterBlock = props => { const talonProps = useFilterBlock({ filterState, items, - initialOpen + initialOpen, + group }); const { handleClick, isExpanded } = talonProps; const iconSrc = isExpanded ? ArrowUp : ArrowDown; diff --git a/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js b/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js index 4fa266eb8a..758400e1be 100644 --- a/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js +++ b/packages/venia-ui/lib/components/FilterSidebar/filterSidebar.js @@ -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; @@ -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) => { @@ -115,6 +142,7 @@ const FilterSidebar = props => { /> + {priceFilters} { + const [minVal, setMinVal] = useState(min); + const [maxVal, setMaxVal] = useState(max); + const minValRef = useRef(min); + const maxValRef = useRef(max); + const range = useRef(null); + + // Convert value to percentage for positioning + const getPercent = useCallback( + value => Math.round(((value - min) / (max - min)) * 100), + [min, max] + ); + + // set width of the range to decrease from the left side + useEffect(() => { + const minPercent = getPercent(minVal); + const maxPercent = getPercent(maxValRef.current); + + if (range.current) { + range.current.style.left = `${minPercent}%`; + range.current.style.width = `${maxPercent - minPercent}%`; + } + }, [minVal, getPercent]); + + // set the width of the range to decrease from right side + useEffect(() => { + const minPercent = getPercent(minValRef.current); + const maxPercent = getPercent(maxVal); + + if (range.current) { + range.current.style.width = `${maxPercent - minPercent}%`; + } + }, [maxVal, getPercent]); + + // Handle min and max value changes + useEffect(() => { + if (minVal !== minValRef.current || maxVal !== maxValRef.current) { + onChange({ min: minVal, max: maxVal }); + minValRef.current = minVal; + maxValRef.current = maxVal; + } + }, [minVal, maxVal, onChange]); + + return ( + //
    + // {/* Display Price Value */} + //
    + // + // {currencyText} {minVal} + // + + //
    + + // + // {currencyText} {maxVal} + // + //
    + + // {/* Slider */} + //
    + //
    + //
    + // {/* Min slider */} + // { + // const value = Math.min(Number(event.target.value), maxVal - 1); + // setMinVal(value); + // }} + // className="thumb thumb-left" + // style={{ + // position: 'absolute', + // zIndex: 2, + // width, + // top: '50%', + // transform: 'translateY(-50%)', + // background: 'none', + // }} + // /> + + // {/* Max slider */} + // { + // const value = Math.max(Number(event.target.value), minVal + 1); + // setMaxVal(value); + // }} + // className="thumb thumb-right" + // style={{ + // position: 'absolute', + // zIndex: 3, + // width, + // top: '50%', + // transform: 'translateY(-50%)', + // background: 'none', + // }} + // /> + //
    + //
    + //
    +
    + {/* Display Price Value */} +
    + {minVal} + {maxVal} +
    + + {/* Style the price range slider */} +
    + { + const value = Math.min( + Number(event.target.value), + maxVal - 1 + ); + setMinVal(value); + }} + className={`${styles.thumb} ${styles.thumbleft}`} + style={{ + width, + zIndex: + minVal > max - 100 || minVal === maxVal + ? 5 + : undefined + }} + /> + + { + const value = Math.max( + Number(event.target.value), + minVal + 1 + ); + setMaxVal(value); + }} + className={`${styles.thumb} ${styles.thumbright}`} + style={{ + width, + zIndex: + minVal > max - 100 || minVal === maxVal + ? 4 + : undefined + }} + /> + +
    +
    + +
    +
    +
    +
    + ); +}; + +export default PriceRangeSlider; diff --git a/packages/venia-ui/lib/components/RangeSlider/rangeSlider.module.css b/packages/venia-ui/lib/components/RangeSlider/rangeSlider.module.css new file mode 100644 index 0000000000..1fe684beac --- /dev/null +++ b/packages/venia-ui/lib/components/RangeSlider/rangeSlider.module.css @@ -0,0 +1,76 @@ +.slider { + position: relative; +} + +.trackslider, +.rangeslider { + position: absolute; +} + +.trackslider, +.rangeslider { + border-radius: 4px; + height: 6px; +} + +.trackslider { + background-color: #022140; + width: 100%; + z-index: 1; +} + +.rangeslider { + z-index: 2; +} + +.thumb, +.thumb::-webkit-slider-thumb { + -webkit-appearance: none; + -webkit-tap-highlight-color: transparent; +} + +.thumb { + pointer-events: none; + position: absolute; + height: 0; + outline: none; +} + +.thumbleft { + z-index: 3; +} + +.thumbright { + z-index: 4; +} + +.thumb::-webkit-slider-thumb { + background-color: #2954fe; + border-radius: 50%; + /* box-shadow: 0 0 1px 1px #f50707; */ + /* border: 4px solid #ff0303; */ + cursor: pointer; + height: 20px; + width: 20px; + margin-top: 4.5px; + pointer-events: all; + position: relative; + transition: transform 0.3s ease-in-out; +} + +.thumb::-webkit-slider-thumb:hover { + transform: scale(1.2); +} + +.thumb::-moz-range-thumb { + background-color: #0a0a0a; + border-radius: 50%; + /* box-shadow: 0 0 1px 1px #f50707; */ + border: 4px solid #ff0303; + cursor: pointer; + height: 28px; + width: 28px; + margin-top: 4.5px; + pointer-events: all; + position: relative; +} From 5e17a9dd6ba6a83f897ab1f88a1d95bd5c4b8430 Mon Sep 17 00:00:00 2001 From: Bharathidasan Elangovan Date: Mon, 20 Jan 2025 12:30:12 +0530 Subject: [PATCH 2/5] Test script changes --- .../__tests__/useFilterBlock.spec.js | 26 +++++++-- .../__snapshots__/useFormError.spec.js.snap | 4 +- .../__tests__/currentFilter.spec.js | 7 ++- .../FilterList/__tests__/filterList.spec.js | 19 ++++++- .../__tests__/filterSidebar.spec.js | 56 ++++++++++++++----- 5 files changed, 86 insertions(+), 26 deletions(-) diff --git a/packages/peregrine/lib/talons/FilterModal/__tests__/useFilterBlock.spec.js b/packages/peregrine/lib/talons/FilterModal/__tests__/useFilterBlock.spec.js index 83b08a5de7..5aef1275e5 100644 --- a/packages/peregrine/lib/talons/FilterModal/__tests__/useFilterBlock.spec.js +++ b/packages/peregrine/lib/talons/FilterModal/__tests__/useFilterBlock.spec.js @@ -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'; @@ -56,7 +56,11 @@ describe('#useFilterBlock', () => { }); it('is closed by default', () => { - createTestInstance(); + createTestInstance( + + + + ); expect(log).toHaveBeenCalledWith({ handleClick: expect.any(Function), @@ -66,7 +70,11 @@ describe('#useFilterBlock', () => { it('is open if passed initially open', () => { givenInitiallyOpen(); - createTestInstance(); + createTestInstance( + + + + ); expect(log).toHaveBeenCalledWith({ handleClick: expect.any(Function), @@ -76,7 +84,11 @@ describe('#useFilterBlock', () => { it('is open if items are selected', () => { givenSelectedItems(); - createTestInstance(); + createTestInstance( + + + + ); expect(log).toHaveBeenCalledWith({ handleClick: expect.any(Function), @@ -85,7 +97,11 @@ describe('#useFilterBlock', () => { }); it('can toggle visibility', () => { - createTestInstance(); + createTestInstance( + + + + ); expect(typeof handleClickProp).toBe('function'); diff --git a/packages/peregrine/lib/talons/FormError/__tests__/__snapshots__/useFormError.spec.js.snap b/packages/peregrine/lib/talons/FormError/__tests__/__snapshots__/useFormError.spec.js.snap index 0f2ab4c9ba..fc13cc8b6c 100644 --- a/packages/peregrine/lib/talons/FormError/__tests__/__snapshots__/useFormError.spec.js.snap +++ b/packages/peregrine/lib/talons/FormError/__tests__/__snapshots__/useFormError.spec.js.snap @@ -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"`; diff --git a/packages/venia-ui/lib/components/FilterModal/CurrentFilters/__tests__/currentFilter.spec.js b/packages/venia-ui/lib/components/FilterModal/CurrentFilters/__tests__/currentFilter.spec.js index 4176744cba..def763d4f1 100644 --- a/packages/venia-ui/lib/components/FilterModal/CurrentFilters/__tests__/currentFilter.spec.js +++ b/packages/venia-ui/lib/components/FilterModal/CurrentFilters/__tests__/currentFilter.spec.js @@ -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'; @@ -19,7 +20,11 @@ jest.mock('../../../Trigger', () => props => ); let inputProps = {}; const Component = () => { - return ; + return ( + + + + ); }; const givenDefaultValues = () => { diff --git a/packages/venia-ui/lib/components/FilterModal/FilterList/__tests__/filterList.spec.js b/packages/venia-ui/lib/components/FilterModal/FilterList/__tests__/filterList.spec.js index d13f05dec1..dba9b88aaf 100644 --- a/packages/venia-ui/lib/components/FilterModal/FilterList/__tests__/filterList.spec.js +++ b/packages/venia-ui/lib/components/FilterModal/FilterList/__tests__/filterList.spec.js @@ -1,4 +1,5 @@ import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; import { createTestInstance } from '@magento/peregrine'; @@ -67,7 +68,11 @@ describe('#FilterList', () => { }); it('renders without show more button', () => { - const { root } = createTestInstance(); + const { root } = createTestInstance( + + + + ); expect(root.findAllByType(FilterItem)).toHaveLength(2); expect(() => root.findByType('button')).toThrow(); @@ -76,7 +81,11 @@ describe('#FilterList', () => { it('renders with show more button', () => { givenShowMore(); - const { root } = createTestInstance(); + const { root } = createTestInstance( + + + + ); expect(() => root.findByType('button')).not.toThrow(); expect(root.findByType('button').children[0]).toBe(mockShowMore); @@ -86,7 +95,11 @@ describe('#FilterList', () => { givenShowMore(); givenExpanded(); - const { root } = createTestInstance(); + const { root } = createTestInstance( + + + + ); expect(() => root.findByType('button')).not.toThrow(); expect(root.findByType('button').children[0]).toBe(mockShowLess); 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..a1b7502f20 100644 --- a/packages/venia-ui/lib/components/FilterSidebar/__tests__/filterSidebar.spec.js +++ b/packages/venia-ui/lib/components/FilterSidebar/__tests__/filterSidebar.spec.js @@ -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'; @@ -161,7 +162,11 @@ describe('#FilterSidebar', () => { }); it('renders without filters', () => { - createTestInstance(); + createTestInstance( + + + + ); expect(mockFilterBlock).not.toHaveBeenCalled(); expect(mockCurrentFilters).toHaveBeenCalled(); @@ -170,7 +175,11 @@ describe('#FilterSidebar', () => { it('renders with filters and no selected filters', () => { givenFilters(); - const { root } = createTestInstance(); + const { root } = createTestInstance( + + + + ); expect(() => root.findByType(LinkButton)).toThrow(); expect(mockFilterBlock).toHaveBeenCalledTimes(mockFilters.length); @@ -179,7 +188,11 @@ describe('#FilterSidebar', () => { it('renders with filters and selected filter', () => { givenSelectedFilters(); - const { root } = createTestInstance(); + const { root } = createTestInstance( + + + + ); expect(() => root.findByType(LinkButton)).not.toThrow(); expect(mockFilterBlock).toHaveBeenCalledTimes(mockFilters.length); @@ -188,7 +201,11 @@ describe('#FilterSidebar', () => { it('handles when a user applies a filter and ref is not provided', () => { givenSelectedFilters(); - const { root } = createTestInstance(); + const { root } = createTestInstance( + + + + ); act(() => { root.findAllByType(FilterBlock)[0].props.onApply(); @@ -206,17 +223,22 @@ describe('#FilterSidebar', () => { value: mockScrollTo }); - const { root } = createTestInstance(, { - createNodeMock: () => { - return { - getBoundingClientRect: mockGetBoundingClientRect.mockReturnValue( - { - top: 250 - } - ) - }; + const { root } = createTestInstance( + + + , + { + createNodeMock: () => { + return { + getBoundingClientRect: mockGetBoundingClientRect.mockReturnValue( + { + top: 250 + } + ) + }; + } } - }); + ); act(() => { root.findAllByType(FilterBlock)[0].props.onApply(); @@ -229,7 +251,11 @@ describe('#FilterSidebar', () => { it('accepts configurable amount of open filters', () => { givenFiltersAndAmountToShow(); - createTestInstance(); + createTestInstance( + + + + ); expect(mockFilterBlock).toHaveBeenCalledTimes(mockFilters.length); From e8db7108dc84b7024b1f8fe8786c3b42eb92fe1c Mon Sep 17 00:00:00 2001 From: Bharathidasan Elangovan Date: Mon, 20 Jan 2025 14:16:16 +0530 Subject: [PATCH 3/5] Fixed lint issues --- .../FilterModal/FilterList/filterList.js | 72 ++++++------------- .../lib/components/RangeSlider/rangeSlider.js | 72 ------------------- 2 files changed, 22 insertions(+), 122 deletions(-) diff --git a/packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js b/packages/venia-ui/lib/components/FilterModal/FilterList/filterList.js index 67462df866..7d5332f0ed 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, useState, useRef } 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'; @@ -32,55 +32,31 @@ const FilterList = props => { const { formatMessage } = useIntl(); 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 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}` + ); - 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); - }; + history.push({ pathname, search: String(nextParams) }); + }, + [group, history, pathname, search] + ); // Memoize item creation const itemElements = useMemo(() => { @@ -154,13 +130,9 @@ const FilterList = props => { isListExpanded, itemCountToShow, onApply, - history, minRange, maxRange, - pathname, - search, - value, - isSliding + handleChange ]); const showMoreLessItem = useMemo(() => { diff --git a/packages/venia-ui/lib/components/RangeSlider/rangeSlider.js b/packages/venia-ui/lib/components/RangeSlider/rangeSlider.js index e8798aa109..8245f315d8 100644 --- a/packages/venia-ui/lib/components/RangeSlider/rangeSlider.js +++ b/packages/venia-ui/lib/components/RangeSlider/rangeSlider.js @@ -1,20 +1,10 @@ import React, { useCallback, useEffect, useState, useRef } from 'react'; import styles from './rangeSlider.module.css'; -const valueCSS = { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - zIndex: 1, - gap: '2px', - paddingTop: '10px' -}; - const PriceRangeSlider = ({ min, max, onChange, - valueStyle = valueCSS, width = '300px', trackColor = '#cecece', rangeColor = '#2954fe' @@ -62,68 +52,6 @@ const PriceRangeSlider = ({ }, [minVal, maxVal, onChange]); return ( - //
    - // {/* Display Price Value */} - //
    - // - // {currencyText} {minVal} - // - - //
    - - // - // {currencyText} {maxVal} - // - //
    - - // {/* Slider */} - //
    - //
    - //
    - // {/* Min slider */} - // { - // const value = Math.min(Number(event.target.value), maxVal - 1); - // setMinVal(value); - // }} - // className="thumb thumb-left" - // style={{ - // position: 'absolute', - // zIndex: 2, - // width, - // top: '50%', - // transform: 'translateY(-50%)', - // background: 'none', - // }} - // /> - - // {/* Max slider */} - // { - // const value = Math.max(Number(event.target.value), minVal + 1); - // setMaxVal(value); - // }} - // className="thumb thumb-right" - // style={{ - // position: 'absolute', - // zIndex: 3, - // width, - // top: '50%', - // transform: 'translateY(-50%)', - // background: 'none', - // }} - // /> - //
    - //
    - //
    {/* Display Price Value */}
    From 561e4806e985cd4545dd154daac9a33156fd529a Mon Sep 17 00:00:00 2001 From: Bharathidasan Elangovan Date: Wed, 22 Jan 2025 16:25:06 +0530 Subject: [PATCH 4/5] Fixed graphql blocker issue for this ticket --- .../CmsDynamicBlock/cmsDynamicBlock.gql.js | 4 -- .../CmsDynamicBlock/useCmsDynamicBlock.js | 60 +++---------------- 2 files changed, 7 insertions(+), 57 deletions(-) diff --git a/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js b/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js index 84c36ce30a..fb46f91480 100644 --- a/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js +++ b/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js @@ -2,16 +2,12 @@ import { gql } from '@apollo/client'; export const GET_CMS_DYNAMIC_BLOCKS = gql` query GetCmsDynamicBlocks( - $cartId: String - $productId: ID $type: DynamicBlockTypeEnum! $locations: [DynamicBlockLocationEnum] $uids: [ID]! ) { dynamicBlocks( input: { - cart_id: $cartId - product_uid: $productId dynamic_block_uids: $uids locations: $locations type: $type diff --git a/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js b/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js index e40b564eed..36eb506257 100644 --- a/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js +++ b/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js @@ -1,6 +1,5 @@ import { useEffect, useCallback } from 'react'; import { useQuery } from '@apollo/client'; -import { useLocation } from 'react-router-dom'; import { useCartContext } from '@magento/peregrine/lib/context/cart'; import mergeOperations from '@magento/peregrine/lib/util/shallowMerge'; @@ -72,51 +71,18 @@ export const flatten = cartData => { export const useCmsDynamicBlock = props => { const { locations, uids, type } = props; const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations); - const { - getCmsDynamicBlocksQuery, - getSalesRulesDataQuery, - getStoreConfigData, - getProductDetailQuery - } = operations; + const { getCmsDynamicBlocksQuery, getSalesRulesDataQuery } = operations; const [{ cartId }] = useCartContext(); - const { pathname } = useLocation(); - - // Get Product Data from cache - const { data: storeConfigData, loading: storeConfigLoading } = useQuery( - getStoreConfigData - ); - const slug = pathname.split('/').pop(); - const productUrlSuffix = storeConfigData?.storeConfig?.product_url_suffix; - const urlKey = productUrlSuffix ? slug.replace(productUrlSuffix, '') : slug; - const { data: productData, loading: productDataLoading } = useQuery( - getProductDetailQuery, - { - skip: !storeConfigData, - variables: { - urlKey - } - } - ); - - // @TODO: Update with uid when done in Product Root Component - const products = - productData?.products?.items && productData.products.items.length > 0 - ? productData.products.items - : []; - const productUid = products.find(item => item.url_key === urlKey)?.uid; const { client, loading, error, data, refetch } = useQuery( getCmsDynamicBlocksQuery, { variables: { - cartId, type, locations, - uids, - ...(productUid ? { productId: productUid } : {}) - }, - skip: !cartId + uids + } } ); @@ -129,8 +95,7 @@ export const useCmsDynamicBlock = props => { ); const currentSalesRulesData = flatten(cartData); - const isLoading = - loading || cartLoading || storeConfigLoading || productDataLoading; + const isLoading = loading || cartLoading; const cachedSalesRulesData = data?.dynamicBlocks?.salesRulesData; const updateSalesRulesData = useCallback( @@ -144,24 +109,13 @@ export const useCmsDynamicBlock = props => { } }, variables: { - cartId, type, locations, - uids, - ...(productUid ? { productId: productUid } : {}) - }, - skip: !cartId + uids + } }); }, - [ - cartId, - client, - getCmsDynamicBlocksQuery, - locations, - productUid, - type, - uids - ] + [client, getCmsDynamicBlocksQuery, locations, type, uids] ); useEffect(() => { From 67db1e8537cb2689b266cd3047928b8e73ec0672 Mon Sep 17 00:00:00 2001 From: Bharathidasan Elangovan Date: Tue, 28 Jan 2025 12:51:23 +0530 Subject: [PATCH 5/5] Reverted dynamic block query changes --- .../CmsDynamicBlock/cmsDynamicBlock.gql.js | 4 ++ .../CmsDynamicBlock/useCmsDynamicBlock.js | 60 ++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js b/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js index fb46f91480..84c36ce30a 100644 --- a/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js +++ b/packages/peregrine/lib/talons/CmsDynamicBlock/cmsDynamicBlock.gql.js @@ -2,12 +2,16 @@ import { gql } from '@apollo/client'; export const GET_CMS_DYNAMIC_BLOCKS = gql` query GetCmsDynamicBlocks( + $cartId: String + $productId: ID $type: DynamicBlockTypeEnum! $locations: [DynamicBlockLocationEnum] $uids: [ID]! ) { dynamicBlocks( input: { + cart_id: $cartId + product_uid: $productId dynamic_block_uids: $uids locations: $locations type: $type diff --git a/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js b/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js index 36eb506257..e40b564eed 100644 --- a/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js +++ b/packages/peregrine/lib/talons/CmsDynamicBlock/useCmsDynamicBlock.js @@ -1,5 +1,6 @@ import { useEffect, useCallback } from 'react'; import { useQuery } from '@apollo/client'; +import { useLocation } from 'react-router-dom'; import { useCartContext } from '@magento/peregrine/lib/context/cart'; import mergeOperations from '@magento/peregrine/lib/util/shallowMerge'; @@ -71,18 +72,51 @@ export const flatten = cartData => { export const useCmsDynamicBlock = props => { const { locations, uids, type } = props; const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations); - const { getCmsDynamicBlocksQuery, getSalesRulesDataQuery } = operations; + const { + getCmsDynamicBlocksQuery, + getSalesRulesDataQuery, + getStoreConfigData, + getProductDetailQuery + } = operations; const [{ cartId }] = useCartContext(); + const { pathname } = useLocation(); + + // Get Product Data from cache + const { data: storeConfigData, loading: storeConfigLoading } = useQuery( + getStoreConfigData + ); + const slug = pathname.split('/').pop(); + const productUrlSuffix = storeConfigData?.storeConfig?.product_url_suffix; + const urlKey = productUrlSuffix ? slug.replace(productUrlSuffix, '') : slug; + const { data: productData, loading: productDataLoading } = useQuery( + getProductDetailQuery, + { + skip: !storeConfigData, + variables: { + urlKey + } + } + ); + + // @TODO: Update with uid when done in Product Root Component + const products = + productData?.products?.items && productData.products.items.length > 0 + ? productData.products.items + : []; + const productUid = products.find(item => item.url_key === urlKey)?.uid; const { client, loading, error, data, refetch } = useQuery( getCmsDynamicBlocksQuery, { variables: { + cartId, type, locations, - uids - } + uids, + ...(productUid ? { productId: productUid } : {}) + }, + skip: !cartId } ); @@ -95,7 +129,8 @@ export const useCmsDynamicBlock = props => { ); const currentSalesRulesData = flatten(cartData); - const isLoading = loading || cartLoading; + const isLoading = + loading || cartLoading || storeConfigLoading || productDataLoading; const cachedSalesRulesData = data?.dynamicBlocks?.salesRulesData; const updateSalesRulesData = useCallback( @@ -109,13 +144,24 @@ export const useCmsDynamicBlock = props => { } }, variables: { + cartId, type, locations, - uids - } + uids, + ...(productUid ? { productId: productUid } : {}) + }, + skip: !cartId }); }, - [client, getCmsDynamicBlocksQuery, locations, type, uids] + [ + cartId, + client, + getCmsDynamicBlocksQuery, + locations, + productUid, + type, + uids + ] ); useEffect(() => {