Any way to create an instructions popout?


#1

Hi,

I’d like to have a popout appear and provide the user with instructions upon opening a page. Is there any way to do this aside from using a javascript alert (which prevents page from loading, and also does not allow for styling)?


#2

Create a modal window. I’m implementing something similar in my application. Here is the basic gist of it:

def modal():
    return html.Div(
        children=[
            html.Div([
                html.Div(
                    className='modal-text',
                    children=[
                        dcc.Markdown(dedent('''
                        # This is the text that will be in the modal
                        '''))
                    ]
                ),
        ],
        id='modal',
        className='modal',
        style={"display": "none"},
    )

And then here is the callbacks to open and close it:


# hide/show modal
@app.callback(Output('modal', 'style'),
              [Input('instructions-button', 'n_clicks')])
def show_modal(n):
    if n > 0:
        return {"display": "block"}
    return {"display": "none"}

# Close modal by resetting info_button click to 0
@app.callback(Output('instructions-button', 'n_clicks'),
              [Input('modal-close-button', 'n_clicks')])
def close_modal(n):
    return 0


#3

Also, in addition to the styling you add to your modal, one quick tip is to give it a z-index of 1002. The modebar for plotly figures have a z-index of 1001 so anything less and small little gray boxes will appear.

Here is my styles I’m using (btw, I use modal for the container, and use modal-content for the text. That way I can create a darkening/raised effect between the content and the application.

.modal {
    position: fixed; 
    z-index: 1002; /* Sit on top, including modebar which has z=1001 */
    left: 0;
    top: 0;
    width: 100%; /* Full width */
    height: 100%; /* Full height */
    background-color: rgba(0, 0, 0, 0.6); /* Black w/ opacity */

}

.modal-content {
    margin: 40px;
    height: 500px;
    padding: 40px;
    background-color: white;
}

#4

Wow, thats great! Thanks.

I got your idea working as I wanted with some minor tweaks. Here is a simplified out of the box working implementation that others may find useful:

import dash
from dash.dependencies import Input, Output
import dash_html_components as html

app = dash.Dash(__name__)

app.layout = html.Div([

    html.H2('Thank Mbkupfer for this modal'),

    html.Div([  # modal div
        html.Div([  # content div
            html.Div([
                'This is the content of the modal',

            ]),

            html.Hr(),
            html.Button('Close', id='modal-close-button')
        ],
            style={'textAlign': 'center', },
            className='modal-content',
        ),
    ],
        id='modal',
        className='modal',
        style={"display": "block"},
    )
])


@app.callback(Output('modal', 'style'),
              [Input('modal-close-button', 'n_clicks')])
def close_modal(n):
    if (n is not None) and (n > 0):
        return {"display": "none"}


if __name__ == '__main__':
    app.run_server(debug=True)

And big thanks mbkupfer for the CSS as well, very nice effects. - copy this into your local CSS file:

.modal {
    position: fixed;
    z-index: 1002; /* Sit on top, including modebar which has z=1001 */
    left: 0;
    top: 0;
    width: 100%; /* Full width */
    height: 100%; /* Full height */
    background-color: rgba(0, 0, 0, 0.6); /* Black w/ opacity */

}

.modal-content {
     margin: 90px;
    padding: 30px;
    background-color: white;
}

#5

@mbkupfer @troystory89
I am building an app where I need to drilldown to detail screen (in this case a modal) from a stacked bar chart. Any way I can trigger the modal on click of a specific bar? n_clicks is not recognized with charts.

Thanks in advance


#6

You can use the graph’s clickData property as a callback input instead of a button as in the example above. You can find some more dcc.Graph properties here.

Note the clickData is a dictionary data type so you will need to use keys to index its values. Perhaps print out to the console to get a feel for how the structure looks.


#7

Thank you for suggesting this @mbkupfer. I tried the below approach and it worked! I referred to this thread as well to store the states, as clickData will always have some value once the chart is clicked. Below is my code:

# -*- coding: utf-8 -*-

“”"
Created on Wed Mar 13 20:02:17 2019

@author: monishaj
“”"

import dash
from dash.dependencies import Input, Output, State
import dash_html_components as html
from plotly import graph_objs as go
from plotly.graph_objs import *
import dash_core_components as dcc
import json

app = dash.Dash(name)

trace1 = go.Bar(
x=[‘a’, ‘b’, ‘c’,‘d’],
y=[1.2,9.8,4.5,0.9],
name=‘A1’
)

trace2 = go.Bar(
x=[‘a’, ‘b’, ‘c’,‘d’],
y=[1.6,9.6,5.6,3.2],
name=‘A2’
)

trace3 = go.Bar(
x=[‘a’, ‘b’, ‘c’,‘d’],
y=[4.2,1.3,2.5,2.4],
name=‘A3’
)

app.layout = html.Div([

html.H2('Simple Modal'),
html.Div([
        html.Div(id='modal-content'),
         dcc.Graph(id='bar_plot',
          figure=go.Figure(data=[trace1, trace2, trace3],
                           layout=go.Layout(barmode='stack',hovermode='closest')),style={'margin-left':'205px'}
         )
          
        ]),

html.Div([  # modal div
    html.Div([  # content div
        html.P('Some Text'),
        html.Button('Close',n_clicks=0, id='modal-close-button')
    ],
        style={'textAlign': 'center', },
        className='modal-content',
    ),
],
    id='modal',
    className='modal',style={"display":"none"}
),
    html.Div(id='modal-button-values', children="Close:0 last:Close",style={"display":"none"})

])

@app.callback(Output(‘modal-button-values’, ‘children’),
[Input(‘bar_plot’, ‘clickData’),Input(‘modal-close-button’, ‘n_clicks’)],
[State(‘modal-button-values’, ‘children’)])
def close_modal(clickData,n,button_values):
button_values = dict([i.split(’:’) for i in button_values.split(’ ')])
l = len(clickData[‘points’][0].keys())

if l:
    if n == 0:
        last_clicked = "Get"
    elif n is not None and n > 0:
        if n > int(button_values["Close"]):
            last_clicked = "Close"
        elif n == int(button_values["Close"]):
            last_clicked = "Get"
return "Close:{0} last:{1}".format(n,last_clicked)  

@app.callback(Output(‘modal’, ‘style’),
[Input(‘modal-button-values’, ‘children’)]
)
def modal_display_status(button_values):
button_values = dict([i.split(’:’) for i in button_values.split(’ ')])

if button_values["last"] == 'Get':
    return {'display': 'block'}
else:
    return {'display': 'none'}

if name == ‘main’:
app.run_server(debug=True)