From 95f11f328b351cd2a8f31d3a8893bc1d5f4787f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?=
<211358+averissimo@users.noreply.github.com>
Date: Wed, 11 Dec 2024 14:39:34 +0000
Subject: [PATCH] Adds decorators to `tm_t_pp_basic_info` (#1282)
Part of https://github.com/insightsengineering/teal/issues/1371
Working example
```r
# Load packages
pkgload::load_all("../teal.modules.clinical", export_all = FALSE)
# Example below
insert_rlisting_footer_decorator <- function(default_caption = "I am a good new footer", .var_to_replace = "table") {
teal_transform_module(
label = "New row",
ui = function(id) shiny::textInput(shiny::NS(id, "new_footer"), "New footer", value = default_caption),
server = make_teal_transform_server(
substitute({
rlistings::main_footer(.var_to_replace) <- new_footer
}, env = list(.var_to_replace = as.name(.var_to_replace)))
)
)
}
data <- teal_data()
data <- within(data, {
ADSL <- tmc_ex_adsl
})
join_keys(data) <- default_cdisc_join_keys[names(data)]
ADSL <- data[["ADSL"]]
init(
data = data,
modules = modules(
tm_t_pp_basic_info(
label = "Basic Info",
dataname = "ADSL",
patient_col = "USUBJID",
vars = choices_selected(choices = variable_choices(ADSL), selected = c("ARM", "AGE", "SEX", "COUNTRY", "RACE", "EOSSTT")),
decorators = list(
table = insert_rlisting_footer_decorator(.var_to_replace = "table")
)
)
)
) |> shiny::runApp()
```
---
R/tm_t_pp_basic_info.R | 44 ++++++++++++++++++++++++++++-----------
man/tm_t_pp_basic_info.Rd | 24 ++++++++++++++++++++-
2 files changed, 55 insertions(+), 13 deletions(-)
diff --git a/R/tm_t_pp_basic_info.R b/R/tm_t_pp_basic_info.R
index 7f61d4148..7a6f2bcfa 100644
--- a/R/tm_t_pp_basic_info.R
+++ b/R/tm_t_pp_basic_info.R
@@ -36,13 +36,11 @@ template_basic_info <- function(dataname = "ANL",
dplyr::select(var, key, value) %>%
dplyr::rename(` ` = var, ` ` = key, ` ` = value)
- result <- rlistings::as_listing(
+ table <- rlistings::as_listing(
result,
default_formatting = list(all = fmt_config(align = "left"))
)
- main_title(result) <- paste("Patient ID:", patient_id)
-
- result
+ main_title(table) <- paste("Patient ID:", patient_id)
}, env = list(
dataname = as.name(dataname),
vars = vars,
@@ -66,6 +64,14 @@ template_basic_info <- function(dataname = "ANL",
#'
#' @inherit module_arguments return
#'
+#' @section Decorating Module:
+#'
+#' This module generates the following objects, which can be modified in place using decorators:
+#' - `table` (`listing_df` - output of `rlistings::as_listing`)
+#'
+#' 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
@@ -104,7 +110,8 @@ tm_t_pp_basic_info <- function(label,
patient_col = "USUBJID",
vars = NULL,
pre_output = NULL,
- post_output = NULL) {
+ post_output = NULL,
+ decorators = NULL) {
message("Initializing tm_t_pp_basic_info")
checkmate::assert_string(label)
checkmate::assert_string(dataname)
@@ -112,6 +119,8 @@ tm_t_pp_basic_info <- function(label,
checkmate::assert_class(vars, "choices_selected", null.ok = TRUE)
checkmate::assert_class(pre_output, classes = "shiny.tag", null.ok = TRUE)
checkmate::assert_class(post_output, classes = "shiny.tag", null.ok = TRUE)
+ decorators <- normalize_decorators(decorators)
+ assert_decorators(decorators, null.ok = TRUE, "table")
args <- as.list(environment())
data_extract_list <- list(
@@ -128,7 +137,8 @@ tm_t_pp_basic_info <- function(label,
list(
dataname = dataname,
label = label,
- patient_col = patient_col
+ patient_col = patient_col,
+ decorators = decorators
)
),
datanames = dataname
@@ -163,7 +173,8 @@ ui_t_basic_info <- function(id, ...) {
label = "Select variable:",
data_extract_spec = ui_args$vars,
is_single_dataset = is_single_dataset_value
- )
+ ),
+ ui_decorate_teal_data(ns("decorator"), decorators = select_decorators(ui_args$decorators, "table")),
),
forms = tagList(
teal.widgets::verbatim_popup_ui(ns("rcode"), button_label = "Show R code")
@@ -181,7 +192,8 @@ srv_t_basic_info <- function(id,
dataname,
patient_col,
vars,
- label) {
+ label,
+ decorators) {
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter")
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI")
checkmate::assert_class(data, "reactive")
@@ -265,11 +277,18 @@ srv_t_basic_info <- function(id,
teal.code::eval_code(as.expression(unlist(my_calls)))
})
+ decorated_table_q <- srv_decorate_teal_data(
+ id = "decorator",
+ data = all_q,
+ decorators = select_decorators(decorators, "table"),
+ expr = table
+ )
+
output$title <- renderText({
paste("
Patient ID:", all_q()[["pt_id"]], "
")
})
- table_r <- reactive(all_q()[["result"]])
+ table_r <- reactive(decorated_table_q()[["table"]])
output$basic_info_table <- DT::renderDataTable(
expr = table_r(),
@@ -278,9 +297,10 @@ srv_t_basic_info <- function(id,
)
)
+ source_code_r <- reactive(teal.code::get_code(req(decorated_table_q())))
teal.widgets::verbatim_popup_srv(
id = "rcode",
- verbatim_content = reactive(teal.code::get_code(all_q())),
+ verbatim_content = source_code_r,
title = label
)
@@ -294,12 +314,12 @@ srv_t_basic_info <- function(id,
filter_panel_api = filter_panel_api
)
card$append_text("Table", "header3")
- card$append_table(table_r())
+ card$append_table(decorated_table_q()[["table"]])
if (!comment == "") {
card$append_text("Comment", "header3")
card$append_text(comment)
}
- card$append_src(teal.code::get_code(all_q()))
+ card$append_src(source_code_r())
card
}
teal.reporter::simple_reporter_srv("simple_reporter", reporter = reporter, card_fun = card_fun)
diff --git a/man/tm_t_pp_basic_info.Rd b/man/tm_t_pp_basic_info.Rd
index 9138d5e51..817a27fdd 100644
--- a/man/tm_t_pp_basic_info.Rd
+++ b/man/tm_t_pp_basic_info.Rd
@@ -10,7 +10,8 @@ tm_t_pp_basic_info(
patient_col = "USUBJID",
vars = NULL,
pre_output = NULL,
- post_output = NULL
+ post_output = NULL,
+ decorators = NULL
)
}
\arguments{
@@ -28,6 +29,15 @@ For example a title.}
\item{post_output}{(\code{shiny.tag}) optional,\cr with text placed after the output to put the output into context.
For example the \code{\link[shiny:helpText]{shiny::helpText()}} elements are useful.}
+
+\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 Module" below for more details.}
}
\value{
a \code{teal_module} object.
@@ -35,6 +45,18 @@ a \code{teal_module} object.
\description{
This module produces a patient profile basic info report using ADaM datasets.
}
+\section{Decorating Module}{
+
+
+This module generates the following objects, which can be modified in place using decorators:
+\itemize{
+\item \code{table} (\code{listing_df} - output of \code{rlistings::as_listing})
+}
+
+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{
data <- teal_data()
data <- within(data, {