fix(slider): calculate the correct value on mark click (#3017)

* fix(slider): calculate the correct value on mark click

* refactor(slider): remove the tests inside describe block

* feat(slider): add tests for thumb move on mark click

* refactor(slider): use val instead of pos
This commit is contained in:
աӄա 2024-05-19 21:12:57 +08:00 committed by GitHub
parent 9d63259eea
commit 5329de42d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 186 additions and 74 deletions

View File

@ -0,0 +1,5 @@
---
"@nextui-org/slider": patch
---
calculate the correct value on mark click (#2980)

View File

@ -213,78 +213,161 @@ describe("Slider", () => {
expect(setValues).toStrictEqual([[15, 25]]);
});
});
it("should supports hideThumb", async function () {
const {container} = render(<Slider hideThumb defaultValue={20} label="The Label" />);
const track = container.querySelector("[data-slot='track']");
expect(track).toHaveAttribute("data-thumb-hidden", "true");
});
it("should supports marks", async function () {
const {container} = render(
<Slider
hideThumb
defaultValue={20}
label="The Label"
marks={[
{
value: 0.2,
label: "20%",
},
{
value: 0.5,
label: "50%",
},
{
value: 0.8,
label: "80%",
},
]}
maxValue={1}
minValue={0}
step={0.1}
/>,
);
const marks = container.querySelectorAll("[data-slot='mark']");
expect(marks).toHaveLength(3);
});
it("should supports marks with hideThumb", async function () {
const {container} = render(
<Slider
hideThumb
defaultValue={20}
label="The Label"
marks={[
{
value: 0.2,
label: "20%",
},
{
value: 0.5,
label: "50%",
},
{
value: 0.8,
label: "80%",
},
]}
maxValue={1}
minValue={0}
step={0.1}
/>,
);
const track = container.querySelector("[data-slot='track']");
expect(track).toHaveAttribute("data-thumb-hidden", "true");
const marks = container.querySelectorAll("[data-slot='mark']");
expect(marks).toHaveLength(3);
it("should supports hideThumb", async function () {
const {container} = render(<Slider hideThumb defaultValue={20} label="The Label" />);
const track = container.querySelector("[data-slot='track']");
expect(track).toHaveAttribute("data-thumb-hidden", "true");
});
it("should supports marks", async function () {
const {container} = render(
<Slider
hideThumb
defaultValue={20}
label="The Label"
marks={[
{
value: 0.2,
label: "20%",
},
{
value: 0.5,
label: "50%",
},
{
value: 0.8,
label: "80%",
},
]}
maxValue={1}
minValue={0}
step={0.1}
/>,
);
const marks = container.querySelectorAll("[data-slot='mark']");
expect(marks).toHaveLength(3);
});
it("should supports marks with hideThumb", async function () {
const {container} = render(
<Slider
hideThumb
defaultValue={20}
label="The Label"
marks={[
{
value: 0.2,
label: "20%",
},
{
value: 0.5,
label: "50%",
},
{
value: 0.8,
label: "80%",
},
]}
maxValue={1}
minValue={0}
step={0.1}
/>,
);
const track = container.querySelector("[data-slot='track']");
expect(track).toHaveAttribute("data-thumb-hidden", "true");
const marks = container.querySelectorAll("[data-slot='mark']");
expect(marks).toHaveLength(3);
});
it("should move thumb after clicking mark (single thumb)", async function () {
const {getByRole, container} = render(
<Slider
hideThumb
defaultValue={0.2}
label="The Label"
marks={[
{
value: 0.2,
label: "20%",
},
{
value: 0.5,
label: "50%",
},
{
value: 0.8,
label: "80%",
},
]}
maxValue={1}
minValue={0}
step={0.1}
/>,
);
const marks = container.querySelectorAll("[data-slot='mark']");
expect(marks).toHaveLength(3);
await act(async () => {
await userEvent.click(marks[1]);
});
const slider = getByRole("slider");
expect(slider).toHaveProperty("value", "0.5");
expect(slider).toHaveAttribute("aria-valuetext", "0.5");
});
it("should move thumb after clicking mark (left and right thumbs)", async function () {
const {getAllByRole, container} = render(
<Slider
hideThumb
defaultValue={[0.2, 0.8]}
label="The Label"
marks={[
{
value: 0.2,
label: "20%",
},
{
value: 0.5,
label: "50%",
},
{
value: 0.8,
label: "80%",
},
]}
maxValue={1}
minValue={0}
step={0.1}
/>,
);
const marks = container.querySelectorAll("[data-slot='mark']");
expect(marks).toHaveLength(3);
await act(async () => {
await userEvent.click(marks[1]);
});
const [leftSlider, rightSlider] = getAllByRole("slider");
expect(leftSlider).toHaveProperty("value", "0.5");
expect(leftSlider).toHaveAttribute("aria-valuetext", "0.5");
expect(rightSlider).toHaveProperty("value", "0.8");
expect(rightSlider).toHaveAttribute("aria-valuetext", "0.8");
});
});

View File

@ -389,6 +389,30 @@ export function useSlider(originalProps: UseSliderProps) {
style: {
[isVertical ? "bottom" : direction === "rtl" ? "right" : "left"]: `${percent * 100}%`,
},
// avoid `onDownTrack` is being called since when you click the mark,
// `onDownTrack` will calculate the percent based on the position you click
// the calculated value will be set instead of the actual value defined in `marks`
onMouseDown: (e: React.MouseEvent) => e.stopPropagation(),
onPointerDown: (e: React.PointerEvent) => e.stopPropagation(),
onClick: (e: any) => {
e.stopPropagation();
if (state.values.length === 1) {
state.setThumbPercent(0, percent);
} else {
const leftThumbVal = state.values[0];
const rightThumbVal = state.values[1];
if (mark.value < leftThumbVal) {
state.setThumbPercent(0, percent);
} else if (mark.value > rightThumbVal) {
state.setThumbPercent(1, percent);
} else if (Math.abs(mark.value - leftThumbVal) < Math.abs(mark.value - rightThumbVal)) {
state.setThumbPercent(0, percent);
} else {
state.setThumbPercent(1, percent);
}
}
},
};
};