📣 Dash 2.15.0 Released - Slider Tooltip Styling, Setting Allowed Axes Range, More Page Routing Inputs

:rocket: We’re excited to announce that Dash 2.15.0 has been released.

pip install dash==2.15.0

Official Changelog :arrow_forward: Dash v2.15.0

Highlights :arrow_down_small:

Callback Context Triggered ID -

You can use dash_clientside.callback_context.triggered_id within a clientside callback to access the ID of the component that triggered the callback.

In this example, we display the triggered_id in the app when a button is clicked.

from dash import Dash, html, Input, Output

app = Dash(prevent_initial_callbacks=True)

app.layout = html.Div(
    [
        html.Button("Button 1", id="btn1"),
        html.Button("Button 2", id="btn2"),
        html.Button("Button 3", id="btn3"),
        html.Div(id="log"),
    ]
)

app.clientside_callback(
    """
    function(){
        console.log(dash_clientside.callback_context);
        const triggered_id = dash_clientside.callback_context.triggered_id;
        return "triggered id: " + triggered_id
    }
    """,
    Output("log", "children"),
    Input("btn1", "n_clicks"),
    Input("btn2", "n_clicks"),
    Input("btn3", "n_clicks"),
)

if __name__ == "__main__":
    app.run_server()

trigger id

RangeSlider Styling Tooltips -

You can customize the style of tooltips with the tooltip.style parameter. This accepts a dictionary of styles to apply. In this example, we set the text color and font size.

from dash import dcc

dcc.RangeSlider(0, 30,
    value=[5, 15],
    marks=None,
    tooltip={
        "placement": "bottom",
        "always_visible": True,
        "style": {"color": "LightSteelBlue", "fontSize": "20px"},
    },
),

RangeSlider Transforming Tooltip Values -

You can transform the value displayed on a tooltip using a JavaScript function by specifying the function name with the tooltip.transform parameter.

To make a custom function available in your app, add it to a file in your app’s assets folder. The function needs to be available in the window.dccFunctions namespace.

In this example, we have a function that converts temperatures in Fahrenheit to Celsius.
This function is saved in assets/tooltip.js:

window.dccFunctions = window.dccFunctions || {};
window.dccFunctions.temperatureInCelsius = function(value) {
     return ((value - 32) * 5/9).toFixed(2);
}

We then pass this function name to the tooltip.transform parameter:

from dash import dcc

dcc.RangeSlider(
    0,
    10,
    value=[3, 7.65],
    marks={0: "0°F", 3: "3°F", 5: "5°F", 7.65: "7.65°F", 10: "10°F"},
    tooltip={"always_visible": True, "transform": "temperatureInCelsius"},
)

RangeSlider Formatting Tooltips -

You can customize the text displayed on tooltips using the tooltip.template parameter. This accepts a string, which must contain {value}. The {value} will be replaced with the string representation of the tooltip value, or with the value returned by a transform function if one was specified using tooltip.transform.

from dash import dcc

dcc.RangeSlider(0, 30,
    value=[5, 15],
    marks=None,
    tooltip={
        "always_visible": True,
        "template": "$ {value}"
    },
),

Additional Inputs to Page Routing -

Using Dash Pages, by default, the routing to different pages is triggered by two Inputs: 1. The URL path name 2. The URL search params.

In some cases you may want to pass more information to the page layout functions, or trigger the layout rendering from other inputs. For instance you may want to:

  • Re-render the whole page content only when a user changes the language dropdown
  • Access additional information when the page renders, without re-rendering the page every time the value is updated (for example, serializing the app state in the URL hash)

You can do this with the new routing_callback_inputs parameter.
Visit our docs to read more about this new feature or watch the video tutorial on the the topic.

:pray: Thank you to @RenaudLN for contributing this feature.


Plotly.py & dcc.Graph Updates

Updated Plotly.js to version 2.27.0

The version of Plotly.js that is built in here is the same one that is bundled with the recently released Plotly.py 5.18.0, so we recommend that you upgrade to Plotly 5.18.0 to get the full benefit of all of these libraries working together. If you have already installed Dash v2.15.0, Plotly 5.18.0 automatically comes installed with it.

