Example #1
0
def layout(pathname):
    lt = str(pathname).strip("/").split("/")
    project = lt[0]
    action = lt[-1]
    api_id = lt[1] if len(lt) == 3 else None

    projects_new = read_project_file()
    projects_delete = [x for x in projects_new if x["Project"] == project]

    print("{} action for project {}".format(action, project))
    if action == "delete":
        for pr in projects_delete:
            projects_new.remove(pr)
        if api_id:
            apis = [
                y for x in projects_delete for y in x["TestCase"]
                if y["ID"] == api_id
            ]
            for api in apis:
                projects_delete[0]["TestCase"].remove(api)

            projects_new.extend(projects_delete)
            write_project_file(projects_new)
            return dbc.Container(
                [html.H1("Deleted Test Case from {} ".format(project))])

        write_project_file(projects_new)

        print("deleted {} ".format(project))
        return dbc.Container([html.H1("Deleted project {} ".format(project))])
Example #2
0
def get_about_html():
    text = """
           This is the implementation of our Data Science Project. The aim of this project is to provide 
           suggestions on suitable relocation areas in Finland, based on housing prices and demographics.
           """
    return html.Div([
        html.H1("About"),
        html.H3(text, id="about_text"),
        html.H1("Team"),
        html.Table([
            html.Thead(
                html.Tr([
                    html.Th("Letizia"),
                    html.Th("Taige"),
                    html.Th("Roope"),
                    html.Th("Trang"),
                    html.Th("Thong")
                ])),
            html.Tbody(
                html.Tr([
                    html.Td(
                        html.A(html.Img(
                            src=
                            "https://avatars1.githubusercontent.com/u/45148109?s=200&v=4",
                            alt="Letizia"),
                               href="https://github.com/letiziaia")),
                    html.Td(
                        html.A(html.Img(
                            src=
                            "https://avatars2.githubusercontent.com/u/16875716?s=200&v=4",
                            alt="Taige"),
                               href="https://github.com/xiaoxiaobt")),
                    html.Td(
                        html.A(html.Img(
                            src=
                            "https://avatars2.githubusercontent.com/u/43811718?s=200&v=4",
                            alt="Roope"),
                               href="https://github.com/rooperuu")),
                    html.Td(
                        html.A(html.Img(
                            src=
                            "https://avatars3.githubusercontent.com/u/55182434?s=200&v=4",
                            alt="Trang"),
                               href="https://github.com/trangmng")),
                    html.Td(
                        html.A(html.Img(
                            src=
                            "https://avatars0.githubusercontent.com/u/32213097?s=200&v=4",
                            alt="Thong"),
                               href="https://github.com/trananhthong"))
                ]))
        ],
                   id="team_table")
    ],
                    id="about_info")
Example #3
0
def layout(project):
    project = project.replace("%20", " ")
    projects = read_project_file()
    print("[x[Project] for x in projects] :", [x["Project"] for x in projects])
    is_exist = project in [x["Project"] for x in projects]
    if is_exist:
        return html.Div([
            html.H1("Project : {}".format(project),
                    style={"text-align": "center"}),
            dbc.Container(get_test_cards(project), className="my-1"),
            CreateTestCaseModal(project).get()
        ], )
    else:
        return dbc.Container([html.H1("Invalid Project : {}".format(project))])
def parse(app, markdown_path, extra_env_vars=None):
    extra_env_vars = extra_env_vars or {}
    raw = (HERE / markdown_path).read_text()

    # we use the markdown package to extract metadata
    md = markdown.Markdown(extensions=["meta"])
    md.convert(raw)
    meta = md.Meta

    content = [
        html.H1(meta["title"][0]),
        html.Div(dcc.Markdown(meta["lead"][0]), className="lead"),
    ]

    raw = HEADER_PATTERN.sub("", raw).strip()

    markdown_blocks = SPLIT_PATTERN.split(raw)
    markdown_blocks = [
        dcc.Markdown(block.strip()) for block in markdown_blocks
    ]

    examples_docs = EXAMPLE_DOC_PATTERN.findall(raw)
    examples_docs = [
        _parse_block(block, app, extra_env_vars) for block in examples_docs
    ]

    content.extend(_interleave(markdown_blocks, examples_docs))
    return html.Div(content, key=str(markdown_path))
