Skip to content

Commit cbf9591

Browse files
authored
Merge pull request #2548 from plotly/fix/cap-side-effect
Fix component as props callback triggering other callbacks not in response.
2 parents fa486be + 1612f5a commit cbf9591

File tree

3 files changed

+70
-10
lines changed

3 files changed

+70
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
77
## Fixed
88

99
- [#2545](https://github.com/plotly/dash/pull/2545) Fix typescript objectOf generation.
10+
- [#2548](https://github.com/plotly/dash/pull/2548) Fix component as props callback triggering other callbacks not in response, fix [#2487](https://github.com/plotly/dash/issues/2487).
1011

1112
## [2.10.0] - 2023-05-25
1213

dash/dash-renderer/src/actions/dependencies_ts.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import {
22
all,
3-
any,
43
assoc,
54
concat,
65
difference,
76
filter,
87
flatten,
9-
includes,
108
isEmpty,
119
keys,
1210
map,
@@ -312,13 +310,12 @@ export const getLayoutCallbacks = (
312310
rootId = stringifyId(rootId);
313311
// Filter inputs that are not present in the response
314312
callbacks = callbacks.filter(cb =>
315-
any(
316-
(inp: any) =>
317-
!(
318-
stringifyId(inp.id) === rootId &&
319-
!includes(inp.property, options.filterRoot)
320-
),
321-
cb.callback.inputs
313+
cb.callback.inputs.reduce(
314+
(previous: any, input: any) =>
315+
previous ||
316+
(stringifyId(input.id) == rootId &&
317+
options.filterRoot.includes(input.property)),
318+
false
322319
)
323320
);
324321
}

tests/integration/renderer/test_component_as_prop.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from dash_test_components import ComponentAsProp
66

7-
from dash.dcc import Checklist
7+
from dash.dcc import Checklist, Dropdown
88
from dash.html import Button, Div, Span
99

1010

@@ -379,3 +379,65 @@ def opts(n):
379379

380380
dash_duo.find_elements("#b label > input")[0].click()
381381
dash_duo.wait_for_text_to_equal("#counter", "1")
382+
383+
384+
def test_rdcap004_side_effect_same_component(dash_duo):
385+
options = [
386+
{"label": "aa1", "value": "aa1"},
387+
{"label": "aa2", "value": "aa2"},
388+
{"label": "aa3", "value": "aa3"},
389+
{"label": "best value", "value": "bb1"},
390+
{"label": "better value", "value": "bb2"},
391+
{"label": "bye", "value": "bb3"},
392+
]
393+
394+
app = Dash(__name__)
395+
396+
app.layout = Div(
397+
[
398+
Div(
399+
["Single dynamic Dropdown", Dropdown(id="my-dynamic-dropdown")],
400+
style={"width": 200, "marginLeft": 20, "marginTop": 20},
401+
),
402+
Button(
403+
"Reset",
404+
id="button",
405+
n_clicks=0,
406+
),
407+
Div(0, id="counter"),
408+
]
409+
)
410+
app.clientside_callback(
411+
"function(_, prev) {return parseInt(prev) + 1}",
412+
Output("counter", "children"),
413+
Input("my-dynamic-dropdown", "value"),
414+
State("counter", "children"),
415+
prevent_initial_call=True,
416+
)
417+
418+
@app.callback(
419+
Output("my-dynamic-dropdown", "options"),
420+
Input("my-dynamic-dropdown", "search_value"),
421+
)
422+
def update_options(search_value):
423+
if search_value is None:
424+
return options
425+
return [o for o in options if search_value in o["label"]]
426+
427+
@app.callback(
428+
Output("my-dynamic-dropdown", "value"),
429+
Input("button", "n_clicks"),
430+
)
431+
def on_button(n_clicks):
432+
return None
433+
434+
dash_duo.start_server(app)
435+
436+
# Initial callback
437+
dash_duo.wait_for_text_to_equal("#counter", "1")
438+
439+
search = dash_duo.wait_for_element("#my-dynamic-dropdown input")
440+
441+
search.send_keys("a")
442+
443+
dash_duo.wait_for_text_to_equal("#counter", "1")

0 commit comments

Comments
 (0)