From b74d1f605755ade2419e51bfa1aa9d5f1213af07 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:07:53 +0200 Subject: [PATCH 001/165] WIP --- DESCRIPTION | 1 + NAMESPACE | 1 + R/decorate.R | 37 ++++++++++++++ man/decorate_teal_data.Rd | 11 ++++ vignettes/decorate-modules-output.Rmd | 73 +++++++++++++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 R/decorate.R create mode 100644 man/decorate_teal_data.Rd create mode 100644 vignettes/decorate-modules-output.Rmd diff --git a/DESCRIPTION b/DESCRIPTION index 0b307d3b26..ccd3c67639 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -90,6 +90,7 @@ RoxygenNote: 7.3.2 Collate: 'TealAppDriver.R' 'checkmate.R' + 'decorate.R' 'dummy_functions.R' 'get_rcode_utils.R' 'include_css_js.R' diff --git a/NAMESPACE b/NAMESPACE index a42856456d..64ec20abbc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -18,6 +18,7 @@ export(TealReportCard) export(as.teal_slices) export(as_tdata) export(build_app_title) +export(decorate_teal_data) export(example_module) export(get_code_tdata) export(get_metadata) diff --git a/R/decorate.R b/R/decorate.R new file mode 100644 index 0000000000..8de10e3495 --- /dev/null +++ b/R/decorate.R @@ -0,0 +1,37 @@ +#' Modify object in teal_data environment +#' @export +setGeneric( + "decorate_teal_data", + function(ui = function(id) NULL, server, expr) { + standardGeneric("decorate_teal_data") + } +) + +setMethod( + "decorate_teal_data", + signature = list(server = "function"), + function(ui = function(id) NULL, server, expr) { + if (!missing(expr)) { + stop("expr argument ignored when ui and server are specified") + } + teal_transform_module(ui, server) + } +) + + +setMethod( # comment: this could be teal_transform_module method + "decorate_teal_data", + signature = list(expr = "language"), + function(ui = function(id) NULL, server, expr) { + decorate_teal_data( + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + data() |> eval_code(expr) + }) + }) + } + ) + } +) diff --git a/man/decorate_teal_data.Rd b/man/decorate_teal_data.Rd new file mode 100644 index 0000000000..e5b39a1753 --- /dev/null +++ b/man/decorate_teal_data.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/decorate.R +\name{decorate_teal_data} +\alias{decorate_teal_data} +\title{Modify object in teal_data environment} +\usage{ +decorate_teal_data(ui = function(id) NULL, server, expr) +} +\description{ +Modify object in teal_data environment +} diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd new file mode 100644 index 0000000000..d20c7d0363 --- /dev/null +++ b/vignettes/decorate-modules-output.Rmd @@ -0,0 +1,73 @@ +--- +title: "Decorate modules output" +author: "NEST CoreDev" +output: + rmarkdown::html_vignette: + toc: true +vignette: > + %\VignetteIndexEntry{Decorate modules output} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```r +tm_decorated_plot <- function(label = "module", decorator = identity_decorator) { + # decorator can be "decorate_module", "expr", "function" + decorator_obj <- new_decorator(decorator, output_name = "plot") + + module( + label = label, + ui = function(id, decorator) { + ns <- NS(id) + div( + selectInput(ns("dataname"), label = "select dataname", choices = NULL), + selectInput(ns("x"), label = "select x", choices = NULL), + selectInput(ns("y"), label = "select y", choices = NULL), + decorator$ui(ns("decorate")), + plotOutput(ns("plot")), + verbatimTextOutput(ns("text")) + ) + }, + server = function(id, data, decorator) { + moduleServer(id, function(input, output, session) { + observeEvent(data(), { + updateSelectInput(inputId = "dataname", choices = teal.data::datanames(data())) + }) + + observeEvent(input$dataname, { + updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) + updateSelectInput(inputId = "y", label = "select y", choices = colnames(data()[[input$dataname]])) + }) + + q1 <- reactive({ + req(input$dataname, input$x, input$y) + data() |> + within( + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }) + + q2 <- decorator$server("decorate", data = q1) + + plot_r <- reactive({ + req(q2()) + q2()[["plot"]] + }) + + output$plot <- renderPlot(plot_r()) + output$text <- renderText({ + teal.code::get_code(q2()) + }) + }) + }, + ui_args = list(decorator = decorator), + server_args = list(decorator = decorator) + ) +} +``` \ No newline at end of file From 6835c188942c9a9f0d0f63258cf60abc417f3b5a Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:25:55 +0200 Subject: [PATCH 002/165] init --- R/decorate.R | 24 ++++++++ vignettes/decorate-modules-output.Rmd | 81 +++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/R/decorate.R b/R/decorate.R index 8de10e3495..81f05613f3 100644 --- a/R/decorate.R +++ b/R/decorate.R @@ -7,6 +7,14 @@ setGeneric( } ) +setMethod( + "decorate_teal_data", + signature = list(server = "missing", expr = "missing"), + function(ui = function(id) NULL, server, expr) { + teal_transform_module(ui, server = function(id, data) data) + } +) + setMethod( "decorate_teal_data", signature = list(server = "function"), @@ -18,6 +26,22 @@ setMethod( } ) +setMethod( # comment: this could be teal_transform_module method + "decorate_teal_data", + signature = list(expr = "language"), + function(ui = function(id) NULL, server, expr) { + decorate_teal_data( + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + data() |> eval_code(expr) + }) + }) + } + ) + } +) setMethod( # comment: this could be teal_transform_module method "decorate_teal_data", diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index d20c7d0363..03bee6fbf7 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -10,11 +10,9 @@ vignette: > %\VignetteEncoding{UTF-8} --- -```r -tm_decorated_plot <- function(label = "module", decorator = identity_decorator) { - # decorator can be "decorate_module", "expr", "function" - decorator_obj <- new_decorator(decorator, output_name = "plot") - +```{r} +library(ggplot2) +tm_decorated_plot <- function(label = "module", decorator = decorate_teal_data()) { module( label = label, ui = function(id, decorator) { @@ -70,4 +68,77 @@ tm_decorated_plot <- function(label = "module", decorator = identity_decorator) server_args = list(decorator = decorator) ) } +``` + + +Decorator without `ui`. +```{r} +static_decorator <- decorate_teal_data( + ui = function(id) NULL, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + data() |> within({ + plot <- plot + + ggtitle("This is title") + + xlab("x axis") + }) + }) + }) + } +) +``` + +Static decorator simplified constructor. +```{r} +static_decorator_from_expr <- decorate_teal_data( + expr = quote(plot <- plot + + ggtitle("This is title") + + xlab("x axis title")) +) +``` + + +Interactive decorator +```{r} +interactive_decorator <- decorate_teal_data( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + data() |> within( + { + plot <- plot + + ggtitle("This is title") + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title + ) + }) + }) + } +) +``` + +```{r} +app <- init( + data = teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot("identity", decorator = decorate_teal_data()), + tm_decorated_plot("static", decorator = static_decorator), + tm_decorated_plot("static", decorator = static_decorator_from_expr), + tm_decorated_plot("interactive", decorator = interactive_decorator) + ) +) + +if (interactive()) { + shinyApp(app$ui, app$server) +} ``` \ No newline at end of file From 9fc5923c229c785c2d5ef17a25aae5351200323a Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 12:13:27 +0200 Subject: [PATCH 003/165] WIP --- R/decorate.R | 51 +++++++++------------- man/decorate_teal_data.Rd | 2 +- vignettes/decorate-modules-output.Rmd | 61 ++++++++++++++++++++------- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/R/decorate.R b/R/decorate.R index 81f05613f3..f3a4516964 100644 --- a/R/decorate.R +++ b/R/decorate.R @@ -2,40 +2,27 @@ #' @export setGeneric( "decorate_teal_data", - function(ui = function(id) NULL, server, expr) { + function(x, output_name) { standardGeneric("decorate_teal_data") } ) setMethod( "decorate_teal_data", - signature = list(server = "missing", expr = "missing"), - function(ui = function(id) NULL, server, expr) { - teal_transform_module(ui, server = function(id, data) data) - } + signature = list(x = "teal_transform_module"), + function(x, output_name) x ) setMethod( "decorate_teal_data", - signature = list(server = "function"), - function(ui = function(id) NULL, server, expr) { - if (!missing(expr)) { - stop("expr argument ignored when ui and server are specified") - } - teal_transform_module(ui, server) - } -) - -setMethod( # comment: this could be teal_transform_module method - "decorate_teal_data", - signature = list(expr = "language"), - function(ui = function(id) NULL, server, expr) { - decorate_teal_data( + signature = list(x = "language"), + function(x, output_name) { + teal_transform_module( server = function(id, data) { moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> eval_code(expr) + eval_code(data(), code = x) }) }) } @@ -43,19 +30,19 @@ setMethod( # comment: this could be teal_transform_module method } ) -setMethod( # comment: this could be teal_transform_module method +setMethod( "decorate_teal_data", - signature = list(expr = "language"), - function(ui = function(id) NULL, server, expr) { - decorate_teal_data( - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - data() |> eval_code(expr) - }) - }) - } + signature = list(x = "function"), + function(x, output_name) { + checkmate::check_string(output_name) + formal_args <- names(formals(x)) # how the args are named is here + expr <- do.call( + substitute, + list( + expr = body(x), + env = setNames(list(as.name(output_name)), formal_args[[1]]) + ) ) + decorate_teal_data(x = expr) } ) diff --git a/man/decorate_teal_data.Rd b/man/decorate_teal_data.Rd index e5b39a1753..090c421c43 100644 --- a/man/decorate_teal_data.Rd +++ b/man/decorate_teal_data.Rd @@ -4,7 +4,7 @@ \alias{decorate_teal_data} \title{Modify object in teal_data environment} \usage{ -decorate_teal_data(ui = function(id) NULL, server, expr) +decorate_teal_data(x, output_name) } \description{ Modify object in teal_data environment diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 03bee6fbf7..acfe2e122a 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -12,7 +12,10 @@ vignette: > ```{r} library(ggplot2) -tm_decorated_plot <- function(label = "module", decorator = decorate_teal_data()) { +tm_decorated_plot <- function(label = "module", decorator = teal_transform_module()) { + # decorator: teal_transform_module, language, function + decorator_obj <- decorate_teal_data(x = decorator, output_name = "plot") # ouputs teal_transform_module + module( label = label, ui = function(id, decorator) { @@ -21,7 +24,7 @@ tm_decorated_plot <- function(label = "module", decorator = decorate_teal_data() selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - decorator$ui(ns("decorate")), + ui_teal_data(ns("decorate"), data_module = decorator), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) @@ -51,7 +54,7 @@ tm_decorated_plot <- function(label = "module", decorator = decorate_teal_data() ) }) - q2 <- decorator$server("decorate", data = q1) + q2 <- srv_teal_data("decorate", data = q1, data_module = decorator, modules = module()) plot_r <- reactive({ req(q2()) @@ -64,8 +67,8 @@ tm_decorated_plot <- function(label = "module", decorator = decorate_teal_data() }) }) }, - ui_args = list(decorator = decorator), - server_args = list(decorator = decorator) + ui_args = list(decorator = decorator_obj), + server_args = list(decorator = decorator_obj) ) } ``` @@ -73,7 +76,7 @@ tm_decorated_plot <- function(label = "module", decorator = decorate_teal_data() Decorator without `ui`. ```{r} -static_decorator <- decorate_teal_data( +static_decorator <- teal_transform_module( ui = function(id) NULL, server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -92,17 +95,15 @@ static_decorator <- decorate_teal_data( Static decorator simplified constructor. ```{r} -static_decorator_from_expr <- decorate_teal_data( - expr = quote(plot <- plot + - ggtitle("This is title") + - xlab("x axis title")) -) +static_decorator_lang <- quote(plot <- plot + + ggtitle("This is title") + + xlab("x axis title")) ``` Interactive decorator ```{r} -interactive_decorator <- decorate_teal_data( +interactive_decorator <- teal_transform_module( ui = function(id) { ns <- NS(id) div( @@ -127,14 +128,42 @@ interactive_decorator <- decorate_teal_data( ) ``` +Static decorator avoiding internal object name. + +```{r} +fun_decorator <- function(x) { + x <- x + ggtitle("This is title") + xlab("x_axis_title") +} +``` + + +failing decorator +```{r} +failing_decorator <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive(stop("Hihi")) + }) + } +) +``` + ```{r} app <- init( data = teal_data(iris = iris, mtcars = mtcars), modules = modules( - tm_decorated_plot("identity", decorator = decorate_teal_data()), - tm_decorated_plot("static", decorator = static_decorator), - tm_decorated_plot("static", decorator = static_decorator_from_expr), - tm_decorated_plot("interactive", decorator = interactive_decorator) + tm_decorated_plot("identity"), + tm_decorated_plot("no-ui", decorator = static_decorator), + tm_decorated_plot("lang", decorator = static_decorator_lang), + tm_decorated_plot("fun", decorator = fun_decorator), + tm_decorated_plot("interactive", decorator = interactive_decorator), + tm_decorated_plot("failing", decorator = failing_decorator) ) ) From 9f4e6dda693aa31c13c3874e4f5662e201105770 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:23:47 +0000 Subject: [PATCH 004/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-modules-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index acfe2e122a..427f70d91b 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -170,4 +170,4 @@ app <- init( if (interactive()) { shinyApp(app$ui, app$server) } -``` \ No newline at end of file +``` From fa117d1811b38dc88b945c98589873fa78d1111f Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:21:43 +0200 Subject: [PATCH 005/165] multiple outputs --- .../decorate-multiple-modules-outputs.Rmd | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 vignettes/decorate-multiple-modules-outputs.Rmd diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd new file mode 100644 index 0000000000..e78ac2771c --- /dev/null +++ b/vignettes/decorate-multiple-modules-outputs.Rmd @@ -0,0 +1,196 @@ +--- +title: "Decorate modules output" +author: "NEST CoreDev" +output: + rmarkdown::html_vignette: + toc: true +vignette: > + %\VignetteIndexEntry{Decorate modules output} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +# ui, server, expr + +```{r} +library(ggplot2) +tm_decorated_plot <- function(label = "module", + decorator = list(plot_1 = teal_transform_module(), plot_2 = teal_transform_module())) { + # decorator: teal_transform_module, language, function + decorator_objs <- list( + plot_1 = decorate_teal_data(x = decorator[["plot_1"]], output_name = "plot_1"), + plot_2 = decorate_teal_data(x = decorator[["plot_2"]], output_name = "plot_2") + ) + # ouputs teal_transform_module + + module( + label = label, + ui = function(id, decorator_objs) { + ns <- NS(id) + div( + selectInput(ns("dataname"), label = "select dataname", choices = NULL), + selectInput(ns("x"), label = "select x", choices = NULL), + selectInput(ns("y"), label = "select y", choices = NULL), + selectInput(ns("plot_type"), "plot type", choices = c("plot 1", "plot 2"), selected = "plot 1"), + div( + id = ns("decorate_1_wrapper"), + ui_teal_data(ns("decorate_1"), data_module = decorator_objs[[1]]) + ), + div( + id = ns("decorate_2_wrapper"), + ui_teal_data(ns("decorate_2"), data_module = decorator_objs[[2]]) + ), + plotOutput(ns("plot")), + verbatimTextOutput(ns("text")) + ) + }, + server = function(id, data, decorator_objs) { + moduleServer(id, function(input, output, session) { + observeEvent(data(), { + updateSelectInput(inputId = "dataname", choices = teal.data::datanames(data())) + }) + + observeEvent(input$dataname, { + updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) + updateSelectInput(inputId = "y", label = "select y", choices = colnames(data()[[input$dataname]])) + }) + + observeEvent(input$plot_type, { + if (input$plot_type == "plot 1") { + shinyjs::hide("decorate_2_wrapper") + shinyjs::show("decorate_1_wrapper") + } else { + shinyjs::show("decorate_2_wrapper") + shinyjs::hide("decorate_1_wrapper") + } + }) + + q1_1 <- reactive({ + req(input$dataname, input$x, input$y) + data() |> + within( + { + plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 1") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }) + + q2_1 <- reactive({ + req(input$dataname, input$x, input$y) + data() |> + within( + { + plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 2") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }) + + q1_2 <- srv_teal_data("decorate_1", data = q1_1, data_module = decorator_objs[[1]], modules = module()) + q2_2 <- srv_teal_data("decorate_2", data = q2_1, data_module = decorator_objs[[2]], modules = module()) + + + plot_r <- reactive({ + req(input$plot_type) + if (input$plot_type == "plot 1") { + req(q1_2()) + q1_2()[["plot_1"]] + } else if (input$plot_type == "plot 2") { + req(q2_2()) + q2_2()[["plot_2"]] + } + }) + + output$plot <- renderPlot(plot_r()) + output$text <- renderText({ + if (input$plot_type == "plot 1") { + teal.code::get_code(q1_2()) + } else if (input$plot_type == "plot 2") { + teal.code::get_code(q2_2()) + } + }) + }) + }, + ui_args = list(decorator_objs = decorator_objs), + server_args = list(decorator_objs = decorator_objs) + ) +} +``` + + +Interactive decorator +```{r} +interactive_decorator_1 <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis 1") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + data() |> within( + { + plot_1 <- plot_1 + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title + ) + }) + }) + } +) + +interactive_decorator_2 <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis 2") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + data() |> within( + { + plot_2 <- plot_2 + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title + ) + }) + }) + } +) +``` + + + +```{r} +app <- init( + data = teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot("identity"), + tm_decorated_plot( + "interactive", + decorator = list(plot_1 = interactive_decorator_1, plot_2 = interactive_decorator_2) + ) + ) +) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +``` \ No newline at end of file From c214c256a6d7d450db3043edd205d4afc8074707 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 08:25:55 +0200 Subject: [PATCH 006/165] reshuffle validation of teal_transform_module --- R/decorate.R | 2 + R/module_nested_tabs.R | 20 +++----- R/module_teal.R | 24 +++++++--- R/module_teal_data.R | 8 ++-- R/module_transform_data.R | 90 ++++++++++++++++++++++++++++-------- man/module_transform_data.Rd | 2 +- 6 files changed, 102 insertions(+), 44 deletions(-) diff --git a/R/decorate.R b/R/decorate.R index f3a4516964..d39adc0773 100644 --- a/R/decorate.R +++ b/R/decorate.R @@ -1,3 +1,5 @@ +setOldClass("teal_transform_module") + #' Modify object in teal_data environment #' @export setGeneric( diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index cae7d9acca..dde25d4cbb 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -92,10 +92,6 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { args <- c(list(id = ns("module")), modules$ui_args) ui_teal <- tagList( - div( - id = ns("validate_datanames"), - ui_validate_reactive_teal_data(ns("validate_datanames")) - ), shinyjs::hidden( tags$div( id = ns("transformer_failure_info"), @@ -108,6 +104,10 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ), tags$div( id = ns("teal_module_ui"), + tags$div( + class = "teal_validated", + ui_check_module_datanames(ns("validate_datanames")) + ), do.call(modules$ui, args) ) ) @@ -132,13 +132,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ) ) } else { - div( - div( - class = "teal_validated", - uiOutput(ns("data_input_error")) - ), - ui_teal - ) + ui_teal } ) ) @@ -275,11 +269,9 @@ srv_teal_module.teal_module <- function(id, observeEvent(any_transformer_failed(), { if (isTRUE(any_transformer_failed())) { shinyjs::hide("teal_module_ui") - shinyjs::hide("validate_datanames") shinyjs::show("transformer_failure_info") } else { shinyjs::show("teal_module_ui") - shinyjs::show("validate_datanames") shinyjs::hide("transformer_failure_info") } }) @@ -291,7 +283,7 @@ srv_teal_module.teal_module <- function(id, .subset_teal_data(all_teal_data, module_datanames) }) - srv_validate_reactive_teal_data( + srv_check_module_datanames( "validate_datanames", data = module_teal_data, modules = modules diff --git a/R/module_teal.R b/R/module_teal.R index 8624636dd2..478e816e91 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -187,12 +187,20 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { ) data_pulled <- srv_init_data("data", data = data) - data_validated <- srv_validate_reactive_teal_data( - "validate", - data = data_pulled, - modules = modules, - validate_shiny_silent_error = FALSE + + validate_ui <- tags$div( + id = session$ns("validate_messages"), + class = "teal_validated", + ui_check_class_teal_data(session$ns("class_teal_data")), + ui_validate_error(session$ns("silent_error")), + ui_check_module_datanames(session$ns("datanames_warning")) ) + srv_check_class_teal_data("class_teal_data", data_pulled) + srv_validate_error("silent_error", data_pulled, validate_shiny_silent_error = FALSE) + srv_check_module_datanames("datanames_warning", data_pulled, modules) + + data_validated <- .trigger_on_success(data_pulled) + data_rv <- reactive({ req(inherits(data_validated(), "teal_data")) is_filter_ok <- check_filter_datanames(filter, ls(teal.code::get_env(data_validated()))) @@ -225,6 +233,8 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { }) } + + if (inherits(data, "teal_data_module")) { setBookmarkExclude(c("teal_modules-active_tab")) shiny::insertTab( @@ -236,7 +246,7 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { value = "teal_data_module", tags$div( ui_init_data(session$ns("data")), - ui_validate_reactive_teal_data(session$ns("validate")) + validate_ui ) ) ) @@ -253,7 +263,7 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { insertUI( selector = sprintf("#%s", session$ns("tabpanel_wrapper")), where = "beforeBegin", - ui = tags$div(ui_validate_reactive_teal_data(session$ns("validate")), tags$br()) + ui = tags$div(validate_ui, tags$br()) ) } diff --git a/R/module_teal_data.R b/R/module_teal_data.R index 899ef14028..91294f3c17 100644 --- a/R/module_teal_data.R +++ b/R/module_teal_data.R @@ -106,7 +106,7 @@ ui_validate_reactive_teal_data <- function(id) { class = "teal_validated", ui_validate_error(ns("silent_error")), ui_check_class_teal_data(ns("class_teal_data")), - ui_check_shiny_warnings(ns("shiny_warnings")) + ui_check_module_datanames(ns("shiny_warnings")) ), div( class = "teal_validated", @@ -129,7 +129,7 @@ srv_validate_reactive_teal_data <- function(id, # nolint: object_length # there is an empty reactive cycle on `init` and `data_rv` has `shiny.silent.error` class srv_validate_error("silent_error", data, validate_shiny_silent_error) srv_check_class_teal_data("class_teal_data", data) - srv_check_shiny_warnings("shiny_warnings", data, modules) + srv_check_module_datanames("shiny_warnings", data, modules) output$previous_failed <- renderUI({ if (hide_validation_error()) { shinyjs::hide("validate_messages") @@ -211,13 +211,13 @@ srv_check_class_teal_data <- function(id, data) { } #' @keywords internal -ui_check_shiny_warnings <- function(id) { +ui_check_module_datanames <- function(id) { ns <- NS(id) uiOutput(NS(id, "message")) } #' @keywords internal -srv_check_shiny_warnings <- function(id, data, modules) { +srv_check_module_datanames <- function(id, data, modules) { checkmate::assert_string(id) moduleServer(id, function(input, output, session) { output$message <- renderUI({ diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 418c81d06d..38dd4ebcd2 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -15,8 +15,14 @@ NULL #' @rdname module_transform_data ui_transform_data <- function(id, transforms, class = "well") { checkmate::assert_string(id) - checkmate::assert_list(transforms, "teal_transform_module", null.ok = TRUE) - ns <- NS(id) + if (length(transforms) == 0L) { + return(NULL) + } + if (inherits(transforms, "teal_transform_module")) { + transforms <- list(transforms) + } + checkmate::assert_list(transforms, "teal_transform_module") + labels <- lapply(transforms, function(x) attr(x, "label")) ids <- get_unique_labels(labels) names(transforms) <- ids @@ -24,8 +30,11 @@ ui_transform_data <- function(id, transforms, class = "well") { lapply( names(transforms), function(name) { + child_id <- NS(id)(name) + ns <- NS(child_id) data_mod <- transforms[[name]] - wrapper_id <- ns(sprintf("wrapper_%s", name)) + transform_wrapper_id <- ns(sprintf("wrapper_%s", name)) + div( # todo: accordion? # class .teal_validated changes the color of the boarder on error in ui_validate_reactive_teal_data # For details see tealValidate.js file. @@ -40,11 +49,16 @@ ui_transform_data <- function(id, transforms, class = "well") { class = "remove pull-right fa fa-angle-down", style = "cursor: pointer;", title = "fold/expand transform panel", - onclick = sprintf("togglePanelItems(this, '%s', 'fa-angle-right', 'fa-angle-down');", wrapper_id) + onclick = sprintf("togglePanelItems(this, '%s', 'fa-angle-right', 'fa-angle-down');", transform_wrapper_id) ), - div( - id = wrapper_id, - ui_teal_data(id = ns(name), data_module = transforms[[name]]$ui) + tags$div( + id = transform_wrapper_id, + data_mod$ui(id = ns(name)), + div( + id = ns("validate_messages"), + class = "teal_validated", + uiOutput(ns("error_wrapper")) + ) ) ) } @@ -52,27 +66,67 @@ ui_transform_data <- function(id, transforms, class = "well") { } #' @rdname module_transform_data -srv_transform_data <- function(id, data, transforms, modules, is_transformer_failed = reactiveValues()) { +srv_transform_data <- function(id, data, transforms, modules = NULL, is_transformer_failed = reactiveValues()) { checkmate::assert_string(id) assert_reactive(data) - checkmate::assert_list(transforms, "teal_transform_module", null.ok = TRUE) - checkmate::assert_class(modules, "teal_module") + checkmate::assert_class(modules, "teal_module", null.ok = TRUE) if (length(transforms) == 0L) { return(data) } + if (inherits(transforms, "teal_transform_module")) { + transforms <- list(transforms) + } + checkmate::assert_list(transforms, "teal_transform_module", null.ok = TRUE) labels <- lapply(transforms, function(x) attr(x, "label")) ids <- get_unique_labels(labels) names(transforms) <- ids + moduleServer(id, function(input, output, session) { - logger::log_debug("srv_teal_data_modules initializing.") Reduce( - function(previous_result, name) { - srv_teal_data( - id = name, - data_module = function(id) transforms[[name]]$server(id, previous_result), - modules = modules, - is_transformer_failed = is_transformer_failed - ) + function(data_previous, name) { + moduleServer(name, function(input, output, session) { + logger::log_debug("srv_transform_data initializing for { name }.") + is_transformer_failed[[name]] <- FALSE + data_out <- transforms[[name]]$server(name, data = data_previous) + data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) + observeEvent(data_handled(), { + if (inherits(data_handled(), "teal_data")) { + is_transformer_failed[[name]] <- FALSE + } else { + is_transformer_failed[[name]] <- TRUE + } + }) + + is_previous_failed <- reactive({ + idx_this <- which(names(is_transformer_failed) == name) + is_transformer_failed_list <- reactiveValuesToList(is_transformer_failed) + idx_failures <- which(unlist(is_transformer_failed_list)) + any(idx_failures < idx_this) + }) + + srv_validate_error("silent_error", data_handled, validate_shiny_silent_error = FALSE) + srv_check_class_teal_data("class_teal_data", data_handled) + if (!is.null(modules)) { + srv_check_module_datanames("datanames_warning", data_handled, modules) + } + + transform_wrapper_id <- sprintf("wrapper_%s", name) + output$error_wrapper <- renderUI({ + if (is_previous_failed()) { + shinyjs::disable(transform_wrapper_id) + tags$div("One of previous transformers failed. Please fix and continue.", class = "teal-output-warning") + } else { + shinyjs::enable(transform_wrapper_id) + shiny::tagList( + ui_validate_error(session$ns("silent_error")), + ui_check_class_teal_data(session$ns("class_teal_data")), + ui_check_module_datanames(session$ns("datanames_warning")) + ) + } + }) + + .trigger_on_success(data_handled) + }) }, x = names(transforms), init = data diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index 2a4a351062..e7a42ebf43 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -12,7 +12,7 @@ srv_transform_data( id, data, transforms, - modules, + modules = NULL, is_transformer_failed = reactiveValues() ) } From 5f7ab791e4bb730a61eb671bee06a205f78ae4c5 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 07:55:48 +0200 Subject: [PATCH 007/165] don't display UI if not specified --- R/module_transform_data.R | 6 +- R/teal_data_module.R | 4 +- vignettes/decorate-modules-output.Rmd | 174 +++++++++++++++----------- 3 files changed, 107 insertions(+), 77 deletions(-) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 38dd4ebcd2..e016739e69 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -53,7 +53,11 @@ ui_transform_data <- function(id, transforms, class = "well") { ), tags$div( id = transform_wrapper_id, - data_mod$ui(id = ns(name)), + if (is.null(data_mod$ui)) { + return(NULL) + } else { + data_mod$ui(id = ns(name)) + }, div( id = ns("validate_messages"), class = "teal_validated", diff --git a/R/teal_data_module.R b/R/teal_data_module.R index 8051b604b4..ac2296a204 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -137,11 +137,11 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' @name teal_transform_module #' #' @export -teal_transform_module <- function(ui = function(id) NULL, +teal_transform_module <- function(ui = NULL, server = function(id, data) data, label = "transform module", datanames = "all") { - checkmate::assert_function(ui, args = "id", nargs = 1) + checkmate::assert_function(ui, args = "id", nargs = 1, null.ok = TRUE) checkmate::assert_function(server, args = c("id", "data"), nargs = 2) checkmate::assert_string(label) structure( diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 427f70d91b..d8c5540520 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -10,74 +10,13 @@ vignette: > %\VignetteEncoding{UTF-8} --- -```{r} -library(ggplot2) -tm_decorated_plot <- function(label = "module", decorator = teal_transform_module()) { - # decorator: teal_transform_module, language, function - decorator_obj <- decorate_teal_data(x = decorator, output_name = "plot") # ouputs teal_transform_module - - module( - label = label, - ui = function(id, decorator) { - ns <- NS(id) - div( - selectInput(ns("dataname"), label = "select dataname", choices = NULL), - selectInput(ns("x"), label = "select x", choices = NULL), - selectInput(ns("y"), label = "select y", choices = NULL), - ui_teal_data(ns("decorate"), data_module = decorator), - plotOutput(ns("plot")), - verbatimTextOutput(ns("text")) - ) - }, - server = function(id, data, decorator) { - moduleServer(id, function(input, output, session) { - observeEvent(data(), { - updateSelectInput(inputId = "dataname", choices = teal.data::datanames(data())) - }) - - observeEvent(input$dataname, { - updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) - updateSelectInput(inputId = "y", label = "select y", choices = colnames(data()[[input$dataname]])) - }) - - q1 <- reactive({ - req(input$dataname, input$x, input$y) - data() |> - within( - { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }) - q2 <- srv_teal_data("decorate", data = q1, data_module = decorator, modules = module()) +## Create a decoration object - plot_r <- reactive({ - req(q2()) - q2()[["plot"]] - }) +`teal_transform_module` without `ui`. Nothing will be displayed, only server code will be executed. - output$plot <- renderPlot(plot_r()) - output$text <- renderText({ - teal.code::get_code(q2()) - }) - }) - }, - ui_args = list(decorator = decorator_obj), - server_args = list(decorator = decorator_obj) - ) -} -``` - - -Decorator without `ui`. -```{r} + ```{r} static_decorator <- teal_transform_module( - ui = function(id) NULL, server = function(id, data) { moduleServer(id, function(input, output, session) { reactive({ @@ -91,18 +30,20 @@ static_decorator <- teal_transform_module( }) } ) -``` + ``` -Static decorator simplified constructor. -```{r} + +Using language will result in `teal_transform_module` without `ui`. + + ```{r} static_decorator_lang <- quote(plot <- plot + ggtitle("This is title") + xlab("x axis title")) -``` + ``` -Interactive decorator -```{r} +Adding interactivity requires creating a proper shiny module. + ```{r} interactive_decorator <- teal_transform_module( ui = function(id) { ns <- NS(id) @@ -126,9 +67,12 @@ interactive_decorator <- teal_transform_module( }) } ) -``` + ``` + -Static decorator avoiding internal object name. +Thanks to the function-constructor app developer doesn't need to know what is the object name inside of the module's +`server`. One can create any single argument function. Mapping `x` -> `plot` happens in `decorate_teal_data` and +requires to specify `output_name = "plot"` (modules' developer duty). ```{r} fun_decorator <- function(x) { @@ -136,9 +80,10 @@ fun_decorator <- function(x) { } ``` +Failures in decorators are handled by internal `teal` mechanism called `trigger_on_success`, which will never +break `data` object inside of the module. Instead decorator will be ignored and relevant error message will be shown. -failing decorator -```{r} + ```{r} failing_decorator <- teal_transform_module( ui = function(id) { ns <- NS(id) @@ -152,8 +97,82 @@ failing_decorator <- teal_transform_module( }) } ) + ``` + +## Example module + +To include decorator to the `teal_module` one needs to add an extra argument to the module-constructor function `tm_`. +Decorator passed to the constructor needs to go through `decorate_teal_data()` in order to cast `language` or `function` object into `teal_transform_module`. _`decorate_teal_data()` wouldn't be necessary if an app developer +always provides `teal_transform_module`. We could extend `teal_transform_module()` constructor by `expr` argument instead but using `function` would be impossible then._ + +Decorators should be passed via `ui_args` and `server_args` to the module's `ui` and `server`, where they should be +consumed by `ui/srv_transform_data`. + + +```{r} +library(ggplot2) +tm_decorated_plot <- function(label = "module", decorator = teal_transform_module()) { + decorator_obj <- decorate_teal_data(x = decorator, output_name = "plot") + + module( + label = label, + ui = function(id, decorator) { + ns <- NS(id) + div( + selectInput(ns("dataname"), label = "select dataname", choices = NULL), + selectInput(ns("x"), label = "select x", choices = NULL), + selectInput(ns("y"), label = "select y", choices = NULL), + ui_transform_data(ns("decorate"), transforms = decorator), + plotOutput(ns("plot")), + verbatimTextOutput(ns("text")) + ) + }, + server = function(id, data, decorator) { + moduleServer(id, function(input, output, session) { + observeEvent(data(), { + updateSelectInput(inputId = "dataname", choices = teal.data::datanames(data())) + }) + + observeEvent(input$dataname, { + updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) + updateSelectInput(inputId = "y", label = "select y", choices = colnames(data()[[input$dataname]])) + }) + + q1 <- reactive({ + req(input$dataname, input$x, input$y) + data() |> + within( + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }) + + q2 <- srv_transform_data("decorate", data = q1, transforms = decorator) + + plot_r <- reactive({ + req(q2()) + q2()[["plot"]] + }) + + output$plot <- renderPlot(plot_r()) + output$text <- renderText({ + teal.code::get_code(q2()) + }) + }) + }, + ui_args = list(decorator = decorator_obj), + server_args = list(decorator = decorator_obj) + ) +} ``` +## Example app + ```{r} app <- init( data = teal_data(iris = iris, mtcars = mtcars), @@ -171,3 +190,10 @@ if (interactive()) { shinyApp(app$ui, app$server) } ``` + +## To improve + +- Error handling when a decorator throws an error. +- Rewrite modules so that they always have a graph as `graph` (or `plot`, `g`, `p` etc.) and table as + `table` (or `t` etc.) and listings as `listings` (or `l` etc.). This simplifies/standardizes a construction of + `teal_transform_module` which can be re-used in multiple modules. \ No newline at end of file From 898015f6dd450a75866429460d4ffb8c38c726eb Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:27:51 +0200 Subject: [PATCH 008/165] Dispatch in teal_transform_module constructor --- R/teal_data_module.R | 141 +++++++++++++++++++++++++++++------ man/teal_transform_module.Rd | 34 ++++++++- 2 files changed, 147 insertions(+), 28 deletions(-) diff --git a/R/teal_data_module.R b/R/teal_data_module.R index ac2296a204..2be681bc90 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -100,10 +100,12 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' #' #' @inheritParams teal_data_module -#' @param server (`function(id, data)`) +#' @param server (`function(id, data)` or `language`) #' `shiny` module server function; that takes `id` and `data` argument, #' where the `id` is the module id and `data` is the reactive `teal_data` input. #' The server function must return reactive expression containing `teal_data` object. +#' Alternatively, `language` object is a simplified version of the `server` where `input$` +#' can be used to link interactive value with the `teal_data` code. #' @param datanames (`character`) #' Names of the datasets that are relevant for the module. The #' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show @@ -111,7 +113,20 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' @examples #' my_transformers <- list( #' teal_transform_module( -#' label = "Custom transform for iris", +#' label = "Static transform for iris", +#' datanames = "iris", +#' server = function(id, data) { +#' moduleServer(id, function(input, output, session) { +#' reactive({ +#' within(data(), { +#' iris <- head(iris, 5) +#' }) +#' }) +#' }) +#' } +#' ), +#' teal_transform_module( +#' label = "Interactive transform for iris", #' datanames = "iris", #' ui = function(id) { #' ns <- NS(id) @@ -131,36 +146,114 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' }) #' }) #' } +#' ), +#' teal_transform_module( +#' label = "Simplified interactive transform for iris", +#' datanames = "iris", +#' ui = function(id) { +#' ns <- NS(id) +#' tags$div( +#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) +#' ) +#' }, +#' server = expression(iris <- head(iris, n_rows)) #' ) #' ) #' #' @name teal_transform_module #' #' @export -teal_transform_module <- function(ui = NULL, - server = function(id, data) data, - label = "transform module", - datanames = "all") { - checkmate::assert_function(ui, args = "id", nargs = 1, null.ok = TRUE) - checkmate::assert_function(server, args = c("id", "data"), nargs = 2) - checkmate::assert_string(label) - structure( - list( +setGeneric( + "teal_transform_module", + function(ui = NULL, + server = function(id, data) data, + label = "transform module", + datanames = "all") { + standardGeneric("teal_transform_module") + } +) + +setMethod( + "teal_transform_module", + signature = c(server = "function"), + function(ui = NULL, + server = function(id, data) data, + label = "transform module", + datanames = "all") { + structure( + list( + ui = ui, + server = function(id, data) { + data_out <- server(id, data) + decorate_err_msg( + assert_reactive(data_out), + pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), + post = "Please make sure that this module returns a 'reactive` object containing 'teal_data' class of object." # nolint: line_length_linter. + ) + } + ), + label = label, + datanames = datanames, + class = c("teal_transform_module", "teal_data_module") + ) + } +) + +setMethod( + "teal_transform_module", + signature = c(server = "missing"), + function(ui = NULL, + server = function(id, data) data, + label = "transform module", + datanames = "all") { + teal_transform_module( + ui = ui, + server = function(id, data) data, + label = label, + datanames = datanames + ) + } +) + +# todo: instead there should be transform_server(expr = ) +setMethod( + "teal_transform_module", + signature = c(server = "expression"), + function(ui = NULL, + server = function(id, data) data, + label = "transform module", + datanames = "all") { + teal_transform_module( ui = ui, server = function(id, data) { - data_out <- server(id, data) - decorate_err_msg( - assert_reactive(data_out), - pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), - post = "Please make sure that this module returns a 'reactive` object containing 'teal_data' class of object." # nolint: line_length_linter. - ) - } - ), - label = label, - datanames = datanames, - class = c("teal_transform_module", "teal_data_module") - ) -} + moduleServer(id, function(input, output, session) { + reactive({ + call_with_inputs <- lapply(server, function(x) { + do.call( + what = substitute, + args = list(expr = x, env = reactiveValuesToList(input)) + ) + }) + eval_code(object = data(), code = as.expression(call_with_inputs)) + }) + }) + }, + label = label, + datanames = datanames + ) + } +) + +setMethod( + "teal_transform_module", + signature = c(server = "language"), + function(ui = NULL, + server = function(id, data) data, + label = "transform module", + datanames = "all") { + teal_transform_module(ui = ui, server = as.expression(server), label = label, datanames = datanames) + } +) #' Extract all `transformers` from `modules`. diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 0f424b329f..8f2a22386b 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -5,7 +5,7 @@ \title{Data module for \code{teal} transformers.} \usage{ teal_transform_module( - ui = function(id) NULL, + ui = NULL, server = function(id, data) data, label = "transform module", datanames = "all" @@ -15,10 +15,12 @@ teal_transform_module( \item{ui}{(\verb{function(id)}) \code{shiny} module UI function; must only take \code{id} argument} -\item{server}{(\verb{function(id, data)}) +\item{server}{(\verb{function(id, data)} or \code{language}) \code{shiny} module server function; that takes \code{id} and \code{data} argument, where the \code{id} is the module id and \code{data} is the reactive \code{teal_data} input. -The server function must return reactive expression containing \code{teal_data} object.} +The server function must return reactive expression containing \code{teal_data} object. +Alternatively, \code{language} object is a simplified version of the \code{server} where \verb{input$} +can be used to link interactive value with the \code{teal_data} code.} \item{label}{(\code{character(1)}) Label of the module.} @@ -43,7 +45,20 @@ See vignette \code{vignette("data-transform-as-shiny-module", package = "teal")} \examples{ my_transformers <- list( teal_transform_module( - label = "Custom transform for iris", + label = "Static transform for iris", + datanames = "iris", + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + within(data(), { + iris <- head(iris, 5) + }) + }) + }) + } + ), + teal_transform_module( + label = "Interactive transform for iris", datanames = "iris", ui = function(id) { ns <- NS(id) @@ -63,6 +78,17 @@ my_transformers <- list( }) }) } + ), + teal_transform_module( + label = "Simplified interactive transform for iris", + datanames = "iris", + ui = function(id) { + ns <- NS(id) + tags$div( + numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) + ) + }, + server = expression(iris <- head(iris, n_rows)) ) ) From b5cb59f41d441d69eeb12705134e3e104ef267b4 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:02:45 +0200 Subject: [PATCH 009/165] - make_teal_transform_server to simplify teal_transform_module's server creation - some documentation - some todo's --- DESCRIPTION | 1 - NAMESPACE | 4 +- R/decorate.R | 50 ---- R/module_nested_tabs.R | 4 +- R/module_teal.R | 2 +- R/module_transform_data.R | 17 +- R/modules.R | 21 +- R/teal_data_module-within.R | 2 +- R/teal_data_module.R | 214 +++++++++--------- man/decorate_teal_data.Rd | 11 - man/example_module.Rd | 7 +- man/make_teal_transform_server.Rd | 37 +++ man/module_teal.Rd | 2 +- ...ata.Rd => module_teal_transform_module.Rd} | 15 +- man/teal_data_module.Rd | 2 +- man/teal_modules.Rd | 13 +- man/teal_transform_module.Rd | 60 +++-- vignettes/data-transform-as-shiny-module.Rmd | 4 +- vignettes/decorate-modules-output.Rmd | 115 +++++++--- 19 files changed, 310 insertions(+), 271 deletions(-) delete mode 100644 R/decorate.R delete mode 100644 man/decorate_teal_data.Rd create mode 100644 man/make_teal_transform_server.Rd rename man/{module_transform_data.Rd => module_teal_transform_module.Rd} (70%) diff --git a/DESCRIPTION b/DESCRIPTION index 06e402770e..0fbab7e2c9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -90,7 +90,6 @@ RoxygenNote: 7.3.2 Collate: 'TealAppDriver.R' 'checkmate.R' - 'decorate.R' 'dummy_functions.R' 'get_rcode_utils.R' 'include_css_js.R' diff --git a/NAMESPACE b/NAMESPACE index 531f46e10d..224c65d040 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,12 +17,12 @@ export(TealReportCard) export(as.teal_slices) export(as_tdata) export(build_app_title) -export(decorate_teal_data) export(example_module) export(get_code_tdata) export(get_metadata) export(init) export(landing_popup_module) +export(make_teal_transform_server) export(module) export(modules) export(new_tdata) @@ -31,12 +31,14 @@ export(reporter_previewer_module) export(set_datanames) export(show_rcode_modal) export(srv_teal) +export(srv_teal_transform_module) export(srv_teal_with_splash) export(tdata2env) export(teal_data_module) export(teal_slices) export(teal_transform_module) export(ui_teal) +export(ui_teal_transform_module) export(ui_teal_with_splash) export(validate_has_data) export(validate_has_elements) diff --git a/R/decorate.R b/R/decorate.R deleted file mode 100644 index d39adc0773..0000000000 --- a/R/decorate.R +++ /dev/null @@ -1,50 +0,0 @@ -setOldClass("teal_transform_module") - -#' Modify object in teal_data environment -#' @export -setGeneric( - "decorate_teal_data", - function(x, output_name) { - standardGeneric("decorate_teal_data") - } -) - -setMethod( - "decorate_teal_data", - signature = list(x = "teal_transform_module"), - function(x, output_name) x -) - -setMethod( - "decorate_teal_data", - signature = list(x = "language"), - function(x, output_name) { - teal_transform_module( - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - eval_code(data(), code = x) - }) - }) - } - ) - } -) - -setMethod( - "decorate_teal_data", - signature = list(x = "function"), - function(x, output_name) { - checkmate::check_string(output_name) - formal_args <- names(formals(x)) # how the args are named is here - expr <- do.call( - substitute, - list( - expr = body(x), - env = setNames(list(as.name(output_name)), formal_args[[1]]) - ) - ) - decorate_teal_data(x = expr) - } -) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index dde25d4cbb..00e54a4f6a 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -126,7 +126,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ui_data_summary(ns("data_summary")), ui_filter_data(ns("filter_panel")), if (length(modules$transformers) > 0 && !isTRUE(attr(modules$transformers, "custom_ui"))) { - ui_transform_data(ns("data_transform"), transforms = modules$transformers, class = "well") + ui_teal_transform_module(ns("data_transform"), transforms = modules$transformers, class = "well") }, class = "teal_secondary_col" ) @@ -256,7 +256,7 @@ srv_teal_module.teal_module <- function(id, ) is_transformer_failed <- reactiveValues() - transformed_teal_data <- srv_transform_data( + transformed_teal_data <- srv_teal_transform_module( "data_transform", data = filtered_teal_data, transforms = modules$transformers, diff --git a/R/module_teal.R b/R/module_teal.R index 478e816e91..e9e6b82f7e 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -20,7 +20,7 @@ #' performed: #' - data loading in [`module_init_data`] #' - data filtering in [`module_filter_data`] -#' - data transformation in [`module_transform_data`] +#' - data transformation in [`module_teal_transform_module`] #' #' ## Fallback on failure #' diff --git a/R/module_transform_data.R b/R/module_transform_data.R index e016739e69..e4b75d3a54 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -1,6 +1,6 @@ #' Module to transform `reactive` `teal_data` #' -#' Module calls multiple [`module_teal_data`] in sequence so that `reactive teal_data` output +#' Module calls [teal_transform_module()] in sequence so that `reactive teal_data` output #' from one module is handed over to the following module's input. #' #' @inheritParams module_teal_data @@ -8,12 +8,12 @@ #' @return `reactive` `teal_data` #' #' -#' @name module_transform_data -#' @keywords internal +#' @name module_teal_transform_module NULL -#' @rdname module_transform_data -ui_transform_data <- function(id, transforms, class = "well") { +#' @export +#' @rdname module_teal_transform_module +ui_teal_transform_module <- function(id, transforms, class = "well") { checkmate::assert_string(id) if (length(transforms) == 0L) { return(NULL) @@ -69,8 +69,9 @@ ui_transform_data <- function(id, transforms, class = "well") { ) } -#' @rdname module_transform_data -srv_transform_data <- function(id, data, transforms, modules = NULL, is_transformer_failed = reactiveValues()) { +#' @export +#' @rdname module_teal_transform_module +srv_teal_transform_module <- function(id, data, transforms, modules = NULL, is_transformer_failed = reactiveValues()) { checkmate::assert_string(id) assert_reactive(data) checkmate::assert_class(modules, "teal_module", null.ok = TRUE) @@ -89,7 +90,7 @@ srv_transform_data <- function(id, data, transforms, modules = NULL, is_transfor Reduce( function(data_previous, name) { moduleServer(name, function(input, output, session) { - logger::log_debug("srv_transform_data initializing for { name }.") + logger::log_debug("srv_teal_transform_module initializing for { name }.") is_transformer_failed[[name]] <- FALSE data_out <- transforms[[name]]$server(name, data = data_previous) data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) diff --git a/R/modules.R b/R/modules.R index 76dea53d68..c91d829822 100644 --- a/R/modules.R +++ b/R/modules.R @@ -48,14 +48,12 @@ setOldClass("teal_modules") #' @param server_args (named `list`) with additional arguments passed on to the server function. #' @param ui_args (named `list`) with additional arguments passed on to the UI function. #' @param x (`teal_module` or `teal_modules`) Object to format/print. -#' @param indent (`integer(1)`) Indention level; each nested element is indented one level more. -#' @param transformers (`list` of `teal_data_module`) that will be applied to transform the data. +#' @param transformers (`list` of `teal_transform_module`) that will be applied to transform module's data input. #' Each transform module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the list. #' If so, the module developer is responsible to display the UI in the module itself. `datanames` of the `transformers` #' will be added to the `datanames`. #' -#' When the transformation does not have sufficient input data, the resulting data will fallback -#' to the last successful transform or, in case there are none, to the filtered data. +#' #' @param ... #' - For `modules()`: (`teal_module` or `teal_modules`) Objects to wrap into a tab. #' - For `format()` and `print()`: Arguments passed to other methods. @@ -315,7 +313,12 @@ modules <- function(..., label = "root") { #' @rdname teal_modules #' @export -format.teal_module <- function(x, indent = 0, ...) { +format.teal_module <- function(x, ...) { + if (is.null(list(...)$indent)) { + indent <- 0L + } else { + indent <- list(...)$indent + } paste0(paste(rep(" ", indent), collapse = ""), "+ ", x$label, "\n", collapse = "") } @@ -330,7 +333,13 @@ print.teal_module <- function(x, ...) { #' @rdname teal_modules #' @export -format.teal_modules <- function(x, indent = 0, ...) { +format.teal_modules <- function(x, ...) { + if (is.null(list(...)$indent)) { + indent <- 0L + } else { + indent <- list(...)$indent + } + paste( c( paste0(rep(" ", indent), "+ ", x$label, "\n"), diff --git a/R/teal_data_module-within.R b/R/teal_data_module-within.R index e4c56b4fcc..034a2db52c 100644 --- a/R/teal_data_module-within.R +++ b/R/teal_data_module-within.R @@ -6,7 +6,7 @@ #' the `...` argument: as `name:value` pairs are passed to `...`, `name` in `expr` will be replaced with `value.` #' #' @param data (`teal_data_module`) object -#' @param expr (`expression`) to evaluate. Must be inline code. See +#' @param expr (`expression`) to evaluate. Must be inline code. See [within()] #' @param ... See `Details`. #' #' @return diff --git a/R/teal_data_module.R b/R/teal_data_module.R index 2be681bc90..4c853fee5f 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -88,28 +88,63 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' @description #' `r lifecycle::badge("experimental")` #' -#' Create a `teal_data_module` object for custom transformation of data for pre-processing -#' before passing the data into the module. +#' `teal_transform_module` creates a shiny-module to transform data in a `teal` application. #' -#' @details -#' `teal_transform_module` creates a [`teal_data_module`] object to transform data in a `teal` -#' application. This transformation happens after the data has passed through the filtering activity -#' in teal. The transformed data is then sent to the server of the [teal_module()]. +#' # Transforming `teal` module's input +#' +#' This transformation happens after the data has passed through the filtering activity in teal. The +#' transformed data is then sent to the server of the [teal_module()]. Process is handled by `teal` +#' internals. #' #' See vignette `vignette("data-transform-as-shiny-module", package = "teal")` for more details. #' +#' # Decorating `teal` module's output +#' +#' `teal_transform_module`'s purpose is to modify any object created in [`teal.data::teal_data`]. It means that an +#' app-developer can use `teal_transform_module` to modify data but also outputted tables, listings and graphs. +#' Some [`teal_modules`] enables app developer to inject custom shiny module to modify displayed output. +#' To handle these `decorators` inside of your module use [ui_teal_transform_module()] and [srv_teal_transform_module]. +#' (todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by +#' ui/srv_teal_transform_module()... . Alternatively, decorators could be a [module()]'s argument) +#' +#' # `server` as a language +#' +#' Server function in `teal_transform_module` must return `reactive` containing [teal.data::teal_data] object. +#' Consider sinmple transformer which doesn't require any advanced reactivity, example `server` might have a +#' following form: +#' +#' ``` +#' function(id, data) { +#' moduleServer(id, function(input, output, session) { +#' reactive({ +#' within( +#' data(), +#' expr = x <- subset(x, col == level), +#' level = input$level +#' ) +#' }) +#' }) +#' } +#' ``` +#' +#' Above can be simplified to presented below, where `level` will be automatically substituted with +#' respective input matched by its name. #' +#' ``` +#' make_teal_transform_module(expr = expression(x <- subset(x, col == level))) +#' ``` #' @inheritParams teal_data_module #' @param server (`function(id, data)` or `language`) #' `shiny` module server function; that takes `id` and `data` argument, #' where the `id` is the module id and `data` is the reactive `teal_data` input. #' The server function must return reactive expression containing `teal_data` object. -#' Alternatively, `language` object is a simplified version of the `server` where `input$` -#' can be used to link interactive value with the `teal_data` code. +#' To simplify use [make_teal_transform_server()]. #' @param datanames (`character`) -#' Names of the datasets that are relevant for the module. The -#' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show -#' filters of all datasets. `datanames` will be automatically appended to the [modules()] `datanames`. +#' Names of the datasets that are relevant for the module. The +#' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show +#' filters of all datasets. `datanames` will be automatically appended to the [modules()] `datanames`. +#' +#' #' @examples #' my_transformers <- list( #' teal_transform_module( @@ -146,115 +181,74 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' }) #' }) #' } -#' ), -#' teal_transform_module( -#' label = "Simplified interactive transform for iris", -#' datanames = "iris", -#' ui = function(id) { -#' ns <- NS(id) -#' tags$div( -#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) -#' ) -#' }, -#' server = expression(iris <- head(iris, n_rows)) #' ) #' ) #' #' @name teal_transform_module #' #' @export -setGeneric( - "teal_transform_module", - function(ui = NULL, - server = function(id, data) data, - label = "transform module", - datanames = "all") { - standardGeneric("teal_transform_module") - } -) - -setMethod( - "teal_transform_module", - signature = c(server = "function"), - function(ui = NULL, - server = function(id, data) data, - label = "transform module", - datanames = "all") { - structure( - list( - ui = ui, - server = function(id, data) { - data_out <- server(id, data) - decorate_err_msg( - assert_reactive(data_out), - pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), - post = "Please make sure that this module returns a 'reactive` object containing 'teal_data' class of object." # nolint: line_length_linter. - ) - } - ), - label = label, - datanames = datanames, - class = c("teal_transform_module", "teal_data_module") - ) - } -) - -setMethod( - "teal_transform_module", - signature = c(server = "missing"), - function(ui = NULL, - server = function(id, data) data, - label = "transform module", - datanames = "all") { - teal_transform_module( - ui = ui, - server = function(id, data) data, - label = label, - datanames = datanames - ) - } -) - -# todo: instead there should be transform_server(expr = ) -setMethod( - "teal_transform_module", - signature = c(server = "expression"), - function(ui = NULL, - server = function(id, data) data, - label = "transform module", - datanames = "all") { - teal_transform_module( +teal_transform_module <- function(ui = NULL, + server = function(id, data) data, + label = "transform module", + datanames = "all") { + structure( + list( ui = ui, server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - call_with_inputs <- lapply(server, function(x) { - do.call( - what = substitute, - args = list(expr = x, env = reactiveValuesToList(input)) - ) - }) - eval_code(object = data(), code = as.expression(call_with_inputs)) - }) - }) - }, - label = label, - datanames = datanames - ) - } -) + data_out <- server(id, data) + decorate_err_msg( + assert_reactive(data_out), + pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), + post = "Please make sure that this module returns a 'reactive` object containing 'teal_data' class of object." # nolint: line_length_linter. + ) + } + ), + label = label, + datanames = datanames, + class = c("teal_transform_module", "teal_data_module") + ) +} -setMethod( - "teal_transform_module", - signature = c(server = "language"), - function(ui = NULL, - server = function(id, data) data, - label = "transform module", - datanames = "all") { - teal_transform_module(ui = ui, server = as.expression(server), label = label, datanames = datanames) +#' Make teal_transform_module's server +#' +#' A factory function to simplify creation of a [`teal_transform_module`]'s server. Specified `expr` +#' is wrapped in a shiny module function and output can be passed to the `server` argument in +#' [teal_transform_module()] call. Such server function can be linked with ui and values from the +#' inputs can be used in the expression. Object names specified in the expression will be substituted +#' with the value of the respective input (matched by the name) - for example in +#' `expression(graph <- graph + ggtitle(title))` object `title` will be replaced with the value of +#' `input$title`. +#' @param expr (`language`) +#' An R call which will be evaluated within [`teal.data::teal_data`] environment. +#' @return `function(id, data)` returning `shiny` module +#' @examples +#' +#' teal_transform_module( +#' label = "Simplified interactive transform for iris", +#' datanames = "iris", +#' ui = function(id) { +#' ns <- NS(id) +#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) +#' }, +#' server = make_teal_transform_server(expression(iris <- head(iris, n_rows))) +#' ) +#' +#' @export +make_teal_transform_server <- function(expr) { + function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + call_with_inputs <- lapply(expr, function(x) { + do.call( + what = substitute, + args = list(expr = x, env = reactiveValuesToList(input)) + ) + }) + eval_code(object = data(), code = as.expression(call_with_inputs)) + }) + }) } -) - +} #' Extract all `transformers` from `modules`. #' diff --git a/man/decorate_teal_data.Rd b/man/decorate_teal_data.Rd deleted file mode 100644 index 090c421c43..0000000000 --- a/man/decorate_teal_data.Rd +++ /dev/null @@ -1,11 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/decorate.R -\name{decorate_teal_data} -\alias{decorate_teal_data} -\title{Modify object in teal_data environment} -\usage{ -decorate_teal_data(x, output_name) -} -\description{ -Modify object in teal_data environment -} diff --git a/man/example_module.Rd b/man/example_module.Rd index 26558f8650..ae86b85909 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -18,13 +18,10 @@ For \code{modules()} defaults to \code{"root"}. See \code{Details}.} The keyword \code{"all"} provides all datasets available in \code{data} passed to \code{teal} application. \code{NULL} will hide the filter panel.} -\item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. +\item{transformers}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformers} -will be added to the \code{datanames}. - -When the transformation does not have sufficient input data, the resulting data will fallback -to the last successful transform or, in case there are none, to the filtered data.} +will be added to the \code{datanames}.} } \value{ A \code{teal} module which can be included in the \code{modules} argument to \code{\link[=init]{init()}}. diff --git a/man/make_teal_transform_server.Rd b/man/make_teal_transform_server.Rd new file mode 100644 index 0000000000..9416656c86 --- /dev/null +++ b/man/make_teal_transform_server.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/teal_data_module.R +\name{make_teal_transform_server} +\alias{make_teal_transform_server} +\title{Make teal_transform_module's server} +\usage{ +make_teal_transform_server(expr) +} +\arguments{ +\item{expr}{(\code{language}) +An R call which will be evaluated within \code{\link[teal.data:teal_data]{teal.data::teal_data}} environment.} +} +\value{ +\verb{function(id, data)} returning \code{shiny} module +} +\description{ +A factory function to simplify creation of a \code{\link{teal_transform_module}}'s server. Specified \code{expr} +is wrapped in a shiny module function and output can be passed to the \code{server} argument in +\code{\link[=teal_transform_module]{teal_transform_module()}} call. Such server function can be linked with ui and values from the +inputs can be used in the expression. Object names specified in the expression will be substituted +with the value of the respective input (matched by the name) - for example in +\code{expression(graph <- graph + ggtitle(title))} object \code{title} will be replaced with the value of +\code{input$title}. +} +\examples{ + +teal_transform_module( + label = "Simplified interactive transform for iris", + datanames = "iris", + ui = function(id) { + ns <- NS(id) + numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) + }, + server = make_teal_transform_server(expression(iris <- head(iris, n_rows))) +) + +} diff --git a/man/module_teal.Rd b/man/module_teal.Rd index f2bc5d95b5..3b5461d6f2 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -66,7 +66,7 @@ performed: \itemize{ \item data loading in \code{\link{module_init_data}} \item data filtering in \code{\link{module_filter_data}} -\item data transformation in \code{\link{module_transform_data}} +\item data transformation in \code{\link{module_teal_transform_module}} } } diff --git a/man/module_transform_data.Rd b/man/module_teal_transform_module.Rd similarity index 70% rename from man/module_transform_data.Rd rename to man/module_teal_transform_module.Rd index e7a42ebf43..79c086ab51 100644 --- a/man/module_transform_data.Rd +++ b/man/module_teal_transform_module.Rd @@ -1,14 +1,14 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/module_transform_data.R -\name{module_transform_data} -\alias{module_transform_data} -\alias{ui_transform_data} -\alias{srv_transform_data} +\name{module_teal_transform_module} +\alias{module_teal_transform_module} +\alias{ui_teal_transform_module} +\alias{srv_teal_transform_module} \title{Module to transform \code{reactive} \code{teal_data}} \usage{ -ui_transform_data(id, transforms, class = "well") +ui_teal_transform_module(id, transforms, class = "well") -srv_transform_data( +srv_teal_transform_module( id, data, transforms, @@ -31,7 +31,6 @@ and display a generic failure message.} \code{reactive} \code{teal_data} } \description{ -Module calls multiple \code{\link{module_teal_data}} in sequence so that \verb{reactive teal_data} output +Module calls \code{\link[=teal_transform_module]{teal_transform_module()}} in sequence so that \verb{reactive teal_data} output from one module is handed over to the following module's input. } -\keyword{internal} diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index a6034cca13..0818e1832e 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -39,7 +39,7 @@ App user will be able to interact and change the data output from the module mul \item{data}{(\code{teal_data_module}) object} -\item{expr}{(\code{expression}) to evaluate. Must be inline code. See} +\item{expr}{(\code{expression}) to evaluate. Must be inline code. See \code{\link[=within]{within()}}} \item{...}{See \code{Details}.} } diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index 33865d976c..29872f6507 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -26,11 +26,11 @@ module( modules(..., label = "root") -\method{format}{teal_module}(x, indent = 0, ...) +\method{format}{teal_module}(x, ...) \method{print}{teal_module}(x, ...) -\method{format}{teal_modules}(x, indent = 0, ...) +\method{format}{teal_modules}(x, ...) set_datanames(modules, datanames) @@ -75,13 +75,10 @@ The keyword \code{"all"} provides all datasets available in \code{data} passed t \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} -\item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. +\item{transformers}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformers} -will be added to the \code{datanames}. - -When the transformation does not have sufficient input data, the resulting data will fallback -to the last successful transform or, in case there are none, to the filtered data.} +will be added to the \code{datanames}.} \item{...}{\itemize{ \item For \code{modules()}: (\code{teal_module} or \code{teal_modules}) Objects to wrap into a tab. @@ -90,8 +87,6 @@ to the last successful transform or, in case there are none, to the filtered dat \item{x}{(\code{teal_module} or \code{teal_modules}) Object to format/print.} -\item{indent}{(\code{integer(1)}) Indention level; each nested element is indented one level more.} - \item{modules}{(\code{teal_module} or \code{teal_modules})} } \value{ diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 8f2a22386b..3bb5ceb627 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -19,8 +19,7 @@ teal_transform_module( \code{shiny} module server function; that takes \code{id} and \code{data} argument, where the \code{id} is the module id and \code{data} is the reactive \code{teal_data} input. The server function must return reactive expression containing \code{teal_data} object. -Alternatively, \code{language} object is a simplified version of the \code{server} where \verb{input$} -can be used to link interactive value with the \code{teal_data} code.} +To simplify use \code{\link[=make_teal_transform_server]{make_teal_transform_server()}}.} \item{label}{(\code{character(1)}) Label of the module.} @@ -32,16 +31,50 @@ filters of all datasets. \code{datanames} will be automatically appended to the \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} -Create a \code{teal_data_module} object for custom transformation of data for pre-processing -before passing the data into the module. +\code{teal_transform_module} creates a shiny-module to transform data in a \code{teal} application. } -\details{ -\code{teal_transform_module} creates a \code{\link{teal_data_module}} object to transform data in a \code{teal} -application. This transformation happens after the data has passed through the filtering activity -in teal. The transformed data is then sent to the server of the \code{\link[=teal_module]{teal_module()}}. +\section{Transforming \code{teal} module's input}{ +This transformation happens after the data has passed through the filtering activity in teal. The +transformed data is then sent to the server of the \code{\link[=teal_module]{teal_module()}}. Process is handled by \code{teal} +internals. See vignette \code{vignette("data-transform-as-shiny-module", package = "teal")} for more details. } + +\section{Decorating \code{teal} module's output}{ +\code{teal_transform_module}'s purpose is to modify any object created in \code{\link[teal.data:teal_data]{teal.data::teal_data}}. It means that an +app-developer can use \code{teal_transform_module} to modify data but also outputted tables, listings and graphs. +Some \code{\link{teal_modules}} enables app developer to inject custom shiny module to modify displayed output. +To handle these \code{decorators} inside of your module use \code{\link[=ui_teal_transform_module]{ui_teal_transform_module()}} and \link{srv_teal_transform_module}. +(todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by +ui/srv_teal_transform_module()... . Alternatively, decorators could be a \code{\link[=module]{module()}}'s argument) +} + +\section{\code{server} as a language}{ +Server function in \code{teal_transform_module} must return \code{reactive} containing \link[teal.data:teal_data]{teal.data::teal_data} object. +Consider sinmple transformer which doesn't require any advanced reactivity, example \code{server} might have a +following form: + +\if{html}{\out{
}}\preformatted{function(id, data) \{ + moduleServer(id, function(input, output, session) \{ + reactive(\{ + within( + data(), + expr = x <- subset(x, col == level), + level = input$level + ) + \}) + \}) +\} +}\if{html}{\out{
}} + +Above can be simplified to presented below, where \code{level} will be automatically substituted with +respective input matched by its name. + +\if{html}{\out{
}}\preformatted{make_teal_transform_module(expr = expression(x <- subset(x, col == level))) +}\if{html}{\out{
}} +} + \examples{ my_transformers <- list( teal_transform_module( @@ -78,17 +111,6 @@ my_transformers <- list( }) }) } - ), - teal_transform_module( - label = "Simplified interactive transform for iris", - datanames = "iris", - ui = function(id) { - ns <- NS(id) - tags$div( - numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) - ) - }, - server = expression(iris <- head(iris, n_rows)) ) ) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index 306dd3217c..7dd164f4df 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -169,7 +169,7 @@ However, there is a way to customize the placement of the UI inside the module c In order to place the transformation UI inside the module there are few things one has to do: 1. Create a custom module wrapper function. 2. Call the desired module in the module wrapper function and store it in a variable so it's UI can be modified. -3. Modify the UI of the module with the transform UI at the desired location by calling the `ui_transform_data`. Note that in order for the transform to work you need to change the namespace of the `id` by passing `NS(gsub("-module$", "", id), "data_transform")`. +3. Modify the UI of the module with the transform UI at the desired location by calling the `ui_teal_transform_module`. Note that in order for the transform to work you need to change the namespace of the `id` by passing `NS(gsub("-module$", "", id), "data_transform")`. 4. Set the `custom_ui` attribute of the `module$transformers` to `TRUE`. Now the custom module should embed the transformation UI inside the module content. @@ -183,7 +183,7 @@ example_module_encoding <- function(label = "example module (on encoding)", data teal.widgets::standard_layout( output = verbatimTextOutput(ns("text")), encoding = tags$div( - ui_transform_data(NS(gsub("-module$", "", id), "data_transform"), transformers), + ui_teal_transform_module(NS(gsub("-module$", "", id), "data_transform"), transformers), selectInput(ns("dataname"), "Choose a dataset", choices = NULL), teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") ) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index d8c5540520..15c838dfe4 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -15,7 +15,8 @@ vignette: > `teal_transform_module` without `ui`. Nothing will be displayed, only server code will be executed. - ```{r} +```{r} +library(teal) static_decorator <- teal_transform_module( server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -30,20 +31,24 @@ static_decorator <- teal_transform_module( }) } ) - ``` - - -Using language will result in `teal_transform_module` without `ui`. +``` - ```{r} -static_decorator_lang <- quote(plot <- plot + - ggtitle("This is title") + - xlab("x axis title")) - ``` +Use `make_teal_transform_server(expr)` to create a transformer without repeating a boiler plate code (`function(id, data)`, `moduleServer`, `reactive`, `within(data, ...)`) +```{r} +static_decorator_lang <- teal_transform_module( + server = make_teal_transform_server( + expression( + plot <- plot + + ggtitle("This is title") + + xlab("x axis title") + ) + ) +) +``` Adding interactivity requires creating a proper shiny module. - ```{r} +```{r} interactive_decorator <- teal_transform_module( ui = function(id) { ns <- NS(id) @@ -67,23 +72,68 @@ interactive_decorator <- teal_transform_module( }) } ) - ``` +``` +There is `make_teal_transform_server(expr)` which simplifies a creation of `teal_transform_module`'s server. One can +simply specify `expression` within which object names can be replaced with respective input. + +```{r} +interactive_decorator_lang <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = make_teal_transform_server( + expression( + plot <- plot + + ggtitle("This is title") + + xlab(x_axis_title) + ) + ) +) +``` -Thanks to the function-constructor app developer doesn't need to know what is the object name inside of the module's -`server`. One can create any single argument function. Mapping `x` -> `plot` happens in `decorate_teal_data` and -requires to specify `output_name = "plot"` (modules' developer duty). + +Since `teal_transform_module` when created has constant object names it means that it is hard to robustly reuse +transformer in any `tm_` module. It is recomended for external parties to collect library of "decorators" as functions +to potentially adjust the content of the evaluated expression to `teal_modules`'s internals. See the following example +and focus on `output_name`. ```{r} -fun_decorator <- function(x) { - x <- x + ggtitle("This is title") + xlab("x_axis_title") +gg_xlab_decorator <- function(output_name) { + teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + data() |> within( + { + output_name <- output_name + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title, + output_name = as.name(output_name) + ) + }) + }) + } + ) } ``` + Failures in decorators are handled by internal `teal` mechanism called `trigger_on_success`, which will never break `data` object inside of the module. Instead decorator will be ignored and relevant error message will be shown. - ```{r} +```{r} failing_decorator <- teal_transform_module( ui = function(id) { ns <- NS(id) @@ -101,33 +151,26 @@ failing_decorator <- teal_transform_module( ## Example module -To include decorator to the `teal_module` one needs to add an extra argument to the module-constructor function `tm_`. -Decorator passed to the constructor needs to go through `decorate_teal_data()` in order to cast `language` or `function` object into `teal_transform_module`. _`decorate_teal_data()` wouldn't be necessary if an app developer -always provides `teal_transform_module`. We could extend `teal_transform_module()` constructor by `expr` argument instead but using `function` would be impossible then._ - -Decorators should be passed via `ui_args` and `server_args` to the module's `ui` and `server`, where they should be -consumed by `ui/srv_transform_data`. +To include decorators to the `teal_module` one needs to add an extra argument to the module-constructor function `tm_`. Decorators should be passed via `ui_args` and `server_args` to the module's `ui` and `server`, where they should be +consumed by `ui/srv_teal_transform_module`. ```{r} -library(ggplot2) -tm_decorated_plot <- function(label = "module", decorator = teal_transform_module()) { - decorator_obj <- decorate_teal_data(x = decorator, output_name = "plot") - +tm_decorated_plot <- function(label = "module", transforms = list(), decorators = teal_transform_module()) { module( label = label, - ui = function(id, decorator) { + ui = function(id, decorators) { ns <- NS(id) div( selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - ui_transform_data(ns("decorate"), transforms = decorator), + ui_teal_transform_module(ns("decorate"), transforms = decorators), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) }, - server = function(id, data, decorator) { + server = function(id, data, decorators) { moduleServer(id, function(input, output, session) { observeEvent(data(), { updateSelectInput(inputId = "dataname", choices = teal.data::datanames(data())) @@ -152,7 +195,7 @@ tm_decorated_plot <- function(label = "module", decorator = teal_transform_modul ) }) - q2 <- srv_transform_data("decorate", data = q1, transforms = decorator) + q2 <- srv_teal_transform_module("decorate", data = q1, transforms = decorators) plot_r <- reactive({ req(q2()) @@ -165,8 +208,8 @@ tm_decorated_plot <- function(label = "module", decorator = teal_transform_modul }) }) }, - ui_args = list(decorator = decorator_obj), - server_args = list(decorator = decorator_obj) + ui_args = list(decorators = decorators), + server_args = list(decorators = decorators) ) } ``` @@ -174,14 +217,16 @@ tm_decorated_plot <- function(label = "module", decorator = teal_transform_modul ## Example app ```{r} +library(ggplot2) app <- init( data = teal_data(iris = iris, mtcars = mtcars), modules = modules( tm_decorated_plot("identity"), tm_decorated_plot("no-ui", decorator = static_decorator), tm_decorated_plot("lang", decorator = static_decorator_lang), - tm_decorated_plot("fun", decorator = fun_decorator), tm_decorated_plot("interactive", decorator = interactive_decorator), + tm_decorated_plot("interactive-from lang", decorator = interactive_decorator_lang), + tm_decorated_plot("from-fun", decorator = gg_xlab_decorator("plot")), tm_decorated_plot("failing", decorator = failing_decorator) ) ) From 30bb2489e7150f8f301ce4435a3b2e78dbb99c27 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:06:36 +0200 Subject: [PATCH 010/165] move teal_transform_module to a new file instead of keeping with teal_data_module --- DESCRIPTION | 1 + R/teal_data_module.R | 180 -------------------------- R/teal_transform_module.R | 179 +++++++++++++++++++++++++ man/extract_transformers.Rd | 2 +- man/make_teal_transform_server.Rd | 2 +- man/teal_transform_module.Rd | 2 +- vignettes/decorate-modules-output.Rmd | 4 +- 7 files changed, 185 insertions(+), 185 deletions(-) create mode 100644 R/teal_transform_module.R diff --git a/DESCRIPTION b/DESCRIPTION index 0fbab7e2c9..1d0c613512 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -119,6 +119,7 @@ Collate: 'teal_reporter.R' 'teal_slices-store.R' 'teal_slices.R' + 'teal_transform_module.R' 'utils.R' 'validate_inputs.R' 'validations.R' diff --git a/R/teal_data_module.R b/R/teal_data_module.R index 4c853fee5f..a08068a85e 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -82,183 +82,3 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { once = once ) } - -#' Data module for `teal` transformers. -#' -#' @description -#' `r lifecycle::badge("experimental")` -#' -#' `teal_transform_module` creates a shiny-module to transform data in a `teal` application. -#' -#' # Transforming `teal` module's input -#' -#' This transformation happens after the data has passed through the filtering activity in teal. The -#' transformed data is then sent to the server of the [teal_module()]. Process is handled by `teal` -#' internals. -#' -#' See vignette `vignette("data-transform-as-shiny-module", package = "teal")` for more details. -#' -#' # Decorating `teal` module's output -#' -#' `teal_transform_module`'s purpose is to modify any object created in [`teal.data::teal_data`]. It means that an -#' app-developer can use `teal_transform_module` to modify data but also outputted tables, listings and graphs. -#' Some [`teal_modules`] enables app developer to inject custom shiny module to modify displayed output. -#' To handle these `decorators` inside of your module use [ui_teal_transform_module()] and [srv_teal_transform_module]. -#' (todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by -#' ui/srv_teal_transform_module()... . Alternatively, decorators could be a [module()]'s argument) -#' -#' # `server` as a language -#' -#' Server function in `teal_transform_module` must return `reactive` containing [teal.data::teal_data] object. -#' Consider sinmple transformer which doesn't require any advanced reactivity, example `server` might have a -#' following form: -#' -#' ``` -#' function(id, data) { -#' moduleServer(id, function(input, output, session) { -#' reactive({ -#' within( -#' data(), -#' expr = x <- subset(x, col == level), -#' level = input$level -#' ) -#' }) -#' }) -#' } -#' ``` -#' -#' Above can be simplified to presented below, where `level` will be automatically substituted with -#' respective input matched by its name. -#' -#' ``` -#' make_teal_transform_module(expr = expression(x <- subset(x, col == level))) -#' ``` -#' @inheritParams teal_data_module -#' @param server (`function(id, data)` or `language`) -#' `shiny` module server function; that takes `id` and `data` argument, -#' where the `id` is the module id and `data` is the reactive `teal_data` input. -#' The server function must return reactive expression containing `teal_data` object. -#' To simplify use [make_teal_transform_server()]. -#' @param datanames (`character`) -#' Names of the datasets that are relevant for the module. The -#' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show -#' filters of all datasets. `datanames` will be automatically appended to the [modules()] `datanames`. -#' -#' -#' @examples -#' my_transformers <- list( -#' teal_transform_module( -#' label = "Static transform for iris", -#' datanames = "iris", -#' server = function(id, data) { -#' moduleServer(id, function(input, output, session) { -#' reactive({ -#' within(data(), { -#' iris <- head(iris, 5) -#' }) -#' }) -#' }) -#' } -#' ), -#' teal_transform_module( -#' label = "Interactive transform for iris", -#' datanames = "iris", -#' ui = function(id) { -#' ns <- NS(id) -#' tags$div( -#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) -#' ) -#' }, -#' server = function(id, data) { -#' moduleServer(id, function(input, output, session) { -#' reactive({ -#' within(data(), -#' { -#' iris <- head(iris, num_rows) -#' }, -#' num_rows = input$n_rows -#' ) -#' }) -#' }) -#' } -#' ) -#' ) -#' -#' @name teal_transform_module -#' -#' @export -teal_transform_module <- function(ui = NULL, - server = function(id, data) data, - label = "transform module", - datanames = "all") { - structure( - list( - ui = ui, - server = function(id, data) { - data_out <- server(id, data) - decorate_err_msg( - assert_reactive(data_out), - pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), - post = "Please make sure that this module returns a 'reactive` object containing 'teal_data' class of object." # nolint: line_length_linter. - ) - } - ), - label = label, - datanames = datanames, - class = c("teal_transform_module", "teal_data_module") - ) -} - -#' Make teal_transform_module's server -#' -#' A factory function to simplify creation of a [`teal_transform_module`]'s server. Specified `expr` -#' is wrapped in a shiny module function and output can be passed to the `server` argument in -#' [teal_transform_module()] call. Such server function can be linked with ui and values from the -#' inputs can be used in the expression. Object names specified in the expression will be substituted -#' with the value of the respective input (matched by the name) - for example in -#' `expression(graph <- graph + ggtitle(title))` object `title` will be replaced with the value of -#' `input$title`. -#' @param expr (`language`) -#' An R call which will be evaluated within [`teal.data::teal_data`] environment. -#' @return `function(id, data)` returning `shiny` module -#' @examples -#' -#' teal_transform_module( -#' label = "Simplified interactive transform for iris", -#' datanames = "iris", -#' ui = function(id) { -#' ns <- NS(id) -#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) -#' }, -#' server = make_teal_transform_server(expression(iris <- head(iris, n_rows))) -#' ) -#' -#' @export -make_teal_transform_server <- function(expr) { - function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - call_with_inputs <- lapply(expr, function(x) { - do.call( - what = substitute, - args = list(expr = x, env = reactiveValuesToList(input)) - ) - }) - eval_code(object = data(), code = as.expression(call_with_inputs)) - }) - }) - } -} - -#' Extract all `transformers` from `modules`. -#' -#' @param modules `teal_modules` or `teal_module` -#' @return A list of `teal_transform_module` nested in the same way as input `modules`. -#' @keywords internal -extract_transformers <- function(modules) { - if (inherits(modules, "teal_module")) { - modules$transformers - } else if (inherits(modules, "teal_modules")) { - lapply(modules$children, extract_transformers) - } -} diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R new file mode 100644 index 0000000000..9596d6df7a --- /dev/null +++ b/R/teal_transform_module.R @@ -0,0 +1,179 @@ +#' Data module for `teal` transformers. +#' +#' @description +#' `r lifecycle::badge("experimental")` +#' +#' `teal_transform_module` creates a shiny-module to transform data in a `teal` application. +#' +#' # Transforming `teal` module's input +#' +#' This transformation happens after the data has passed through the filtering activity in teal. The +#' transformed data is then sent to the server of the [teal_module()]. Process is handled by `teal` +#' internals. +#' +#' See vignette `vignette("data-transform-as-shiny-module", package = "teal")` for more details. +#' +#' # Decorating `teal` module's output +#' +#' `teal_transform_module`'s purpose is to modify any object created in [`teal.data::teal_data`]. It means that an +#' app-developer can use `teal_transform_module` to modify data but also outputted tables, listings and graphs. +#' Some [`teal_modules`] enables app developer to inject custom shiny module to modify displayed output. +#' To handle these `decorators` inside of your module use [ui_teal_transform_module()] and [srv_teal_transform_module]. +#' (todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by +#' ui/srv_teal_transform_module()... . Alternatively, decorators could be a [module()]'s argument) +#' +#' # `server` as a language +#' +#' Server function in `teal_transform_module` must return `reactive` containing [teal.data::teal_data] object. +#' Consider sinmple transformer which doesn't require any advanced reactivity, example `server` might have a +#' following form: +#' +#' ``` +#' function(id, data) { +#' moduleServer(id, function(input, output, session) { +#' reactive({ +#' within( +#' data(), +#' expr = x <- subset(x, col == level), +#' level = input$level +#' ) +#' }) +#' }) +#' } +#' ``` +#' +#' Above can be simplified to presented below, where `level` will be automatically substituted with +#' respective input matched by its name. +#' +#' ``` +#' make_teal_transform_module(expr = expression(x <- subset(x, col == level))) +#' ``` +#' @inheritParams teal_data_module +#' @param server (`function(id, data)` or `language`) +#' `shiny` module server function; that takes `id` and `data` argument, +#' where the `id` is the module id and `data` is the reactive `teal_data` input. +#' The server function must return reactive expression containing `teal_data` object. +#' To simplify use [make_teal_transform_server()]. +#' @param datanames (`character`) +#' Names of the datasets that are relevant for the module. The +#' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show +#' filters of all datasets. `datanames` will be automatically appended to the [modules()] `datanames`. +#' +#' +#' @examples +#' my_transformers <- list( +#' teal_transform_module( +#' label = "Static transform for iris", +#' datanames = "iris", +#' server = function(id, data) { +#' moduleServer(id, function(input, output, session) { +#' reactive({ +#' within(data(), { +#' iris <- head(iris, 5) +#' }) +#' }) +#' }) +#' } +#' ), +#' teal_transform_module( +#' label = "Interactive transform for iris", +#' datanames = "iris", +#' ui = function(id) { +#' ns <- NS(id) +#' tags$div( +#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) +#' ) +#' }, +#' server = function(id, data) { +#' moduleServer(id, function(input, output, session) { +#' reactive({ +#' within(data(), +#' { +#' iris <- head(iris, num_rows) +#' }, +#' num_rows = input$n_rows +#' ) +#' }) +#' }) +#' } +#' ) +#' ) +#' +#' @name teal_transform_module +#' +#' @export +teal_transform_module <- function(ui = NULL, + server = function(id, data) data, + label = "transform module", + datanames = "all") { + structure( + list( + ui = ui, + server = function(id, data) { + data_out <- server(id, data) + decorate_err_msg( + assert_reactive(data_out), + pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), + post = "Please make sure that this module returns a 'reactive` object containing 'teal_data' class of object." # nolint: line_length_linter. + ) + } + ), + label = label, + datanames = datanames, + class = c("teal_transform_module", "teal_data_module") + ) +} + +#' Make teal_transform_module's server +#' +#' A factory function to simplify creation of a [`teal_transform_module`]'s server. Specified `expr` +#' is wrapped in a shiny module function and output can be passed to the `server` argument in +#' [teal_transform_module()] call. Such server function can be linked with ui and values from the +#' inputs can be used in the expression. Object names specified in the expression will be substituted +#' with the value of the respective input (matched by the name) - for example in +#' `expression(graph <- graph + ggtitle(title))` object `title` will be replaced with the value of +#' `input$title`. +#' @param expr (`language`) +#' An R call which will be evaluated within [`teal.data::teal_data`] environment. +#' @return `function(id, data)` returning `shiny` module +#' @examples +#' +#' teal_transform_module( +#' label = "Simplified interactive transform for iris", +#' datanames = "iris", +#' ui = function(id) { +#' ns <- NS(id) +#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) +#' }, +#' server = make_teal_transform_server(expression(iris <- head(iris, n_rows))) +#' ) +#' +#' @export +make_teal_transform_server <- function(expr) { + function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + call_with_inputs <- lapply(expr, function(x) { + do.call( + what = substitute, + args = list(expr = x, env = reactiveValuesToList(input)) + ) + }) + eval_code(object = data(), code = as.expression(call_with_inputs)) + }) + }) + } +} + +#' Extract all `transformers` from `modules`. +#' +#' @param modules `teal_modules` or `teal_module` +#' @return A list of `teal_transform_module` nested in the same way as input `modules`. +#' @keywords internal +extract_transformers <- function(modules) { + if (inherits(modules, "teal_module")) { + modules$transformers + } else if (inherits(modules, "teal_modules")) { + lapply(modules$children, extract_transformers) + } +} diff --git a/man/extract_transformers.Rd b/man/extract_transformers.Rd index 9af99abe12..7d786e30ed 100644 --- a/man/extract_transformers.Rd +++ b/man/extract_transformers.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_data_module.R +% Please edit documentation in R/teal_transform_module.R \name{extract_transformers} \alias{extract_transformers} \title{Extract all \code{transformers} from \code{modules}.} diff --git a/man/make_teal_transform_server.Rd b/man/make_teal_transform_server.Rd index 9416656c86..10b5cf9643 100644 --- a/man/make_teal_transform_server.Rd +++ b/man/make_teal_transform_server.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_data_module.R +% Please edit documentation in R/teal_transform_module.R \name{make_teal_transform_server} \alias{make_teal_transform_server} \title{Make teal_transform_module's server} diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 3bb5ceb627..7575ceaba9 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_data_module.R +% Please edit documentation in R/teal_transform_module.R \name{teal_transform_module} \alias{teal_transform_module} \title{Data module for \code{teal} transformers.} diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 15c838dfe4..b5fa1e298b 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -101,7 +101,7 @@ transformer in any `tm_` module. It is recomended for external parties to collec to potentially adjust the content of the evaluated expression to `teal_modules`'s internals. See the following example and focus on `output_name`. -```{r} + ```{r} gg_xlab_decorator <- function(output_name) { teal_transform_module( ui = function(id) { @@ -127,7 +127,7 @@ gg_xlab_decorator <- function(output_name) { } ) } -``` + ``` Failures in decorators are handled by internal `teal` mechanism called `trigger_on_success`, which will never From 1c75925e5a564d5a672d2ccdcb782fe3b0c76451 Mon Sep 17 00:00:00 2001 From: go_gonzo Date: Wed, 30 Oct 2024 13:25:31 +0100 Subject: [PATCH 011/165] post merge fixes --- NAMESPACE | 4 +- R/dummy_functions.R | 4 +- R/init.R | 2 +- R/module_nested_tabs.R | 31 ++++++-------- R/module_teal.R | 2 +- R/module_teal_data.R | 22 +++++----- R/module_transform_data.R | 32 +++++++-------- R/modules.R | 32 +++++++-------- R/teal_transform_module.R | 20 ++++----- man/example_module.Rd | 11 ++--- ..._transformers.Rd => extract_transforms.Rd} | 10 ++--- man/module_teal.Rd | 2 +- man/module_teal_data.Rd | 6 +-- man/module_teal_transform_module.Rd | 40 ------------------ man/module_transform_data.Rd | 41 +++++++++++++++++++ man/teal_modules.Rd | 21 +++++----- man/teal_transform_module.Rd | 24 +++++------ 17 files changed, 150 insertions(+), 154 deletions(-) rename man/{extract_transformers.Rd => extract_transforms.Rd} (61%) delete mode 100644 man/module_teal_transform_module.Rd create mode 100644 man/module_transform_data.Rd diff --git a/NAMESPACE b/NAMESPACE index 224c65d040..e2535df1aa 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -31,15 +31,15 @@ export(reporter_previewer_module) export(set_datanames) export(show_rcode_modal) export(srv_teal) -export(srv_teal_transform_module) export(srv_teal_with_splash) +export(srv_transform_data) export(tdata2env) export(teal_data_module) export(teal_slices) export(teal_transform_module) export(ui_teal) -export(ui_teal_transform_module) export(ui_teal_with_splash) +export(ui_transform_data) export(validate_has_data) export(validate_has_elements) export(validate_has_variable) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index fd3f067699..ed76b5aa55 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -13,7 +13,7 @@ #' shinyApp(app$ui, app$server) #' } #' @export -example_module <- function(label = "example teal module", datanames = "all", transformers = list()) { +example_module <- function(label = "example teal module", datanames = "all", transforms = list()) { checkmate::assert_string(label) ans <- module( label, @@ -59,7 +59,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra ) }, datanames = datanames, - transformers = transformers + transforms = transforms ) attr(ans, "teal_bookmarkable") <- TRUE ans diff --git a/R/init.R b/R/init.R index a62034eaf8..eddb9e6d19 100644 --- a/R/init.R +++ b/R/init.R @@ -212,7 +212,7 @@ init <- function(data, } is_modules_ok <- check_modules_datanames(modules, ls(teal.code::get_env(data))) - if (!isTRUE(is_modules_ok) && length(unlist(extract_transformers(modules))) == 0) { + if (!isTRUE(is_modules_ok) && length(unlist(extract_transforms(modules))) == 0) { lapply(is_modules_ok$string, warning, call. = FALSE) } diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 02f5738529..15afe4b854 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -94,11 +94,11 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ui_teal <- tagList( shinyjs::hidden( tags$div( - id = ns("transformer_failure_info"), + id = ns("transform_failure_info"), class = "teal_validated", div( class = "teal-output-warning", - "One of transformers failed. Please fix and continue." + "One of transforms failed. Please fix and continue." ) ) ), @@ -125,10 +125,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { width = 3, ui_data_summary(ns("data_summary")), ui_filter_data(ns("filter_panel")), - if (length(modules$transformers) > 0 && !isTRUE(attr(modules$transformers, "custom_ui"))) { - ui_teal_transform_module(ns("data_transform"), transforms = modules$transformers, class = "well") - }, - ui_transform_data(ns("data_transform"), transformers = modules$transformers, class = "well"), + ui_transform_data(ns("data_transform"), transforms = modules$transforms, class = "well"), class = "teal_secondary_col" ) ) @@ -263,27 +260,25 @@ srv_teal_module.teal_module <- function(id, data_rv = data_rv, is_active = is_active ) - is_transformer_failed <- reactiveValues() + is_transform_failed <- reactiveValues() transformed_teal_data <- srv_transform_data( "data_transform", data = filtered_teal_data, - transformers = modules$transformers, + transforms = modules$transforms, modules = modules, - is_transformer_failed = is_transformer_failed + is_transform_failed = is_transform_failed ) - any_transformer_failed <- reactive({ - any(unlist(reactiveValuesToList(is_transformer_failed))) + any_transform_failed <- reactive({ + any(unlist(reactiveValuesToList(is_transform_failed))) }) - observeEvent(any_transformer_failed(), { - if (isTRUE(any_transformer_failed())) { + observeEvent(any_transform_failed(), { + if (isTRUE(any_transform_failed())) { shinyjs::hide("teal_module_ui") - shinyjs::hide("validate_datanames") - shinyjs::show("transformer_failure_info") + shinyjs::show("transform_failure_info") } else { shinyjs::show("teal_module_ui") - shinyjs::show("validate_datanames") - shinyjs::hide("transformer_failure_info") + shinyjs::hide("transform_failure_info") } }) @@ -294,7 +289,7 @@ srv_teal_module.teal_module <- function(id, .subset_teal_data(all_teal_data, module_datanames) }) - srv_validate_reactive_teal_data( + srv_check_module_datanames( "validate_datanames", data = module_teal_data, modules = modules diff --git a/R/module_teal.R b/R/module_teal.R index e9e6b82f7e..478e816e91 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -20,7 +20,7 @@ #' performed: #' - data loading in [`module_init_data`] #' - data filtering in [`module_filter_data`] -#' - data transformation in [`module_teal_transform_module`] +#' - data transformation in [`module_transform_data`] #' #' ## Fallback on failure #' diff --git a/R/module_teal_data.R b/R/module_teal_data.R index 91294f3c17..3ea560ee4d 100644 --- a/R/module_teal_data.R +++ b/R/module_teal_data.R @@ -25,8 +25,8 @@ #' @param data_module (`teal_data_module`) #' @param modules (`teal_modules` or `teal_module`) For `datanames` validation purpose #' @param validate_shiny_silent_error (`logical`) If `TRUE`, then `shiny.silent.error` is validated and -#' @param is_transformer_failed (`reactiveValues`) contains `logical` flags named after each transformer. -#' Help to determine if any previous transformer failed, so that following transformers can be disabled +#' @param is_transform_failed (`reactiveValues`) contains `logical` flags named after each transform. +#' Help to determine if any previous transform failed, so that following transforms can be disabled #' and display a generic failure message. #' #' @return `reactive` `teal_data` @@ -53,29 +53,29 @@ srv_teal_data <- function(id, data_module = function(id) NULL, modules = NULL, validate_shiny_silent_error = TRUE, - is_transformer_failed = reactiveValues()) { + is_transform_failed = reactiveValues()) { checkmate::assert_string(id) checkmate::assert_function(data_module, args = "id") checkmate::assert_multi_class(modules, c("teal_modules", "teal_module"), null.ok = TRUE) - checkmate::assert_class(is_transformer_failed, "reactivevalues") + checkmate::assert_class(is_transform_failed, "reactivevalues") moduleServer(id, function(input, output, session) { logger::log_debug("srv_teal_data initializing.") - is_transformer_failed[[id]] <- FALSE + is_transform_failed[[id]] <- FALSE data_out <- data_module(id = "data") data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) observeEvent(data_handled(), { if (!inherits(data_handled(), "teal_data")) { - is_transformer_failed[[id]] <- TRUE + is_transform_failed[[id]] <- TRUE } else { - is_transformer_failed[[id]] <- FALSE + is_transform_failed[[id]] <- FALSE } }) is_previous_failed <- reactive({ - idx_this <- which(names(is_transformer_failed) == id) - is_transformer_failed_list <- reactiveValuesToList(is_transformer_failed) - idx_failures <- which(unlist(is_transformer_failed_list)) + idx_this <- which(names(is_transform_failed) == id) + is_transform_failed_list <- reactiveValuesToList(is_transform_failed) + idx_failures <- which(unlist(is_transform_failed_list)) any(idx_failures < idx_this) }) @@ -133,7 +133,7 @@ srv_validate_reactive_teal_data <- function(id, # nolint: object_length output$previous_failed <- renderUI({ if (hide_validation_error()) { shinyjs::hide("validate_messages") - tags$div("One of previous transformers failed. Please fix and continue.", class = "teal-output-warning") + tags$div("One of previous transforms failed. Please fix and continue.", class = "teal-output-warning") } else { shinyjs::show("validate_messages") NULL diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 6b9e04f6b8..1fd7e52211 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -8,12 +8,12 @@ #' @return `reactive` `teal_data` #' #' -#' @name module_teal_transform_module +#' @name module_transform_data NULL #' @export -#' @rdname module_teal_transform_module -ui_teal_transform_module <- function(id, transforms, class = "well") { +#' @rdname module_transform_data +ui_transform_data <- function(id, transforms, class = "well") { checkmate::assert_string(id) if (length(transforms) == 0L) { return(NULL) @@ -25,10 +25,10 @@ ui_teal_transform_module <- function(id, transforms, class = "well") { labels <- lapply(transforms, function(x) attr(x, "label")) ids <- get_unique_labels(labels) - names(transformers) <- ids + names(transforms) <- ids lapply( - names(transformers), + names(transforms), function(name) { child_id <- NS(id)(name) ns <- NS(child_id) @@ -70,8 +70,8 @@ ui_teal_transform_module <- function(id, transforms, class = "well") { } #' @export -#' @rdname module_teal_transform_module -srv_teal_transform_module <- function(id, data, transforms, modules = NULL, is_transformer_failed = reactiveValues()) { +#' @rdname module_transform_data +srv_transform_data <- function(id, data, transforms, modules = NULL, is_transform_failed = reactiveValues()) { checkmate::assert_string(id) assert_reactive(data) checkmate::assert_class(modules, "teal_module", null.ok = TRUE) @@ -90,22 +90,22 @@ srv_teal_transform_module <- function(id, data, transforms, modules = NULL, is_t Reduce( function(data_previous, name) { moduleServer(name, function(input, output, session) { - logger::log_debug("srv_teal_transform_module initializing for { name }.") - is_transformer_failed[[name]] <- FALSE + logger::log_debug("srv_transform_data initializing for { name }.") + is_transform_failed[[name]] <- FALSE data_out <- transforms[[name]]$server(name, data = data_previous) data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) observeEvent(data_handled(), { if (inherits(data_handled(), "teal_data")) { - is_transformer_failed[[name]] <- FALSE + is_transform_failed[[name]] <- FALSE } else { - is_transformer_failed[[name]] <- TRUE + is_transform_failed[[name]] <- TRUE } }) is_previous_failed <- reactive({ - idx_this <- which(names(is_transformer_failed) == name) - is_transformer_failed_list <- reactiveValuesToList(is_transformer_failed) - idx_failures <- which(unlist(is_transformer_failed_list)) + 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) }) @@ -119,7 +119,7 @@ srv_teal_transform_module <- function(id, data, transforms, modules = NULL, is_t output$error_wrapper <- renderUI({ if (is_previous_failed()) { shinyjs::disable(transform_wrapper_id) - tags$div("One of previous transformers failed. Please fix and continue.", class = "teal-output-warning") + tags$div("One of previous transforms failed. Please fix and continue.", class = "teal-output-warning") } else { shinyjs::enable(transform_wrapper_id) shiny::tagList( @@ -133,7 +133,7 @@ srv_teal_transform_module <- function(id, data, transforms, modules = NULL, is_t .trigger_on_success(data_handled) }) }, - x = names(transformers), + x = names(transforms), init = data ) }) diff --git a/R/modules.R b/R/modules.R index fc270e5f54..15d2c13caa 100644 --- a/R/modules.R +++ b/R/modules.R @@ -35,12 +35,12 @@ setOldClass("teal_modules") #' in a warning. Datasets with names starting with . are ignored globally unless explicitly listed #' in `datanames`. #' -#' # `datanames` with `transformers` -#' When transformers are specified, their `datanames` are added to the module’s `datanames`, which +#' # `datanames` with `transforms` +#' When transforms are specified, their `datanames` are added to the module’s `datanames`, which #' changes the behavior as follows: -#' - If `module(datanames)` is `NULL` and the `transformers` have defined `datanames`, the sidebar -#' will appear showing the `transformers`' datasets, instead of being hidden. -#' - If `module(datanames)` is set to specific values and any `transformer` has `datanames = "all"`, +#' - If `module(datanames)` is `NULL` and the `transforms` have defined `datanames`, the sidebar +#' will appear showing the `transforms`' datasets, instead of being hidden. +#' - If `module(datanames)` is set to specific values and any `transform` has `datanames = "all"`, #' the module may receive extra datasets that could be unnecessary #' #' @param label (`character(1)`) Label shown in the navigation item for the module or module group. @@ -69,14 +69,14 @@ setOldClass("teal_modules") #' There are 2 reserved values that have specific behaviors: #' - The keyword `"all"` includes all datasets available in the data passed to the teal application. #' - `NULL` hides the sidebar panel completely. -#' - If `transformers` are specified, their `datanames` are automatically added to this `datanames` +#' - If `transforms` are specified, their `datanames` are automatically added to this `datanames` #' argument. #' @param server_args (named `list`) with additional arguments passed on to the server function. #' @param ui_args (named `list`) with additional arguments passed on to the UI function. #' @param x (`teal_module` or `teal_modules`) Object to format/print. -#' @param transformers (`list` of `teal_transform_module`) that will be applied to transform module's data input. +#' @param transforms (`list` of `teal_transform_module`) that will be applied to transform module's data input. #' Each transform module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the list. -#' If so, the module developer is responsible to display the UI in the module itself. `datanames` of the `transformers` +#' If so, the module developer is responsible to display the UI in the module itself. `datanames` of the `transforms` #' will be added to the `datanames`. #' #' @@ -159,7 +159,7 @@ module <- function(label = "module", datanames = "all", server_args = NULL, ui_args = NULL, - transformers = list()) { + transforms = list()) { # argument checking (independent) ## `label` checkmate::assert_string(label) @@ -266,16 +266,16 @@ module <- function(label = "module", ) } - ## `transformers` - if (inherits(transformers, "teal_transform_module")) { - transformers <- list(transformers) + ## `transforms` + if (inherits(transforms, "teal_transform_module")) { + transforms <- list(transforms) } - checkmate::assert_list(transformers, types = "teal_transform_module") - transformer_datanames <- unlist(lapply(transformers, attr, "datanames")) + checkmate::assert_list(transforms, types = "teal_transform_module") + transform_datanames <- unlist(lapply(transforms, attr, "datanames")) combined_datanames <- if (identical(datanames, "all")) { "all" } else { - union(datanames, transformer_datanames) + union(datanames, transform_datanames) } structure( @@ -286,7 +286,7 @@ module <- function(label = "module", datanames = combined_datanames, server_args = server_args, ui_args = ui_args, - transformers = transformers + transforms = transforms ), class = "teal_module" ) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 9596d6df7a..9c1b0f8504 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -1,4 +1,4 @@ -#' Data module for `teal` transformers. +#' Data module for `teal` transforms. #' #' @description #' `r lifecycle::badge("experimental")` @@ -18,14 +18,14 @@ #' `teal_transform_module`'s purpose is to modify any object created in [`teal.data::teal_data`]. It means that an #' app-developer can use `teal_transform_module` to modify data but also outputted tables, listings and graphs. #' Some [`teal_modules`] enables app developer to inject custom shiny module to modify displayed output. -#' To handle these `decorators` inside of your module use [ui_teal_transform_module()] and [srv_teal_transform_module]. +#' To handle these `decorators` inside of your module use [ui_transform_data()] and [srv_transform_data]. #' (todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by -#' ui/srv_teal_transform_module()... . Alternatively, decorators could be a [module()]'s argument) +#' ui/srv_transform_data()... . Alternatively, decorators could be a [module()]'s argument) #' #' # `server` as a language #' #' Server function in `teal_transform_module` must return `reactive` containing [teal.data::teal_data] object. -#' Consider sinmple transformer which doesn't require any advanced reactivity, example `server` might have a +#' Consider sinmple transform which doesn't require any advanced reactivity, example `server` might have a #' following form: #' #' ``` @@ -46,7 +46,7 @@ #' respective input matched by its name. #' #' ``` -#' make_teal_transform_module(expr = expression(x <- subset(x, col == level))) +#' make_transform_data(expr = expression(x <- subset(x, col == level))) #' ``` #' @inheritParams teal_data_module #' @param server (`function(id, data)` or `language`) @@ -61,7 +61,7 @@ #' #' #' @examples -#' my_transformers <- list( +#' my_transforms <- list( #' teal_transform_module( #' label = "Static transform for iris", #' datanames = "iris", @@ -165,15 +165,15 @@ make_teal_transform_server <- function(expr) { } } -#' Extract all `transformers` from `modules`. +#' Extract all `transforms` from `modules`. #' #' @param modules `teal_modules` or `teal_module` #' @return A list of `teal_transform_module` nested in the same way as input `modules`. #' @keywords internal -extract_transformers <- function(modules) { +extract_transforms <- function(modules) { if (inherits(modules, "teal_module")) { - modules$transformers + modules$transforms } else if (inherits(modules, "teal_modules")) { - lapply(modules$children, extract_transformers) + lapply(modules$children, extract_transforms) } } diff --git a/man/example_module.Rd b/man/example_module.Rd index 7f59bc01b6..41adb1e4d0 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -7,7 +7,7 @@ example_module( label = "example teal module", datanames = "all", - transformers = list() + transforms = list() ) } \arguments{ @@ -19,13 +19,14 @@ There are 2 reserved values that have specific behaviors: \itemize{ \item The keyword \code{"all"} includes all datasets available in the data passed to the teal application. \item \code{NULL} hides the sidebar panel completely. -\item If \code{transformers} are specified, their \code{datanames} are automatically added to this \code{datanames} +\item If \code{transforms} are specified, their \code{datanames} are automatically added to this \code{datanames} argument. }} -\item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. -Each transform module UI will appear in the \code{teal}'s sidebar panel. -Transformers' \code{datanames} are added to the \code{datanames}. See \code{\link[=teal_transform_module]{teal_transform_module()}}.} +\item{transforms}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. +Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. +If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transforms} +will be added to the \code{datanames}.} } \value{ A \code{teal} module which can be included in the \code{modules} argument to \code{\link[=init]{init()}}. diff --git a/man/extract_transformers.Rd b/man/extract_transforms.Rd similarity index 61% rename from man/extract_transformers.Rd rename to man/extract_transforms.Rd index 7d786e30ed..2d5d1f0a3d 100644 --- a/man/extract_transformers.Rd +++ b/man/extract_transforms.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/teal_transform_module.R -\name{extract_transformers} -\alias{extract_transformers} -\title{Extract all \code{transformers} from \code{modules}.} +\name{extract_transforms} +\alias{extract_transforms} +\title{Extract all \code{transforms} from \code{modules}.} \usage{ -extract_transformers(modules) +extract_transforms(modules) } \arguments{ \item{modules}{\code{teal_modules} or \code{teal_module}} @@ -13,6 +13,6 @@ extract_transformers(modules) A list of \code{teal_transform_module} nested in the same way as input \code{modules}. } \description{ -Extract all \code{transformers} from \code{modules}. +Extract all \code{transforms} from \code{modules}. } \keyword{internal} diff --git a/man/module_teal.Rd b/man/module_teal.Rd index 3b5461d6f2..f2bc5d95b5 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -66,7 +66,7 @@ performed: \itemize{ \item data loading in \code{\link{module_init_data}} \item data filtering in \code{\link{module_filter_data}} -\item data transformation in \code{\link{module_teal_transform_module}} +\item data transformation in \code{\link{module_transform_data}} } } diff --git a/man/module_teal_data.Rd b/man/module_teal_data.Rd index dee0b1087e..90fc509ad5 100644 --- a/man/module_teal_data.Rd +++ b/man/module_teal_data.Rd @@ -15,7 +15,7 @@ srv_teal_data( data_module = function(id) NULL, modules = NULL, validate_shiny_silent_error = TRUE, - is_transformer_failed = reactiveValues() + is_transform_failed = reactiveValues() ) ui_validate_reactive_teal_data(id) @@ -37,8 +37,8 @@ srv_validate_reactive_teal_data( \item{validate_shiny_silent_error}{(\code{logical}) If \code{TRUE}, then \code{shiny.silent.error} is validated and} -\item{is_transformer_failed}{(\code{reactiveValues}) contains \code{logical} flags named after each transformer. -Help to determine if any previous transformer failed, so that following transformers can be disabled +\item{is_transform_failed}{(\code{reactiveValues}) contains \code{logical} flags named after each transform. +Help to determine if any previous transform failed, so that following transforms can be disabled and display a generic failure message.} \item{data}{(\verb{reactive teal_data})} diff --git a/man/module_teal_transform_module.Rd b/man/module_teal_transform_module.Rd deleted file mode 100644 index 3f715a38f6..0000000000 --- a/man/module_teal_transform_module.Rd +++ /dev/null @@ -1,40 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/module_transform_data.R -\name{module_teal_transform_module} -\alias{module_teal_transform_module} -\alias{ui_teal_transform_module} -\alias{srv_teal_transform_module} -\title{Module to transform \code{reactive} \code{teal_data}} -\usage{ -ui_transform_data(id, transformers = list(), class = "well") - -srv_teal_transform_module( - id, - data, - transformers = list(), - modules, - is_transformer_failed = reactiveValues() -) -} -\arguments{ -\item{id}{(\code{character(1)}) Module id} - -\item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. -Each transform module UI will appear in the \code{teal}'s sidebar panel. -Transformers' \code{datanames} are added to the \code{datanames}. See \code{\link[=teal_transform_module]{teal_transform_module()}}.} - -\item{data}{(\verb{reactive teal_data})} - -\item{modules}{(\code{teal_modules} or \code{teal_module}) For \code{datanames} validation purpose} - -\item{is_transformer_failed}{(\code{reactiveValues}) contains \code{logical} flags named after each transformer. -Help to determine if any previous transformer failed, so that following transformers can be disabled -and display a generic failure message.} -} -\value{ -\code{reactive} \code{teal_data} -} -\description{ -Module calls \code{\link[=teal_transform_module]{teal_transform_module()}} in sequence so that \verb{reactive teal_data} output -from one module is handed over to the following module's input. -} diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd new file mode 100644 index 0000000000..84f34cd5d8 --- /dev/null +++ b/man/module_transform_data.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/module_transform_data.R +\name{module_transform_data} +\alias{module_transform_data} +\alias{ui_transform_data} +\alias{srv_transform_data} +\title{Module to transform \code{reactive} \code{teal_data}} +\usage{ +ui_transform_data(id, transforms, class = "well") + +srv_transform_data( + id, + data, + transforms, + modules = NULL, + is_transform_failed = reactiveValues() +) +} +\arguments{ +\item{id}{(\code{character(1)}) Module id} + +\item{transforms}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. +Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. +If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transforms} +will be added to the \code{datanames}.} + +\item{data}{(\verb{reactive 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 transform. +Help to determine if any previous transform failed, so that following transforms can be disabled +and display a generic failure message.} +} +\value{ +\code{reactive} \code{teal_data} +} +\description{ +Module calls \code{\link[=teal_transform_module]{teal_transform_module()}} in sequence so that \verb{reactive teal_data} output +from one module is handed over to the following module's input. +} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index c7278af834..73fe9d90a5 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -21,7 +21,7 @@ module( datanames = "all", server_args = NULL, ui_args = NULL, - transformers = list() + transforms = list() ) modules(..., label = "root") @@ -72,7 +72,7 @@ There are 2 reserved values that have specific behaviors: \itemize{ \item The keyword \code{"all"} includes all datasets available in the data passed to the teal application. \item \code{NULL} hides the sidebar panel completely. -\item If \code{transformers} are specified, their \code{datanames} are automatically added to this \code{datanames} +\item If \code{transforms} are specified, their \code{datanames} are automatically added to this \code{datanames} argument. }} @@ -80,9 +80,10 @@ argument. \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} -\item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. -Each transform module UI will appear in the \code{teal}'s sidebar panel. -Transformers' \code{datanames} are added to the \code{datanames}. See \code{\link[=teal_transform_module]{teal_transform_module()}}.} +\item{transforms}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. +Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. +If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transforms} +will be added to the \code{datanames}.} \item{...}{\itemize{ \item For \code{modules()}: (\code{teal_module} or \code{teal_modules}) Objects to wrap into a tab. @@ -138,13 +139,13 @@ in a warning. Datasets with names starting with . are ignored globally unless ex in \code{datanames}. } -\section{\code{datanames} with \code{transformers}}{ -When transformers are specified, their \code{datanames} are added to the module’s \code{datanames}, which +\section{\code{datanames} with \code{transforms}}{ +When transforms are specified, their \code{datanames} are added to the module’s \code{datanames}, which changes the behavior as follows: \itemize{ -\item If \code{module(datanames)} is \code{NULL} and the \code{transformers} have defined \code{datanames}, the sidebar -will appear showing the \code{transformers}' datasets, instead of being hidden. -\item If \code{module(datanames)} is set to specific values and any \code{transformer} has \code{datanames = "all"}, +\item If \code{module(datanames)} is \code{NULL} and the \code{transforms} have defined \code{datanames}, the sidebar +will appear showing the \code{transforms}' datasets, instead of being hidden. +\item If \code{module(datanames)} is set to specific values and any \code{transform} has \code{datanames = "all"}, the module may receive extra datasets that could be unnecessary } } diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index ad17ddb897..5a1b09b228 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -2,13 +2,13 @@ % Please edit documentation in R/teal_transform_module.R \name{teal_transform_module} \alias{teal_transform_module} -\title{Data module for \code{teal} transformers.} +\title{Data module for \code{teal} transforms.} \usage{ teal_transform_module( ui = NULL, server = function(id, data) data, label = "transform module", - datanames = character(0) + datanames = "all" ) } \arguments{ @@ -19,16 +19,14 @@ teal_transform_module( \code{shiny} module server function; that takes \code{id} and \code{data} argument, where the \code{id} is the module id and \code{data} is the reactive \code{teal_data} input. The server function must return reactive expression containing \code{teal_data} object. - -The server function definition should not use \code{eventReactive} as it may lead to -unexpected behavior. -See \code{vignettes("data-transform-as-shiny-module")} for more information.} +To simplify use \code{\link[=make_teal_transform_server]{make_teal_transform_server()}}.} \item{label}{(\code{character(1)}) Label of the module.} \item{datanames}{(\code{character}) -Names of the datasets that are relevant for this module to evaluate. If set to \code{character(0)} -then module would receive \code{\link[=modules]{modules()}} \code{datanames}.} +Names of the datasets that are relevant for the module. The +filter panel will only display filters for specified \code{datanames}. The keyword \code{"all"} will show +filters of all datasets. \code{datanames} will be automatically appended to the \code{\link[=modules]{modules()}} \code{datanames}.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} @@ -47,14 +45,14 @@ See vignette \code{vignette("data-transform-as-shiny-module", package = "teal")} \code{teal_transform_module}'s purpose is to modify any object created in \code{\link[teal.data:teal_data]{teal.data::teal_data}}. It means that an app-developer can use \code{teal_transform_module} to modify data but also outputted tables, listings and graphs. Some \code{\link{teal_modules}} enables app developer to inject custom shiny module to modify displayed output. -To handle these \code{decorators} inside of your module use \code{\link[=ui_teal_transform_module]{ui_teal_transform_module()}} and \link{srv_teal_transform_module}. +To handle these \code{decorators} inside of your module use \code{\link[=ui_transform_data]{ui_transform_data()}} and \link{srv_transform_data}. (todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by -ui/srv_teal_transform_module()... . Alternatively, decorators could be a \code{\link[=module]{module()}}'s argument) +ui/srv_transform_data()... . Alternatively, decorators could be a \code{\link[=module]{module()}}'s argument) } \section{\code{server} as a language}{ Server function in \code{teal_transform_module} must return \code{reactive} containing \link[teal.data:teal_data]{teal.data::teal_data} object. -Consider sinmple transformer which doesn't require any advanced reactivity, example \code{server} might have a +Consider sinmple transform which doesn't require any advanced reactivity, example \code{server} might have a following form: \if{html}{\out{
}}\preformatted{function(id, data) \{ @@ -73,12 +71,12 @@ following form: Above can be simplified to presented below, where \code{level} will be automatically substituted with respective input matched by its name. -\if{html}{\out{
}}\preformatted{make_teal_transform_module(expr = expression(x <- subset(x, col == level))) +\if{html}{\out{
}}\preformatted{make_transform_data(expr = expression(x <- subset(x, col == level))) }\if{html}{\out{
}} } \examples{ -my_transformers <- list( +my_transforms <- list( teal_transform_module( label = "Static transform for iris", datanames = "iris", From d2626b1f57d033412354718218bc76ed9b7ee60c Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:57:28 +0000 Subject: [PATCH 012/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-modules-output.Rmd | 2 +- vignettes/decorate-multiple-modules-outputs.Rmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index b5fa1e298b..0cff90ac89 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -241,4 +241,4 @@ if (interactive()) { - Error handling when a decorator throws an error. - Rewrite modules so that they always have a graph as `graph` (or `plot`, `g`, `p` etc.) and table as `table` (or `t` etc.) and listings as `listings` (or `l` etc.). This simplifies/standardizes a construction of - `teal_transform_module` which can be re-used in multiple modules. \ No newline at end of file + `teal_transform_module` which can be re-used in multiple modules. diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd index e78ac2771c..e53d7dfa1a 100644 --- a/vignettes/decorate-multiple-modules-outputs.Rmd +++ b/vignettes/decorate-multiple-modules-outputs.Rmd @@ -193,4 +193,4 @@ app <- init( if (interactive()) { shinyApp(app$ui, app$server) } -``` \ No newline at end of file +``` From 3cd47614fe48660c086d166b069f5b28236cdf3c Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:06:41 +0000 Subject: [PATCH 013/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_modules.Rd | 2 -- 1 file changed, 2 deletions(-) diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index 84ad1544e2..bfebe46704 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -99,8 +99,6 @@ will be added to the \code{datanames}.} \item{x}{(\code{teal_module} or \code{teal_modules}) Object to format/print.} -\item{indent}{(\code{integer(1)}) Indention level; each nested element is indented one level more.} - \item{is_last}{(\code{logical(1)}) Whether this is the last item in its parent's children list. Affects the tree branch character used (L- vs |-)} From a0977eb386f85922c048711e10ac01ed2e22f9de Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:17:53 +0100 Subject: [PATCH 014/165] Update R/teal_transform_module.R Co-authored-by: Konrad Pagacz Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/teal_transform_module.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 9c1b0f8504..4ce257a540 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -53,7 +53,7 @@ #' `shiny` module server function; that takes `id` and `data` argument, #' where the `id` is the module id and `data` is the reactive `teal_data` input. #' The server function must return reactive expression containing `teal_data` object. -#' To simplify use [make_teal_transform_server()]. +#' To simplify, use [make_teal_transform_server()]. #' @param datanames (`character`) #' Names of the datasets that are relevant for the module. The #' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show From 51e10cdc7fb98260bfc09c52715ee32f01864a57 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:18:14 +0100 Subject: [PATCH 015/165] Update R/teal_transform_module.R Co-authored-by: Konrad Pagacz Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/teal_transform_module.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 4ce257a540..88652557c7 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -43,7 +43,7 @@ #' ``` #' #' Above can be simplified to presented below, where `level` will be automatically substituted with -#' respective input matched by its name. +#' the respective input matched by its name. #' #' ``` #' make_transform_data(expr = expression(x <- subset(x, col == level))) From 9dfce2d367b39f5a6fe4c949deacb6f35605ae48 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:26:47 +0000 Subject: [PATCH 016/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_transform_module.Rd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 5a1b09b228..904fa2db65 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -19,7 +19,7 @@ teal_transform_module( \code{shiny} module server function; that takes \code{id} and \code{data} argument, where the \code{id} is the module id and \code{data} is the reactive \code{teal_data} input. The server function must return reactive expression containing \code{teal_data} object. -To simplify use \code{\link[=make_teal_transform_server]{make_teal_transform_server()}}.} +To simplify, use \code{\link[=make_teal_transform_server]{make_teal_transform_server()}}.} \item{label}{(\code{character(1)}) Label of the module.} @@ -69,7 +69,7 @@ following form: }\if{html}{\out{
}} Above can be simplified to presented below, where \code{level} will be automatically substituted with -respective input matched by its name. +the respective input matched by its name. \if{html}{\out{
}}\preformatted{make_transform_data(expr = expression(x <- subset(x, col == level))) }\if{html}{\out{
}} From e46964e5fb82ba7b6d0e1399922c1623fa3a18f4 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 13:05:43 +0100 Subject: [PATCH 017/165] redact decorate-modules-output vignette --- vignettes/decorate-modules-output.Rmd | 69 +++++++++++++++++++-------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 0cff90ac89..0ca2899a18 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -1,19 +1,40 @@ --- -title: "Decorate modules output" +title: "Customizing module output" author: "NEST CoreDev" output: rmarkdown::html_vignette: toc: true vignette: > - %\VignetteIndexEntry{Decorate modules output} + %\VignetteIndexEntry{Customizing modules output} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- +## Introduction -## Create a decoration object +`teal` is a powerful `shiny`-based framework with built-in modules for interactive data analysis. +This document outlines the customization options available for modifying the output of `teal` modules. +You will learn how to use `teal_transform_module` to modify and enhance the objects created by `teal` modules, +enabling you to tailor the outputs to your specific requirements without rewriting the original module code. -`teal_transform_module` without `ui`. Nothing will be displayed, only server code will be executed. +Adjusting input data or customizing module outputs in `teal` is accomplished using `transformers` created through +`teal_transform_module`. This document will focus on modifying outputs, and for clarity, we will refer to these +output-modifying objects as decoration objects or decorators. + +Throughout the examples, you will see references to a `data()` object. +This reactive object, built with `teal_data()`[https://insightsengineering.github.io/teal.data/latest-tag/articles/teal-data.html], +holds all the data used `within` the module as well as the generated output objects. +`teal_transform_module` operates on this object and can execute custom R code within the `data()` environment using the `within function`. +This approach allows you to pass code directly and evaluate it in the `data()` environment. + +## Decorators + +### Server + +The simplest way to create a decorator is to use `teal_transform_module` without specifying a `ui`. +This approach adds functionality solely to the server code of the `teal` app. +In the following example, we assume that the module contains an object (of class ggplot2) named `plot`. +We modify the title and x-axis label of plot: ```{r} library(teal) @@ -33,7 +54,9 @@ static_decorator <- teal_transform_module( ) ``` -Use `make_teal_transform_server(expr)` to create a transformer without repeating a boiler plate code (`function(id, data)`, `moduleServer`, `reactive`, `within(data, ...)`) +To simplify the repetitive elements of writing new decorators +(e.g., `function(id, data), moduleServer, reactive, within(data, ...)`), +you can use the `make_teal_transform_server(expr)` wrapper, which takes an `expression` as input: ```{r} static_decorator_lang <- teal_transform_module( @@ -47,7 +70,12 @@ static_decorator_lang <- teal_transform_module( ) ``` -Adding interactivity requires creating a proper shiny module. +### UI + +To create a decorator with user interactivity, you can incorporate a `shiny` UI module. +In the example below, the x-axis title is set dynamically via a `textInput`, allowing users to specify their preferred label. +Note how the input parameters are passed to the `within` function (e.g., `x_axis_title`): + ```{r} interactive_decorator <- teal_transform_module( ui = function(id) { @@ -60,8 +88,7 @@ interactive_decorator <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> within( - { + data() |> within({ plot <- plot + ggtitle("This is title") + xlab(x_axis_title) @@ -74,8 +101,8 @@ interactive_decorator <- teal_transform_module( ) ``` -There is `make_teal_transform_server(expr)` which simplifies a creation of `teal_transform_module`'s server. One can -simply specify `expression` within which object names can be replaced with respective input. +As in the earlier examples, `make_teal_transform_server(expr)` can simplify the creation of the server component. +This wrapper allows you to use `input` object names directly in the expression: ```{r} interactive_decorator_lang <- teal_transform_module( @@ -95,11 +122,12 @@ interactive_decorator_lang <- teal_transform_module( ) ``` +## Handling Various Object Names -Since `teal_transform_module` when created has constant object names it means that it is hard to robustly reuse -transformer in any `tm_` module. It is recomended for external parties to collect library of "decorators" as functions -to potentially adjust the content of the evaluated expression to `teal_modules`'s internals. See the following example -and focus on `output_name`. +`teal_transform_module` relies on the names of objects created within a module. +Writing a decorator that applies to any module can be challenging because different modules may use different object names. +It is recommended to create a library of decorator functions that can be adapted to the specific object names used in `teal` modules. +In the following example, focus on the `output_name` parameter to see how decorator can be applied to multiple modules: ```{r} gg_xlab_decorator <- function(output_name) { @@ -129,9 +157,8 @@ gg_xlab_decorator <- function(output_name) { } ``` - -Failures in decorators are handled by internal `teal` mechanism called `trigger_on_success`, which will never -break `data` object inside of the module. Instead decorator will be ignored and relevant error message will be shown. +Decorator failures are managed by an internal `teal` mechanism called `trigger_on_success`, which ensures that the `data` +object within the module remains intact. If a decorator fails, it will be ignored, and an appropriate error message will be displayed. ```{r} failing_decorator <- teal_transform_module( @@ -143,16 +170,16 @@ failing_decorator <- teal_transform_module( }, server = function(id, data) { moduleServer(id, function(input, output, session) { - reactive(stop("Hihi")) + reactive(stop("This is error")) }) } ) ``` -## Example module +## Example Module -To include decorators to the `teal_module` one needs to add an extra argument to the module-constructor function `tm_`. Decorators should be passed via `ui_args` and `server_args` to the module's `ui` and `server`, where they should be -consumed by `ui/srv_teal_transform_module`. +To include decorators in a `teal` module, pass them as arguments (`ui_args` and `server_args`) to the module’s `ui` and +`server` components, where they will be used by `ui/srv_teal_transform_module`: ```{r} From 8f03d2efebb861878875a9dfc76582e2bd452752 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:08:02 +0000 Subject: [PATCH 018/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-modules-output.Rmd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 0ca2899a18..787888d41a 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -88,7 +88,8 @@ interactive_decorator <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> within({ + data() |> within( + { plot <- plot + ggtitle("This is title") + xlab(x_axis_title) From c2e530d7dae4b2d07c76e72ab94b3ad40a49ebbf Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 13:14:51 +0100 Subject: [PATCH 019/165] small edits to the second vignette --- vignettes/decorate-modules-output.Rmd | 4 ++-- .../decorate-multiple-modules-outputs.Rmd | 24 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 0ca2899a18..6fc6d84dab 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -1,11 +1,11 @@ --- -title: "Customizing module output" +title: "Customizing Module Output" author: "NEST CoreDev" output: rmarkdown::html_vignette: toc: true vignette: > - %\VignetteIndexEntry{Customizing modules output} + %\VignetteIndexEntry{Customizing Module Output} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd index e53d7dfa1a..e85b8942b0 100644 --- a/vignettes/decorate-multiple-modules-outputs.Rmd +++ b/vignettes/decorate-multiple-modules-outputs.Rmd @@ -1,27 +1,33 @@ --- -title: "Decorate modules output" +title: "Customizing Multiple Modules Outputs" author: "NEST CoreDev" output: rmarkdown::html_vignette: toc: true vignette: > - %\VignetteIndexEntry{Decorate modules output} + %\VignetteIndexEntry{Customizing Multiple Modules Outputs} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- -# ui, server, expr +Below document contains example code presenting how you can change multiple objects inside one module. +This is an extension of `Customizing Module Output` vignette. ```{r} library(ggplot2) -tm_decorated_plot <- function(label = "module", - decorator = list(plot_1 = teal_transform_module(), plot_2 = teal_transform_module())) { +tm_decorated_plot <- function( + label = "module", + decorator = list( + plot_1 = teal_transform_module(), + plot_2 = teal_transform_module() + ) + ) { # decorator: teal_transform_module, language, function decorator_objs <- list( plot_1 = decorate_teal_data(x = decorator[["plot_1"]], output_name = "plot_1"), plot_2 = decorate_teal_data(x = decorator[["plot_2"]], output_name = "plot_2") ) - # ouputs teal_transform_module + # outputs teal_transform_module module( label = label, @@ -127,7 +133,6 @@ tm_decorated_plot <- function(label = "module", ``` -Interactive decorator ```{r} interactive_decorator_1 <- teal_transform_module( ui = function(id) { @@ -185,7 +190,10 @@ app <- init( tm_decorated_plot("identity"), tm_decorated_plot( "interactive", - decorator = list(plot_1 = interactive_decorator_1, plot_2 = interactive_decorator_2) + decorator = list( + plot_1 = interactive_decorator_1, + plot_2 = interactive_decorator_2 + ) ) ) ) From 02de5193af9a0ad48eab741d519cf0141ce062ed Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:23:34 +0000 Subject: [PATCH 020/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-multiple-modules-outputs.Rmd | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd index e85b8942b0..8d3e281583 100644 --- a/vignettes/decorate-multiple-modules-outputs.Rmd +++ b/vignettes/decorate-multiple-modules-outputs.Rmd @@ -18,10 +18,9 @@ library(ggplot2) tm_decorated_plot <- function( label = "module", decorator = list( - plot_1 = teal_transform_module(), + plot_1 = teal_transform_module(), plot_2 = teal_transform_module() - ) - ) { + )) { # decorator: teal_transform_module, language, function decorator_objs <- list( plot_1 = decorate_teal_data(x = decorator[["plot_1"]], output_name = "plot_1"), @@ -191,7 +190,7 @@ app <- init( tm_decorated_plot( "interactive", decorator = list( - plot_1 = interactive_decorator_1, + plot_1 = interactive_decorator_1, plot_2 = interactive_decorator_2 ) ) From b09ff428374a6b04cac5d619bf08331d89ff2cd2 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 14:12:01 +0100 Subject: [PATCH 021/165] add init part to examples of teal_transform_data so it's clear where to pass transformers inside modules --- R/teal_transform_module.R | 30 +++++++++++++++++++++++------- man/make_teal_transform_server.Rd | 10 +++++++++- man/teal_transform_module.Rd | 20 ++++++++++++++------ 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 88652557c7..206b2f9cbf 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -61,7 +61,7 @@ #' #' #' @examples -#' my_transforms <- list( +#' data_transformers <- list( #' teal_transform_module( #' label = "Static transform for iris", #' datanames = "iris", @@ -81,17 +81,17 @@ #' ui = function(id) { #' ns <- NS(id) #' tags$div( -#' numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) +#' numericInput(ns("n_cols"), "Show n columns", value = 5, min = 1, max = 5, step = 1) #' ) #' }, #' server = function(id, data) { #' moduleServer(id, function(input, output, session) { #' reactive({ #' within(data(), -#' { -#' iris <- head(iris, num_rows) -#' }, -#' num_rows = input$n_rows +#' { +#' iris <- iris[, 1:n_cols] +#' }, +#' n_cols = input$n_cols #' ) #' }) #' }) @@ -99,6 +99,14 @@ #' ) #' ) #' +#' app <- init( +#' data = teal_data(iris = iris), +#' modules = example_module(transforms = data_transformers) +#' ) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +#' #' @name teal_transform_module #' #' @export @@ -138,7 +146,7 @@ teal_transform_module <- function(ui = NULL, #' @return `function(id, data)` returning `shiny` module #' @examples #' -#' teal_transform_module( +#' trim_iris <- teal_transform_module( #' label = "Simplified interactive transform for iris", #' datanames = "iris", #' ui = function(id) { @@ -148,6 +156,14 @@ teal_transform_module <- function(ui = NULL, #' server = make_teal_transform_server(expression(iris <- head(iris, n_rows))) #' ) #' +#' app <- init( +#' data = teal_data(iris = iris), +#' modules = example_module(transforms = trim_iris) +#' ) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +#' #' @export make_teal_transform_server <- function(expr) { function(id, data) { diff --git a/man/make_teal_transform_server.Rd b/man/make_teal_transform_server.Rd index 10b5cf9643..b6bc2e9e79 100644 --- a/man/make_teal_transform_server.Rd +++ b/man/make_teal_transform_server.Rd @@ -24,7 +24,7 @@ with the value of the respective input (matched by the name) - for example in } \examples{ -teal_transform_module( +trim_iris <- teal_transform_module( label = "Simplified interactive transform for iris", datanames = "iris", ui = function(id) { @@ -34,4 +34,12 @@ teal_transform_module( server = make_teal_transform_server(expression(iris <- head(iris, n_rows))) ) +app <- init( + data = teal_data(iris = iris), + modules = example_module(transforms = trim_iris) +) +if (interactive()) { + shinyApp(app$ui, app$server) +} + } diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 904fa2db65..95223386e8 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -76,7 +76,7 @@ the respective input matched by its name. } \examples{ -my_transforms <- list( +data_transformers <- list( teal_transform_module( label = "Static transform for iris", datanames = "iris", @@ -96,17 +96,17 @@ my_transforms <- list( ui = function(id) { ns <- NS(id) tags$div( - numericInput(ns("n_rows"), "Subset n rows", value = 6, min = 1, max = 150, step = 1) + numericInput(ns("n_cols"), "Show n columns", value = 5, min = 1, max = 5, step = 1) ) }, server = function(id, data) { moduleServer(id, function(input, output, session) { reactive({ within(data(), - { - iris <- head(iris, num_rows) - }, - num_rows = input$n_rows + { + iris <- iris[, 1:n_cols] + }, + n_cols = input$n_cols ) }) }) @@ -114,4 +114,12 @@ my_transforms <- list( ) ) +app <- init( + data = teal_data(iris = iris), + modules = example_module(transforms = data_transformers) +) +if (interactive()) { + shinyApp(app$ui, app$server) +} + } From ac475e8cebd2a6e3509515f8d2e10ea6832c9794 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:14:14 +0000 Subject: [PATCH 022/165] [skip style] [skip vbump] Restyle files --- R/teal_transform_module.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 206b2f9cbf..e851bcc2e1 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -88,10 +88,10 @@ #' moduleServer(id, function(input, output, session) { #' reactive({ #' within(data(), -#' { -#' iris <- iris[, 1:n_cols] -#' }, -#' n_cols = input$n_cols +#' { +#' iris <- iris[, 1:n_cols] +#' }, +#' n_cols = input$n_cols #' ) #' }) #' }) From 106bf4cd12914075745089fffb879a78c8ada9f5 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 14:18:00 +0100 Subject: [PATCH 023/165] decorator -> decorators --- vignettes/decorate-modules-output.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index bd1244aa0a..93979b3189 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -250,12 +250,12 @@ app <- init( data = teal_data(iris = iris, mtcars = mtcars), modules = modules( tm_decorated_plot("identity"), - tm_decorated_plot("no-ui", decorator = static_decorator), - tm_decorated_plot("lang", decorator = static_decorator_lang), - tm_decorated_plot("interactive", decorator = interactive_decorator), - tm_decorated_plot("interactive-from lang", decorator = interactive_decorator_lang), - tm_decorated_plot("from-fun", decorator = gg_xlab_decorator("plot")), - tm_decorated_plot("failing", decorator = failing_decorator) + tm_decorated_plot("no-ui", decorators = static_decorator), + tm_decorated_plot("lang", decorators = static_decorator_lang), + tm_decorated_plot("interactive", decorators = interactive_decorator), + tm_decorated_plot("interactive-from lang", decorators = interactive_decorator_lang), + tm_decorated_plot("from-fun", decorators = gg_xlab_decorator("plot")), + tm_decorated_plot("failing", decorators = failing_decorator) ) ) From a1888c04428eb156cae0c9267722c68aee37f186 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:22:42 +0100 Subject: [PATCH 024/165] Update R/teal_transform_module.R Co-authored-by: Konrad Pagacz Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/teal_transform_module.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index e851bcc2e1..74507b6dd6 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -136,7 +136,7 @@ teal_transform_module <- function(ui = NULL, #' #' A factory function to simplify creation of a [`teal_transform_module`]'s server. Specified `expr` #' is wrapped in a shiny module function and output can be passed to the `server` argument in -#' [teal_transform_module()] call. Such server function can be linked with ui and values from the +#' [teal_transform_module()] call. Such a server function can be linked with ui and values from the #' inputs can be used in the expression. Object names specified in the expression will be substituted #' with the value of the respective input (matched by the name) - for example in #' `expression(graph <- graph + ggtitle(title))` object `title` will be replaced with the value of From 4cabc0c3417433ce2c419c03b1324c20f86ab742 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:31:50 +0000 Subject: [PATCH 025/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/make_teal_transform_server.Rd | 2 +- man/teal_transform_module.Rd | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/man/make_teal_transform_server.Rd b/man/make_teal_transform_server.Rd index b6bc2e9e79..85ea933886 100644 --- a/man/make_teal_transform_server.Rd +++ b/man/make_teal_transform_server.Rd @@ -16,7 +16,7 @@ An R call which will be evaluated within \code{\link[teal.data:teal_data]{teal.d \description{ A factory function to simplify creation of a \code{\link{teal_transform_module}}'s server. Specified \code{expr} is wrapped in a shiny module function and output can be passed to the \code{server} argument in -\code{\link[=teal_transform_module]{teal_transform_module()}} call. Such server function can be linked with ui and values from the +\code{\link[=teal_transform_module]{teal_transform_module()}} call. Such a server function can be linked with ui and values from the inputs can be used in the expression. Object names specified in the expression will be substituted with the value of the respective input (matched by the name) - for example in \code{expression(graph <- graph + ggtitle(title))} object \code{title} will be replaced with the value of diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 95223386e8..c9da5a57c5 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -103,10 +103,10 @@ data_transformers <- list( moduleServer(id, function(input, output, session) { reactive({ within(data(), - { - iris <- iris[, 1:n_cols] - }, - n_cols = input$n_cols + { + iris <- iris[, 1:n_cols] + }, + n_cols = input$n_cols ) }) }) From e5e2e586c19fefef1cd7a04ce19502d0c126cb5f Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 14:38:25 +0100 Subject: [PATCH 026/165] teal.data::datanames to names --- vignettes/decorate-modules-output.Rmd | 2 +- vignettes/decorate-multiple-modules-outputs.Rmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 93979b3189..e77acfcfe3 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -201,7 +201,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators server = function(id, data, decorators) { moduleServer(id, function(input, output, session) { observeEvent(data(), { - updateSelectInput(inputId = "dataname", choices = teal.data::datanames(data())) + updateSelectInput(inputId = "dataname", choices = names(data())) }) observeEvent(input$dataname, { diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd index 8d3e281583..c619f5a458 100644 --- a/vignettes/decorate-multiple-modules-outputs.Rmd +++ b/vignettes/decorate-multiple-modules-outputs.Rmd @@ -52,7 +52,7 @@ tm_decorated_plot <- function( server = function(id, data, decorator_objs) { moduleServer(id, function(input, output, session) { observeEvent(data(), { - updateSelectInput(inputId = "dataname", choices = teal.data::datanames(data())) + updateSelectInput(inputId = "dataname", choices = names(data())) }) observeEvent(input$dataname, { From f33669d75efe0ffda3208ffbf7fbbdc6cdaafc88 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 14:43:11 +0100 Subject: [PATCH 027/165] woops --- vignettes/decorate-modules-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index e77acfcfe3..be4a00e883 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -156,7 +156,7 @@ gg_xlab_decorator <- function(output_name) { } ) } - ``` +``` Decorator failures are managed by an internal `teal` mechanism called `trigger_on_success`, which ensures that the `data` object within the module remains intact. If a decorator fails, it will be ignored, and an appropriate error message will be displayed. From 8182e6debaf93a2f95fbafc326940fe7d5ac2e9c Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 14:46:58 +0100 Subject: [PATCH 028/165] ugh --- vignettes/decorate-modules-output.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index be4a00e883..475212714c 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -130,7 +130,7 @@ Writing a decorator that applies to any module can be challenging because differ It is recommended to create a library of decorator functions that can be adapted to the specific object names used in `teal` modules. In the following example, focus on the `output_name` parameter to see how decorator can be applied to multiple modules: - ```{r} +```{r} gg_xlab_decorator <- function(output_name) { teal_transform_module( ui = function(id) { @@ -175,7 +175,7 @@ failing_decorator <- teal_transform_module( }) } ) - ``` +``` ## Example Module From 38335b7119d673c52ca0b6c13e2ca96d8e005d3c Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 15:15:11 +0100 Subject: [PATCH 029/165] bring back extract_transformers --- R/teal_data_module.R | 13 +++++++++++++ man/extract_transformers.Rd | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 man/extract_transformers.Rd diff --git a/R/teal_data_module.R b/R/teal_data_module.R index 64ed9d9cba..e4ce683548 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -81,3 +81,16 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { once = once ) } + +#' Extract all `transformers` from `modules`. +#' +#' @param modules `teal_modules` or `teal_module` +#' @return A list of `teal_transform_module` nested in the same way as input `modules`. +#' @keywords internal +extract_transformers <- function(modules) { + if (inherits(modules, "teal_module")) { + modules$transformers + } else if (inherits(modules, "teal_modules")) { + lapply(modules$children, extract_transformers) + } +} diff --git a/man/extract_transformers.Rd b/man/extract_transformers.Rd new file mode 100644 index 0000000000..9af99abe12 --- /dev/null +++ b/man/extract_transformers.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/teal_data_module.R +\name{extract_transformers} +\alias{extract_transformers} +\title{Extract all \code{transformers} from \code{modules}.} +\usage{ +extract_transformers(modules) +} +\arguments{ +\item{modules}{\code{teal_modules} or \code{teal_module}} +} +\value{ +A list of \code{teal_transform_module} nested in the same way as input \code{modules}. +} +\description{ +Extract all \code{transformers} from \code{modules}. +} +\keyword{internal} From cabf9f2f71c66effb4d47d9eb66a627c6577daf7 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 15:17:46 +0100 Subject: [PATCH 030/165] Revert "bring back extract_transformers" This reverts commit 38335b7119d673c52ca0b6c13e2ca96d8e005d3c. --- R/teal_data_module.R | 13 ------------- man/extract_transformers.Rd | 18 ------------------ 2 files changed, 31 deletions(-) delete mode 100644 man/extract_transformers.Rd diff --git a/R/teal_data_module.R b/R/teal_data_module.R index e4ce683548..64ed9d9cba 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -81,16 +81,3 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { once = once ) } - -#' Extract all `transformers` from `modules`. -#' -#' @param modules `teal_modules` or `teal_module` -#' @return A list of `teal_transform_module` nested in the same way as input `modules`. -#' @keywords internal -extract_transformers <- function(modules) { - if (inherits(modules, "teal_module")) { - modules$transformers - } else if (inherits(modules, "teal_modules")) { - lapply(modules$children, extract_transformers) - } -} diff --git a/man/extract_transformers.Rd b/man/extract_transformers.Rd deleted file mode 100644 index 9af99abe12..0000000000 --- a/man/extract_transformers.Rd +++ /dev/null @@ -1,18 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_data_module.R -\name{extract_transformers} -\alias{extract_transformers} -\title{Extract all \code{transformers} from \code{modules}.} -\usage{ -extract_transformers(modules) -} -\arguments{ -\item{modules}{\code{teal_modules} or \code{teal_module}} -} -\value{ -A list of \code{teal_transform_module} nested in the same way as input \code{modules}. -} -\description{ -Extract all \code{transformers} from \code{modules}. -} -\keyword{internal} From 350d9900892c88e7fe790390b52219c1f267f66f Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 15:24:42 +0100 Subject: [PATCH 031/165] unify naming convention transformers->transforms --- R/init.R | 2 +- R/modules.R | 24 ++++++------- R/teal_transform_module.R | 4 +-- man/teal_modules.Rd | 16 ++++----- man/teal_transform_module.Rd | 4 +-- tests/testthat/test-init.R | 8 ++--- tests/testthat/test-module_teal.R | 36 +++++++++++--------- tests/testthat/test-modules.R | 12 +++---- vignettes/data-transform-as-shiny-module.Rmd | 14 ++++---- vignettes/decorate-modules-output.Rmd | 2 +- 10 files changed, 62 insertions(+), 60 deletions(-) diff --git a/R/init.R b/R/init.R index a2c1fac786..a8fd46656b 100644 --- a/R/init.R +++ b/R/init.R @@ -212,7 +212,7 @@ init <- function(data, } is_modules_ok <- check_modules_datanames(modules, names(data)) - if (!isTRUE(is_modules_ok) && length(unlist(extract_transformers(modules))) == 0) { + if (!isTRUE(is_modules_ok) && length(unlist(extract_transforms(modules))) == 0) { warning(is_modules_ok, call. = FALSE) } diff --git a/R/modules.R b/R/modules.R index 181e47111e..9cfc2b1c78 100644 --- a/R/modules.R +++ b/R/modules.R @@ -329,14 +329,14 @@ modules <- function(..., label = "root") { #' @param is_root (`logical(1)`) Whether this is the root node of the tree. Only used in #' format.teal_modules(). Determines whether to show "TEAL ROOT" header #' @param what (`character`) Specifies which metadata to display. -#' Possible values: "datasets", "properties", "ui_args", "server_args", "transformers" +#' Possible values: "datasets", "properties", "ui_args", "server_args", "transforms" #' @examples #' mod <- module( #' label = "My Custom Module", #' server = function(id, data, ...) {}, #' ui = function(id, ...) {}, #' datanames = c("ADSL", "ADTTE"), -#' transformers = list(), +#' transforms = list(), #' ui_args = list(a = 1, b = "b"), #' server_args = list(x = 5, y = list(p = 1)) #' ) @@ -344,7 +344,7 @@ modules <- function(..., label = "root") { #' @export format.teal_module <- function( x, indent = 0, is_last = FALSE, parent_prefix = "", - what = c("datasets", "properties", "ui_args", "server_args", "transformers"), ...) { + what = c("datasets", "properties", "ui_args", "server_args", "transforms"), ...) { empty_text <- "" branch <- if (is_last) "L-" else "|-" current_prefix <- paste0(parent_prefix, branch, " ") @@ -381,8 +381,8 @@ format.teal_module <- function( bookmarkable <- isTRUE(attr(x, "teal_bookmarkable")) reportable <- "reporter" %in% names(formals(x$server)) - transformers <- if (length(x$transformers) > 0) { - paste(sapply(x$transformers, function(t) attr(t, "label")), collapse = ", ") + transforms <- if (length(x$transforms) > 0) { + paste(sapply(x$transforms, function(t) attr(t, "label")), collapse = ", ") } else { empty_text } @@ -417,10 +417,10 @@ format.teal_module <- function( content_prefix, "|- ", crayon::green("Server Arguments : "), server_args_formatted, "\n" ) } - if ("transformers" %in% what) { + if ("transforms" %in% what) { output <- paste0( output, - content_prefix, "L- ", crayon::magenta("Transformers : "), transformers, "\n" + content_prefix, "L- ", crayon::magenta("Transforms : "), transforms, "\n" ) } @@ -431,14 +431,14 @@ format.teal_module <- function( #' @examples #' custom_module <- function( #' label = "label", ui_args = NULL, server_args = NULL, -#' datanames = "all", transformers = list(), bk = FALSE) { +#' datanames = "all", transforms = list(), bk = FALSE) { #' ans <- module( #' label, #' server = function(id, data, ...) {}, #' ui = function(id, ...) { #' }, #' datanames = datanames, -#' transformers = transformers, +#' transforms = transforms, #' ui_args = ui_args, #' server_args = server_args #' ) @@ -475,7 +475,7 @@ format.teal_module <- function( #' cache = TRUE, #' debounce = 1000 #' ), -#' transformers = list(dummy_transformer), +#' transforms = list(dummy_transformer), #' bk = TRUE #' ), #' modules( @@ -493,7 +493,7 @@ format.teal_module <- function( #' render_type = "svg", #' cache_plots = TRUE #' ), -#' transformers = list(dummy_transformer, plot_transformer), +#' transforms = list(dummy_transformer, plot_transformer), #' bk = TRUE #' ), #' modules( @@ -525,7 +525,7 @@ format.teal_module <- function( #' ) #' #' cat(format(complete_modules)) -#' cat(format(complete_modules, what = c("ui_args", "server_args", "transformers"))) +#' cat(format(complete_modules, what = c("ui_args", "server_args", "transforms"))) #' @export format.teal_modules <- function(x, indent = 0, is_root = TRUE, is_last = FALSE, parent_prefix = "", ...) { if (is_root) { diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 74507b6dd6..9c177bdc14 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -61,7 +61,7 @@ #' #' #' @examples -#' data_transformers <- list( +#' data_transforms <- list( #' teal_transform_module( #' label = "Static transform for iris", #' datanames = "iris", @@ -101,7 +101,7 @@ #' #' app <- init( #' data = teal_data(iris = iris), -#' modules = example_module(transforms = data_transformers) +#' modules = example_module(transforms = data_transforms) #' ) #' if (interactive()) { #' shinyApp(app$ui, app$server) diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index bfebe46704..7f379472a2 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -31,7 +31,7 @@ modules(..., label = "root") indent = 0, is_last = FALSE, parent_prefix = "", - what = c("datasets", "properties", "ui_args", "server_args", "transformers"), + what = c("datasets", "properties", "ui_args", "server_args", "transforms"), ... ) @@ -106,7 +106,7 @@ Affects the tree branch character used (L- vs |-)} used to maintain the tree structure in nested levels} \item{what}{(\code{character}) Specifies which metadata to display. -Possible values: "datasets", "properties", "ui_args", "server_args", "transformers"} +Possible values: "datasets", "properties", "ui_args", "server_args", "transforms"} \item{is_root}{(\code{logical(1)}) Whether this is the root node of the tree. Only used in format.teal_modules(). Determines whether to show "TEAL ROOT" header} @@ -228,21 +228,21 @@ mod <- module( server = function(id, data, ...) {}, ui = function(id, ...) {}, datanames = c("ADSL", "ADTTE"), - transformers = list(), + transforms = list(), ui_args = list(a = 1, b = "b"), server_args = list(x = 5, y = list(p = 1)) ) cat(format(mod)) custom_module <- function( label = "label", ui_args = NULL, server_args = NULL, - datanames = "all", transformers = list(), bk = FALSE) { + datanames = "all", transforms = list(), bk = FALSE) { ans <- module( label, server = function(id, data, ...) {}, ui = function(id, ...) { }, datanames = datanames, - transformers = transformers, + transforms = transforms, ui_args = ui_args, server_args = server_args ) @@ -279,7 +279,7 @@ complete_modules <- modules( cache = TRUE, debounce = 1000 ), - transformers = list(dummy_transformer), + transforms = list(dummy_transformer), bk = TRUE ), modules( @@ -297,7 +297,7 @@ complete_modules <- modules( render_type = "svg", cache_plots = TRUE ), - transformers = list(dummy_transformer, plot_transformer), + transforms = list(dummy_transformer, plot_transformer), bk = TRUE ), modules( @@ -329,7 +329,7 @@ complete_modules <- modules( ) cat(format(complete_modules)) -cat(format(complete_modules, what = c("ui_args", "server_args", "transformers"))) +cat(format(complete_modules, what = c("ui_args", "server_args", "transforms"))) # change the module's datanames set_datanames(module(datanames = "all"), "a") diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index c9da5a57c5..4ff69fbc9f 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -76,7 +76,7 @@ the respective input matched by its name. } \examples{ -data_transformers <- list( +data_transforms <- list( teal_transform_module( label = "Static transform for iris", datanames = "iris", @@ -116,7 +116,7 @@ data_transformers <- list( app <- init( data = teal_data(iris = iris), - modules = example_module(transforms = data_transformers) + modules = example_module(transforms = data_transforms) ) if (interactive()) { shinyApp(app$ui, app$server) diff --git a/tests/testthat/test-init.R b/tests/testthat/test-init.R index 1ebca65fa2..861f40b744 100644 --- a/tests/testthat/test-init.R +++ b/tests/testthat/test-init.R @@ -57,7 +57,7 @@ testthat::test_that("init throws when an empty `data` is used", { }) testthat::test_that( - "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transformers", + "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transforms", { testthat::expect_warning( init( @@ -70,7 +70,7 @@ testthat::test_that( ) testthat::test_that( - "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transformers", + "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transforms", { testthat::expect_warning( init( @@ -83,7 +83,7 @@ testthat::test_that( ) testthat::test_that( - "init does not throw warning when datanames in modules incompatible w/ datanames in data and there are transformers", + "init does not throw warning when datanames in modules incompatible w/ datanames in data and there are transforms", { testthat::expect_no_warning( init( @@ -91,7 +91,7 @@ testthat::test_that( modules = list( example_module( datanames = "iris", - transformers = list( + transforms = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 2e91a13f57..3a1a143329 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -65,6 +65,8 @@ transform_list <<- list( ) ) +decorators <- + testthat::describe("srv_teal lockfile", { testthat::it(paste0( "creation process is invoked for teal.lockfile.mode = \"enabled\" ", @@ -738,7 +740,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( label = "Dummy", server = function(id, data) { @@ -775,7 +777,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( label = "Dummy", server = function(id, data) { @@ -834,7 +836,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( label = "Dummy", ui = function(id) div("(does nothing)"), @@ -873,7 +875,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( label = "Dummy", server = function(id, data) { @@ -1548,7 +1550,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transformers = transform_list[c("iris", "mtcars")] + transforms = transform_list[c("iris", "mtcars")] ) ) ), @@ -1577,7 +1579,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transformers = transform_list[c("iris", "mtcars")] + transforms = transform_list[c("iris", "mtcars")] ) ) ), @@ -1621,7 +1623,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transformers = transform_list[c("iris", "mtcars")] + transforms = transform_list[c("iris", "mtcars")] ) ) ), @@ -1671,7 +1673,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { label = "module_1", server = function(id, data) data, datanames = c("iris", "data_from_transform"), - transformers = list( + transforms = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -1708,7 +1710,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( module( server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( ui = function(id) textInput("a", "an input"), server = function(id, data) eventReactive(input$a, data()) @@ -1740,7 +1742,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( module( server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) "whatever" @@ -1773,7 +1775,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -1801,7 +1803,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -1829,7 +1831,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -1857,7 +1859,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transformers = list( + transforms = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -2077,7 +2079,7 @@ testthat::describe("srv_teal summary table", { module( "module_1", server = function(id, data) data, - transformers = teal_transform_module( + transforms = teal_transform_module( datanames = character(0), server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -2116,7 +2118,7 @@ testthat::describe("srv_teal summary table", { module( "module_1", server = function(id, data) data, - transformers = transform_list["iris"] + transforms = transform_list["iris"] ) ) ), @@ -2220,7 +2222,7 @@ testthat::describe("srv_teal summary table", { args = list( id = "test", data = data, - modules = modules(module("module_1", server = function(id, data) data, datanames = "all", transformers = list( + modules = modules(module("module_1", server = function(id, data) data, datanames = "all", transforms = list( teal_transform_module( server = function(id, data) { reactive({ diff --git a/tests/testthat/test-modules.R b/tests/testthat/test-modules.R index b1658af98c..605e0f1654 100644 --- a/tests/testthat/test-modules.R +++ b/tests/testthat/test-modules.R @@ -121,7 +121,7 @@ testthat::test_that("module() returns list of class 'teal_module' containing inp ui_args = NULL ) testthat::expect_s3_class(test_module, "teal_module") - testthat::expect_named(test_module, c("label", "server", "ui", "datanames", "server_args", "ui_args", "transformers")) + testthat::expect_named(test_module, c("label", "server", "ui", "datanames", "server_args", "ui_args", "transforms")) testthat::expect_identical(test_module$label, "aaa1") testthat::expect_identical(test_module$server, call_module_server_fun) testthat::expect_identical(test_module$ui, ui_fun1) @@ -509,12 +509,12 @@ testthat::test_that("format.teal_modules returns proper structure", { testthat::expect_equal( gsub("\033\\[[0-9;]*m", "", format(appended_mods)), - "TEAL ROOT\n |- a\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- Transformers : \n |- c\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- Transformers : \n L- c\n |- Datasets : all\n |- Properties:\n | |- Bookmarkable : FALSE\n | L- Reportable : FALSE\n |- UI Arguments : \n |- Server Arguments : \n L- Transformers : \n" # nolint: line_length + "TEAL ROOT\n |- a\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transforms : \n |- c\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transforms : \n L- c\n |- Datasets : all\n |- Properties:\n | |- Bookmarkable : FALSE\n | L- Reportable : FALSE\n |- UI Arguments : \n |- Server Arguments : \n L- transforms : \n" # nolint: line_length ) }) -testthat::test_that("module datanames is appended by its transformers datanames", { +testthat::test_that("module datanames is appended by its transforms datanames", { transformer_w_datanames <- teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -530,11 +530,11 @@ testthat::test_that("module datanames is appended by its transformers datanames" datanames = c("a", "b") ) - out <- module(datanames = "c", transformers = list(transformer_w_datanames)) + out <- module(datanames = "c", transforms = list(transformer_w_datanames)) testthat::expect_identical(out$datanames, c("c", "a", "b")) }) -testthat::test_that("module datanames stays 'all' regardless of transformers", { +testthat::test_that("module datanames stays 'all' regardless of transforms", { transformer_w_datanames <- teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -550,6 +550,6 @@ testthat::test_that("module datanames stays 'all' regardless of transformers", { datanames = c("a", "b") ) - out <- module(datanames = "all", transformers = list(transformer_w_datanames)) + out <- module(datanames = "all", transforms = list(transformer_w_datanames)) testthat::expect_identical(out$datanames, "all") }) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index be8b202019..2fc687601b 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -12,7 +12,7 @@ vignette: > ## Introduction -`teal` version `0.16` introduced new argument in `teal::module` called `transformers`. +`teal` version `0.16` introduced new argument in `teal::module` called `transforms`. This argument allows to pass a `list` of `teal_data_module` objects that are created using `teal_transform_module()` function. The main benefit of `teal_transform_module()` is the ability to transform data before passing it @@ -59,7 +59,7 @@ data <- within(teal_data(), { mtcars <- mtcars }) -my_transformers <- list( +my_transforms <- list( teal_transform_module( label = "Custom transform for iris", ui = function(id) { @@ -85,7 +85,7 @@ my_transformers <- list( app <- init( data = data, - modules = teal::example_module(transformers = my_transformers) + modules = teal::example_module(transformers = my_transforms) ) if (interactive()) { @@ -97,9 +97,9 @@ _Note_: It is recommended to return `reactive()` with `teal_data()` in `server` If you are planning on using `eventReactive()` in the server, the event should include `data()` _(example `eventReactive(list(input$a, data()), {...})`)_. More in [this discussion](https://github.com/insightsengineering/teal/issues/1303#issuecomment-2286239832). -### Multiple Transformers +### Multiple Transforms -Note that we can add multiple `teal` transformers by including `teal_transform_module` in a list. +Note that we can add multiple `teal` transforms by including `teal_transform_module` in a list. Let's add another transformation to the `mtcars` dataset that creates a column with `rownames` of `mtcars`. Note that this module does not have interactive UI elements. @@ -110,7 +110,7 @@ data <- within(teal_data(), { mtcars <- mtcars }) -my_transformers <- list( +my_transforms <- list( teal_transform_module( label = "Custom transform for iris", ui = function(id) { @@ -155,7 +155,7 @@ my_transformers <- list( app <- init( data = data, - modules = teal::example_module(transformers = my_transformers) + modules = teal::example_module(transformers = my_transforms) ) if (interactive()) { diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 475212714c..e88e8c17ee 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -17,7 +17,7 @@ This document outlines the customization options available for modifying the out You will learn how to use `teal_transform_module` to modify and enhance the objects created by `teal` modules, enabling you to tailor the outputs to your specific requirements without rewriting the original module code. -Adjusting input data or customizing module outputs in `teal` is accomplished using `transformers` created through +Adjusting input data or customizing module outputs in `teal` is accomplished using `transforms` created through `teal_transform_module`. This document will focus on modifying outputs, and for clarity, we will refer to these output-modifying objects as decoration objects or decorators. From c36ab6628e986637ff953912fc43ac42ca1a457e Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:27:06 +0000 Subject: [PATCH 032/165] [skip style] [skip vbump] Restyle files --- tests/testthat/test-module_teal.R | 103 +++++++++++++++--------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 3a1a143329..8a4f902c71 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -66,60 +66,59 @@ transform_list <<- list( ) decorators <- - -testthat::describe("srv_teal lockfile", { - testthat::it(paste0( - "creation process is invoked for teal.lockfile.mode = \"enabled\" ", - "and snapshot is copied to teal_app.lock and removed after session ended" - ), { - testthat::skip_if_not_installed("mirai") - testthat::skip_if_not_installed("renv") - withr::with_options( - list(teal.lockfile.mode = "enabled"), - { - renv_filename <- "teal_app.lock" - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris), - modules = modules(example_module()) - ), - expr = { - iter <- 1 - while (!file.exists(renv_filename) && iter <= 1000) { - Sys.sleep(0.5) - iter <- iter + 1 # max wait time is 500 seconds + testthat::describe("srv_teal lockfile", { + testthat::it(paste0( + "creation process is invoked for teal.lockfile.mode = \"enabled\" ", + "and snapshot is copied to teal_app.lock and removed after session ended" + ), { + testthat::skip_if_not_installed("mirai") + testthat::skip_if_not_installed("renv") + withr::with_options( + list(teal.lockfile.mode = "enabled"), + { + renv_filename <- "teal_app.lock" + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules(example_module()) + ), + expr = { + iter <- 1 + while (!file.exists(renv_filename) && iter <= 1000) { + Sys.sleep(0.5) + iter <- iter + 1 # max wait time is 500 seconds + } + testthat::expect_true(file.exists(renv_filename)) } - testthat::expect_true(file.exists(renv_filename)) - } - ) - testthat::expect_false(file.exists(renv_filename)) - } - ) - }) - testthat::it("creation process is not invoked for teal.lockfile.mode = \"disabled\"", { - testthat::skip_if_not_installed("mirai") - testthat::skip_if_not_installed("renv") - withr::with_options( - list(teal.lockfile.mode = "disabled"), - { - renv_filename <- "teal_app.lock" - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris), - modules = modules(example_module()) - ), - expr = { - testthat::expect_false(file.exists(renv_filename)) - } - ) - } - ) + ) + testthat::expect_false(file.exists(renv_filename)) + } + ) + }) + testthat::it("creation process is not invoked for teal.lockfile.mode = \"disabled\"", { + testthat::skip_if_not_installed("mirai") + testthat::skip_if_not_installed("renv") + withr::with_options( + list(teal.lockfile.mode = "disabled"), + { + renv_filename <- "teal_app.lock" + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules(example_module()) + ), + expr = { + testthat::expect_false(file.exists(renv_filename)) + } + ) + } + ) + }) }) -}) testthat::describe("srv_teal arguments", { testthat::it("accepts data to be teal_data", { From 6eccdb8f8110f35caf2738aabea8fdead05e8c3d Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 15:48:00 +0100 Subject: [PATCH 033/165] fix 3 tests --- tests/testthat/test-module_teal.R | 5 +++-- tests/testthat/test-modules.R | 2 +- tests/testthat/test-teal_data_module.R | 7 ------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 3a1a143329..c98567ffbe 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -565,7 +565,7 @@ testthat::describe("srv_teal teal_modules", { trimws( rvest::html_text2( rvest::read_html( - output[["teal_modules-module_1-validate_datanames-shiny_warnings-message"]]$html + output[["teal_modules-module_1-validate_datanames-message"]]$html ) ) ), @@ -642,12 +642,13 @@ testthat::describe("srv_teal teal_modules", { ) ), expr = { + browser() session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_equal( trimws( rvest::html_text2( rvest::read_html( - output[["validate-shiny_warnings-message"]]$html + output[["teal-datanames_warning-message"]]$html ) ) ), diff --git a/tests/testthat/test-modules.R b/tests/testthat/test-modules.R index 605e0f1654..91c95cc790 100644 --- a/tests/testthat/test-modules.R +++ b/tests/testthat/test-modules.R @@ -509,7 +509,7 @@ testthat::test_that("format.teal_modules returns proper structure", { testthat::expect_equal( gsub("\033\\[[0-9;]*m", "", format(appended_mods)), - "TEAL ROOT\n |- a\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transforms : \n |- c\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transforms : \n L- c\n |- Datasets : all\n |- Properties:\n | |- Bookmarkable : FALSE\n | L- Reportable : FALSE\n |- UI Arguments : \n |- Server Arguments : \n L- transforms : \n" # nolint: line_length + "TEAL ROOT\n |- a\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- Transforms : \n |- c\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- Transforms : \n L- c\n |- Datasets : all\n |- Properties:\n | |- Bookmarkable : FALSE\n | L- Reportable : FALSE\n |- UI Arguments : \n |- Server Arguments : \n L- Transforms : \n" # nolint: line_length ) }) diff --git a/tests/testthat/test-teal_data_module.R b/tests/testthat/test-teal_data_module.R index 0361ea4ea4..d9e4bccae8 100644 --- a/tests/testthat/test-teal_data_module.R +++ b/tests/testthat/test-teal_data_module.R @@ -23,10 +23,3 @@ testthat::test_that("teal_data_module throws when server has other formals than ) }) - -testthat::test_that("teal_transform_module doesn't accept datanames = 'all'", { - testthat::expect_error( - teal_transform_module(datanames = "all"), - "can't have datanames property equal to 'all'" - ) -}) From bf210b439a9c3c535e4eadcbfea052f73edd1586 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:50:32 +0000 Subject: [PATCH 034/165] [skip style] [skip vbump] Restyle files --- tests/testthat/test-teal_data_module.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-teal_data_module.R b/tests/testthat/test-teal_data_module.R index d9e4bccae8..d751cdc000 100644 --- a/tests/testthat/test-teal_data_module.R +++ b/tests/testthat/test-teal_data_module.R @@ -22,4 +22,3 @@ testthat::test_that("teal_data_module throws when server has other formals than ".*formal arguments.*" ) }) - From 1004af47f5f70d0b9fe1f5d8ce6b6cff60d6327b Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 11 Nov 2024 16:02:46 +0100 Subject: [PATCH 035/165] fix 2 more tests --- tests/testthat/test-module_teal.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 029a73c15c..ffb0f435cf 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -592,7 +592,7 @@ testthat::describe("srv_teal teal_modules", { trimws( rvest::html_text2( rvest::read_html( - output[["teal_modules-module_1-validate_datanames-shiny_warnings-message"]]$html + output[["teal_modules-module_1-validate_datanames-message"]]$html ) ) ), @@ -619,7 +619,7 @@ testthat::describe("srv_teal teal_modules", { trimws( rvest::html_text2( rvest::read_html( - output[["teal_modules-module_1-validate_datanames-shiny_warnings-message"]]$html + output[["teal_modules-module_1-validate_datanames-message"]]$html ) ) ), @@ -641,7 +641,6 @@ testthat::describe("srv_teal teal_modules", { ) ), expr = { - browser() session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_equal( trimws( From 5b7e9012a52b3bf136b93113e214485139b8177a Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 12 Nov 2024 10:41:05 +0100 Subject: [PATCH 036/165] change names of ui/srv_transform_data in vignette --- vignettes/decorate-modules-output.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index e88e8c17ee..7ba143c471 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -193,7 +193,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - ui_teal_transform_module(ns("decorate"), transforms = decorators), + ui_transform_data(ns("decorate"), transforms = decorators), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) @@ -223,7 +223,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators ) }) - q2 <- srv_teal_transform_module("decorate", data = q1, transforms = decorators) + q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) plot_r <- reactive({ req(q2()) From 9d82fea19335db6e477da6ef37b516b0f185b4ca Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 12 Nov 2024 13:13:10 +0100 Subject: [PATCH 037/165] fix vignette code --- vignettes/decorate-modules-output.Rmd | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 7ba143c471..b5706e2d58 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -200,13 +200,28 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators }, server = function(id, data, decorators) { moduleServer(id, function(input, output, session) { - observeEvent(data(), { - updateSelectInput(inputId = "dataname", choices = names(data())) + + datasetnames <- reactive({ + names(data()) + }) + + observeEvent(datasetnames(), { + updateSelectInput( + session = session, + inputId = "dataname", + choices = datasetnames(), + selected = datasetnames()[1] + ) + }, once = TRUE) + + datacolnames <- reactive({ + req(input$dataname) + colnames(data()[[input$dataname]]) }) observeEvent(input$dataname, { - updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) - updateSelectInput(inputId = "y", label = "select y", choices = colnames(data()[[input$dataname]])) + updateSelectInput(session = session, inputId = "x", choices = datacolnames(), selected = datacolnames()[1]) + updateSelectInput(session = session, inputId = "y", choices = datacolnames(), selected = datacolnames()[2]) }) q1 <- reactive({ @@ -221,7 +236,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators x = as.name(input$x), y = as.name(input$y) ) - }) + }) |> debounce(200) q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) @@ -232,7 +247,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators output$plot <- renderPlot(plot_r()) output$text <- renderText({ - teal.code::get_code(q2()) + teal.code::get_code(req(q2())) }) }) }, From 9d4eeacbff2e9a51f350c9bcb6d5f7b19c90e550 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:15:35 +0000 Subject: [PATCH 038/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-modules-output.Rmd | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index b5706e2d58..f0f564f0fe 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -200,19 +200,21 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators }, server = function(id, data, decorators) { moduleServer(id, function(input, output, session) { - datasetnames <- reactive({ names(data()) }) - observeEvent(datasetnames(), { - updateSelectInput( - session = session, - inputId = "dataname", - choices = datasetnames(), - selected = datasetnames()[1] - ) - }, once = TRUE) + observeEvent(datasetnames(), + { + updateSelectInput( + session = session, + inputId = "dataname", + choices = datasetnames(), + selected = datasetnames()[1] + ) + }, + once = TRUE + ) datacolnames <- reactive({ req(input$dataname) From e13221174409f113cce48c5fa4d22945a5f24797 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 12 Nov 2024 13:56:27 +0100 Subject: [PATCH 039/165] remove pipe operator from vignette --- vignettes/decorate-modules-output.Rmd | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index b5706e2d58..be104dab9c 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -224,19 +224,19 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators updateSelectInput(session = session, inputId = "y", choices = datacolnames(), selected = datacolnames()[2]) }) - q1 <- reactive({ - req(input$dataname, input$x, input$y) - data() |> - within( - { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }) |> debounce(200) + q1 <- debounce( + reactive({ + req(input$dataname, input$x, input$y) + within(data(), + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }), 200) q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) From c84cbb6d8a5e6abdd5064e12664530af8ee9f4dc Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:59:08 +0000 Subject: [PATCH 040/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-modules-output.Rmd | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index ccc2a35af2..fd35272298 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -229,16 +229,17 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators q1 <- debounce( reactive({ req(input$dataname, input$x, input$y) - within(data(), - { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }), 200) + within(data(), + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }), 200 + ) q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) From 64e7f5d13b6df4062a35e2ff3e30e39c4d02464a Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 12 Nov 2024 14:59:55 +0100 Subject: [PATCH 041/165] remove pipes, simplify example in the vignette, add skeleton tests --- tests/testthat/test-module_teal.R | 287 +++++++++++++++++++++++++- vignettes/decorate-modules-output.Rmd | 32 +-- 2 files changed, 293 insertions(+), 26 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index ffb0f435cf..56b7bf05c9 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1,5 +1,10 @@ # comment: srv_teal is exported so the tests here are extensive and cover srv_data as well. # testing of srv_data is not needed. + + +# utils ----------------------------------------------------------------------------------------------------------- + + module_summary_table <<- function(output, id) { testthat::skip_if_not_installed("rvest") table_id <- sprintf("teal_modules-%s-data_summary-table", id) @@ -19,6 +24,9 @@ is_slices_equivalent <<- function(x, y, with_attrs = TRUE) { identical(x_list, y_list) } + +# transforms ------------------------------------------------------------------------------------------------------ + transform_list <<- list( fail = teal_transform_module( ui = function(id) NULL, @@ -65,7 +73,177 @@ transform_list <<- list( ) ) -decorators <- + +# decorators ------------------------------------------------------------------------------------------------------ + + +gg_xlab_decorator <<- function(output_name) { + teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + output_name <- output_name + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title, + output_name = as.name(output_name) + ) + }) + }) + } + ) +} + +decorators <<- list( + static_decorator = teal_transform_module( + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), { + plot <- plot + + ggtitle("This is title") + + xlab("x axis") + }) + }) + }) + } + ), + static_decorator_lang = teal_transform_module( + server = make_teal_transform_server( + expression( + plot <- plot + + ggtitle("This is title") + + xlab("x axis title") + ) + ) + ), + interactive_decorator = teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + plot <- plot + + ggtitle("This is title") + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title + ) + }) + }) + } + ), + interactive_decorator_lang = teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = make_teal_transform_server( + expression( + plot <- plot + + ggtitle("This is title") + + xlab(x_axis_title) + ) + ) + ), + gg_xlab_decorator = gg_xlab_decorator("plot"), + failing_decorator = teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive(stop("This is error")) + }) + } + ) +) + +tm_decorated_plot <<- function(label = "module", transforms = list(), decorators = teal_transform_module()) { + module( + label = label, + ui = function(id, decorators) { + ns <- NS(id) + div( + selectInput(ns("dataname"), label = "select dataname", choices = NULL), + selectInput(ns("x"), label = "select x", choices = NULL), + selectInput(ns("y"), label = "select y", choices = NULL), + ui_transform_data(ns("decorate"), transforms = decorators), + plotOutput(ns("plot")), + verbatimTextOutput(ns("text")) + ) + }, + server = function(id, data, decorators) { + moduleServer(id, function(input, output, session) { + observeEvent(data(), { + updateSelectInput(inputId = "dataname", choices = names(data())) + }) + + observeEvent(input$dataname, { + req(input$dataname) + updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) + updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) + }) + + q1 <- debounce( + reactive({ + req(input$dataname, input$x, input$y) + within(data(), + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }), 200) + + q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) + + plot_r <- reactive({ + req(q2()) + q2()[["plot"]] + }) + + output$plot <- renderPlot(plot_r()) + output$text <- renderText({ + teal.code::get_code(req(q2())) + }) + }) + }, + ui_args = list(decorators = decorators), + server_args = list(decorators = decorators) + ) +} + + +# tests ----------------------------------------------------------------------------------------------------------- + + + testthat::describe("srv_teal lockfile", { testthat::it(paste0( "creation process is invoked for teal.lockfile.mode = \"enabled\" ", @@ -1875,6 +2053,111 @@ testthat::describe("srv_teal teal_module(s) transformer", { } ) }) + + testthat::it("shows the decorator ui when decorator has it", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris, mtcars = mtcars), + modules = modules(tm_decorated_plot("interactive", decorators = decorators[['interactive_decorator']])) + ), + expr = { + # TODO + } + ) + }) + + testthat::it("applies the decorator ui changes when module has a decorator with ui", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot( + "interactive_decorator_lang", + decorators = decorators[['interactive_decorator_lang']]) + ) + ), + expr = { + # TODO + } + ) + }) + + testthat::it("changes module output for a module with a static decorator", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris, mtcars = mtcars), + modules = modules(tm_decorated_plot("static_decorator", decorators = decorators[['static_decorator']])) + ), + expr = { + # TODO + } + ) + }) + + testthat::it("changes module output for a module with a static decorator that uses expression", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot( + "static_decorator_lang", + decorators = decorators[['static_decorator_lang']] + ) + ) + ), + expr = { + # TODO + } + ) + }) + + testthat::it("changes module output for a module with a decorator that is a function of object name", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot( + "gg_xlab_decorator", + decorators = decorators[['gg_xlab_decorator']] + ) + ) + ), + expr = { + # TODO + } + ) + }) + + testthat::it("shows failure message when module with decorator fails", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot( + "failing_decorator", + decorators = decorators[['failing_decorator']] + ) + ) + ), + expr = { + # TODO + } + ) + }) + + }) testthat::describe("srv_teal summary table", { @@ -2210,7 +2493,7 @@ testthat::describe("srv_teal summary table", { ) }) - testthat::test_that("summary table displays MAE dataset added in transforms", { + testthat::it("summary table displays MAE dataset added in transforms", { data <- within(teal.data::teal_data(), { iris <- iris mtcars <- mtcars diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index ccc2a35af2..4dce72b0ca 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -43,7 +43,7 @@ static_decorator <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> within({ + within(data(), { plot <- plot + ggtitle("This is title") + xlab("x axis") @@ -88,7 +88,7 @@ interactive_decorator <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> within( + within(data(), { plot <- plot + ggtitle("This is title") + @@ -143,7 +143,7 @@ gg_xlab_decorator <- function(output_name) { moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> within( + within(data(), { output_name <- output_name + xlab(x_axis_title) @@ -200,30 +200,14 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators }, server = function(id, data, decorators) { moduleServer(id, function(input, output, session) { - datasetnames <- reactive({ - names(data()) - }) - - observeEvent(datasetnames(), - { - updateSelectInput( - session = session, - inputId = "dataname", - choices = datasetnames(), - selected = datasetnames()[1] - ) - }, - once = TRUE - ) - - datacolnames <- reactive({ - req(input$dataname) - colnames(data()[[input$dataname]]) + observeEvent(data(), { + updateSelectInput(inputId = "dataname", choices = names(data())) }) observeEvent(input$dataname, { - updateSelectInput(session = session, inputId = "x", choices = datacolnames(), selected = datacolnames()[1]) - updateSelectInput(session = session, inputId = "y", choices = datacolnames(), selected = datacolnames()[2]) + req(input$dataname) + updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) + updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) q1 <- debounce( From 411741787f7f960875814c76d5827e02dfc0c85f Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:02:47 +0000 Subject: [PATCH 042/165] [skip style] [skip vbump] Restyle files --- tests/testthat/test-module_teal.R | 134 +++++++++++++------------- vignettes/decorate-modules-output.Rmd | 4 +- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 56b7bf05c9..b31e70059d 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -211,15 +211,16 @@ tm_decorated_plot <<- function(label = "module", transforms = list(), decorators reactive({ req(input$dataname, input$x, input$y) within(data(), - { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) ) - }), 200) + }), 200 + ) q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) @@ -244,59 +245,59 @@ tm_decorated_plot <<- function(label = "module", transforms = list(), decorators - testthat::describe("srv_teal lockfile", { - testthat::it(paste0( - "creation process is invoked for teal.lockfile.mode = \"enabled\" ", - "and snapshot is copied to teal_app.lock and removed after session ended" - ), { - testthat::skip_if_not_installed("mirai") - testthat::skip_if_not_installed("renv") - withr::with_options( - list(teal.lockfile.mode = "enabled"), - { - renv_filename <- "teal_app.lock" - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris), - modules = modules(example_module()) - ), - expr = { - iter <- 1 - while (!file.exists(renv_filename) && iter <= 1000) { - Sys.sleep(0.5) - iter <- iter + 1 # max wait time is 500 seconds - } - testthat::expect_true(file.exists(renv_filename)) - } - ) - testthat::expect_false(file.exists(renv_filename)) - } - ) - }) - testthat::it("creation process is not invoked for teal.lockfile.mode = \"disabled\"", { - testthat::skip_if_not_installed("mirai") - testthat::skip_if_not_installed("renv") - withr::with_options( - list(teal.lockfile.mode = "disabled"), - { - renv_filename <- "teal_app.lock" - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris), - modules = modules(example_module()) - ), - expr = { - testthat::expect_false(file.exists(renv_filename)) +testthat::describe("srv_teal lockfile", { + testthat::it(paste0( + "creation process is invoked for teal.lockfile.mode = \"enabled\" ", + "and snapshot is copied to teal_app.lock and removed after session ended" + ), { + testthat::skip_if_not_installed("mirai") + testthat::skip_if_not_installed("renv") + withr::with_options( + list(teal.lockfile.mode = "enabled"), + { + renv_filename <- "teal_app.lock" + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules(example_module()) + ), + expr = { + iter <- 1 + while (!file.exists(renv_filename) && iter <= 1000) { + Sys.sleep(0.5) + iter <- iter + 1 # max wait time is 500 seconds } - ) - } - ) - }) + testthat::expect_true(file.exists(renv_filename)) + } + ) + testthat::expect_false(file.exists(renv_filename)) + } + ) }) + testthat::it("creation process is not invoked for teal.lockfile.mode = \"disabled\"", { + testthat::skip_if_not_installed("mirai") + testthat::skip_if_not_installed("renv") + withr::with_options( + list(teal.lockfile.mode = "disabled"), + { + renv_filename <- "teal_app.lock" + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules(example_module()) + ), + expr = { + testthat::expect_false(file.exists(renv_filename)) + } + ) + } + ) + }) +}) testthat::describe("srv_teal arguments", { testthat::it("accepts data to be teal_data", { @@ -2060,7 +2061,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { args = list( id = "test", data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(tm_decorated_plot("interactive", decorators = decorators[['interactive_decorator']])) + modules = modules(tm_decorated_plot("interactive", decorators = decorators[["interactive_decorator"]])) ), expr = { # TODO @@ -2077,8 +2078,9 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( tm_decorated_plot( "interactive_decorator_lang", - decorators = decorators[['interactive_decorator_lang']]) + decorators = decorators[["interactive_decorator_lang"]] ) + ) ), expr = { # TODO @@ -2092,7 +2094,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { args = list( id = "test", data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(tm_decorated_plot("static_decorator", decorators = decorators[['static_decorator']])) + modules = modules(tm_decorated_plot("static_decorator", decorators = decorators[["static_decorator"]])) ), expr = { # TODO @@ -2109,7 +2111,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( tm_decorated_plot( "static_decorator_lang", - decorators = decorators[['static_decorator_lang']] + decorators = decorators[["static_decorator_lang"]] ) ) ), @@ -2128,7 +2130,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( tm_decorated_plot( "gg_xlab_decorator", - decorators = decorators[['gg_xlab_decorator']] + decorators = decorators[["gg_xlab_decorator"]] ) ) ), @@ -2147,7 +2149,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( tm_decorated_plot( "failing_decorator", - decorators = decorators[['failing_decorator']] + decorators = decorators[["failing_decorator"]] ) ) ), @@ -2156,8 +2158,6 @@ testthat::describe("srv_teal teal_module(s) transformer", { } ) }) - - }) testthat::describe("srv_teal summary table", { diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 4530625d40..94febe5c4f 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -88,7 +88,7 @@ interactive_decorator <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - within(data(), + within(data(), { plot <- plot + ggtitle("This is title") + @@ -143,7 +143,7 @@ gg_xlab_decorator <- function(output_name) { moduleServer(id, function(input, output, session) { reactive({ req(data()) - within(data(), + within(data(), { output_name <- output_name + xlab(x_axis_title) From f6a4513ce89b5004e9f5c665a3d24daa406d2405 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 13 Nov 2024 14:46:44 +0100 Subject: [PATCH 043/165] documentation enhancemenes --- R/teal_transform_module.R | 57 ++++++++++++++++++------------------ man/teal_transform_module.Rd | 57 ++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 56 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 9c177bdc14..c6df2f11fe 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -1,32 +1,34 @@ -#' Data module for `teal` transforms. +#' Data module for `teal` transformations and output customization #' #' @description #' `r lifecycle::badge("experimental")` #' -#' `teal_transform_module` creates a shiny-module to transform data in a `teal` application. +#' `teal_transform_module` provides a `shiny` module that enables data transformations within a `teal` application +#' and allows for customization of outputs generated by modules. #' -#' # Transforming `teal` module's input +#' # Transforming Module Inputs in `teal` #' -#' This transformation happens after the data has passed through the filtering activity in teal. The -#' transformed data is then sent to the server of the [teal_module()]. Process is handled by `teal` -#' internals. +#' Data transformations occur after data has been filtered in `teal`. +#' The transformed data is then passed to the `server` of [`teal_module()`] and managed by `teal`'s internal processes. +#' The primary advantage of `teal_transform_module` over custom modules is in its error handling, where all warnings and +#' errors are managed by `teal`, allowing developers to focus on transformation logic. #' -#' See vignette `vignette("data-transform-as-shiny-module", package = "teal")` for more details. +#' For more details, see the vignette: `vignette("data-transform-as-shiny-module", package = "teal")`. #' -#' # Decorating `teal` module's output +#' # Customizing Module Outputs #' -#' `teal_transform_module`'s purpose is to modify any object created in [`teal.data::teal_data`]. It means that an -#' app-developer can use `teal_transform_module` to modify data but also outputted tables, listings and graphs. -#' Some [`teal_modules`] enables app developer to inject custom shiny module to modify displayed output. -#' To handle these `decorators` inside of your module use [ui_transform_data()] and [srv_transform_data]. -#' (todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by -#' ui/srv_transform_data()... . Alternatively, decorators could be a [module()]'s argument) +#' `teal_transform_module` also allows developers to modify any object created within [`teal.data::teal_data`]. +#' This means you can use it to customize not only datasets but also tables, listings, and graphs. +#' Some [`teal_modules`] permit developers to inject custom `shiny` modules to enhance displayed outputs. +#' To manage these `decorators` within your module, use [`ui_transform_data()`] and [`srv_transform_data()`]. +#' (For further guidance on managing decorators, refer to `ui_args` and `srv_args` in the vignette documentation.) +#' +#' See the vignette `vignette("decorate-modules-output", package = "teal")` for additional examples. #' #' # `server` as a language #' -#' Server function in `teal_transform_module` must return `reactive` containing [teal.data::teal_data] object. -#' Consider sinmple transform which doesn't require any advanced reactivity, example `server` might have a -#' following form: +#' The `server` function in `teal_transform_module` must return a reactive [`teal.data::teal_data`] object. +#' For simple transformations without complex reactivity, the `server` function might look like this:s #' #' ``` #' function(id, data) { @@ -42,22 +44,21 @@ #' } #' ``` #' -#' Above can be simplified to presented below, where `level` will be automatically substituted with -#' the respective input matched by its name. +#' The example above can be simplified using `make_teal_transform_server`, where `level` is automatically matched to the +#' corresponding `input` parameter: #' #' ``` -#' make_transform_data(expr = expression(x <- subset(x, col == level))) +#' make_teal_transform_server(expr = expression(x <- subset(x, col == level))) #' ``` #' @inheritParams teal_data_module -#' @param server (`function(id, data)` or `language`) -#' `shiny` module server function; that takes `id` and `data` argument, -#' where the `id` is the module id and `data` is the reactive `teal_data` input. -#' The server function must return reactive expression containing `teal_data` object. -#' To simplify, use [make_teal_transform_server()]. +#' @param server (`function(id, data)` or `expression`) +#' A `shiny` module server function that takes `id` and `data` as arguments, where `id` is the module id and `data` +#' is the reactive `teal_data` input. The `server` function must return a reactive expression containing a `teal_data` +#' object. For simplified syntax, use [`make_teal_transform_server()`]. #' @param datanames (`character`) -#' Names of the datasets that are relevant for the module. The -#' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show -#' filters of all datasets. `datanames` will be automatically appended to the [modules()] `datanames`. +#' Specifies the names of datasets relevant to the module. Only filters for the specified `datanames` will be displayed +#' in the filter panel. The keyword `"all"` can be used to display filters for all datasets. `datanames` are +#' automatically appended to the [`modules()`] `datanames`. #' #' #' @examples diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 4ff69fbc9f..f537170339 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/teal_transform_module.R \name{teal_transform_module} \alias{teal_transform_module} -\title{Data module for \code{teal} transforms.} +\title{Data module for \code{teal} transformations and output customization} \usage{ teal_transform_module( ui = NULL, @@ -15,45 +15,46 @@ teal_transform_module( \item{ui}{(\verb{function(id)}) \code{shiny} module UI function; must only take \code{id} argument} -\item{server}{(\verb{function(id, data)} or \code{language}) -\code{shiny} module server function; that takes \code{id} and \code{data} argument, -where the \code{id} is the module id and \code{data} is the reactive \code{teal_data} input. -The server function must return reactive expression containing \code{teal_data} object. -To simplify, use \code{\link[=make_teal_transform_server]{make_teal_transform_server()}}.} +\item{server}{(\verb{function(id, data)} or \code{expression}) +A \code{shiny} module server function that takes \code{id} and \code{data} as arguments, where \code{id} is the module id and \code{data} +is the reactive \code{teal_data} input. The \code{server} function must return a reactive expression containing a \code{teal_data} +object. For simplified syntax, use \code{\link[=make_teal_transform_server]{make_teal_transform_server()}}.} \item{label}{(\code{character(1)}) Label of the module.} \item{datanames}{(\code{character}) -Names of the datasets that are relevant for the module. The -filter panel will only display filters for specified \code{datanames}. The keyword \code{"all"} will show -filters of all datasets. \code{datanames} will be automatically appended to the \code{\link[=modules]{modules()}} \code{datanames}.} +Specifies the names of datasets relevant to the module. Only filters for the specified \code{datanames} will be displayed +in the filter panel. The keyword \code{"all"} can be used to display filters for all datasets. \code{datanames} are +automatically appended to the \code{\link[=modules]{modules()}} \code{datanames}.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} -\code{teal_transform_module} creates a shiny-module to transform data in a \code{teal} application. +\code{teal_transform_module} provides a \code{shiny} module that enables data transformations within a \code{teal} application +and allows for customization of outputs generated by modules. } -\section{Transforming \code{teal} module's input}{ -This transformation happens after the data has passed through the filtering activity in teal. The -transformed data is then sent to the server of the \code{\link[=teal_module]{teal_module()}}. Process is handled by \code{teal} -internals. +\section{Transforming Module Inputs in \code{teal}}{ +Data transformations occur after data has been filtered in \code{teal}. +The transformed data is then passed to the \code{server} of \code{\link[=teal_module]{teal_module()}} and managed by \code{teal}'s internal processes. +The primary advantage of \code{teal_transform_module} over custom modules is in its error handling, where all warnings and +errors are managed by \code{teal}, allowing developers to focus on transformation logic. -See vignette \code{vignette("data-transform-as-shiny-module", package = "teal")} for more details. +For more details, see the vignette: \code{vignette("data-transform-as-shiny-module", package = "teal")}. } -\section{Decorating \code{teal} module's output}{ -\code{teal_transform_module}'s purpose is to modify any object created in \code{\link[teal.data:teal_data]{teal.data::teal_data}}. It means that an -app-developer can use \code{teal_transform_module} to modify data but also outputted tables, listings and graphs. -Some \code{\link{teal_modules}} enables app developer to inject custom shiny module to modify displayed output. -To handle these \code{decorators} inside of your module use \code{\link[=ui_transform_data]{ui_transform_data()}} and \link{srv_transform_data}. -(todo: write more about how to handle decorators: they need to go through ui_args/srv_args and then be consumed by -ui/srv_transform_data()... . Alternatively, decorators could be a \code{\link[=module]{module()}}'s argument) +\section{Customizing Module Outputs}{ +\code{teal_transform_module} also allows developers to modify any object created within \code{\link[teal.data:teal_data]{teal.data::teal_data}}. +This means you can use it to customize not only datasets but also tables, listings, and graphs. +Some \code{\link{teal_modules}} permit developers to inject custom \code{shiny} modules to enhance displayed outputs. +To manage these \code{decorators} within your module, use \code{\link[=ui_transform_data]{ui_transform_data()}} and \code{\link[=srv_transform_data]{srv_transform_data()}}. +(For further guidance on managing decorators, refer to \code{ui_args} and \code{srv_args} in the vignette documentation.) + +See the vignette \code{vignette("decorate-modules-output", package = "teal")} for additional examples. } \section{\code{server} as a language}{ -Server function in \code{teal_transform_module} must return \code{reactive} containing \link[teal.data:teal_data]{teal.data::teal_data} object. -Consider sinmple transform which doesn't require any advanced reactivity, example \code{server} might have a -following form: +The \code{server} function in \code{teal_transform_module} must return a reactive \code{\link[teal.data:teal_data]{teal.data::teal_data}} object. +For simple transformations without complex reactivity, the \code{server} function might look like this:s \if{html}{\out{
}}\preformatted{function(id, data) \{ moduleServer(id, function(input, output, session) \{ @@ -68,10 +69,10 @@ following form: \} }\if{html}{\out{
}} -Above can be simplified to presented below, where \code{level} will be automatically substituted with -the respective input matched by its name. +The example above can be simplified using \code{make_teal_transform_server}, where \code{level} is automatically matched to the +corresponding \code{input} parameter: -\if{html}{\out{
}}\preformatted{make_transform_data(expr = expression(x <- subset(x, col == level))) +\if{html}{\out{
}}\preformatted{make_teal_transform_server(expr = expression(x <- subset(x, col == level))) }\if{html}{\out{
}} } From 81e1277c889cf605da2fe63bd27a5b2df5d80679 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 13 Nov 2024 15:34:15 +0100 Subject: [PATCH 044/165] decorator example with DT table --- DESCRIPTION | 3 +- vignettes/decorate-modules-output.Rmd | 129 ++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 8 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3f376a8ceb..9a186470cc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -57,6 +57,7 @@ Imports: utils Suggests: bslib, + DT (>= 0.33), knitr (>= 1.42), mirai (>= 1.1.1), MultiAssayExperiment, @@ -82,7 +83,7 @@ Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, insightsengineering/teal.reporter, insightsengineering/teal.widgets, rstudio/bslib, yihui/knitr, bioc::MultiAssayExperiment, r-lib/R6, rstudio/rmarkdown, tidyverse/rvest, rstudio/shinytest2, - rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, + rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, rstudio/DT, yaml=vubiostat/r-yaml Config/Needs/website: insightsengineering/nesttemplate Encoding: UTF-8 diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 94febe5c4f..8fd42f71d9 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -177,7 +177,9 @@ failing_decorator <- teal_transform_module( ) ``` -## Example Module +## Decorating Plots + +### Example Module To include decorators in a `teal` module, pass them as arguments (`ui_args` and `server_args`) to the module’s `ui` and `server` components, where they will be used by `ui/srv_teal_transform_module`: @@ -244,7 +246,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators } ``` -## Example app +### Application ```{r} library(ggplot2) @@ -266,9 +268,122 @@ if (interactive()) { } ``` -## To improve +## Decorating Tables + +### Example Module -- Error handling when a decorator throws an error. -- Rewrite modules so that they always have a graph as `graph` (or `plot`, `g`, `p` etc.) and table as - `table` (or `t` etc.) and listings as `listings` (or `l` etc.). This simplifies/standardizes a construction of - `teal_transform_module` which can be re-used in multiple modules. +```{r, eval=requireNamespace("DT")} +library(DT) + +# Define a decorator module for customizing table appearance +custom_table_decorator <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + selectInput( + ns("style"), + "Table Style", + choices = c("Default", "Striped", "Hover"), + selected = "Default" + ) + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + if (style == "Striped") { + table <- + formatStyle( + table, + columns = attr(table$x, "colnames")[-1], + target = 'row', + backgroundColor = '#f9f9f9' + ) + } else if (style == "Hover") { + table <- + formatStyle( + table, + columns = attr(table$x, "colnames")[-1], + target = 'row', + backgroundColor = '#f0f0f0' + ) + } + }, + style = input$style + ) + }) + }) + } +) + +# Main module to display the table with the decorator applied +tm_custom_table <- function(label = "Customized Table Module", decorators = teal_transform_module()) { + module( + label = label, + ui = function(id, decorators) { + ns <- NS(id) + div( + selectInput(ns("dataset"), "Select Dataset", choices = NULL), + ui_transform_data(ns("decorate"), transforms = decorators), + DT::dataTableOutput(ns("table_output")), + verbatimTextOutput(ns("code_output")) + ) + }, + server = function(id, data, decorators) { + moduleServer(id, function(input, output, session) { + observeEvent(data(), { + updateSelectInput(inputId = "dataset", choices = names(data())) + }) + + base_table <- reactive({ + req(input$dataset) + within(data(), + { + table <- + DT::datatable( + dataset, + options = list( + dom = "t", + autoWidth = TRUE + ) + ) + }, + dataset = as.name(input$dataset) + ) + }) + + decorated_table <- + srv_transform_data("decorate", data = base_table, transforms = decorators) + + output$table_output <- DT::renderDT({ + req(decorated_table()) + decorated_table()[["table"]] + }) + + output$code_output <- renderText({ + teal.code::get_code(req(decorated_table())) + }) + }) + }, + ui_args = list(decorators = decorators), + server_args = list(decorators = decorators) + ) +} +``` + +### Application + +```{r, eval=requireNamespace("DT")} +app <- init( + data = teal_data(mtcars = mtcars, iris = iris), + modules = modules( + tm_custom_table("custom_table", decorators = custom_table_decorator) + ) +) + +if (interactive()) { + shinyApp(app$ui, app$server) +}``` From 6729e4d12631d14cc52dde1f67cb0aba4e9c6293 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 13 Nov 2024 16:54:39 +0100 Subject: [PATCH 045/165] update names in vignette --- vignettes/decorate-modules-output.Rmd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 8fd42f71d9..d22a89850b 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -212,7 +212,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) - q1 <- debounce( + plot_data <- debounce( reactive({ req(input$dataname, input$x, input$y) within(data(), @@ -227,16 +227,16 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators }), 200 ) - q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) + plot_data_decorated <- srv_transform_data("decorate", data = plot_data, transforms = decorators) plot_r <- reactive({ - req(q2()) - q2()[["plot"]] + req(plot_data_decorated()) + plot_data_decorated()[["plot"]] }) output$plot <- renderPlot(plot_r()) output$text <- renderText({ - teal.code::get_code(req(q2())) + teal.code::get_code(req(plot_data_decorated())) }) }) }, From ecbf96f7f7263d4557b8b730f75f0ccd38afa048 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 13 Nov 2024 16:54:57 +0100 Subject: [PATCH 046/165] extend example_module so it handles decorations --- R/dummy_functions.R | 41 +++++++++++++++++++++++++++------------ R/teal_transform_module.R | 10 +++++++++- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index a0e93e0af7..400a99ba1a 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -13,11 +13,12 @@ #' shinyApp(app$ui, app$server) #' } #' @export -example_module <- function(label = "example teal module", datanames = "all", transforms = list()) { +example_module <- function(label = "example teal module", datanames = "all", transforms = list(), + decorators = teal_transform_module()) { checkmate::assert_string(label) ans <- module( label, - server = function(id, data) { + server = function(id, data, decorators) { checkmate::assert_class(isolate(data()), "teal_data") moduleServer(id, function(input, output, session) { datanames_rv <- reactive(names(req(data()))) @@ -36,28 +37,44 @@ example_module <- function(label = "example teal module", datanames = "all", tra ) }) + table_data <- reactive({ + within(data(), + { + table <- dataname + }, + dataname = as.name(input$dataname) + ) + }) + + table_data_decorated <- srv_transform_data("decorate", data = table_data, transforms = decorators) + output$text <- renderPrint({ - req(input$dataname) - data()[[input$dataname]] + req(table_data_decorated) + table_data_decorated()[['table']] }) teal.widgets::verbatim_popup_srv( id = "rcode", - verbatim_content = reactive(teal.code::get_code(data())), + verbatim_content = reactive(teal.code::get_code(req(table_data_decorated()))), title = "Example Code" ) }) }, - ui = function(id) { + ui = function(id, decorators) { ns <- NS(id) - teal.widgets::standard_layout( - output = verbatimTextOutput(ns("text")), - encoding = tags$div( - selectInput(ns("dataname"), "Choose a dataset", choices = NULL), - teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") - ) + div( + teal.widgets::standard_layout( + output = verbatimTextOutput(ns("text")), + encoding = tags$div( + selectInput(ns("dataname"), "Choose a dataset", choices = NULL), + teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") + ) + ), + ui_transform_data(ns("decorate"), transforms = decorators) ) }, + ui_args = list(decorators = decorators), + server_args = list(decorators = decorators), datanames = datanames, transforms = transforms ) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index c6df2f11fe..8a2a1ccc53 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -100,9 +100,17 @@ #' ) #' ) #' +#' output_decorator <- teal_transform_module( +#' server = make_teal_transform_server( +#' expression( +#' table <- rev(table) +#' ) +#' ) +#' ) +#' #' app <- init( #' data = teal_data(iris = iris), -#' modules = example_module(transforms = data_transforms) +#' modules = example_module(transforms = data_transforms, decorators = output_decorator) #' ) #' if (interactive()) { #' shinyApp(app$ui, app$server) From 99c7a7c7124202cb03d6f6dfc33d13e974fd6145 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:57:25 +0000 Subject: [PATCH 047/165] [skip style] [skip vbump] Restyle files --- R/dummy_functions.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 400a99ba1a..2f686e9700 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -50,7 +50,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra output$text <- renderPrint({ req(table_data_decorated) - table_data_decorated()[['table']] + table_data_decorated()[["table"]] }) teal.widgets::verbatim_popup_srv( From e0bcb236bab20bf50afbd672774ea8a2247abae7 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 13 Nov 2024 16:58:11 +0100 Subject: [PATCH 048/165] update NEWS --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 1401db5eae..8b1d56d23b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,7 +3,7 @@ ### New features * Possible to call `ui_teal` and `srv_teal` directly in any application by delivering `data` argument as a `reactive` returning `teal_data` object. #669 -* Introduced `teal_transform_module` to provide a way to interactively modify data delivered to `teal_module`'s `server`. #1228 +* Introduced `teal_transform_module` to provide a way to interactively modify data delivered to `teal_module`'s `server` and to modify module outputs. #1228 #1384 * Introduced a new argument `once = FALSE` in `teal_data_module` to possibly reload data during a run time. * Possibility to download lockfile to restore app session for reproducibility. #479 * Introduced a function `set_datanames()` to change a `datanames` of the `teal_module`. From cc1f7bec13c0b80eb05f809cc914ad06297b82e0 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 08:33:41 +0100 Subject: [PATCH 049/165] remove indent from format.teal_module --- R/modules.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/modules.R b/R/modules.R index 9cfc2b1c78..5510086473 100644 --- a/R/modules.R +++ b/R/modules.R @@ -343,7 +343,7 @@ modules <- function(..., label = "root") { #' cat(format(mod)) #' @export format.teal_module <- function( - x, indent = 0, is_last = FALSE, parent_prefix = "", + x, is_last = FALSE, parent_prefix = "", what = c("datasets", "properties", "ui_args", "server_args", "transforms"), ...) { empty_text <- "" branch <- if (is_last) "L-" else "|-" From a1aaee2e1c725a8427fb8c2250569b41c42943fe Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 08:39:27 +0100 Subject: [PATCH 050/165] fix Spell Check --- vignettes/decorate-modules-output.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index d22a89850b..06f6de0a21 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -22,7 +22,7 @@ Adjusting input data or customizing module outputs in `teal` is accomplished usi output-modifying objects as decoration objects or decorators. Throughout the examples, you will see references to a `data()` object. -This reactive object, built with `teal_data()`[https://insightsengineering.github.io/teal.data/latest-tag/articles/teal-data.html], +This reactive object, built with [`teal_data()`](https://insightsengineering.github.io/teal.data/latest-tag/articles/teal-data.html), holds all the data used `within` the module as well as the generated output objects. `teal_transform_module` operates on this object and can execute custom R code within the `data()` environment using the `within function`. This approach allows you to pass code directly and evaluate it in the `data()` environment. @@ -33,7 +33,7 @@ This approach allows you to pass code directly and evaluate it in the `data()` e The simplest way to create a decorator is to use `teal_transform_module` without specifying a `ui`. This approach adds functionality solely to the server code of the `teal` app. -In the following example, we assume that the module contains an object (of class ggplot2) named `plot`. +In the following example, we assume that the module contains an object (of class `ggplot2`) named `plot`. We modify the title and x-axis label of plot: ```{r} @@ -195,7 +195,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - ui_transform_data(ns("decorate"), transforms = decorators), + ui_teal_transform_data(ns("decorate"), transforms = decorators), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) @@ -227,7 +227,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators }), 200 ) - plot_data_decorated <- srv_transform_data("decorate", data = plot_data, transforms = decorators) + plot_data_decorated <- srv_teal_transform_data("decorate", data = plot_data, transforms = decorators) plot_r <- reactive({ req(plot_data_decorated()) @@ -327,7 +327,7 @@ tm_custom_table <- function(label = "Customized Table Module", decorators = teal ns <- NS(id) div( selectInput(ns("dataset"), "Select Dataset", choices = NULL), - ui_transform_data(ns("decorate"), transforms = decorators), + ui_teal_transform_data(ns("decorate"), transforms = decorators), DT::dataTableOutput(ns("table_output")), verbatimTextOutput(ns("code_output")) ) @@ -356,7 +356,7 @@ tm_custom_table <- function(label = "Customized Table Module", decorators = teal }) decorated_table <- - srv_transform_data("decorate", data = base_table, transforms = decorators) + srv_teal_transform_data("decorate", data = base_table, transforms = decorators) output$table_output <- DT::renderDT({ req(decorated_table()) From fc596d4ef13f52bf173b79cbaa91596dadf24654 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 08:44:59 +0100 Subject: [PATCH 051/165] missing chunk end typo --- vignettes/decorate-modules-output.Rmd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 06f6de0a21..08e8fd7fc6 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -386,4 +386,6 @@ app <- init( if (interactive()) { shinyApp(app$ui, app$server) -}``` +} +``` + From 986a7c8d5879ac5b366e6f4eec8cb997e8c96d74 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 08:45:43 +0100 Subject: [PATCH 052/165] rename *_transofrm_data -> *_teal_transform_data --- NAMESPACE | 4 ++-- R/dummy_functions.R | 4 ++-- R/module_nested_tabs.R | 4 ++-- R/module_transform_data.R | 6 +++--- R/teal_transform_module.R | 2 +- man/example_module.Rd | 3 ++- man/module_transform_data.Rd | 8 ++++---- man/teal_modules.Rd | 1 - man/teal_transform_module.Rd | 12 ++++++++++-- 9 files changed, 26 insertions(+), 18 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index e2535df1aa..cd5a987d12 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -31,15 +31,15 @@ export(reporter_previewer_module) export(set_datanames) export(show_rcode_modal) export(srv_teal) +export(srv_teal_transform_data) export(srv_teal_with_splash) -export(srv_transform_data) export(tdata2env) export(teal_data_module) export(teal_slices) export(teal_transform_module) export(ui_teal) +export(ui_teal_transform_data) export(ui_teal_with_splash) -export(ui_transform_data) export(validate_has_data) export(validate_has_elements) export(validate_has_variable) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 2f686e9700..28a300574a 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -46,7 +46,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra ) }) - table_data_decorated <- srv_transform_data("decorate", data = table_data, transforms = decorators) + table_data_decorated <- srv_teal_transform_data("decorate", data = table_data, transforms = decorators) output$text <- renderPrint({ req(table_data_decorated) @@ -70,7 +70,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") ) ), - ui_transform_data(ns("decorate"), transforms = decorators) + ui_teal_transform_data(ns("decorate"), transforms = decorators) ) }, ui_args = list(decorators = decorators), diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index d010af5838..667898b421 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -125,7 +125,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { width = 3, ui_data_summary(ns("data_summary")), ui_filter_data(ns("filter_panel")), - ui_transform_data(ns("data_transform"), transforms = modules$transforms, class = "well"), + ui_teal_transform_data(ns("data_transform"), transforms = modules$transforms, class = "well"), class = "teal_secondary_col" ) ) @@ -261,7 +261,7 @@ srv_teal_module.teal_module <- function(id, is_active = is_active ) is_transform_failed <- reactiveValues() - transformed_teal_data <- srv_transform_data( + transformed_teal_data <- srv_teal_transform_data( "data_transform", data = filtered_teal_data, transforms = modules$transforms, diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 1fd7e52211..756c348a11 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -13,7 +13,7 @@ NULL #' @export #' @rdname module_transform_data -ui_transform_data <- function(id, transforms, class = "well") { +ui_teal_transform_data <- function(id, transforms, class = "well") { checkmate::assert_string(id) if (length(transforms) == 0L) { return(NULL) @@ -71,7 +71,7 @@ ui_transform_data <- function(id, transforms, class = "well") { #' @export #' @rdname module_transform_data -srv_transform_data <- function(id, data, transforms, modules = NULL, is_transform_failed = reactiveValues()) { +srv_teal_transform_data <- function(id, data, transforms, modules = NULL, is_transform_failed = reactiveValues()) { checkmate::assert_string(id) assert_reactive(data) checkmate::assert_class(modules, "teal_module", null.ok = TRUE) @@ -90,7 +90,7 @@ srv_transform_data <- function(id, data, transforms, modules = NULL, is_transfor Reduce( function(data_previous, name) { moduleServer(name, function(input, output, session) { - logger::log_debug("srv_transform_data initializing for { name }.") + logger::log_debug("srv_teal_transform_data initializing for { name }.") is_transform_failed[[name]] <- FALSE data_out <- transforms[[name]]$server(name, data = data_previous) data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 8a2a1ccc53..52e9343b04 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -20,7 +20,7 @@ #' `teal_transform_module` also allows developers to modify any object created within [`teal.data::teal_data`]. #' This means you can use it to customize not only datasets but also tables, listings, and graphs. #' Some [`teal_modules`] permit developers to inject custom `shiny` modules to enhance displayed outputs. -#' To manage these `decorators` within your module, use [`ui_transform_data()`] and [`srv_transform_data()`]. +#' To manage these `decorators` within your module, use [`ui_teal_transform_data()`] and [`srv_teal_transform_data()`]. #' (For further guidance on managing decorators, refer to `ui_args` and `srv_args` in the vignette documentation.) #' #' See the vignette `vignette("decorate-modules-output", package = "teal")` for additional examples. diff --git a/man/example_module.Rd b/man/example_module.Rd index 41adb1e4d0..c166348804 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -7,7 +7,8 @@ example_module( label = "example teal module", datanames = "all", - transforms = list() + transforms = list(), + decorators = teal_transform_module() ) } \arguments{ diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index 84f34cd5d8..9af4823a2b 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -2,13 +2,13 @@ % Please edit documentation in R/module_transform_data.R \name{module_transform_data} \alias{module_transform_data} -\alias{ui_transform_data} -\alias{srv_transform_data} +\alias{ui_teal_transform_data} +\alias{srv_teal_transform_data} \title{Module to transform \code{reactive} \code{teal_data}} \usage{ -ui_transform_data(id, transforms, class = "well") +ui_teal_transform_data(id, transforms, class = "well") -srv_transform_data( +srv_teal_transform_data( id, data, transforms, diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index 7f379472a2..ccefb2226c 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -28,7 +28,6 @@ modules(..., label = "root") \method{format}{teal_module}( x, - indent = 0, is_last = FALSE, parent_prefix = "", what = c("datasets", "properties", "ui_args", "server_args", "transforms"), diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index f537170339..e7395fa26d 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -46,7 +46,7 @@ For more details, see the vignette: \code{vignette("data-transform-as-shiny-modu \code{teal_transform_module} also allows developers to modify any object created within \code{\link[teal.data:teal_data]{teal.data::teal_data}}. This means you can use it to customize not only datasets but also tables, listings, and graphs. Some \code{\link{teal_modules}} permit developers to inject custom \code{shiny} modules to enhance displayed outputs. -To manage these \code{decorators} within your module, use \code{\link[=ui_transform_data]{ui_transform_data()}} and \code{\link[=srv_transform_data]{srv_transform_data()}}. +To manage these \code{decorators} within your module, use \code{\link[=ui_teal_transform_data]{ui_teal_transform_data()}} and \code{\link[=srv_teal_transform_data]{srv_teal_transform_data()}}. (For further guidance on managing decorators, refer to \code{ui_args} and \code{srv_args} in the vignette documentation.) See the vignette \code{vignette("decorate-modules-output", package = "teal")} for additional examples. @@ -115,9 +115,17 @@ data_transforms <- list( ) ) +output_decorator <- teal_transform_module( + server = make_teal_transform_server( + expression( + table <- rev(table) + ) + ) +) + app <- init( data = teal_data(iris = iris), - modules = example_module(transforms = data_transforms) + modules = example_module(transforms = data_transforms, decorators = output_decorator) ) if (interactive()) { shinyApp(app$ui, app$server) From efdf8d2746e7fe1a6112f872f39a6d478d1b3064 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 07:48:13 +0000 Subject: [PATCH 053/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-modules-output.Rmd | 46 +++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-modules-output.Rmd index 08e8fd7fc6..44656fab2b 100644 --- a/vignettes/decorate-modules-output.Rmd +++ b/vignettes/decorate-modules-output.Rmd @@ -281,9 +281,9 @@ custom_table_decorator <- teal_transform_module( ns <- NS(id) div( selectInput( - ns("style"), - "Table Style", - choices = c("Default", "Striped", "Hover"), + ns("style"), + "Table Style", + choices = c("Default", "Striped", "Hover"), selected = "Default" ) ) @@ -292,26 +292,26 @@ custom_table_decorator <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - within(data(), + within(data(), { if (style == "Striped") { - table <- + table <- formatStyle( - table, - columns = attr(table$x, "colnames")[-1], - target = 'row', - backgroundColor = '#f9f9f9' + table, + columns = attr(table$x, "colnames")[-1], + target = "row", + backgroundColor = "#f9f9f9" ) } else if (style == "Hover") { - table <- + table <- formatStyle( - table, - columns = attr(table$x, "colnames")[-1], - target = 'row', - backgroundColor = '#f0f0f0' + table, + columns = attr(table$x, "colnames")[-1], + target = "row", + backgroundColor = "#f0f0f0" ) } - }, + }, style = input$style ) }) @@ -337,12 +337,12 @@ tm_custom_table <- function(label = "Customized Table Module", decorators = teal observeEvent(data(), { updateSelectInput(inputId = "dataset", choices = names(data())) }) - + base_table <- reactive({ req(input$dataset) - within(data(), + within(data(), { - table <- + table <- DT::datatable( dataset, options = list( @@ -350,19 +350,19 @@ tm_custom_table <- function(label = "Customized Table Module", decorators = teal autoWidth = TRUE ) ) - }, + }, dataset = as.name(input$dataset) ) }) - - decorated_table <- + + decorated_table <- srv_teal_transform_data("decorate", data = base_table, transforms = decorators) - + output$table_output <- DT::renderDT({ req(decorated_table()) decorated_table()[["table"]] }) - + output$code_output <- renderText({ teal.code::get_code(req(decorated_table())) }) From e6c2ff078fb2463d98ca849d0d7cbca8dba711ac Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 08:51:40 +0100 Subject: [PATCH 054/165] fix pkgdown --- _pkgdown.yml | 3 +++ ...{decorate-modules-output.Rmd => decorate-module-output.Rmd} | 0 2 files changed, 3 insertions(+) rename vignettes/{decorate-modules-output.Rmd => decorate-module-output.Rmd} (100%) diff --git a/_pkgdown.yml b/_pkgdown.yml index 14c6b437fe..94e595d09e 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -73,6 +73,7 @@ articles: contents: - creating-custom-modules - adding-support-for-reporting + - decorate-module-output - title: 📃 Technical blueprint desc: > The purpose of the blueprint is to aid new developer’s comprehension of the @@ -100,6 +101,8 @@ reference: - init - teal_data_module - teal_transform_module + - module_transform_data + - make_teal_transform_server - module_teal_with_splash - module_teal - module diff --git a/vignettes/decorate-modules-output.Rmd b/vignettes/decorate-module-output.Rmd similarity index 100% rename from vignettes/decorate-modules-output.Rmd rename to vignettes/decorate-module-output.Rmd From 875ccdba2fd074d214ef0471ac080eb5f9f599e2 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 08:58:03 +0100 Subject: [PATCH 055/165] update names in tests --- tests/testthat/test-module_teal.R | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index b31e70059d..0be457ed75 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -190,7 +190,7 @@ tm_decorated_plot <<- function(label = "module", transforms = list(), decorators selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - ui_transform_data(ns("decorate"), transforms = decorators), + ui_teal_transform_data(ns("decorate"), transforms = decorators), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) @@ -222,7 +222,7 @@ tm_decorated_plot <<- function(label = "module", transforms = list(), decorators }), 200 ) - q2 <- srv_transform_data("decorate", data = q1, transforms = decorators) + q2 <- srv_teal_transform_data("decorate", data = q1, transforms = decorators) plot_r <- reactive({ req(q2()) @@ -2097,7 +2097,8 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules(tm_decorated_plot("static_decorator", decorators = decorators[["static_decorator"]])) ), expr = { - # TODO + # session$setInputs(`teal_modules-active_tab` = "static_decorator") + # testthat::expect_identical(modules_output$static_decorator()()[["plot"]], TODO) } ) }) From 56872ec449ee07da08f22194f83a03751fa1482e Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:08:07 +0000 Subject: [PATCH 056/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_data_module.Rd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 77a7805d4b..683c6d9ef9 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,7 +35,9 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character} or \code{language}) code to evaluate. If \code{character}, comments are retained.} +\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. +It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an +\code{expression} being a result of \code{parse(keep.source = TRUE)}.} \item{data}{(\code{teal_data_module}) object} From e86e19f5c3edf089c5c2c187f785f49d2f410415 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:42:43 +0100 Subject: [PATCH 057/165] Apply suggestions from code review Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-multiple-modules-outputs.Rmd | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd index c619f5a458..8cdf514fb6 100644 --- a/vignettes/decorate-multiple-modules-outputs.Rmd +++ b/vignettes/decorate-multiple-modules-outputs.Rmd @@ -56,6 +56,7 @@ tm_decorated_plot <- function( }) observeEvent(input$dataname, { + req(input$dataname) updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) updateSelectInput(inputId = "y", label = "select y", choices = colnames(data()[[input$dataname]])) }) @@ -72,8 +73,7 @@ tm_decorated_plot <- function( q1_1 <- reactive({ req(input$dataname, input$x, input$y) - data() |> - within( + within(data(), { plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() + @@ -87,8 +87,7 @@ tm_decorated_plot <- function( q2_1 <- reactive({ req(input$dataname, input$x, input$y) - data() |> - within( + within(data(), { plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() + @@ -100,8 +99,8 @@ tm_decorated_plot <- function( ) }) - q1_2 <- srv_teal_data("decorate_1", data = q1_1, data_module = decorator_objs[[1]], modules = module()) - q2_2 <- srv_teal_data("decorate_2", data = q2_1, data_module = decorator_objs[[2]], modules = module()) + q1_2 <- srv_transform_data("decorate_1", data = q1_1, data_module = decorator_objs[[1]], modules = module()) + q2_2 <- srv_transform_data("decorate_2", data = q2_1, data_module = decorator_objs[[2]], modules = module()) plot_r <- reactive({ @@ -144,7 +143,7 @@ interactive_decorator_1 <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> within( + within(data(), { plot_1 <- plot_1 + xlab(x_axis_title) @@ -167,7 +166,7 @@ interactive_decorator_2 <- teal_transform_module( moduleServer(id, function(input, output, session) { reactive({ req(data()) - data() |> within( + within(data(), { plot_2 <- plot_2 + xlab(x_axis_title) From c2bc50dfca0550c199dbf46c77d04f50f8049166 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:45:03 +0000 Subject: [PATCH 058/165] [skip style] [skip vbump] Restyle files --- .../decorate-multiple-modules-outputs.Rmd | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd index 8cdf514fb6..f13299fc0e 100644 --- a/vignettes/decorate-multiple-modules-outputs.Rmd +++ b/vignettes/decorate-multiple-modules-outputs.Rmd @@ -74,29 +74,29 @@ tm_decorated_plot <- function( q1_1 <- reactive({ req(input$dataname, input$x, input$y) within(data(), - { - plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggtitle("plot 1") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) + { + plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 1") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) }) q2_1 <- reactive({ req(input$dataname, input$x, input$y) within(data(), - { - plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggtitle("plot 2") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) + { + plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 2") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) }) q1_2 <- srv_transform_data("decorate_1", data = q1_1, data_module = decorator_objs[[1]], modules = module()) From e14f9b1c773dd578ddec2a8150695a53dcdd676d Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:06:34 +0100 Subject: [PATCH 059/165] Update NEWS.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dawid Kałędkowski Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 8b1d56d23b..137777c6b1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,7 +3,7 @@ ### New features * Possible to call `ui_teal` and `srv_teal` directly in any application by delivering `data` argument as a `reactive` returning `teal_data` object. #669 -* Introduced `teal_transform_module` to provide a way to interactively modify data delivered to `teal_module`'s `server` and to modify module outputs. #1228 #1384 +* Introduced `teal_transform_module` to provide a way to interactively modify data delivered to `teal_module`'s `server` and to decorate module outputs. #1228 #1384 * Introduced a new argument `once = FALSE` in `teal_data_module` to possibly reload data during a run time. * Possibility to download lockfile to restore app session for reproducibility. #479 * Introduced a function `set_datanames()` to change a `datanames` of the `teal_module`. From f9bdb60c692ffc465975f747232eee164b36a2d9 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 10:14:05 +0100 Subject: [PATCH 060/165] @gogonzo suggestions --- vignettes/decorate-module-output.Rmd | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 44656fab2b..06ba40fa73 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -17,15 +17,10 @@ This document outlines the customization options available for modifying the out You will learn how to use `teal_transform_module` to modify and enhance the objects created by `teal` modules, enabling you to tailor the outputs to your specific requirements without rewriting the original module code. -Adjusting input data or customizing module outputs in `teal` is accomplished using `transforms` created through +Adjusting input data or customizing module outputs in `teal` is accomplished using `transformators` created through `teal_transform_module`. This document will focus on modifying outputs, and for clarity, we will refer to these output-modifying objects as decoration objects or decorators. -Throughout the examples, you will see references to a `data()` object. -This reactive object, built with [`teal_data()`](https://insightsengineering.github.io/teal.data/latest-tag/articles/teal-data.html), -holds all the data used `within` the module as well as the generated output objects. -`teal_transform_module` operates on this object and can execute custom R code within the `data()` environment using the `within function`. -This approach allows you to pass code directly and evaluate it in the `data()` environment. ## Decorators @@ -158,7 +153,7 @@ gg_xlab_decorator <- function(output_name) { } ``` -Decorator failures are managed by an internal `teal` mechanism called `trigger_on_success`, which ensures that the `data` +Decorator failures are managed by an internal `teal` mechanism called **trigger on success**, which ensures that the `data` object within the module remains intact. If a decorator fails, it will be ignored, and an appropriate error message will be displayed. ```{r} @@ -186,7 +181,7 @@ To include decorators in a `teal` module, pass them as arguments (`ui_args` and ```{r} -tm_decorated_plot <- function(label = "module", transforms = list(), decorators = teal_transform_module()) { +tm_decorated_plot <- function(label = "module", transformators = list(), decorators = teal_transform_module()) { module( label = label, ui = function(id, decorators) { @@ -195,7 +190,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - ui_teal_transform_data(ns("decorate"), transforms = decorators), + ui_teal_transform_data(ns("decorate"), transformators = decorators), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) @@ -227,7 +222,7 @@ tm_decorated_plot <- function(label = "module", transforms = list(), decorators }), 200 ) - plot_data_decorated <- srv_teal_transform_data("decorate", data = plot_data, transforms = decorators) + plot_data_decorated <- srv_teal_transform_data("decorate", data = plot_data, transformators = decorators) plot_r <- reactive({ req(plot_data_decorated()) @@ -272,6 +267,9 @@ if (interactive()) { ### Example Module +The next example contains a decorator that formats the background style of `DT::datable`, depending on the selected `Table Style` (`style`) input. + + ```{r, eval=requireNamespace("DT")} library(DT) @@ -318,7 +316,11 @@ custom_table_decorator <- teal_transform_module( }) } ) +``` + + +```{r, eval=requireNamespace("DT")} # Main module to display the table with the decorator applied tm_custom_table <- function(label = "Customized Table Module", decorators = teal_transform_module()) { module( @@ -327,7 +329,7 @@ tm_custom_table <- function(label = "Customized Table Module", decorators = teal ns <- NS(id) div( selectInput(ns("dataset"), "Select Dataset", choices = NULL), - ui_teal_transform_data(ns("decorate"), transforms = decorators), + ui_teal_transform_data(ns("decorate"), transformators = decorators), DT::dataTableOutput(ns("table_output")), verbatimTextOutput(ns("code_output")) ) @@ -356,7 +358,7 @@ tm_custom_table <- function(label = "Customized Table Module", decorators = teal }) decorated_table <- - srv_teal_transform_data("decorate", data = base_table, transforms = decorators) + srv_teal_transform_data("decorate", data = base_table, transformators = decorators) output$table_output <- DT::renderDT({ req(decorated_table()) From f0b87f8fd9ffa01804fd416294889b984ee53d5b Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 10:14:24 +0100 Subject: [PATCH 061/165] change transform to transformator --- R/dummy_functions.R | 8 +-- R/init.R | 2 +- R/module_nested_tabs.R | 6 +- R/module_teal_data.R | 6 +- R/module_transform_data.R | 40 ++++++------ R/modules.R | 60 ++++++++--------- R/teal_transform_module.R | 20 +++--- man/example_module.Rd | 8 +-- ...ransforms.Rd => extract_transformators.Rd} | 10 +-- man/make_teal_transform_server.Rd | 2 +- man/module_teal_data.Rd | 2 +- man/module_transform_data.Rd | 10 +-- man/teal_data_module.Rd | 4 +- man/teal_modules.Rd | 36 +++++------ man/teal_transform_module.Rd | 4 +- tests/testthat/test-init.R | 8 +-- tests/testthat/test-module_teal.R | 64 +++++++++---------- tests/testthat/test-modules.R | 16 ++--- vignettes/data-transform-as-shiny-module.Rmd | 24 +++---- 19 files changed, 164 insertions(+), 166 deletions(-) rename man/{extract_transforms.Rd => extract_transformators.Rd} (59%) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 28a300574a..9588c50586 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -13,7 +13,7 @@ #' shinyApp(app$ui, app$server) #' } #' @export -example_module <- function(label = "example teal module", datanames = "all", transforms = list(), +example_module <- function(label = "example teal module", datanames = "all", transformators = list(), decorators = teal_transform_module()) { checkmate::assert_string(label) ans <- module( @@ -46,7 +46,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra ) }) - table_data_decorated <- srv_teal_transform_data("decorate", data = table_data, transforms = decorators) + table_data_decorated <- srv_teal_transform_data("decorate", data = table_data, transformators = decorators) output$text <- renderPrint({ req(table_data_decorated) @@ -70,13 +70,13 @@ example_module <- function(label = "example teal module", datanames = "all", tra teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") ) ), - ui_teal_transform_data(ns("decorate"), transforms = decorators) + ui_teal_transform_data(ns("decorate"), transformators = decorators) ) }, ui_args = list(decorators = decorators), server_args = list(decorators = decorators), datanames = datanames, - transforms = transforms + transformators = transformators ) attr(ans, "teal_bookmarkable") <- TRUE ans diff --git a/R/init.R b/R/init.R index a8fd46656b..83ad65fa55 100644 --- a/R/init.R +++ b/R/init.R @@ -212,7 +212,7 @@ init <- function(data, } is_modules_ok <- check_modules_datanames(modules, names(data)) - if (!isTRUE(is_modules_ok) && length(unlist(extract_transforms(modules))) == 0) { + if (!isTRUE(is_modules_ok) && length(unlist(extract_transformators(modules))) == 0) { warning(is_modules_ok, call. = FALSE) } diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 667898b421..62ab4e35f7 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -98,7 +98,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { class = "teal_validated", div( class = "teal-output-warning", - "One of transforms failed. Please fix and continue." + "One of transformators failed. Please fix and continue." ) ) ), @@ -125,7 +125,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { width = 3, ui_data_summary(ns("data_summary")), ui_filter_data(ns("filter_panel")), - ui_teal_transform_data(ns("data_transform"), transforms = modules$transforms, class = "well"), + ui_teal_transform_data(ns("data_transform"), transformators = modules$transformators, class = "well"), class = "teal_secondary_col" ) ) @@ -264,7 +264,7 @@ srv_teal_module.teal_module <- function(id, transformed_teal_data <- srv_teal_transform_data( "data_transform", data = filtered_teal_data, - transforms = modules$transforms, + transformators = modules$transformators, modules = modules, is_transform_failed = is_transform_failed ) diff --git a/R/module_teal_data.R b/R/module_teal_data.R index 5ae927eb32..75b23e783c 100644 --- a/R/module_teal_data.R +++ b/R/module_teal_data.R @@ -25,8 +25,8 @@ #' @param data_module (`teal_data_module`) #' @param modules (`teal_modules` or `teal_module`) For `datanames` validation purpose #' @param validate_shiny_silent_error (`logical`) If `TRUE`, then `shiny.silent.error` is validated and -#' @param is_transform_failed (`reactiveValues`) contains `logical` flags named after each transform. -#' Help to determine if any previous transform failed, so that following transforms can be disabled +#' @param is_transform_failed (`reactiveValues`) contains `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. #' #' @return `reactive` `teal_data` @@ -133,7 +133,7 @@ srv_validate_reactive_teal_data <- function(id, # nolint: object_length output$previous_failed <- renderUI({ if (hide_validation_error()) { shinyjs::hide("validate_messages") - tags$div("One of previous transforms failed. Please fix and continue.", class = "teal-output-warning") + tags$div("One of previous transformators failed. Please fix and continue.", class = "teal-output-warning") } else { shinyjs::show("validate_messages") NULL diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 756c348a11..61b06f6af6 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -13,26 +13,26 @@ NULL #' @export #' @rdname module_transform_data -ui_teal_transform_data <- function(id, transforms, class = "well") { +ui_teal_transform_data <- function(id, transformators, class = "well") { checkmate::assert_string(id) - if (length(transforms) == 0L) { + if (length(transformators) == 0L) { return(NULL) } - if (inherits(transforms, "teal_transform_module")) { - transforms <- list(transforms) + if (inherits(transformators, "teal_transform_module")) { + transformators <- list(transformators) } - checkmate::assert_list(transforms, "teal_transform_module") + checkmate::assert_list(transformators, "teal_transform_module") - labels <- lapply(transforms, function(x) attr(x, "label")) + labels <- lapply(transformators, function(x) attr(x, "label")) ids <- get_unique_labels(labels) - names(transforms) <- ids + names(transformators) <- ids lapply( - names(transforms), + names(transformators), function(name) { child_id <- NS(id)(name) ns <- NS(child_id) - data_mod <- transforms[[name]] + data_mod <- transformators[[name]] transform_wrapper_id <- ns(sprintf("wrapper_%s", name)) div( # todo: accordion? @@ -48,7 +48,7 @@ ui_teal_transform_data <- function(id, transforms, class = "well") { tags$i( class = "remove pull-right fa fa-angle-down", style = "cursor: pointer;", - title = "fold/expand transform panel", + title = "fold/expand transformator panel", onclick = sprintf("togglePanelItems(this, '%s', 'fa-angle-right', 'fa-angle-down');", transform_wrapper_id) ), tags$div( @@ -71,20 +71,20 @@ ui_teal_transform_data <- function(id, transforms, class = "well") { #' @export #' @rdname module_transform_data -srv_teal_transform_data <- function(id, data, transforms, modules = NULL, is_transform_failed = reactiveValues()) { +srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is_transform_failed = reactiveValues()) { checkmate::assert_string(id) assert_reactive(data) checkmate::assert_class(modules, "teal_module", null.ok = TRUE) - if (length(transforms) == 0L) { + if (length(transformators) == 0L) { return(data) } - if (inherits(transforms, "teal_transform_module")) { - transforms <- list(transforms) + if (inherits(transformators, "teal_transform_module")) { + transformators <- list(transformators) } - checkmate::assert_list(transforms, "teal_transform_module", null.ok = TRUE) - labels <- lapply(transforms, function(x) attr(x, "label")) + checkmate::assert_list(transformators, "teal_transform_module", null.ok = TRUE) + labels <- lapply(transformators, function(x) attr(x, "label")) ids <- get_unique_labels(labels) - names(transforms) <- ids + names(transformators) <- ids moduleServer(id, function(input, output, session) { Reduce( @@ -92,7 +92,7 @@ srv_teal_transform_data <- function(id, data, transforms, modules = NULL, is_tra moduleServer(name, function(input, output, session) { logger::log_debug("srv_teal_transform_data initializing for { name }.") is_transform_failed[[name]] <- FALSE - data_out <- transforms[[name]]$server(name, data = data_previous) + data_out <- transformators[[name]]$server(name, data = data_previous) data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) observeEvent(data_handled(), { if (inherits(data_handled(), "teal_data")) { @@ -119,7 +119,7 @@ srv_teal_transform_data <- function(id, data, transforms, modules = NULL, is_tra output$error_wrapper <- renderUI({ if (is_previous_failed()) { shinyjs::disable(transform_wrapper_id) - tags$div("One of previous transforms failed. Please fix and continue.", class = "teal-output-warning") + tags$div("One of previous transformators failed. Please fix and continue.", class = "teal-output-warning") } else { shinyjs::enable(transform_wrapper_id) shiny::tagList( @@ -133,7 +133,7 @@ srv_teal_transform_data <- function(id, data, transforms, modules = NULL, is_tra .trigger_on_success(data_handled) }) }, - x = names(transforms), + x = names(transformators), init = data ) }) diff --git a/R/modules.R b/R/modules.R index 5510086473..129180ad71 100644 --- a/R/modules.R +++ b/R/modules.R @@ -35,12 +35,12 @@ setOldClass("teal_modules") #' in a warning. Datasets with names starting with . are ignored globally unless explicitly listed #' in `datanames`. #' -#' # `datanames` with `transforms` -#' When transforms are specified, their `datanames` are added to the module’s `datanames`, which +#' # `datanames` with `transformators` +#' When transformators are specified, their `datanames` are added to the module’s `datanames`, which #' changes the behavior as follows: -#' - If `module(datanames)` is `NULL` and the `transforms` have defined `datanames`, the sidebar -#' will appear showing the `transforms`' datasets, instead of being hidden. -#' - If `module(datanames)` is set to specific values and any `transform` has `datanames = "all"`, +#' - If `module(datanames)` is `NULL` and the `transformators` have defined `datanames`, the sidebar +#' will appear showing the `transformators`' datasets, instead of being hidden. +#' - If `module(datanames)` is set to specific values and any `transformator` has `datanames = "all"`, #' the module may receive extra datasets that could be unnecessary #' #' @param label (`character(1)`) Label shown in the navigation item for the module or module group. @@ -69,14 +69,14 @@ setOldClass("teal_modules") #' There are 2 reserved values that have specific behaviors: #' - The keyword `"all"` includes all datasets available in the data passed to the teal application. #' - `NULL` hides the sidebar panel completely. -#' - If `transforms` are specified, their `datanames` are automatically added to this `datanames` +#' - If `transformators` are specified, their `datanames` are automatically added to this `datanames` #' argument. #' @param server_args (named `list`) with additional arguments passed on to the server function. #' @param ui_args (named `list`) with additional arguments passed on to the UI function. #' @param x (`teal_module` or `teal_modules`) Object to format/print. -#' @param transforms (`list` of `teal_transform_module`) that will be applied to transform module's data input. -#' Each transform module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the list. -#' If so, the module developer is responsible to display the UI in the module itself. `datanames` of the `transforms` +#' @param transformators (`list` of `teal_transform_module`) that will be applied to transformator module's data input. +#' Each transformator module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the list. +#' If so, the module developer is responsible to display the UI in the module itself. `datanames` of the `transformators` #' will be added to the `datanames`. #' #' @@ -159,7 +159,7 @@ module <- function(label = "module", datanames = "all", server_args = NULL, ui_args = NULL, - transforms = list()) { + transformators = list()) { # argument checking (independent) ## `label` checkmate::assert_string(label) @@ -266,12 +266,12 @@ module <- function(label = "module", ) } - ## `transforms` - if (inherits(transforms, "teal_transform_module")) { - transforms <- list(transforms) + ## `transformators` + if (inherits(transformators, "teal_transform_module")) { + transformators <- list(transformators) } - checkmate::assert_list(transforms, types = "teal_transform_module") - transform_datanames <- unlist(lapply(transforms, attr, "datanames")) + checkmate::assert_list(transformators, types = "teal_transform_module") + transform_datanames <- unlist(lapply(transformators, attr, "datanames")) combined_datanames <- if (identical(datanames, "all")) { "all" } else { @@ -286,7 +286,7 @@ module <- function(label = "module", datanames = combined_datanames, server_args = server_args, ui_args = ui_args, - transforms = transforms + transformators = transformators ), class = "teal_module" ) @@ -329,14 +329,14 @@ modules <- function(..., label = "root") { #' @param is_root (`logical(1)`) Whether this is the root node of the tree. Only used in #' format.teal_modules(). Determines whether to show "TEAL ROOT" header #' @param what (`character`) Specifies which metadata to display. -#' Possible values: "datasets", "properties", "ui_args", "server_args", "transforms" +#' Possible values: "datasets", "properties", "ui_args", "server_args", "transformators" #' @examples #' mod <- module( #' label = "My Custom Module", #' server = function(id, data, ...) {}, #' ui = function(id, ...) {}, #' datanames = c("ADSL", "ADTTE"), -#' transforms = list(), +#' transformators = list(), #' ui_args = list(a = 1, b = "b"), #' server_args = list(x = 5, y = list(p = 1)) #' ) @@ -344,7 +344,7 @@ modules <- function(..., label = "root") { #' @export format.teal_module <- function( x, is_last = FALSE, parent_prefix = "", - what = c("datasets", "properties", "ui_args", "server_args", "transforms"), ...) { + what = c("datasets", "properties", "ui_args", "server_args", "transformators"), ...) { empty_text <- "" branch <- if (is_last) "L-" else "|-" current_prefix <- paste0(parent_prefix, branch, " ") @@ -381,8 +381,8 @@ format.teal_module <- function( bookmarkable <- isTRUE(attr(x, "teal_bookmarkable")) reportable <- "reporter" %in% names(formals(x$server)) - transforms <- if (length(x$transforms) > 0) { - paste(sapply(x$transforms, function(t) attr(t, "label")), collapse = ", ") + transformators <- if (length(x$transformators) > 0) { + paste(sapply(x$transformators, function(t) attr(t, "label")), collapse = ", ") } else { empty_text } @@ -417,10 +417,10 @@ format.teal_module <- function( content_prefix, "|- ", crayon::green("Server Arguments : "), server_args_formatted, "\n" ) } - if ("transforms" %in% what) { + if ("transformators" %in% what) { output <- paste0( output, - content_prefix, "L- ", crayon::magenta("Transforms : "), transforms, "\n" + content_prefix, "L- ", crayon::magenta("transformators : "), transformators, "\n" ) } @@ -431,14 +431,14 @@ format.teal_module <- function( #' @examples #' custom_module <- function( #' label = "label", ui_args = NULL, server_args = NULL, -#' datanames = "all", transforms = list(), bk = FALSE) { +#' datanames = "all", transformators = list(), bk = FALSE) { #' ans <- module( #' label, #' server = function(id, data, ...) {}, #' ui = function(id, ...) { #' }, #' datanames = datanames, -#' transforms = transforms, +#' transformators = transformators, #' ui_args = ui_args, #' server_args = server_args #' ) @@ -446,7 +446,7 @@ format.teal_module <- function( #' ans #' } #' -#' dummy_transformer <- teal_transform_module( +#' dummy_transformator <- teal_transform_module( #' label = "Dummy Transform", #' ui = function(id) div("(does nothing)"), #' server = function(id, data) { @@ -454,7 +454,7 @@ format.teal_module <- function( #' } #' ) #' -#' plot_transformer <- teal_transform_module( +#' plot_transformator <- teal_transform_module( #' label = "Plot Settings", #' ui = function(id) div("(does nothing)"), #' server = function(id, data) { @@ -475,7 +475,7 @@ format.teal_module <- function( #' cache = TRUE, #' debounce = 1000 #' ), -#' transforms = list(dummy_transformer), +#' transformators = list(dummy_transformator), #' bk = TRUE #' ), #' modules( @@ -493,7 +493,7 @@ format.teal_module <- function( #' render_type = "svg", #' cache_plots = TRUE #' ), -#' transforms = list(dummy_transformer, plot_transformer), +#' transformators = list(dummy_transformator, plot_transformator), #' bk = TRUE #' ), #' modules( @@ -525,7 +525,7 @@ format.teal_module <- function( #' ) #' #' cat(format(complete_modules)) -#' cat(format(complete_modules, what = c("ui_args", "server_args", "transforms"))) +#' cat(format(complete_modules, what = c("ui_args", "server_args", "transformators"))) #' @export format.teal_modules <- function(x, indent = 0, is_root = TRUE, is_last = FALSE, parent_prefix = "", ...) { if (is_root) { diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 52e9343b04..dca138917f 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -62,9 +62,9 @@ #' #' #' @examples -#' data_transforms <- list( +#' data_transformators <- list( #' teal_transform_module( -#' label = "Static transform for iris", +#' label = "Static transformator for iris", #' datanames = "iris", #' server = function(id, data) { #' moduleServer(id, function(input, output, session) { @@ -77,7 +77,7 @@ #' } #' ), #' teal_transform_module( -#' label = "Interactive transform for iris", +#' label = "Interactive transformator for iris", #' datanames = "iris", #' ui = function(id) { #' ns <- NS(id) @@ -110,7 +110,7 @@ #' #' app <- init( #' data = teal_data(iris = iris), -#' modules = example_module(transforms = data_transforms, decorators = output_decorator) +#' modules = example_module(transformators = data_transformators, decorators = output_decorator) #' ) #' if (interactive()) { #' shinyApp(app$ui, app$server) @@ -156,7 +156,7 @@ teal_transform_module <- function(ui = NULL, #' @examples #' #' trim_iris <- teal_transform_module( -#' label = "Simplified interactive transform for iris", +#' label = "Simplified interactive transformator for iris", #' datanames = "iris", #' ui = function(id) { #' ns <- NS(id) @@ -167,7 +167,7 @@ teal_transform_module <- function(ui = NULL, #' #' app <- init( #' data = teal_data(iris = iris), -#' modules = example_module(transforms = trim_iris) +#' modules = example_module(transformators = trim_iris) #' ) #' if (interactive()) { #' shinyApp(app$ui, app$server) @@ -190,15 +190,15 @@ make_teal_transform_server <- function(expr) { } } -#' Extract all `transforms` from `modules`. +#' Extract all `transformators` from `modules`. #' #' @param modules `teal_modules` or `teal_module` #' @return A list of `teal_transform_module` nested in the same way as input `modules`. #' @keywords internal -extract_transforms <- function(modules) { +extract_transformators <- function(modules) { if (inherits(modules, "teal_module")) { - modules$transforms + modules$transformators } else if (inherits(modules, "teal_modules")) { - lapply(modules$children, extract_transforms) + lapply(modules$children, extract_transformators) } } diff --git a/man/example_module.Rd b/man/example_module.Rd index c166348804..73fe986c63 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -7,7 +7,7 @@ example_module( label = "example teal module", datanames = "all", - transforms = list(), + transformators = list(), decorators = teal_transform_module() ) } @@ -20,13 +20,13 @@ There are 2 reserved values that have specific behaviors: \itemize{ \item The keyword \code{"all"} includes all datasets available in the data passed to the teal application. \item \code{NULL} hides the sidebar panel completely. -\item If \code{transforms} are specified, their \code{datanames} are automatically added to this \code{datanames} +\item If \code{transformators} are specified, their \code{datanames} are automatically added to this \code{datanames} argument. }} -\item{transforms}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transforms} +If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} will be added to the \code{datanames}.} } \value{ diff --git a/man/extract_transforms.Rd b/man/extract_transformators.Rd similarity index 59% rename from man/extract_transforms.Rd rename to man/extract_transformators.Rd index 2d5d1f0a3d..ea076b1bf2 100644 --- a/man/extract_transforms.Rd +++ b/man/extract_transformators.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/teal_transform_module.R -\name{extract_transforms} -\alias{extract_transforms} -\title{Extract all \code{transforms} from \code{modules}.} +\name{extract_transformators} +\alias{extract_transformators} +\title{Extract all \code{transformators} from \code{modules}.} \usage{ -extract_transforms(modules) +extract_transformators(modules) } \arguments{ \item{modules}{\code{teal_modules} or \code{teal_module}} @@ -13,6 +13,6 @@ extract_transforms(modules) A list of \code{teal_transform_module} nested in the same way as input \code{modules}. } \description{ -Extract all \code{transforms} from \code{modules}. +Extract all \code{transformators} from \code{modules}. } \keyword{internal} diff --git a/man/make_teal_transform_server.Rd b/man/make_teal_transform_server.Rd index 85ea933886..f642f94954 100644 --- a/man/make_teal_transform_server.Rd +++ b/man/make_teal_transform_server.Rd @@ -36,7 +36,7 @@ trim_iris <- teal_transform_module( app <- init( data = teal_data(iris = iris), - modules = example_module(transforms = trim_iris) + modules = example_module(transformators = trim_iris) ) if (interactive()) { shinyApp(app$ui, app$server) diff --git a/man/module_teal_data.Rd b/man/module_teal_data.Rd index 90fc509ad5..ab4be37c20 100644 --- a/man/module_teal_data.Rd +++ b/man/module_teal_data.Rd @@ -38,7 +38,7 @@ srv_validate_reactive_teal_data( \item{validate_shiny_silent_error}{(\code{logical}) If \code{TRUE}, then \code{shiny.silent.error} is validated and} \item{is_transform_failed}{(\code{reactiveValues}) contains \code{logical} flags named after each transform. -Help to determine if any previous transform failed, so that following transforms can be disabled +Help to determine if any previous transform failed, so that following transformators can be disabled and display a generic failure message.} \item{data}{(\verb{reactive teal_data})} diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index 9af4823a2b..41bcdc7dc1 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -6,12 +6,12 @@ \alias{srv_teal_transform_data} \title{Module to transform \code{reactive} \code{teal_data}} \usage{ -ui_teal_transform_data(id, transforms, class = "well") +ui_teal_transform_data(id, transformators, class = "well") srv_teal_transform_data( id, data, - transforms, + transformators, modules = NULL, is_transform_failed = reactiveValues() ) @@ -19,9 +19,9 @@ srv_teal_transform_data( \arguments{ \item{id}{(\code{character(1)}) Module id} -\item{transforms}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transforms} +If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} will be added to the \code{datanames}.} \item{data}{(\verb{reactive teal_data})} @@ -29,7 +29,7 @@ will be added to the \code{datanames}.} \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 transform. -Help to determine if any previous transform failed, so that following transforms can be disabled +Help to determine if any previous transform failed, so that following transformators can be disabled and display a generic failure message.} } \value{ diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 683c6d9ef9..77a7805d4b 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,9 +35,7 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. -It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an -\code{expression} being a result of \code{parse(keep.source = TRUE)}.} +\item{code}{(\code{character} or \code{language}) code to evaluate. If \code{character}, comments are retained.} \item{data}{(\code{teal_data_module}) object} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index ccefb2226c..78a547b60e 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -21,7 +21,7 @@ module( datanames = "all", server_args = NULL, ui_args = NULL, - transforms = list() + transformators = list() ) modules(..., label = "root") @@ -30,7 +30,7 @@ modules(..., label = "root") x, is_last = FALSE, parent_prefix = "", - what = c("datasets", "properties", "ui_args", "server_args", "transforms"), + what = c("datasets", "properties", "ui_args", "server_args", "transformators"), ... ) @@ -78,7 +78,7 @@ There are 2 reserved values that have specific behaviors: \itemize{ \item The keyword \code{"all"} includes all datasets available in the data passed to the teal application. \item \code{NULL} hides the sidebar panel completely. -\item If \code{transforms} are specified, their \code{datanames} are automatically added to this \code{datanames} +\item If \code{transformators} are specified, their \code{datanames} are automatically added to this \code{datanames} argument. }} @@ -86,9 +86,9 @@ argument. \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} -\item{transforms}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transforms} +If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} will be added to the \code{datanames}.} \item{...}{\itemize{ @@ -105,7 +105,7 @@ Affects the tree branch character used (L- vs |-)} used to maintain the tree structure in nested levels} \item{what}{(\code{character}) Specifies which metadata to display. -Possible values: "datasets", "properties", "ui_args", "server_args", "transforms"} +Possible values: "datasets", "properties", "ui_args", "server_args", "transformators"} \item{is_root}{(\code{logical(1)}) Whether this is the root node of the tree. Only used in format.teal_modules(). Determines whether to show "TEAL ROOT" header} @@ -157,12 +157,12 @@ in a warning. Datasets with names starting with . are ignored globally unless ex in \code{datanames}. } -\section{\code{datanames} with \code{transforms}}{ -When transforms are specified, their \code{datanames} are added to the module’s \code{datanames}, which +\section{\code{datanames} with \code{transformators}}{ +When transformators are specified, their \code{datanames} are added to the module’s \code{datanames}, which changes the behavior as follows: \itemize{ -\item If \code{module(datanames)} is \code{NULL} and the \code{transforms} have defined \code{datanames}, the sidebar -will appear showing the \code{transforms}' datasets, instead of being hidden. +\item If \code{module(datanames)} is \code{NULL} and the \code{transformators} have defined \code{datanames}, the sidebar +will appear showing the \code{transformators}' datasets, instead of being hidden. \item If \code{module(datanames)} is set to specific values and any \code{transform} has \code{datanames = "all"}, the module may receive extra datasets that could be unnecessary } @@ -227,21 +227,21 @@ mod <- module( server = function(id, data, ...) {}, ui = function(id, ...) {}, datanames = c("ADSL", "ADTTE"), - transforms = list(), + transformators = list(), ui_args = list(a = 1, b = "b"), server_args = list(x = 5, y = list(p = 1)) ) cat(format(mod)) custom_module <- function( label = "label", ui_args = NULL, server_args = NULL, - datanames = "all", transforms = list(), bk = FALSE) { + datanames = "all", transformators = list(), bk = FALSE) { ans <- module( label, server = function(id, data, ...) {}, ui = function(id, ...) { }, datanames = datanames, - transforms = transforms, + transformators = transformators, ui_args = ui_args, server_args = server_args ) @@ -249,7 +249,7 @@ custom_module <- function( ans } -dummy_transformer <- teal_transform_module( +dummy_transformator <- teal_transform_module( label = "Dummy Transform", ui = function(id) div("(does nothing)"), server = function(id, data) { @@ -257,7 +257,7 @@ dummy_transformer <- teal_transform_module( } ) -plot_transformer <- teal_transform_module( +plot_transformator <- teal_transform_module( label = "Plot Settings", ui = function(id) div("(does nothing)"), server = function(id, data) { @@ -278,7 +278,7 @@ complete_modules <- modules( cache = TRUE, debounce = 1000 ), - transforms = list(dummy_transformer), + transformators = list(dummy_transformator), bk = TRUE ), modules( @@ -296,7 +296,7 @@ complete_modules <- modules( render_type = "svg", cache_plots = TRUE ), - transforms = list(dummy_transformer, plot_transformer), + transformators = list(dummy_transformator, plot_transformator), bk = TRUE ), modules( @@ -328,7 +328,7 @@ complete_modules <- modules( ) cat(format(complete_modules)) -cat(format(complete_modules, what = c("ui_args", "server_args", "transforms"))) +cat(format(complete_modules, what = c("ui_args", "server_args", "transformators"))) # change the module's datanames set_datanames(module(datanames = "all"), "a") diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index e7395fa26d..b4f68825e5 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -77,7 +77,7 @@ corresponding \code{input} parameter: } \examples{ -data_transforms <- list( +data_transformators <- list( teal_transform_module( label = "Static transform for iris", datanames = "iris", @@ -125,7 +125,7 @@ output_decorator <- teal_transform_module( app <- init( data = teal_data(iris = iris), - modules = example_module(transforms = data_transforms, decorators = output_decorator) + modules = example_module(transformators = data_transformators, decorators = output_decorator) ) if (interactive()) { shinyApp(app$ui, app$server) diff --git a/tests/testthat/test-init.R b/tests/testthat/test-init.R index 861f40b744..b288a629cc 100644 --- a/tests/testthat/test-init.R +++ b/tests/testthat/test-init.R @@ -57,7 +57,7 @@ testthat::test_that("init throws when an empty `data` is used", { }) testthat::test_that( - "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transforms", + "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transformators", { testthat::expect_warning( init( @@ -70,7 +70,7 @@ testthat::test_that( ) testthat::test_that( - "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transforms", + "init throws warning when datanames in modules incompatible w/ datanames in data and there is no transformators", { testthat::expect_warning( init( @@ -83,7 +83,7 @@ testthat::test_that( ) testthat::test_that( - "init does not throw warning when datanames in modules incompatible w/ datanames in data and there are transforms", + "init does not throw warning when datanames in modules incompatible w/ datanames in data and there are transformators", { testthat::expect_no_warning( init( @@ -91,7 +91,7 @@ testthat::test_that( modules = list( example_module( datanames = "iris", - transforms = list( + transformators = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 0be457ed75..25ac7d3e95 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -25,7 +25,7 @@ is_slices_equivalent <<- function(x, y, with_attrs = TRUE) { } -# transforms ------------------------------------------------------------------------------------------------------ +# transformators ------------------------------------------------------------------------------------------------------ transform_list <<- list( fail = teal_transform_module( @@ -181,7 +181,7 @@ decorators <<- list( ) ) -tm_decorated_plot <<- function(label = "module", transforms = list(), decorators = teal_transform_module()) { +tm_decorated_plot <<- function(label = "module", transformators = list(), decorators = teal_transform_module()) { module( label = label, ui = function(id, decorators) { @@ -190,7 +190,7 @@ tm_decorated_plot <<- function(label = "module", transforms = list(), decorators selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - ui_teal_transform_data(ns("decorate"), transforms = decorators), + ui_teal_transform_data(ns("decorate"), transformators = decorators), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) @@ -222,7 +222,7 @@ tm_decorated_plot <<- function(label = "module", transforms = list(), decorators }), 200 ) - q2 <- srv_teal_transform_data("decorate", data = q1, transforms = decorators) + q2 <- srv_teal_transform_data("decorate", data = q1, transformators = decorators) plot_r <- reactive({ req(q2()) @@ -902,7 +902,7 @@ testthat::describe("srv_teal teal_modules", { ) }) - testthat::it("receives all transform datasets if module$datanames == 'all'", { + testthat::it("receives all transformator datasets if module$datanames == 'all'", { shiny::testServer( app = srv_teal, args = list( @@ -918,7 +918,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( label = "Dummy", server = function(id, data) { @@ -955,7 +955,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( label = "Dummy", server = function(id, data) { @@ -1014,7 +1014,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( label = "Dummy", ui = function(id) div("(does nothing)"), @@ -1037,7 +1037,7 @@ testthat::describe("srv_teal teal_modules", { ) }) - testthat::it("does not receive transform datasets not specified in transform$datanames nor modue$datanames", { + testthat::it("does not receive transformator datasets not specified in transform$datanames nor modue$datanames", { shiny::testServer( app = srv_teal, args = list( @@ -1053,7 +1053,7 @@ testthat::describe("srv_teal teal_modules", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( label = "Dummy", server = function(id, data) { @@ -1717,7 +1717,7 @@ testthat::describe("srv_teal data reload", { }) }) -testthat::describe("srv_teal teal_module(s) transformer", { +testthat::describe("srv_teal teal_module(s) transformator", { testthat::it("evaluates custom qenv call and pass updated teal_data to the module", { shiny::testServer( app = srv_teal, @@ -1728,7 +1728,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transforms = transform_list[c("iris", "mtcars")] + transformators = transform_list[c("iris", "mtcars")] ) ) ), @@ -1757,7 +1757,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transforms = transform_list[c("iris", "mtcars")] + transformators = transform_list[c("iris", "mtcars")] ) ) ), @@ -1801,7 +1801,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transforms = transform_list[c("iris", "mtcars")] + transformators = transform_list[c("iris", "mtcars")] ) ) ), @@ -1851,7 +1851,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { label = "module_1", server = function(id, data) data, datanames = c("iris", "data_from_transform"), - transforms = list( + transformators = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -1878,7 +1878,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { ) }) - testthat::it("throws warning when transformer return reactive.event", { + testthat::it("throws warning when transformator return reactive.event", { testthat::expect_warning( testServer( app = srv_teal, @@ -1888,7 +1888,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( module( server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( ui = function(id) textInput("a", "an input"), server = function(id, data) eventReactive(input$a, data()) @@ -1906,7 +1906,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { ) }) - testthat::it("fails when transformer doesn't return reactive", { + testthat::it("fails when transformator doesn't return reactive", { testthat::expect_warning( # error decorator is mocked to avoid showing the trace error during the # test. @@ -1920,7 +1920,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { modules = modules( module( server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) "whatever" @@ -1943,7 +1943,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { ) }) - testthat::it("pauses when transformer throws validation error", { + testthat::it("pauses when transformator throws validation error", { shiny::testServer( app = srv_teal, args = list( @@ -1953,7 +1953,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -1971,7 +1971,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { ) }) - testthat::it("pauses when transformer throws validation error", { + testthat::it("pauses when transformator throws validation error", { shiny::testServer( app = srv_teal, args = list( @@ -1981,7 +1981,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -1999,7 +1999,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { ) }) - testthat::it("pauses when transformer throws qenv error", { + testthat::it("pauses when transformator throws qenv error", { shiny::testServer( app = srv_teal, args = list( @@ -2009,7 +2009,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -2037,7 +2037,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { module( label = "module_1", server = function(id, data) data, - transforms = list( + transformators = list( teal_transform_module( ui = function(id) NULL, server = function(id, data) { @@ -2352,7 +2352,7 @@ testthat::describe("srv_teal summary table", { ) }) - testthat::it("reflects transform adding new dataset if specified in module", { + testthat::it("reflects transformator adding new dataset if specified in module", { shiny::testServer( app = srv_teal, args = list( @@ -2362,7 +2362,7 @@ testthat::describe("srv_teal summary table", { module( "module_1", server = function(id, data) data, - transforms = teal_transform_module( + transformators = teal_transform_module( datanames = character(0), server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -2390,7 +2390,7 @@ testthat::describe("srv_teal summary table", { ) }) - testthat::it("reflects transform filtering", { + testthat::it("reflects transformator filtering", { testthat::it("displays parent's Subjects with count based on primary key", { shiny::testServer( app = srv_teal, @@ -2401,7 +2401,7 @@ testthat::describe("srv_teal summary table", { module( "module_1", server = function(id, data) data, - transforms = transform_list["iris"] + transformators = transform_list["iris"] ) ) ), @@ -2494,7 +2494,7 @@ testthat::describe("srv_teal summary table", { ) }) - testthat::it("summary table displays MAE dataset added in transforms", { + testthat::it("summary table displays MAE dataset added in transformators", { data <- within(teal.data::teal_data(), { iris <- iris mtcars <- mtcars @@ -2505,7 +2505,7 @@ testthat::describe("srv_teal summary table", { args = list( id = "test", data = data, - modules = modules(module("module_1", server = function(id, data) data, datanames = "all", transforms = list( + modules = modules(module("module_1", server = function(id, data) data, datanames = "all", transformators = list( teal_transform_module( server = function(id, data) { reactive({ diff --git a/tests/testthat/test-modules.R b/tests/testthat/test-modules.R index 91c95cc790..43627fbf5a 100644 --- a/tests/testthat/test-modules.R +++ b/tests/testthat/test-modules.R @@ -121,7 +121,7 @@ testthat::test_that("module() returns list of class 'teal_module' containing inp ui_args = NULL ) testthat::expect_s3_class(test_module, "teal_module") - testthat::expect_named(test_module, c("label", "server", "ui", "datanames", "server_args", "ui_args", "transforms")) + testthat::expect_named(test_module, c("label", "server", "ui", "datanames", "server_args", "ui_args", "transformators")) testthat::expect_identical(test_module$label, "aaa1") testthat::expect_identical(test_module$server, call_module_server_fun) testthat::expect_identical(test_module$ui, ui_fun1) @@ -509,13 +509,13 @@ testthat::test_that("format.teal_modules returns proper structure", { testthat::expect_equal( gsub("\033\\[[0-9;]*m", "", format(appended_mods)), - "TEAL ROOT\n |- a\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- Transforms : \n |- c\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- Transforms : \n L- c\n |- Datasets : all\n |- Properties:\n | |- Bookmarkable : FALSE\n | L- Reportable : FALSE\n |- UI Arguments : \n |- Server Arguments : \n L- Transforms : \n" # nolint: line_length + "TEAL ROOT\n |- a\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transformators : \n |- c\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transformators : \n L- c\n |- Datasets : all\n |- Properties:\n | |- Bookmarkable : FALSE\n | L- Reportable : FALSE\n |- UI Arguments : \n |- Server Arguments : \n L- transformators : \n" # nolint: line_length ) }) -testthat::test_that("module datanames is appended by its transforms datanames", { - transformer_w_datanames <- teal_transform_module( +testthat::test_that("module datanames is appended by its transformators datanames", { + transformator_w_datanames <- teal_transform_module( ui = function(id) NULL, server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -530,12 +530,12 @@ testthat::test_that("module datanames is appended by its transforms datanames", datanames = c("a", "b") ) - out <- module(datanames = "c", transforms = list(transformer_w_datanames)) + out <- module(datanames = "c", transformators = list(transformator_w_datanames)) testthat::expect_identical(out$datanames, c("c", "a", "b")) }) -testthat::test_that("module datanames stays 'all' regardless of transforms", { - transformer_w_datanames <- teal_transform_module( +testthat::test_that("module datanames stays 'all' regardless of transformators", { + transformator_w_datanames <- teal_transform_module( ui = function(id) NULL, server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -550,6 +550,6 @@ testthat::test_that("module datanames stays 'all' regardless of transforms", { datanames = c("a", "b") ) - out <- module(datanames = "all", transforms = list(transformer_w_datanames)) + out <- module(datanames = "all", transformators = list(transformator_w_datanames)) testthat::expect_identical(out$datanames, "all") }) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index 2fc687601b..f4b1f736fc 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -12,13 +12,13 @@ vignette: > ## Introduction -`teal` version `0.16` introduced new argument in `teal::module` called `transforms`. +`teal` version `0.16` introduced new argument in `teal::module` called `transformators`. This argument allows to pass a `list` of `teal_data_module` objects that are created using `teal_transform_module()` function. The main benefit of `teal_transform_module()` is the ability to transform data before passing it to the module. This feature allows to extend the regular behavior of existing modules by specifying custom data operations on data inside this module. -`teal_transform_module()` is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformer inputs. +`teal_transform_module()` is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformator inputs. This vignette presents the way on how to manage custom data transformations in `teal` apps. @@ -46,7 +46,7 @@ if (interactive()) { } ``` -### Single Transformer +### Single transformator Let's create a simple `teal_transform_module` that returns the first `n` number of rows of `iris` based on the user input. @@ -59,9 +59,9 @@ data <- within(teal_data(), { mtcars <- mtcars }) -my_transforms <- list( +my_transformators <- list( teal_transform_module( - label = "Custom transform for iris", + label = "Custom transformator for iris", ui = function(id) { ns <- NS(id) tags$div( @@ -85,7 +85,7 @@ my_transforms <- list( app <- init( data = data, - modules = teal::example_module(transformers = my_transforms) + modules = teal::example_module(transformators = my_transformators) ) if (interactive()) { @@ -97,9 +97,9 @@ _Note_: It is recommended to return `reactive()` with `teal_data()` in `server` If you are planning on using `eventReactive()` in the server, the event should include `data()` _(example `eventReactive(list(input$a, data()), {...})`)_. More in [this discussion](https://github.com/insightsengineering/teal/issues/1303#issuecomment-2286239832). -### Multiple Transforms +### Multiple transformators -Note that we can add multiple `teal` transforms by including `teal_transform_module` in a list. +Note that we can add multiple `teal` transformators by including `teal_transform_module` in a list. Let's add another transformation to the `mtcars` dataset that creates a column with `rownames` of `mtcars`. Note that this module does not have interactive UI elements. @@ -110,9 +110,9 @@ data <- within(teal_data(), { mtcars <- mtcars }) -my_transforms <- list( +my_transformators <- list( teal_transform_module( - label = "Custom transform for iris", + label = "Custom transformator for iris", ui = function(id) { ns <- NS(id) tags$div( @@ -133,7 +133,7 @@ my_transforms <- list( } ), teal_transform_module( - label = "Custom transform for mtcars", + label = "Custom transformator for mtcars", ui = function(id) { ns <- NS(id) tags$div( @@ -155,7 +155,7 @@ my_transforms <- list( app <- init( data = data, - modules = teal::example_module(transformers = my_transforms) + modules = teal::example_module(transformators = my_transformators) ) if (interactive()) { From a90544fd0bf4b47a6c2b637bf07ca56dae139bb2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:23:35 +0000 Subject: [PATCH 062/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/example_module.Rd | 4 ++-- man/make_teal_transform_server.Rd | 2 +- man/module_teal_data.Rd | 4 ++-- man/module_transform_data.Rd | 8 ++++---- man/teal_data_module.Rd | 4 +++- man/teal_modules.Rd | 6 +++--- man/teal_transform_module.Rd | 4 ++-- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/man/example_module.Rd b/man/example_module.Rd index 73fe986c63..e3898b696a 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -24,8 +24,8 @@ There are 2 reserved values that have specific behaviors: argument. }} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. -Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. +Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} will be added to the \code{datanames}.} } diff --git a/man/make_teal_transform_server.Rd b/man/make_teal_transform_server.Rd index f642f94954..ecda077502 100644 --- a/man/make_teal_transform_server.Rd +++ b/man/make_teal_transform_server.Rd @@ -25,7 +25,7 @@ with the value of the respective input (matched by the name) - for example in \examples{ trim_iris <- teal_transform_module( - label = "Simplified interactive transform for iris", + label = "Simplified interactive transformator for iris", datanames = "iris", ui = function(id) { ns <- NS(id) diff --git a/man/module_teal_data.Rd b/man/module_teal_data.Rd index ab4be37c20..4f700e8596 100644 --- a/man/module_teal_data.Rd +++ b/man/module_teal_data.Rd @@ -37,8 +37,8 @@ srv_validate_reactive_teal_data( \item{validate_shiny_silent_error}{(\code{logical}) If \code{TRUE}, then \code{shiny.silent.error} is validated and} -\item{is_transform_failed}{(\code{reactiveValues}) contains \code{logical} flags named after each transform. -Help to determine if any previous transform failed, so that following transformators can be disabled +\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.} \item{data}{(\verb{reactive teal_data})} diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index 41bcdc7dc1..8b51fd6f85 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -19,8 +19,8 @@ srv_teal_transform_data( \arguments{ \item{id}{(\code{character(1)}) Module id} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. -Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. +Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} will be added to the \code{datanames}.} @@ -28,8 +28,8 @@ will be added to the \code{datanames}.} \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 transform. -Help to determine if any previous transform failed, so that following transformators can be disabled +\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{ diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 77a7805d4b..683c6d9ef9 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,7 +35,9 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character} or \code{language}) code to evaluate. If \code{character}, comments are retained.} +\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. +It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an +\code{expression} being a result of \code{parse(keep.source = TRUE)}.} \item{data}{(\code{teal_data_module}) object} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index 78a547b60e..ed0c641da9 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -86,8 +86,8 @@ argument. \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. -Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. +Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} will be added to the \code{datanames}.} @@ -163,7 +163,7 @@ changes the behavior as follows: \itemize{ \item If \code{module(datanames)} is \code{NULL} and the \code{transformators} have defined \code{datanames}, the sidebar will appear showing the \code{transformators}' datasets, instead of being hidden. -\item If \code{module(datanames)} is set to specific values and any \code{transform} has \code{datanames = "all"}, +\item If \code{module(datanames)} is set to specific values and any \code{transformator} has \code{datanames = "all"}, the module may receive extra datasets that could be unnecessary } } diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index b4f68825e5..4e4b7e8cea 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -79,7 +79,7 @@ corresponding \code{input} parameter: \examples{ data_transformators <- list( teal_transform_module( - label = "Static transform for iris", + label = "Static transformator for iris", datanames = "iris", server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -92,7 +92,7 @@ data_transformators <- list( } ), teal_transform_module( - label = "Interactive transform for iris", + label = "Interactive transformator for iris", datanames = "iris", ui = function(id) { ns <- NS(id) From d7f8366b6217549b38b3654ebde0a5748c20dda2 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 10:41:27 +0100 Subject: [PATCH 063/165] update WORDLIST --- inst/WORDLIST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inst/WORDLIST b/inst/WORDLIST index 41727a7f93..c7d124a12e 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -29,5 +29,7 @@ summarization tabset themer theming +transformator +transformators ui uncheck From 0481d150ed5e75922eb5f0be179af211357d78db Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 10:47:41 +0100 Subject: [PATCH 064/165] update docuemntaion --- man/teal_data_module.Rd | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 683c6d9ef9..77a7805d4b 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,9 +35,7 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. -It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an -\code{expression} being a result of \code{parse(keep.source = TRUE)}.} +\item{code}{(\code{character} or \code{language}) code to evaluate. If \code{character}, comments are retained.} \item{data}{(\code{teal_data_module}) object} From 103ad2733d59fc721122fb93ae4629055304d1ea Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 10:47:55 +0100 Subject: [PATCH 065/165] change the object in example_module and document it --- R/dummy_functions.R | 10 ++++++++-- man/example_module.Rd | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 9588c50586..3b23a73850 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -2,6 +2,12 @@ #' #' `r lifecycle::badge("experimental")` #' +#' This module creates an object called `object` that can be modified with decorators. +#' The `object` is determined by what's selected in `Choose a dataset` input in UI. +#' The object can be anything that can be handled by `renderPrint()`. +#' See the `vignette("decorate-modules-output", package = "teal")` or [`teal_transform_module`] +#' to read more about decorators. +#' #' @inheritParams teal_modules #' @return A `teal` module which can be included in the `modules` argument to [init()]. #' @examples @@ -40,7 +46,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra table_data <- reactive({ within(data(), { - table <- dataname + object <- dataname }, dataname = as.name(input$dataname) ) @@ -50,7 +56,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra output$text <- renderPrint({ req(table_data_decorated) - table_data_decorated()[["table"]] + table_data_decorated()[["object"]] }) teal.widgets::verbatim_popup_srv( diff --git a/man/example_module.Rd b/man/example_module.Rd index e3898b696a..c36f7c6451 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -35,6 +35,13 @@ A \code{teal} module which can be included in the \code{modules} argument to \co \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} } +\details{ +This module creates an object called \code{object} that can be modified with decorators. +The \code{object} is determined by what's selected in \verb{Choose a dataset} input in UI. +The object can be anything that can be handled by \code{renderPrint()}. +See the \code{vignette("decorate-modules-output", package = "teal")} or \code{\link{teal_transform_module}} +to read more about decorators. +} \examples{ app <- init( data = teal_data(IRIS = iris, MTCARS = mtcars), From 0e1d83fe01229fbc3185f3d79712faf70ecc652f Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:56:35 +0000 Subject: [PATCH 066/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_data_module.Rd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 77a7805d4b..683c6d9ef9 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,7 +35,9 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character} or \code{language}) code to evaluate. If \code{character}, comments are retained.} +\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. +It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an +\code{expression} being a result of \code{parse(keep.source = TRUE)}.} \item{data}{(\code{teal_data_module}) object} From e91d198443f93db99319a13091dbffe6c35f8f46 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 13:36:11 +0100 Subject: [PATCH 067/165] update examples --- R/teal_transform_module.R | 2 +- man/teal_data_module.Rd | 4 +--- man/teal_transform_module.Rd | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index dca138917f..654e4216a2 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -103,7 +103,7 @@ #' output_decorator <- teal_transform_module( #' server = make_teal_transform_server( #' expression( -#' table <- rev(table) +#' object <- rev(object) #' ) #' ) #' ) diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 683c6d9ef9..77a7805d4b 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,9 +35,7 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. -It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an -\code{expression} being a result of \code{parse(keep.source = TRUE)}.} +\item{code}{(\code{character} or \code{language}) code to evaluate. If \code{character}, comments are retained.} \item{data}{(\code{teal_data_module}) object} diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 4e4b7e8cea..0bede146e2 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -118,7 +118,7 @@ data_transformators <- list( output_decorator <- teal_transform_module( server = make_teal_transform_server( expression( - table <- rev(table) + object <- rev(object) ) ) ) From 7576e118b196299f557d0e2316ae2b8a86ab5eb4 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:45:30 +0000 Subject: [PATCH 068/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_data_module.Rd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 77a7805d4b..683c6d9ef9 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,7 +35,9 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character} or \code{language}) code to evaluate. If \code{character}, comments are retained.} +\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. +It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an +\code{expression} being a result of \code{parse(keep.source = TRUE)}.} \item{data}{(\code{teal_data_module}) object} From 8ced169d2f9f381c7e022e25971fd965c2628ec9 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 14:23:57 +0100 Subject: [PATCH 069/165] create two shinytest2 tests for decorators --- tests/testthat/test-shinytest2-decorators.R | 129 ++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 tests/testthat/test-shinytest2-decorators.R diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R new file mode 100644 index 0000000000..afe2fb2b01 --- /dev/null +++ b/tests/testthat/test-shinytest2-decorators.R @@ -0,0 +1,129 @@ +testthat::skip_if_not_installed("shinytest2") +testthat::skip_if_not_installed("rvest") + +testthat::test_that("e2e: module with decorator shows decorator's UI and output reacts to changes in UI", { + skip_if_too_deep(5) + + interactive_decorator <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("append_text"), "Append text", value = "random text") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + object <- paste0(object, append_text) + }, + append_text = input$append_text + ) + }) + }) + } + ) + + app <- TealAppDriver$new( + data = teal.data::teal_data(x = "Text Input"), + modules = example_module(label = "Example Module", decorators = interactive_decorator) + ) + + app$navigate_teal_tab("Example Module") + + input_id <- 'decorate-transform_module-transform_module-append_text' + + testthat::expect_true( + app$is_visible( + sprintf( + '#%s-%s', + app$active_module_ns(), + input_id + ) + ) + ) + + testthat::expect_identical( + app$active_module_element_text( + paste0(input_id, '-label') + ), + "Append text" + ) + + testthat::expect_identical( + app$get_active_module_input(input_id), + "random text" + ) + + testthat::expect_identical( + app$get_active_module_output('text'), + paste0('[1] \"', "Text Input", "random text", '\"'), + "[1] \"Text Inputrandom text\"" + ) + + app$set_active_module_input(input_id, "new text") + + testthat::expect_identical( + app$get_active_module_output('text'), + paste0('[1] \"', "Text Input", "new text", '\"'), + "[1] \"Text Inputrandom text\"" + ) + + app$stop() +}) + +testthat::test_that("e2e: module with decorator, where server fails, shows shiny error message", { + skip_if_too_deep(5) + failing_decorator <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("append_text"), "Append text", value = "random text") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive(stop("This is error")) + }) + } + ) + app <- TealAppDriver$new( + data = teal.data::teal_data(iris = iris), + modules = example_module(label = "Example Module", decorators = failing_decorator) + ) + + app$navigate_teal_tab("Example Module") + + input_id <- 'decorate-transform_module-silent_error-message' + + testthat::expect_true( + app$is_visible( + sprintf( + '#%s-%s', + app$active_module_ns(), + input_id + ) + ) + ) + + app$expect_validation_error() + + testthat::expect_identical( + app$active_module_element_text(input_id), + paste( + "Shiny error when executing the `data` module.", + "This is error", + "Check your inputs or contact app developer if error persists.", + sep = "\n" + ) + ) + + testthat::expect_identical( + app$get_active_module_output('text'), + "NULL" + ) + + app$stop() +}) From 532fe7364b079e71097c42807d6e0b584eb34af2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:26:45 +0000 Subject: [PATCH 070/165] [skip style] [skip vbump] Restyle files --- tests/testthat/test-shinytest2-decorators.R | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R index afe2fb2b01..946bb9ce59 100644 --- a/tests/testthat/test-shinytest2-decorators.R +++ b/tests/testthat/test-shinytest2-decorators.R @@ -33,12 +33,12 @@ testthat::test_that("e2e: module with decorator shows decorator's UI and output app$navigate_teal_tab("Example Module") - input_id <- 'decorate-transform_module-transform_module-append_text' + input_id <- "decorate-transform_module-transform_module-append_text" testthat::expect_true( app$is_visible( sprintf( - '#%s-%s', + "#%s-%s", app$active_module_ns(), input_id ) @@ -47,7 +47,7 @@ testthat::test_that("e2e: module with decorator shows decorator's UI and output testthat::expect_identical( app$active_module_element_text( - paste0(input_id, '-label') + paste0(input_id, "-label") ), "Append text" ) @@ -58,7 +58,7 @@ testthat::test_that("e2e: module with decorator shows decorator's UI and output ) testthat::expect_identical( - app$get_active_module_output('text'), + app$get_active_module_output("text"), paste0('[1] \"', "Text Input", "random text", '\"'), "[1] \"Text Inputrandom text\"" ) @@ -66,7 +66,7 @@ testthat::test_that("e2e: module with decorator shows decorator's UI and output app$set_active_module_input(input_id, "new text") testthat::expect_identical( - app$get_active_module_output('text'), + app$get_active_module_output("text"), paste0('[1] \"', "Text Input", "new text", '\"'), "[1] \"Text Inputrandom text\"" ) @@ -96,12 +96,12 @@ testthat::test_that("e2e: module with decorator, where server fails, shows shin app$navigate_teal_tab("Example Module") - input_id <- 'decorate-transform_module-silent_error-message' + input_id <- "decorate-transform_module-silent_error-message" testthat::expect_true( app$is_visible( sprintf( - '#%s-%s', + "#%s-%s", app$active_module_ns(), input_id ) @@ -121,7 +121,7 @@ testthat::test_that("e2e: module with decorator, where server fails, shows shin ) testthat::expect_identical( - app$get_active_module_output('text'), + app$get_active_module_output("text"), "NULL" ) From 40c73fd3755802f8f778ad6389f7d9eae334f548 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 14:42:34 +0100 Subject: [PATCH 071/165] combine decorator vignettes into one --- vignettes/decorate-module-output.Rmd | 189 ++++++++++++++++ .../decorate-multiple-modules-outputs.Rmd | 202 ------------------ 2 files changed, 189 insertions(+), 202 deletions(-) delete mode 100644 vignettes/decorate-multiple-modules-outputs.Rmd diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 06ba40fa73..ec3fd99219 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -391,3 +391,192 @@ if (interactive()) { } ``` +## Multiple Decorators + +### Example Module + +It is also possible to pass multiple decorators into a module. See below example that shows how you can +manipulate multiple decorators passed to the module. In this scenario different decorator is selected, +depending on the input of the module, to be applied to the `ggplot2` plot. + +```{r} +library(ggplot2) +tm_decorated_plot <- function( + label = "module", + decorators = list( + plot_1 = teal_transform_module(), + plot_2 = teal_transform_module() + )) { + + module( + label = label, + ui = function(id, decorators) { + ns <- NS(id) + div( + selectInput(ns("dataname"), label = "select dataname", choices = NULL), + selectInput(ns("x"), label = "select x", choices = NULL), + selectInput(ns("y"), label = "select y", choices = NULL), + selectInput(ns("plot_type"), "plot type", choices = c("plot 1", "plot 2"), selected = "plot 1"), + div( + id = ns("decorate_1_wrapper"), + ui_teal_transform_data(ns("decorate_1"), transformators = decorators[[1]]) + ), + div( + id = ns("decorate_2_wrapper"), + ui_teal_transform_data(ns("decorate_2"), transformators = decorators[[2]]) + ), + plotOutput(ns("plot")), + verbatimTextOutput(ns("text")) + ) + }, + server = function(id, data, decorators) { + moduleServer(id, function(input, output, session) { + observeEvent(data(), { + updateSelectInput(inputId = "dataname", choices = names(data())) + }) + + observeEvent(input$dataname, { + req(input$dataname) + updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) + updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) + }) + + observeEvent(input$plot_type, { + if (input$plot_type == "plot 1") { + shinyjs::hide("decorate_2_wrapper") + shinyjs::show("decorate_1_wrapper") + } else { + shinyjs::show("decorate_2_wrapper") + shinyjs::hide("decorate_1_wrapper") + } + }) + + plot_1_data <- debounce(reactive({ + req(input$dataname, input$x, input$y) + within(data(), + { + plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 1") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }), 200) + + plot_2_data <- debounce(reactive({ + req(input$dataname, input$x, input$y) + within(data(), + { + plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 2") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) + ) + }), 200) + + plot_1_data_decorated <- srv_teal_transform_data("decorate_1", data = plot_1_data, transformators = decorators[[1]]) + plot_2_data_decorated <- srv_teal_transform_data("decorate_2", data = plot_2_data, transformators = decorators[[2]]) + + + plot_r <- reactive({ + req(input$plot_type) + if (input$plot_type == "plot 1") { + req(plot_1_data_decorated()) + plot_1_data_decorated()[["plot_1"]] + } else if (input$plot_type == "plot 2") { + req(plot_2_data_decorated()) + plot_2_data_decorated()[["plot_2"]] + } + }) + + output$plot <- renderPlot(plot_r()) + output$text <- renderText({ + if (input$plot_type == "plot 1") { + teal.code::get_code(req(plot_1_data())) + } else if (input$plot_type == "plot 2") { + teal.code::get_code(req(plot_2_data())) + } + }) + }) + }, + ui_args = list(decorators = decorators), + server_args = list(decorators = decorators) + ) +} +``` + + +```{r} +interactive_decorator_1 <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis 1") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + plot_1 <- plot_1 + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title + ) + }) + }) + } +) + +interactive_decorator_2 <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis 2") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + plot_2 <- plot_2 + + xlab(x_axis_title) + }, + x_axis_title = input$x_axis_title + ) + }) + }) + } +) +``` + +### Application + +```{r} +app <- init( + data = teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot("identity"), + tm_decorated_plot( + "interactive", + decorators = list( + plot_1 = interactive_decorator_1, + plot_2 = interactive_decorator_2 + ) + ) + ) +) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +``` diff --git a/vignettes/decorate-multiple-modules-outputs.Rmd b/vignettes/decorate-multiple-modules-outputs.Rmd deleted file mode 100644 index f13299fc0e..0000000000 --- a/vignettes/decorate-multiple-modules-outputs.Rmd +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: "Customizing Multiple Modules Outputs" -author: "NEST CoreDev" -output: - rmarkdown::html_vignette: - toc: true -vignette: > - %\VignetteIndexEntry{Customizing Multiple Modules Outputs} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -Below document contains example code presenting how you can change multiple objects inside one module. -This is an extension of `Customizing Module Output` vignette. - -```{r} -library(ggplot2) -tm_decorated_plot <- function( - label = "module", - decorator = list( - plot_1 = teal_transform_module(), - plot_2 = teal_transform_module() - )) { - # decorator: teal_transform_module, language, function - decorator_objs <- list( - plot_1 = decorate_teal_data(x = decorator[["plot_1"]], output_name = "plot_1"), - plot_2 = decorate_teal_data(x = decorator[["plot_2"]], output_name = "plot_2") - ) - # outputs teal_transform_module - - module( - label = label, - ui = function(id, decorator_objs) { - ns <- NS(id) - div( - selectInput(ns("dataname"), label = "select dataname", choices = NULL), - selectInput(ns("x"), label = "select x", choices = NULL), - selectInput(ns("y"), label = "select y", choices = NULL), - selectInput(ns("plot_type"), "plot type", choices = c("plot 1", "plot 2"), selected = "plot 1"), - div( - id = ns("decorate_1_wrapper"), - ui_teal_data(ns("decorate_1"), data_module = decorator_objs[[1]]) - ), - div( - id = ns("decorate_2_wrapper"), - ui_teal_data(ns("decorate_2"), data_module = decorator_objs[[2]]) - ), - plotOutput(ns("plot")), - verbatimTextOutput(ns("text")) - ) - }, - server = function(id, data, decorator_objs) { - moduleServer(id, function(input, output, session) { - observeEvent(data(), { - updateSelectInput(inputId = "dataname", choices = names(data())) - }) - - observeEvent(input$dataname, { - req(input$dataname) - updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) - updateSelectInput(inputId = "y", label = "select y", choices = colnames(data()[[input$dataname]])) - }) - - observeEvent(input$plot_type, { - if (input$plot_type == "plot 1") { - shinyjs::hide("decorate_2_wrapper") - shinyjs::show("decorate_1_wrapper") - } else { - shinyjs::show("decorate_2_wrapper") - shinyjs::hide("decorate_1_wrapper") - } - }) - - q1_1 <- reactive({ - req(input$dataname, input$x, input$y) - within(data(), - { - plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggtitle("plot 1") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }) - - q2_1 <- reactive({ - req(input$dataname, input$x, input$y) - within(data(), - { - plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggtitle("plot 2") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }) - - q1_2 <- srv_transform_data("decorate_1", data = q1_1, data_module = decorator_objs[[1]], modules = module()) - q2_2 <- srv_transform_data("decorate_2", data = q2_1, data_module = decorator_objs[[2]], modules = module()) - - - plot_r <- reactive({ - req(input$plot_type) - if (input$plot_type == "plot 1") { - req(q1_2()) - q1_2()[["plot_1"]] - } else if (input$plot_type == "plot 2") { - req(q2_2()) - q2_2()[["plot_2"]] - } - }) - - output$plot <- renderPlot(plot_r()) - output$text <- renderText({ - if (input$plot_type == "plot 1") { - teal.code::get_code(q1_2()) - } else if (input$plot_type == "plot 2") { - teal.code::get_code(q2_2()) - } - }) - }) - }, - ui_args = list(decorator_objs = decorator_objs), - server_args = list(decorator_objs = decorator_objs) - ) -} -``` - - -```{r} -interactive_decorator_1 <- teal_transform_module( - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("x_axis_title"), "X axis title", value = "x axis 1") - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - within(data(), - { - plot_1 <- plot_1 + - xlab(x_axis_title) - }, - x_axis_title = input$x_axis_title - ) - }) - }) - } -) - -interactive_decorator_2 <- teal_transform_module( - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("x_axis_title"), "X axis title", value = "x axis 2") - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - within(data(), - { - plot_2 <- plot_2 + - xlab(x_axis_title) - }, - x_axis_title = input$x_axis_title - ) - }) - }) - } -) -``` - - - -```{r} -app <- init( - data = teal_data(iris = iris, mtcars = mtcars), - modules = modules( - tm_decorated_plot("identity"), - tm_decorated_plot( - "interactive", - decorator = list( - plot_1 = interactive_decorator_1, - plot_2 = interactive_decorator_2 - ) - ) - ) -) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -``` From 80af8a263cf2c7f1e9fcbbdd8651ba2b04fadd8c Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:45:19 +0000 Subject: [PATCH 072/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-module-output.Rmd | 49 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index ec3fd99219..3d53c34335 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -407,7 +407,6 @@ tm_decorated_plot <- function( plot_1 = teal_transform_module(), plot_2 = teal_transform_module() )) { - module( label = label, ui = function(id, decorators) { @@ -434,13 +433,13 @@ tm_decorated_plot <- function( observeEvent(data(), { updateSelectInput(inputId = "dataname", choices = names(data())) }) - + observeEvent(input$dataname, { req(input$dataname) updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) - + observeEvent(input$plot_type, { if (input$plot_type == "plot 1") { shinyjs::hide("decorate_2_wrapper") @@ -450,39 +449,39 @@ tm_decorated_plot <- function( shinyjs::hide("decorate_1_wrapper") } }) - + plot_1_data <- debounce(reactive({ req(input$dataname, input$x, input$y) within(data(), - { - plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggtitle("plot 1") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) + { + plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 1") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) ) }), 200) - + plot_2_data <- debounce(reactive({ req(input$dataname, input$x, input$y) within(data(), - { - plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggtitle("plot 2") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) + { + plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + + ggtitle("plot 2") + }, + dataname = as.name(input$dataname), + x = as.name(input$x), + y = as.name(input$y) ) }), 200) - + plot_1_data_decorated <- srv_teal_transform_data("decorate_1", data = plot_1_data, transformators = decorators[[1]]) plot_2_data_decorated <- srv_teal_transform_data("decorate_2", data = plot_2_data, transformators = decorators[[2]]) - - + + plot_r <- reactive({ req(input$plot_type) if (input$plot_type == "plot 1") { @@ -493,7 +492,7 @@ tm_decorated_plot <- function( plot_2_data_decorated()[["plot_2"]] } }) - + output$plot <- renderPlot(plot_r()) output$text <- renderText({ if (input$plot_type == "plot 1") { From 5c839b5d1199678a76490cea220a4582febd1472 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 14:56:58 +0100 Subject: [PATCH 073/165] lintr fixes --- R/modules.R | 6 +++--- tests/testthat/test-init.R | 2 +- tests/testthat/test-modules.R | 5 ++++- vignettes/decorate-module-output.Rmd | 10 ++++++++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/R/modules.R b/R/modules.R index 129180ad71..0614e9bf20 100644 --- a/R/modules.R +++ b/R/modules.R @@ -75,9 +75,9 @@ setOldClass("teal_modules") #' @param ui_args (named `list`) with additional arguments passed on to the UI function. #' @param x (`teal_module` or `teal_modules`) Object to format/print. #' @param transformators (`list` of `teal_transform_module`) that will be applied to transformator module's data input. -#' Each transformator module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the list. -#' If so, the module developer is responsible to display the UI in the module itself. `datanames` of the `transformators` -#' will be added to the `datanames`. +#' Each transformator module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the +#' list. If so, the module developer is responsible to display the UI in the module itself. `datanames` of the +#' `transformators` will be added to the `datanames`. #' #' #' @param ... diff --git a/tests/testthat/test-init.R b/tests/testthat/test-init.R index b288a629cc..ad6daeda9e 100644 --- a/tests/testthat/test-init.R +++ b/tests/testthat/test-init.R @@ -83,7 +83,7 @@ testthat::test_that( ) testthat::test_that( - "init does not throw warning when datanames in modules incompatible w/ datanames in data and there are transformators", + "init doesn't throw warning when datanames in modules incompatible w/ datanames in data and there are transformators", { testthat::expect_no_warning( init( diff --git a/tests/testthat/test-modules.R b/tests/testthat/test-modules.R index 43627fbf5a..0ab9704bf5 100644 --- a/tests/testthat/test-modules.R +++ b/tests/testthat/test-modules.R @@ -121,7 +121,10 @@ testthat::test_that("module() returns list of class 'teal_module' containing inp ui_args = NULL ) testthat::expect_s3_class(test_module, "teal_module") - testthat::expect_named(test_module, c("label", "server", "ui", "datanames", "server_args", "ui_args", "transformators")) + testthat::expect_named( + test_module, + c("label", "server", "ui", "datanames", "server_args", "ui_args", "transformators") + ) testthat::expect_identical(test_module$label, "aaa1") testthat::expect_identical(test_module$server, call_module_server_fun) testthat::expect_identical(test_module$ui, ui_fun1) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 3d53c34335..54f293057b 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -478,8 +478,14 @@ tm_decorated_plot <- function( ) }), 200) - plot_1_data_decorated <- srv_teal_transform_data("decorate_1", data = plot_1_data, transformators = decorators[[1]]) - plot_2_data_decorated <- srv_teal_transform_data("decorate_2", data = plot_2_data, transformators = decorators[[2]]) + plot_1_data_decorated <- + srv_teal_transform_data( + "decorate_1", data = plot_1_data, transformators = decorators[[1]] + ) + plot_2_data_decorated <- + srv_teal_transform_data( + "decorate_2", data = plot_2_data, transformators = decorators[[2]] + ) plot_r <- reactive({ From 8bc8c1081546eea340add61875823a8fd11e9f3d Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:00:44 +0000 Subject: [PATCH 074/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-module-output.Rmd | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 54f293057b..115be66bf3 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -478,13 +478,15 @@ tm_decorated_plot <- function( ) }), 200) - plot_1_data_decorated <- + plot_1_data_decorated <- srv_teal_transform_data( - "decorate_1", data = plot_1_data, transformators = decorators[[1]] + "decorate_1", + data = plot_1_data, transformators = decorators[[1]] ) - plot_2_data_decorated <- + plot_2_data_decorated <- srv_teal_transform_data( - "decorate_2", data = plot_2_data, transformators = decorators[[2]] + "decorate_2", + data = plot_2_data, transformators = decorators[[2]] ) From 3d5bc4e76e42c6dc2da434bb4902714fa8331408 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 15:10:45 +0100 Subject: [PATCH 075/165] rebuild docs --- man/example_module.Rd | 6 +++--- man/module_transform_data.Rd | 6 +++--- man/teal_modules.Rd | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/man/example_module.Rd b/man/example_module.Rd index c36f7c6451..ced662b383 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -25,9 +25,9 @@ argument. }} \item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} -will be added to the \code{datanames}.} +Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the +list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the +\code{transformators} will be added to the \code{datanames}.} } \value{ A \code{teal} module which can be included in the \code{modules} argument to \code{\link[=init]{init()}}. diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index 8b51fd6f85..d1b28fdca3 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -20,9 +20,9 @@ srv_teal_transform_data( \item{id}{(\code{character(1)}) Module id} \item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} -will be added to the \code{datanames}.} +Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the +list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the +\code{transformators} will be added to the \code{datanames}.} \item{data}{(\verb{reactive teal_data})} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index ed0c641da9..d187a0c5ee 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -87,9 +87,9 @@ argument. \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} \item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformators} -will be added to the \code{datanames}.} +Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the +list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the +\code{transformators} will be added to the \code{datanames}.} \item{...}{\itemize{ \item For \code{modules()}: (\code{teal_module} or \code{teal_modules}) Objects to wrap into a tab. From 87acaec0aec7d33125fbca286412b9a9e7b6973f Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 15:36:01 +0100 Subject: [PATCH 076/165] fix one more test --- tests/testthat/test-module_teal.R | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 25ac7d3e95..71b32659e7 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1878,8 +1878,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { ) }) - testthat::it("throws warning when transformator return reactive.event", { - testthat::expect_warning( + testthat::it("does not throw a warning when transformator returns reactive.event", { testServer( app = srv_teal, args = list( @@ -1901,9 +1900,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { session$setInputs("teal_modules-active_tab" = "module") session$flushReact() } - ), - "Using eventReactive in teal_transform module server code should be avoided" - ) + ) }) testthat::it("fails when transformator doesn't return reactive", { From 2d7089c4852a7c821392468841d78c143a44a1ce Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:40:02 +0000 Subject: [PATCH 077/165] [skip style] [skip vbump] Restyle files --- tests/testthat/test-module_teal.R | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 71b32659e7..8e7c730aad 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1879,28 +1879,28 @@ testthat::describe("srv_teal teal_module(s) transformator", { }) testthat::it("does not throw a warning when transformator returns reactive.event", { - testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris), - modules = modules( - module( - server = function(id, data) data, - transformators = list( - teal_transform_module( - ui = function(id) textInput("a", "an input"), - server = function(id, data) eventReactive(input$a, data()) - ) + testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules( + module( + server = function(id, data) data, + transformators = list( + teal_transform_module( + ui = function(id) textInput("a", "an input"), + server = function(id, data) eventReactive(input$a, data()) ) ) ) - ), - expr = { - session$setInputs("teal_modules-active_tab" = "module") - session$flushReact() - } - ) + ) + ), + expr = { + session$setInputs("teal_modules-active_tab" = "module") + session$flushReact() + } + ) }) testthat::it("fails when transformator doesn't return reactive", { From 28f17da9594c5395358d695210c47e3a02e05400 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:04:54 +0100 Subject: [PATCH 078/165] Update tests/testthat/test-shinytest2-decorators.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dawid Kałędkowski Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- tests/testthat/test-shinytest2-decorators.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R index 946bb9ce59..192ccad59c 100644 --- a/tests/testthat/test-shinytest2-decorators.R +++ b/tests/testthat/test-shinytest2-decorators.R @@ -1,7 +1,7 @@ testthat::skip_if_not_installed("shinytest2") testthat::skip_if_not_installed("rvest") -testthat::test_that("e2e: module with decorator shows decorator's UI and output reacts to changes in UI", { +testthat::test_that("e2e: module with decorator shows decorator's UI and module output is modified interactively upon changes in decorate module", { skip_if_too_deep(5) interactive_decorator <- teal_transform_module( From 8d3a465350a5dae3ffea914640c2de94445b1819 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:08:40 +0100 Subject: [PATCH 079/165] Update R/modules.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dawid Kałędkowski Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/modules.R | 3 --- 1 file changed, 3 deletions(-) diff --git a/R/modules.R b/R/modules.R index 0614e9bf20..f8e260e4ad 100644 --- a/R/modules.R +++ b/R/modules.R @@ -75,9 +75,6 @@ setOldClass("teal_modules") #' @param ui_args (named `list`) with additional arguments passed on to the UI function. #' @param x (`teal_module` or `teal_modules`) Object to format/print. #' @param transformators (`list` of `teal_transform_module`) that will be applied to transformator module's data input. -#' Each transformator module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the -#' list. If so, the module developer is responsible to display the UI in the module itself. `datanames` of the -#' `transformators` will be added to the `datanames`. #' #' #' @param ... From 30002e11b7637744f2814a1d23764cfd7378b34a Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 16:14:05 +0100 Subject: [PATCH 080/165] bring back test for eventReactive value returned by teal_transform_module --- R/teal_transform_module.R | 13 +++++++++++++ tests/testthat/test-module_teal.R | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 654e4216a2..3a434baf50 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -128,6 +128,19 @@ teal_transform_module <- function(ui = NULL, ui = ui, server = function(id, data) { data_out <- server(id, data) + + if (inherits(data_out, "reactive.event")) { + # This warning message partially detects when `eventReactive` is used in `data_module`. + warning( + "teal_transform_module() ", + "Using eventReactive in teal_transform module server code should be avoided as it ", + "may lead to unexpected behavior. See the vignettes for more information ", + "(`vignette(\"data-transform-as-shiny-module\", package = \"teal\")`).", + call. = FALSE + ) + } + + decorate_err_msg( assert_reactive(data_out), pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 71b32659e7..c4ef1ad13c 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1878,7 +1878,8 @@ testthat::describe("srv_teal teal_module(s) transformator", { ) }) - testthat::it("does not throw a warning when transformator returns reactive.event", { + testthat::it("throws a warning when transformator returns reactive.event", { + testthat::expect_warning( testServer( app = srv_teal, args = list( @@ -1900,7 +1901,9 @@ testthat::describe("srv_teal teal_module(s) transformator", { session$setInputs("teal_modules-active_tab" = "module") session$flushReact() } - ) + ), + "Using eventReactive in teal_transform module server code should be avoided" + ) }) testthat::it("fails when transformator doesn't return reactive", { From af099e8976beb12a7b0c45817e9865610f970023 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 16:15:22 +0100 Subject: [PATCH 081/165] prefix in vignette --- vignettes/decorate-module-output.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 115be66bf3..e0d0d0c845 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -456,7 +456,7 @@ tm_decorated_plot <- function( { plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() + - ggtitle("plot 1") + ggplot2::ggtitle("plot 1") }, dataname = as.name(input$dataname), x = as.name(input$x), @@ -470,7 +470,7 @@ tm_decorated_plot <- function( { plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() + - ggtitle("plot 2") + ggplot2::ggtitle("plot 2") }, dataname = as.name(input$dataname), x = as.name(input$x), From 37dc22679a9dd2f89248deb748ab0a51ef2b72f1 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 16:16:02 +0100 Subject: [PATCH 082/165] rerun docs --- man/example_module.Rd | 5 +---- man/module_transform_data.Rd | 5 +---- man/teal_modules.Rd | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/man/example_module.Rd b/man/example_module.Rd index ced662b383..92aaedfc8f 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -24,10 +24,7 @@ There are 2 reserved values that have specific behaviors: argument. }} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the -list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the -\code{transformators} will be added to the \code{datanames}.} +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} } \value{ A \code{teal} module which can be included in the \code{modules} argument to \code{\link[=init]{init()}}. diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index d1b28fdca3..e37dc0707a 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -19,10 +19,7 @@ srv_teal_transform_data( \arguments{ \item{id}{(\code{character(1)}) Module id} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the -list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the -\code{transformators} will be added to the \code{datanames}.} +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} \item{data}{(\verb{reactive teal_data})} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index d187a0c5ee..99821a7fb2 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -86,10 +86,7 @@ argument. \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the -list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the -\code{transformators} will be added to the \code{datanames}.} +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} \item{...}{\itemize{ \item For \code{modules()}: (\code{teal_module} or \code{teal_modules}) Objects to wrap into a tab. From 5663ca278ed022e4643b2f3d3b1f2e4c6c38fca2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:17:38 +0000 Subject: [PATCH 083/165] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/example_module.Rd | 5 +---- man/module_transform_data.Rd | 5 +---- man/teal_modules.Rd | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/man/example_module.Rd b/man/example_module.Rd index ced662b383..92aaedfc8f 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -24,10 +24,7 @@ There are 2 reserved values that have specific behaviors: argument. }} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the -list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the -\code{transformators} will be added to the \code{datanames}.} +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} } \value{ A \code{teal} module which can be included in the \code{modules} argument to \code{\link[=init]{init()}}. diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index d1b28fdca3..e37dc0707a 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -19,10 +19,7 @@ srv_teal_transform_data( \arguments{ \item{id}{(\code{character(1)}) Module id} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the -list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the -\code{transformators} will be added to the \code{datanames}.} +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} \item{data}{(\verb{reactive teal_data})} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index d187a0c5ee..99821a7fb2 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -86,10 +86,7 @@ argument. \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} -\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input. -Each transformator module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the -list. If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the -\code{transformators} will be added to the \code{datanames}.} +\item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} \item{...}{\itemize{ \item For \code{modules()}: (\code{teal_module} or \code{teal_modules}) Objects to wrap into a tab. From 31aa2d2cab3fbee4a19c8accaebef86a00fa3840 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 14 Nov 2024 16:34:10 +0100 Subject: [PATCH 084/165] cleanup tests - remove ggplot2 examples --- tests/testthat/test-module_teal.R | 304 ++++++------------------------ 1 file changed, 61 insertions(+), 243 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index c4ef1ad13c..e236449033 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1,10 +1,5 @@ # comment: srv_teal is exported so the tests here are extensive and cover srv_data as well. # testing of srv_data is not needed. - - -# utils ----------------------------------------------------------------------------------------------------------- - - module_summary_table <<- function(output, id) { testthat::skip_if_not_installed("rvest") table_id <- sprintf("teal_modules-%s-data_summary-table", id) @@ -24,9 +19,6 @@ is_slices_equivalent <<- function(x, y, with_attrs = TRUE) { identical(x_list, y_list) } - -# transformators ------------------------------------------------------------------------------------------------------ - transform_list <<- list( fail = teal_transform_module( ui = function(id) NULL, @@ -73,178 +65,6 @@ transform_list <<- list( ) ) - -# decorators ------------------------------------------------------------------------------------------------------ - - -gg_xlab_decorator <<- function(output_name) { - teal_transform_module( - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("x_axis_title"), "X axis title", value = "x axis") - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - within(data(), - { - output_name <- output_name + - xlab(x_axis_title) - }, - x_axis_title = input$x_axis_title, - output_name = as.name(output_name) - ) - }) - }) - } - ) -} - -decorators <<- list( - static_decorator = teal_transform_module( - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - within(data(), { - plot <- plot + - ggtitle("This is title") + - xlab("x axis") - }) - }) - }) - } - ), - static_decorator_lang = teal_transform_module( - server = make_teal_transform_server( - expression( - plot <- plot + - ggtitle("This is title") + - xlab("x axis title") - ) - ) - ), - interactive_decorator = teal_transform_module( - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("x_axis_title"), "X axis title", value = "x axis") - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - within(data(), - { - plot <- plot + - ggtitle("This is title") + - xlab(x_axis_title) - }, - x_axis_title = input$x_axis_title - ) - }) - }) - } - ), - interactive_decorator_lang = teal_transform_module( - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("x_axis_title"), "X axis title", value = "x axis") - ) - }, - server = make_teal_transform_server( - expression( - plot <- plot + - ggtitle("This is title") + - xlab(x_axis_title) - ) - ) - ), - gg_xlab_decorator = gg_xlab_decorator("plot"), - failing_decorator = teal_transform_module( - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("x_axis_title"), "X axis title", value = "x axis") - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive(stop("This is error")) - }) - } - ) -) - -tm_decorated_plot <<- function(label = "module", transformators = list(), decorators = teal_transform_module()) { - module( - label = label, - ui = function(id, decorators) { - ns <- NS(id) - div( - selectInput(ns("dataname"), label = "select dataname", choices = NULL), - selectInput(ns("x"), label = "select x", choices = NULL), - selectInput(ns("y"), label = "select y", choices = NULL), - ui_teal_transform_data(ns("decorate"), transformators = decorators), - plotOutput(ns("plot")), - verbatimTextOutput(ns("text")) - ) - }, - server = function(id, data, decorators) { - moduleServer(id, function(input, output, session) { - observeEvent(data(), { - updateSelectInput(inputId = "dataname", choices = names(data())) - }) - - observeEvent(input$dataname, { - req(input$dataname) - updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) - updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) - }) - - q1 <- debounce( - reactive({ - req(input$dataname, input$x, input$y) - within(data(), - { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }), 200 - ) - - q2 <- srv_teal_transform_data("decorate", data = q1, transformators = decorators) - - plot_r <- reactive({ - req(q2()) - q2()[["plot"]] - }) - - output$plot <- renderPlot(plot_r()) - output$text <- renderText({ - teal.code::get_code(req(q2())) - }) - }) - }, - ui_args = list(decorators = decorators), - server_args = list(decorators = decorators) - ) -} - - -# tests ----------------------------------------------------------------------------------------------------------- - - - testthat::describe("srv_teal lockfile", { testthat::it(paste0( "creation process is invoked for teal.lockfile.mode = \"enabled\" ", @@ -2055,66 +1875,64 @@ testthat::describe("srv_teal teal_module(s) transformator", { ) }) - testthat::it("shows the decorator ui when decorator has it", { - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(tm_decorated_plot("interactive", decorators = decorators[["interactive_decorator"]])) - ), - expr = { - # TODO - } - ) - }) - - testthat::it("applies the decorator ui changes when module has a decorator with ui", { - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules( - tm_decorated_plot( - "interactive_decorator_lang", - decorators = decorators[["interactive_decorator_lang"]] - ) + testthat::it("changes module output for a module with a static decorator", { + testthat::skip("TODO") + output_decorator <- teal_transform_module( + server = make_teal_transform_server( + expression( + object <- rev(object) ) - ), - expr = { - # TODO - } + ) ) - }) - testthat::it("changes module output for a module with a static decorator", { shiny::testServer( app = srv_teal, args = list( id = "test", data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(tm_decorated_plot("static_decorator", decorators = decorators[["static_decorator"]])) + modules = modules(example_module("module 1", decorators = output_decorator)) ), expr = { - # session$setInputs(`teal_modules-active_tab` = "static_decorator") - # testthat::expect_identical(modules_output$static_decorator()()[["plot"]], TODO) + # session$setInputs(`teal_modules-active_tab` = "module 1") + # testthat::expect_identical(modules_output$module_1()()[["object"]], TODO) } ) }) - testthat::it("changes module output for a module with a static decorator that uses expression", { + + testthat::it("changes module output for a module with a decorator that is a function of object name", { + testthat::skip("TODO") + output_decorator <- function(output_name) { + teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("append_text"), "Append text", value = "random text") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + output_name <- paste0(output_name, append_text) + }, + append_text = input$append_text, + output_name = as.name(output_name) + ) + }) + }) + } + ) + } + shiny::testServer( app = srv_teal, args = list( id = "test", data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules( - tm_decorated_plot( - "static_decorator_lang", - decorators = decorators[["static_decorator_lang"]] - ) - ) + modules = modules(example_module("module 1", decorators = output_decorator[['object']])) ), expr = { # TODO @@ -2122,43 +1940,43 @@ testthat::describe("srv_teal teal_module(s) transformator", { ) }) - testthat::it("changes module output for a module with a decorator that is a function of object name", { - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules( - tm_decorated_plot( - "gg_xlab_decorator", - decorators = decorators[["gg_xlab_decorator"]] - ) + testthat::it("changes module output for a module with a interactive decorator", { + testthat::skip("TODO") + output_decorator <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("append_text"), "Append text", value = "random text") ) - ), - expr = { - # TODO + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + object <- paste0(object, append_text) + }, + append_text = input$append_text + ) + }) + }) } ) - }) - testthat::it("shows failure message when module with decorator fails", { shiny::testServer( app = srv_teal, args = list( id = "test", data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules( - tm_decorated_plot( - "failing_decorator", - decorators = decorators[["failing_decorator"]] - ) - ) + modules = modules(example_module("module 1", decorators = output_decorator)) ), expr = { # TODO } ) }) + }) testthat::describe("srv_teal summary table", { From ceb97ff9badf1dc62cf25e52e6d548a71f1847d0 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:35:54 +0100 Subject: [PATCH 085/165] Update R/dummy_functions.R Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/dummy_functions.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 3b23a73850..bff7964a8d 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -19,7 +19,9 @@ #' shinyApp(app$ui, app$server) #' } #' @export -example_module <- function(label = "example teal module", datanames = "all", transformators = list(), +example_module <- function(label = "example teal module", + datanames = "all", + transformators = list(), decorators = teal_transform_module()) { checkmate::assert_string(label) ans <- module( From 9ceff8b035fe2524e69644d5819afa91c47b7875 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:38:23 +0000 Subject: [PATCH 086/165] [skip style] [skip vbump] Restyle files --- R/dummy_functions.R | 4 ++-- tests/testthat/test-module_teal.R | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index bff7964a8d..1dd5c1ad5f 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -19,8 +19,8 @@ #' shinyApp(app$ui, app$server) #' } #' @export -example_module <- function(label = "example teal module", - datanames = "all", +example_module <- function(label = "example teal module", + datanames = "all", transformators = list(), decorators = teal_transform_module()) { checkmate::assert_string(label) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index e236449033..4f7feeffa6 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1915,11 +1915,11 @@ testthat::describe("srv_teal teal_module(s) transformator", { reactive({ req(data()) within(data(), - { - output_name <- paste0(output_name, append_text) - }, - append_text = input$append_text, - output_name = as.name(output_name) + { + output_name <- paste0(output_name, append_text) + }, + append_text = input$append_text, + output_name = as.name(output_name) ) }) }) @@ -1932,7 +1932,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { args = list( id = "test", data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(example_module("module 1", decorators = output_decorator[['object']])) + modules = modules(example_module("module 1", decorators = output_decorator[["object"]])) ), expr = { # TODO @@ -1954,10 +1954,10 @@ testthat::describe("srv_teal teal_module(s) transformator", { reactive({ req(data()) within(data(), - { - object <- paste0(object, append_text) - }, - append_text = input$append_text + { + object <- paste0(object, append_text) + }, + append_text = input$append_text ) }) }) @@ -1976,7 +1976,6 @@ testthat::describe("srv_teal teal_module(s) transformator", { } ) }) - }) testthat::describe("srv_teal summary table", { From 0860bdf944a73de789484848678607646b40d85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:12:32 +0100 Subject: [PATCH 087/165] fix: test with datanames validation --- tests/testthat/test-module_teal.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 4f7feeffa6..7229b2c97e 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -645,7 +645,7 @@ testthat::describe("srv_teal teal_modules", { trimws( rvest::html_text2( rvest::read_html( - output[["teal-datanames_warning-message"]]$html + output[["datanames_warning-message"]]$html ) ) ), From 1f97c13ac197f319df07199631a7ff828e97b808 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 08:48:12 +0100 Subject: [PATCH 088/165] Update vignettes/decorate-module-output.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index e0d0d0c845..32fb236872 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -27,7 +27,7 @@ output-modifying objects as decoration objects or decorators. ### Server The simplest way to create a decorator is to use `teal_transform_module` without specifying a `ui`. -This approach adds functionality solely to the server code of the `teal` app. +This approach adds functionality solely to the server code of the module. In the following example, we assume that the module contains an object (of class `ggplot2`) named `plot`. We modify the title and x-axis label of plot: From 2bb9291b0307ba68005279a8f96d352ca8494c51 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 08:50:04 +0100 Subject: [PATCH 089/165] Update vignettes/decorate-module-output.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 32fb236872..c21f84f720 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -69,7 +69,7 @@ static_decorator_lang <- teal_transform_module( To create a decorator with user interactivity, you can incorporate a `shiny` UI module. In the example below, the x-axis title is set dynamically via a `textInput`, allowing users to specify their preferred label. -Note how the input parameters are passed to the `within` function (e.g., `x_axis_title`): +Note how the input parameters are passed to the `within` function using its `...` argument. ```{r} interactive_decorator <- teal_transform_module( From 17afb2b50428c0b3f8cc0b1830117fa6db62b8e3 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 08:50:16 +0100 Subject: [PATCH 090/165] Update vignettes/decorate-module-output.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index c21f84f720..1bbb4dc721 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -177,7 +177,9 @@ failing_decorator <- teal_transform_module( ### Example Module To include decorators in a `teal` module, pass them as arguments (`ui_args` and `server_args`) to the module’s `ui` and -`server` components, where they will be used by `ui/srv_teal_transform_module`: +`server` components, where they will be used by `ui/srv_teal_transform_module`. + +Please find an example module for the sake of this article: ```{r} From 72e33f4ae4933b1e4aeb80864a752c3e064f3e33 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 09:49:02 +0100 Subject: [PATCH 091/165] remove DT from vignette/dependencies --- DESCRIPTION | 3 +- vignettes/decorate-module-output.Rmd | 128 --------------------------- 2 files changed, 1 insertion(+), 130 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b0440f0211..cd054ecb10 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -57,7 +57,6 @@ Imports: utils Suggests: bslib, - DT (>= 0.33), knitr (>= 1.42), mirai (>= 1.1.1), MultiAssayExperiment, @@ -83,7 +82,7 @@ Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, insightsengineering/teal.reporter, insightsengineering/teal.widgets, rstudio/bslib, yihui/knitr, bioc::MultiAssayExperiment, r-lib/R6, rstudio/rmarkdown, tidyverse/rvest, rstudio/shinytest2, - rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, rstudio/DT, + rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, yaml=vubiostat/r-yaml Config/Needs/website: insightsengineering/nesttemplate Encoding: UTF-8 diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 1bbb4dc721..670e3cd849 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -265,134 +265,6 @@ if (interactive()) { } ``` -## Decorating Tables - -### Example Module - -The next example contains a decorator that formats the background style of `DT::datable`, depending on the selected `Table Style` (`style`) input. - - -```{r, eval=requireNamespace("DT")} -library(DT) - -# Define a decorator module for customizing table appearance -custom_table_decorator <- teal_transform_module( - ui = function(id) { - ns <- NS(id) - div( - selectInput( - ns("style"), - "Table Style", - choices = c("Default", "Striped", "Hover"), - selected = "Default" - ) - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - within(data(), - { - if (style == "Striped") { - table <- - formatStyle( - table, - columns = attr(table$x, "colnames")[-1], - target = "row", - backgroundColor = "#f9f9f9" - ) - } else if (style == "Hover") { - table <- - formatStyle( - table, - columns = attr(table$x, "colnames")[-1], - target = "row", - backgroundColor = "#f0f0f0" - ) - } - }, - style = input$style - ) - }) - }) - } -) -``` - - - -```{r, eval=requireNamespace("DT")} -# Main module to display the table with the decorator applied -tm_custom_table <- function(label = "Customized Table Module", decorators = teal_transform_module()) { - module( - label = label, - ui = function(id, decorators) { - ns <- NS(id) - div( - selectInput(ns("dataset"), "Select Dataset", choices = NULL), - ui_teal_transform_data(ns("decorate"), transformators = decorators), - DT::dataTableOutput(ns("table_output")), - verbatimTextOutput(ns("code_output")) - ) - }, - server = function(id, data, decorators) { - moduleServer(id, function(input, output, session) { - observeEvent(data(), { - updateSelectInput(inputId = "dataset", choices = names(data())) - }) - - base_table <- reactive({ - req(input$dataset) - within(data(), - { - table <- - DT::datatable( - dataset, - options = list( - dom = "t", - autoWidth = TRUE - ) - ) - }, - dataset = as.name(input$dataset) - ) - }) - - decorated_table <- - srv_teal_transform_data("decorate", data = base_table, transformators = decorators) - - output$table_output <- DT::renderDT({ - req(decorated_table()) - decorated_table()[["table"]] - }) - - output$code_output <- renderText({ - teal.code::get_code(req(decorated_table())) - }) - }) - }, - ui_args = list(decorators = decorators), - server_args = list(decorators = decorators) - ) -} -``` - -### Application - -```{r, eval=requireNamespace("DT")} -app <- init( - data = teal_data(mtcars = mtcars, iris = iris), - modules = modules( - tm_custom_table("custom_table", decorators = custom_table_decorator) - ) -) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -``` - ## Multiple Decorators ### Example Module From 6e9ae0c20ac15b95d3d06e98e85b7e7e0c9fef0e Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 09:58:45 +0100 Subject: [PATCH 092/165] change expression to language - as that's how it is documented in the vignette --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 670e3cd849..4407c4fbb5 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -51,7 +51,7 @@ static_decorator <- teal_transform_module( To simplify the repetitive elements of writing new decorators (e.g., `function(id, data), moduleServer, reactive, within(data, ...)`), -you can use the `make_teal_transform_server(expr)` wrapper, which takes an `expression` as input: +you can use the `make_teal_transform_server(expr)` wrapper, which takes a `language` as input: ```{r} static_decorator_lang <- teal_transform_module( From 0f51ce57af086efc4e52b2bc2fd4e2b46f596399 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 09:59:25 +0100 Subject: [PATCH 093/165] wrapper -> convenience function --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 4407c4fbb5..b53f271d2e 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -51,7 +51,7 @@ static_decorator <- teal_transform_module( To simplify the repetitive elements of writing new decorators (e.g., `function(id, data), moduleServer, reactive, within(data, ...)`), -you can use the `make_teal_transform_server(expr)` wrapper, which takes a `language` as input: +you can use the `make_teal_transform_server(expr)` convenience function, which takes a `language` as input: ```{r} static_decorator_lang <- teal_transform_module( From 5d09fcf6c4e028cd66b1466dcdd906eec502f135 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 10:05:34 +0100 Subject: [PATCH 094/165] adjust intro --- vignettes/decorate-module-output.Rmd | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index b53f271d2e..70801b25e9 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -17,12 +17,16 @@ This document outlines the customization options available for modifying the out You will learn how to use `teal_transform_module` to modify and enhance the objects created by `teal` modules, enabling you to tailor the outputs to your specific requirements without rewriting the original module code. -Adjusting input data or customizing module outputs in `teal` is accomplished using `transformators` created through -`teal_transform_module`. This document will focus on modifying outputs, and for clarity, we will refer to these -output-modifying objects as decoration objects or decorators. +## Decorators +One of ways of adjusting input data or customizing module outputs in `teal` is the usage of `transformators` +created through `teal_transform_module`. This document will focus on modifying outputs, and for clarity, we will refer +to these output-modifying objects as decoration objects or decorators. -## Decorators +In below chapter we will present how to create the simplest static decorator with just a server part. Later, we will +present examples on more advanced usage, where decorators contain UI. You will also learn about a convenience +function that makes it easier to write decorators, called `make_teal_transform_server`. The chapter ends with an +example module that utilizes decorators and a snippet that uses this module in `teal` application. ### Server From afbe5beb792514387384769a148acf626055e265 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 10:41:01 +0100 Subject: [PATCH 095/165] shiny::testServer tests for srv_teal_transform --- tests/testthat/test-module_teal.R | 59 +++++++++++++++++++------------ 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 283da5371c..bda252a9fb 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1948,34 +1948,39 @@ testthat::describe("srv_teal teal_module(s) transformator", { }) testthat::it("changes module output for a module with a static decorator", { - testthat::skip("TODO") + label <- "output_decorator" output_decorator <- teal_transform_module( + label = label, server = make_teal_transform_server( expression( - object <- rev(object) + data1 <- rev(data1) ) ) ) shiny::testServer( - app = srv_teal, + app = srv_teal_transform_data, args = list( id = "test", - data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(example_module("module 1", decorators = output_decorator)) + data = reactive(teal.data::teal_data(data1 = iris, data2 = mtcars)), + transformators = output_decorator ), expr = { - # session$setInputs(`teal_modules-active_tab` = "module 1") - # testthat::expect_identical(modules_output$module_1()()[["object"]], TODO) + data_out <- transformators[[label]]$server(label, data = data) + testthat::expect_identical( + data_out()[['data1']], + rev(iris) + ) } ) }) - testthat::it("changes module output for a module with a decorator that is a function of object name", { - testthat::skip("TODO") - output_decorator <- function(output_name) { + testthat::it("changes module output for a module with a decorator that is a function of an object name", { + label <- "output_decorator_name" + output_decorator_name <- function(output_name, label) { teal_transform_module( + label = label, ui = function(id) { ns <- NS(id) div( @@ -2000,21 +2005,27 @@ testthat::describe("srv_teal teal_module(s) transformator", { } shiny::testServer( - app = srv_teal, + app = srv_teal_transform_data, args = list( id = "test", - data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(example_module("module 1", decorators = output_decorator[["object"]])) + data = reactive(teal.data::teal_data(x1 = "ABC")), + transformators = output_decorator_name(output_name = "x1", label = label) ), expr = { - # TODO + data_out <- transformators[[label]]$server(label, data = data) + testthat::expect_identical( + data_out()[['x1']], + paste0("ABC", "random text") # "random text" is not appended + ) } ) }) - testthat::it("changes module output for a module with a interactive decorator", { - testthat::skip("TODO") - output_decorator <- teal_transform_module( + testthat::it("changes module output for a module with an interactive decorator", { + + label = "output_decorator_int" + output_decorator_int <- teal_transform_module( + label = label, ui = function(id) { ns <- NS(id) div( @@ -2027,7 +2038,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { req(data()) within(data(), { - object <- paste0(object, append_text) + x1 <- paste0(x1, append_text) }, append_text = input$append_text ) @@ -2037,14 +2048,18 @@ testthat::describe("srv_teal teal_module(s) transformator", { ) shiny::testServer( - app = srv_teal, + app = srv_teal_transform_data, args = list( id = "test", - data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules(example_module("module 1", decorators = output_decorator)) + data = reactive(teal.data::teal_data(x1 = "ABC")), + transformators = output_decorator_int ), expr = { - # TODO + data_out <- transformators[[label]]$server(label, data = data) + testthat::expect_identical( + data_out()[['x1']], + paste0("ABC", "random text") # "random text" is not appended + ) } ) }) From 4ba6320e487df5d3e573c447d262f33c96fcd8f1 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:43:30 +0000 Subject: [PATCH 096/165] [skip style] [skip vbump] Restyle files --- tests/testthat/test-module_teal.R | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index bda252a9fb..8224dcd735 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1968,7 +1968,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { expr = { data_out <- transformators[[label]]$server(label, data = data) testthat::expect_identical( - data_out()[['data1']], + data_out()[["data1"]], rev(iris) ) } @@ -2014,7 +2014,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { expr = { data_out <- transformators[[label]]$server(label, data = data) testthat::expect_identical( - data_out()[['x1']], + data_out()[["x1"]], paste0("ABC", "random text") # "random text" is not appended ) } @@ -2022,8 +2022,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { }) testthat::it("changes module output for a module with an interactive decorator", { - - label = "output_decorator_int" + label <- "output_decorator_int" output_decorator_int <- teal_transform_module( label = label, ui = function(id) { @@ -2057,7 +2056,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { expr = { data_out <- transformators[[label]]$server(label, data = data) testthat::expect_identical( - data_out()[['x1']], + data_out()[["x1"]], paste0("ABC", "random text") # "random text" is not appended ) } From 2dae2b65460d1bfcb347192df1cca3fdcf9a8685 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 10:47:14 +0100 Subject: [PATCH 097/165] add pkgdown links to 1st appearance of the function in the decorators vignette --- vignettes/decorate-module-output.Rmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 70801b25e9..9a652a78b6 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -14,7 +14,7 @@ vignette: > `teal` is a powerful `shiny`-based framework with built-in modules for interactive data analysis. This document outlines the customization options available for modifying the output of `teal` modules. -You will learn how to use `teal_transform_module` to modify and enhance the objects created by `teal` modules, +You will learn how to use [`teal_transform_module`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) to modify and enhance the objects created by `teal` modules, enabling you to tailor the outputs to your specific requirements without rewriting the original module code. ## Decorators @@ -25,7 +25,7 @@ to these output-modifying objects as decoration objects or decorators. In below chapter we will present how to create the simplest static decorator with just a server part. Later, we will present examples on more advanced usage, where decorators contain UI. You will also learn about a convenience -function that makes it easier to write decorators, called `make_teal_transform_server`. The chapter ends with an +function that makes it easier to write decorators, called [`make_teal_transform_server`](https://insightsengineering.github.io/teal/latest-tagx/reference/make_teal_transform_server.html). The chapter ends with an example module that utilizes decorators and a snippet that uses this module in `teal` application. ### Server @@ -73,7 +73,7 @@ static_decorator_lang <- teal_transform_module( To create a decorator with user interactivity, you can incorporate a `shiny` UI module. In the example below, the x-axis title is set dynamically via a `textInput`, allowing users to specify their preferred label. -Note how the input parameters are passed to the `within` function using its `...` argument. +Note how the input parameters are passed to the [`within`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_data_module.html) function using its `...` argument. ```{r} interactive_decorator <- teal_transform_module( From 60eec6e8fc66db73d4d4a56d4979d2dfee59580f Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 10:56:01 +0100 Subject: [PATCH 098/165] be direct about which modules we talk --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 9a652a78b6..ceebf34e36 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -14,7 +14,7 @@ vignette: > `teal` is a powerful `shiny`-based framework with built-in modules for interactive data analysis. This document outlines the customization options available for modifying the output of `teal` modules. -You will learn how to use [`teal_transform_module`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) to modify and enhance the objects created by `teal` modules, +You will learn how to use [`teal_transform_module`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) to modify and enhance the objects created by `teal::modules()`, enabling you to tailor the outputs to your specific requirements without rewriting the original module code. ## Decorators From 5007a82060182f801dd9f870f8046c2d5cf55fb3 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:30:02 +0100 Subject: [PATCH 099/165] Update R/dummy_functions.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/dummy_functions.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 1dd5c1ad5f..2ac0e986e4 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -46,6 +46,7 @@ example_module <- function(label = "example teal module", }) table_data <- reactive({ + req(input$dataname) within(data(), { object <- dataname From d395ac6e3ec6338f78630303c149a312a0c2b8bf Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:31:16 +0100 Subject: [PATCH 100/165] Update R/dummy_functions.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/dummy_functions.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 2ac0e986e4..d83a53ea06 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -58,7 +58,7 @@ example_module <- function(label = "example teal module", table_data_decorated <- srv_teal_transform_data("decorate", data = table_data, transformators = decorators) output$text <- renderPrint({ - req(table_data_decorated) + req(table_data_decorated()) table_data_decorated()[["object"]] }) From d7c1f7e4c73f2ac501ff24c0f6bd7cc9acdd0904 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:35:05 +0100 Subject: [PATCH 101/165] Update R/modules.R Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/modules.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/modules.R b/R/modules.R index 207384b8d9..806938531f 100644 --- a/R/modules.R +++ b/R/modules.R @@ -417,7 +417,7 @@ format.teal_module <- function( if ("transformators" %in% what) { output <- paste0( output, - content_prefix, "L- ", crayon::magenta("transformators : "), transformators, "\n" + content_prefix, "L- ", crayon::magenta("Transformators : "), transformators, "\n" ) } From 29ea4f17cc7008c4b9731fd6889b2fd8847cc067 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 14:37:56 +0100 Subject: [PATCH 102/165] change order of setnences in transofrmator vignette --- vignettes/data-transform-as-shiny-module.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index f4b1f736fc..beaed9fc21 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -12,14 +12,14 @@ vignette: > ## Introduction +`teal_transform_module()` is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformator inputs. + `teal` version `0.16` introduced new argument in `teal::module` called `transformators`. This argument allows to pass a `list` of `teal_data_module` objects that are created using `teal_transform_module()` function. The main benefit of `teal_transform_module()` is the ability to transform data before passing it to the module. This feature allows to extend the regular behavior of existing modules by specifying custom data operations on data inside this module. -`teal_transform_module()` is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformator inputs. - This vignette presents the way on how to manage custom data transformations in `teal` apps. ## Creating your first custom data transformation module From 9de76b90a78ed4e60f2f5549fa95b165b87eeeb8 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:40:50 +0100 Subject: [PATCH 103/165] Update vignettes/decorate-module-output.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index ceebf34e36..0f34ee4305 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -101,7 +101,7 @@ interactive_decorator <- teal_transform_module( ) ``` -As in the earlier examples, `make_teal_transform_server(expr)` can simplify the creation of the server component. +As in the earlier examples, [`make_teal_transform_server()`](https://insightsengineering.github.io/teal/latest-tag/reference/make_teal_transform_server.html) can simplify the creation of the server component. This wrapper allows you to use `input` object names directly in the expression: ```{r} From f90bb779c16bdc2307b4419f3eafa186c404bade Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:41:00 +0100 Subject: [PATCH 104/165] Update vignettes/decorate-module-output.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 0f34ee4305..4275cf2c15 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -55,7 +55,7 @@ static_decorator <- teal_transform_module( To simplify the repetitive elements of writing new decorators (e.g., `function(id, data), moduleServer, reactive, within(data, ...)`), -you can use the `make_teal_transform_server(expr)` convenience function, which takes a `language` as input: +you can use the [`make_teal_transform_server()`](https://insightsengineering.github.io/teal/latest-tag/reference/make_teal_transform_server.html) convenience function, which takes a `language` as input: ```{r} static_decorator_lang <- teal_transform_module( From 75b41b926983b227c79d14b0a92ce93af41b0425 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:41:07 +0100 Subject: [PATCH 105/165] Update vignettes/decorate-module-output.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 4275cf2c15..2114e0a865 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -30,7 +30,7 @@ example module that utilizes decorators and a snippet that uses this module in ` ### Server -The simplest way to create a decorator is to use `teal_transform_module` without specifying a `ui`. +The simplest way to create a decorator is to use `teal_transform_module` with just `server` argument provided (i.e. without UI part). This approach adds functionality solely to the server code of the module. In the following example, we assume that the module contains an object (of class `ggplot2`) named `plot`. We modify the title and x-axis label of plot: From 1bdd1592c7b991e1c8bdfb4437a1901e82f7bd21 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 14:42:04 +0100 Subject: [PATCH 106/165] link to teal_transform_module in transform vignettes --- vignettes/data-transform-as-shiny-module.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index beaed9fc21..6e5b0f55ec 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -12,7 +12,7 @@ vignette: > ## Introduction -`teal_transform_module()` is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformator inputs. +[`teal_transform_module()`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformator inputs. `teal` version `0.16` introduced new argument in `teal::module` called `transformators`. This argument allows to pass a `list` of `teal_data_module` objects that are created using `teal_transform_module()` function. From fd78283540b70ef346f02e3f95454381f9e8fae3 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:43:46 +0100 Subject: [PATCH 107/165] Update vignettes/data-transform-as-shiny-module.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/data-transform-as-shiny-module.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index 6e5b0f55ec..8aa26f2650 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -15,7 +15,7 @@ vignette: > [`teal_transform_module()`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformator inputs. `teal` version `0.16` introduced new argument in `teal::module` called `transformators`. -This argument allows to pass a `list` of `teal_data_module` objects that are created using `teal_transform_module()` function. +This argument allows to pass a `list` of `"teal_data_module"` class of objects created using [`teal_transform_module()`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) function. The main benefit of `teal_transform_module()` is the ability to transform data before passing it to the module. This feature allows to extend the regular behavior of existing modules by specifying custom data operations on data inside this module. From 9e0018388416cbe9960168d637fdb11f771253f3 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:43:55 +0100 Subject: [PATCH 108/165] Update vignettes/data-transform-as-shiny-module.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/data-transform-as-shiny-module.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index 8aa26f2650..da6764dfdc 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -14,7 +14,7 @@ vignette: > [`teal_transform_module()`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformator inputs. -`teal` version `0.16` introduced new argument in `teal::module` called `transformators`. +`teal` version `0.16` introduced a new, optional argument in `teal::module` named `transformators`. This argument allows to pass a `list` of `"teal_data_module"` class of objects created using [`teal_transform_module()`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) function. The main benefit of `teal_transform_module()` is the ability to transform data before passing it From f329c0eab09a58ad07e9dcf5205377980eeb8019 Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 18 Nov 2024 14:47:52 +0100 Subject: [PATCH 109/165] write about what is a decorator in general --- vignettes/decorate-module-output.Rmd | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 2114e0a865..8bc09e0cd7 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -19,6 +19,21 @@ enabling you to tailor the outputs to your specific requirements without rewriti ## Decorators +In programming, **decoration** refers to the process of modifying an object while preserving its original class. For instance, given an object `x` of class `"my_class"`, a function `foo(x)` is considered a **decorator function** if it modifies `x` and returns an object that retains the same class. In this context, `x` is referred to as the **decorated object**, and `foo()` is the **decorator function**. Decorators can perform a variety of operations, such as adding new methods or modifying data, while ensuring the object remains compatible with its original usage. + +In the context of `teal` applications, decoration is specifically used to modify module outputs, such as plots or tables. For example, consider a decorator function `add_title(x, )` that takes a `ggplot2` plot object (`x`) as input, applies a title modification, and returns a modified `ggplot2` plot object. This function qualifies as a decorator because it preserves the original class of the input object. Conversely, a function like `create_plot(, , )`, which generates a new plot object, is **not** a decorator, as it produces an output of a different class. + +Preserving the object's class during decoration is essential for compatibility. It ensures that the subsequent "display" logic can seamlessly handle both decorated and undecorated objects. + +The decoration process can vary in complexity: +- **Simple Decorations**: Single-step modifications, such as a single method call that does not require additional data. +- **Complex Decorations**: Multi-step operations that may involve interdependent transformations, potentially requiring input from dedicated `shiny` UI elements. + +This powerful functionality empowers application developers to significantly customize outputs beyond the default capabilities provided by existing module parameters. Decorations allow for advanced modifications, enabling highly tailored and dynamic user experiences in `teal` applications. + + +## Decorators in `teal` + One of ways of adjusting input data or customizing module outputs in `teal` is the usage of `transformators` created through `teal_transform_module`. This document will focus on modifying outputs, and for clarity, we will refer to these output-modifying objects as decoration objects or decorators. From 585a3ba3495cf8f3da422c239cbb154551b2ad6e Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:22:17 +0100 Subject: [PATCH 110/165] Update vignettes/decorate-module-output.Rmd Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 8bc09e0cd7..4b6537b15d 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -86,7 +86,7 @@ static_decorator_lang <- teal_transform_module( ### UI -To create a decorator with user interactivity, you can incorporate a `shiny` UI module. +To create a decorator with user interactivity, you can add (optional) UI part and use it in server accordingly (i.e. a typical `shiny` module). In the example below, the x-axis title is set dynamically via a `textInput`, allowing users to specify their preferred label. Note how the input parameters are passed to the [`within`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_data_module.html) function using its `...` argument. From 33b308f3f70d31f2a0f88f94a79914f2193eba4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:24:54 +0000 Subject: [PATCH 111/165] fix: tests --- R/dummy_functions.R | 2 + R/module_data_summary.R | 4 +- R/module_transform_data.R | 2 +- R/teal_transform_module.R | 5 + tests/testthat/test-module_teal.R | 122 ++++++++++---------- tests/testthat/test-modules.R | 32 ++++- tests/testthat/test-teal_transform_module.R | 47 ++++++++ 7 files changed, 144 insertions(+), 70 deletions(-) create mode 100644 tests/testthat/test-teal_transform_module.R diff --git a/R/dummy_functions.R b/R/dummy_functions.R index d83a53ea06..e8817c7f86 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -67,6 +67,8 @@ example_module <- function(label = "example teal module", verbatim_content = reactive(teal.code::get_code(req(table_data_decorated()))), title = "Example Code" ) + + table_data_decorated }) }, ui = function(id, decorators) { diff --git a/R/module_data_summary.R b/R/module_data_summary.R index 6fe96702b0..82fc0cc349 100644 --- a/R/module_data_summary.R +++ b/R/module_data_summary.R @@ -148,10 +148,10 @@ get_filter_overview_wrapper <- function(teal_data) { current_data_objs <- sapply( datanames, - function(name) teal.code::get_var(teal_data(), name), + function(name) teal_data()[[name]], simplify = FALSE ) - initial_data_objs <- teal.code::get_var(teal_data(), ".raw_data") + initial_data_objs <- teal_data()[[".raw_data"]] out <- lapply( datanames, diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 61b06f6af6..70484c2e40 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -92,7 +92,7 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is moduleServer(name, function(input, output, session) { logger::log_debug("srv_teal_transform_data initializing for { name }.") is_transform_failed[[name]] <- FALSE - data_out <- transformators[[name]]$server(name, data = data_previous) + data_out <- transformators[[name]]$server("transform", data = data_previous) data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) observeEvent(data_handled(), { if (inherits(data_handled(), "teal_data")) { diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 3a434baf50..a63b7b7f87 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -188,6 +188,11 @@ teal_transform_module <- function(ui = NULL, #' #' @export make_teal_transform_server <- function(expr) { + if (is.call(expr)) { + expr <- as.expression(expr) + } + checkmate::assert_multi_class(expr, c("call", "expression")) + function(id, data) { moduleServer(id, function(input, output, session) { reactive({ diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 8224dcd735..491404f9b3 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1948,27 +1948,24 @@ testthat::describe("srv_teal teal_module(s) transformator", { }) testthat::it("changes module output for a module with a static decorator", { - label <- "output_decorator" output_decorator <- teal_transform_module( - label = label, - server = make_teal_transform_server( - expression( - data1 <- rev(data1) - ) - ) + label = "output_decorator", + server = make_teal_transform_server(expression(object <- rev(object))) ) shiny::testServer( - app = srv_teal_transform_data, + app = srv_teal, args = list( id = "test", - data = reactive(teal.data::teal_data(data1 = iris, data2 = mtcars)), - transformators = output_decorator + data = teal.data::teal_data(object = iris), + modules = modules(example_module("mod1", decorators = output_decorator)) ), expr = { - data_out <- transformators[[label]]$server(label, data = data) + session$setInputs(`teal_modules-active_tab` = "mod1") + session$setInputs(`teal_modules-mod1-module-dataname` = "object") + session$flushReact() testthat::expect_identical( - data_out()[["data1"]], + modules_output$mod1()()[["object"]], rev(iris) ) } @@ -1977,25 +1974,16 @@ testthat::describe("srv_teal teal_module(s) transformator", { testthat::it("changes module output for a module with a decorator that is a function of an object name", { - label <- "output_decorator_name" - output_decorator_name <- function(output_name, label) { + decorator_name <- function(output_name, label) { teal_transform_module( label = label, - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("append_text"), "Append text", value = "random text") - ) - }, server = function(id, data) { moduleServer(id, function(input, output, session) { reactive({ - req(data()) - within(data(), - { - output_name <- paste0(output_name, append_text) - }, - append_text = input$append_text, + within( + data(), + output_name <- paste0(output_name, " lorem ipsum"), + text = input$text, output_name = as.name(output_name) ) }) @@ -2005,60 +1993,66 @@ testthat::describe("srv_teal teal_module(s) transformator", { } shiny::testServer( - app = srv_teal_transform_data, + app = srv_teal, args = list( id = "test", - data = reactive(teal.data::teal_data(x1 = "ABC")), - transformators = output_decorator_name(output_name = "x1", label = label) + data = teal.data::teal_data(x1 = "ABC"), + modules = modules( + example_module( + "mod1", + decorators = decorator_name(output_name = "object", label = "decorator_name") + ) + ) ), expr = { - data_out <- transformators[[label]]$server(label, data = data) - testthat::expect_identical( - data_out()[["x1"]], - paste0("ABC", "random text") # "random text" is not appended - ) + session$setInputs(`teal_modules-active_tab` = "mod1") + session$setInputs(`teal_modules-mod1-module-dataname` = "x1") + session$flushReact() + + testthat::expect_identical(modules_output$mod1()()[["object"]], "ABC lorem ipsum") } ) }) testthat::it("changes module output for a module with an interactive decorator", { - label <- "output_decorator_int" - output_decorator_int <- teal_transform_module( - label = label, - ui = function(id) { - ns <- NS(id) - div( - textInput(ns("append_text"), "Append text", value = "random text") - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - req(data()) - within(data(), - { - x1 <- paste0(x1, append_text) - }, - append_text = input$append_text - ) + decorator_name <- function(output_name, label) { + teal_transform_module( + label = label, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data(), input$text) + within( + data(), + output_name <- paste0(output_name, " ", text), + text = input$text, + output_name = as.name(output_name) + ) + }) }) - }) - } - ) + } + ) + } shiny::testServer( - app = srv_teal_transform_data, + app = srv_teal, args = list( id = "test", - data = reactive(teal.data::teal_data(x1 = "ABC")), - transformators = output_decorator_int + data = teal.data::teal_data(x1 = "ABC"), + modules = modules( + example_module( + "mod1", + decorators = decorator_name(output_name = "object", label = "decorator_name") + ) + ) ), expr = { - data_out <- transformators[[label]]$server(label, data = data) - testthat::expect_identical( - data_out()[["x1"]], - paste0("ABC", "random text") # "random text" is not appended - ) + session$setInputs(`teal_modules-active_tab` = "mod1") + session$setInputs(`teal_modules-mod1-module-dataname` = "x1") + session$setInputs(`teal_modules-mod1-module-decorate-decorator_name-transform-text` = "lorem ipsum dolor") + session$flushReact() + + testthat::expect_identical(modules_output$mod1()()[["object"]], "ABC lorem ipsum dolor") } ) }) diff --git a/tests/testthat/test-modules.R b/tests/testthat/test-modules.R index 0a51e483af..5a1da7b37f 100644 --- a/tests/testthat/test-modules.R +++ b/tests/testthat/test-modules.R @@ -509,9 +509,35 @@ testthat::test_that("format.teal_modules returns proper structure", { appended_mods <- append_module(mods, mod3) - testthat::expect_equal( - gsub("\033\\[[0-9;]*m", "", format(appended_mods)), - "TEAL ROOT\n |- a\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transformators : \n |- c\n | |- Datasets : all\n | |- Properties:\n | | |- Bookmarkable : FALSE\n | | L- Reportable : FALSE\n | |- UI Arguments : \n | |- Server Arguments : \n | L- transformators : \n L- c\n |- Datasets : all\n |- Properties:\n | |- Bookmarkable : FALSE\n | L- Reportable : FALSE\n |- UI Arguments : \n |- Server Arguments : \n L- transformators : \n" # nolint: line_length + testthat::expect_setequal( + strsplit(gsub("\033\\[[0-9;]*m", "", format(appended_mods)), "\n")[[1]], + c( + "TEAL ROOT", + " |- a", + " | |- Datasets : all", + " | |- Properties:", + " | | |- Bookmarkable : FALSE", + " | | L- Reportable : FALSE", + " | |- UI Arguments : ", + " | |- Server Arguments : ", + " | L- Transformators : ", + " |- c", + " | |- Datasets : all", + " | |- Properties:", + " | | |- Bookmarkable : FALSE", + " | | L- Reportable : FALSE", + " | |- UI Arguments : ", + " | |- Server Arguments : ", + " | L- Transformators : ", + " L- c", + " |- Datasets : all", + " |- Properties:", + " | |- Bookmarkable : FALSE", + " | L- Reportable : FALSE", + " |- UI Arguments : ", + " |- Server Arguments : ", + " L- Transformators : " + ) ) }) diff --git a/tests/testthat/test-teal_transform_module.R b/tests/testthat/test-teal_transform_module.R new file mode 100644 index 0000000000..408133155e --- /dev/null +++ b/tests/testthat/test-teal_transform_module.R @@ -0,0 +1,47 @@ +testthat::describe("make_teal_transform_server produces a valid teal_transform_module", { + testthat::it("expression", { + label <- "output_decorator" + output_decorator <- teal_transform_module( + label = label, + server = make_teal_transform_server( + expression(data1 <- rev(data1)) + ) + ) + + shiny::testServer( + app = srv_teal_transform_data, + args = list( + id = "test", + data = reactive(teal.data::teal_data(data1 = iris, data2 = mtcars)), + transformators = output_decorator + ), + expr = { + data_out <- transformators[[label]]$server(label, data = data) + testthat::expect_identical(data_out()[["data1"]], rev(iris)) + } + ) + }) + + testthat::it("quote", { + label <- "output_decorator" + output_decorator <- teal_transform_module( + label = label, + server = make_teal_transform_server( + quote(data1 <- rev(data1)) + ) + ) + + shiny::testServer( + app = srv_teal_transform_data, + args = list( + id = "test", + data = reactive(teal.data::teal_data(data1 = iris, data2 = mtcars)), + transformators = output_decorator + ), + expr = { + data_out <- transformators[[label]]$server(label, data = data) + testthat::expect_identical(data_out()[["data1"]], rev(iris)) + } + ) + }) +}) From 44e4bc37ac93a5c4172964ff0ee0d6bbdb06a9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:40:37 +0000 Subject: [PATCH 112/165] fix: long test name --- tests/testthat/test-shinytest2-decorators.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R index 192ccad59c..4e7b4e678f 100644 --- a/tests/testthat/test-shinytest2-decorators.R +++ b/tests/testthat/test-shinytest2-decorators.R @@ -1,7 +1,7 @@ testthat::skip_if_not_installed("shinytest2") testthat::skip_if_not_installed("rvest") -testthat::test_that("e2e: module with decorator shows decorator's UI and module output is modified interactively upon changes in decorate module", { +testthat::test_that("e2e: module with decorator UI and output is modified interactively upon changes in decorate module", { skip_if_too_deep(5) interactive_decorator <- teal_transform_module( From 134dcf4cdb04a1e8bd6ed8e671aefdc9afef0226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:47:29 +0000 Subject: [PATCH 113/165] fix: long test name --- tests/testthat/test-shinytest2-decorators.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R index 4e7b4e678f..24a9d4350c 100644 --- a/tests/testthat/test-shinytest2-decorators.R +++ b/tests/testthat/test-shinytest2-decorators.R @@ -1,7 +1,7 @@ testthat::skip_if_not_installed("shinytest2") testthat::skip_if_not_installed("rvest") -testthat::test_that("e2e: module with decorator UI and output is modified interactively upon changes in decorate module", { +testthat::test_that("e2e: module with decorator UI and output is modified interactively upon changes in decorator", { skip_if_too_deep(5) interactive_decorator <- teal_transform_module( From 21d0e365d3b777826363f3ddc0f631d1ab728461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:42:11 +0000 Subject: [PATCH 114/165] fix: namespace in tests --- tests/testthat/test-module_teal.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 491404f9b3..9d6f2ce09a 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -573,7 +573,7 @@ testthat::describe("srv_teal teal_modules", { trimws( rvest::html_text2( rvest::read_html( - output[["teal_modules-module_1-validate_datanames-shiny_warnings-message"]]$html + output[["teal_modules-module_1-validate_datanames-message"]]$html ) ) ), @@ -602,11 +602,13 @@ testthat::describe("srv_teal teal_modules", { ), expr = { session$setInputs("teal_modules-active_tab" = "module_1") + session$flushReact() + testthat::expect_equal( trimws( rvest::html_text2( rvest::read_html( - output[["teal_modules-module_1-validate_datanames-shiny_warnings-message"]]$html + output[["teal_modules-module_1-validate_datanames-message"]]$html ) ) ), From b91b8c0a54a932c3563b69127546cb9676323c19 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:19:40 +0100 Subject: [PATCH 115/165] Apply suggestions from code review Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 4b6537b15d..cfbe34e5ec 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -19,7 +19,7 @@ enabling you to tailor the outputs to your specific requirements without rewriti ## Decorators -In programming, **decoration** refers to the process of modifying an object while preserving its original class. For instance, given an object `x` of class `"my_class"`, a function `foo(x)` is considered a **decorator function** if it modifies `x` and returns an object that retains the same class. In this context, `x` is referred to as the **decorated object**, and `foo()` is the **decorator function**. Decorators can perform a variety of operations, such as adding new methods or modifying data, while ensuring the object remains compatible with its original usage. +In programming, **decoration** refers to the process of modifying an object while preserving its original class. For instance, given an object `x` of class `"my_class"`, a function `foo(x)` is considered a **decorator function** if it modifies `x` and returns an object that retains the same class. In this context, `x` is referred to as the **decorated object**, and `foo()` is the **decorator function** or **decorator**. Decorators can perform a variety of operations, such as adding new methods or modifying data, while ensuring the object remains compatible with its original usage. In the context of `teal` applications, decoration is specifically used to modify module outputs, such as plots or tables. For example, consider a decorator function `add_title(x, )` that takes a `ggplot2` plot object (`x`) as input, applies a title modification, and returns a modified `ggplot2` plot object. This function qualifies as a decorator because it preserves the original class of the input object. Conversely, a function like `create_plot(, , )`, which generates a new plot object, is **not** a decorator, as it produces an output of a different class. @@ -35,8 +35,7 @@ This powerful functionality empowers application developers to significantly cus ## Decorators in `teal` One of ways of adjusting input data or customizing module outputs in `teal` is the usage of `transformators` -created through `teal_transform_module`. This document will focus on modifying outputs, and for clarity, we will refer -to these output-modifying objects as decoration objects or decorators. +created through `teal_transform_module`. In below chapter we will present how to create the simplest static decorator with just a server part. Later, we will present examples on more advanced usage, where decorators contain UI. You will also learn about a convenience @@ -45,7 +44,7 @@ example module that utilizes decorators and a snippet that uses this module in ` ### Server -The simplest way to create a decorator is to use `teal_transform_module` with just `server` argument provided (i.e. without UI part). +The simplest way to create a decorator is to use [`teal_transform_module`](https://insightsengineering.github.io/teal/latest-tag/reference/teal_transform_module.html) with only `server` argument provided (i.e. without UI part). This approach adds functionality solely to the server code of the module. In the following example, we assume that the module contains an object (of class `ggplot2`) named `plot`. We modify the title and x-axis label of plot: @@ -106,9 +105,9 @@ interactive_decorator <- teal_transform_module( { plot <- plot + ggtitle("This is title") + - xlab(x_axis_title) + xlab(my_title) }, - x_axis_title = input$x_axis_title + my_title = input$x_axis_title ) }) }) @@ -117,7 +116,7 @@ interactive_decorator <- teal_transform_module( ``` As in the earlier examples, [`make_teal_transform_server()`](https://insightsengineering.github.io/teal/latest-tag/reference/make_teal_transform_server.html) can simplify the creation of the server component. -This wrapper allows you to use `input` object names directly in the expression: +This wrapper requires you to use `input` object names directly in the expression - note that we have `xlab(x_axis_table)` and not `my_title = input$x_axis_title` together with `xlab(my_title)`. ```{r} interactive_decorator_lang <- teal_transform_module( From cbc401e6787ec697e8d48fd401f67d2d3978fb56 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 19 Nov 2024 11:04:15 +0100 Subject: [PATCH 116/165] this req is not needed --- R/dummy_functions.R | 1 - vignettes/decorate-module-output.Rmd | 3 --- 2 files changed, 4 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index e8817c7f86..99ff2ff2ac 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -58,7 +58,6 @@ example_module <- function(label = "example teal module", table_data_decorated <- srv_teal_transform_data("decorate", data = table_data, transformators = decorators) output$text <- renderPrint({ - req(table_data_decorated()) table_data_decorated()[["object"]] }) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index cfbe34e5ec..afa03d3f14 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -245,7 +245,6 @@ tm_decorated_plot <- function(label = "module", transformators = list(), decorat plot_data_decorated <- srv_teal_transform_data("decorate", data = plot_data, transformators = decorators) plot_r <- reactive({ - req(plot_data_decorated()) plot_data_decorated()[["plot"]] }) @@ -385,10 +384,8 @@ tm_decorated_plot <- function( plot_r <- reactive({ req(input$plot_type) if (input$plot_type == "plot 1") { - req(plot_1_data_decorated()) plot_1_data_decorated()[["plot_1"]] } else if (input$plot_type == "plot 2") { - req(plot_2_data_decorated()) plot_2_data_decorated()[["plot_2"]] } }) From 8a34f0f30017db059e2c62d883683ca54b8c93ef Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 19 Nov 2024 14:30:33 +0100 Subject: [PATCH 117/165] apply @pawelru feedback --- vignettes/decorate-module-output.Rmd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index afa03d3f14..667c8a994a 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -31,6 +31,14 @@ The decoration process can vary in complexity: This powerful functionality empowers application developers to significantly customize outputs beyond the default capabilities provided by existing module parameters. Decorations allow for advanced modifications, enabling highly tailored and dynamic user experiences in `teal` applications. +## Requirements and Limitations + +To use decorators effectively, certain requirements must be met: + +1. **Module Support**: While `teal` provides the core functionality for decorators, the module must explicitly support this functionality. Developers should ensure that the module has been designed to work with decorators. +2. **Matching Object Names**: Decorators must reference object names that align with the internal naming conventions of the module. Each module may use different names for its output objects, such as `plot` or `table`. This alignment is critical for successful decoration. + +It is recommended to review the module documentation or source code to understand its internal object naming before applying decorators. ## Decorators in `teal` From 2c0dbf0b2271ca3548ff79d96339cccbf9088512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:37:11 +0000 Subject: [PATCH 118/165] fix: namespace change in server function applied to UI --- R/module_transform_data.R | 7 ++-- tests/testthat/test-teal_transform_module.R | 38 ++++++++++++++++++--- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 70484c2e40..30a81e69e3 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -30,7 +30,7 @@ ui_teal_transform_data <- function(id, transformators, class = "well") { lapply( names(transformators), function(name) { - child_id <- NS(id)(name) + child_id <- NS(id, name) ns <- NS(child_id) data_mod <- transformators[[name]] transform_wrapper_id <- ns(sprintf("wrapper_%s", name)) @@ -56,7 +56,7 @@ ui_teal_transform_data <- function(id, transformators, class = "well") { if (is.null(data_mod$ui)) { return(NULL) } else { - data_mod$ui(id = ns(name)) + data_mod$ui(id = ns("transform")) }, div( id = ns("validate_messages"), @@ -87,7 +87,7 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is names(transformators) <- ids moduleServer(id, function(input, output, session) { - Reduce( + module_output <- Reduce( function(data_previous, name) { moduleServer(name, function(input, output, session) { logger::log_debug("srv_teal_transform_data initializing for { name }.") @@ -136,5 +136,6 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is x = names(transformators), init = data ) + module_output }) } diff --git a/tests/testthat/test-teal_transform_module.R b/tests/testthat/test-teal_transform_module.R index 408133155e..6b600d1c45 100644 --- a/tests/testthat/test-teal_transform_module.R +++ b/tests/testthat/test-teal_transform_module.R @@ -16,8 +16,8 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m transformators = output_decorator ), expr = { - data_out <- transformators[[label]]$server(label, data = data) - testthat::expect_identical(data_out()[["data1"]], rev(iris)) + session$flushReact() + testthat::expect_identical(module_output()[["data1"]], rev(iris)) } ) }) @@ -39,9 +39,39 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m transformators = output_decorator ), expr = { - data_out <- transformators[[label]]$server(label, data = data) - testthat::expect_identical(data_out()[["data1"]], rev(iris)) + session$flushReact() + testthat::expect_identical(module_output()[["data1"]], rev(iris)) } ) }) }) + +testthat::test_that("same namespace is shared between tranformator UI and server", { + ttm <- teal_transform_module( + ui = function(id) tags$div(id = NS(id, "a_div"), "a div"), + server = function(id, data) { + moduleServer(id, function(input, output, session) { + full_id <- session$ns("a_div") + reactive(within(data(), id <- full_id, full_id = full_id)) + }) + } + ) + + initial_id <- "a-path-to-an-inner-namespace" + ui <- ui_teal_transform_data(initial_id, ttm) + # Find element that ends in "-a_div" + expected_id <- unname(unlist(ui)[grepl(".*-a_div$", unlist(ui))][1]) + + testServer( + app = srv_teal_transform_data, + args = list( + id = initial_id, + data = reactive(within(teal_data(), iris <- iris)), + transformators = ttm + ), + expr = { + session$flushReact() + testthat::expect_equal(module_output()$id, expected_id) + } + ) +}) From ad54746e9ec9f13595d4cbd55286299c9a8825e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:26:25 +0000 Subject: [PATCH 119/165] docs: test title --- tests/testthat/test-teal_transform_module.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-teal_transform_module.R b/tests/testthat/test-teal_transform_module.R index 6b600d1c45..76d6aceb44 100644 --- a/tests/testthat/test-teal_transform_module.R +++ b/tests/testthat/test-teal_transform_module.R @@ -46,7 +46,7 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m }) }) -testthat::test_that("same namespace is shared between tranformator UI and server", { +testthat::test_that("xxx_teal_transform_datasame the same namespace is shared between tranformator UI and server", { ttm <- teal_transform_module( ui = function(id) tags$div(id = NS(id, "a_div"), "a div"), server = function(id, data) { From 5b402e2682db43b8a254eb35939d67b8326f60f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:55:23 +0000 Subject: [PATCH 120/165] fix: reactivity issue --- R/teal_transform_module.R | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index a63b7b7f87..cd5390242d 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -195,12 +195,14 @@ make_teal_transform_server <- function(expr) { function(id, data) { moduleServer(id, function(input, output, session) { + list_env <- eventReactive( + input, + lapply(rlang::set_names(names(input)), function(x) input[[x]]) + ) + reactive({ call_with_inputs <- lapply(expr, function(x) { - do.call( - what = substitute, - args = list(expr = x, env = reactiveValuesToList(input)) - ) + do.call(what = substitute, args = list(expr = x, env = list_env())) }) eval_code(object = data(), code = as.expression(call_with_inputs)) }) From ffe1a19ffdc7012ff5ddcc745c2e9d3fb3edf594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:24:35 +0000 Subject: [PATCH 121/165] docs: fix spelling --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 667c8a994a..a683845725 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -23,7 +23,7 @@ In programming, **decoration** refers to the process of modifying an object whil In the context of `teal` applications, decoration is specifically used to modify module outputs, such as plots or tables. For example, consider a decorator function `add_title(x, )` that takes a `ggplot2` plot object (`x`) as input, applies a title modification, and returns a modified `ggplot2` plot object. This function qualifies as a decorator because it preserves the original class of the input object. Conversely, a function like `create_plot(, , )`, which generates a new plot object, is **not** a decorator, as it produces an output of a different class. -Preserving the object's class during decoration is essential for compatibility. It ensures that the subsequent "display" logic can seamlessly handle both decorated and undecorated objects. +Preserving the object's class during decoration is essential for compatibility. It ensures that the subsequent "display" logic can seamlessly handle both decorated and non-decorated objects. The decoration process can vary in complexity: - **Simple Decorations**: Single-step modifications, such as a single method call that does not require additional data. From 20a3962d2579cb5c1ef1e61f4fd68536ee3abd58 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:59:23 +0100 Subject: [PATCH 122/165] Update tests/testthat/test-teal_transform_module.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- tests/testthat/test-teal_transform_module.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-teal_transform_module.R b/tests/testthat/test-teal_transform_module.R index 76d6aceb44..8b5ec31d2a 100644 --- a/tests/testthat/test-teal_transform_module.R +++ b/tests/testthat/test-teal_transform_module.R @@ -46,7 +46,7 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m }) }) -testthat::test_that("xxx_teal_transform_datasame the same namespace is shared between tranformator UI and server", { +testthat::test_that("ui_teal_transform_dataname and srv_teal_transform_dataname have the same namespace for transform module", { ttm <- teal_transform_module( ui = function(id) tags$div(id = NS(id, "a_div"), "a div"), server = function(id, data) { From 0003fef718eb10310f3a8be8d95c1db86ec02d8e Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 20 Nov 2024 11:06:34 +0100 Subject: [PATCH 123/165] @averissimo suggestions on debounce removal --- vignettes/decorate-module-output.Rmd | 34 ++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index a683845725..f682490a5f 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -235,20 +235,26 @@ tm_decorated_plot <- function(label = "module", transformators = list(), decorat updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) - plot_data <- debounce( - reactive({ - req(input$dataname, input$x, input$y) - within(data(), - { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }), 200 - ) + dataname <- reactive(req(input$dataname)) + x <- reactive({ + req(input$x, input$x %in% colnames(data()[[dataname()]])) + input$x + }) + + y <- reactive({ + req(input$y, input$y %in% colnames(data()[[dataname()]])) + input$y + }) + plot_data <- reactive({ + req(dataname(), x(), y()) + within(data(), { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() + }, + dataname = as.name(dataname()), + x = as.name(x()), + y = as.name(y()) + ) + }) plot_data_decorated <- srv_teal_transform_data("decorate", data = plot_data, transformators = decorators) From 1b0d5fd2c5411aa0080f1f2623d7a9b13a389b14 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:09:02 +0000 Subject: [PATCH 124/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-module-output.Rmd | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index f682490a5f..a8327958ec 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -240,19 +240,21 @@ tm_decorated_plot <- function(label = "module", transformators = list(), decorat req(input$x, input$x %in% colnames(data()[[dataname()]])) input$x }) - + y <- reactive({ req(input$y, input$y %in% colnames(data()[[dataname()]])) input$y }) plot_data <- reactive({ req(dataname(), x(), y()) - within(data(), { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() - }, - dataname = as.name(dataname()), - x = as.name(x()), - y = as.name(y()) + within(data(), + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(dataname()), + x = as.name(x()), + y = as.name(y()) ) }) From 42035b7877c2da21bd2c616ed8d7f7c3dc931482 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:15:42 +0100 Subject: [PATCH 125/165] Update decorators vignette for multiple decorators (#1418) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Still working on this example as the final module does not produce the plot --------- Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 196 ++++++++++++++------------- 1 file changed, 100 insertions(+), 96 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index a8327958ec..f317ef5012 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -302,34 +302,33 @@ if (interactive()) { ### Example Module -It is also possible to pass multiple decorators into a module. See below example that shows how you can -manipulate multiple decorators passed to the module. In this scenario different decorator is selected, -depending on the input of the module, to be applied to the `ggplot2` plot. +It is possible to pass any number of decorators (n) to a module. The example below demonstrates how to handle a dynamic number of decorators, allowing the user to choose which decorator to apply from a list. This makes the module more flexible and capable of accommodating various customization requirements. ```{r} library(ggplot2) -tm_decorated_plot <- function( - label = "module", - decorators = list( - plot_1 = teal_transform_module(), - plot_2 = teal_transform_module() - )) { +tm_decorated_plot <- function(label = "module", decorators = list(teal_transform_module())) { module( label = label, ui = function(id, decorators) { ns <- NS(id) div( - selectInput(ns("dataname"), label = "select dataname", choices = NULL), - selectInput(ns("x"), label = "select x", choices = NULL), - selectInput(ns("y"), label = "select y", choices = NULL), - selectInput(ns("plot_type"), "plot type", choices = c("plot 1", "plot 2"), selected = "plot 1"), - div( - id = ns("decorate_1_wrapper"), - ui_teal_transform_data(ns("decorate_1"), transformators = decorators[[1]]) + selectInput(ns("dataname"), label = "Select dataset", choices = NULL), + selectInput(ns("x"), label = "Select x-axis", choices = NULL), + selectInput(ns("y"), label = "Select y-axis", choices = NULL), + selectInput( + ns("decorator_choice"), + "Choose decorator", + choices = names(decorators), + selected = names(decorators)[1] ), div( - id = ns("decorate_2_wrapper"), - ui_teal_transform_data(ns("decorate_2"), transformators = decorators[[2]]) + id = ns("decorate_wrapper"), + lapply(names(decorators), function(decorator_name) { + div( + id = ns(paste0("decorate_", decorator_name)), + ui_teal_transform_data(ns(paste0("decorate_", decorator_name)), transformators = decorators[[decorator_name]]) + ) + }) ), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) @@ -340,79 +339,60 @@ tm_decorated_plot <- function( observeEvent(data(), { updateSelectInput(inputId = "dataname", choices = names(data())) }) - + observeEvent(input$dataname, { req(input$dataname) updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) - - observeEvent(input$plot_type, { - if (input$plot_type == "plot 1") { - shinyjs::hide("decorate_2_wrapper") - shinyjs::show("decorate_1_wrapper") - } else { - shinyjs::show("decorate_2_wrapper") - shinyjs::hide("decorate_1_wrapper") - } + + observeEvent(input$decorator_choice, { + # Dynamically show only the selected decorator's UI + lapply(names(decorators), function(decorator_name) { + if (decorator_name == input$decorator_choice) { + shinyjs::show(paste0("decorate_", decorator_name)) + } else { + shinyjs::hide(paste0("decorate_", decorator_name)) + } + }) }) - - plot_1_data <- debounce(reactive({ - req(input$dataname, input$x, input$y) - within(data(), - { - plot_1 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggplot2::ggtitle("plot 1") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }), 200) - - plot_2_data <- debounce(reactive({ - req(input$dataname, input$x, input$y) - within(data(), - { - plot_2 <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + - ggplot2::geom_point() + - ggplot2::ggtitle("plot 2") - }, - dataname = as.name(input$dataname), - x = as.name(input$x), - y = as.name(input$y) - ) - }), 200) - - plot_1_data_decorated <- - srv_teal_transform_data( - "decorate_1", - data = plot_1_data, transformators = decorators[[1]] - ) - plot_2_data_decorated <- - srv_teal_transform_data( - "decorate_2", - data = plot_2_data, transformators = decorators[[2]] + + dataname <- reactive(req(input$dataname)) + x <- reactive({ + req(input$x, input$x %in% colnames(data()[[dataname()]])) + input$x + }) + + y <- reactive({ + req(input$y, input$y %in% colnames(data()[[dataname()]])) + input$y + }) + plot_data <- reactive({ + req(dataname(), x(), y()) + within(data(), { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() + }, + dataname = as.name(dataname()), + x = as.name(x()), + y = as.name(y()) ) - - - plot_r <- reactive({ - req(input$plot_type) - if (input$plot_type == "plot 1") { - plot_1_data_decorated()[["plot_1"]] - } else if (input$plot_type == "plot 2") { - plot_2_data_decorated()[["plot_2"]] - } }) - - output$plot <- renderPlot(plot_r()) + + selected_decorator <- reactive({ + req(input$decorator_choice) + input$decorator_choice + }) + + decorated_data <- srv_teal_transform_data( + paste0("decorate_", selected_decorator()), + data = plot_data, + transformators = decorators[[selected_decorator()]] + ) + + output$plot <- renderPlot(decorated_data()[["plot"]]) output$text <- renderText({ - if (input$plot_type == "plot 1") { - teal.code::get_code(req(plot_1_data())) - } else if (input$plot_type == "plot 2") { - teal.code::get_code(req(plot_2_data())) - } + req(input$decorator_choice) + teal.code::get_code(req(decorated_data())) }) }) }, @@ -420,6 +400,7 @@ tm_decorated_plot <- function( server_args = list(decorators = decorators) ) } + ``` @@ -436,11 +417,11 @@ interactive_decorator_1 <- teal_transform_module( reactive({ req(data()) within(data(), - { - plot_1 <- plot_1 + - xlab(x_axis_title) - }, - x_axis_title = input$x_axis_title + { + plot <- plot + + xlab(title) + }, + title = input$x_axis_title ) }) }) @@ -459,11 +440,34 @@ interactive_decorator_2 <- teal_transform_module( reactive({ req(data()) within(data(), - { - plot_2 <- plot_2 + - xlab(x_axis_title) - }, - x_axis_title = input$x_axis_title + { + plot <- plot + + xlab(title) + }, + title = input$x_axis_title + ) + }) + }) + } +) + +interactive_decorator_3 <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("x_axis_title"), "X axis title", value = "x axis 3") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + plot <- plot + + xlab(title) + }, + title = input$x_axis_title ) }) }) @@ -477,12 +481,12 @@ interactive_decorator_2 <- teal_transform_module( app <- init( data = teal_data(iris = iris, mtcars = mtcars), modules = modules( - tm_decorated_plot("identity"), tm_decorated_plot( - "interactive", + "dynamic_decorators", decorators = list( - plot_1 = interactive_decorator_1, - plot_2 = interactive_decorator_2 + decorator_1 = interactive_decorator_1, + decorator_2 = interactive_decorator_2, + decorator_3 = interactive_decorator_3 ) ) ) From cc32fe01e5ac16582c70281bdd8709f5a830ffde Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:18:12 +0000 Subject: [PATCH 126/165] [skip style] [skip vbump] Restyle files --- vignettes/decorate-module-output.Rmd | 59 ++++++++++++++-------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index f317ef5012..feecea47ae 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -339,13 +339,13 @@ tm_decorated_plot <- function(label = "module", decorators = list(teal_transform observeEvent(data(), { updateSelectInput(inputId = "dataname", choices = names(data())) }) - + observeEvent(input$dataname, { req(input$dataname) updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) - + observeEvent(input$decorator_choice, { # Dynamically show only the selected decorator's UI lapply(names(decorators), function(decorator_name) { @@ -356,39 +356,41 @@ tm_decorated_plot <- function(label = "module", decorators = list(teal_transform } }) }) - + dataname <- reactive(req(input$dataname)) x <- reactive({ req(input$x, input$x %in% colnames(data()[[dataname()]])) input$x }) - + y <- reactive({ req(input$y, input$y %in% colnames(data()[[dataname()]])) input$y }) plot_data <- reactive({ req(dataname(), x(), y()) - within(data(), { - plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + ggplot2::geom_point() - }, - dataname = as.name(dataname()), - x = as.name(x()), - y = as.name(y()) + within(data(), + { + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) + + ggplot2::geom_point() + }, + dataname = as.name(dataname()), + x = as.name(x()), + y = as.name(y()) ) }) - + selected_decorator <- reactive({ req(input$decorator_choice) input$decorator_choice }) - + decorated_data <- srv_teal_transform_data( paste0("decorate_", selected_decorator()), data = plot_data, transformators = decorators[[selected_decorator()]] ) - + output$plot <- renderPlot(decorated_data()[["plot"]]) output$text <- renderText({ req(input$decorator_choice) @@ -400,7 +402,6 @@ tm_decorated_plot <- function(label = "module", decorators = list(teal_transform server_args = list(decorators = decorators) ) } - ``` @@ -417,11 +418,11 @@ interactive_decorator_1 <- teal_transform_module( reactive({ req(data()) within(data(), - { - plot <- plot + - xlab(title) - }, - title = input$x_axis_title + { + plot <- plot + + xlab(title) + }, + title = input$x_axis_title ) }) }) @@ -440,11 +441,11 @@ interactive_decorator_2 <- teal_transform_module( reactive({ req(data()) within(data(), - { - plot <- plot + - xlab(title) - }, - title = input$x_axis_title + { + plot <- plot + + xlab(title) + }, + title = input$x_axis_title ) }) }) @@ -463,11 +464,11 @@ interactive_decorator_3 <- teal_transform_module( reactive({ req(data()) within(data(), - { - plot <- plot + - xlab(title) - }, - title = input$x_axis_title + { + plot <- plot + + xlab(title) + }, + title = input$x_axis_title ) }) }) From e1e49da351301d13087675b838c2608cf60bca37 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 20 Nov 2024 12:41:42 +0100 Subject: [PATCH 127/165] fix superlintr --- tests/testthat/test-teal_transform_module.R | 3 ++- vignettes/decorate-module-output.Rmd | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-teal_transform_module.R b/tests/testthat/test-teal_transform_module.R index 8b5ec31d2a..a61f467a76 100644 --- a/tests/testthat/test-teal_transform_module.R +++ b/tests/testthat/test-teal_transform_module.R @@ -46,7 +46,8 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m }) }) -testthat::test_that("ui_teal_transform_dataname and srv_teal_transform_dataname have the same namespace for transform module", { +testthat::test_that( + "ui_teal_transform_dataname and srv_teal_transform_dataname have the same namespace for transform module", { ttm <- teal_transform_module( ui = function(id) tags$div(id = NS(id, "a_div"), "a div"), server = function(id, data) { diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index feecea47ae..09ed17abfa 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -326,7 +326,10 @@ tm_decorated_plot <- function(label = "module", decorators = list(teal_transform lapply(names(decorators), function(decorator_name) { div( id = ns(paste0("decorate_", decorator_name)), - ui_teal_transform_data(ns(paste0("decorate_", decorator_name)), transformators = decorators[[decorator_name]]) + ui_teal_transform_data( + ns(paste0("decorate_", decorator_name)), + transformators = decorators[[decorator_name]] + ) ) }) ), From fbd34d87722934fc10c5366a356fe5b7451e28d7 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:44:11 +0000 Subject: [PATCH 128/165] [skip style] [skip vbump] Restyle files --- tests/testthat/test-teal_transform_module.R | 56 +++++++++++---------- vignettes/decorate-module-output.Rmd | 2 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/tests/testthat/test-teal_transform_module.R b/tests/testthat/test-teal_transform_module.R index a61f467a76..01ba455c70 100644 --- a/tests/testthat/test-teal_transform_module.R +++ b/tests/testthat/test-teal_transform_module.R @@ -47,32 +47,34 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m }) testthat::test_that( - "ui_teal_transform_dataname and srv_teal_transform_dataname have the same namespace for transform module", { - ttm <- teal_transform_module( - ui = function(id) tags$div(id = NS(id, "a_div"), "a div"), - server = function(id, data) { - moduleServer(id, function(input, output, session) { - full_id <- session$ns("a_div") - reactive(within(data(), id <- full_id, full_id = full_id)) - }) - } - ) + "ui_teal_transform_dataname and srv_teal_transform_dataname have the same namespace for transform module", + { + ttm <- teal_transform_module( + ui = function(id) tags$div(id = NS(id, "a_div"), "a div"), + server = function(id, data) { + moduleServer(id, function(input, output, session) { + full_id <- session$ns("a_div") + reactive(within(data(), id <- full_id, full_id = full_id)) + }) + } + ) - initial_id <- "a-path-to-an-inner-namespace" - ui <- ui_teal_transform_data(initial_id, ttm) - # Find element that ends in "-a_div" - expected_id <- unname(unlist(ui)[grepl(".*-a_div$", unlist(ui))][1]) + initial_id <- "a-path-to-an-inner-namespace" + ui <- ui_teal_transform_data(initial_id, ttm) + # Find element that ends in "-a_div" + expected_id <- unname(unlist(ui)[grepl(".*-a_div$", unlist(ui))][1]) - testServer( - app = srv_teal_transform_data, - args = list( - id = initial_id, - data = reactive(within(teal_data(), iris <- iris)), - transformators = ttm - ), - expr = { - session$flushReact() - testthat::expect_equal(module_output()$id, expected_id) - } - ) -}) + testServer( + app = srv_teal_transform_data, + args = list( + id = initial_id, + data = reactive(within(teal_data(), iris <- iris)), + transformators = ttm + ), + expr = { + session$flushReact() + testthat::expect_equal(module_output()$id, expected_id) + } + ) + } +) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 09ed17abfa..3e4f5a1597 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -327,7 +327,7 @@ tm_decorated_plot <- function(label = "module", decorators = list(teal_transform div( id = ns(paste0("decorate_", decorator_name)), ui_teal_transform_data( - ns(paste0("decorate_", decorator_name)), + ns(paste0("decorate_", decorator_name)), transformators = decorators[[decorator_name]] ) ) From 113b0c77190968596a75aa6272ae90a6f3f16878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:39:06 +0000 Subject: [PATCH 129/165] feat: updates decorators argument on dummy --- R/dummy_functions.R | 8 ++++++-- man/example_module.Rd | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 99ff2ff2ac..6b86194220 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -22,8 +22,10 @@ example_module <- function(label = "example teal module", datanames = "all", transformators = list(), - decorators = teal_transform_module()) { + decorators = NULL) { checkmate::assert_string(label) + checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE) + ans <- module( label, server = function(id, data, decorators) { @@ -55,9 +57,11 @@ example_module <- function(label = "example teal module", ) }) - table_data_decorated <- srv_teal_transform_data("decorate", data = table_data, transformators = decorators) + table_data_decorated_no_print <- srv_teal_transform_data("decorate", data = table_data, transformators = decorators) + table_data_decorated <- reactive(within(table_data_decorated_no_print(), expr = object)) output$text <- renderPrint({ + req(table_data) # Ensure original errors from module are displayed table_data_decorated()[["object"]] }) diff --git a/man/example_module.Rd b/man/example_module.Rd index 92aaedfc8f..694b7558a9 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -8,7 +8,7 @@ example_module( label = "example teal module", datanames = "all", transformators = list(), - decorators = teal_transform_module() + decorators = NULL ) } \arguments{ From 28c50a9cb52e0815976eb00f09d8c94baf35cc3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:11:21 +0000 Subject: [PATCH 130/165] feat: add print statement to code --- vignettes/decorate-module-output.Rmd | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 3e4f5a1597..0b2ca12dc2 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -209,7 +209,9 @@ Please find an example module for the sake of this article: ```{r} -tm_decorated_plot <- function(label = "module", transformators = list(), decorators = teal_transform_module()) { +tm_decorated_plot <- function(label = "module", transformators = list(), decorators = NULL) { + checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE) + module( label = label, ui = function(id, decorators) { @@ -258,7 +260,8 @@ tm_decorated_plot <- function(label = "module", transformators = list(), decorat ) }) - plot_data_decorated <- srv_teal_transform_data("decorate", data = plot_data, transformators = decorators) + plot_data_decorated_no_print <- srv_teal_transform_data("decorate", data = plot_data, transformators = decorators) + plot_data_decorated <- reactive(within(req(plot_data_decorated_no_print()), expr = plot)) plot_r <- reactive({ plot_data_decorated()[["plot"]] @@ -284,12 +287,12 @@ app <- init( data = teal_data(iris = iris, mtcars = mtcars), modules = modules( tm_decorated_plot("identity"), - tm_decorated_plot("no-ui", decorators = static_decorator), - tm_decorated_plot("lang", decorators = static_decorator_lang), - tm_decorated_plot("interactive", decorators = interactive_decorator), - tm_decorated_plot("interactive-from lang", decorators = interactive_decorator_lang), - tm_decorated_plot("from-fun", decorators = gg_xlab_decorator("plot")), - tm_decorated_plot("failing", decorators = failing_decorator) + tm_decorated_plot("no-ui", decorators = list(static_decorator)), + tm_decorated_plot("lang", decorators = list(static_decorator_lang)), + tm_decorated_plot("interactive", decorators = list(interactive_decorator)), + tm_decorated_plot("interactive-from lang", decorators = list(interactive_decorator_lang)), + tm_decorated_plot("from-fun", decorators = list(gg_xlab_decorator("plot"))), + tm_decorated_plot("failing", decorators = list(failing_decorator)) ) ) @@ -306,7 +309,8 @@ It is possible to pass any number of decorators (n) to a module. The example bel ```{r} library(ggplot2) -tm_decorated_plot <- function(label = "module", decorators = list(teal_transform_module())) { +tm_decorated_plot <- function(label = "module", decorators = NULL) { + checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE) module( label = label, ui = function(id, decorators) { @@ -388,11 +392,12 @@ tm_decorated_plot <- function(label = "module", decorators = list(teal_transform input$decorator_choice }) - decorated_data <- srv_teal_transform_data( + decorated_data_no_print <- srv_teal_transform_data( paste0("decorate_", selected_decorator()), data = plot_data, transformators = decorators[[selected_decorator()]] ) + decorated_data <- reactive(within(req(decorated_data_no_print()), expr = plot)) output$plot <- renderPlot(decorated_data()[["plot"]]) output$text <- renderText({ From 62913ae6cbf192156074ec19d3f88c15a6de587a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:11:50 +0000 Subject: [PATCH 131/165] fix: tests uses list of decorators --- tests/testthat/test-module_teal.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 9d6f2ce09a..6f46e07894 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1960,7 +1960,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { args = list( id = "test", data = teal.data::teal_data(object = iris), - modules = modules(example_module("mod1", decorators = output_decorator)) + modules = modules(example_module("mod1", decorators = list(output_decorator))) ), expr = { session$setInputs(`teal_modules-active_tab` = "mod1") @@ -2002,7 +2002,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { modules = modules( example_module( "mod1", - decorators = decorator_name(output_name = "object", label = "decorator_name") + decorators = list(decorator_name(output_name = "object", label = "decorator_name")) ) ) ), @@ -2044,7 +2044,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { modules = modules( example_module( "mod1", - decorators = decorator_name(output_name = "object", label = "decorator_name") + decorators = list(decorator_name(output_name = "object", label = "decorator_name")) ) ) ), From f493ee11e4918deb7dc4898d026ad3c0ae4f9c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:46:35 +0000 Subject: [PATCH 132/165] fix: corrects e2e tests and small fixes --- R/dummy_functions.R | 8 ++-- tests/testthat/test-shinytest2-decorators.R | 47 +++++++++------------ 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 6b86194220..e627e6f25b 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -58,10 +58,10 @@ example_module <- function(label = "example teal module", }) table_data_decorated_no_print <- srv_teal_transform_data("decorate", data = table_data, transformators = decorators) - table_data_decorated <- reactive(within(table_data_decorated_no_print(), expr = object)) + table_data_decorated <- reactive(within(req(table_data_decorated_no_print()), expr = object)) output$text <- renderPrint({ - req(table_data) # Ensure original errors from module are displayed + req(table_data()) # Ensure original errors from module are displayed table_data_decorated()[["object"]] }) @@ -81,10 +81,10 @@ example_module <- function(label = "example teal module", output = verbatimTextOutput(ns("text")), encoding = tags$div( selectInput(ns("dataname"), "Choose a dataset", choices = NULL), + ui_teal_transform_data(ns("decorate"), transformators = decorators), teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") ) - ), - ui_teal_transform_data(ns("decorate"), transformators = decorators) + ) ) }, ui_args = list(decorators = decorators), diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R index 24a9d4350c..382778292e 100644 --- a/tests/testthat/test-shinytest2-decorators.R +++ b/tests/testthat/test-shinytest2-decorators.R @@ -28,21 +28,18 @@ testthat::test_that("e2e: module with decorator UI and output is modified intera app <- TealAppDriver$new( data = teal.data::teal_data(x = "Text Input"), - modules = example_module(label = "Example Module", decorators = interactive_decorator) + modules = example_module(label = "Example Module", decorators = list(interactive_decorator)) ) app$navigate_teal_tab("Example Module") - input_id <- "decorate-transform_module-transform_module-append_text" + input_id <- Reduce( + shiny::NS, + c("decorate", "transform_module", "transform", "append_text") + ) testthat::expect_true( - app$is_visible( - sprintf( - "#%s-%s", - app$active_module_ns(), - input_id - ) - ) + app$is_visible(sprintf("#%s-%s", app$active_module_ns(), input_id)) ) testthat::expect_identical( @@ -91,38 +88,32 @@ testthat::test_that("e2e: module with decorator, where server fails, shows shin ) app <- TealAppDriver$new( data = teal.data::teal_data(iris = iris), - modules = example_module(label = "Example Module", decorators = failing_decorator) + modules = example_module(label = "Example Module", decorators = list(failing_decorator)) ) app$navigate_teal_tab("Example Module") - input_id <- "decorate-transform_module-silent_error-message" - - testthat::expect_true( - app$is_visible( - sprintf( - "#%s-%s", - app$active_module_ns(), - input_id - ) - ) + input_id <- Reduce( + shiny::NS, + c("decorate", "transform_module", "silent_error", "message") ) + testthat::expect_true(app$is_visible(sprintf("#%s-%s", app$active_module_ns(), input_id))) + app$expect_validation_error() - testthat::expect_identical( - app$active_module_element_text(input_id), - paste( + testthat::expect_setequal( + strsplit(app$active_module_element_text(input_id), "\n")[[1]], + c( "Shiny error when executing the `data` module.", "This is error", - "Check your inputs or contact app developer if error persists.", - sep = "\n" + "Check your inputs or contact app developer if error persists." ) ) - testthat::expect_identical( - app$get_active_module_output("text"), - "NULL" + testthat::expect_setequal( + app$get_active_module_output("text")$type, + c("shiny.silent.error", "validation") ) app$stop() From fa298c8b330d7922a9780c99bc97988a171318e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:47:44 +0000 Subject: [PATCH 133/165] docs: add decorators param --- R/dummy_functions.R | 3 +++ man/example_module.Rd | 3 +++ 2 files changed, 6 insertions(+) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index e627e6f25b..ea64da8330 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -9,6 +9,9 @@ #' to read more about decorators. #' #' @inheritParams teal_modules +#' @param decorators `r lifecycle::badge("experimental")` (`list` of `teal_transform_module` or `NULL`) optional, +#' if not `NULL`, decorator for tables or plots included in the module. +#' #' @return A `teal` module which can be included in the `modules` argument to [init()]. #' @examples #' app <- init( diff --git a/man/example_module.Rd b/man/example_module.Rd index 694b7558a9..dfccaaa42b 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -25,6 +25,9 @@ argument. }} \item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} + +\item{decorators}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} (\code{list} of \code{teal_transform_module} or \code{NULL}) optional, +if not \code{NULL}, decorator for tables or plots included in the module.} } \value{ A \code{teal} module which can be included in the \code{modules} argument to \code{\link[=init]{init()}}. From ed921deab77bd2b48f6afbfebe5650336c9af906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:50:06 +0000 Subject: [PATCH 134/165] chore: fix linter errors --- R/dummy_functions.R | 5 ++++- vignettes/decorate-module-output.Rmd | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index ea64da8330..90345bd457 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -60,7 +60,10 @@ example_module <- function(label = "example teal module", ) }) - table_data_decorated_no_print <- srv_teal_transform_data("decorate", data = table_data, transformators = decorators) + table_data_decorated_no_print <- srv_teal_transform_data( + "decorate", + data = table_data, transformators = decorators + ) table_data_decorated <- reactive(within(req(table_data_decorated_no_print()), expr = object)) output$text <- renderPrint({ diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 0b2ca12dc2..15ed14e7ce 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -260,8 +260,14 @@ tm_decorated_plot <- function(label = "module", transformators = list(), decorat ) }) - plot_data_decorated_no_print <- srv_teal_transform_data("decorate", data = plot_data, transformators = decorators) - plot_data_decorated <- reactive(within(req(plot_data_decorated_no_print()), expr = plot)) + plot_data_decorated_no_print <- srv_teal_transform_data( + "decorate", + data = plot_data, + transformators = decorators + ) + plot_data_decorated <- reactive( + within(req(plot_data_decorated_no_print()), expr = plot) + ) plot_r <- reactive({ plot_data_decorated()[["plot"]] From 26ceb2d37e0c41dcfad3d58d2bfd0b91274ff878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:44:25 +0100 Subject: [PATCH 135/165] Update R/dummy_functions.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- R/dummy_functions.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 90345bd457..1317936ad0 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -62,7 +62,8 @@ example_module <- function(label = "example teal module", table_data_decorated_no_print <- srv_teal_transform_data( "decorate", - data = table_data, transformators = decorators + data = table_data, + transformators = decorators ) table_data_decorated <- reactive(within(req(table_data_decorated_no_print()), expr = object)) From 452c9dad40ba45a2ed96c7d4b05b3ba7d54b9661 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:46:51 +0000 Subject: [PATCH 136/165] [skip style] [skip vbump] Restyle files --- R/dummy_functions.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 1317936ad0..22634c617a 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -62,7 +62,7 @@ example_module <- function(label = "example teal module", table_data_decorated_no_print <- srv_teal_transform_data( "decorate", - data = table_data, + data = table_data, transformators = decorators ) table_data_decorated <- reactive(within(req(table_data_decorated_no_print()), expr = object)) From 2078b1f129be7a018818e330dee0ee5e7229e1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:43:27 +0000 Subject: [PATCH 137/165] docs: fixes params documentation --- R/module_transform_data.R | 3 ++- R/modules.R | 4 +--- man/module_transform_data.Rd | 2 ++ man/teal_modules.Rd | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 30a81e69e3..c7796f38b8 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -5,9 +5,10 @@ #' #' @inheritParams module_teal_data #' @inheritParams teal_modules +#' @param class (character(1)) CSS class to be added in the `div` wrapper tag. + #' @return `reactive` `teal_data` #' -#' #' @name module_transform_data NULL diff --git a/R/modules.R b/R/modules.R index 806938531f..2b27d5bb41 100644 --- a/R/modules.R +++ b/R/modules.R @@ -524,7 +524,7 @@ format.teal_module <- function( #' cat(format(complete_modules)) #' cat(format(complete_modules, what = c("ui_args", "server_args", "transformators"))) #' @export -format.teal_modules <- function(x, indent = 0, is_root = TRUE, is_last = FALSE, parent_prefix = "", ...) { +format.teal_modules <- function(x, is_root = TRUE, is_last = FALSE, parent_prefix = "", ...) { if (is_root) { header <- pasten(crayon::bold("TEAL ROOT")) new_parent_prefix <- " " #' Initial indent for root level @@ -551,7 +551,6 @@ format.teal_modules <- function(x, indent = 0, is_root = TRUE, is_last = FALSE, children_output <- c( children_output, format(child, - indent = indent, is_root = FALSE, is_last = is_last_child, parent_prefix = new_parent_prefix, @@ -562,7 +561,6 @@ format.teal_modules <- function(x, indent = 0, is_root = TRUE, is_last = FALSE, children_output <- c( children_output, format(child, - indent = indent, is_last = is_last_child, parent_prefix = new_parent_prefix, ... diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index e37dc0707a..9ebe37f105 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -21,6 +21,8 @@ srv_teal_transform_data( \item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transformator module's data input.} +\item{class}{(character(1)) CSS class to be added in the \code{div} wrapper tag.} + \item{data}{(\verb{reactive teal_data})} \item{modules}{(\code{teal_modules} or \code{teal_module}) For \code{datanames} validation purpose} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index 99821a7fb2..1467bd0aef 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -34,7 +34,7 @@ modules(..., label = "root") ... ) -\method{format}{teal_modules}(x, indent = 0, is_root = TRUE, is_last = FALSE, parent_prefix = "", ...) +\method{format}{teal_modules}(x, is_root = TRUE, is_last = FALSE, parent_prefix = "", ...) \method{print}{teal_module}(x, ...) From 4340c2bee02add08078a58a75837bac6e59bf1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:45:45 +0000 Subject: [PATCH 138/165] chore: adds ggplot2 to suggests --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index d14c97e162..e422a586da 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -57,6 +57,7 @@ Imports: utils Suggests: bslib, + ggplot2 (>= 3.4.0), knitr (>= 1.42), mirai (>= 1.1.1), MultiAssayExperiment, From 8f42166ea80b2a37d041abd4b6f44c3990777c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:47:05 +0000 Subject: [PATCH 139/165] fix: update e2e test with newer examples code --- tests/testthat/test-shinytest2-show-rcode.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test-shinytest2-show-rcode.R b/tests/testthat/test-shinytest2-show-rcode.R index a7504573f0..c2d8deda1d 100644 --- a/tests/testthat/test-shinytest2-show-rcode.R +++ b/tests/testthat/test-shinytest2-show-rcode.R @@ -50,7 +50,9 @@ testthat::test_that("e2e: teal app initializes with Show R Code modal", { sprintf('stopifnot(rlang::hash(iris) == "%s") # @linksto iris', rlang::hash(iris)), sprintf('stopifnot(rlang::hash(mtcars) == "%s") # @linksto mtcars', rlang::hash(mtcars)), ".raw_data <- list2env(list(iris = iris, mtcars = mtcars))", - "lockEnvironment(.raw_data) # @linksto .raw_data" + "lockEnvironment(.raw_data) # @linksto .raw_data", + "object <- iris", + "object" ) ) From ab8fe49adbcb590af94ea76fe0f312f7a2ab46c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:51:57 +0000 Subject: [PATCH 140/165] fix: examples --- R/teal_transform_module.R | 2 +- man/teal_transform_module.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index cd5390242d..801a88fe67 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -110,7 +110,7 @@ #' #' app <- init( #' data = teal_data(iris = iris), -#' modules = example_module(transformators = data_transformators, decorators = output_decorator) +#' modules = example_module(transformators = data_transformators, decorators = list(output_decorator)) #' ) #' if (interactive()) { #' shinyApp(app$ui, app$server) diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 0bede146e2..afe9ef7898 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -125,7 +125,7 @@ output_decorator <- teal_transform_module( app <- init( data = teal_data(iris = iris), - modules = example_module(transformators = data_transformators, decorators = output_decorator) + modules = example_module(transformators = data_transformators, decorators = list(output_decorator)) ) if (interactive()) { shinyApp(app$ui, app$server) From 8cb4a52cd5cc0d8f6f63ea56c8c78c654371edd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:17:36 +0000 Subject: [PATCH 141/165] fix: R CMD check note --- R/teal_transform_module.R | 5 ++++- man/teal_transform_module.Rd | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 801a88fe67..0ede26dc85 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -110,7 +110,10 @@ #' #' app <- init( #' data = teal_data(iris = iris), -#' modules = example_module(transformators = data_transformators, decorators = list(output_decorator)) +#' modules = example_module( +#' transformators = data_transformators, +#' decorators = list(output_decorator) +#' ) #' ) #' if (interactive()) { #' shinyApp(app$ui, app$server) diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index afe9ef7898..c89b8ae01c 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -125,7 +125,10 @@ output_decorator <- teal_transform_module( app <- init( data = teal_data(iris = iris), - modules = example_module(transformators = data_transformators, decorators = list(output_decorator)) + modules = example_module( + transformators = data_transformators, + decorators = list(output_decorator) + ) ) if (interactive()) { shinyApp(app$ui, app$server) From e0b9e0e97f9b126b3c37526a02b4613fde5b95da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:21:07 +0000 Subject: [PATCH 142/165] fix: remove unnecessary div --- R/dummy_functions.R | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 22634c617a..0d48e668d8 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -83,14 +83,12 @@ example_module <- function(label = "example teal module", }, ui = function(id, decorators) { ns <- NS(id) - div( - teal.widgets::standard_layout( - output = verbatimTextOutput(ns("text")), - encoding = tags$div( - selectInput(ns("dataname"), "Choose a dataset", choices = NULL), - ui_teal_transform_data(ns("decorate"), transformators = decorators), - teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") - ) + teal.widgets::standard_layout( + output = verbatimTextOutput(ns("text")), + encoding = tags$div( + selectInput(ns("dataname"), "Choose a dataset", choices = NULL), + ui_teal_transform_data(ns("decorate"), transformators = decorators), + teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") ) ) }, From a9a0b0e8fd11815c775c7580b1c7e6bbe0f8fa3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 09:27:49 +0000 Subject: [PATCH 143/165] chore: improve on error message --- R/module_teal_data.R | 2 +- R/module_transform_data.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/module_teal_data.R b/R/module_teal_data.R index 75b23e783c..056c2ffdcc 100644 --- a/R/module_teal_data.R +++ b/R/module_teal_data.R @@ -133,7 +133,7 @@ srv_validate_reactive_teal_data <- function(id, # nolint: object_length output$previous_failed <- renderUI({ if (hide_validation_error()) { shinyjs::hide("validate_messages") - tags$div("One of previous transformators failed. Please fix and continue.", class = "teal-output-warning") + tags$div("One of previous transformators failed. Please check its inputs.", class = "teal-output-warning") } else { shinyjs::show("validate_messages") NULL diff --git a/R/module_transform_data.R b/R/module_transform_data.R index c7796f38b8..d0781eeedc 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -120,7 +120,7 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is output$error_wrapper <- renderUI({ if (is_previous_failed()) { shinyjs::disable(transform_wrapper_id) - tags$div("One of previous transformators failed. Please fix and continue.", class = "teal-output-warning") + tags$div("One of previous transformators failed. Please check its inputs.", class = "teal-output-warning") } else { shinyjs::enable(transform_wrapper_id) shiny::tagList( From e7747c5e8e6b971e4995dc5ab5e4748d4cb985ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:46:02 +0000 Subject: [PATCH 144/165] chore: improve on error message (missing on last commit) --- R/module_nested_tabs.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 62ab4e35f7..6b0103d8ce 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -98,7 +98,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { class = "teal_validated", div( class = "teal-output-warning", - "One of transformators failed. Please fix and continue." + "One of transformators failed. Please check its inputs." ) ) ), From a71e6d5600467b25da1faf2611811cf76a9b338b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:46:29 +0000 Subject: [PATCH 145/165] chore: use safer ids in transformators --- R/module_transform_data.R | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index d0781eeedc..5c2469decd 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -23,10 +23,7 @@ ui_teal_transform_data <- function(id, transformators, class = "well") { transformators <- list(transformators) } checkmate::assert_list(transformators, "teal_transform_module") - - labels <- lapply(transformators, function(x) attr(x, "label")) - ids <- get_unique_labels(labels) - names(transformators) <- ids + names(transformators) <- sprintf("transformator_%d", seq_len(length(transformators))) lapply( names(transformators), @@ -83,9 +80,7 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is transformators <- list(transformators) } checkmate::assert_list(transformators, "teal_transform_module", null.ok = TRUE) - labels <- lapply(transformators, function(x) attr(x, "label")) - ids <- get_unique_labels(labels) - names(transformators) <- ids + names(transformators) <- sprintf("transformator_%d", seq_len(length(transformators))) moduleServer(id, function(input, output, session) { module_output <- Reduce( From be49c50291fc87ef52e231d5095093dd6bf4d1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:51:15 +0000 Subject: [PATCH 146/165] chore: minor rename of id --- R/module_transform_data.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 5c2469decd..ad9ceb34a6 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -23,7 +23,7 @@ ui_teal_transform_data <- function(id, transformators, class = "well") { transformators <- list(transformators) } checkmate::assert_list(transformators, "teal_transform_module") - names(transformators) <- sprintf("transformator_%d", seq_len(length(transformators))) + names(transformators) <- sprintf("transform_%d", seq_len(length(transformators))) lapply( names(transformators), @@ -80,7 +80,7 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is transformators <- list(transformators) } checkmate::assert_list(transformators, "teal_transform_module", null.ok = TRUE) - names(transformators) <- sprintf("transformator_%d", seq_len(length(transformators))) + names(transformators) <- sprintf("transform_%d", seq_len(length(transformators))) moduleServer(id, function(input, output, session) { module_output <- Reduce( From 0f9565c62300f8877ef21f7b481dab2d34bf86a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:14:50 +0000 Subject: [PATCH 147/165] fix: show errors in transform even if there is no UI --- R/module_transform_data.R | 66 +++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index ad9ceb34a6..221f3d36e6 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -33,33 +33,38 @@ ui_teal_transform_data <- function(id, transformators, class = "well") { data_mod <- transformators[[name]] transform_wrapper_id <- ns(sprintf("wrapper_%s", name)) - div( # todo: accordion? - # class .teal_validated changes the color of the boarder on error in ui_validate_reactive_teal_data - # For details see tealValidate.js file. - class = c(class, "teal_validated"), - title = attr(data_mod, "label"), - tags$span( - class = "text-primary mb-4", - icon("fas fa-square-pen"), - attr(data_mod, "label") - ), - tags$i( - class = "remove pull-right fa fa-angle-down", - style = "cursor: pointer;", - title = "fold/expand transformator panel", - onclick = sprintf("togglePanelItems(this, '%s', 'fa-angle-right', 'fa-angle-down');", transform_wrapper_id) - ), - tags$div( - id = transform_wrapper_id, - if (is.null(data_mod$ui)) { - return(NULL) - } else { - data_mod$ui(id = ns("transform")) - }, - div( - id = ns("validate_messages"), - class = "teal_validated", - uiOutput(ns("error_wrapper")) + display_fun <- if (is.null(data_mod$ui)) shinyjs::hidden else function(x) x + + display_fun( + div( # todo: accordion? + # class .teal_validated changes the color of the boarder on error in ui_validate_reactive_teal_data + # For details see tealValidate.js file. + id = ns("wrapper"), + class = c(class, "teal_validated"), + title = attr(data_mod, "label"), + tags$span( + class = "text-primary mb-4", + icon("fas fa-square-pen"), + attr(data_mod, "label") + ), + tags$i( + class = "remove pull-right fa fa-angle-down", + style = "cursor: pointer;", + title = "fold/expand transformator panel", + onclick = sprintf("togglePanelItems(this, '%s', 'fa-angle-right', 'fa-angle-down');", transform_wrapper_id) + ), + tags$div( + id = transform_wrapper_id, + if (is.null(data_mod$ui)) { + NULL + } else { + data_mod$ui(id = ns("transform")) + }, + div( + id = ns("validate_messages"), + class = "teal_validated", + uiOutput(ns("error_wrapper")) + ) ) ) ) @@ -111,6 +116,13 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is srv_check_module_datanames("datanames_warning", data_handled, modules) } + # When there is no UI (`ui = NULL`) it should still show the errors + observe({ + if (!inherits(data_handled(), "teal_data") && !is_previous_failed()) { + shinyjs::show("wrapper") + } + }) + transform_wrapper_id <- sprintf("wrapper_%s", name) output$error_wrapper <- renderUI({ if (is_previous_failed()) { From f67584f1beac0708053ecbd0f47b75740b1c63c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:06:56 +0000 Subject: [PATCH 148/165] chore: rename ui/srv_teal_transform_data to ui/srv_transform_teal_data --- NAMESPACE | 4 ++-- R/dummy_functions.R | 4 ++-- R/module_nested_tabs.R | 4 ++-- R/module_transform_data.R | 8 ++++---- R/teal_transform_module.R | 2 +- man/module_transform_data.Rd | 8 ++++---- man/teal_transform_module.Rd | 2 +- tests/testthat/test-module_teal.R | 2 +- tests/testthat/test-shinytest2-decorators.R | 2 +- tests/testthat/test-teal_transform_module.R | 10 +++++----- vignettes/decorate-module-output.Rmd | 8 ++++---- 11 files changed, 27 insertions(+), 27 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index cd5a987d12..3e156a1e6f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -31,15 +31,15 @@ export(reporter_previewer_module) export(set_datanames) export(show_rcode_modal) export(srv_teal) -export(srv_teal_transform_data) export(srv_teal_with_splash) +export(srv_transform_teal_data) export(tdata2env) export(teal_data_module) export(teal_slices) export(teal_transform_module) export(ui_teal) -export(ui_teal_transform_data) export(ui_teal_with_splash) +export(ui_transform_teal_data) export(validate_has_data) export(validate_has_elements) export(validate_has_variable) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 0d48e668d8..b04c4a488b 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -60,7 +60,7 @@ example_module <- function(label = "example teal module", ) }) - table_data_decorated_no_print <- srv_teal_transform_data( + table_data_decorated_no_print <- srv_transform_teal_data( "decorate", data = table_data, transformators = decorators @@ -87,7 +87,7 @@ example_module <- function(label = "example teal module", output = verbatimTextOutput(ns("text")), encoding = tags$div( selectInput(ns("dataname"), "Choose a dataset", choices = NULL), - ui_teal_transform_data(ns("decorate"), transformators = decorators), + ui_transform_teal_data(ns("decorate"), transformators = decorators), teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") ) ) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 6b0103d8ce..4994d4eb85 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -125,7 +125,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { width = 3, ui_data_summary(ns("data_summary")), ui_filter_data(ns("filter_panel")), - ui_teal_transform_data(ns("data_transform"), transformators = modules$transformators, class = "well"), + ui_transform_teal_data(ns("data_transform"), transformators = modules$transformators, class = "well"), class = "teal_secondary_col" ) ) @@ -261,7 +261,7 @@ srv_teal_module.teal_module <- function(id, is_active = is_active ) is_transform_failed <- reactiveValues() - transformed_teal_data <- srv_teal_transform_data( + transformed_teal_data <- srv_transform_teal_data( "data_transform", data = filtered_teal_data, transformators = modules$transformators, diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 221f3d36e6..128214f9b5 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -14,7 +14,7 @@ NULL #' @export #' @rdname module_transform_data -ui_teal_transform_data <- function(id, transformators, class = "well") { +ui_transform_teal_data <- function(id, transformators, class = "well") { checkmate::assert_string(id) if (length(transformators) == 0L) { return(NULL) @@ -56,7 +56,7 @@ ui_teal_transform_data <- function(id, transformators, class = "well") { tags$div( id = transform_wrapper_id, if (is.null(data_mod$ui)) { - NULL + return(NULL) } else { data_mod$ui(id = ns("transform")) }, @@ -74,7 +74,7 @@ ui_teal_transform_data <- function(id, transformators, class = "well") { #' @export #' @rdname module_transform_data -srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is_transform_failed = reactiveValues()) { +srv_transform_teal_data <- function(id, data, transformators, modules = NULL, is_transform_failed = reactiveValues()) { checkmate::assert_string(id) assert_reactive(data) checkmate::assert_class(modules, "teal_module", null.ok = TRUE) @@ -91,7 +91,7 @@ srv_teal_transform_data <- function(id, data, transformators, modules = NULL, is module_output <- Reduce( function(data_previous, name) { moduleServer(name, function(input, output, session) { - logger::log_debug("srv_teal_transform_data initializing for { name }.") + logger::log_debug("srv_transform_teal_data initializing for { name }.") is_transform_failed[[name]] <- FALSE data_out <- transformators[[name]]$server("transform", data = data_previous) data_handled <- reactive(tryCatch(data_out(), error = function(e) e)) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 0ede26dc85..258017e613 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -20,7 +20,7 @@ #' `teal_transform_module` also allows developers to modify any object created within [`teal.data::teal_data`]. #' This means you can use it to customize not only datasets but also tables, listings, and graphs. #' Some [`teal_modules`] permit developers to inject custom `shiny` modules to enhance displayed outputs. -#' To manage these `decorators` within your module, use [`ui_teal_transform_data()`] and [`srv_teal_transform_data()`]. +#' To manage these `decorators` within your module, use [`ui_transform_teal_data()`] and [`srv_teal_transform_data()`]. #' (For further guidance on managing decorators, refer to `ui_args` and `srv_args` in the vignette documentation.) #' #' See the vignette `vignette("decorate-modules-output", package = "teal")` for additional examples. diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index 9ebe37f105..4dcd515ae3 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -2,13 +2,13 @@ % Please edit documentation in R/module_transform_data.R \name{module_transform_data} \alias{module_transform_data} -\alias{ui_teal_transform_data} -\alias{srv_teal_transform_data} +\alias{ui_transform_teal_data} +\alias{srv_transform_teal_data} \title{Module to transform \code{reactive} \code{teal_data}} \usage{ -ui_teal_transform_data(id, transformators, class = "well") +ui_transform_teal_data(id, transformators, class = "well") -srv_teal_transform_data( +srv_transform_teal_data( id, data, transformators, diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index c89b8ae01c..78803743eb 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -46,7 +46,7 @@ For more details, see the vignette: \code{vignette("data-transform-as-shiny-modu \code{teal_transform_module} also allows developers to modify any object created within \code{\link[teal.data:teal_data]{teal.data::teal_data}}. This means you can use it to customize not only datasets but also tables, listings, and graphs. Some \code{\link{teal_modules}} permit developers to inject custom \code{shiny} modules to enhance displayed outputs. -To manage these \code{decorators} within your module, use \code{\link[=ui_teal_transform_data]{ui_teal_transform_data()}} and \code{\link[=srv_teal_transform_data]{srv_teal_transform_data()}}. +To manage these \code{decorators} within your module, use \code{\link[=ui_transform_teal_data]{ui_transform_teal_data()}} and \code{\link[=srv_teal_transform_data]{srv_teal_transform_data()}}. (For further guidance on managing decorators, refer to \code{ui_args} and \code{srv_args} in the vignette documentation.) See the vignette \code{vignette("decorate-modules-output", package = "teal")} for additional examples. diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 6f46e07894..b4eced1301 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -2051,7 +2051,7 @@ testthat::describe("srv_teal teal_module(s) transformator", { expr = { session$setInputs(`teal_modules-active_tab` = "mod1") session$setInputs(`teal_modules-mod1-module-dataname` = "x1") - session$setInputs(`teal_modules-mod1-module-decorate-decorator_name-transform-text` = "lorem ipsum dolor") + session$setInputs(`teal_modules-mod1-module-decorate-transform_1-transform-text` = "lorem ipsum dolor") session$flushReact() testthat::expect_identical(modules_output$mod1()()[["object"]], "ABC lorem ipsum dolor") diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R index 382778292e..3cd299f646 100644 --- a/tests/testthat/test-shinytest2-decorators.R +++ b/tests/testthat/test-shinytest2-decorators.R @@ -35,7 +35,7 @@ testthat::test_that("e2e: module with decorator UI and output is modified intera input_id <- Reduce( shiny::NS, - c("decorate", "transform_module", "transform", "append_text") + c("decorate", "transform_1", "transform", "append_text") ) testthat::expect_true( diff --git a/tests/testthat/test-teal_transform_module.R b/tests/testthat/test-teal_transform_module.R index 01ba455c70..74c8837acb 100644 --- a/tests/testthat/test-teal_transform_module.R +++ b/tests/testthat/test-teal_transform_module.R @@ -9,7 +9,7 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m ) shiny::testServer( - app = srv_teal_transform_data, + app = srv_transform_teal_data, args = list( id = "test", data = reactive(teal.data::teal_data(data1 = iris, data2 = mtcars)), @@ -32,7 +32,7 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m ) shiny::testServer( - app = srv_teal_transform_data, + app = srv_transform_teal_data, args = list( id = "test", data = reactive(teal.data::teal_data(data1 = iris, data2 = mtcars)), @@ -47,7 +47,7 @@ testthat::describe("make_teal_transform_server produces a valid teal_transform_m }) testthat::test_that( - "ui_teal_transform_dataname and srv_teal_transform_dataname have the same namespace for transform module", + "ui_transform_teal_data and srv_transform_teal_data have the same namespace for transform module", { ttm <- teal_transform_module( ui = function(id) tags$div(id = NS(id, "a_div"), "a div"), @@ -60,12 +60,12 @@ testthat::test_that( ) initial_id <- "a-path-to-an-inner-namespace" - ui <- ui_teal_transform_data(initial_id, ttm) + ui <- ui_transform_teal_data(initial_id, ttm) # Find element that ends in "-a_div" expected_id <- unname(unlist(ui)[grepl(".*-a_div$", unlist(ui))][1]) testServer( - app = srv_teal_transform_data, + app = srv_transform_teal_data, args = list( id = initial_id, data = reactive(within(teal_data(), iris <- iris)), diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 15ed14e7ce..d1bea0e371 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -220,7 +220,7 @@ tm_decorated_plot <- function(label = "module", transformators = list(), decorat selectInput(ns("dataname"), label = "select dataname", choices = NULL), selectInput(ns("x"), label = "select x", choices = NULL), selectInput(ns("y"), label = "select y", choices = NULL), - ui_teal_transform_data(ns("decorate"), transformators = decorators), + ui_transform_teal_data(ns("decorate"), transformators = decorators), plotOutput(ns("plot")), verbatimTextOutput(ns("text")) ) @@ -260,7 +260,7 @@ tm_decorated_plot <- function(label = "module", transformators = list(), decorat ) }) - plot_data_decorated_no_print <- srv_teal_transform_data( + plot_data_decorated_no_print <- srv_transform_teal_data( "decorate", data = plot_data, transformators = decorators @@ -336,7 +336,7 @@ tm_decorated_plot <- function(label = "module", decorators = NULL) { lapply(names(decorators), function(decorator_name) { div( id = ns(paste0("decorate_", decorator_name)), - ui_teal_transform_data( + ui_transform_teal_data( ns(paste0("decorate_", decorator_name)), transformators = decorators[[decorator_name]] ) @@ -398,7 +398,7 @@ tm_decorated_plot <- function(label = "module", decorators = NULL) { input$decorator_choice }) - decorated_data_no_print <- srv_teal_transform_data( + decorated_data_no_print <- srv_transform_teal_data( paste0("decorate_", selected_decorator()), data = plot_data, transformators = decorators[[selected_decorator()]] From 63892329dc047c79e1d7c554ff43b2629934fffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:31:09 +0000 Subject: [PATCH 149/165] fix: e2e test that had the old namespace --- tests/testthat/test-shinytest2-decorators.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-shinytest2-decorators.R b/tests/testthat/test-shinytest2-decorators.R index 3cd299f646..dafd0d7c19 100644 --- a/tests/testthat/test-shinytest2-decorators.R +++ b/tests/testthat/test-shinytest2-decorators.R @@ -95,7 +95,7 @@ testthat::test_that("e2e: module with decorator, where server fails, shows shin input_id <- Reduce( shiny::NS, - c("decorate", "transform_module", "silent_error", "message") + c("decorate", "transform_1", "silent_error", "message") ) testthat::expect_true(app$is_visible(sprintf("#%s-%s", app$active_module_ns(), input_id))) From 347b82a5246b1d2cc31b453de58c38cd8328b042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:43:34 +0000 Subject: [PATCH 150/165] fix: typo on documentation --- R/teal_transform_module.R | 2 +- man/teal_transform_module.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 258017e613..64edb00701 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -20,7 +20,7 @@ #' `teal_transform_module` also allows developers to modify any object created within [`teal.data::teal_data`]. #' This means you can use it to customize not only datasets but also tables, listings, and graphs. #' Some [`teal_modules`] permit developers to inject custom `shiny` modules to enhance displayed outputs. -#' To manage these `decorators` within your module, use [`ui_transform_teal_data()`] and [`srv_teal_transform_data()`]. +#' To manage these `decorators` within your module, use [`ui_transform_teal_data()`] and [`srv_transform_teal_data()`]. #' (For further guidance on managing decorators, refer to `ui_args` and `srv_args` in the vignette documentation.) #' #' See the vignette `vignette("decorate-modules-output", package = "teal")` for additional examples. diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 78803743eb..e2bdf8fbf4 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -46,7 +46,7 @@ For more details, see the vignette: \code{vignette("data-transform-as-shiny-modu \code{teal_transform_module} also allows developers to modify any object created within \code{\link[teal.data:teal_data]{teal.data::teal_data}}. This means you can use it to customize not only datasets but also tables, listings, and graphs. Some \code{\link{teal_modules}} permit developers to inject custom \code{shiny} modules to enhance displayed outputs. -To manage these \code{decorators} within your module, use \code{\link[=ui_transform_teal_data]{ui_transform_teal_data()}} and \code{\link[=srv_teal_transform_data]{srv_teal_transform_data()}}. +To manage these \code{decorators} within your module, use \code{\link[=ui_transform_teal_data]{ui_transform_teal_data()}} and \code{\link[=srv_transform_teal_data]{srv_transform_teal_data()}}. (For further guidance on managing decorators, refer to \code{ui_args} and \code{srv_args} in the vignette documentation.) See the vignette \code{vignette("decorate-modules-output", package = "teal")} for additional examples. From 0a15929d382f7a7c7cfd41fbefb803f97d61f666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Revilla?= <185338939+llrs-roche@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:18:38 +0100 Subject: [PATCH 151/165] Fix examples on decorator vignette (#1423) Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Fixes comments on vignette example on #1357 --- vignettes/decorate-module-output.Rmd | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index d1bea0e371..5e5cdd650e 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -311,7 +311,9 @@ if (interactive()) { ### Example Module -It is possible to pass any number of decorators (n) to a module. The example below demonstrates how to handle a dynamic number of decorators, allowing the user to choose which decorator to apply from a list. This makes the module more flexible and capable of accommodating various customization requirements. +It is possible to pass any number of decorators (n) to a module. +The example below demonstrates how to handle a dynamic number of decorators, allowing the user to choose which decorator to apply from a list. +This makes the module more flexible and capable of accommodating various customization requirements. ```{r} library(ggplot2) @@ -418,6 +420,11 @@ tm_decorated_plot <- function(label = "module", decorators = NULL) { } ``` +By order of the decorator we will: + +1. Change the x axis title +2. Change the y axis title +3. Replace the x axis title ```{r} interactive_decorator_1 <- teal_transform_module( @@ -447,7 +454,7 @@ interactive_decorator_2 <- teal_transform_module( ui = function(id) { ns <- NS(id) div( - textInput(ns("x_axis_title"), "X axis title", value = "x axis 2") + textInput(ns("y_axis_title"), "Y axis title", value = "y axis 1") ) }, server = function(id, data) { @@ -457,9 +464,9 @@ interactive_decorator_2 <- teal_transform_module( within(data(), { plot <- plot + - xlab(title) + ylab(title) }, - title = input$x_axis_title + title = input$y_axis_title ) }) }) @@ -492,6 +499,8 @@ interactive_decorator_3 <- teal_transform_module( ### Application +As you might have noted, the x axis title from the first decorator will be used but won't show up on the resulting plot: + ```{r} app <- init( data = teal_data(iris = iris, mtcars = mtcars), From 6c353fe697f19a485ce8fdec528ee7a790fd4e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Tue, 26 Nov 2024 07:37:11 +0000 Subject: [PATCH 152/165] fix: reactivity on input --- R/teal_transform_module.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/teal_transform_module.R b/R/teal_transform_module.R index 64edb00701..b4a6a9deda 100644 --- a/R/teal_transform_module.R +++ b/R/teal_transform_module.R @@ -198,8 +198,7 @@ make_teal_transform_server <- function(expr) { function(id, data) { moduleServer(id, function(input, output, session) { - list_env <- eventReactive( - input, + list_env <- reactive( lapply(rlang::set_names(names(input)), function(x) input[[x]]) ) From 3a9c68fe78eb7fdc39ad3a783199b06f4925e282 Mon Sep 17 00:00:00 2001 From: m7pr Date: Fri, 29 Nov 2024 14:10:46 +0100 Subject: [PATCH 153/165] example for multiple outputs --- vignettes/decorate-module-output.Rmd | 192 +++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 5e5cdd650e..7089fa40d6 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -520,3 +520,195 @@ if (interactive()) { shinyApp(app$ui, app$server) } ``` + + +# Modules with Multiple Outputs + +In this section, we demonstrate how to extend a teal module to handle multiple outputs and allow separate decoration for each. Specifically, the module will have two outputs +- a `ggplot` plot +- and a table + +We will apply independent decorators to each. + +## Example Module with Two Outputs + +The following module generates both a scatter plot and a summary table. Each of these outputs can be decorated independently using decorators passed to the module: + +```{r} +tm_decorated_plot_table <- function(label = "module with two outputs", decorators = list()) { + checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE) + + module( + label = label, + ui = function(id, decorators) { + ns <- NS(id) + div( + selectInput(ns("dataname"), label = "Select dataset", choices = NULL), + selectInput(ns("x"), label = "Select x-axis", choices = NULL), + selectInput(ns("y"), label = "Select y-axis", choices = NULL), + div( + id = ns("decorate_plot"), + ui_transform_teal_data(ns("decorate_plot"), transformators = decorators$plot) + ), + div( + id = ns("decorate_table"), + ui_transform_teal_data(ns("decorate_table"), transformators = decorators$table) + ), + plotOutput(ns("plot")), + tableOutput(ns("table")), + verbatimTextOutput(ns("text")) + ) + }, + server = function(id, data, decorators) { + moduleServer(id, function(input, output, session) { + observeEvent(data(), { + updateSelectInput(inputId = "dataname", choices = names(data())) + }) + + observeEvent(input$dataname, { + req(input$dataname) + updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) + updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) + }) + + dataname <- reactive(req(input$dataname)) + x <- reactive({ + req(input$x, input$x %in% colnames(data()[[dataname()]])) + input$x + }) + + y <- reactive({ + req(input$y, input$y %in% colnames(data()[[dataname()]])) + input$y + }) + + # Generate plot data + plot_data <- reactive({ + req(dataname(), x(), y()) + within(data(), + { + plot <- ggplot2::ggplot(dataname, aes(x = xvar, y = yvar)) + + ggplot2::geom_point() + }, + dataname = as.name(dataname()), + xvar = as.name(x()), + yvar = as.name(y()) + ) + }) + + # Generate table data + table_data <- reactive({ + req(dataname()) + within(data(), + { + table_data <- dataname %>% + dplyr::summarise(dplyr::across(dplyr::everything(), mean, na.rm = TRUE)) + }, + dataname = as.name(dataname()) + ) + }) + + # Apply decorators to plot + decorated_plot <- srv_transform_teal_data( + "decorate_plot", + data = plot_data, + transformators = decorators$plot + ) + + # Apply decorators to table + decorated_table <- srv_transform_teal_data( + "decorate_table", + data = table_data, + transformators = decorators$table + ) + + output$plot <- renderPlot({ + req(decorated_plot()) + decorated_plot()[["plot"]] + }) + + output$table <- renderTable({ + req(decorated_table()) + decorated_table()[["table_data"]] + }) + + output$text <- renderText({ + plot_code <- teal.code::get_code(req(decorated_plot())) + table_code <- teal.code::get_code(req(decorated_table())) + paste("Plot Code:", plot_code, "\nTable Code:", table_code) + }) + }) + }, + ui_args = list(decorators = decorators), + server_args = list(decorators = decorators) + ) +} + +``` + + +## Example Decorators + +1. **Plot Decorator**: Adds a title to the plot. + +```{r} +plot_decorator <- teal_transform_module( + ui = function(id) { + ns <- NS(id) + div( + textInput(ns("plot_title"), "Plot Title", value = "Default Title") + ) + }, + server = function(id, data) { + moduleServer(id, function(input, output, session) { + reactive({ + req(data()) + within(data(), + { + plot <- plot + + ggtitle(ptitle) + }, + ptitle = input$plot_title + ) + }) + }) + } +) +``` + + +2. **Table Decorator**: Adds row names to the summary table. + +```{r} +table_decorator <- teal_transform_module( + server = make_teal_transform_server( + expression({ + rownames(table_data) <- paste0("Row ", seq_len(nrow(table_data))) + }) + ) +) + +``` + + +## Application + +```{r} +app <- init( + data = teal_data(iris = iris, mtcars = mtcars), + modules = modules( + tm_decorated_plot_table( + "plot_and_table", + decorators = list( + plot = plot_decorator, + table = table_decorator + ) + ) + ) +) + +if (interactive()) { + shinyApp(app$ui, app$server) +} + +``` From 8992555d11b4b52132a761ebf4ea72947f4758b2 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:02:40 +0100 Subject: [PATCH 154/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 7089fa40d6..4bef8a927c 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -587,7 +587,7 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator req(dataname(), x(), y()) within(data(), { - plot <- ggplot2::ggplot(dataname, aes(x = xvar, y = yvar)) + + plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = xvar, y = yvar)) + ggplot2::geom_point() }, dataname = as.name(dataname()), From 6cd966259985041a216b9e418d6380b649ccd490 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:04:35 +0100 Subject: [PATCH 155/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 4bef8a927c..627661a15f 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -655,9 +655,7 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator plot_decorator <- teal_transform_module( ui = function(id) { ns <- NS(id) - div( - textInput(ns("plot_title"), "Plot Title", value = "Default Title") - ) + textInput(ns("plot_title"), "Plot Title", value = "Default Title") }, server = function(id, data) { moduleServer(id, function(input, output, session) { From a91a1cffe7393613fe02f2defba1f6909841e8a9 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:05:28 +0100 Subject: [PATCH 156/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 627661a15f..8ac7dd0b85 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -546,14 +546,8 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator selectInput(ns("dataname"), label = "Select dataset", choices = NULL), selectInput(ns("x"), label = "Select x-axis", choices = NULL), selectInput(ns("y"), label = "Select y-axis", choices = NULL), - div( - id = ns("decorate_plot"), - ui_transform_teal_data(ns("decorate_plot"), transformators = decorators$plot) - ), - div( - id = ns("decorate_table"), - ui_transform_teal_data(ns("decorate_table"), transformators = decorators$table) - ), + ui_transform_teal_data(ns("decorate_plot"), transformators = decorators$plot), + ui_transform_teal_data(ns("decorate_table"), transformators = decorators$table), plotOutput(ns("plot")), tableOutput(ns("table")), verbatimTextOutput(ns("text")) From 2685527ac7465b528dde99d397f3cbea2abb8477 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:06:01 +0100 Subject: [PATCH 157/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 8ac7dd0b85..5446e6227f 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -658,7 +658,7 @@ plot_decorator <- teal_transform_module( within(data(), { plot <- plot + - ggtitle(ptitle) + ggplot2::ggtitle(ptitle) }, ptitle = input$plot_title ) From e04a892bd5800c46caf79702e54c4132b6cc4bfa Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:06:13 +0100 Subject: [PATCH 158/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 5446e6227f..009eee66a0 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -528,7 +528,7 @@ In this section, we demonstrate how to extend a teal module to handle multiple o - a `ggplot` plot - and a table -We will apply independent decorators to each. +We will apply independent decorators to each output. ## Example Module with Two Outputs From 33749d8ba7db47d5ca23585655c15545f5c0bc60 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:06:24 +0100 Subject: [PATCH 159/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 009eee66a0..f9437280e4 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -524,8 +524,9 @@ if (interactive()) { # Modules with Multiple Outputs -In this section, we demonstrate how to extend a teal module to handle multiple outputs and allow separate decoration for each. Specifically, the module will have two outputs -- a `ggplot` plot +In this section, we demonstrate how to extend a teal module to handle multiple outputs and allow separate decoration for each. Specifically, the module will have two outputs: + +- a `ggplot` plot - and a table We will apply independent decorators to each output. From be6fa32fb0b6cc321ab6f04d8989c7569f4e5efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:21:23 +0100 Subject: [PATCH 160/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com> Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 1 + 1 file changed, 1 insertion(+) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index f9437280e4..4bff99fe9a 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -26,6 +26,7 @@ In the context of `teal` applications, decoration is specifically used to modify Preserving the object's class during decoration is essential for compatibility. It ensures that the subsequent "display" logic can seamlessly handle both decorated and non-decorated objects. The decoration process can vary in complexity: + - **Simple Decorations**: Single-step modifications, such as a single method call that does not require additional data. - **Complex Decorations**: Multi-step operations that may involve interdependent transformations, potentially requiring input from dedicated `shiny` UI elements. From 51b09647cdaa6ed8be58b883d5df7b62f5d725b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:25:47 +0100 Subject: [PATCH 161/165] Update vignettes/decorate-module-output.Rmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- vignettes/decorate-module-output.Rmd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 4bff99fe9a..639d4d13d8 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -181,7 +181,8 @@ gg_xlab_decorator <- function(output_name) { ``` Decorator failures are managed by an internal `teal` mechanism called **trigger on success**, which ensures that the `data` -object within the module remains intact. If a decorator fails, it will be ignored, and an appropriate error message will be displayed. +object within the module remains intact. +If a decorator fails, the outputs will not be shown, and an appropriate error message will be displayed. ```{r} failing_decorator <- teal_transform_module( From 2fd5402db6c7f647710a4b863abd897826a3abd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:47:09 +0000 Subject: [PATCH 162/165] docs: vignette cleanup --- vignettes/decorate-module-output.Rmd | 66 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 639d4d13d8..9f048aa653 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -61,6 +61,7 @@ We modify the title and x-axis label of plot: ```{r} library(teal) static_decorator <- teal_transform_module( + label = "Static decorator", server = function(id, data) { moduleServer(id, function(input, output, session) { reactive({ @@ -82,6 +83,7 @@ you can use the [`make_teal_transform_server()`](https://insightsengineering.git ```{r} static_decorator_lang <- teal_transform_module( + label = "Static decorator (language)", server = make_teal_transform_server( expression( plot <- plot + @@ -100,6 +102,7 @@ Note how the input parameters are passed to the [`within`](https://insightsengin ```{r} interactive_decorator <- teal_transform_module( + label = "Interactive decorator", ui = function(id) { ns <- NS(id) div( @@ -129,6 +132,7 @@ This wrapper requires you to use `input` object names directly in the expression ```{r} interactive_decorator_lang <- teal_transform_module( + label = "Interactive decorator (language)", ui = function(id) { ns <- NS(id) div( @@ -155,6 +159,7 @@ In the following example, focus on the `output_name` parameter to see how decora ```{r} gg_xlab_decorator <- function(output_name) { teal_transform_module( + label = "X-axis decorator", ui = function(id) { ns <- NS(id) div( @@ -186,6 +191,7 @@ If a decorator fails, the outputs will not be shown, and an appropriate error me ```{r} failing_decorator <- teal_transform_module( + label = "Failing decorator", ui = function(id) { ns <- NS(id) div( @@ -194,7 +200,7 @@ failing_decorator <- teal_transform_module( }, server = function(id, data) { moduleServer(id, function(input, output, session) { - reactive(stop("This is error")) + reactive(stop("\nThis is an error produced by decorator\n")) }) } ) @@ -357,8 +363,9 @@ tm_decorated_plot <- function(label = "module", decorators = NULL) { updateSelectInput(inputId = "dataname", choices = names(data())) }) - observeEvent(input$dataname, { - req(input$dataname) + dataname <- reactive(req(input$dataname)) + + observeEvent(dataname(), { updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) @@ -374,7 +381,6 @@ tm_decorated_plot <- function(label = "module", decorators = NULL) { }) }) - dataname <- reactive(req(input$dataname)) x <- reactive({ req(input$x, input$x %in% colnames(data()[[dataname()]])) input$x @@ -403,7 +409,7 @@ tm_decorated_plot <- function(label = "module", decorators = NULL) { }) decorated_data_no_print <- srv_transform_teal_data( - paste0("decorate_", selected_decorator()), + sprintf("decorate_%s", selected_decorator()), data = plot_data, transformators = decorators[[selected_decorator()]] ) @@ -430,6 +436,7 @@ By order of the decorator we will: ```{r} interactive_decorator_1 <- teal_transform_module( + label = "Interactive decorator 1", ui = function(id) { ns <- NS(id) div( @@ -453,6 +460,7 @@ interactive_decorator_1 <- teal_transform_module( ) interactive_decorator_2 <- teal_transform_module( + label = "Interactive decorator 2", ui = function(id) { ns <- NS(id) div( @@ -476,6 +484,7 @@ interactive_decorator_2 <- teal_transform_module( ) interactive_decorator_3 <- teal_transform_module( + label = "Interactive decorator 3", ui = function(id) { ns <- NS(id) div( @@ -540,7 +549,7 @@ The following module generates both a scatter plot and a summary table. Each of ```{r} tm_decorated_plot_table <- function(label = "module with two outputs", decorators = list()) { checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE) - + module( label = label, ui = function(id, decorators) { @@ -562,13 +571,13 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator updateSelectInput(inputId = "dataname", choices = names(data())) }) - observeEvent(input$dataname, { - req(input$dataname) + dataname <- reactive(req(input$dataname)) + + observeEvent(dataname(), { updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]])) updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]])) }) - dataname <- reactive(req(input$dataname)) x <- reactive({ req(input$x, input$x %in% colnames(data()[[dataname()]])) input$x @@ -597,11 +606,11 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator table_data <- reactive({ req(dataname()) within(data(), - { - table_data <- dataname %>% - dplyr::summarise(dplyr::across(dplyr::everything(), mean, na.rm = TRUE)) - }, - dataname = as.name(dataname()) + { + table_data <- dataname %>% + dplyr::summarise(dplyr::across(dplyr::everything(), mean, na.rm = TRUE)) + }, + dataname = as.name(dataname()) ) }) @@ -619,20 +628,14 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator transformators = decorators$table ) - output$plot <- renderPlot({ - req(decorated_plot()) - decorated_plot()[["plot"]] - }) + output$plot <- renderPlot(decorated_plot()[["plot"]]) - output$table <- renderTable({ - req(decorated_table()) - decorated_table()[["table_data"]] - }) + output$table <- renderTable(decorated_table()[["table_data"]]) output$text <- renderText({ plot_code <- teal.code::get_code(req(decorated_plot())) table_code <- teal.code::get_code(req(decorated_table())) - paste("Plot Code:", plot_code, "\nTable Code:", table_code) + paste("# Plot Code:", plot_code, "\n\n# Table Code:", table_code) }) }) }, @@ -640,7 +643,6 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator server_args = list(decorators = decorators) ) } - ``` @@ -659,11 +661,10 @@ plot_decorator <- teal_transform_module( reactive({ req(data()) within(data(), - { - plot <- plot + - ggplot2::ggtitle(ptitle) - }, - ptitle = input$plot_title + { + plot <- plot + ggplot2::ggtitle(ptitle) + }, + ptitle = input$plot_title ) }) }) @@ -676,13 +677,15 @@ plot_decorator <- teal_transform_module( ```{r} table_decorator <- teal_transform_module( + label = "Decorate plot", server = make_teal_transform_server( expression({ - rownames(table_data) <- paste0("Row ", seq_len(nrow(table_data))) + table_data$id <- paste0("Row ", seq_len(nrow(table_data))) + table_data <- table_data[, union("id", colnames(table_data))] + colnames(table_data)[1] <- "ID (decorated)" }) ) ) - ``` @@ -705,5 +708,4 @@ app <- init( if (interactive()) { shinyApp(app$ui, app$server) } - ``` From e2f58e9ce6e3498f813905302ab8a6c58760674d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:39:56 +0000 Subject: [PATCH 163/165] pr feedback: simplify --- vignettes/decorate-module-output.Rmd | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 9f048aa653..693acc399b 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -680,9 +680,7 @@ table_decorator <- teal_transform_module( label = "Decorate plot", server = make_teal_transform_server( expression({ - table_data$id <- paste0("Row ", seq_len(nrow(table_data))) - table_data <- table_data[, union("id", colnames(table_data))] - colnames(table_data)[1] <- "ID (decorated)" + table_data[["ID (decorated)"]] <- paste0("Row ", seq_len(nrow(table_data))) }) ) ) From e5acbdcbbc485b56e6532dde6ef0020686f1bedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:42:39 +0000 Subject: [PATCH 164/165] chore: add formatters to lookup references --- .github/workflows/check.yaml | 4 ++++ .github/workflows/docs.yaml | 1 + .github/workflows/release.yaml | 4 ++++ .github/workflows/scheduled.yaml | 2 ++ 4 files changed, 11 insertions(+) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 35c7bb7c53..9734e55bf7 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -51,6 +51,7 @@ jobs: insightsengineering/teal.reporter insightsengineering/teal.widgets insightsengineering/rtables + insightsengineering/formatters insightsengineering/rtables.officer r-cmd-non-cran: @@ -84,6 +85,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters coverage: name: Coverage 📔 @@ -103,6 +105,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters linter: if: github.event_name != 'push' name: SuperLinter 🦸‍♀️ @@ -124,6 +127,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters gitleaks: name: gitleaks 💧 uses: insightsengineering/r.pkg.template/.github/workflows/gitleaks.yaml@main diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index d4fae1f3f3..05754a3ce0 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -52,3 +52,4 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index bddb1c4374..8bb1e979d8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -26,6 +26,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters validation: name: R Package Validation report 📃 needs: release @@ -43,6 +44,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters release: name: Create release 🎉 uses: insightsengineering/r.pkg.template/.github/workflows/release.yaml@main @@ -77,6 +79,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters coverage: name: Coverage 📔 needs: [release, docs] @@ -96,6 +99,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters wasm: name: Build WASM packages 🧑‍🏭 needs: release diff --git a/.github/workflows/scheduled.yaml b/.github/workflows/scheduled.yaml index 4060f5cf99..63d21a4355 100644 --- a/.github/workflows/scheduled.yaml +++ b/.github/workflows/scheduled.yaml @@ -66,6 +66,7 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters rhub: if: > github.event_name == 'schedule' || ( @@ -84,3 +85,4 @@ jobs: insightsengineering/teal.widgets insightsengineering/rtables insightsengineering/rtables.officer + insightsengineering/formatters From c36e7b95ba4f910e9e394eb5da35d6a42bbac67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 29 Nov 2024 16:30:06 +0000 Subject: [PATCH 165/165] chore: minor clean of vignette --- vignettes/decorate-module-output.Rmd | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/vignettes/decorate-module-output.Rmd b/vignettes/decorate-module-output.Rmd index 693acc399b..ade9479644 100644 --- a/vignettes/decorate-module-output.Rmd +++ b/vignettes/decorate-module-output.Rmd @@ -544,7 +544,8 @@ We will apply independent decorators to each output. ## Example Module with Two Outputs -The following module generates both a scatter plot and a summary table. Each of these outputs can be decorated independently using decorators passed to the module: +The following module generates both a scatter plot and a summary table. +Each of these outputs can be decorated independently using decorators passed to the module: ```{r} tm_decorated_plot_table <- function(label = "module with two outputs", decorators = list()) { @@ -607,8 +608,7 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator req(dataname()) within(data(), { - table_data <- dataname %>% - dplyr::summarise(dplyr::across(dplyr::everything(), mean, na.rm = TRUE)) + table_data <- data.frame(Filter(Negate(is.na), lapply(dataname, mean, na.rm = TRUE))) }, dataname = as.name(dataname()) ) @@ -652,9 +652,10 @@ tm_decorated_plot_table <- function(label = "module with two outputs", decorator ```{r} plot_decorator <- teal_transform_module( + label = "Decorate plot", ui = function(id) { ns <- NS(id) - textInput(ns("plot_title"), "Plot Title", value = "Default Title") + textInput(ns("plot_title"), "Plot Title", value = "Decorated Title (editable)") }, server = function(id, data) { moduleServer(id, function(input, output, session) { @@ -662,7 +663,11 @@ plot_decorator <- teal_transform_module( req(data()) within(data(), { - plot <- plot + ggplot2::ggtitle(ptitle) + plot <- plot + ggplot2::ggtitle(ptitle) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = element_text(face = "bold", size = 30, color = "blue") + ) }, ptitle = input$plot_title ) @@ -677,10 +682,11 @@ plot_decorator <- teal_transform_module( ```{r} table_decorator <- teal_transform_module( - label = "Decorate plot", + label = "Decorate table", + ui = function(id) shiny::tags$p("No UI needed for table decorator and could be ommited."), server = make_teal_transform_server( expression({ - table_data[["ID (decorated)"]] <- paste0("Row ", seq_len(nrow(table_data))) + table_data[["Added by decorator"]] <- paste0("Row ", seq_len(nrow(table_data))) }) ) )