Example #5
0
def display_page(pathname):
    print("path : ", pathname, type(pathname))
    print(pathname and len(str(pathname).split("/")) == 2)

    paths = pathname.split("/") if pathname else pathname
    print("path list", paths)
    if pathname == '/':
        return home.layout()
    elif pathname and len(str(pathname).split("/")) == 2:
        return project.layout(pathname.strip("/").split("/")[0])
    elif pathname and len(str(pathname).split("/")) in [3, 4]:
        if str(pathname).split("/")[-1] == "delete":
            return delete_test_case.layout(pathname)
        elif str(pathname).split("/")[-1] == "test":
            return run_test_case.layout(pathname)
        elif str(pathname).split("/")[-2] == "export":
            print("pathname", pathname)
            if str(pathname).split("/")[-1] in FILE_FORMATS:
                return redirect("{}".format(pathname), code=302)
            else:
                return export_text_case.invalid_file_format_html()
        else:
            home.layout()
    else:
        return dbc.Container([html.H1("404")])
Example #6
0
def init_dashboard(server):
    dash_app = dash.Dash(
        server=server,
        routes_pathname_prefix="/",
        external_stylesheets=[dbc.themes.SUPERHERO],
    )
    
    dash_app.title = 'Covid-19 Dashboard'
    
    #lo Create Layout
    dash_app.layout = dbc.Container(
        [
            html.H1("USA Covid-19 Dashboard"),
            html.P(children=['Last updated: ',last_update], style={'text-align': 'right'}),
            html.Hr(),
            dbc.Tabs(
                [
                    dbc.Tab(label="Overview", tab_id="tab-1"),
                    dbc.Tab(label="Testing", tab_id="tab-3"),
                    dbc.Tab(label="Hospitalizations", tab_id="tab-4"),
                    dbc.Tab(label="Deaths", tab_id="tab-5"),
                    dbc.Tab(label="Vaccination", tab_id="tab-2"),
                    dbc.Tab(label="About", tab_id="tab-6"),
                    ],
                id = 'tabs',
                active_tab = 'tab-1',
                ),
            html.Div(id="tab-content", className="p-4"),
            html.Br(),
            html.Div(id="drop_figure"),
            ]
        )
    init_callbacks(dash_app)
    
    return dash_app.server
Example #7
0
def test_tabs005_disabled(dash_dcc):
    app = Dash(__name__, assets_folder="../../assets")
    app.layout = html.Div([
        html.H1("Dash Tabs component with disabled tab demo"),
        dcc.Tabs(
            id="tabs-example",
            value="tab-2",
            children=[
                dcc.Tab(
                    label="Disabled Tab",
                    value="tab-1",
                    id="tab-1",
                    className="test-custom-tab",
                    disabled=True,
                ),
                dcc.Tab(
                    label="Active Tab",
                    value="tab-2",
                    id="tab-2",
                    className="test-custom-tab",
                ),
            ],
        ),
        html.Div(id="tabs-content-example"),
    ])

    dash_dcc.start_server(app)

    dash_dcc.wait_for_element("#tab-2")
    dash_dcc.wait_for_element(".tab--disabled")
    assert dash_dcc.get_logs() == []
