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/2] 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/2] 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() == []