From 1ac634ca4840d49c1b8866feca264558cea5d0dd Mon Sep 17 00:00:00 2001
From: Marcin <133694481+m7pr@users.noreply.github.com>
Date: Wed, 4 Dec 2024 15:06:09 +0100
Subject: [PATCH] introduce decorators for `tm_g_barchart_simple` (#1267)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Part of https://github.com/insightsengineering/teal/issues/1371
#### Checklist for final review:
- Has documentation:
- [ ] `` #' @param decorators `r roxygen_decorators_param("tm_X_XXX")`
``
- [ ] Section for decorators
- [ ] In case of multiple decorators, section has pseudo code explaining
how to limit calls
- [ ] Code shows in "Show R code"
- [ ] Reporter shows both the outputs and code
- [ ] Fix order of `assert_decorators` function (`null.ok = TRUE` is
sometimes second argument and looks better at 3^rd)
#### Example
Working Example
```r
devtools::load_all("../teal.reporter")
devtools::load_all("../teal")
devtools::load_all(".")
library(nestcolor)
library(dplyr)
data <- teal_data()
data <- within(data, {
ADSL <- tmc_ex_adsl %>%
mutate(ITTFL = factor("Y") %>%
with_label("Intent-To-Treat Population Flag"))
ADAE <- tmc_ex_adae %>%
filter(!((AETOXGR == 1) & (AESEV == "MILD") & (ARM == "A: Drug X")))
})
join_keys(data) <- default_cdisc_join_keys[names(data)]
ADSL <- data[["ADSL"]]
ADAE <- data[["ADAE"]]
caption_decorator <- function(default_caption = "I am a good decorator", .var_to_replace = "plot") {
teal_transform_module(
label = "Caption",
ui = function(id) shiny::textInput(shiny::NS(id, "footnote"), "Footnote", value = default_caption),
server = make_teal_transform_server(
substitute({
.var_to_replace <- .var_to_replace + ggplot2::labs(caption = footnote)
}, env = list(.var_to_replace = as.name(.var_to_replace)))
)
)
}
head_decorator <- function(default_value = 6, .var_to_replace = "object") {
teal_transform_module(
label = "Head",
ui = function(id) shiny::numericInput(shiny::NS(id, "n"), "N rows", value = default_value),
server = make_teal_transform_server(
substitute({
.var_to_replace <- utils::head(.var_to_replace, n = n)
}, env = list(.var_to_replace = as.name(.var_to_replace)))
)
)
}
app <- init(
data = data,
modules = modules(
tm_g_barchart_simple(
label = "ADAE Analysis",
x = data_extract_spec(
dataname = "ADSL",
select = select_spec(
choices = variable_choices(
ADSL,
c(
"ARM", "ACTARM", "SEX",
"RACE", "ITTFL", "SAFFL", "STRATA2"
)
),
selected = "ACTARM",
multiple = FALSE
)
),
fill = list(
data_extract_spec(
dataname = "ADSL",
select = select_spec(
choices = variable_choices(
ADSL,
c(
"ARM", "ACTARM", "SEX",
"RACE", "ITTFL", "SAFFL", "STRATA2"
)
),
selected = "SEX",
multiple = FALSE
)
),
data_extract_spec(
dataname = "ADAE",
select = select_spec(
choices = variable_choices(ADAE, c("AETOXGR", "AESEV", "AESER")),
selected = NULL,
multiple = FALSE
)
)
),
x_facet = list(
data_extract_spec(
dataname = "ADAE",
select = select_spec(
choices = variable_choices(ADAE, c("AETOXGR", "AESEV", "AESER")),
selected = "AETOXGR",
multiple = FALSE
)
),
data_extract_spec(
dataname = "ADSL",
select = select_spec(
choices = variable_choices(
ADSL,
c(
"ARM", "ACTARM", "SEX",
"RACE", "ITTFL", "SAFFL", "STRATA2"
)
),
selected = NULL,
multiple = FALSE
)
)
),
y_facet = list(
data_extract_spec(
dataname = "ADAE",
select = select_spec(
choices = variable_choices(ADAE, c("AETOXGR", "AESEV", "AESER")),
selected = "AESEV",
multiple = FALSE
)
),
data_extract_spec(
dataname = "ADSL",
select = select_spec(
choices = variable_choices(
ADSL,
c(
"ARM", "ACTARM", "SEX",
"RACE", "ITTFL", "SAFFL", "STRATA2"
)
),
selected = NULL,
multiple = FALSE
)
)
),
decorators = list(plot = caption_decorator('Marcin', 'plot'))
)
)
)
if (interactive()) {
shinyApp(app$ui, app$server)
}
```
---------
Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com>
Co-authored-by: Lluís Revilla
Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com>
Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com>
---
R/tm_g_barchart_simple.R | 38 +++++++++++++++++++++++++++----------
man/tm_g_barchart_simple.Rd | 21 +++++++++++++++++++-
2 files changed, 48 insertions(+), 11 deletions(-)
diff --git a/R/tm_g_barchart_simple.R b/R/tm_g_barchart_simple.R
index ecd8d463a..dfa3d65b7 100644
--- a/R/tm_g_barchart_simple.R
+++ b/R/tm_g_barchart_simple.R
@@ -12,9 +12,18 @@
#' @param x_facet (`data_extract_spec`)\cr row-wise faceting groups.
#' @param y_facet (`data_extract_spec`)\cr column-wise faceting groups.
#' @param plot_options (`list`)\cr list of plot options.
+#' @param decorators `r roxygen_decorators_param("tm_g_barchart_simple")`
#'
#' @inherit module_arguments return seealso
#'
+#' @section Decorating `tm_g_barchart_simple`:
+#'
+#' This module generates the following objects, which can be modified in place using decorators:
+#' - `plot` (`ggplot2`)
+#'
+#' For additional details and examples of decorators, refer to the vignette
+#' `vignette("decorate-modules-output", package = "teal")` or the [`teal_transform_module()`] documentation.
+#'
#' @examplesShinylive
#' library(teal.modules.clinical)
#' interactive <- function() TRUE
@@ -146,7 +155,8 @@ tm_g_barchart_simple <- function(x = NULL,
plot_width = NULL,
pre_output = NULL,
post_output = NULL,
- ggplot2_args = teal.widgets::ggplot2_args()) {
+ ggplot2_args = teal.widgets::ggplot2_args(),
+ decorators = NULL) {
message("Initializing tm_g_barchart_simple")
checkmate::assert_string(label)
checkmate::assert_list(plot_options, null.ok = TRUE)
@@ -171,6 +181,8 @@ tm_g_barchart_simple <- function(x = NULL,
checkmate::assert_class(pre_output, classes = "shiny.tag", null.ok = TRUE)
checkmate::assert_class(post_output, classes = "shiny.tag", null.ok = TRUE)
checkmate::assert_class(ggplot2_args, "ggplot2_args")
+ decorators <- normalize_decorators(decorators)
+ assert_decorators(decorators, names = "plot", null.ok = TRUE)
plot_options <- utils::modifyList(
list(stacked = FALSE), # default
@@ -190,7 +202,8 @@ tm_g_barchart_simple <- function(x = NULL,
y_facet = y_facet,
plot_height = plot_height,
plot_width = plot_width,
- ggplot2_args = ggplot2_args
+ ggplot2_args = ggplot2_args,
+ decorators = decorators
),
datanames = "all"
)
@@ -249,6 +262,7 @@ ui_g_barchart_simple <- function(id, ...) {
is_single_dataset = is_single_dataset_value
)
},
+ ui_decorate_teal_data(ns("decorator"), decorators = select_decorators(args$decorators, "plot")),
teal.widgets::panel_group(
teal.widgets::panel_item(
"Additional plot settings",
@@ -336,7 +350,8 @@ srv_g_barchart_simple <- function(id,
y_facet,
plot_height,
plot_width,
- ggplot2_args) {
+ ggplot2_args,
+ decorators) {
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter")
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI")
checkmate::assert_class(data, "reactive")
@@ -506,13 +521,16 @@ srv_g_barchart_simple <- function(id,
)
)) %>%
teal.code::eval_code(code = plot_call)
-
- # explicitly calling print on the plot inside the qenv evaluates
- # the ggplot call and therefore catches errors
- teal.code::eval_code(all_q, code = quote(print(plot)))
})
- plot_r <- reactive(all_q()[["plot"]])
+ decorated_all_q_code <- srv_decorate_teal_data(
+ "decorator",
+ data = all_q,
+ decorators = select_decorators(decorators, "plot"),
+ expr = print(plot)
+ )
+
+ plot_r <- reactive(decorated_all_q_code()[["plot"]])
output$table <- renderTable({
req(iv_r()$is_valid())
@@ -550,7 +568,7 @@ srv_g_barchart_simple <- function(id,
teal.widgets::verbatim_popup_srv(
id = "rcode",
- verbatim_content = reactive(teal.code::get_code(all_q())),
+ verbatim_content = reactive(teal.code::get_code(req(decorated_all_q_code()))),
title = "Bar Chart"
)
@@ -569,7 +587,7 @@ srv_g_barchart_simple <- function(id,
card$append_text("Comment", "header3")
card$append_text(comment)
}
- card$append_src(teal.code::get_code(all_q()))
+ card$append_src(teal.code::get_code(req(decorated_all_q_code())))
card
}
teal.reporter::simple_reporter_srv("simple_reporter", reporter = reporter, card_fun = card_fun)
diff --git a/man/tm_g_barchart_simple.Rd b/man/tm_g_barchart_simple.Rd
index f1e92bc46..1a436a496 100644
--- a/man/tm_g_barchart_simple.Rd
+++ b/man/tm_g_barchart_simple.Rd
@@ -15,7 +15,8 @@ tm_g_barchart_simple(
plot_width = NULL,
pre_output = NULL,
post_output = NULL,
- ggplot2_args = teal.widgets::ggplot2_args()
+ ggplot2_args = teal.widgets::ggplot2_args(),
+ decorators = NULL
)
}
\arguments{
@@ -47,6 +48,12 @@ For example the \code{\link[shiny:helpText]{shiny::helpText()}} elements are use
for the module plot. The argument is merged with option \code{teal.ggplot2_args} and with default module arguments
(hard coded in the module body).
For more details, see the vignette: \code{vignette("custom-ggplot2-arguments", package = "teal.widgets")}.}
+
+\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}, named \code{list} of \code{teal_transform_module} or \code{NULL}) optional, if not \code{NULL}, decorator for tables or plots included in the module. When a named list of \code{teal_transform_module}, the decorators are applied to the respective output objects.
+
+Otherwise, the decorators are applied to all objects, which is equivalent as using the name \code{default}.
+
+See section "Decorating \code{tm_g_barchart_simple}" below for more details.}
}
\value{
a \code{teal_module} object.
@@ -58,6 +65,18 @@ This module produces a \code{\link[ggplot2:ggplot]{ggplot2::ggplot()}} type bar
Categories can be defined up to four levels deep and are defined through the \code{x}, \code{fill},
\code{x_facet}, and \code{y_facet} parameters. Any parameters set to \code{NULL} (default) are ignored.
}
+\section{Decorating \code{tm_g_barchart_simple}}{
+
+
+This module generates the following objects, which can be modified in place using decorators:
+\itemize{
+\item \code{plot} (\code{ggplot2})
+}
+
+For additional details and examples of decorators, refer to the vignette
+\code{vignette("decorate-modules-output", package = "teal")} or the \code{\link[=teal_transform_module]{teal_transform_module()}} documentation.
+}
+
\examples{
library(nestcolor)
library(dplyr)