pip install plotly==5.18.0

Official Changelog :arrow_forward: Plotly v5.18.0

:arrow_right: dcc.Graph -

Exclude Inside Tick Labels from Range

You can use insiderange instead of range on an axis if you have tick labels positioned on the inside of another axis and you don’t want the range to overlap with those labels.

In this example, we have a y axis with ticklabelposition="inside" and by setting insiderange=['2018-10-01', '2019-01-01'] on the x axis, the data point of 2018-10-01 is displayed after the y axis labels.

from dash import Dash, html, dcc
import plotly.express as px
import pandas as pd

df = px.data.stocks(indexed=True)
fig = px.line(df, df.index, y="GOOG")
fig.update_yaxes(ticklabelposition="inside", title="Price")
fig.update_xaxes(insiderange=['2018-10-01', '2019-01-01'], title="Date")

app = Dash()
app.layout = html.Div([
    dcc.Graph(figure=fig)
])


if __name__ == "__main__":
    app.run()

Setting only a Lower or Upper Bound for Range -

You can also set just a lower or upper bound manually and have autorange applied to the other bound by setting it to None. In the following example, we set a an upper bound of 4.5 on the x axes, while specifying None for the lower bound, meaning it will use autorange. On the y axes, we set the lower bound, and use None for the upper bound, meaning that uses autorange.

from dash import Dash, html, dcc
import plotly.express as px
import pandas as pd

df = px.data.iris()
fig = px.scatter(df, x="sepal_width", y="sepal_length", facet_col="species")
fig.update_xaxes(range=[None, 4.5])
fig.update_yaxes(range=[3, None])

app = Dash()
app.layout = html.Div([
    dcc.Graph(figure=fig)
])


if __name__ == "__main__":
    app.run()

Setting a Maximum and Minimum Allowed Axis Value -

When setting a range manually, you can also set a maxallowed or minallowed for an axis. With this set, you won’t be able to pan further than the min or max allowed. In this example, we’ve set the minimum allowed on the x-axis to 1 and the maximum allowed on the y-axis to 10.

import plotly.express as px

df = px.data.iris()

fig = px.scatter(df, x="sepal_width", y="sepal_length")
fig.update_xaxes(range=[1.5, 4.5], minallowed=1)
fig.update_yaxes(range=[3, 9], maxallowed=10)

fig.show()

Specifying Minimum and Maximum Allowed Values -

Using autorangeoptions.maxallowed, you can specify an exact value to use as the autorange maximum. With autorangeoptions.minallowed, you can specify an exact value to use as the autorange minimum.

import plotly.express as px

df = px.data.iris()

fig = px.scatter(df, x="sepal_width", y="sepal_length")
fig.update_yaxes(autorangeoptions=dict(minallowed=3))
fig.update_xaxes(autorangeoptions=dict(maxallowed=5))

fig.show()


Notable Bug Fixes, Additions & Minor Changes -

Dash:

Added

  • #2695 Adds triggered_id to dash_clientside.callback_context. Fixes #2692. Thank you @annmarieW for the pull request.
  • #2723 Improve dcc Slider/RangeSlider tooltips. Fixes #1846
    • Add tooltip.template a string for the format template, {value} will be formatted with the actual value.
    • Add tooltip.style a style object to give to the div of the tooltip.
    • Add tooltip.transform a reference to a function in the window.dccFunctions namespace.
  • #2732 Add special key _dash_error to setProps, allowing component developers to send error without throwing in render. Usage props.setProps({_dash_error: new Error("custom error")})
  • #2647 routing_callback_inputs allowing to pass more Input and/or State arguments to the pages routing callback. Thank you @RenaudLN for the pull request.
  • #2649 Add _allow_dynamic_callbacks, register new callbacks inside other callbacks. WARNING: dynamic callback creation can be dangerous, use at you own risk. It is not intended for use in a production app, multi-user, or multiprocess. It only works for a single user.

Changed

  • #2652 dcc.Clipboard supports html_content and triggers a copy to clipboard when n_clicks are changed. Thank you @Lxstr for the contribution.
  • #2721 Remove ansi2html, fixes #2613
  • #2674 Raise flask & werkzeug limits to <3.1
  • #2635 Get proper app module name, remove need to give __name__ to Dash constructor.