Example #8
0
def test_tabs004_without_value(dash_dcc):
    app = Dash(__name__)

    app.layout = html.Div([
        html.H1("Dash Tabs component demo"),
        dcc.Tabs(
            id="tabs-without-value",
            children=[
                dcc.Tab(label="Tab One", value="tab-1"),
                dcc.Tab(label="Tab Two", value="tab-2"),
            ],
        ),
        html.Div(id="tabs-content"),
    ])

    @app.callback(Output("tabs-content", "children"),
                  [Input("tabs-without-value", "value")])
    def render_content(tab):
        if tab == "tab-1":
            return html.H3("Default selected Tab content 1")
        elif tab == "tab-2":
            return html.H3("Tab content 2")

    dash_dcc.start_server(app)
    dash_dcc.wait_for_text_to_equal("#tabs-content",
                                    "Default selected Tab content 1")
    dash_dcc.percy_snapshot("Core Tab 1 should be selected by default")
    assert dash_dcc.get_logs() == []
Example #9
0
 def layout(self, spa, message='404 Not Found'):
     return html.Div([
         html.Div([
             html.Div([
                 html.Div([
                     html.H1('Oops!'),
                     html.H2(message),
                     html.Div(
                         'Sorry, an error has occurred, Requested page not found!',
                         className='error-details'),
                     html.Div([
                         dcc.Link([
                             html.Span(className='fa fa-home'),
                             ' Take Me Home'
                         ],
                                  href='/',
                                  className='btn btn-secondary btn-lg'),
                         dcc.Link([
                             html.Span(className='fa fa-envelope'),
                             ' Contact Support'
                         ],
                                  href='/support',
                                  className='btn btn-secondary btn-lg'),
                     ],
                              className='error-actions')
                 ],
                          className='error-template')
             ],
                      className='col-md-12')
         ],
                  className='row')
     ],
                     className='container')
Example #10
0
def test_grbs004_graph_loading_state_updates(dash_dcc):
    lock = Lock()
    app = Dash(__name__, suppress_callback_exceptions=True)
    app.layout = html.Div(
        [
            html.H1(id="title", children="loading state updates"),
            dcc.Graph(id="my-graph"),
        ]
    )

    @app.callback(Output("my-graph", "figure"), [Input("title", "n_clicks")])
    def update_graph(n_clicks):
        values = [0, n_clicks]
        ranges = [0, n_clicks]

        with lock:
            return {
                "data": [{"x": ranges, "y": values, "line": {"shape": "spline"}}],
            }

    dash_dcc.start_server(app)
    dash_dcc.wait_for_element("#my-graph:not([data-dash-is-loading])")

    with lock:
        title = dash_dcc.wait_for_element("#title")
        title.click()
        dash_dcc.wait_for_element('#my-graph[data-dash-is-loading="true"]')

    dash_dcc.wait_for_element("#my-graph:not([data-dash-is-loading])")

    assert dash_dcc.get_logs() == []
Example #11
0
def layout(pathname):
    lt = str(pathname).strip("/").split("/")
    project = lt[0]
    action = lt[-1]
    if not (action == "test"):
        return

    api_id = lt[1] if len(lt) == 3 else None

    projects_new = read_project_file()

    lt = []
    for p in projects_new:
        if p["Project"] == project:
            if api_id:
                for tc in p["TestCase"]:
                    if tc["ID"] == api_id:
                        lt.append(RunTestCase().run(tc))
            else:
                lt = [
                    html.H1("Testing project {} ".format(project), style={"text-align": "center", "color": "Blue"})
                ]
                for test_case in p["TestCase"]:
                    lt.append(RunTestCase().run(test_case))

    write_project_file(projects_new)
    return dbc.Container(
        lt
    )
Example #12
0
 def run(self, test_case):
     response = HttpClient(test_case["API"]).method(test_case["Method"])(test_case["Body"])
     self.html_response = html.Div(
         [
             html.H1("Test Case : {}".format(test_case["API"]), style={"text-align": "center", "color": "red"}),
             dbc.Row(
                 [
                     dbc.Col(
                         html.H2("Method : {}".format(test_case["Method"]),
                                 style={"text-align": "left", "color": "yellow"}),
                         style={"width": 6}
                     ),
                     dbc.Col(
                         html.H2("Status : {}".format(response.status_code),
                                 style={"text-align": "right", "color": "green"}),
                         style={"width": 6}
                     ),
                 ],
             ),
             html.P("Response : {}".format(response.body))
         ]
     )
     test_case["ResponseBody"] = response.body
     test_case["StatusCode"] = response.status_code
     test_case["Status"] = response.status
     return self.html_response
