Skip to content

Commit 8cde224

Browse files
authored
Merge branch 'main' into generalize-the-TealAppDriver@main
2 parents 8c413a7 + 04409d7 commit 8cde224

19 files changed

+754
-277
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Type: Package
22
Package: teal
33
Title: Exploratory Web Apps for Analyzing Clinical Trials Data
4-
Version: 1.0.0.9015
5-
Date: 2025-10-15
4+
Version: 1.0.0.9020
5+
Date: 2025-10-28
66
Authors@R: c(
77
person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"),
88
comment = c(ORCID = "0000-0001-9533-457X")),

NAMESPACE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ S3method(.srv_teal_module,teal_modules)
66
S3method(.ui_teal_module,default)
77
S3method(.ui_teal_module,teal_module)
88
S3method(.ui_teal_module,teal_modules)
9+
S3method(after,default)
10+
S3method(after,teal_module)
11+
S3method(after,teal_modules)
912
S3method(c,teal_slices)
1013
S3method(format,teal_module)
1114
S3method(format,teal_modules)

NEWS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# teal 1.0.0.9015
1+
# teal 1.0.0.9020
22

33
### New features
44

R/after.R

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,123 @@
11
#' Executes modifications to the result of a module
22
#'
3-
#' Primarily used to modify the output object of module to change the containing
4-
#' report.
5-
#' @param x (`teal_module`).
6-
#' @param ui (`function(id, elem, ...)`) function to receive output (`shiny.tag`) from `x$ui`
7-
#' @param server (`function(input, output, session, data, ...)`) function to receive output data from `x$server`
8-
#' @param ... additional argument passed to `ui` and `server` by matching their formals names.
9-
#' @return A `teal_report` object with the result of the server function.
3+
#' @description
4+
#' `r lifecycle::badge("experimental")`
5+
#'
6+
#' Exported to be able to use methods not to be used directly by module-developers or app-users.
7+
#' Primarily used to modify the output object of module.
8+
#' @seealso [disable_src()], [disable_report()]
9+
#' @param x (`teal_module` or `teal_modules`).
10+
#' @param server (`function(input, output, session, data, ...)`) function to receive output data from `tm$server`.
11+
#' Must return data
12+
#' @param ... Additional arguments passed to the server wrapper function by matching their formal names.
13+
#' @return A `teal_module` or `teal_modules` object with a wrapped server.
1014
#' @export
15+
#' @keywords internal
16+
#' @examples
17+
#' library("teal.reporter")
18+
#' hide_code <- function(input, output, session, data) {
19+
#' teal_card(data) <- Filter(function(x) !inherits(x, "code_chunk"), teal_card(data))
20+
#' data
21+
#' }
22+
#' app <- init(
23+
#' data = teal_data(IRIS = iris, MTCARS = mtcars),
24+
#' modules = example_module() |>
25+
#' after(server = hide_code)
26+
#' )
27+
#'
28+
#' if (interactive()) {
29+
#' runApp(app)
30+
#' }
1131
after <- function(x,
12-
ui = function(id, elem) elem,
1332
server = function(input, output, session, data) data,
1433
...) {
34+
UseMethod("after")
35+
}
36+
37+
38+
#' @export
39+
after.default <- function(x,
40+
server = function(input, output, session, data) data,
41+
...) {
42+
stop("`after` is only implemented for `teal_module` and `teal_modules` objects.")
43+
}
44+
45+
#' @export
46+
after.teal_modules <- function(x,
47+
server = function(input, output, session, data) data,
48+
...) {
49+
x$children <- lapply(x$children, after,
50+
server = server, ...
51+
)
52+
x
53+
}
54+
55+
#' @export
56+
after.teal_module <- function(x,
57+
server = function(input, output, session, data) data,
58+
...) {
1559
checkmate::assert_multi_class(x, "teal_module")
16-
if (!is.function(ui) || !all(names(formals(ui)) %in% c("id", "elem"))) {
17-
stop("ui should be a function of id and elem")
18-
}
19-
if (!is.function(server) || !all(names(formals(server)) %in% c("input", "output", "session", "data"))) {
20-
stop("server should be a function of `input` and `output`, `session`, `data`")
60+
61+
names_srv <- names(formals(server))
62+
args_callModule <- c("input", "output", "session", "data") # nolint object_name_linter.
63+
if (!is.function(server) || !(!all(identical(names_srv, c("id", "data"))) || !all(names_srv %in% args_callModule))) {
64+
stop("`server` must be a function whose arguments are a subset of c('input', 'output', 'session', 'data'), or exactly 'id' AND 'data'.") # nolint line_length_lint
2165
}
2266

2367
additional_args <- list(...)
24-
new_x <- x # because overwriting x$ui/server will cause infinite recursion
25-
new_x$ui <- .after_ui(x$ui, ui, additional_args)
26-
new_x$server <- .after_server(x$server, server, additional_args)
27-
new_x
68+
x$ui <- after_ui(x$ui, function(id, elem) {
69+
elem
70+
}, additional_args)
71+
x$server <- after_srv(x$server, server, additional_args)
72+
x
2873
}
2974

30-
.after_ui <- function(x, y, additional_args) {
31-
# add `_`-prefix to make sure objects are not masked in the wrapper functions
32-
`_x` <- x # nolint: object_name.
33-
`_y` <- y # nolint: object_name.
34-
new_x <- function(id, ...) {
75+
after_ui <- function(old, new, additional_args) {
76+
new_ui <- function(id, ...) {
3577
original_args <- as.list(environment())
36-
if ("..." %in% names(formals(`_x`))) {
78+
if ("..." %in% names(formals(old))) {
3779
original_args <- c(original_args, list(...))
3880
}
3981
ns <- NS(id)
4082
original_args$id <- ns("wrapped")
41-
original_out <- do.call(`_x`, original_args, quote = TRUE)
83+
original_out <- do.call(old, original_args, quote = TRUE)
4284

4385
wrapper_args <- c(
4486
additional_args,
4587
list(id = ns("wrapper"), elem = original_out)
4688
)
47-
do.call(`_y`, args = wrapper_args[names(formals(`_y`))])
89+
do.call(new, args = wrapper_args[names(formals(new))])
4890
}
49-
formals(new_x) <- formals(x)
50-
new_x
91+
formals(new_ui) <- formals(old)
92+
new_ui
5193
}
5294

53-
.after_server <- function(x, y, additional_args) {
54-
# add `_`-prefix to make sure objects are not masked in the wrapper functions
55-
`_x` <- x # nolint: object_name.
56-
`_y` <- y # nolint: object_name.
57-
new_x <- function(id, ...) {
95+
after_srv <- function(old, new, additional_args) {
96+
new_srv <- function(id, ...) {
5897
original_args <- as.list(environment())
5998
original_args$id <- "wrapped"
60-
if ("..." %in% names(formals(`_x`))) {
99+
if ("..." %in% names(formals(old))) {
61100
original_args <- c(original_args, list(...))
62101
}
63102
moduleServer(id, function(input, output, session) {
64-
original_out <- if (all(c("input", "output", "session") %in% names(formals(`_x`)))) {
65-
original_args$module <- `_x`
103+
original_out <- if (all(c("input", "output", "session") %in% names(formals(old)))) {
104+
original_args$module <- old
66105
do.call(shiny::callModule, args = original_args)
67106
} else {
68-
do.call(`_x`, original_args)
107+
do.call(old, original_args)
69108
}
70-
original_out_r <- reactive(
71-
if (is.reactive(original_out)) {
72-
original_out()
73-
} else {
74-
original_out
75-
}
76-
)
109+
77110
wrapper_args <- utils::modifyList(
78111
additional_args,
79112
list(id = "wrapper", input = input, output = output, session = session)
80113
)
81114
reactive({
82-
req(original_out_r())
115+
req(original_out())
83116
wrapper_args$data <- original_out()
84-
do.call(`_y`, wrapper_args[names(formals(`_y`))], quote = TRUE)
117+
do.call(new, wrapper_args[names(formals(new))], quote = TRUE)
85118
})
86119
})
87120
}
88-
formals(new_x) <- formals(x)
89-
new_x
121+
formals(new_srv) <- formals(old)
122+
new_srv
90123
}

R/module_nested_tabs.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ srv_teal_module <- function(id,
502502
ignoreNULL = TRUE,
503503
handlerExpr = {
504504
out <- .call_teal_module(modules, datasets, module_teal_data, reporter)
505-
srv_add_reporter("add_reporter_wrapper", module_out = out, reporter = reporter)
505+
srv_add_reporter("add_reporter_wrapper", module_out = out, reporter = reporter, module_label = modules$label)
506506
srv_source_code("source_code_wrapper", out)
507507
module_out(out)
508508
}

R/module_source_code.R

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ srv_source_code <- function(id, module_out) {
5454
"The source code functionality is disabled for this module."
5555
} else if (inherits(mod_out_r(), "error")) {
5656
"The module returned an error, check it for errors."
57-
} else if (is.null(code_out())) {
57+
} else if (!nzchar(code_out())) {
5858
"The module does not support source code functionality."
5959
}
6060
})
@@ -65,7 +65,7 @@ srv_source_code <- function(id, module_out) {
6565
if (length(reason)) {
6666
icon <- if (grepl("error", reason)) "exclamation-triangle-fill" else "info-circle-fill"
6767
shiny::div(
68-
class = if (grepl("error", reason)) "text-danger" else "text-info",
68+
class = if (grepl("error", reason)) "text-warning" else "text-info",
6969
style = "padding-top: 0.5em;",
7070
bsicons::bs_icon(name = icon),
7171
reason
@@ -92,7 +92,13 @@ srv_source_code <- function(id, module_out) {
9292
#' @param x (`teal_module`) a `teal_module` object.
9393
#' @return modified data object that indicates that it should not show the "Show R Code"
9494
#' button in the UI.
95+
#' @seealso [disable_report()]
96+
#' @examplesShinylive
97+
#' library(teal)
98+
#' interactive <- function() TRUE
99+
#' {{ next_example }}
95100
#' @examples
101+
#' # Disabling source on a single module
96102
#' app <- init(
97103
#' data = within(teal_data(), iris <- iris),
98104
#' modules = modules(
@@ -102,6 +108,23 @@ srv_source_code <- function(id, module_out) {
102108
#' if (interactive()) {
103109
#' shinyApp(app$ui, app$server)
104110
#' }
111+
#' @examplesShinylive
112+
#' library(teal)
113+
#' interactive <- function() TRUE
114+
#' {{ next_example }}
115+
#' @examples
116+
#' # Multiple modules
117+
#' app <- init(
118+
#' data = within(teal_data(), iris <- iris),
119+
#' modules = modules(
120+
#' example_module(label = "example 1"),
121+
#' example_module(label = "example 2")
122+
#' ) |> disable_src()
123+
#' )
124+
#'
125+
#' if (interactive()) {
126+
#' shinyApp(app$ui, app$server)
127+
#' }
105128
#' @export
106129
disable_src <- function(x) {
107130
checkmate::assert_multi_class(x, c("teal_module", "teal_modules"))

R/module_teal_reporter.R

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ ui_add_reporter <- function(id) {
120120
}
121121

122122
#' @noRd
123-
srv_add_reporter <- function(id, module_out, reporter) {
123+
srv_add_reporter <- function(id, module_out, reporter, module_label = "") {
124124
checkmate::assert_string(id)
125125
checkmate::assert_class(reporter, "Reporter", null.ok = TRUE)
126+
checkmate::assert_string(module_label)
126127
if (is.null(reporter)) {
127128
return(FALSE)
128129
} # early exit
@@ -178,7 +179,12 @@ srv_add_reporter <- function(id, module_out, reporter) {
178179
shinyjs::toggleState("report_add_wrapper", condition = is.null(reason_r()))
179180
})
180181

181-
teal.reporter::add_card_button_srv("reporter_add", reporter = reporter, card_fun = doc_out)
182+
teal.reporter::add_card_button_srv(
183+
"reporter_add",
184+
reporter = reporter,
185+
card_fun = doc_out,
186+
card_title = module_label
187+
)
182188
}
183189
})
184190
}
@@ -189,8 +195,14 @@ srv_add_reporter <- function(id, module_out, reporter) {
189195
#' to the report previewer.
190196
#' @param x (`teal_module`) a `teal_module` object.
191197
#' @return modified data object that indicates that it should disable the reporter functionality.
198+
#' @seealso [disable_src()]
192199
#' @export
200+
#' @examplesShinylive
201+
#' library(teal)
202+
#' interactive <- function() TRUE
203+
#' {{ next_example }}
193204
#' @examples
205+
#' # Disabling report on a single module
194206
#' app <- init(
195207
#' data = within(teal_data(), iris <- iris),
196208
#' modules = modules(
@@ -200,6 +212,39 @@ srv_add_reporter <- function(id, module_out, reporter) {
200212
#' if (interactive()) {
201213
#' shinyApp(app$ui, app$server)
202214
#' }
215+
#'
216+
#' @examplesShinylive
217+
#' library(teal)
218+
#' interactive <- function() TRUE
219+
#' {{ next_example }}
220+
#' @examples
221+
#' # Disabling report on multiple modules
222+
#' app <- init(
223+
#' data = within(teal_data(), iris <- iris),
224+
#' modules = modules(
225+
#' example_module(label = "example 1"),
226+
#' example_module(label = "example 2")
227+
#' ) |> disable_report()
228+
#' )
229+
#' if (interactive()) {
230+
#' shinyApp(app$ui, app$server)
231+
#' }
232+
#' @examplesShinylive
233+
#' library(teal)
234+
#' interactive <- function() TRUE
235+
#' {{ next_example }}
236+
#' @examples
237+
#' # Disabling reporting for the app
238+
#' app <- init(
239+
#' data = within(teal_data(), iris <- iris),
240+
#' modules = modules(
241+
#' example_module(label = "example teal module")
242+
#' ),
243+
#' reporter = NULL
244+
#' )
245+
#' if (interactive()) {
246+
#' shinyApp(app$ui, app$server)
247+
#' }
203248
disable_report <- function(x) {
204249
checkmate::assert_multi_class(x, c("teal_module", "teal_modules"))
205250
after(x, server = function(data) {

_pkgdown.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ reference:
133133
- disable_report
134134
- title: Modifying output of modules
135135
contents:
136-
- after
137136
- disable_src
138137
- disable_report
139138
- title: Landing popup

0 commit comments

Comments
 (0)