@faststore/ui 1.9.9 → 1.9.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/atoms/Slider/Slider.d.ts +14 -6
- package/dist/atoms/Slider/Slider.js +23 -39
- package/dist/atoms/Slider/Slider.js.map +1 -1
- package/dist/molecules/PriceRange/PriceRange.d.ts +1 -1
- package/dist/molecules/PriceRange/PriceRange.js +8 -15
- package/dist/molecules/PriceRange/PriceRange.js.map +1 -1
- package/package.json +4 -4
- package/src/atoms/Slider/Slider.test.tsx +13 -2
- package/src/atoms/Slider/Slider.tsx +50 -55
- package/src/molecules/PriceRange/PriceRange.test.tsx +8 -2
- package/src/molecules/PriceRange/PriceRange.tsx +12 -20
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.9.14](https://github.com/vtex/faststore/compare/v1.9.13...v1.9.14) (2022-06-23)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @faststore/ui
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [1.9.11](https://github.com/vtex/faststore/compare/v1.9.10...v1.9.11) (2022-06-19)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* Price range filter on PLP ([#1364](https://github.com/vtex/faststore/issues/1364)) ([a4c3fa7](https://github.com/vtex/faststore/commit/a4c3fa79c32d1db7bc737f5221479e6db1488866))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## [1.9.10](https://github.com/vtex/faststore/compare/v1.9.9...v1.9.10) (2022-06-18)
|
|
26
|
+
|
|
27
|
+
**Note:** Version bump only for package @faststore/ui
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
6
33
|
## 1.9.9 (2022-06-17)
|
|
7
34
|
|
|
8
35
|
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This code is inspired by the work of [sandra-lewis](https://codesandbox.io/u/sandra-lewis)
|
|
3
|
-
*/
|
|
4
1
|
/// <reference types="react" />
|
|
2
|
+
interface Range {
|
|
3
|
+
absolute: number;
|
|
4
|
+
selected: number;
|
|
5
|
+
}
|
|
5
6
|
export declare type SliderProps = {
|
|
6
7
|
/**
|
|
7
8
|
* The minimum value of the slider.
|
|
8
9
|
*/
|
|
9
|
-
min:
|
|
10
|
+
min: Range;
|
|
10
11
|
/**
|
|
11
12
|
* The maximum value of the slider.
|
|
12
13
|
*/
|
|
13
|
-
max:
|
|
14
|
+
max: Range;
|
|
14
15
|
/**
|
|
15
16
|
* ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
|
|
16
17
|
*
|
|
@@ -24,6 +25,13 @@ export declare type SliderProps = {
|
|
|
24
25
|
min: number;
|
|
25
26
|
max: number;
|
|
26
27
|
}) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Callback that fires when the slider value ends changing.
|
|
30
|
+
*/
|
|
31
|
+
onEnd?: (value: {
|
|
32
|
+
min: number;
|
|
33
|
+
max: number;
|
|
34
|
+
}) => void;
|
|
27
35
|
/**
|
|
28
36
|
* A function used to set a human-readable value text based on the slider's current value.
|
|
29
37
|
*/
|
|
@@ -33,5 +41,5 @@ export declare type SliderProps = {
|
|
|
33
41
|
*/
|
|
34
42
|
className?: string;
|
|
35
43
|
};
|
|
36
|
-
declare const Slider: ({ min, max, onChange, testId, getAriaValueText, className, }: SliderProps) => JSX.Element;
|
|
44
|
+
declare const Slider: ({ min, max, onChange, onEnd, testId, getAriaValueText, className, }: SliderProps) => JSX.Element;
|
|
37
45
|
export default Slider;
|
|
@@ -1,46 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This code is inspired by the work of [sandra-lewis](https://codesandbox.io/u/sandra-lewis)
|
|
3
3
|
*/
|
|
4
|
-
import React, {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const [
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (range.current) {
|
|
17
|
-
range.current.style.left = `${minPercent}%`;
|
|
18
|
-
range.current.style.width = `${maxPercent - minPercent}%`;
|
|
19
|
-
}
|
|
20
|
-
}, [minVal, getPercent]);
|
|
21
|
-
// width of the range to reduce from the right side
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
const minPercent = getPercent(minValRef.current);
|
|
24
|
-
const maxPercent = getPercent(maxVal);
|
|
25
|
-
if (range.current) {
|
|
26
|
-
range.current.style.width = `${maxPercent - minPercent}%`;
|
|
27
|
-
}
|
|
28
|
-
}, [maxVal, getPercent]);
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
onChange?.({ min: minVal, max: maxVal });
|
|
31
|
-
}, [minVal, maxVal, onChange]);
|
|
4
|
+
import React, { useState, useMemo } from 'react';
|
|
5
|
+
const percent = (value, min, max) => Math.round(((value - min) / (max - min)) * 100);
|
|
6
|
+
const Slider = ({ min, max, onChange, onEnd, testId = 'store-slider', getAriaValueText, className, }) => {
|
|
7
|
+
const [minPercent, setMinPercent] = useState(() => percent(min.selected, min.absolute, max.absolute));
|
|
8
|
+
const [maxPercent, setMaxPercent] = useState(() => percent(max.selected, min.absolute, max.absolute));
|
|
9
|
+
const { minVal, maxVal } = useMemo(() => {
|
|
10
|
+
const widthPercent = (max.absolute - min.absolute) / 100;
|
|
11
|
+
return {
|
|
12
|
+
minVal: min.absolute + minPercent * widthPercent,
|
|
13
|
+
maxVal: min.absolute + maxPercent * widthPercent,
|
|
14
|
+
};
|
|
15
|
+
}, [min, max, maxPercent, minPercent]);
|
|
32
16
|
return (React.createElement("div", { "data-store-slider": true, "data-testid": testId, className: className },
|
|
33
|
-
React.createElement("div", {
|
|
34
|
-
React.createElement("input", { type: "range", min: min, max: max, value: minVal, onChange: (event) => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}, "data-slider-thumb": "left", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": minVal, "aria-label": String(minVal), "aria-labelledby": getAriaValueText
|
|
39
|
-
React.createElement("input", { type: "range", min: min, max: max, value: maxVal, onChange: (event) => {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}, "data-slider-thumb": "right", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": maxVal, "aria-label": String(maxVal), "aria-labelledby": getAriaValueText
|
|
17
|
+
React.createElement("div", { style: { left: `${minPercent}%`, width: `${maxPercent - minPercent}%` }, "data-slider-range": true }),
|
|
18
|
+
React.createElement("input", { type: "range", min: min.absolute, max: max.absolute, value: minVal, onMouseUp: () => onEnd?.({ min: minVal, max: maxVal }), onTouchEnd: () => onEnd?.({ min: minVal, max: maxVal }), onChange: (event) => {
|
|
19
|
+
const minValue = Math.min(Number(event.target.value), maxVal);
|
|
20
|
+
setMinPercent(percent(minValue, min.absolute, max.absolute));
|
|
21
|
+
onChange?.({ min: minValue, max: maxVal });
|
|
22
|
+
}, "data-slider-thumb": "left", "aria-valuemin": min.absolute, "aria-valuemax": max.absolute, "aria-valuenow": minVal, "aria-label": String(minVal), "aria-labelledby": getAriaValueText?.(minVal, 'min') }),
|
|
23
|
+
React.createElement("input", { type: "range", min: min.absolute, max: max.absolute, value: maxVal, onMouseUp: () => onEnd?.({ min: minVal, max: maxVal }), onTouchEnd: () => onEnd?.({ min: minVal, max: maxVal }), onChange: (event) => {
|
|
24
|
+
const maxValue = Math.max(Number(event.target.value), minVal);
|
|
25
|
+
setMaxPercent(percent(maxValue, min.absolute, max.absolute));
|
|
26
|
+
onChange?.({ min: minVal, max: maxValue });
|
|
27
|
+
}, "data-slider-thumb": "right", "aria-valuemin": min.absolute, "aria-valuemax": max.absolute, "aria-valuenow": maxVal, "aria-label": String(maxVal), "aria-labelledby": getAriaValueText?.(maxVal, 'max') })));
|
|
44
28
|
};
|
|
45
29
|
export default Slider;
|
|
46
30
|
//# sourceMappingURL=Slider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Slider.js","sourceRoot":"","sources":["../../../src/atoms/Slider/Slider.tsx"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"Slider.js","sourceRoot":"","sources":["../../../src/atoms/Slider/Slider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -19,5 +19,5 @@ export declare type PriceRangeProps = SliderProps & {
|
|
|
19
19
|
*/
|
|
20
20
|
'aria-label'?: AriaAttributes['aria-label'];
|
|
21
21
|
};
|
|
22
|
-
declare const PriceRange: ({ className, formatter, max, min, onChange, testId, variant, "aria-label": ariaLabel, }: PriceRangeProps) => JSX.Element;
|
|
22
|
+
declare const PriceRange: ({ className, formatter, max, min, onChange, onEnd, testId, variant, "aria-label": ariaLabel, }: PriceRangeProps) => JSX.Element;
|
|
23
23
|
export default PriceRange;
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import Price from '../../atoms/Price';
|
|
3
3
|
import Slider from '../../atoms/Slider';
|
|
4
|
-
const PriceRange = ({ className, formatter, max, min, onChange, testId = 'store-price-range', variant, 'aria-label': ariaLabel, }) => {
|
|
5
|
-
const [
|
|
6
|
-
const [maxVal, setMaxVal] = useState(max);
|
|
7
|
-
const handleChange = (values) => {
|
|
8
|
-
if (values.min !== minVal) {
|
|
9
|
-
setMinVal(values.min);
|
|
10
|
-
}
|
|
11
|
-
if (values.max !== maxVal) {
|
|
12
|
-
setMaxVal(values.max);
|
|
13
|
-
}
|
|
14
|
-
onChange?.(values);
|
|
15
|
-
};
|
|
4
|
+
const PriceRange = ({ className, formatter, max, min, onChange, onEnd, testId = 'store-price-range', variant, 'aria-label': ariaLabel, }) => {
|
|
5
|
+
const [edges, setEdges] = useState({ min: min.selected, max: max.selected });
|
|
16
6
|
return (React.createElement("div", { "data-store-price-range": true, "data-testid": testId, className: className },
|
|
17
|
-
React.createElement(Slider, { min: min, max: max,
|
|
7
|
+
React.createElement(Slider, { min: min, max: max, onEnd: onEnd, onChange: (value) => {
|
|
8
|
+
setEdges(value);
|
|
9
|
+
onChange?.(value);
|
|
10
|
+
}, "aria-label": ariaLabel }),
|
|
18
11
|
React.createElement("div", { "data-price-range-values": true },
|
|
19
|
-
React.createElement(Price, { formatter: formatter, "data-price-range-value": "min", value:
|
|
20
|
-
React.createElement(Price, { formatter: formatter, "data-price-range-value": "max", value:
|
|
12
|
+
React.createElement(Price, { formatter: formatter, "data-price-range-value": "min", value: edges.min, variant: variant }),
|
|
13
|
+
React.createElement(Price, { formatter: formatter, "data-price-range-value": "max", value: edges.max, variant: variant }))));
|
|
21
14
|
};
|
|
22
15
|
export default PriceRange;
|
|
23
16
|
//# sourceMappingURL=PriceRange.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PriceRange.js","sourceRoot":"","sources":["../../../src/molecules/PriceRange/PriceRange.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PriceRange.js","sourceRoot":"","sources":["../../../src/molecules/PriceRange/PriceRange.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGvC,OAAO,KAAK,MAAM,mBAAmB,CAAA;AACrC,OAAO,MAAM,MAAM,oBAAoB,CAAA;AAuBvC,MAAM,UAAU,GAAG,CAAC,EAClB,SAAS,EACT,SAAS,EACT,GAAG,EACH,GAAG,EACH,QAAQ,EACR,KAAK,EACL,MAAM,GAAG,mBAAmB,EAC5B,OAAO,EACP,YAAY,EAAE,SAAS,GACP,EAAE,EAAE;IACpB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAA;IAE5E,OAAO,CACL,4EAAyC,MAAM,EAAE,SAAS,EAAE,SAAS;QACnE,oBAAC,MAAM,IACL,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBAClB,QAAQ,CAAC,KAAK,CAAC,CAAA;gBACf,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAA;YACnB,CAAC,gBACW,SAAS,GACrB;QACF;YACE,oBAAC,KAAK,IACJ,SAAS,EAAE,SAAS,4BACG,KAAK,EAC5B,KAAK,EAAE,KAAK,CAAC,GAAG,EAChB,OAAO,EAAE,OAAO,GAChB;YACF,oBAAC,KAAK,IACJ,SAAS,EAAE,SAAS,4BACG,KAAK,EAC5B,KAAK,EAAE,KAAK,CAAC,GAAG,EAChB,OAAO,EAAE,OAAO,GAChB,CACE,CACF,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,UAAU,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststore/ui",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.14",
|
|
4
4
|
"description": "A lightweight, framework agnostic component library for React",
|
|
5
5
|
"author": "emersonlaurentino",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
"directory": "packages/ui"
|
|
11
11
|
},
|
|
12
12
|
"browserslist": "supports es6-module and not dead and last 2 version",
|
|
13
|
-
"
|
|
14
|
-
"module": "dist/ui.esm.js",
|
|
13
|
+
"module": "dist/index.js",
|
|
15
14
|
"typings": "dist/index.d.ts",
|
|
16
15
|
"sideEffects": false,
|
|
17
16
|
"engines": {
|
|
@@ -81,10 +80,11 @@
|
|
|
81
80
|
"react": "^17.0.2",
|
|
82
81
|
"react-docgen-typescript-loader": "^3.7.2",
|
|
83
82
|
"react-dom": "^17.0.2",
|
|
83
|
+
"shared": "^1.9.14",
|
|
84
84
|
"size-limit": "^7.0.8",
|
|
85
85
|
"storybook-addon-themes": "^6.1.0",
|
|
86
86
|
"tsdx": "^0.14.1",
|
|
87
87
|
"typescript": "^4.2.4"
|
|
88
88
|
},
|
|
89
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "d37facb5b238781bb18ce48afb1b245e5c53038b"
|
|
90
90
|
}
|
|
@@ -4,16 +4,27 @@ import React from 'react'
|
|
|
4
4
|
|
|
5
5
|
import Slider from './Slider'
|
|
6
6
|
|
|
7
|
+
const props = {
|
|
8
|
+
min: {
|
|
9
|
+
absolute: 0,
|
|
10
|
+
selected: 0,
|
|
11
|
+
},
|
|
12
|
+
max: {
|
|
13
|
+
absolute: 100,
|
|
14
|
+
selected: 100,
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
describe('Slider', () => {
|
|
8
19
|
it('`data-store-slider` is present', () => {
|
|
9
|
-
const { getByTestId } = render(<Slider
|
|
20
|
+
const { getByTestId } = render(<Slider {...props} />)
|
|
10
21
|
|
|
11
22
|
expect(getByTestId('store-slider')).toHaveAttribute('data-store-slider')
|
|
12
23
|
})
|
|
13
24
|
|
|
14
25
|
describe('Accessibility', () => {
|
|
15
26
|
it('should have no violations', async () => {
|
|
16
|
-
const { getByTestId } = render(<Slider
|
|
27
|
+
const { getByTestId } = render(<Slider {...props} />)
|
|
17
28
|
|
|
18
29
|
expect(await axe(getByTestId('store-slider'))).toHaveNoViolations()
|
|
19
30
|
})
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This code is inspired by the work of [sandra-lewis](https://codesandbox.io/u/sandra-lewis)
|
|
3
3
|
*/
|
|
4
|
+
import React, { useState, useMemo } from 'react'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
interface Range {
|
|
7
|
+
absolute: number
|
|
8
|
+
selected: number
|
|
9
|
+
}
|
|
6
10
|
|
|
7
11
|
export type SliderProps = {
|
|
8
12
|
/**
|
|
9
13
|
* The minimum value of the slider.
|
|
10
14
|
*/
|
|
11
|
-
min:
|
|
15
|
+
min: Range
|
|
12
16
|
/**
|
|
13
17
|
* The maximum value of the slider.
|
|
14
18
|
*/
|
|
15
|
-
max:
|
|
19
|
+
max: Range
|
|
16
20
|
/**
|
|
17
21
|
* ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
|
|
18
22
|
*
|
|
@@ -23,6 +27,10 @@ export type SliderProps = {
|
|
|
23
27
|
* Callback that fires when the slider value changes.
|
|
24
28
|
*/
|
|
25
29
|
onChange?: (value: { min: number; max: number }) => void
|
|
30
|
+
/**
|
|
31
|
+
* Callback that fires when the slider value ends changing.
|
|
32
|
+
*/
|
|
33
|
+
onEnd?: (value: { min: number; max: number }) => void
|
|
26
34
|
/**
|
|
27
35
|
* A function used to set a human-readable value text based on the slider's current value.
|
|
28
36
|
*/
|
|
@@ -33,93 +41,80 @@ export type SliderProps = {
|
|
|
33
41
|
className?: string
|
|
34
42
|
}
|
|
35
43
|
|
|
44
|
+
const percent = (value: number, min: number, max: number) =>
|
|
45
|
+
Math.round(((value - min) / (max - min)) * 100)
|
|
46
|
+
|
|
36
47
|
const Slider = ({
|
|
37
48
|
min,
|
|
38
49
|
max,
|
|
39
50
|
onChange,
|
|
51
|
+
onEnd,
|
|
40
52
|
testId = 'store-slider',
|
|
41
53
|
getAriaValueText,
|
|
42
54
|
className,
|
|
43
55
|
}: SliderProps) => {
|
|
44
|
-
const [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const minValRef = useRef(min)
|
|
48
|
-
const maxValRef = useRef(max)
|
|
49
|
-
const range = useRef<HTMLDivElement>(null)
|
|
50
|
-
|
|
51
|
-
const getPercent = useCallback(
|
|
52
|
-
(value: number) => Math.round(((value - min) / (max - min)) * 100),
|
|
53
|
-
[min, max]
|
|
56
|
+
const [minPercent, setMinPercent] = useState(() =>
|
|
57
|
+
percent(min.selected, min.absolute, max.absolute)
|
|
54
58
|
)
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const maxPercent = getPercent(maxValRef.current)
|
|
60
|
-
|
|
61
|
-
if (range.current) {
|
|
62
|
-
range.current.style.left = `${minPercent}%`
|
|
63
|
-
range.current.style.width = `${maxPercent - minPercent}%`
|
|
64
|
-
}
|
|
65
|
-
}, [minVal, getPercent])
|
|
60
|
+
const [maxPercent, setMaxPercent] = useState(() =>
|
|
61
|
+
percent(max.selected, min.absolute, max.absolute)
|
|
62
|
+
)
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const minPercent = getPercent(minValRef.current)
|
|
70
|
-
const maxPercent = getPercent(maxVal)
|
|
64
|
+
const { minVal, maxVal } = useMemo(() => {
|
|
65
|
+
const widthPercent = (max.absolute - min.absolute) / 100
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
return {
|
|
68
|
+
minVal: min.absolute + minPercent * widthPercent,
|
|
69
|
+
maxVal: min.absolute + maxPercent * widthPercent,
|
|
74
70
|
}
|
|
75
|
-
}, [
|
|
76
|
-
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
onChange?.({ min: minVal, max: maxVal })
|
|
79
|
-
}, [minVal, maxVal, onChange])
|
|
71
|
+
}, [min, max, maxPercent, minPercent])
|
|
80
72
|
|
|
81
73
|
return (
|
|
82
74
|
<div data-store-slider data-testid={testId} className={className}>
|
|
83
|
-
<div
|
|
75
|
+
<div
|
|
76
|
+
style={{ left: `${minPercent}%`, width: `${maxPercent - minPercent}%` }}
|
|
77
|
+
data-slider-range
|
|
78
|
+
/>
|
|
84
79
|
<input
|
|
85
80
|
type="range"
|
|
86
|
-
min={min}
|
|
87
|
-
max={max}
|
|
81
|
+
min={min.absolute}
|
|
82
|
+
max={max.absolute}
|
|
88
83
|
value={minVal}
|
|
84
|
+
onMouseUp={() => onEnd?.({ min: minVal, max: maxVal })}
|
|
85
|
+
onTouchEnd={() => onEnd?.({ min: minVal, max: maxVal })}
|
|
89
86
|
onChange={(event) => {
|
|
90
|
-
const
|
|
87
|
+
const minValue = Math.min(Number(event.target.value), maxVal)
|
|
91
88
|
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
setMinPercent(percent(minValue, min.absolute, max.absolute))
|
|
90
|
+
onChange?.({ min: minValue, max: maxVal })
|
|
94
91
|
}}
|
|
95
92
|
data-slider-thumb="left"
|
|
96
|
-
aria-valuemin={min}
|
|
97
|
-
aria-valuemax={max}
|
|
93
|
+
aria-valuemin={min.absolute}
|
|
94
|
+
aria-valuemax={max.absolute}
|
|
98
95
|
aria-valuenow={minVal}
|
|
99
96
|
aria-label={String(minVal)}
|
|
100
|
-
aria-labelledby={
|
|
101
|
-
getAriaValueText ? getAriaValueText(minVal, 'min') : undefined
|
|
102
|
-
}
|
|
97
|
+
aria-labelledby={getAriaValueText?.(minVal, 'min')}
|
|
103
98
|
/>
|
|
104
99
|
<input
|
|
105
100
|
type="range"
|
|
106
|
-
min={min}
|
|
107
|
-
max={max}
|
|
101
|
+
min={min.absolute}
|
|
102
|
+
max={max.absolute}
|
|
108
103
|
value={maxVal}
|
|
104
|
+
onMouseUp={() => onEnd?.({ min: minVal, max: maxVal })}
|
|
105
|
+
onTouchEnd={() => onEnd?.({ min: minVal, max: maxVal })}
|
|
109
106
|
onChange={(event) => {
|
|
110
|
-
const
|
|
107
|
+
const maxValue = Math.max(Number(event.target.value), minVal)
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
setMaxPercent(percent(maxValue, min.absolute, max.absolute))
|
|
110
|
+
onChange?.({ min: minVal, max: maxValue })
|
|
114
111
|
}}
|
|
115
112
|
data-slider-thumb="right"
|
|
116
|
-
aria-valuemin={min}
|
|
117
|
-
aria-valuemax={max}
|
|
113
|
+
aria-valuemin={min.absolute}
|
|
114
|
+
aria-valuemax={max.absolute}
|
|
118
115
|
aria-valuenow={maxVal}
|
|
119
116
|
aria-label={String(maxVal)}
|
|
120
|
-
aria-labelledby={
|
|
121
|
-
getAriaValueText ? getAriaValueText(maxVal, 'max') : undefined
|
|
122
|
-
}
|
|
117
|
+
aria-labelledby={getAriaValueText?.(maxVal, 'max')}
|
|
123
118
|
/>
|
|
124
119
|
</div>
|
|
125
120
|
)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { AriaAttributes } from 'react'
|
|
2
1
|
import React, { useState } from 'react'
|
|
2
|
+
import type { AriaAttributes } from 'react'
|
|
3
3
|
|
|
4
|
-
import type { PriceProps } from '../../atoms/Price'
|
|
5
4
|
import Price from '../../atoms/Price'
|
|
6
|
-
import type { SliderProps } from '../../atoms/Slider'
|
|
7
5
|
import Slider from '../../atoms/Slider'
|
|
6
|
+
import type { PriceProps } from '../../atoms/Price'
|
|
7
|
+
import type { SliderProps } from '../../atoms/Slider'
|
|
8
8
|
|
|
9
9
|
export type PriceRangeProps = SliderProps & {
|
|
10
10
|
/**
|
|
@@ -31,44 +31,36 @@ const PriceRange = ({
|
|
|
31
31
|
max,
|
|
32
32
|
min,
|
|
33
33
|
onChange,
|
|
34
|
+
onEnd,
|
|
34
35
|
testId = 'store-price-range',
|
|
35
36
|
variant,
|
|
36
37
|
'aria-label': ariaLabel,
|
|
37
38
|
}: PriceRangeProps) => {
|
|
38
|
-
const [
|
|
39
|
-
const [maxVal, setMaxVal] = useState(max)
|
|
40
|
-
|
|
41
|
-
const handleChange: SliderProps['onChange'] = (values) => {
|
|
42
|
-
if (values.min !== minVal) {
|
|
43
|
-
setMinVal(values.min)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (values.max !== maxVal) {
|
|
47
|
-
setMaxVal(values.max)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
onChange?.(values)
|
|
51
|
-
}
|
|
39
|
+
const [edges, setEdges] = useState({ min: min.selected, max: max.selected })
|
|
52
40
|
|
|
53
41
|
return (
|
|
54
42
|
<div data-store-price-range data-testid={testId} className={className}>
|
|
55
43
|
<Slider
|
|
56
44
|
min={min}
|
|
57
45
|
max={max}
|
|
58
|
-
|
|
46
|
+
onEnd={onEnd}
|
|
47
|
+
onChange={(value) => {
|
|
48
|
+
setEdges(value)
|
|
49
|
+
onChange?.(value)
|
|
50
|
+
}}
|
|
59
51
|
aria-label={ariaLabel}
|
|
60
52
|
/>
|
|
61
53
|
<div data-price-range-values>
|
|
62
54
|
<Price
|
|
63
55
|
formatter={formatter}
|
|
64
56
|
data-price-range-value="min"
|
|
65
|
-
value={
|
|
57
|
+
value={edges.min}
|
|
66
58
|
variant={variant}
|
|
67
59
|
/>
|
|
68
60
|
<Price
|
|
69
61
|
formatter={formatter}
|
|
70
62
|
data-price-range-value="max"
|
|
71
|
-
value={
|
|
63
|
+
value={edges.max}
|
|
72
64
|
variant={variant}
|
|
73
65
|
/>
|
|
74
66
|
</div>
|