Skip to content

Commit ff858ac

Browse files
HammadTheOnerpkyle
andauthored
Fix setCallbackContext for wildcard and ordinary inputs (#237)
* Update setCallbackContext * Adding graphs test * Slight fix * bump version and update CHANGELOG * Less flaky test Co-authored-by: rpkyle <[email protected]>
1 parent b238a57 commit ff858ac

File tree

4 files changed

+141
-6
lines changed

4 files changed

+141
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
All notable changes to `dash` will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## [0.8.1] - 2020-10-29
6+
### Fixed
7+
- Fixes a minor bug in `setCallbackContext` (described in [#236](https://github.com/plotly/dashR/issues/236)) which prevented pattern-matching callbacks from working properly if one or more `input` statements did not include a selector. [#237](https://github.com/plotly/dashR/pull/237)
8+
59
## [0.8.0] - 2020-10-27
610
### Fixed
711
- Usage of `glue` has been corrected to address [#232](https://github.com/plotly/dashR/issues/232) via [#233](https://github.com/plotly/dashR/pull/233).

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: dash
22
Title: An Interface to the Dash Ecosystem for Authoring Reactive Web Applications
3-
Version: 0.8.0
3+
Version: 0.8.1
44
Authors@R: c(person("Chris", "Parmer", role = c("aut"), email = "[email protected]"), person("Ryan Patrick", "Kyle", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-5829-9867"), email = "[email protected]"), person("Carson", "Sievert", role = c("aut"), comment = c(ORCID = "0000-0002-4958-2844")), person("Hammad", "Khan", role = c("aut"), comment = c(ORCID = "0000-0003-2479-9841"), email = "[email protected]"), person(family = "Plotly Technologies", role = "cph"))
55
Description: A framework for building analytical web applications, Dash offers a pleasant and productive development experience. No JavaScript required.
66
Depends:

R/utils.R

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,26 +1054,30 @@ setCallbackContext <- function(callback_elements) {
10541054
function(x) {
10551055
input_id <- splitIdProp(x)[1]
10561056
prop <- splitIdProp(x)[2]
1057-
1057+
10581058
# The following conditionals check whether the callback is a pattern-matching callback and if it has been triggered.
10591059
if (startsWith(input_id, "{")){
10601060
id_match <- vapply(callback_elements$inputs, function(x) {
10611061
x <- unlist(x)
10621062
any(x[grepl("id.", names(x))] %in% jsonlite::fromJSON(input_id)[[1]])
10631063
}, logical(1))[[1]]
10641064
} else {
1065-
id_match <- vapply(callback_elements$inputs, function(x) x$id %in% input_id, logical(1))
1065+
id_match <- vapply(callback_elements$inputs, function(x) {
1066+
unlist(x)
1067+
any(x$id %in% input_id)}, logical(1))
10661068
}
1067-
1069+
10681070
if (startsWith(input_id, "{")){
10691071
prop_match <- vapply(callback_elements$inputs, function(x) {
10701072
x <- unlist(x)
10711073
any(x[names(x) == "property"] %in% prop)
10721074
}, logical(1))[[1]]
10731075
} else {
1074-
prop_match <- vapply(callback_elements$inputs, function(x) x$property %in% prop, logical(1))
1076+
prop_match <- vapply(callback_elements$inputs, function(x) {
1077+
unlist(x)
1078+
any(x$property %in% prop)}, logical(1))
10751079
}
1076-
1080+
10771081
if (startsWith(input_id, "{")){
10781082
if (length(callback_elements$inputs) == 1 || !is.null(unlist(callback_elements$inputs, recursive = F)$value)) {
10791083
value <- sapply(callback_elements$inputs[id_match & prop_match], `[[`, "value")

tests/integration/callbacks/test_pattern_matching.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,120 @@
318318
"""
319319

320320

321+
graphs_app = """
322+
library(dash)
323+
library(dashHtmlComponents)
324+
library(dashCoreComponents)
325+
library(plotly)
326+
327+
df <- read.csv(
328+
file = "https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv",
329+
stringsAsFactor=FALSE,
330+
check.names=FALSE
331+
)
332+
333+
app <- Dash$new()
334+
335+
app$layout(
336+
htmlDiv(
337+
list(
338+
htmlDiv(
339+
list(
340+
dccDropdown(options = lapply(unique(df[,"country"]), function(x) {
341+
list(label = x, value = x)
342+
}),
343+
value = "Canada",
344+
id = "country",
345+
style = list(display = "inline-block",
346+
width = 200)
347+
),
348+
htmlButton(
349+
"add Chart",
350+
id = "add-chart",
351+
n_clicks = 0,
352+
style = list(display = "inline-block")
353+
)
354+
)
355+
),
356+
htmlDiv(id = "container", children=list()),
357+
htmlDiv(id = "output-delay")
358+
)
359+
)
360+
)
361+
362+
create_figure <- function(df, column_x, column_y, country) {
363+
df <- df[which(df[, "country"] == country),]
364+
if (column_x == "year") {
365+
fig <- plot_ly(df, x = df[,column_x], y = df[,column_y], name = column_x, type = "scatter", mode = "lines")
366+
} else {
367+
fig <- plot_ly(df, x = df[,column_x], y = df[,column_y], name = column_x, type = "scatter", mode = "markers")
368+
}
369+
fig <- plotly::layout(fig, plot_bgcolor="lightblue", xaxis = list(title=""),
370+
yaxis = list(title=""), title=list(text=paste(country, column_y, "vs", column_x),
371+
xanchor="right", margin_l=10, margin_r=0, margin_b=30))
372+
return(fig)
373+
}
374+
375+
app$callback(
376+
output = list(
377+
output(id = "container", property = "children"),
378+
output(id = "output-delay", property = "children")
379+
),
380+
params = list(
381+
input(id = "add-chart", property = "n_clicks"),
382+
state(id = "country", property = "value"),
383+
state(id = "container", property = "children")
384+
),
385+
function(n_clicks, country, children) {
386+
default_column_x <- "year"
387+
default_column_y <- "gdpPercap"
388+
389+
new_element <- htmlDiv(
390+
style = list(width = "23%", display = "inline-block", outline = "thin lightgrey solid", padding = 10),
391+
children = list(
392+
dccGraph(
393+
id = list(type = "dynamic-output", index = n_clicks),
394+
style = list(height = 300),
395+
figure = create_figure(df, default_column_x, default_column_y, country)
396+
),
397+
dccDropdown(
398+
id = list(type = "dynamic-dropdown-x", index = n_clicks),
399+
options = lapply(colnames(df), function(x) {
400+
list(label = x, value = x)
401+
}),
402+
value = default_column_x
403+
),
404+
dccDropdown(
405+
id = list(type = "dynamic-dropdown-y", index = n_clicks),
406+
options = lapply(colnames(df), function(x) {
407+
list(label = x, value = x)
408+
}),
409+
value = default_column_y
410+
)
411+
)
412+
)
413+
414+
children <- c(children, list(new_element))
415+
return(list(children, n_clicks))
416+
}
417+
)
418+
419+
app$callback(
420+
output(id = list("index" = MATCH, "type" = "dynamic-output"), property = "figure"),
421+
params = list(
422+
input(id = list("index" = MATCH, "type" = "dynamic-dropdown-x"), property = "value"),
423+
input(id = list("index" = MATCH, "type" = "dynamic-dropdown-y"), property = "value"),
424+
input(id = "country", property = "value")
425+
),
426+
function(column_x, column_y, country) {
427+
return(create_figure(df, column_x, column_y, country))
428+
}
429+
)
430+
431+
app$run_server()
432+
"""
433+
434+
321435
def test_rpmc001_pattern_matching_all(dashr):
322436
dashr.start_server(all_app)
323437
dashr.find_element("#add-filter").click()
@@ -370,3 +484,16 @@ def test_rpmc004_pattern_matching_todo(dashr):
370484
dashr.find_element("#add").click()
371485
dashr.find_element('#\\{\\"index\\"\\:1\\,\\"type\\"\\:\\"done\\"\\}').click()
372486
assert dashr.wait_for_text_to_equal("#totals", "1 of 1 items completed - 100%")
487+
488+
489+
def test_rpmc005_pattern_matching_graphs(dashr):
490+
dashr.start_server(graphs_app)
491+
dashr.select_dcc_dropdown("#country", "Cameroon")
492+
dashr.wait_for_text_to_equal("#output-delay", "0")
493+
dashr.find_element("#add-chart").click()
494+
dashr.wait_for_text_to_equal("#output-delay", "1")
495+
dashr.find_element('#\\{\\"index\\"\\:1\\,\\"type\\"\\:\\"dynamic-output\\"\\}')
496+
dashr.select_dcc_dropdown('#\\{\\"index\\"\\:1\\,\\"type\\"\\:\\"dynamic-dropdown-x\\"\\}', "year")
497+
dashr.select_dcc_dropdown('#\\{\\"index\\"\\:1\\,\\"type\\"\\:\\"dynamic-dropdown-y\\"\\}', "pop")
498+
dashr.percy_snapshot("r-pmc-graphs")
499+
dashr.wait_for_element('#\\{\\"index\\"\\:1\\,\\"type\\"\\:\\"dynamic-output\\"\\}')

0 commit comments

Comments
 (0)