diff --git a/components/dash-core-components/src/fragments/Slider.react.js b/components/dash-core-components/src/fragments/Slider.react.js index eeebde563d..f2e25d77dc 100644 --- a/components/dash-core-components/src/fragments/Slider.react.js +++ b/components/dash-core-components/src/fragments/Slider.react.js @@ -17,6 +17,8 @@ import { } from '../utils/formatSliderTooltip'; import LoadingElement from '../utils/LoadingElement'; +const MAX_MARKS = 500; + const sliderProps = [ 'min', 'max', @@ -76,6 +78,21 @@ export default class Slider extends Component { } = this.props; const value = this.state.value; + // Check if marks exceed 500 limit for performance + let processedMarks = marks; + if (marks && typeof marks === 'object' && marks !== null) { + const marksCount = Object.keys(marks).length; + if (marksCount > MAX_MARKS) { + /* eslint-disable no-console */ + console.error( + `dcc.Slider: Too many marks (${marksCount}) provided. ` + + `For performance reasons, marks are limited to 500. ` + + `Using auto-generated marks instead.` + ); + processedMarks = undefined; + } + } + let tipProps, tipFormatter; if (tooltip) { /** @@ -136,11 +153,16 @@ export default class Slider extends Component { tipFormatter={tipFormatter} style={{position: 'relative'}} value={value} - marks={sanitizeMarks({min, max, marks, step})} - max={setUndefined(min, max, marks).max_mark} - min={setUndefined(min, max, marks).min_mark} + marks={sanitizeMarks({ + min, + max, + marks: processedMarks, + step, + })} + max={setUndefined(min, max, processedMarks).max_mark} + min={setUndefined(min, max, processedMarks).min_mark} step={ - step === null && !isNil(marks) + step === null && !isNil(processedMarks) ? null : calcStep(min, max, step) } diff --git a/components/dash-core-components/tests/integration/sliders/test_sliders.py b/components/dash-core-components/tests/integration/sliders/test_sliders.py index 08e303da37..527a855e5e 100644 --- a/components/dash-core-components/tests/integration/sliders/test_sliders.py +++ b/components/dash-core-components/tests/integration/sliders/test_sliders.py @@ -616,3 +616,50 @@ def test_sls016_sliders_format_tooltips(dash_dcc): dash_dcc.percy_snapshot("sliders-format-tooltips") assert dash_dcc.get_logs() == [] + + +def test_slsl017_marks_limit_500(dash_dcc): + """Test that slider works with exactly 500 marks""" + app = Dash(__name__) + marks_500 = {str(i): f"Mark {i}" for i in range(500)} + app.layout = html.Div( + [ + dcc.Slider(id="slider", min=0, max=499, marks=marks_500, value=250), + html.Div(id="output"), + ] + ) + + @app.callback(Output("output", "children"), [Input("slider", "value")]) + def update_output(value): + return f"Selected: {value}" + + dash_dcc.start_server(app) + dash_dcc.wait_for_text_to_equal("#output", "Selected: 250") + + # No warnings should be logged for 500 marks + assert dash_dcc.get_logs() == [] + + +def test_slsl018_marks_limit_exceeded(dash_dcc): + """Test behavior when marks exceed 500 limit""" + app = Dash(__name__) + marks_501 = {str(i): f"Mark {i}" for i in range(501)} + app.layout = html.Div( + [ + dcc.Slider(id="slider", min=0, max=500, marks=marks_501, value=250), + html.Div(id="output"), + ] + ) + + @app.callback(Output("output", "children"), [Input("slider", "value")]) + def update_output(value): + return f"Selected: {value}" + + dash_dcc.start_server(app) + dash_dcc.wait_for_text_to_equal("#output", "Selected: 250") + + # Check that warning is logged + logs = dash_dcc.get_logs() + assert len(logs) > 0 + warning_found = any("Too many marks" in log["message"] for log in logs) + assert warning_found, "Expected warning about too many marks not found in logs"