Skip to content

fix: flatten outputs before building loadingOutputs#3641

Open
i-murray wants to merge 2 commits intoplotly:devfrom
i-murray:bugfix/loading-all-wildcard-3619
Open

fix: flatten outputs before building loadingOutputs#3641
i-murray wants to merge 2 commits intoplotly:devfrom
i-murray:bugfix/loading-all-wildcard-3619

Conversation

@i-murray
Copy link

@i-murray i-murray commented Mar 4, 2026

Summary

Fix dcc.Loading spinner not triggering when a callback Output uses the ALL wildcard.

Problem

In dash-renderer/src/actions/callbacks.ts, loadingOutputs was built with outputs.map(...). When the output spec contains ALL, unwrapIfNotMulti returns resolved outputs as a nested array. Iterating with .map() over this nested structure meant out.id was undefined, so getPath returned undefined and the loading reducer never matched the component path.

Fix

Changed outputs.map(...) to flatten(outputs).map(...) on L794, consistent with how outputs are already flattened on L829 and L912 in the same function.

Fixes #3619

@AnnMarieW
Copy link
Collaborator

Thanks for the fix @i-murray

I tried your sample app after the fix and verified that it works. Here is a simplified version that can be added as a test in this file: https://github.com/plotly/dash/blob/dev/components/dash-core-components/tests/integration/loading/test_loading_component.py

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

@AnnMarieW
Copy link
Collaborator

@T4rk1n This is ready for review - I ran the new test locally, but I don't think I can add it to the PR since I'm not a maintainer.

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.
@i-murray
Copy link
Author

@AnnMarieW, thank you for writing the test for this fix. I've added it.

Copy link
Contributor

@T4rk1n T4rk1n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💃 Looks good, just need to run npm run format to pass the linting.

@ndrezn
Copy link
Member

ndrezn commented Mar 18, 2026

Thanks for the contribution @i-murray !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] dcc.Loading spinner does not trigger when callback Output uses ALL wildcard

5 participants