Skip to content

Commit 177bc08

Browse files
committed
Add outputs and outputs_list to window.dash_clientside.callback_context
1 parent b1c6efb commit 177bc08

File tree

2 files changed

+263
-1
lines changed

2 files changed

+263
-1
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ async function handleClientside(
241241
});
242242
}
243243

244-
const {inputs, outputs, state} = payload;
244+
const {inputs, output, outputs, state} = payload;
245245
const requestTime = Date.now();
246246

247247
const inputDict = inputsToDict(inputs);
@@ -269,6 +269,11 @@ async function handleClientside(
269269
dc.callback_context.inputs = inputDict;
270270
dc.callback_context.states_list = state;
271271
dc.callback_context.states = stateDict;
272+
dc.callback_context.outputs_list =
273+
Object.prototype.toString.call(outputs) === '[object Object]'
274+
? [outputs]
275+
: outputs;
276+
dc.callback_context.outputs = output;
272277

273278
let returnValue = dc[namespace][function_name](...args);
274279

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
from dash import (
2+
Dash,
3+
Input,
4+
Output,
5+
html,
6+
clientside_callback,
7+
)
8+
9+
10+
def test_clol001_clientside_outputs_list_by_single_output(dash_duo):
11+
app = Dash(__name__)
12+
app.layout = html.Div(
13+
[html.Button("trigger", id="trigger-demo"), html.Pre(id="output-demo")],
14+
style={"padding": 50},
15+
)
16+
17+
clientside_callback(
18+
"""(n_clicks) => {
19+
return JSON.stringify(window.dash_clientside.callback_context.outputs_list);
20+
}""",
21+
Output("output-demo", "children"),
22+
Input("trigger-demo", "n_clicks"),
23+
prevent_initial_call=True,
24+
)
25+
26+
dash_duo.start_server(app)
27+
28+
trigger_clicker = dash_duo.wait_for_element("#trigger-demo")
29+
trigger_clicker.click()
30+
dash_duo.wait_for_text_to_equal(
31+
"#output-demo",
32+
'[{"id":"output-demo","property":"children"}]',
33+
)
34+
35+
36+
def test_clol002_clientside_outputs_list_by_multiple_output1(dash_duo):
37+
app = Dash(__name__)
38+
app.layout = html.Div(
39+
[
40+
html.Button("trigger", id="trigger-demo"),
41+
html.Pre(id="output-demo1"),
42+
html.Pre(id="output-demo2"),
43+
],
44+
style={"padding": 50},
45+
)
46+
47+
clientside_callback(
48+
"""(n_clicks) => {
49+
return [JSON.stringify(window.dash_clientside.callback_context.outputs_list), JSON.stringify(window.dash_clientside.callback_context.outputs_list)];
50+
}""",
51+
[Output("output-demo1", "children"), Output("output-demo2", "children")],
52+
Input("trigger-demo", "n_clicks"),
53+
prevent_initial_call=True,
54+
)
55+
56+
dash_duo.start_server(app)
57+
58+
dash_duo.find_element("#trigger-demo").click()
59+
dash_duo.wait_for_text_to_equal(
60+
"#output-demo1",
61+
'[{"id":"output-demo1","property":"children"},{"id":"output-demo2","property":"children"}]',
62+
)
63+
dash_duo.wait_for_text_to_equal(
64+
"#output-demo2",
65+
'[{"id":"output-demo1","property":"children"},{"id":"output-demo2","property":"children"}]',
66+
)
67+
68+
69+
def test_clol003_clientside_outputs_list_by_multiple_output2(dash_duo):
70+
app = Dash(__name__)
71+
app.layout = html.Div(
72+
[
73+
html.Button("trigger1", id="trigger-demo1"),
74+
html.Button("trigger2", id="trigger-demo2"),
75+
html.Pre(id="output-demo1"),
76+
html.Pre(id="output-demo2"),
77+
],
78+
style={"padding": 50},
79+
)
80+
81+
clientside_callback(
82+
"""(n_clicks1, n_clicks2) => {
83+
if (window.dash_clientside.callback_context.triggered_id === 'trigger-demo1') {
84+
return [JSON.stringify(window.dash_clientside.callback_context.outputs_list), window.dash_clientside.no_update];
85+
} else if (window.dash_clientside.callback_context.triggered_id === 'trigger-demo2') {
86+
return [window.dash_clientside.no_update, JSON.stringify(window.dash_clientside.callback_context.outputs_list)];
87+
}
88+
return [window.dash_clientside.no_update, window.dash_clientside.no_update];
89+
}""",
90+
[Output("output-demo1", "children"), Output("output-demo2", "children")],
91+
[Input("trigger-demo1", "n_clicks"), Input("trigger-demo2", "n_clicks")],
92+
prevent_initial_call=True,
93+
)
94+
95+
dash_duo.start_server(app)
96+
97+
dash_duo.find_element("#trigger-demo1").click()
98+
dash_duo.wait_for_text_to_equal(
99+
"#output-demo1",
100+
'[{"id":"output-demo1","property":"children"},{"id":"output-demo2","property":"children"}]',
101+
)
102+
dash_duo.find_element("#trigger-demo2").click()
103+
dash_duo.wait_for_text_to_equal(
104+
"#output-demo2",
105+
'[{"id":"output-demo1","property":"children"},{"id":"output-demo2","property":"children"}]',
106+
)
107+
108+
109+
def test_clol004_clientside_outputs_list_by_no_output(dash_duo):
110+
app = Dash(__name__)
111+
app.layout = html.Div(
112+
[html.Button("trigger", id="trigger-demo"), html.Pre(id="output-demo")],
113+
style={"padding": 50},
114+
)
115+
116+
clientside_callback(
117+
"""(n_clicks) => {
118+
window.dash_clientside.set_props('output-demo', {'children': JSON.stringify(window.dash_clientside.callback_context.outputs_list)});
119+
}""",
120+
Input("trigger-demo", "n_clicks"),
121+
prevent_initial_call=True,
122+
)
123+
124+
dash_duo.start_server(app)
125+
126+
dash_duo.find_element("#trigger-demo").click()
127+
dash_duo.wait_for_text_to_equal(
128+
"#output-demo",
129+
"",
130+
)
131+
132+
133+
def test_clo001_clientside_outputs_by_single_output(dash_duo):
134+
app = Dash(__name__)
135+
app.layout = html.Div(
136+
[html.Button("trigger", id="trigger-demo"), html.Pre(id="output-demo")],
137+
style={"padding": 50},
138+
)
139+
140+
clientside_callback(
141+
"""(n_clicks) => {
142+
return window.dash_clientside.callback_context.outputs;
143+
}""",
144+
Output("output-demo", "children"),
145+
Input("trigger-demo", "n_clicks"),
146+
prevent_initial_call=True,
147+
)
148+
149+
dash_duo.start_server(app)
150+
151+
trigger_clicker = dash_duo.wait_for_element("#trigger-demo")
152+
trigger_clicker.click()
153+
dash_duo.wait_for_text_to_equal(
154+
"#output-demo",
155+
"output-demo.children",
156+
)
157+
158+
159+
def test_clo002_clientside_outputs_by_multiple_output1(dash_duo):
160+
app = Dash(__name__)
161+
app.layout = html.Div(
162+
[
163+
html.Button("trigger", id="trigger-demo"),
164+
html.Pre(id="output-demo1"),
165+
html.Pre(id="output-demo2"),
166+
],
167+
style={"padding": 50},
168+
)
169+
170+
clientside_callback(
171+
"""(n_clicks) => {
172+
return [window.dash_clientside.callback_context.outputs, window.dash_clientside.callback_context.outputs];
173+
}""",
174+
[Output("output-demo1", "children"), Output("output-demo2", "children")],
175+
Input("trigger-demo", "n_clicks"),
176+
prevent_initial_call=True,
177+
)
178+
179+
dash_duo.start_server(app)
180+
181+
dash_duo.find_element("#trigger-demo").click()
182+
dash_duo.wait_for_text_to_equal(
183+
"#output-demo1",
184+
"..output-demo1.children...output-demo2.children..",
185+
)
186+
dash_duo.wait_for_text_to_equal(
187+
"#output-demo2",
188+
"..output-demo1.children...output-demo2.children..",
189+
)
190+
191+
192+
def test_clo003_clientside_outputs_by_multiple_output2(dash_duo):
193+
app = Dash(__name__)
194+
app.layout = html.Div(
195+
[
196+
html.Button("trigger1", id="trigger-demo1"),
197+
html.Button("trigger2", id="trigger-demo2"),
198+
html.Pre(id="output-demo1"),
199+
html.Pre(id="output-demo2"),
200+
],
201+
style={"padding": 50},
202+
)
203+
204+
clientside_callback(
205+
"""(n_clicks1, n_clicks2) => {
206+
if (window.dash_clientside.callback_context.triggered_id === 'trigger-demo1') {
207+
return [window.dash_clientside.callback_context.outputs, window.dash_clientside.no_update];
208+
} else if (window.dash_clientside.callback_context.triggered_id === 'trigger-demo2') {
209+
return [window.dash_clientside.no_update, window.dash_clientside.callback_context.outputs];
210+
}
211+
return [window.dash_clientside.no_update, window.dash_clientside.no_update];
212+
}""",
213+
[Output("output-demo1", "children"), Output("output-demo2", "children")],
214+
[Input("trigger-demo1", "n_clicks"), Input("trigger-demo2", "n_clicks")],
215+
prevent_initial_call=True,
216+
)
217+
218+
dash_duo.start_server(app)
219+
220+
dash_duo.find_element("#trigger-demo1").click()
221+
dash_duo.wait_for_text_to_equal(
222+
"#output-demo1",
223+
"..output-demo1.children...output-demo2.children..",
224+
)
225+
dash_duo.find_element("#trigger-demo2").click()
226+
dash_duo.wait_for_text_to_equal(
227+
"#output-demo2",
228+
"..output-demo1.children...output-demo2.children..",
229+
)
230+
231+
232+
def test_clo004_clientside_outputs_by_no_output(dash_duo):
233+
app = Dash(__name__)
234+
app.layout = html.Div(
235+
[html.Button("trigger", id="trigger-demo"), html.Pre(id="output-demo")],
236+
style={"padding": 50},
237+
)
238+
239+
clientside_callback(
240+
"""(n_clicks) => {
241+
if (window.dash_clientside.callback_context.outputs) {
242+
window.dash_clientside.set_props('output-demo', {'children': 'success-outputs'});
243+
} else {
244+
window.dash_clientside.set_props('output-demo', {'children': 'error-outputs'});
245+
}
246+
}""",
247+
Input("trigger-demo", "n_clicks"),
248+
prevent_initial_call=True,
249+
)
250+
251+
dash_duo.start_server(app)
252+
253+
dash_duo.find_element("#trigger-demo").click()
254+
dash_duo.wait_for_text_to_equal(
255+
"#output-demo",
256+
"success-outputs",
257+
)

0 commit comments

Comments
 (0)