Create a Modal When Clicking on Barchart with Dash Plotly

I have a bar chart and I would like to create a Modal when I click on them. Do you know how I can handle that? I tried something but nothing appears.

I knew how to do it with a datatable. But here I have a bar chart, I added a Modal object at the end of the design but I can't make it appear despite.

Here is a minimal reproducible example:

import dash
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
from dash.dependencies import Input, Output

df = pd.read_excel("")
pv = pd.pivot_table(df, index=['Name'], columns=["Status"], values=['Quantity'], aggfunc=sum, fill_value=0)

trace1 = go.Bar(x=pv.index, y=pv[('Quantity', 'declined')], name='Declined')
trace2 = go.Bar(x=pv.index, y=pv[('Quantity', 'pending')], name='Pending')
trace3 = go.Bar(x=pv.index, y=pv[('Quantity', 'presented')], name='Presented')
trace4 = go.Bar(x=pv.index, y=pv[('Quantity', 'won')], name='Won')

app = dash.Dash()

app.layout = html.Div(children=[
    html.H1(children='Sales Funnel Report'),
    html.Div(children='''National Sales Funnel Report.'''),
    dcc.Graph(
        id='graph',
        figure={
            'data': [trace1, trace2, trace3, trace4],
            'layout':
                go.Layout(title='Order Status by Customer', barmode='stack')
        }),
    dbc.Modal(
        [
            dbc.ModalHeader("Header"),
            dbc.ModalBody("This is the content of the modal"),
            dbc.ModalFooter(
                dbc.Button("Close", id="close", className="ml-auto")
            ),
        ],
        size="xl",
        id="modal",
    )
])


@app.callback(
    Output('modal', 'children'),
    [Input('graph', 'clickData')])
def display_click_data(clickData):
    print("clickData: ", clickData)
    return [
        dbc.ModalHeader("Test"),
        dbc.ModalBody(
            html.Div([
                html.Div([
                    html.H6('Sales', style={'textAlign': 'center', 'padding': 10}),
                    html.P("Bitch", id="sales_stocks", style={'textAlign': 'center', 'padding': 10})
                ], className='pretty_container four columns'),
                html.Div([
                    html.H5('Current ratio', style={'textAlign': 'center', 'padding': 10})
                ], className='pretty_container seven columns')
            ])),
        dbc.ModalFooter(dbc.Button("Close", id="close"))
    ]


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    app.run_server(debug=True)

1 Answer

I encountered a similar question, but conceptually the same: click on something in a plotly/dash-object and then display a modal. Researching the issue, I also found this post on the plotly forum, which encounters very similar problems. The proposed solution there gave me the idea on how to solve.

There are 2 issues at hand if I copy your code.

  1. its missing the link to the bootstrap "external stylesheet" which can be easily added in your sample above, replacing app = dash.Dash() with app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
  2. the call back now only alters the modal's children. However, to be displayed, it's attribute, open should be set to True. When the button, with id=close is pressed, the open be altered to False.

I worked your code snipped out into something working. Hope this helps you out as well!

import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
# added State to be used in callbacks
from dash.dependencies import Input, Output, State

# using #type: ignore to suppress warnings in my editor (vs code). 
df = pd.read_excel("")
pv = pd.pivot_table(df, index=['Name'], columns=["Status"], values=['Quantity'], aggfunc=sum, fill_value=0)#type: ignore

trace1 = go.Bar(x=pv.index, y=pv[('Quantity', 'declined')], name='Declined') #type: ignore
trace2 = go.Bar(x=pv.index, y=pv[('Quantity', 'pending')], name='Pending')#type: ignore
trace3 = go.Bar(x=pv.index, y=pv[('Quantity', 'presented')], name='Presented')#type: ignore
trace4 = go.Bar(x=pv.index, y=pv[('Quantity', 'won')], name='Won')#type: ignore

app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div(children=[
    html.H1(children='Sales Funnel Report'),
    html.Div(children='''National Sales Funnel Report.'''),
    dcc.Graph(
        id='graph',
        figure={
            'data': [trace1, trace2, trace3, trace4],
            'layout':
                go.Layout(title='Order Status by Customer', barmode='stack')#type: ignore
        }),
    dbc.Button("toggle", id="open"),
    dbc.Modal(
        [
            dbc.ModalHeader("Header",id = 'modalheader'),
            dbc.ModalBody("This is the content of the modal"),
            dbc.ModalFooter(
                dbc.Button("Close", id="close", className="ml-auto")
            ),
        ],
        size="xl",
        id="modal",
    )
])

@app.callback([Output("modal", "children"),
               Output("modal", "is_open")],
              [Input("graph", "clickData"),
               Input("close", "n_clicks")],
              [State("modal", "is_open"),
               State("modal", "children")])
def set_content(value,clicked,is_open,children):
    ctx = dash.callback_context

    if ctx.triggered[0]['prop_id'] == 'close.n_clicks':
        # you pressed the closed button, keeping the modal children as is, and 
        # close the model itself. 
        return children, False 
    elif ctx.triggered[0]['prop_id'] == 'graph.clickData':
        # you clicked in the graph, returning the modal children and opening it
        return [dbc.ModalHeader("Test"),
                dbc.ModalBody(
                html.Div([
                    html.Div([
                        html.H6('Sales', style={'textAlign': 'center', 'padding': 10}),
                        html.P("Bitch", id="sales_stocks", style={'textAlign': 'center', 'padding': 10})
                    ], className='pretty_container four columns'),
                    html.Div([
                        html.H5('Current ratio', style={'textAlign': 'center', 'padding': 10}),
                        html.P(str(value),style = {'fontFamily':'monospace'})
                    ], className='pretty_container seven columns')
                ])),
                dbc.ModalFooter(dbc.Button("Close", id="close"))
                ], True
    else:
        raise dash.exceptions.PreventUpdate

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    app.run_server(debug=True)
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Marcus Vance

Marcus Vance

Cybersecurity & Digital Privacy Researcher

Marcus Vance is a cybersecurity auditor and technology writer dedicated to educating the public about online safety, data privacy regulations, enterprise security, and emerging cyber threats.

Share this article
Twitter Facebook Pinterest