diff --git a/.circleci/config.yml b/.circleci/config.yml index 1938c119..677d90bf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,6 +24,7 @@ jobs: echo "RUNNING JOB: ${CIRCLE_JOB}" echo "JOB PARALLELISM: ${CIRCLE_NODE_TOTAL}" echo "CIRCLE_REPOSITORY_URL: ${CIRCLE_REPOSITORY_URL}" + export PERCY_ENABLE=0 echo $CIRCLE_JOB > circlejob.txt git rev-parse HEAD | tr -d '\n' > commit.txt diff --git a/R/dash.R b/R/dash.R index 48db012c..3f293ee9 100644 --- a/R/dash.R +++ b/R/dash.R @@ -399,7 +399,6 @@ Dash <- R6::R6Class( dash_update <- paste0(self$config$routes_pathname_prefix, "_dash-update-component") route$add_handler("post", dash_update, function(request, response, keys, ...) { request <- request_parse_json(request) - if (!"output" %in% names(request$body)) { response$body <- "Couldn't find output component in request body" response$status <- 500L @@ -474,7 +473,7 @@ Dash <- R6::R6Class( if (substr(request$body$output, 1, 2) == '..') { # omit return objects of class "no_update" from output_value - updatable_outputs <- "no_update" != vapply(output_value, class, character(1)) + updatable_outputs <- vapply(output_value, function(x) !("no_update" %in% class(x)), logical(1)) output_value <- output_value[updatable_outputs] # if multi-output callback, isolate the output IDs and properties diff --git a/tests/integration/callbacks/test_multiple_outputs_return_callback.py b/tests/integration/callbacks/test_multiple_outputs_return_callback.py new file mode 100644 index 00000000..8e1f5815 --- /dev/null +++ b/tests/integration/callbacks/test_multiple_outputs_return_callback.py @@ -0,0 +1,61 @@ +app_returning_component = """ +library(dash) +library(dashHtmlComponents) +library(dashCoreComponents) + +app <- Dash$new() +app$layout( + htmlDiv(list( + htmlH1('Multi-outputs with callback returning component'), + htmlDiv(children = 'Click button to render or remove component here', id = 'inner-container'), + htmlButton('Render/Unrender', id='submit-val', n_clicks=0), + htmlDiv(children = 'Button clicked 0 times.', id = 'clicks-count') + ), + id = 'container' + ) +) + +app$callback(output=list( + output(id='clicks-count', property='children'), + output(id='inner-container', property='children') +), +params=list( + input(id='submit-val', property='n_clicks') +), +function(value) { + click_total <- as.numeric(value) + result <- htmlDiv('String in container', id = 'string-container') + + if (click_total == 0) { + return(dashNoUpdate()) + } else if (click_total %% 2 == 1) { + result <- 'String, no container' + } + + return(list(sprintf('Button clicked %s times.', click_total), + result)) +} +) +app$run_server(debug=TRUE) +""" + + +def test_rsnu002_multiple_outputs(dashr): + dashr.start_server(app_returning_component) + dashr.wait_for_text_to_equal( + "#inner-container", + "Click button to render or remove component here" + ) + dashr.find_element("#submit-val").click() + dashr.wait_for_text_to_equal( + "#clicks-count", + "Button clicked 1 times." + ) + assert dashr.find_element("#inner-container").text == "String, no container" + dashr.find_element("#submit-val").click() + dashr.wait_for_text_to_equal( + "#clicks-count", + "Button clicked 2 times." + ) + assert dashr.find_element("#string-container").text == "String in container" +