Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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() == []