Example #13
0
def test_tabs002_without_children(dash_dcc):
    app = Dash(__name__)
    app.layout = html.Div([
        html.H1("Dash Tabs component demo"),
        dcc.Tabs(
            id="tabs",
            value="tab-2",
            children=[
                dcc.Tab(label="Tab one", value="tab-1", id="tab-1"),
                dcc.Tab(label="Tab two", value="tab-2", id="tab-2"),
            ],
        ),
        html.Div(id="tabs-content"),
    ])

    @app.callback(
        Output("tabs-content", "children"),
        [Input("tabs", "value")],
    )
    def render_content(tab):
        if tab == "tab-1":
            return html.Div([html.H3("Test content 1")], id="test-tab-1")
        elif tab == "tab-2":
            return html.Div([html.H3("Test content 2")], id="test-tab-2")

    dash_dcc.start_server(app)
    dash_dcc.wait_for_text_to_equal("#tabs-content", "Test content 2")
    dash_dcc.percy_snapshot("Core initial tab - tab 2")

    dash_dcc.wait_for_element("#tab-1").click()
    dash_dcc.wait_for_text_to_equal("#tabs-content", "Test content 1")
    assert dash_dcc.get_logs() == []
Example #14
0
    def welcomePage(self):
        title = html.H1(
            "Recount",
            style={
                "marginBottom": "-1rem",
                "fontSize": "8rem",
                "fontWeight": "bold",
            },
        )
        subtext_title = html.Strong("Gonna check dat budget!",
                                    style={"fontSize": "3rem"})
        upper_welcome = html.Div([title, subtext_title],
                                 style={"textAlign": "center"})

        welcome = html.H2([html.Br(), "Welcome"],
                          style={
                              "fontSize": "6rem",
                              "fontWeight": "bold"
                          })
        paraf1 = html.P([
            "Bonjour et bienvenu sur ",
            html.Strong("Recount"),
            ", une application qui permet de visualiser ses dépenses.",
        ])
        paraf2 = html.P([
            html.Br(),
            "La ",
            "version 0.1",
            " viens de sortir, et ",
            html.Strong("vous avez été choisi pour la tester!"),
        ])
        paraf3 = html.P([
            "Testez, jouez avec les données, et faites moi des retours par mail quand vous le souhaitez à ",
            html.A("*****@*****.**"),
        ])
        paraf4 = html.P([
            "Si vous voulez un petit ",
            html.Strong("tuto"),
            " pour vous lancer, j'ai préparé ",
            html.A("celui-ci."),
        ])
        paraf5 = html.P([
            html.Br(),
            "Niveau sécurité, tout les fichiers sont chiffrés et les connexions sécurisées. Mais je ne vais pas mentir : tout est cadenassé, mais les clefs ne sont pas cachées. Il faut partir du principe qu'",
            html.Strong("une fuite de données peut arriver TRÈS facilement."),
            " Si cela vous dérange d'entrer vos dépenses personnelles, utilisez les données de l'exemple ou inventez vos propres données.",
        ])
        paraf6 = html.P([
            html.Br(),
            "Je reste joignable sur mon téléphone, mail ou via facebook."
        ])
        paraf7 = html.P([html.Br(), "Amusez vous bien!"])

        list_parafs = [paraf1, paraf2, paraf3, paraf4, paraf5, paraf6, paraf7]
        for i in range(len(list_parafs)):
            list_parafs[i].style = {"fontSize": "xx-large"}
        parafs = html.Div(list_parafs)

        return html.Div([upper_welcome, welcome, parafs])