Fixed

  • #2732 Sanitize html props that are vulnerable to xss vulnerability if user data is inserted. Fix Validate url to prevent XSS attacks #2729
  • #2700 Fix _allow_dynamic_callbacks for newly-added components.
  • #2672 Fix get_caller_name in case the source is not available.
  • #2634 Fix deprecation warning on pkg_resources, fix #2631

Plotly:

Added

  • Add insiderange to cartesian axes to help avoid overlap between visible grid lines and tick labels of the counter axis when they are positioned inside [#6735], this feature was anonymously sponsored: thank you to our sponsor!
  • Add “min”, “max”, “min reversed” and “max reversed” autorange options and handle partial ranges (i.e. one end being null), add autorangeoptions (clipmin, clipmax, minallowed, maxallowed, include) as well as minallowed and maxallowed to cartesian, gl3d and radial axes [#6547]
  • Add [n]-sigma (std deviations) box plots as an alternative to quartiles [#6697], with thanks to @28raining for the contribution!
  • Add “top left” & “top center” side options to legend title [#6711], with thanks to @28raining for the contribution!
  • Add “false” option to scaleanchor to allow removing a constraint that is set by default [#6712], with thanks to @lvlte for the contribution!

Fixed

  • Fix column order changes on hover [#6718], with thanks to @bhavinpatel1109 for the contribution!
  • Fix hover at timestamp ‘1970-01-01 00:00:00’ [#6752], with thanks to @adamjhawley for the contribution!
  • Fix clearing empty candlestick using react [#6757]
  • Repair crash on Matplotlib 3.8 related to get_offset_position [#4372], with thanks to @NeilGirdhar for the contribution!
  • Handle deprecation of pandas.Series.dt.to_pydatetime() calls and suppress the FutureWarning they currently emit. [#4379]. Thanks to @ned2 for the contribution.
  • Fixed two issues with px.imshow: [#4330] when facet_col is an earlier dimension than animation_frame for xarrays and [#4329] when facet_col has string coordinates in xarrays [#4331]. Thank you @Karl-Krauth for the pull request.

Previous Releases

:mega: Dash 2.13.0 Released - Coupling with Plotly.py, Shapes in Legends, and Broader DataFrame Support
:mega: Dash 2.11.0 Released - Dash in Jupyter, Locked Flask versions, and dcc.Graph Updates
:mega: Dash 2.9.2 Released - Partial Property Updates, Duplicate Outputs, dcc.Geolocation, Scatter Group Attributes
:mega: Dash 2.7 Released - Directional Arrows Feature, Map Bounds, and DataTable Filter Text
:mega: Dash 2.6 Released - Background Callbacks, Unit Testing, Persistent Selections, Dropdown Features

4 Likes

RangeSlider Formatting Tooltips
You can customize the text displayed on tooltips using the tooltip.template parameter. This accepts a string, which must contain {value}. The {value} will be replaced with the string representation of the tooltip value, or with the value returned by a transform function if one was specified using tooltip.transform.

Thank youuu, this has been a pain point for me for a long long time and I’m finally happy to see that I can add $ signs to the tooltips.

If I wanted to add thousands separators (like a comma: i.e 6,500) that would be accomplished via tooltip.transform correct?

1 Like

hi @the.oldest.house
I’m glad you finally have that capability. Yes, tooltip.transform should allow you to add a thousands separator.

Hello everyone,

I also really appreciate the work that went into customisation of RangeSlider Tooltip Values.

I have a question: Would it be possible to pass additional data to the function that does the Tooltip value transformation?

Something like this:

window.dccFunctions = window.dccFunctions || {};
window.dccFunctions.modifiedRangeSliderTooltipValue = function(value, to_display) {
    // Check if to_display is an object and contains the key corresponding to the value
    if (typeof to_display === 'object' && to_display !== null && to_display.hasOwnProperty(value)) {
        return to_display[value];
    }

    // Fallback: if the value is not found in to_display, return the value
    return value;
}

In my particular use-case, Slider values are indices of a list. What I would like to display with the Tooltip is the actual values from the list instead of the indices.