Black Lives Matter. Please consider donating to Black Girls Code today.

Tooltips don't change if you use custom pagination

Here’s the problem. I set up a bunch of tooltips for specific cells. They display perfectly. But when I go to the next page, the tooltips are still displaying on the same cells, they should be display the tooltip appropriate for the ID of the row, but instead they are displaying the tooltip for the index of the row of the table. If I had to guess, I’d say that there’s some offset that should be automatically applied when the new data is loaded, and it’s not being used to index the tooltips.

Let’s say I create a DataTable and use the following attributes.

            page_current=0,
            data = df.to_dict('records'),
            page_size=50,
            page_action='custom',
            tooltip_data=[
                {'src_series_title': {'duration': None, 'type': 'markdown', 'value': '**TEST**: foo1'}},
                {'src_series_title': {'duration': None, 'type': 'markdown', 'value': '**TEST**: foo2'}},
                {'src_series_title': {'duration': None, 'type': 'markdown', 'value': '**TEST**: foo3'}},
                {'src_series_title': {'duration': None, 'type': 'markdown', 'value': '**TEST**: foo4'}},
                {'src_series_title': {'duration': None, 'type': 'markdown', 'value': '**TEST**: foo5'}},
            ],

And here’s my update callback (please ignore the temporary use of a global variable, I’ll be stashing it properly later).

@app.callback(
    [
        Output('datatable-paging', 'data'),
        Output('datatable-paging', 'page_count'),
    ],
    [Input('datatable-paging', "page_current"),
     Input('datatable-paging', "page_size"),
     Input('datatable-paging', "filter_query"),
     Input('datatable-paging', "sort_by"),
     Input('errorlevel', 'value')],
)
def update_table(page_current, page_size, filter, sort_by, errorlevel):
    dff = app.df

    print("update_table", type(filter), filter)
    print("update_table", type(errorlevel), errorlevel)
    if errorlevel is not None:
        if filter is None:
            filter = errorlevel
        else:
            filter = filter + ' && ' + errorlevel

    if filter is not None and len(filter) > 0:
        filtering_expressions = filter.split(' && ')
        for filter_part in filtering_expressions:
            col_name, operator, filter_value = split_filter_part(filter_part)

            if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
                # these operators match pandas series operator method names
                dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
            elif operator == 'contains':
                dff = dff.loc[dff[col_name].str.contains(filter_value)]
            elif operator == 'datestartswith':
                # this is a simplification of the front-end filtering logic,
                # only works with complete fields in standard format
                dff = dff.loc[dff[col_name].str.startswith(filter_value)]

    if sort_by is not None and len(sort_by) > 0:
        dff = dff.sort_values(
            sort_by[0]['column_id'],
            ascending=sort_by[0]['direction'] == 'asc',
            inplace=False
        )

    size = dff.size / len(dff.columns)

    dff = dff.iloc[page_current*page_size:(page_current+1)*page_size]
    print(dff.head())
    return dff.to_dict('records'), int(size / page_size)+1

Let me know if you want a small standalone example. But in my code, if I do the following, the first few rows of the table always display the same tooltips.

I’d greatly appreciate a suggestion for a patch or workaround.

Thanks!

P.S. For what it’s worth, I played around with setting trying to set the derived_viewport_indices or row_ids in the Output. (Oddly, they always return the first page data if I ask for them via State, which seems wrong.) But that didn’t change anything.

i haven’t tried this, but i believe that if you are updating the underlying data of the table then you also need to update the tooltips in your callback as well.

You’re absolutely right.

tooltips = [orig_tooltips[i] for i in dff['id']]

in the callback after filtering and sorting does the trick. Just write it to the tooltip_data output.

I still think it could have been made to work, but on the other hand, if my table is too big to send up all at once, I probably shouldn’t be sending the tooltips up either.

The whole tooltip thing needs better docs. It took me forever to find out the data format (the docs still say it’s a dictionary, but it’s an array, which doesn’t help), and there are lots of people asking. If I get to a break I’ll try to blog some good examples.

Thanks for the feedback! Agreed, the docs need improvement. Perhaps even a dedicated chapter in https://dash.plot.ly/datatable

Some examples would be great and so would a pull request or an issue in the documentation repo (it is a Dash app itself): https://github.com/plotly/dash-docs

Ah, great. I’m happy to do that for the docs. I was wondering if that was an option. I’ll take a look.
And thanks so much for the help, that worked perfectly.