def get_polar_html(old_code="02150", new_code="00100"):
    # H1
    location_string = f"Hey, how about this one? {zip_name_dict[new_code]}, {new_code}"
    # H2
    sell_price_string = get_attribute(postalcode=new_code, column="Sell price")
    sell_price_string = f"{float(sell_price_string):.2f}" if sell_price_string != "0.0" else "--"
    rent_ara_price_string = get_attribute(postalcode=new_code,
                                          column="Rent price with ARA")
    rent_ara_price_string = f"{float(rent_ara_price_string):.2f}" if rent_ara_price_string != "0.0" else "--"
    rent_noara_price_string = get_attribute(postalcode=new_code,
                                            column="Rent price without ARA")
    rent_noara_price_string = f"{float(rent_noara_price_string):.2f}" if rent_noara_price_string != "0.0" else "--"
    trend_near_future_string = f"{float(get_attribute(new_code, column='Trend near future')):+.2%}"

    # H3
    average_age_string = get_attribute(postalcode=new_code,
                                       column="Average age of inhabitants")

    categories = [
        'Education', 'Services', 'Public Transportation', 'Average Income',
        'Population Density'
    ]
    fig = go.Figure()
    fig.add_trace(
        go.Scatterpolar(r=radar_value(old_code),
                        theta=categories,
                        fill='toself',
                        name='Current location'))
    fig.add_trace(
        go.Scatterpolar(r=radar_value(new_code),
                        theta=categories,
                        fill='toself',
                        name='New location'))
    fig.update_layout(polar=dict(radialaxis=dict(range=[0, 1])),
                      margin={
                          "r": 20,
                          "t": 50,
                          "l": 20,
                          "b": 20
                      })

    return html.Div(
        children=[
            html.H1(location_string),
            html.H2(f"🛈 Last 12 months sell price: {sell_price_string} €/m²",
                    id="sell_12"),
            html.
            H2(f"🛈 Last 12 months rent price: {rent_ara_price_string} €/m² (including ARA), {rent_noara_price_string} €/m² (private only)",
               id="rent_12"),
            html.H3(f"Trend of price: {trend_near_future_string}"),
            html.H3(f"Average age: {average_age_string} years"),
            # html.H3(percentage_degree + "% of the people has a higher university degree"),
            make_dash_table(old_code, new_code),
            dcc.Graph(figure=fig,
                      id="polar_graph",
                      config={'displayModeBar': False})
        ],
        id="analysis_info")
def make_brand(**kwargs):
    return html.Header(
        className="brand",
        children=dcc.Link(
            href=get_url(""),
            children=html.H1([fa("far fa-chart-bar"), server.config["TITLE"]]),
        ),
        **kwargs,
    )
    def serve(self):
        assert (self._built)

        self._app.layout = html.Div(children=[
            html.H1(["WARA-SW TEP Dashboard: ",
                     html.B(self.title)]),
            self.render()
        ])
        self._app.run_server(debug=True)
Example #18
0
    def make_header(self):

        header = html.Div([
            html.H1("Overview of Successful Response Rate Last 24 hours"),
            dcc.Interval(id="main-content-table-interval",
                         interval=30 * 1000,
                         n_intervals=0)
        ], id="main-content-header")

        return header
Example #19
0
 def layout(self) -> html.Div:
     return html.Div(
         [
             html.H1(self.title),
             html.Button(
                 id=self.uuid("submit-button"), n_clicks=0, children="Submit"
             ),
             html.Div(id=self.uuid("output-state")),
         ]
     )
Example #20
0
def render_page_content(pathname):
    if pathname[1:] in COMPRESSOR_TAGS:
        return page_layout()
    # If the user tries to reach a different page, return a 404 message
    return dbc.Jumbotron(
        [
            html.H1("404: Not found", className="text-danger"),
            html.Hr(),
            html.P(f"The pathname {pathname} was not recognised..."),
        ]
    )
Example #21
0
    def make_header(self):

        header = html.Div([
            html.H1("SOA Serial Monitor Last 24 hours"),
            dcc.Interval(id="main-content-table-interval",
                         interval=30 * 1000,
                         n_intervals=0)
        ],
                          id="main-content-header")

        return header
