diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 59e819a2a0..978c5b3a33 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -336,33 +336,26 @@ srv_teal_module.teal_module <- function(id, data = data, is_active = is_active ) - is_transform_failed <- reactiveValues() transformed_teal_data <- srv_transform_teal_data( "data_transform", data = filtered_teal_data, transformators = modules$transformators, - modules = modules, - is_transform_failed = is_transform_failed + modules = modules ) any_transform_failed <- reactive({ - any(unlist(reactiveValuesToList(is_transform_failed))) + !inherits(try(transformed_teal_data(), silent = TRUE), "teal_data") && + inherits(filtered_teal_data(), "teal_data") }) observeEvent(any_transform_failed(), { - if (isTRUE(any_transform_failed())) { - shinyjs::hide("teal_module_ui") - shinyjs::show("transform_failure_info") - } else { - shinyjs::show("teal_module_ui") - shinyjs::hide("transform_failure_info") - } + shinyjs::toggleElement("transform_failure_info", condition = any_transform_failed()) }) - module_teal_data <- reactive({ - req(inherits(transformed_teal_data(), "teal_data")) - all_teal_data <- transformed_teal_data() - module_datanames <- .resolve_module_datanames(data = all_teal_data, modules = modules) - all_teal_data[c(module_datanames, ".raw_data")] + summary_data <- reactiveVal(NULL) + module_teal_data <- eventReactive(transformed_teal_data(), { + module_datanames <- .resolve_module_datanames(data = transformed_teal_data(), modules = modules) + summary_data(transformed_teal_data()[c(module_datanames, ".raw_data")]) + summary_data() }) srv_check_module_datanames( @@ -371,7 +364,7 @@ srv_teal_module.teal_module <- function(id, modules = modules ) - summary_table <- srv_data_summary("data_summary", module_teal_data) + summary_table <- srv_data_summary("data_summary", summary_data) # Only updates on success observeEvent(input$data_summary_toggle, { bslib::toggle_sidebar(id = "teal_module_sidebar", open = TRUE) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 26b49f0b47..7a82c9322e 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -37,9 +37,19 @@ ui_transform_teal_data <- function(id, transformators, class = "well") { display_fun( bslib::accordion( + id = ns("wrapper"), + class = "validation-wrapper", bslib::accordion_panel( + id = ns("wrapper_panel"), + class = "validation-panel", attr(data_mod, "label"), icon = bsicons::bs_icon("palette-fill"), + tags$div( + class = "disabled-info", + title = "Disabled until data becomes valid", + bsicons::bs_icon("info-circle"), + "Disabled until data becomes valid. Check your inputs." + ), tags$div( id = transform_wrapper_id, if (is.null(data_mod$ui)) { @@ -62,7 +72,7 @@ ui_transform_teal_data <- function(id, transformators, class = "well") { #' @export #' @rdname module_transform_data -srv_transform_teal_data <- function(id, data, transformators, modules = NULL, is_transform_failed = reactiveValues()) { +srv_transform_teal_data <- function(id, data, transformators, modules = NULL) { checkmate::assert_string(id) assert_reactive(data) checkmate::assert_class(modules, "teal_module", null.ok = TRUE) @@ -76,39 +86,47 @@ srv_transform_teal_data <- function(id, data, transformators, modules = NULL, is names(transformators) <- sprintf("transform_%d", seq_len(length(transformators))) moduleServer(id, function(input, output, session) { + data_original_handled <- reactive(tryCatch(data(), error = function(e) e)) module_output <- Reduce( function(data_previous, name) { moduleServer(name, function(input, output, session) { logger::log_debug("srv_transform_teal_data@1 initializing module for { name }.") - data_out <- reactiveVal() + + # Disable all elements if original data is not yet a teal_data + observeEvent(data_original_handled(), { + shinyjs::toggleState( + "wrapper_panel", + condition = inherits(data_original_handled(), "teal_data") + ) + }) + .call_once_when(inherits(data_previous(), "teal_data"), { logger::log_debug("srv_teal_transform_teal_data@2 triggering a transform module call for { name }.") data_unhandled <- transformators[[name]]$server("transform", data = data_previous) data_handled <- reactive(tryCatch(data_unhandled(), error = function(e) e)) - observeEvent(data_handled(), { - if (inherits(data_handled(), "teal_data")) { - if (!identical(data_handled(), data_out())) { + observeEvent( + { + data_handled() + data_original_handled() + }, + { + if (inherits(data_original_handled(), "condition")) { + data_out(data_original_handled()) + } else if (inherits(data_handled(), "teal_data")) { + if (!identical(data_handled(), data_out())) { + data_out(data_handled()) + } + } else { data_out(data_handled()) } } - }) - - is_transform_failed[[name]] <- FALSE - observeEvent(data_handled(), { - if (inherits(data_handled(), "teal_data")) { - is_transform_failed[[name]] <- FALSE - } else { - is_transform_failed[[name]] <- TRUE - } - }) + ) is_previous_failed <- reactive({ - idx_this <- which(names(is_transform_failed) == name) - is_transform_failed_list <- reactiveValuesToList(is_transform_failed) - idx_failures <- which(unlist(is_transform_failed_list)) - any(idx_failures < idx_this) + inherits(data_original_handled(), "teal_data") && + !inherits(try(data_previous(), silent = TRUE), "teal_data") }) srv_validate_error("silent_error", data_handled, validate_shiny_silent_error = FALSE) @@ -118,6 +136,7 @@ srv_transform_teal_data <- function(id, data, transformators, modules = NULL, is } # When there is no UI (`ui = NULL`) it should still show the errors + # It is a 1-way operation as there is no UI to correct the state observe({ if (!inherits(data_handled(), "teal_data") && !is_previous_failed()) { shinyjs::show("wrapper") @@ -132,7 +151,7 @@ srv_transform_teal_data <- function(id, data, transformators, modules = NULL, is "One of previous transformators failed. Please check its inputs.", class = "teal-output-warning" ) - } else { + } else if (inherits(data_original_handled(), "teal_data")) { shinyjs::enable(transform_wrapper_id) shiny::tagList( ui_validate_error(session$ns("silent_error")), @@ -145,7 +164,8 @@ srv_transform_teal_data <- function(id, data, transformators, modules = NULL, is # Ignoring unwanted reactivity breaks during initialization reactive({ - req(data_out()) + validate(need(!inherits(data_out(), "condition"), message = data_out()$message)) # rethrow message + data_out() }) }) }, diff --git a/inst/css/validation.css b/inst/css/validation.css index 665fac2d73..1734e73fe5 100644 --- a/inst/css/validation.css +++ b/inst/css/validation.css @@ -47,3 +47,31 @@ border: 1px solid red; padding: 1em; } + +.validation-wrapper:has(.validation-panel[disabled="disabled"]) { + --bs-accordion-bg: var(--bs-gray-100); + background-color: var(--bs-gray-100); +} + +.validation-wrapper:has(.validation-panel[disabled="disabled"]) .teal_validated, +.validation-wrapper:has(.validation-panel[disabled="disabled"]) .shiny-output-error, +.validation-wrapper:has(.validation-panel[disabled="disabled"]) .teal-output-warning { + display: none; +} + +.validation-wrapper .disabled-info { + display: none; +} + +.validation-wrapper .validation-panel[disabled="disabled"] { + padding-top: 0; +} + +.validation-wrapper .validation-panel[disabled="disabled"] .disabled-info { + display: block; + border: 1px solid var(--bs-info); + border-radius: 4px; + padding: 1em; + color: color-mix(in srgb, var(--bs-info), black 20%); + background-color: color-mix(in srgb, var(--bs-info) 5%, transparent); +} diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index fca1273403..28386f13d3 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -8,13 +8,7 @@ \usage{ ui_transform_teal_data(id, transformators, class = "well") -srv_transform_teal_data( - id, - data, - transformators, - modules = NULL, - is_transform_failed = reactiveValues() -) +srv_transform_teal_data(id, data, transformators, modules = NULL) } \arguments{ \item{id}{(\code{character(1)}) \code{shiny} module instance id.} @@ -27,10 +21,6 @@ To learn more check \code{vignette("transform-input-data", package = "teal")}.} \item{data}{(\code{reactive} returning \code{teal_data})} \item{modules}{(\code{teal_modules} or \code{teal_module}) For \code{datanames} validation purpose} - -\item{is_transform_failed}{(\code{reactiveValues}) contains \code{logical} flags named after each transformator. -Help to determine if any previous transformator failed, so that following transformators can be disabled -and display a generic failure message.} } \value{ \code{reactive} \code{teal_data} diff --git a/teal.Rproj b/teal.Rproj index ab99014abb..876e0b7f96 100644 --- a/teal.Rproj +++ b/teal.Rproj @@ -1,4 +1,5 @@ Version: 1.0 +ProjectId: fa9b6a6c-a470-42a8-bc1a-165563e5ac01 RestoreWorkspace: Default SaveWorkspace: Default