Skip to content

Commit 5329de4

Browse files
authored
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
1 parent 9d63259 commit 5329de4

File tree

3 files changed

+175
-63
lines changed

3 files changed

+175
-63
lines changed

.changeset/olive-kids-hide.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@nextui-org/slider": patch
3+
---
4+
5+
calculate the correct value on mark click (#2980)

packages/components/slider/__tests__/slider.test.tsx

+146-63
Original file line numberDiff line numberDiff line change
@@ -213,78 +213,161 @@ describe("Slider", () => {
213213

214214
expect(setValues).toStrictEqual([[15, 25]]);
215215
});
216-
});
217216

218-
it("should supports hideThumb", async function () {
219-
const {container} = render(<Slider hideThumb defaultValue={20} label="The Label" />);
217+
it("should supports hideThumb", async function () {
218+
const {container} = render(<Slider hideThumb defaultValue={20} label="The Label" />);
220219

221-
const track = container.querySelector("[data-slot='track']");
220+
const track = container.querySelector("[data-slot='track']");
222221

223-
expect(track).toHaveAttribute("data-thumb-hidden", "true");
224-
});
222+
expect(track).toHaveAttribute("data-thumb-hidden", "true");
223+
});
225224

226-
it("should supports marks", async function () {
227-
const {container} = render(
228-
<Slider
229-
hideThumb
230-
defaultValue={20}
231-
label="The Label"
232-
marks={[
233-
{
234-
value: 0.2,
235-
label: "20%",
236-
},
237-
{
238-
value: 0.5,
239-
label: "50%",
240-
},
241-
{
242-
value: 0.8,
243-
label: "80%",
244-
},
245-
]}
246-
maxValue={1}
247-
minValue={0}
248-
step={0.1}
249-
/>,
250-
);
225+
it("should supports marks", async function () {
226+
const {container} = render(
227+
<Slider
228+
hideThumb
229+
defaultValue={20}
230+
label="The Label"
231+
marks={[
232+
{
233+
value: 0.2,
234+
label: "20%",
235+
},
236+
{
237+
value: 0.5,
238+
label: "50%",
239+
},
240+
{
241+
value: 0.8,
242+
label: "80%",
243+
},
244+
]}
245+
maxValue={1}
246+
minValue={0}
247+
step={0.1}
248+
/>,
249+
);
251250

252-
const marks = container.querySelectorAll("[data-slot='mark']");
251+
const marks = container.querySelectorAll("[data-slot='mark']");
253252

254-
expect(marks).toHaveLength(3);
255-
});
253+
expect(marks).toHaveLength(3);
254+
});
256255

