diff --git a/CHANGELOG.md b/CHANGELOG.md index 379c34f3af..70b41ad382 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## Fixed - [#3264](https://github.com/plotly/dash/pull/3264) Fixed an issue where moving components inside of children would not update the `setProps` path, leading to hashes being incorrect +- [#3265](https://github.com/plotly/dash/pull/3265) Fixed issue where the resize of graphs was cancelling others + ## [3.0.2] - 2025-04-01 diff --git a/components/dash-core-components/src/utils/ResizeDetector.js b/components/dash-core-components/src/utils/ResizeDetector.js index 654ce7232b..f3b65865d9 100644 --- a/components/dash-core-components/src/utils/ResizeDetector.js +++ b/components/dash-core-components/src/utils/ResizeDetector.js @@ -4,11 +4,10 @@ import PropTypes from 'prop-types'; // Debounce 50 ms const DELAY = 50; -let resizeTimeout; - const ResizeDetector = props => { const {onResize, children, targets} = props; const ref = createRef(); + let resizeTimeout; const debouncedResizeHandler = useCallback(() => { if (resizeTimeout) { diff --git a/components/dash-core-components/tests/integration/graph/test_graph_responsive.py b/components/dash-core-components/tests/integration/graph/test_graph_responsive.py index 223a1b8066..aea426e393 100644 --- a/components/dash-core-components/tests/integration/graph/test_graph_responsive.py +++ b/components/dash-core-components/tests/integration/graph/test_graph_responsive.py @@ -1,6 +1,7 @@ import pytest from dash import Dash, Input, Output, State, dcc, html +import plotly.graph_objects as go from dash.exceptions import PreventUpdate from dash.testing import wait @@ -134,3 +135,77 @@ def resize(n_clicks, style): ) assert dash_dcc.get_logs() == [] + + +def test_grrs002_graph(dash_dcc): + app = Dash(__name__) + + app.layout = html.Div( + [ + html.Button("Generate Figures", id="generate-btn", n_clicks=0), + html.Button("Get Bounding Box", id="bounding-btn"), + html.Div( + id="graph-container", + children=[ + html.Div(id="bounding-output"), + dcc.Graph( + id="prec-climate-daily", + style={"height": "45vh"}, + config={"responsive": True}, + ), + dcc.Graph( + id="temp-climate-daily", + style={"height": "45vh"}, + config={"responsive": True}, + ), + ], + style={"display": "none"}, + ), + ] + ) + + app.clientside_callback( + """() => { + pcd_container = document.querySelector("#prec-climate-daily") + pcd_container_bbox = pcd_container.getBoundingClientRect() + pcd_graph = pcd_container.querySelector('.main-svg') + pcd_graph_bbox = pcd_graph.getBoundingClientRect() + tcd_container = document.querySelector("#temp-climate-daily") + tcd_container_bbox = tcd_container.getBoundingClientRect() + tcd_graph = tcd_container.querySelector('.main-svg') + tcd_graph_bbox = tcd_graph.getBoundingClientRect() + return JSON.stringify( + pcd_container_bbox.height == pcd_graph_bbox.height && + pcd_container_bbox.width == pcd_graph_bbox.width && + tcd_container_bbox.height == tcd_graph_bbox.height && + tcd_container_bbox.width == tcd_graph_bbox.width + ) + }""", + Output("bounding-output", "children"), + Input("bounding-btn", "n_clicks"), + prevent_initial_call=True, + ) + + @app.callback( + [ + Output("prec-climate-daily", "figure"), + Output("temp-climate-daily", "figure"), + Output("graph-container", "style"), + Output("bounding-output", "children", allow_duplicate=True), + ], + [Input("generate-btn", "n_clicks")], + prevent_initial_call=True, + ) + def update_figures(n_clicks): + fig_acc = go.Figure(data=[go.Scatter(x=[0, 1, 2], y=[0, 1, 0], mode="lines")]) + fig_daily = go.Figure(data=[go.Scatter(x=[0, 1, 2], y=[1, 0, 1], mode="lines")]) + return fig_acc, fig_daily, {"display": "block"}, "loaded" + + dash_dcc.start_server(app) + dash_dcc.wait_for_text_to_equal("#generate-btn", "Generate Figures") + dash_dcc.find_element("#generate-btn").click() + dash_dcc.wait_for_text_to_equal("#bounding-output", "loaded") + dash_dcc.find_element(".dash-graph .js-plotly-plot.dash-graph--pending") + dash_dcc.find_element(".dash-graph .js-plotly-plot:not(.dash-graph--pending)") + dash_dcc.find_element("#bounding-btn").click() + dash_dcc.wait_for_text_to_equal("#bounding-output", "true") diff --git a/tests/integration/callbacks/test_prevent_update.py b/tests/integration/callbacks/test_prevent_update.py index 7fbcd4ac50..038622fe96 100644 --- a/tests/integration/callbacks/test_prevent_update.py +++ b/tests/integration/callbacks/test_prevent_update.py @@ -36,7 +36,11 @@ def callback1(value): raise PreventUpdate("testing callback does not update") return value - @app.callback(Output("output2", "children"), [Input("output1", "children")]) + @app.callback( + Output("output2", "children"), + [Input("output1", "children")], + prevent_initial_call=True, + ) def callback2(value): callback2_count.value += 1 return value