✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

Non-callback Tabs jumping back to default view on element hover

This is an issue that’s cropped up since dash-core-components 0.27.2 (i.e. the fix which removed the non-callback Tabs defaulting to tab 2 behaviour, amongst other things), and remains in 0.28.0. To explain what I mean, a screenshot and brief summary:


(please excuse the lazily blacked out sensitive details)

In this simple dashboard there are five charts (two within the tabs at top-right), the two left hand charts acting as ‘feeders’ for the others. Specifically:

  • The Team Summary chart is defined entirely within the app layout, and has a click action output which determines how to filter which team’s data to show in the Income, Arrears Actions and Top 20 Accounts in Arrears charts.
  • The Top 20 Accounts In Arrears chart is generated based on the Team Summary chart, and has a click action which selects which account to feature in the Account Details & 12 Months of Balances chart and summary data.

With previous versions of dash-core-components this dashboard behaved as expected (bar the tabs defaulting to tab 2, Arrears Actions, rather than tab 1). Since the update, however, while the tabs now default to the expected tab 1, they misbehave whenever the cursor passes over either of the two charts with callback outputs: specifically, whenever the Team Summary or Top 20 Accounts in Arrears charts generate a hover action, the tabs jump back to their default view of tab 1.

Potentially relevant notes:

  1. This behaviour only manifests if the tabs are generated in non-callback form.
  2. The tabs do not reset to default on a hover action of the 12 Months of Balances chart (despite it having a hover label active), hence my assumption that this is triggered specifically by charts with a callback output
  3. The output of the Top 20 Accounts in Arrears callback does not have any effect on either of the charts held within the tabs, suggesting that a relationship between the charts is not a factor.
  4. The charts which cause the reset to default do not have a callback output associated with their hover action, only with a click action, so although it seems that having a callback output is a necessary factor, the nature of said output doesn’t seem to be relevant

Obviously there are workarounds available here (either using callback tabs, or reverting to an earlier version of dash-core-components), but I thought it worth flagging up!

Would you mind creating a really simple example from scratch to reproduce the issue? That’d really help us narrow things down

Hi Chris,

Thanks for the quick reply! In trying to replicate the error with a simple example I’ve only been able to create a rather different set of errors, which suggests that either something is horribly wrong with my code (entirely plausible), or that there’s something odd going on with my environment.

The demo script I’ve come up with (intentionally avoiding custom CSS and thus leading to a particularly charmless output) is this:

import dash
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

some_ints = [1,5,4,8,1]
some_strs = ['a','b','c','d','e']

app.layout = html.Div([
    dcc.Graph(
        id='example-graph',
        figure={
            'data': [
                {'x': some_strs, 'y': some_ints,
                 'type': 'bar'}
                ]}),
    dcc.Dropdown(id='str_selecter',
                            options=[{'label': i, 'value': i}
                                for i in some_strs]),    
    dcc.Graph(
        id='second-graph',
        figure={
            'data': [
                {'x': [1, 2, 3], 'y': [4, 1, 2],
                 'type': 'bar', 'name': 'SF'}
                ]}),
    dcc.Tabs(id="tabs", children=[
        dcc.Tab(label='Tab one', children=[
            html.Div([
                html.Div(dcc.Graph(
                    id='tab-graph'
                    ),
                 className = "six columns")
            ])
        ]),
        dcc.Tab(label='Tab two', children=[
            html.Div([
                html.H1("This is the content in tab 2"),
            ])
        ]),
    ])
])

@app.callback(
        dash.dependencies.Output('tab-graph','figure'),
        [dash.dependencies.Input('example-graph','clickData')])

def select_str(selected):
    if selected is None:
        return{
            'data': [
                {'x': ['A','B','C'], 'y': [4, 1, 2],
                 'type': 'bar', 'name': 'SF'}]
            }
    else:
        return{
            'data': [
                {'x': ['A','B','C'], 'y': [2, 1, 4],
                 'type': 'bar', 'name': 'SF'}]
            }

The above produces a purely vertical layout featuring, in order:

  • A chart which is the source for a clickData output
  • A ‘dumb’ dropdown with no callback functionality
  • A ‘dumb’ chart with no callback functionality
  • Tabs featuring a chart which takes clickData input from the first chart

In this snippet, the tabs behave as they ought - defaulting to tab 1, and remaining on the user-selected tab until the users chooses otherwise. However, the snippet displays three different sets of undesirable behaviour:

  1. Closest to my originally reported issue, if a selection is made in the dropdown, it is statically held until the moment the user hovers on the first chart (i.e. the only chart with an output), at which point its held value is cleared. Any other chart hover has no effect.
  2. The third chart (i.e. the only chart which takes an input, and the only chart in a tab) starts out as an unpopulated chart (despite the fact that its input should trigger a functional output even in a None state). This can only be rectified by switching to the second tab, then back.
  3. The click event never appears to fire. Clicking on the first chart has no noticeable effect on the final chart (I’ve tested this a little further than the above code, and found that even implementing a callback outputting a pure text output of the first chart’s clickData into an html.P container returned nothing)

I’m particularly surprised at the utter lack of updates to clickData that I’m seeing, as that’s an issue I’ve not had before with any dashboard, but I can’t see an obvious cause. Which likely means that there’s an extremely obvious error in my code, as that seems to be how things tend to go!

For reference, my current dash module versions:

  • dash==0.26.3
  • dash-core-components==0.28.1
  • dash-html-components==0.11.0
  • dash-renderer==0.13.2