257-
it("should supports marks with hideThumb", async function () {
258-
const {container} = render(
259-
<Slider
260-
hideThumb
261-
defaultValue={20}
262-
label="The Label"
263-
marks={[
264-
{
265-
value: 0.2,
266-
label: "20%",
267-
},
268-
{
269-
value: 0.5,
270-
label: "50%",
271-
},
272-
{
273-
value: 0.8,
274-
label: "80%",
275-
},
276-
]}
277-
maxValue={1}
278-
minValue={0}
279-
step={0.1}
280-
/>,
281-
);
256+
it("should supports marks with hideThumb", async function () {
257+
const {container} = render(
258+
<Slider
259+
hideThumb
260+
defaultValue={20}
261+
label="The Label"
262+
marks={[
263+
{
264+
value: 0.2,
265+
label: "20%",
266+
},
267+
{
268+
value: 0.5,
269+
label: "50%",
270+
},
271+
{
272+
value: 0.8,
273+
label: "80%",
274+
},
275+
]}
276+
maxValue={1}
277+
minValue={0}
278+
step={0.1}
279+
/>,
280+
);
281+
282+
const track = container.querySelector("[data-slot='track']");
283+
284+
expect(track).toHaveAttribute("data-thumb-hidden", "true");
282285

283-
const track = container.querySelector("[data-slot='track']");
286+
const marks = container.querySelectorAll("[data-slot='mark']");
287+
288+
expect(marks).toHaveLength(3);
289+
});
290+
291+
it("should move thumb after clicking mark (single thumb)", async function () {
292+
const {getByRole, container} = render(
293+
<Slider
294+
hideThumb
295+
defaultValue={0.2}
296+
label="The Label"
297+
marks={[
298+
{
299+
value: 0.2,
300+
label: "20%",
301+
},
302+
{
303+
value: 0.5,
304+
label: "50%",
305+
},
306+
{
307+
value: 0.8,
308+
label: "80%",
309+
},
310+
]}
311+
maxValue={1}
312+
minValue={0}
313+
step={0.1}
314+
/>,
315+
);
284316

285-
expect(track).toHaveAttribute("data-thumb-hidden", "true");
317+
const marks = container.querySelectorAll("[data-slot='mark']");
286318

287-
const marks = container.querySelectorAll("[data-slot='mark']");
319+
expect(marks).toHaveLength(3);
288320

289-
expect(marks).toHaveLength(3);
321+
await act(async () => {
322+
await userEvent.click(marks[1]);
323+
});
324+
325+
const slider = getByRole("slider");
326+
327+
expect(slider).toHaveProperty("value", "0.5");
328+
expect(slider).toHaveAttribute("aria-valuetext", "0.5");
329+
});
330+
331+
it("should move thumb after clicking mark (left and right thumbs)", async function () {
332+
const {getAllByRole, container} = render(
333+
<Slider
334+
hideThumb
335+
defaultValue={[0.2, 0.8]}
336+
label="The Label"
337+
marks={[
338+
{
339+
value: 0.2,
340+
label: "20%",
341+
},
342+
{
343+
value: 0.5,
344+
label: "50%",
345+
},
346+
{
347+
value: 0.8,
348+
label: "80%",
349+
},
350+
]}
351+
maxValue={1}
352+
minValue={0}
353+
step={0.1}
354+
/>,
355+
);
356+
357+
const marks = container.querySelectorAll("[data-slot='mark']");
358+
359+
expect(marks).toHaveLength(3);
360+
361+
await act(async () => {
362+
await userEvent.click(marks[1]);
363+
});
364+
365+
const [leftSlider, rightSlider] = getAllByRole("slider");
366+
367+
expect(leftSlider).toHaveProperty("value", "0.5");
368+
expect(leftSlider).toHaveAttribute("aria-valuetext", "0.5");
369+
370+
expect(rightSlider).toHaveProperty("value", "0.8");
371+
expect(rightSlider).toHaveAttribute("aria-valuetext", "0.8");
372+
});
290373
});

packages/components/slider/src/use-slider.ts

+24
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,30 @@ export function useSlider(originalProps: UseSliderProps) {
389389
style: {
390390
[isVertical ? "bottom" : direction === "rtl" ? "right" : "left"]: `${percent * 100}%`,
391391
},
392+
// avoid `onDownTrack` is being called since when you click the mark,
393+
// `onDownTrack` will calculate the percent based on the position you click
394+
// the calculated value will be set instead of the actual value defined in `marks`
395+
onMouseDown: (e: React.MouseEvent) => e.stopPropagation(),
396+
onPointerDown: (e: React.PointerEvent) => e.stopPropagation(),
397+
onClick: (e: any) => {
398+
e.stopPropagation();
399+
if (state.values.length === 1) {
400+
state.setThumbPercent(0, percent);
401+
} else {
402+
const leftThumbVal = state.values[0];
403+
const rightThumbVal = state.values[1];
404+
405+
if (mark.value < leftThumbVal) {
406+
state.setThumbPercent(0, percent);
407+
} else if (mark.value > rightThumbVal) {
408+
state.setThumbPercent(1, percent);
409+
} else if (Math.abs(mark.value - leftThumbVal) < Math.abs(mark.value - rightThumbVal)) {
410+
state.setThumbPercent(0, percent);
411+
} else {
412+
state.setThumbPercent(1, percent);
413+
}
414+
}
415+
},
392416
};
393417
};
394418

0 commit comments

Comments
 (0)