Example #22
0
    def make_header(self):

        header = html.Div([
            html.H1("Authoritative DNS Server Service Level Monitor"),
            dcc.Interval(id="main-content-graph-interval",
                         interval=150 * 1000,
                         n_intervals=0)
        ],
                          id="main-content-header")

        return header
Example #23
0
def populate_checklist(data, day):
    if day is None:
        return html.H1("Please choose a day on bargraph page!")
    dff = pd.DataFrame(data)
    dff = dff[dff["day"] == day]
    my_table = dash_table.DataTable(
                    id='table',
                    columns=[{"name": i, "id": i} for i in dff.columns],
                    data=dff.to_dict('records'),
                )
    return my_table
Example #24
0
def add_header(el):
    return dbc.Row([
        dbc.Col(
            dcc.Link(html.H1('My car info'),
                     href=dash_app.requests_pathname_external_prefix,
                     style={"text-decoration": "none"})),
        dbc.Col(
            dcc.Link(html.Img(src="assets/images/settings.svg", width="30veh"),
                     href=dash_app.requests_pathname_external_prefix +
                     "config",
                     className="float-end"))
    ]), el
Example #25
0
    def render_page_content(pathname):
        if pathname in ["", "/", "/dash", "/dash/table"]:
            return table.layout
        elif pathname == "/dash/map":
            return map.layout

        # If the user tries to reach a different page, return a 404 message
        return dbc.Jumbotron([
            html.H1("404: Not found", className="text-danger"),
            html.Hr(),
            html.P(f"The pathname {pathname} was not recognised..."),
        ])
Example #26
0
 def get_header():
     return dbc.Jumbotron([
         html.H1("Carbon Footprint", style={"textAlign": "center"}),
         html.P(
             "Measure Compute Emissions",
             style={
                 "textAlign": "center",
                 "paddingLeft": "0.5%"
             },
             className="lead",
         ),
     ])
Example #27
0
def test_tabs003_without_children_undefined(dash_dcc):
    app = Dash(__name__)
    app.layout = html.Div([
        html.H1("Dash Tabs component demo"),
        dcc.Tabs(id="tabs", value="tab-1"),
        html.Div(id="tabs-content"),
    ])

    dash_dcc.start_server(app)
    dash_dcc.wait_for_element("#tabs-content")
    dash_dcc.percy_snapshot("Core Tabs component with children undefined")
    assert dash_dcc.get_logs() == []
Example #28
0
def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
    """
    Sample Dash application from Plotly: https://github.com/plotly/dash-hello-world/blob/master/app.py
    """
    server = flask.Flask(__name__)
    server.secret_key = os.environ.get('secret_key', 'secret')

    df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/hello-world-stock.csv')

    app = dash.Dash(__name__, server=server, requests_pathname_prefix=requests_pathname_prefix)

    app.scripts.config.serve_locally = False
    dcc._js_dist[0]['external_url'] = 'https://cdn.plot.ly/plotly-basic-latest.min.js'

    app.layout = html.Div([
        html.H1('Stock Tickers'),
        dcc.Dropdown(
            id='my-dropdown',
            options=[
                {'label': 'Tesla', 'value': 'TSLA'},
                {'label': 'Apple', 'value': 'AAPL'},
                {'label': 'Coke', 'value': 'COKE'}
            ],
            value='TSLA'
        ),
        dcc.Graph(id='my-graph')
    ], className="container")

    @app.callback(Output('my-graph', 'figure'),
                  [Input('my-dropdown', 'value')])
    def update_graph(selected_dropdown_value):
        dff = df[df['Stock'] == selected_dropdown_value]
        return {
            'data': [{
                'x': dff.Date,
                'y': dff.Close,
                'line': {
                    'width': 3,
                    'shape': 'spline'
                }
            }],
            'layout': {
                'margin': {
                    'l': 30,
                    'r': 20,
                    'b': 30,
                    't': 20
                }
            }
        }

    return app
