Skip to content

Commit 09e5504

Browse files
committed
fix bug and add test for slider steps
1 parent 31807a1 commit 09e5504

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

components/dash-core-components/src/components/RangeSlider.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ export default function RangeSlider({
2424
// Some considerations for the default value of `step`:
2525
// If the range consists of integers, default to a value of `1`
2626
// Otherwise, leave it undefined
27-
if (Number.isInteger(props.min) && Number.isInteger(props.max)) {
27+
if (
28+
typeof step === 'undefined' &&
29+
Number.isInteger(props.min) &&
30+
Number.isInteger(props.max)
31+
) {
2832
step = 1;
2933
}
3034

components/dash-core-components/src/components/Slider.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default function Slider({
2222
persistence_type = PersistenceTypes.local,
2323
// eslint-disable-next-line no-magic-numbers
2424
verticalHeight = 400,
25-
step = 1,
25+
step = undefined,
2626
setProps,
2727
value,
2828
drag_value,
@@ -31,6 +31,17 @@ export default function Slider({
3131
// This is actually a wrapper around a RangeSlider.
3232
// We'll modify key `Slider` props to be compatible with a Range Slider.
3333

34+
// Some considerations for the default value of `step`:
35+
// If the range consists of integers, default to a value of `1`
36+
// Otherwise, leave it undefined
37+
if (
38+
typeof step === 'undefined' &&
39+
Number.isInteger(props.min) &&
40+
Number.isInteger(props.max)
41+
) {
42+
step = 1;
43+
}
44+
3445
const mappedValue: RangeSliderProps['value'] = useMemo(() => {
3546
return typeof value === 'number' ? [value] : value;
3647
}, [value]);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import pytest
2+
from dash import Dash, Input, Output, dcc, html
3+
from humanfriendly import parse_size
4+
5+
test_cases = [
6+
{"step": 2, "min": 0, "max": 10, "value": 6},
7+
{"step": 3, "min": 0, "max": 100, "value": 33},
8+
{"step": 0.05, "min": 0, "max": 1, "value": 0.5},
9+
{"step": 1_000_000, "min": 1e9, "max": 1e10, "value": 1e10},
10+
]
11+
12+
13+
def slider_value_divisible_by_step(slider_args, slider_value) -> bool:
14+
if type(slider_value) is str:
15+
slider_value = float(slider_value.split()[-1])
16+
17+
if slider_value == slider_args["min"] or slider_value == slider_args["max"]:
18+
return True
19+
20+
step = slider_args["step"]
21+
remainder = slider_value % step
22+
23+
# For float equality, we check if the remainder is close to 0 or close to step
24+
return remainder < 1e-10 or abs(remainder - step) < 1e-10
25+
26+
27+
@pytest.mark.parametrize("test_case", test_cases)
28+
def test_slst001_step_params(dash_dcc, test_case):
29+
30+
app = Dash(__name__)
31+
app.layout = html.Div(
32+
[
33+
dcc.Slider(id="slider", **test_case),
34+
html.Div(id="out"),
35+
]
36+
)
37+
38+
@app.callback(Output("out", "children"), [Input("slider", "value")])
39+
def update_output(value):
40+
return f"{value}"
41+
42+
dash_dcc.start_server(app)
43+
dash_dcc.driver.set_window_size(800, 600)
44+
45+
slider = dash_dcc.find_element("#slider")
46+
marks = dash_dcc.find_elements(".dash-slider-mark")
47+
48+
# Expect to find some amount of marks in between the first and last mark
49+
assert len(marks) > 2
50+
51+
# Every mark must be divisible by the given `step`.
52+
for mark in marks:
53+
value = parse_size(mark.text)
54+
assert slider_value_divisible_by_step(test_case, value)
55+
56+
# Perform multiple clicks along the slider track. After every click, the
57+
# resulting slider value must be divisible by the step
58+
i = 0
59+
while i < 1:
60+
dash_dcc.click_at_coord_fractions(slider, i, 0.25)
61+
value = dash_dcc.find_element("#out").text
62+
assert slider_value_divisible_by_step(test_case, value)
63+
i += 0.05
64+
65+
assert dash_dcc.get_logs() == []

0 commit comments

Comments
 (0)