From fd30f336844dc3a7981c93cec41cb71d62cfcd7a Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Fri, 19 Sep 2025 15:10:33 -0600 Subject: [PATCH 1/7] Be more judicious about setting a default step for non-integer sliders --- .../dash-core-components/src/components/RangeSlider.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/dash-core-components/src/components/RangeSlider.tsx b/components/dash-core-components/src/components/RangeSlider.tsx index 35f0f00845..b1d8175bf8 100644 --- a/components/dash-core-components/src/components/RangeSlider.tsx +++ b/components/dash-core-components/src/components/RangeSlider.tsx @@ -18,9 +18,16 @@ export default function RangeSlider({ persistence_type = PersistenceTypes.local, // eslint-disable-next-line no-magic-numbers verticalHeight = 400, - step = 1, + step = undefined, ...props }: RangeSliderProps) { + // Some considerations for the default value of `step`: + // If the range consists of integers, default to a value of `1` + // Otherwise, leave it undefined + if (Number.isInteger(props.min) && Number.isInteger(props.max)) { + step = 1; + } + return ( Date: Fri, 19 Sep 2025 15:10:54 -0600 Subject: [PATCH 2/7] Visual tweaks for tooltips --- components/dash-core-components/src/components/css/sliders.css | 1 + 1 file changed, 1 insertion(+) diff --git a/components/dash-core-components/src/components/css/sliders.css b/components/dash-core-components/src/components/css/sliders.css index bacaf6fc89..94ab919f02 100644 --- a/components/dash-core-components/src/components/css/sliders.css +++ b/components/dash-core-components/src/components/css/sliders.css @@ -172,6 +172,7 @@ .dash-slider-wrapper { flex: 1; min-width: 0; + z-index: 1; } .dash-range-slider-inputs { From b7183a44ccf1f0d708a9ed5c99560937c64abfac Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Fri, 19 Sep 2025 15:19:12 -0600 Subject: [PATCH 3/7] update tab order of slider inputs --- .../src/components/css/sliders.css | 4 + .../src/fragments/RangeSlider.tsx | 142 +++++++++--------- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/components/dash-core-components/src/components/css/sliders.css b/components/dash-core-components/src/components/css/sliders.css index 94ab919f02..0ce56ade8e 100644 --- a/components/dash-core-components/src/components/css/sliders.css +++ b/components/dash-core-components/src/components/css/sliders.css @@ -186,6 +186,10 @@ min-width: 64px; } +.dash-range-slider-max-input { + order: 1; +} + .dash-range-slider-input { width: 64px; margin-top: 8px; diff --git a/components/dash-core-components/src/fragments/RangeSlider.tsx b/components/dash-core-components/src/fragments/RangeSlider.tsx index b99a97a8ee..0e8164630d 100644 --- a/components/dash-core-components/src/fragments/RangeSlider.tsx +++ b/components/dash-core-components/src/fragments/RangeSlider.tsx @@ -262,6 +262,77 @@ export default function RangeSlider(props: RangeSliderProps) { disabled={disabled} /> )} + {showInputs && !vertical && ( + { + const inputValue = e.target.value; + // Allow empty string (user is clearing the field) + if (inputValue === '') { + // Don't update props while user is typing, just update local state + const newValue = [...value]; + newValue[newValue.length - 1] = '' as any; + setValue(newValue); + } else { + const newMax = parseFloat(inputValue); + const constrainedMax = Math.max( + minMaxValues.min_mark, + Math.min(minMaxValues.max_mark, newMax) + ); + + if (newMax === constrainedMax) { + const newValue = [...value]; + newValue[newValue.length - 1] = newMax; + setProps({ + value: newValue, + drag_value: newValue, + }); + } + } + }} + onBlur={e => { + const inputValue = e.target.value; + let newMax: number; + + // If empty, default to current value or max_mark + if (inputValue === '') { + newMax = + value[value.length - 1] ?? + minMaxValues.max_mark; + } else { + newMax = parseFloat(inputValue); + newMax = isNaN(newMax) + ? minMaxValues.max_mark + : newMax; + } + + const constrainedMax = Math.min( + minMaxValues.max_mark, + Math.max( + value[0] ?? minMaxValues.min_mark, + newMax + ) + ); + const newValue = [...value]; + newValue[newValue.length - 1] = constrainedMax; + setValue(newValue); + if (updatemode === 'mouseup') { + setProps({value: newValue}); + } + }} + pattern="^\\d*\\.?\\d*$" + min={ + value.length === 1 + ? minMaxValues.min_mark + : value[0] + } + max={minMaxValues.max_mark} + step={step || undefined} + disabled={disabled} + /> + )}
e.preventDefault()} // prevent interactions from "clicking" the parent, particularly when slider is inside a label tag @@ -334,77 +405,6 @@ export default function RangeSlider(props: RangeSliderProps) { })}
- {showInputs && !vertical && ( - { - const inputValue = e.target.value; - // Allow empty string (user is clearing the field) - if (inputValue === '') { - // Don't update props while user is typing, just update local state - const newValue = [...value]; - newValue[newValue.length - 1] = '' as any; - setValue(newValue); - } else { - const newMax = parseFloat(inputValue); - const constrainedMax = Math.max( - minMaxValues.min_mark, - Math.min(minMaxValues.max_mark, newMax) - ); - - if (newMax === constrainedMax) { - const newValue = [...value]; - newValue[newValue.length - 1] = newMax; - setProps({ - value: newValue, - drag_value: newValue, - }); - } - } - }} - onBlur={e => { - const inputValue = e.target.value; - let newMax: number; - - // If empty, default to current value or max_mark - if (inputValue === '') { - newMax = - value[value.length - 1] ?? - minMaxValues.max_mark; - } else { - newMax = parseFloat(inputValue); - newMax = isNaN(newMax) - ? minMaxValues.max_mark - : newMax; - } - - const constrainedMax = Math.min( - minMaxValues.max_mark, - Math.max( - value[0] ?? minMaxValues.min_mark, - newMax - ) - ); - const newValue = [...value]; - newValue[newValue.length - 1] = constrainedMax; - setValue(newValue); - if (updatemode === 'mouseup') { - setProps({value: newValue}); - } - }} - pattern="^\\d*\\.?\\d*$" - min={ - value.length === 1 - ? minMaxValues.min_mark - : value[0] - } - max={minMaxValues.max_mark} - step={step || undefined} - disabled={disabled} - /> - )} )} From 1f96d459bee6f783b5e8fb9cc7b498355d31f7e2 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Fri, 19 Sep 2025 15:35:18 -0600 Subject: [PATCH 4/7] add missing vendor prefixes --- components/dash-core-components/src/components/css/sliders.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/dash-core-components/src/components/css/sliders.css b/components/dash-core-components/src/components/css/sliders.css index 0ce56ade8e..38bf152264 100644 --- a/components/dash-core-components/src/components/css/sliders.css +++ b/components/dash-core-components/src/components/css/sliders.css @@ -193,7 +193,9 @@ .dash-range-slider-input { width: 64px; margin-top: 8px; + -webkit-appearance: textfield; -moz-appearance: textfield; + appearance: textfield; } /* Hide the number input spinners */ From 2348ae06545cca46c3def1e19278c36798edfebe Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Mon, 22 Sep 2025 10:03:42 -0600 Subject: [PATCH 5/7] Move z-index ordering --- components/dash-core-components/src/components/css/sliders.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/dash-core-components/src/components/css/sliders.css b/components/dash-core-components/src/components/css/sliders.css index 38bf152264..0c570f7d4c 100644 --- a/components/dash-core-components/src/components/css/sliders.css +++ b/components/dash-core-components/src/components/css/sliders.css @@ -50,6 +50,8 @@ } .dash-slider-thumb { + position: relative; + z-index: 1; display: block; width: 16px; height: 16px; @@ -172,7 +174,6 @@ .dash-slider-wrapper { flex: 1; min-width: 0; - z-index: 1; } .dash-range-slider-inputs { From af6626621827bc16ffa5db270cc31b9dba1c57f0 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Mon, 22 Sep 2025 13:13:28 -0600 Subject: [PATCH 6/7] commit for ci From 4ed1f6580158abe517fd4befa8ae5fd33a824c3a Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Mon, 22 Sep 2025 13:46:10 -0600 Subject: [PATCH 7/7] Improve test reliability --- .../tests/integration/misc/test_dcc_components_as_props.py | 1 + 1 file changed, 1 insertion(+) diff --git a/components/dash-core-components/tests/integration/misc/test_dcc_components_as_props.py b/components/dash-core-components/tests/integration/misc/test_dcc_components_as_props.py index 6145477636..df9191ae03 100644 --- a/components/dash-core-components/tests/integration/misc/test_dcc_components_as_props.py +++ b/components/dash-core-components/tests/integration/misc/test_dcc_components_as_props.py @@ -54,6 +54,7 @@ def test_mdcap001_dcc_components_as_props(dash_dcc): search_input = dash_dcc.find_element("#dropdown .dash-dropdown-search") search_input.send_keys("4") + sleep(0.25) options = dash_dcc.find_elements("#dropdown .dash-dropdown-option") wait.until(lambda: len(options) == 1, 1)