Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
All notable changes to `dash` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## UNRELEASED

## Added
- Add a prop to sliders, `allow_direct_input`, that can be used to disable the inputs rendered with sliders.
- Improve CSS styles in calendar when looking at selected dates outside the current calendar month (`show_outside_days=True`)

## [4.0.0rc6] - 2026-01-07

## Added
Expand All @@ -13,7 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## [4.0.0rc5] - 2025-12-16

## Added
- New prop in `dcc.Upload` allows users to recursively upload entire folders at once
- [#3464](https://github.com/plotly/dash/issues/3464) Add folder upload functionality to `dcc.Upload` component. When `multiple=True`, users can now select and upload entire folders in addition to individual files. The folder hierarchy is preserved in filenames (e.g., `folder/subfolder/file.txt`). Files within folders are filtered according to the `accept` prop. Folder support is available in Chrome, Edge, and Opera; other browsers gracefully fall back to file-only mode. The uploaded files use the same output API as multiple file uploads.

## Changed
- Bugfixes for feedback received in `rc4`
Expand Down Expand Up @@ -63,7 +69,6 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## [3.3.0] - 2025-11-12

## Added
- [#3464](https://github.com/plotly/dash/issues/3464) Add folder upload functionality to `dcc.Upload` component. When `multiple=True`, users can now select and upload entire folders in addition to individual files. The folder hierarchy is preserved in filenames (e.g., `folder/subfolder/file.txt`). Files within folders are filtered according to the `accept` prop. Folder support is available in Chrome, Edge, and Opera; other browsers gracefully fall back to file-only mode. The uploaded files use the same output API as multiple file uploads.
- [#3395](https://github.com/plotly/dash/pull/3396) Add position argument to hooks.devtool
- [#3403](https://github.com/plotly/dash/pull/3403) Add app_context to get_app, allowing to get the current app in routes.
- [#3407](https://github.com/plotly/dash/pull/3407) Add `hidden` to callback arguments, hiding the callback from appearing in the devtool callback graph.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default function RangeSlider({
// eslint-disable-next-line no-magic-numbers
verticalHeight = 400,
step = undefined,
allow_direct_input = true,
...props
}: RangeSliderProps) {
// Some considerations for the default value of `step`:
Expand All @@ -38,6 +39,7 @@ export default function RangeSlider({
updatemode={updatemode}
verticalHeight={verticalHeight}
step={step}
allow_direct_input={allow_direct_input}
{...props}
/>
</Suspense>
Expand Down
2 changes: 2 additions & 0 deletions components/dash-core-components/src/components/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default function Slider({
// eslint-disable-next-line no-magic-numbers
verticalHeight = 400,
step = undefined,
allow_direct_input = true,
setProps,
value,
drag_value,
Expand Down Expand Up @@ -77,6 +78,7 @@ export default function Slider({
updatemode={updatemode}
verticalHeight={verticalHeight}
step={step}
allow_direct_input={allow_direct_input}
value={mappedValue}
drag_value={mappedDragValue}
setProps={mappedSetProps}
Expand Down
23 changes: 20 additions & 3 deletions components/dash-core-components/src/components/css/calendar.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,34 @@
position: relative;
}

.dash-datepicker-calendar td.dash-datepicker-calendar-date-highlighted {
/* Highlighted dates (i.e. dates within a selected range) get highlight colours */
.dash-datepicker-calendar
td.dash-datepicker-calendar-date-highlighted:not(
.dash-datepicker-calendar-date-outside
) {
background-color: var(--Dash-Fill-Interactive-Weak);
color: var(--Dash-Fill-Interactive-Strong);
}

.dash-datepicker-calendar td.dash-datepicker-calendar-date-selected {
/* Outside days get highlighted colours only on hover */
.dash-datepicker-calendar
td.dash-datepicker-calendar-date-highlighted.dash-datepicker-calendar-date-outside:hover {
background-color: var(--Dash-Fill-Interactive-Weak);
color: var(--Dash-Fill-Interactive-Strong);
}

/* Selected dates (start & end) get accented colours */
.dash-datepicker-calendar
td.dash-datepicker-calendar-date-selected:not(
.dash-datepicker-calendar-date-outside
) {
background-color: var(--Dash-Fill-Interactive-Strong);
color: var(--Dash-Fill-Inverse-Strong);
}

.dash-datepicker-calendar td.dash-datepicker-calendar-date-selected {
/* Outside days, when selected, get accented colours only when active (being clicked) */
.dash-datepicker-calendar
td.dash-datepicker-calendar-date-outside.dash-datepicker-calendar-date-selected:active {
background-color: var(--Dash-Fill-Interactive-Strong);
color: var(--Dash-Fill-Inverse-Strong);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default function RangeSlider(props: RangeSliderProps) {
pushable,
count,
reverse,
allow_direct_input = true,
} = props;

// For range slider, we expect an array of values
Expand Down Expand Up @@ -263,6 +264,7 @@ export default function RangeSlider(props: RangeSliderProps) {

// Determine if inputs should be rendered at all (CSS will handle responsive visibility)
const shouldShowInputs =
allow_direct_input !== false && // Not disabled by allow_direct_input
step !== null && // Not disabled by step=None
value.length <= 2 && // Only for single or range sliders
!vertical; // Only for horizontal sliders
Expand Down
12 changes: 12 additions & 0 deletions components/dash-core-components/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,12 @@ export interface SliderProps extends BaseDccProps<SliderProps> {
* The height, in px, of the slider if it is vertical.
*/
verticalHeight?: number;

/**
* If false, the input elements for directly entering values will be hidden.
* Only the slider will be visible and it will occupy 100% width of the container.
*/
allow_direct_input?: boolean;
}

export interface RangeSliderProps extends BaseDccProps<RangeSliderProps> {
Expand Down Expand Up @@ -604,6 +610,12 @@ export interface RangeSliderProps extends BaseDccProps<RangeSliderProps> {
* The height, in px, of the slider if it is vertical.
*/
verticalHeight?: number;

/**
* If false, the input elements for directly entering values will be hidden.
* Only the slider will be visible and it will occupy 100% width of the container.
*/
allow_direct_input?: boolean;
}

export type OptionValue = string | number | boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,83 @@ def update_output(value):
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"


def test_slsl019_allow_direct_input_false(dash_dcc):
"""Test that allow_direct_input=False hides input elements for both Slider and RangeSlider"""
app = Dash(__name__)
app.layout = html.Div(
[
html.Div(
[
html.Label("Slider with allow_direct_input=False"),
dcc.Slider(
id="slider-no-input",
min=0,
max=100,
step=5,
value=50,
allow_direct_input=False,
),
html.Div(id="slider-output"),
]
),
html.Div(
[
html.Label("RangeSlider with allow_direct_input=False"),
dcc.RangeSlider(
id="rangeslider-no-input",
min=0,
max=100,
step=5,
value=[25, 75],
allow_direct_input=False,
),
html.Div(id="rangeslider-output"),
]
),
]
)

@app.callback(
Output("slider-output", "children"), [Input("slider-no-input", "value")]
)
def update_slider(value):
return f"Slider: {value}"

@app.callback(
Output("rangeslider-output", "children"),
[Input("rangeslider-no-input", "value")],
)
def update_rangeslider(value):
return f"RangeSlider: {value[0]}-{value[1]}"

dash_dcc.start_server(app)
dash_dcc.wait_for_text_to_equal("#slider-output", "Slider: 50")
dash_dcc.wait_for_text_to_equal("#rangeslider-output", "RangeSlider: 25-75")

# Verify no input elements exist for slider with allow_direct_input=False
slider_inputs = dash_dcc.find_elements("#slider-no-input .dash-range-slider-input")
assert (
len(slider_inputs) == 0
), "Expected 0 inputs for slider with allow_direct_input=False"

# Verify no input elements exist for rangeslider with allow_direct_input=False
rangeslider_inputs = dash_dcc.find_elements(
"#rangeslider-no-input .dash-range-slider-input"
)
assert (
len(rangeslider_inputs) == 0
), "Expected 0 inputs for rangeslider with allow_direct_input=False"

# Verify sliders are still functional by clicking them
slider = dash_dcc.find_element("#slider-no-input")
dash_dcc.click_at_coord_fractions(slider, 0.75, 0.5)
dash_dcc.wait_for_text_to_equal("#slider-output", "Slider: 75")

rangeslider = dash_dcc.find_element("#rangeslider-no-input")
# Click closer to the left to move the lower handle
dash_dcc.click_at_coord_fractions(rangeslider, 0.1, 0.5)
dash_dcc.wait_for_text_to_equal("#rangeslider-output", "RangeSlider: 10-75")

assert dash_dcc.get_logs() == []