Example #29
0
def get_app_layout():
    return dbc.Container([
        dcc.Store(id='selected-data-left'),
        dcc.Store(id='selected-data-right'),
        dcc.Store(id='session-id', data=str(uuid.uuid4())),
        dcc.Store(id='filter-trigger', data=0),
        dcc.Store(id='left-hide-trigger', data=0),
        dcc.Store(id='file-loaded-trigger', data=0),
        dcc.Store(id='dummy-export-scatter2d-left'),
        dcc.Store(id='dummy-export-scatter2d-right'),
        dcc.Store(id='dummy-export-histogram'),
        dcc.Store(id='dummy-export-violin'),
        dcc.Store(id='dummy-export-parallel'),
        dcc.Store(id='dummy-export-heatmap'),
        dcc.Store(id='dummy-export-data'),

        html.Div(
            [
                html.Div(html.Img(
                    src=app.get_asset_url('sensorview_logo.svg'),
                    id='sensorview-image',
                    style={
                        'height': '100px',
                        'width': 'auto',
                    },
                ), className="text-center"),
                html.H1(app.title, className="text-center"),
                html.Hr(className="my-2"),
                html.P(
                    'Sensor Data Visualization', className="text-center"
                ),
            ],
            className="bg-light rounded-3 my-2 p-3",
        ),

        dbc.Row([
            dbc.Col(
                dbc.CardGroup([
                    testcase_card,
                    datafile_card
                ])
            )], className='mb-3'),

        tabs,

        dcc.Markdown(
            'Designed and developed by **Zhengyu Peng** \
                | Powered by [Dash](https://plotly.com/dash/),\
                [Redis](https://redis.io/),\
                [Celery](https://docs.celeryproject.org/en/stable/),\
                [Docker](https://www.docker.com/)'),
    ], fluid=True, className="dbc_light")
Example #30
0
def test_cbmt003_chain_with_table(dash_duo):
    # see https://github.com/plotly/dash/issues/1071
    app = Dash(__name__)
    app.layout = html.Div(
        [
            html.Div(id="a1"),
            html.Div(id="a2"),
            html.Div(id="b1"),
            html.H1(id="b2"),
            html.Button("Update", id="button"),
            dash_table.DataTable(id="table"),
        ]
    )

    @app.callback(
        # Changing the order of outputs here fixes the issue
        [Output("a2", "children"), Output("a1", "children")],
        [Input("button", "n_clicks")],
    )
    def a12(n):
        return "a2: {!s}".format(n), "a1: {!s}".format(n)

    @app.callback(Output("b1", "children"), [Input("a1", "children")])
    def b1(a1):
        return "b1: '{!s}'".format(a1)

    @app.callback(
        Output("b2", "children"),
        [Input("a2", "children"), Input("table", "selected_cells")],
    )
    def b2(a2, selected_cells):
        return "b2: '{!s}', {!s}".format(a2, selected_cells)

    dash_duo.start_server(app)

    dash_duo.wait_for_text_to_equal("#a1", "a1: None")
    dash_duo.wait_for_text_to_equal("#a2", "a2: None")
    dash_duo.wait_for_text_to_equal("#b1", "b1: 'a1: None'")
    dash_duo.wait_for_text_to_equal("#b2", "b2: 'a2: None', None")

    dash_duo.find_element("#button").click()
    dash_duo.wait_for_text_to_equal("#a1", "a1: 1")
    dash_duo.wait_for_text_to_equal("#a2", "a2: 1")
    dash_duo.wait_for_text_to_equal("#b1", "b1: 'a1: 1'")
    dash_duo.wait_for_text_to_equal("#b2", "b2: 'a2: 1', None")

    dash_duo.find_element("#button").click()
    dash_duo.wait_for_text_to_equal("#a1", "a1: 2")
    dash_duo.wait_for_text_to_equal("#a2", "a2: 2")
    dash_duo.wait_for_text_to_equal("#b1", "b1: 'a1: 2'")
    dash_duo.wait_for_text_to_equal("#b2", "b2: 'a2: 2', None")