From 6de4b8d39e942957fdb482e395056e0c16fd0f2c Mon Sep 17 00:00:00 2001 From: Ian MacDougall Murray Date: Wed, 4 Mar 2026 17:35:06 +0000 Subject: [PATCH 1/3] fix: flatten outputs before building loadingOutputs Fixes #3619 --- dash/dash-renderer/src/actions/callbacks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/dash-renderer/src/actions/callbacks.ts b/dash/dash-renderer/src/actions/callbacks.ts index ee3a251378..37aab3f194 100644 --- a/dash/dash-renderer/src/actions/callbacks.ts +++ b/dash/dash-renderer/src/actions/callbacks.ts @@ -791,7 +791,7 @@ export function executeCallback( } const __execute = async (): Promise => { - const loadingOutputs = outputs.map(out => ({ + const loadingOutputs = flatten(outputs).map(out => ({ path: getPath(paths, out.id), property: out.property?.split('@')[0], id: stringifyId(out.id) From bebfe63f0a10b69db011708ee1d060b1b19189ec Mon Sep 17 00:00:00 2001 From: Ian MacDougall Murray Date: Tue, 17 Mar 2026 21:11:16 +0000 Subject: [PATCH 2/3] test: add test for loading spinner with ALL wildcard pattern matching Add test_ldcp019_loading_component_pattern_matching to verify that dcc.Loading spinner triggers correctly when callback Output uses the ALL wildcard with pattern-matching IDs. --- .../loading/test_loading_component.py | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/components/dash-core-components/tests/integration/loading/test_loading_component.py b/components/dash-core-components/tests/integration/loading/test_loading_component.py index 35507dfe8a..261094965a 100644 --- a/components/dash-core-components/tests/integration/loading/test_loading_component.py +++ b/components/dash-core-components/tests/integration/loading/test_loading_component.py @@ -1,5 +1,5 @@ from multiprocessing import Lock -from dash import Dash, Input, Output, dcc, html +from dash import Dash, Input, Output, dcc, html, MATCH, ALL from dash.dependencies import stringify_id from dash.testing import wait import time @@ -839,3 +839,54 @@ def update_btn1_children(n_clicks): assert spinners == [] assert dash_dcc.get_logs() == [] + + +# loading spinner triggers when callback Output uses ALL wildcard +def test_ldcp019_loading_component_pattern_matching(dash_dcc): + lock = Lock() + + app = Dash(__name__) + + app.layout = html.Div( + [ + dcc.Loading( + [ + html.Div( + id={"type": "div-1", "index": 1, "name": "test"}, + className="div-1", + ) + ], + className="loading", + ) + ], + id={"type": "root", "index": 1, "name": "test"}, + className="root", + ) + + @app.callback( + Output( + {"type": "div-1", "index": ALL, "name": MATCH}, "children" + ), + Input( + {"type": "root", "index": ALL, "name": MATCH}, "n_clicks" + ), + ) + def updateDiv(n_clicks): + if n_clicks == [1]: + time.sleep(0.1) + return ["changed"] + return ["content"] + + with lock: + dash_dcc.start_server(app) + dash_dcc.wait_for_text_to_equal(".div-1", "content") + + dash_dcc.find_element(".root").click() + + dash_dcc.find_element(".loading .dash-spinner") + # mounted but hidden, so looks like no text + dash_dcc.wait_for_text_to_equal(".div-1", "") + + dash_dcc.wait_for_text_to_equal(".div-1", "changed") + + assert dash_dcc.get_logs() == [] From ada7f354a63b25d2a0a7220d7eb4df72be6eb301 Mon Sep 17 00:00:00 2001 From: Ian MacDougall Murray Date: Tue, 24 Mar 2026 07:38:31 +0000 Subject: [PATCH 3/3] style: apply black formatting and fix duplicate test name --- .../integration/loading/test_loading_component.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/components/dash-core-components/tests/integration/loading/test_loading_component.py b/components/dash-core-components/tests/integration/loading/test_loading_component.py index 261094965a..165ecbc524 100644 --- a/components/dash-core-components/tests/integration/loading/test_loading_component.py +++ b/components/dash-core-components/tests/integration/loading/test_loading_component.py @@ -417,7 +417,7 @@ def updateDiv(n_clicks): # update multiple props of same component, only targeted id/prop triggers spinner # test that target_components id can be a dict id -def test_ldcp011_loading_component_target_components(dash_dcc): +def test_ldcp010_loading_component_target_components(dash_dcc): lock = Lock() app = Dash(__name__) @@ -864,12 +864,8 @@ def test_ldcp019_loading_component_pattern_matching(dash_dcc): ) @app.callback( - Output( - {"type": "div-1", "index": ALL, "name": MATCH}, "children" - ), - Input( - {"type": "root", "index": ALL, "name": MATCH}, "n_clicks" - ), + Output({"type": "div-1", "index": ALL, "name": MATCH}, "children"), + Input({"type": "root", "index": ALL, "name": MATCH}, "n_clicks"), ) def updateDiv(n_clicks): if n_clicks == [1]: