diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e652c64c1..3e1bd29118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## Fixed - [#3080](https://github.com/plotly/dash/pull/3080) Fix docstring generation for components using single-line or nonstandard-indent leading comments +- [#3103](https://github.com/plotly/dash/pull/3103) Fix Graph component becomes unresponsive if an invalid figure is passed ## [2.18.2] - 2024-11-04 diff --git a/components/dash-core-components/src/fragments/Graph.react.js b/components/dash-core-components/src/fragments/Graph.react.js index bed0975b89..6027404e56 100644 --- a/components/dash-core-components/src/fragments/Graph.react.js +++ b/components/dash-core-components/src/fragments/Graph.react.js @@ -173,9 +173,9 @@ class PlotlyGraph extends Component { configClone.typesetMath = mathjax; const figureClone = { - data: figure.data, - layout: this.getLayout(figure.layout, responsive), - frames: figure.frames, + data: figure?.data, + layout: this.getLayout(figure?.layout, responsive), + frames: figure?.frames, config: configClone, }; diff --git a/components/dash-core-components/tests/integration/graph/test_graph_basics.py b/components/dash-core-components/tests/integration/graph/test_graph_basics.py index 5a4d37a738..9126375021 100644 --- a/components/dash-core-components/tests/integration/graph/test_graph_basics.py +++ b/components/dash-core-components/tests/integration/graph/test_graph_basics.py @@ -370,3 +370,44 @@ def handleClick(clickData): data = json.loads(data) assert "customdata" in data["points"][0], "graph clickData must contain customdata" assert data["points"][0]["customdata"][0] == expected_value + + +def test_grbs008_graph_with_empty_figure(dash_dcc): + app = Dash(__name__) + app.layout = html.Div( + [ + html.Button("Toggle graph", id="btn"), + dcc.Graph( + id="graph", + figure=None, + ), + ] + ) + + @app.callback(Output("graph", "figure"), [Input("btn", "n_clicks")]) + def toggle_figure(n_clicks): + if int(n_clicks or 0) % 2 == 0: + # a valid figure + return go.Figure([], layout=go.Layout(title="Valid Figure")) + else: + # an invalid figure + return None + + dash_dcc.start_server(app) + + # Click the toggle button a couple of times and expect the graph to change between the + # valid and invalid figures, using the "title" as the indicator. + dash_dcc.wait_for_element("#graph") + wait.until( + lambda: dash_dcc.find_element(".gtitle").text == "Valid Figure", timeout=2 + ) + + dash_dcc.find_element("#btn").click() + wait.until(lambda: len(dash_dcc.find_elements(".gtitle")) == 0, timeout=2) + + dash_dcc.find_element("#btn").click() + wait.until( + lambda: dash_dcc.find_element(".gtitle").text == "Valid Figure", timeout=2 + ) + + assert dash_dcc.get_logs() == []