From b410a2fbb3c2eb082ecdcfa738a2a45eb95c6262 Mon Sep 17 00:00:00 2001 From: vedhav Date: Tue, 7 Jan 2025 13:08:05 +0530 Subject: [PATCH 01/50] feat: add teal app modifiers and deprecate init args --- NAMESPACE | 4 + R/init.R | 243 +++++++++++++----- R/landing_popup_module.R | 37 +-- man/add_custom_server.Rd | 26 ++ man/add_footer.Rd | 23 ++ man/add_header.Rd | 23 ++ man/add_title.Rd | 23 ++ man/init.Rd | 27 +- man/landing_popup_module.Rd | 36 +-- man/module_teal.Rd | 15 +- man/module_teal_with_splash.Rd | 15 +- .../testthat/test-shinytest2-landing_popup.R | 32 +-- 12 files changed, 364 insertions(+), 140 deletions(-) create mode 100644 man/add_custom_server.Rd create mode 100644 man/add_footer.Rd create mode 100644 man/add_header.Rd create mode 100644 man/add_title.Rd diff --git a/NAMESPACE b/NAMESPACE index 3e156a1e6f..cb96ee7bda 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,6 +14,10 @@ S3method(ui_teal_module,teal_module) S3method(ui_teal_module,teal_modules) S3method(within,teal_data_module) export(TealReportCard) +export(add_custom_server) +export(add_footer) +export(add_header) +export(add_title) export(as.teal_slices) export(as_tdata) export(build_app_title) diff --git a/R/init.R b/R/init.R index 24ca5e0ef1..35b186ae4f 100644 --- a/R/init.R +++ b/R/init.R @@ -20,19 +20,20 @@ #' more details. #' @param filter (`teal_slices`) Optionally, #' specifies the initial filter using [teal_slices()]. -#' @param title (`shiny.tag` or `character(1)`) Optionally, +#' @param title (`shiny.tag` or `character(1)`) `r lifecycle::badge("deprecated")` Optionally, #' the browser window title. Defaults to a title "teal app" with the icon of NEST. #' Can be created using the `build_app_title()` or #' by passing a valid `shiny.tag` which is a head tag with title and link tag. -#' @param header (`shiny.tag` or `character(1)`) Optionally, +#' This parameter is deprecated. Use `add_title()` on the teal app object instead. +#' @param header (`shiny.tag` or `character(1)`) `r lifecycle::badge("deprecated")` Optionally, #' the header of the app. -#' @param footer (`shiny.tag` or `character(1)`) Optionally, +#' This parameter is deprecated. Use `add_header()` on the teal app object instead. +#' @param footer (`shiny.tag` or `character(1)`) `r lifecycle::badge("deprecated")` Optionally, #' the footer of the app. +#' This parameter is deprecated. Use `add_footer()` on the teal app object instead. #' @param id (`character`) Optionally, #' a string specifying the `shiny` module id in cases it is used as a `shiny` module #' rather than a standalone `shiny` app. This is a legacy feature. -#' @param landing_popup (`teal_module_landing`) Optionally, -#' a `landing_popup_module` to show up as soon as the teal app is initialized. #' #' @return Named list containing server and UI functions. #' @@ -94,17 +95,15 @@ init <- function(data, modules, filter = teal_slices(), - title = build_app_title(), - header = tags$p(), - footer = tags$p(), - id = character(0), - landing_popup = NULL) { + title = NULL, + header = NULL, + footer = NULL, + id = character(0)) { logger::log_debug("init initializing teal app with: data ('{ class(data) }').") # argument checking (independent) ## `data` checkmate::assert_multi_class(data, c("teal_data", "teal_data_module")) - checkmate::assert_class(landing_popup, "teal_module_landing", null.ok = TRUE) ## `modules` checkmate::assert( @@ -121,46 +120,11 @@ init <- function(data, ## `filter` checkmate::assert_class(filter, "teal_slices") - - ## all other arguments - checkmate::assert( - .var.name = "title", - checkmate::check_string(title), - checkmate::check_multi_class(title, c("shiny.tag", "shiny.tag.list", "html")) - ) - checkmate::assert( - .var.name = "header", - checkmate::check_string(header), - checkmate::check_multi_class(header, c("shiny.tag", "shiny.tag.list", "html")) - ) - checkmate::assert( - .var.name = "footer", - checkmate::check_string(footer), - checkmate::check_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html")) - ) checkmate::assert_character(id, max.len = 1, any.missing = FALSE) # log teal.logger::log_system_info() - # argument transformations - ## `modules` - landing module - landing <- extract_module(modules, "teal_module_landing") - if (length(landing) == 1L) { - landing_popup <- landing[[1L]] - modules <- drop_module(modules, "teal_module_landing") - lifecycle::deprecate_soft( - when = "0.15.3", - what = "landing_popup_module()", - details = paste( - "Pass `landing_popup_module` to the `landing_popup` argument of the `init` ", - "instead of wrapping it into `modules()` and passing to the `modules` argument" - ) - ) - } else if (length(landing) > 1L) { - stop("Only one `landing_popup_module` can be used.") - } - ## `filter` - set app_id attribute unless present (when restoring bookmark) if (is.null(attr(filter, "app_id", exact = TRUE))) attr(filter, "app_id") <- create_app_id(data, modules) @@ -223,26 +187,181 @@ init <- function(data, } ns <- NS(id) - # Note: UI must be a function to support bookmarking. - res <- list( - ui = function(request) { - ui_teal( - id = ns("teal"), - modules = modules, - title = title, - header = header, - footer = footer + res <- new.env(parent = emptyenv()) + res$title <- build_app_title() + res$header <- tags$p() + res$footer <- tags$p() + + if (!is.null(title)) { + checkmate::assert( + .var.name = "title", + checkmate::check_string(title), + checkmate::check_multi_class(title, c("shiny.tag", "shiny.tag.list", "html")) + ) + res$title <- title + lifecycle::deprecate_soft( + when = "0.15.3", + what = "init(title)", + details = paste( + "Use `add_title()` on the teal app object instead." + ) + ) + } + if (!is.null(header)) { + checkmate::assert( + .var.name = "header", + checkmate::check_string(header), + checkmate::check_multi_class(header, c("shiny.tag", "shiny.tag.list", "html")) + ) + res$header <- header + lifecycle::deprecate_soft( + when = "0.15.3", + what = "init(header)", + details = paste( + "Use `add_header()` on the teal app object instead." + ) + ) + } + if (!is.null(footer)) { + checkmate::assert( + .var.name = "footer", + checkmate::check_string(footer), + checkmate::check_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html")) + ) + res$footer <- footer + lifecycle::deprecate_soft( + when = "0.15.3", + what = "init(footer)", + details = paste( + "Use `add_footer()` on the teal app object instead." + ) + ) + } + + # argument transformations + ## `modules` - landing module + landing <- extract_module(modules, "teal_module_landing") + if (length(landing) == 1L) { + res$landing_popup <- landing[[1L]] + modules <- drop_module(modules, "teal_module_landing") + lifecycle::deprecate_soft( + when = "0.15.3", + what = "landing_popup_module()", + details = paste( + "landing_popup_module() is deprecated.", + "Use `add_custom_server()` to add a landing popup at the start of the app", + "using the `shiny::showModal()` and `shiny::modalDialog()` functions instead." ) - }, - server = function(input, output, session) { - if (!is.null(landing_popup)) { - do.call(landing_popup$server, c(list(id = "landing_module_shiny_id"), landing_popup$server_args)) - } - srv_teal(id = ns("teal"), data = data, modules = modules, filter = deep_copy_filter(filter)) + ) + } else if (length(landing) > 1L) { + stop("Only one `landing_popup_module` can be used.") + } + + # Note: UI must be a function to support bookmarking. + res$ui <- function(request) { + ui_teal( + id = ns("teal"), + modules = modules, + title = res$title, + header = res$header, + footer = res$footer + ) + } + res$server <- function(input, output, session) { + if (!is.null(res$landing_popup)) { + do.call(res$landing_popup$server, c(list(id = "landing_module_shiny_id"), res$landing_popup$server_args)) } - ) + if (!is.null(res$custom_server)) { + res$custom_server(input, output, session) + } + srv_teal(id = ns("teal"), data = data, modules = modules, filter = deep_copy_filter(filter)) + } logger::log_debug("init teal app has been initialized.") res } + + +#' Add a Title to a `teal` App +#' +#' @description Adds a browser window title or app title to the `teal` app. +#' +#' @param app (`environment`) The `teal` app environment. +#' @param title (`shiny.tag` or `character(1)`) The title to set. Defaults to the result of [build_app_title()]. +#' +#' @return The modified `teal` app environment, invisibly. +#' @export +#' +#' @examples +#' app <- list2env(list(title = NULL)) +#' app <- add_title(app, "My App Title") +add_title <- function(app, title = build_app_title()) { + checkmate::assert_environment(app) + checkmate::assert_multi_class(title, c("shiny.tag", "character")) + app$title <- title + invisible(app) +} + +#' Add a Header to a `teal` App +#' +#' @description Adds a header to the `teal` app. +#' +#' @param app (`environment`) The `teal` app environment. +#' @param header (`shiny.tag` or `character(1)`) The header content to set. Defaults to an empty paragraph tag. +#' +#' @return The modified `teal` app environment, invisibly. +#' @export +#' +#' @examples +#' app <- list2env(list(header = NULL)) +#' app <- add_header(app, tags$h1("App Header")) +add_header <- function(app, header = tags$p()) { + checkmate::assert_environment(app) + checkmate::assert_multi_class(header, c("shiny.tag", "character")) + app$header <- header + invisible(app) +} + +#' Add a Footer to a `teal` App +#' +#' @description Adds a footer to the `teal` app. +#' +#' @param app (`environment`) The `teal` app environment. +#' @param footer (`shiny.tag` or `character(1)`) The footer content to set. Defaults to an empty paragraph tag. +#' +#' @return The modified `teal` app environment, invisibly. +#' @export +#' +#' @examples +#' app <- list2env(list(footer = NULL)) +#' app <- add_footer(app, tags$p("App Footer")) +add_footer <- function(app, footer = tags$p()) { + checkmate::assert_environment(app) + checkmate::assert_multi_class(footer, c("shiny.tag", "character")) + app$footer <- footer + invisible(app) +} + +#' Add a Custom Server Function to a `teal` App +#' +#' @description Adds a custom server function to the `teal` app. This function can define additional server logic. +#' +#' @param app (`environment`) The `teal` app environment. +#' @param custom_server (`function`) The custom server function to set. +#' +#' @return The modified `teal` app environment, invisibly. +#' @export +#' +#' @examples +#' app <- list2env(list(custom_server = NULL)) +#' custom_server <- function(input, output, session) { +#' message("Custom server logic here") +#' } +#' app <- add_custom_server(app, custom_server) +add_custom_server <- function(app, custom_server) { + checkmate::assert_environment(app) + checkmate::assert_function(custom_server) + app$custom_server <- custom_server + invisible(app) +} diff --git a/R/landing_popup_module.R b/R/landing_popup_module.R index 2876a9f1ef..23b4c6606c 100644 --- a/R/landing_popup_module.R +++ b/R/landing_popup_module.R @@ -17,11 +17,11 @@ #' app1 <- init( #' data = teal_data(iris = iris), #' modules = modules( -#' example_module() -#' ), -#' landing_popup = landing_popup_module( -#' content = "A place for the welcome message or a disclaimer statement.", -#' buttons = modalButton("Proceed") +#' example_module(), +#' landing_popup_module( +#' content = "A place for the welcome message or a disclaimer statement.", +#' buttons = modalButton("Proceed") +#' ) #' ) #' ) #' if (interactive()) { @@ -31,20 +31,20 @@ #' app2 <- init( #' data = teal_data(iris = iris), #' modules = modules( -#' example_module() -#' ), -#' landing_popup = landing_popup_module( -#' title = "Welcome", -#' content = tags$b( -#' "A place for the welcome message or a disclaimer statement.", -#' style = "color: red;" -#' ), -#' buttons = tagList( -#' modalButton("Proceed"), -#' actionButton("read", "Read more", -#' onclick = "window.open('http://google.com', '_blank')" +#' example_module(), +#' landing_popup_module( +#' title = "Welcome", +#' content = tags$b( +#' "A place for the welcome message or a disclaimer statement.", +#' style = "color: red;" #' ), -#' actionButton("close", "Reject", onclick = "window.close()") +#' buttons = tagList( +#' modalButton("Proceed"), +#' actionButton("read", "Read more", +#' onclick = "window.open('http://google.com', '_blank')" +#' ), +#' actionButton("close", "Reject", onclick = "window.close()") +#' ) #' ) #' ) #' ) @@ -70,6 +70,7 @@ landing_popup_module <- function(label = "Landing Popup", module <- module( label = label, + datanames = NULL, server = function(id) { moduleServer(id, function(input, output, session) { showModal( diff --git a/man/add_custom_server.Rd b/man/add_custom_server.Rd new file mode 100644 index 0000000000..c2cbaf7dd2 --- /dev/null +++ b/man/add_custom_server.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/init.R +\name{add_custom_server} +\alias{add_custom_server} +\title{Add a Custom Server Function to a \code{teal} App} +\usage{ +add_custom_server(app, custom_server) +} +\arguments{ +\item{app}{(\code{environment}) The \code{teal} app environment.} + +\item{custom_server}{(\code{function}) The custom server function to set.} +} +\value{ +The modified \code{teal} app environment, invisibly. +} +\description{ +Adds a custom server function to the \code{teal} app. This function can define additional server logic. +} +\examples{ +app <- list2env(list(custom_server = NULL)) +custom_server <- function(input, output, session) { + message("Custom server logic here") +} +app <- add_custom_server(app, custom_server) +} diff --git a/man/add_footer.Rd b/man/add_footer.Rd new file mode 100644 index 0000000000..7247057ddc --- /dev/null +++ b/man/add_footer.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/init.R +\name{add_footer} +\alias{add_footer} +\title{Add a Footer to a \code{teal} App} +\usage{ +add_footer(app, footer = tags$p()) +} +\arguments{ +\item{app}{(\code{environment}) The \code{teal} app environment.} + +\item{footer}{(\code{shiny.tag} or \code{character(1)}) The footer content to set. Defaults to an empty paragraph tag.} +} +\value{ +The modified \code{teal} app environment, invisibly. +} +\description{ +Adds a footer to the \code{teal} app. +} +\examples{ +app <- list2env(list(footer = NULL)) +app <- add_footer(app, tags$p("App Footer")) +} diff --git a/man/add_header.Rd b/man/add_header.Rd new file mode 100644 index 0000000000..eabe13fb3a --- /dev/null +++ b/man/add_header.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/init.R +\name{add_header} +\alias{add_header} +\title{Add a Header to a \code{teal} App} +\usage{ +add_header(messages, header = "") +} +\arguments{ +\item{header}{(\code{shiny.tag} or \code{character(1)}) The header content to set. Defaults to an empty paragraph tag.} + +\item{app}{(\code{environment}) The \code{teal} app environment.} +} +\value{ +The modified \code{teal} app environment, invisibly. +} +\description{ +Adds a header to the \code{teal} app. +} +\examples{ +app <- list2env(list(header = NULL)) +app <- add_header(app, tags$h1("App Header")) +} diff --git a/man/add_title.Rd b/man/add_title.Rd new file mode 100644 index 0000000000..4361909b68 --- /dev/null +++ b/man/add_title.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/init.R +\name{add_title} +\alias{add_title} +\title{Add a Title to a \code{teal} App} +\usage{ +add_title(app, title = build_app_title()) +} +\arguments{ +\item{app}{(\code{environment}) The \code{teal} app environment.} + +\item{title}{(\code{shiny.tag} or \code{character(1)}) The title to set. Defaults to the result of \code{\link[=build_app_title]{build_app_title()}}.} +} +\value{ +The modified \code{teal} app environment, invisibly. +} +\description{ +Adds a browser window title or app title to the \code{teal} app. +} +\examples{ +app <- list2env(list(title = NULL)) +app <- add_title(app, "My App Title") +} diff --git a/man/init.Rd b/man/init.Rd index cf5a171764..1976b54286 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -8,11 +8,10 @@ init( data, modules, filter = teal_slices(), - title = build_app_title(), - header = tags$p(), - footer = tags$p(), - id = character(0), - landing_popup = NULL + title = NULL, + header = NULL, + footer = NULL, + id = character(0) ) } \arguments{ @@ -29,23 +28,23 @@ more details.} \item{filter}{(\code{teal_slices}) Optionally, specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} -\item{title}{(\code{shiny.tag} or \code{character(1)}) Optionally, +\item{title}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or -by passing a valid \code{shiny.tag} which is a head tag with title and link tag.} +by passing a valid \code{shiny.tag} which is a head tag with title and link tag. +This parameter is deprecated. Use \code{add_title()} on the teal app object instead.} -\item{header}{(\code{shiny.tag} or \code{character(1)}) Optionally, -the header of the app.} +\item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the header of the app. +This parameter is deprecated. Use \code{add_header()} on the teal app object instead.} -\item{footer}{(\code{shiny.tag} or \code{character(1)}) Optionally, -the footer of the app.} +\item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the footer of the app. +This parameter is deprecated. Use \code{add_footer()} on the teal app object instead.} \item{id}{(\code{character}) Optionally, a string specifying the \code{shiny} module id in cases it is used as a \code{shiny} module rather than a standalone \code{shiny} app. This is a legacy feature.} - -\item{landing_popup}{(\code{teal_module_landing}) Optionally, -a \code{landing_popup_module} to show up as soon as the teal app is initialized.} } \value{ Named list containing server and UI functions. diff --git a/man/landing_popup_module.Rd b/man/landing_popup_module.Rd index af7c79c726..43e15dd769 100644 --- a/man/landing_popup_module.Rd +++ b/man/landing_popup_module.Rd @@ -34,11 +34,11 @@ The dialog blocks access to the application and must be closed with a button bef app1 <- init( data = teal_data(iris = iris), modules = modules( - example_module() - ), - landing_popup = landing_popup_module( - content = "A place for the welcome message or a disclaimer statement.", - buttons = modalButton("Proceed") + example_module(), + landing_popup_module( + content = "A place for the welcome message or a disclaimer statement.", + buttons = modalButton("Proceed") + ) ) ) if (interactive()) { @@ -48,20 +48,20 @@ if (interactive()) { app2 <- init( data = teal_data(iris = iris), modules = modules( - example_module() - ), - landing_popup = landing_popup_module( - title = "Welcome", - content = tags$b( - "A place for the welcome message or a disclaimer statement.", - style = "color: red;" - ), - buttons = tagList( - modalButton("Proceed"), - actionButton("read", "Read more", - onclick = "window.open('http://google.com', '_blank')" + example_module(), + landing_popup_module( + title = "Welcome", + content = tags$b( + "A place for the welcome message or a disclaimer statement.", + style = "color: red;" ), - actionButton("close", "Reject", onclick = "window.close()") + buttons = tagList( + modalButton("Proceed"), + actionButton("read", "Read more", + onclick = "window.open('http://google.com', '_blank')" + ), + actionButton("close", "Reject", onclick = "window.close()") + ) ) ) ) diff --git a/man/module_teal.Rd b/man/module_teal.Rd index f2bc5d95b5..dbf5149492 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -27,16 +27,19 @@ Nested list of \code{teal_modules} or \code{teal_module} objects or a single will be displayed in the \code{teal} application. See \code{\link[=modules]{modules()}} and \code{\link[=module]{module()}} for more details.} -\item{title}{(\code{shiny.tag} or \code{character(1)}) Optionally, +\item{title}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or -by passing a valid \code{shiny.tag} which is a head tag with title and link tag.} +by passing a valid \code{shiny.tag} which is a head tag with title and link tag. +This parameter is deprecated. Use \code{add_title()} on the teal app object instead.} -\item{header}{(\code{shiny.tag} or \code{character(1)}) Optionally, -the header of the app.} +\item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the header of the app. +This parameter is deprecated. Use \code{add_header()} on the teal app object instead.} -\item{footer}{(\code{shiny.tag} or \code{character(1)}) Optionally, -the footer of the app.} +\item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the footer of the app. +This parameter is deprecated. Use \code{add_footer()} on the teal app object instead.} \item{data}{(\code{teal_data}, \code{teal_data_module}, or \code{reactive} returning \code{teal_data}) The data which application will depend on.} diff --git a/man/module_teal_with_splash.Rd b/man/module_teal_with_splash.Rd index 98b49551fb..240ab8bb64 100644 --- a/man/module_teal_with_splash.Rd +++ b/man/module_teal_with_splash.Rd @@ -24,16 +24,19 @@ rather than a standalone \code{shiny} app. This is a legacy feature.} \item{data}{(\code{teal_data}, \code{teal_data_module}, or \code{reactive} returning \code{teal_data}) The data which application will depend on.} -\item{title}{(\code{shiny.tag} or \code{character(1)}) Optionally, +\item{title}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or -by passing a valid \code{shiny.tag} which is a head tag with title and link tag.} +by passing a valid \code{shiny.tag} which is a head tag with title and link tag. +This parameter is deprecated. Use \code{add_title()} on the teal app object instead.} -\item{header}{(\code{shiny.tag} or \code{character(1)}) Optionally, -the header of the app.} +\item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the header of the app. +This parameter is deprecated. Use \code{add_header()} on the teal app object instead.} -\item{footer}{(\code{shiny.tag} or \code{character(1)}) Optionally, -the footer of the app.} +\item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the footer of the app. +This parameter is deprecated. Use \code{add_footer()} on the teal app object instead.} \item{modules}{(\code{list} or \code{teal_modules} or \code{teal_module}) Nested list of \code{teal_modules} or \code{teal_module} objects or a single diff --git a/tests/testthat/test-shinytest2-landing_popup.R b/tests/testthat/test-shinytest2-landing_popup.R index d2e8ba3093..b402b60771 100644 --- a/tests/testthat/test-shinytest2-landing_popup.R +++ b/tests/testthat/test-shinytest2-landing_popup.R @@ -26,9 +26,9 @@ testthat::test_that("e2e: app with default landing_popup_module creates modal co app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module() - ), - landing_popup = landing_popup_module() + example_module(), + landing_popup_module() + ) ) testthat::expect_equal( @@ -44,9 +44,9 @@ testthat::test_that("e2e: when default landing_popup_module is closed, it shows app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module() - ), - landing_popup = landing_popup_module() + example_module(), + landing_popup_module() + ) ) # Button is clicked. @@ -94,12 +94,12 @@ testthat::test_that( app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module() - ), - landing_popup = landing_popup_module( - title = modal_title, - content = modal_content, - buttons = modal_buttons + example_module(), + landing_popup_module( + title = modal_title, + content = modal_content, + buttons = modal_buttons + ) ) ) @@ -148,10 +148,10 @@ testthat::test_that("e2e: when customized button in landing_popup_module is clic app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module() - ), - landing_popup = landing_popup_module( - buttons = actionButton("read", "Read more", onclick = onclick_text) + example_module(), + landing_popup_module( + buttons = actionButton("read", "Read more", onclick = onclick_text) + ) ) ) From 65a7e1030cfce97a568d1c61f360c12e610ee9d3 Mon Sep 17 00:00:00 2001 From: vedhav Date: Tue, 7 Jan 2025 13:26:57 +0530 Subject: [PATCH 02/50] docs: add pkgdown index for the new functions --- _pkgdown.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 94e595d09e..c7bd40ce70 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -108,6 +108,13 @@ reference: - module - modules - teal_slices + - title: Teal app modifiers + desc: Functions to modify the `teal` app object + contents: + - add_footer + - add_header + - add_title + - add_custom_server - title: Helper Functions desc: Helper functions for `teal` contents: From 7063e665d29affde6d464a46539f9d62180f3ee7 Mon Sep 17 00:00:00 2001 From: vedhav Date: Wed, 8 Jan 2025 13:43:59 +0530 Subject: [PATCH 03/50] feat: add a modifier for landing popup --- NAMESPACE | 1 + R/TealAppDriver.R | 10 +-- R/init.R | 134 +++++++++++++++++++++++++++++---------- _pkgdown.yml | 1 + man/TealAppDriver.Rd | 4 -- man/add_custom_server.Rd | 19 +++--- man/add_footer.Rd | 12 ++-- man/add_header.Rd | 16 +++-- man/add_landing_popup.Rd | 48 ++++++++++++++ man/add_title.Rd | 19 +++--- 10 files changed, 191 insertions(+), 73 deletions(-) create mode 100644 man/add_landing_popup.Rd diff --git a/NAMESPACE b/NAMESPACE index cb96ee7bda..634dbfc9e1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,6 +17,7 @@ export(TealReportCard) export(add_custom_server) export(add_footer) export(add_header) +export(add_landing_popup) export(add_title) export(as.teal_slices) export(as_tdata) diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index f170e515a7..af81466744 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -45,10 +45,6 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. initialize = function(data, modules, filter = teal_slices(), - title = build_app_title(), - header = tags$p(), - footer = tags$p(), - landing_popup = NULL, timeout = rlang::missing_arg(), load_timeout = rlang::missing_arg(), ...) { @@ -58,11 +54,7 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. app <- init( data = data, modules = modules, - filter = filter, - title = title, - header = header, - footer = footer, - landing_popup = landing_popup, + filter = filter ) # Default timeout is hardcoded to 4s in shinytest2:::resolve_timeout diff --git a/R/init.R b/R/init.R index 35b186ae4f..541011fe1f 100644 --- a/R/init.R +++ b/R/init.R @@ -191,6 +191,7 @@ init <- function(data, res$title <- build_app_title() res$header <- tags$p() res$footer <- tags$p() + res$landing_popup_server <- NULL if (!is.null(title)) { checkmate::assert( @@ -242,7 +243,7 @@ init <- function(data, ## `modules` - landing module landing <- extract_module(modules, "teal_module_landing") if (length(landing) == 1L) { - res$landing_popup <- landing[[1L]] + res$landing_popup_server <- landing[[1L]]$server modules <- drop_module(modules, "teal_module_landing") lifecycle::deprecate_soft( when = "0.15.3", @@ -268,8 +269,12 @@ init <- function(data, ) } res$server <- function(input, output, session) { - if (!is.null(res$landing_popup)) { - do.call(res$landing_popup$server, c(list(id = "landing_module_shiny_id"), res$landing_popup$server_args)) + if (!is.null(res$landing_popup_server)) { + if (identical(names(formals(res$landing_popup_server)), "id")) { + do.call(res$landing_popup_server, c(list(id = "landing_module_shiny_id"))) + } else { + res$landing_popup_server(input, output, session) + } } if (!is.null(res$custom_server)) { res$custom_server(input, output, session) @@ -282,20 +287,19 @@ init <- function(data, res } - -#' Add a Title to a `teal` App -#' -#' @description Adds a browser window title or app title to the `teal` app. +#' Add a custom title to the app #' -#' @param app (`environment`) The `teal` app environment. -#' @param title (`shiny.tag` or `character(1)`) The title to set. Defaults to the result of [build_app_title()]. -#' -#' @return The modified `teal` app environment, invisibly. +#' @param title The title to add +#' @return The modified app object #' @export -#' #' @examples -#' app <- list2env(list(title = NULL)) -#' app <- add_title(app, "My App Title") +#' app <- init( +#' data = teal_data(IRIS = iris, MTCARS = mtcars), +#' modules = modules(example_module()) +#' ) |> +#' add_title("Custom title") +#' +#' shinyApp(app$ui, app$server) add_title <- function(app, title = build_app_title()) { checkmate::assert_environment(app) checkmate::assert_multi_class(title, c("shiny.tag", "character")) @@ -309,13 +313,19 @@ add_title <- function(app, title = build_app_title()) { #' #' @param app (`environment`) The `teal` app environment. #' @param header (`shiny.tag` or `character(1)`) The header content to set. Defaults to an empty paragraph tag. -#' -#' @return The modified `teal` app environment, invisibly. #' @export -#' #' @examples -#' app <- list2env(list(header = NULL)) -#' app <- add_header(app, tags$h1("App Header")) +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' add_header( +#' tags$div( +#' h3("Custom header") +#' ) +#' ) +#' +#' shinyApp(app$ui, app$server) add_header <- function(app, header = tags$p()) { checkmate::assert_environment(app) checkmate::assert_multi_class(header, c("shiny.tag", "character")) @@ -329,13 +339,15 @@ add_header <- function(app, header = tags$p()) { #' #' @param app (`environment`) The `teal` app environment. #' @param footer (`shiny.tag` or `character(1)`) The footer content to set. Defaults to an empty paragraph tag. -#' -#' @return The modified `teal` app environment, invisibly. #' @export -#' #' @examples -#' app <- list2env(list(footer = NULL)) -#' app <- add_footer(app, tags$p("App Footer")) +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' add_footer("Custom footer") +#' +#' shinyApp(app$ui, app$server) add_footer <- function(app, footer = tags$p()) { checkmate::assert_environment(app) checkmate::assert_multi_class(footer, c("shiny.tag", "character")) @@ -343,25 +355,81 @@ add_footer <- function(app, footer = tags$p()) { invisible(app) } -#' Add a Custom Server Function to a `teal` App +#' Add a Custom Server Logic to a `teal` App #' #' @description Adds a custom server function to the `teal` app. This function can define additional server logic. #' #' @param app (`environment`) The `teal` app environment. #' @param custom_server (`function`) The custom server function to set. -#' -#' @return The modified `teal` app environment, invisibly. #' @export -#' #' @examples -#' app <- list2env(list(custom_server = NULL)) -#' custom_server <- function(input, output, session) { -#' message("Custom server logic here") -#' } -#' app <- add_custom_server(app, custom_server) +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' add_custom_server(function(input, output, session) { +#' print("injected server logic to the main shiny server function") +#' }) +#' +#' shinyApp(app$ui, app$server) add_custom_server <- function(app, custom_server) { checkmate::assert_environment(app) checkmate::assert_function(custom_server) app$custom_server <- custom_server invisible(app) } + + +#' Add a Landing Popup to a `teal` App +#' +#' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. +#' +#' This modifier is used to display a popup dialog when the application starts. +#' The dialog blocks access to the application and must be closed with a button before the application can be viewed. +#' +#' @param app (`environment`) The `teal` app environment. +#' @param id (`character(1)`) The ID of the modal dialog. +#' @param label (`character(1)`) Label of the module. +#' @param title (`character(1)`) Text to be displayed as popup title. +#' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. +#' Passed to `...` of `shiny::modalDialog`. See examples. +#' @param buttons (`shiny.tag` or `shiny.tag.list`) Typically a `modalButton` or `actionButton`. See examples. +#' @export +#' @examples +#' app <- init( +#' data = teal_data(IRIS = iris, MTCARS = mtcars), +#' modules = modules(example_module()) +#' ) |> +#' add_landing_popup( +#' title = "Welcome", +#' content = "This is a landing popup.", +#' buttons = modalButton("Accept") +#' ) +#' +#' shinyApp(app$ui, app$server) +add_landing_popup <- function( + app, + id = "landingpopup", + label = "Landing Popup", + title = NULL, + content = NULL, + buttons = modalButton("Accept")) { + checkmate::assert_environment(app) + checkmate::assert_string(id) + checkmate::assert_string(label) + checkmate::assert_class(title, "character", null.ok = TRUE) + checkmate::assert_multi_class(content, c("shiny.tag", "character"), null.ok = TRUE) + checkmate::assert_multi_class(buttons, c("shiny.tag", "character")) + + app$landing_popup_server <- function(input, output, session) { + showModal( + modalDialog( + id = id, + title = title, + content, + footer = buttons + ) + ) + } + invisible(app) +} diff --git a/_pkgdown.yml b/_pkgdown.yml index c7bd40ce70..61b6020508 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -115,6 +115,7 @@ reference: - add_header - add_title - add_custom_server + - add_landing_popup - title: Helper Functions desc: Helper functions for `teal` contents: diff --git a/man/TealAppDriver.Rd b/man/TealAppDriver.Rd index cfdd8110df..8a433d6263 100644 --- a/man/TealAppDriver.Rd +++ b/man/TealAppDriver.Rd @@ -98,10 +98,6 @@ Initialize a \code{TealAppDriver} object for testing a \code{teal} application. data, modules, filter = teal_slices(), - title = build_app_title(), - header = tags$p(), - footer = tags$p(), - landing_popup = NULL, timeout = rlang::missing_arg(), load_timeout = rlang::missing_arg(), ... diff --git a/man/add_custom_server.Rd b/man/add_custom_server.Rd index c2cbaf7dd2..8414de06f1 100644 --- a/man/add_custom_server.Rd +++ b/man/add_custom_server.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/init.R \name{add_custom_server} \alias{add_custom_server} -\title{Add a Custom Server Function to a \code{teal} App} +\title{Add a Custom Server Logic to a \code{teal} App} \usage{ add_custom_server(app, custom_server) } @@ -11,16 +11,17 @@ add_custom_server(app, custom_server) \item{custom_server}{(\code{function}) The custom server function to set.} } -\value{ -The modified \code{teal} app environment, invisibly. -} \description{ Adds a custom server function to the \code{teal} app. This function can define additional server logic. } \examples{ -app <- list2env(list(custom_server = NULL)) -custom_server <- function(input, output, session) { - message("Custom server logic here") -} -app <- add_custom_server(app, custom_server) +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + add_custom_server(function(input, output, session) { + print("injected server logic to the main shiny server function") + }) + +shinyApp(app$ui, app$server) } diff --git a/man/add_footer.Rd b/man/add_footer.Rd index 7247057ddc..4d487d9cc7 100644 --- a/man/add_footer.Rd +++ b/man/add_footer.Rd @@ -11,13 +11,15 @@ add_footer(app, footer = tags$p()) \item{footer}{(\code{shiny.tag} or \code{character(1)}) The footer content to set. Defaults to an empty paragraph tag.} } -\value{ -The modified \code{teal} app environment, invisibly. -} \description{ Adds a footer to the \code{teal} app. } \examples{ -app <- list2env(list(footer = NULL)) -app <- add_footer(app, tags$p("App Footer")) +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + add_footer("Custom footer") + +shinyApp(app$ui, app$server) } diff --git a/man/add_header.Rd b/man/add_header.Rd index eabe13fb3a..c3f66d3e73 100644 --- a/man/add_header.Rd +++ b/man/add_header.Rd @@ -11,13 +11,19 @@ add_header(messages, header = "") \item{app}{(\code{environment}) The \code{teal} app environment.} } -\value{ -The modified \code{teal} app environment, invisibly. -} \description{ Adds a header to the \code{teal} app. } \examples{ -app <- list2env(list(header = NULL)) -app <- add_header(app, tags$h1("App Header")) +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + add_header( + tags$div( + h3("Custom header") + ) + ) + +shinyApp(app$ui, app$server) } diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd new file mode 100644 index 0000000000..961ac2b3c9 --- /dev/null +++ b/man/add_landing_popup.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/init.R +\name{add_landing_popup} +\alias{add_landing_popup} +\title{Add a Landing Popup to a \code{teal} App} +\usage{ +add_landing_popup( + app, + id = "landingpopup", + label = "Landing Popup", + title = NULL, + content = NULL, + buttons = modalButton("Accept") +) +} +\arguments{ +\item{app}{(\code{environment}) The \code{teal} app environment.} + +\item{id}{(\code{character(1)}) The ID of the modal dialog.} + +\item{label}{(\code{character(1)}) Label of the module.} + +\item{title}{(\code{character(1)}) Text to be displayed as popup title.} + +\item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. +Passed to \code{...} of \code{shiny::modalDialog}. See examples.} + +\item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}. See examples.} +} +\description{ +Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. + +This modifier is used to display a popup dialog when the application starts. +The dialog blocks access to the application and must be closed with a button before the application can be viewed. +} +\examples{ +app <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules(example_module()) +) |> + add_landing_popup( + title = "Welcome", + content = "This is a landing popup.", + buttons = modalButton("Accept") + ) + +shinyApp(app$ui, app$server) +} diff --git a/man/add_title.Rd b/man/add_title.Rd index 4361909b68..5a3eec9bd6 100644 --- a/man/add_title.Rd +++ b/man/add_title.Rd @@ -2,22 +2,25 @@ % Please edit documentation in R/init.R \name{add_title} \alias{add_title} -\title{Add a Title to a \code{teal} App} +\title{Add a custom title to the app} \usage{ add_title(app, title = build_app_title()) } \arguments{ -\item{app}{(\code{environment}) The \code{teal} app environment.} - -\item{title}{(\code{shiny.tag} or \code{character(1)}) The title to set. Defaults to the result of \code{\link[=build_app_title]{build_app_title()}}.} +\item{title}{The title to add} } \value{ -The modified \code{teal} app environment, invisibly. +The modified app object } \description{ -Adds a browser window title or app title to the \code{teal} app. +Add a custom title to the app } \examples{ -app <- list2env(list(title = NULL)) -app <- add_title(app, "My App Title") +app <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules(example_module()) +) |> + add_title("Custom title") + +shinyApp(app$ui, app$server) } From 029b24e4e95a9bdedc10256cae83ba15f74a875f Mon Sep 17 00:00:00 2001 From: vedhav Date: Wed, 8 Jan 2025 13:47:29 +0530 Subject: [PATCH 04/50] chore: change the depreciation message for landing popup --- R/init.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/init.R b/R/init.R index 541011fe1f..654a4ced0f 100644 --- a/R/init.R +++ b/R/init.R @@ -250,8 +250,7 @@ init <- function(data, what = "landing_popup_module()", details = paste( "landing_popup_module() is deprecated.", - "Use `add_custom_server()` to add a landing popup at the start of the app", - "using the `shiny::showModal()` and `shiny::modalDialog()` functions instead." + "Use `add_landing_popup()` on the teal app object instead." ) ) } else if (length(landing) > 1L) { From 404dbd6e4a79530cca5b9229a711b1a8f100b63e Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 13:23:50 +0530 Subject: [PATCH 05/50] feat: modify using functions --- NAMESPACE | 6 +- R/init.R | 207 ++++++++++++------------ R/module_teal.R | 6 +- R/utils.R | 5 + _pkgdown.yml | 6 +- man/add_landing_popup.Rd | 5 +- man/init.Rd | 12 +- man/{add_footer.Rd => modify_footer.Rd} | 8 +- man/{add_header.Rd => modify_header.Rd} | 12 +- man/{add_title.Rd => modify_title.Rd} | 13 +- man/module_teal.Rd | 6 +- man/module_teal_with_splash.Rd | 6 +- 12 files changed, 149 insertions(+), 143 deletions(-) rename man/{add_footer.Rd => modify_footer.Rd} (82%) rename man/{add_header.Rd => modify_header.Rd} (85%) rename man/{add_title.Rd => modify_title.Rd} (64%) diff --git a/NAMESPACE b/NAMESPACE index 634dbfc9e1..7dab025892 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,10 +15,7 @@ S3method(ui_teal_module,teal_modules) S3method(within,teal_data_module) export(TealReportCard) export(add_custom_server) -export(add_footer) -export(add_header) export(add_landing_popup) -export(add_title) export(as.teal_slices) export(as_tdata) export(build_app_title) @@ -28,6 +25,9 @@ export(get_metadata) export(init) export(landing_popup_module) export(make_teal_transform_server) +export(modify_footer) +export(modify_header) +export(modify_title) export(module) export(modules) export(new_tdata) diff --git a/R/init.R b/R/init.R index 654a4ced0f..e81f3bc5a9 100644 --- a/R/init.R +++ b/R/init.R @@ -24,13 +24,13 @@ #' the browser window title. Defaults to a title "teal app" with the icon of NEST. #' Can be created using the `build_app_title()` or #' by passing a valid `shiny.tag` which is a head tag with title and link tag. -#' This parameter is deprecated. Use `add_title()` on the teal app object instead. +#' This parameter is deprecated. Use `modify_title()` on the teal app object instead. #' @param header (`shiny.tag` or `character(1)`) `r lifecycle::badge("deprecated")` Optionally, #' the header of the app. -#' This parameter is deprecated. Use `add_header()` on the teal app object instead. +#' This parameter is deprecated. Use `modify_header()` on the teal app object instead. #' @param footer (`shiny.tag` or `character(1)`) `r lifecycle::badge("deprecated")` Optionally, #' the footer of the app. -#' This parameter is deprecated. Use `add_footer()` on the teal app object instead. +#' This parameter is deprecated. Use `modify_footer()` on the teal app object instead. #' @param id (`character`) Optionally, #' a string specifying the `shiny` module id in cases it is used as a `shiny` module #' rather than a standalone `shiny` app. This is a legacy feature. @@ -95,9 +95,9 @@ init <- function(data, modules, filter = teal_slices(), - title = NULL, - header = NULL, - footer = NULL, + title = build_app_title(), + header = tags$p(), + footer = tags$p(), id = character(0)) { logger::log_debug("init initializing teal app with: data ('{ class(data) }').") @@ -186,55 +186,34 @@ init <- function(data, ) } - ns <- NS(id) - res <- new.env(parent = emptyenv()) - res$title <- build_app_title() - res$header <- tags$p() - res$footer <- tags$p() + res <- list() res$landing_popup_server <- NULL if (!is.null(title)) { - checkmate::assert( - .var.name = "title", - checkmate::check_string(title), - checkmate::check_multi_class(title, c("shiny.tag", "shiny.tag.list", "html")) - ) - res$title <- title - lifecycle::deprecate_soft( + checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) + lifecycle::deprecate_warn( when = "0.15.3", what = "init(title)", - details = paste( - "Use `add_title()` on the teal app object instead." - ) + details = "Use `modify_title()` on the teal app object instead." ) } if (!is.null(header)) { - checkmate::assert( - .var.name = "header", - checkmate::check_string(header), - checkmate::check_multi_class(header, c("shiny.tag", "shiny.tag.list", "html")) - ) - res$header <- header - lifecycle::deprecate_soft( + checkmate::assert_multi_class(header, c("shiny.tag", "shiny.tag.list", "html", "character")) + lifecycle::deprecate_warn( when = "0.15.3", what = "init(header)", details = paste( - "Use `add_header()` on the teal app object instead." + "Use `modify_header()` on the teal app object instead." ) ) } if (!is.null(footer)) { - checkmate::assert( - .var.name = "footer", - checkmate::check_string(footer), - checkmate::check_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html")) - ) - res$footer <- footer - lifecycle::deprecate_soft( + checkmate::assert_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html", "character")) + lifecycle::deprecate_warn( when = "0.15.3", what = "init(footer)", details = paste( - "Use `add_footer()` on the teal app object instead." + "Use `modify_footer()` on the teal app object instead." ) ) } @@ -245,12 +224,12 @@ init <- function(data, if (length(landing) == 1L) { res$landing_popup_server <- landing[[1L]]$server modules <- drop_module(modules, "teal_module_landing") - lifecycle::deprecate_soft( + lifecycle::deprecate_warn( when = "0.15.3", what = "landing_popup_module()", details = paste( "landing_popup_module() is deprecated.", - "Use `add_landing_popup()` on the teal app object instead." + "Use add_landing_popup() on the teal app object instead." ) ) } else if (length(landing) > 1L) { @@ -258,27 +237,20 @@ init <- function(data, } # Note: UI must be a function to support bookmarking. - res$ui <- function(request) { + res$ui <- function(request, ...) { ui_teal( - id = ns("teal"), + id = "teal", modules = modules, - title = res$title, - header = res$header, - footer = res$footer + title = title, + header = header, + footer = footer ) } res$server <- function(input, output, session) { if (!is.null(res$landing_popup_server)) { - if (identical(names(formals(res$landing_popup_server)), "id")) { - do.call(res$landing_popup_server, c(list(id = "landing_module_shiny_id"))) - } else { - res$landing_popup_server(input, output, session) - } - } - if (!is.null(res$custom_server)) { - res$custom_server(input, output, session) + do.call(res$landing_popup_server, c(list(id = "landing_module_shiny_id"))) } - srv_teal(id = ns("teal"), data = data, modules = modules, filter = deep_copy_filter(filter)) + srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) } logger::log_debug("init teal app has been initialized.") @@ -296,14 +268,32 @@ init <- function(data, #' data = teal_data(IRIS = iris, MTCARS = mtcars), #' modules = modules(example_module()) #' ) |> -#' add_title("Custom title") +#' modify_title("Custom title") #' #' shinyApp(app$ui, app$server) -add_title <- function(app, title = build_app_title()) { - checkmate::assert_environment(app) - checkmate::assert_multi_class(title, c("shiny.tag", "character")) - app$title <- title - invisible(app) +modify_title <- function( + app, + title = "teal app", + favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { + res <- app + res$ui <- function(request, ...) { + args <- list(...) + args$title <- tags$div( + id = "teal-title", + tags$head( + tags$title(title), + tags$link( + rel = "icon", + href = favicon, + sizes = "any" + ) + ) + ) + ui_tq <- do.call(app$ui, c(list(request = request), args)) |> + htmltools::tagQuery() + ui_tq$find("#teal-title")$replaceWith(args$title)$allTags() + } + res } #' Add a Header to a `teal` App @@ -318,18 +308,23 @@ add_title <- function(app, title = build_app_title()) { #' data = teal_data(IRIS = iris), #' modules = modules(example_module()) #' ) |> -#' add_header( +#' modify_header( #' tags$div( #' h3("Custom header") #' ) #' ) #' #' shinyApp(app$ui, app$server) -add_header <- function(app, header = tags$p()) { - checkmate::assert_environment(app) - checkmate::assert_multi_class(header, c("shiny.tag", "character")) - app$header <- header - invisible(app) +modify_header <- function(app, header = "test") { + res <- app + res$ui <- function(request, ...) { + args <- list(...) + args$header <- header + ui_tq <- do.call(app$ui, c(list(request = request), args)) |> + htmltools::tagQuery() + ui_tq$find("#teal-header")$replaceWith(tags$header(id = "teal-header", args$header))$allTags() + } + res } #' Add a Footer to a `teal` App @@ -344,41 +339,21 @@ add_header <- function(app, header = tags$p()) { #' data = teal_data(IRIS = iris), #' modules = modules(example_module()) #' ) |> -#' add_footer("Custom footer") +#' modify_footer("Custom footer") #' #' shinyApp(app$ui, app$server) -add_footer <- function(app, footer = tags$p()) { - checkmate::assert_environment(app) - checkmate::assert_multi_class(footer, c("shiny.tag", "character")) - app$footer <- footer - invisible(app) -} - -#' Add a Custom Server Logic to a `teal` App -#' -#' @description Adds a custom server function to the `teal` app. This function can define additional server logic. -#' -#' @param app (`environment`) The `teal` app environment. -#' @param custom_server (`function`) The custom server function to set. -#' @export -#' @examples -#' app <- init( -#' data = teal_data(IRIS = iris), -#' modules = modules(example_module()) -#' ) |> -#' add_custom_server(function(input, output, session) { -#' print("injected server logic to the main shiny server function") -#' }) -#' -#' shinyApp(app$ui, app$server) -add_custom_server <- function(app, custom_server) { - checkmate::assert_environment(app) - checkmate::assert_function(custom_server) - app$custom_server <- custom_server - invisible(app) +modify_footer <- function(app, footer = "test") { + res <- app + res$ui <- function(request, ...) { + args <- list(...) + args$footer <- footer + ui_tq <- do.call(app$ui, c(list(request = request), args)) |> + htmltools::tagQuery() + ui_tq$find("#teal-footer")$replaceWith(tags$div(id = "teal-footer", args$footer))$allTags() + } + res } - #' Add a Landing Popup to a `teal` App #' #' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. @@ -409,18 +384,13 @@ add_custom_server <- function(app, custom_server) { add_landing_popup <- function( app, id = "landingpopup", - label = "Landing Popup", title = NULL, content = NULL, buttons = modalButton("Accept")) { - checkmate::assert_environment(app) - checkmate::assert_string(id) - checkmate::assert_string(label) - checkmate::assert_class(title, "character", null.ok = TRUE) - checkmate::assert_multi_class(content, c("shiny.tag", "character"), null.ok = TRUE) - checkmate::assert_multi_class(buttons, c("shiny.tag", "character")) + old_server <- app$server - app$landing_popup_server <- function(input, output, session) { + app$server <- function(input, output, session) { + old_server(input, output, session) showModal( modalDialog( id = id, @@ -430,5 +400,32 @@ add_landing_popup <- function( ) ) } - invisible(app) + app +} + +#' Add a Custom Server Logic to a `teal` App +#' +#' @description Adds a custom server function to the `teal` app. This function can define additional server logic. +#' +#' @param app (`environment`) The `teal` app environment. +#' @param custom_server (`function`) The custom server function to set. +#' @export +#' @examples +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' add_custom_server(function(input, output, session) { +#' print("injected server logic to the main shiny server function") +#' }) +#' +#' shinyApp(app$ui, app$server) +add_custom_server <- function(app, custom_server) { + old_server <- app$server + + app$server <- function(input, output, session) { + old_server(input, output, session) + custom_server(input, output, session) + } + app } diff --git a/R/module_teal.R b/R/module_teal.R index 7834d1d8ad..639d96900e 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -92,10 +92,10 @@ ui_teal <- function(id, fluidPage( id = id, - title = title, + title = tags$div(id = "teal-title", title), theme = get_teal_bs_theme(), include_teal_css_js(), - tags$header(header), + tags$header(id = "teal-header", header), tags$hr(class = "my-2"), shiny_busy_message_panel, tags$div( @@ -133,7 +133,7 @@ ui_teal <- function(id, tags$hr(), tags$footer( tags$div( - footer, + tags$div(id = "teal-footer", footer), teal.widgets::verbatim_popup_ui(ns("sessionInfo"), "Session Info", type = "link"), br(), ui_teal_lockfile(ns("lockfile")), diff --git a/R/utils.R b/R/utils.R index 46fcddc1a3..a5276e9563 100644 --- a/R/utils.R +++ b/R/utils.R @@ -315,6 +315,11 @@ build_app_title <- function( favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { checkmate::assert_string(title, null.ok = TRUE) checkmate::assert_string(favicon, null.ok = TRUE) + lifecycle::deprecate_warn( + when = "0.15.3", + what = "build_app_title()", + details = "Use `modify_title()` on the teal app object instead." + ) tags$head( tags$title(title), tags$link( diff --git a/_pkgdown.yml b/_pkgdown.yml index 61b6020508..f0eca2c428 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -111,9 +111,9 @@ reference: - title: Teal app modifiers desc: Functions to modify the `teal` app object contents: - - add_footer - - add_header - - add_title + - modify_footer + - modify_header + - modify_title - add_custom_server - add_landing_popup - title: Helper Functions diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd index 961ac2b3c9..27656e38c7 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_popup.Rd @@ -7,7 +7,6 @@ add_landing_popup( app, id = "landingpopup", - label = "Landing Popup", title = NULL, content = NULL, buttons = modalButton("Accept") @@ -18,14 +17,14 @@ add_landing_popup( \item{id}{(\code{character(1)}) The ID of the modal dialog.} -\item{label}{(\code{character(1)}) Label of the module.} - \item{title}{(\code{character(1)}) Text to be displayed as popup title.} \item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. Passed to \code{...} of \code{shiny::modalDialog}. See examples.} \item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}. See examples.} + +\item{label}{(\code{character(1)}) Label of the module.} } \description{ Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. diff --git a/man/init.Rd b/man/init.Rd index 1976b54286..cdb3b09c50 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -8,9 +8,9 @@ init( data, modules, filter = teal_slices(), - title = NULL, - header = NULL, - footer = NULL, + title = build_app_title(), + header = tags$p(), + footer = tags$p(), id = character(0) ) } @@ -32,15 +32,15 @@ specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or by passing a valid \code{shiny.tag} which is a head tag with title and link tag. -This parameter is deprecated. Use \code{add_title()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} \item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the header of the app. -This parameter is deprecated. Use \code{add_header()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} \item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the footer of the app. -This parameter is deprecated. Use \code{add_footer()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} \item{id}{(\code{character}) Optionally, a string specifying the \code{shiny} module id in cases it is used as a \code{shiny} module diff --git a/man/add_footer.Rd b/man/modify_footer.Rd similarity index 82% rename from man/add_footer.Rd rename to man/modify_footer.Rd index 4d487d9cc7..b25a4d8d93 100644 --- a/man/add_footer.Rd +++ b/man/modify_footer.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/init.R -\name{add_footer} -\alias{add_footer} +\name{modify_footer} +\alias{modify_footer} \title{Add a Footer to a \code{teal} App} \usage{ -add_footer(app, footer = tags$p()) +modify_footer(app, footer = "test") } \arguments{ \item{app}{(\code{environment}) The \code{teal} app environment.} @@ -19,7 +19,7 @@ app <- init( data = teal_data(IRIS = iris), modules = modules(example_module()) ) |> - add_footer("Custom footer") + modify_footer("Custom footer") shinyApp(app$ui, app$server) } diff --git a/man/add_header.Rd b/man/modify_header.Rd similarity index 85% rename from man/add_header.Rd rename to man/modify_header.Rd index c3f66d3e73..93ab8ada75 100644 --- a/man/add_header.Rd +++ b/man/modify_header.Rd @@ -1,15 +1,15 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/init.R -\name{add_header} -\alias{add_header} +\name{modify_header} +\alias{modify_header} \title{Add a Header to a \code{teal} App} \usage{ -add_header(messages, header = "") +modify_header(app, header = "test") } \arguments{ -\item{header}{(\code{shiny.tag} or \code{character(1)}) The header content to set. Defaults to an empty paragraph tag.} - \item{app}{(\code{environment}) The \code{teal} app environment.} + +\item{header}{(\code{shiny.tag} or \code{character(1)}) The header content to set. Defaults to an empty paragraph tag.} } \description{ Adds a header to the \code{teal} app. @@ -19,7 +19,7 @@ app <- init( data = teal_data(IRIS = iris), modules = modules(example_module()) ) |> - add_header( + modify_header( tags$div( h3("Custom header") ) diff --git a/man/add_title.Rd b/man/modify_title.Rd similarity index 64% rename from man/add_title.Rd rename to man/modify_title.Rd index 5a3eec9bd6..9f6e845b40 100644 --- a/man/add_title.Rd +++ b/man/modify_title.Rd @@ -1,10 +1,15 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/init.R -\name{add_title} -\alias{add_title} +\name{modify_title} +\alias{modify_title} \title{Add a custom title to the app} \usage{ -add_title(app, title = build_app_title()) +modify_title( + app, + title = "teal app", + favicon = + "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png" +) } \arguments{ \item{title}{The title to add} @@ -20,7 +25,7 @@ app <- init( data = teal_data(IRIS = iris, MTCARS = mtcars), modules = modules(example_module()) ) |> - add_title("Custom title") + modify_title("Custom title") shinyApp(app$ui, app$server) } diff --git a/man/module_teal.Rd b/man/module_teal.Rd index dbf5149492..7e34b653e6 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -31,15 +31,15 @@ more details.} the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or by passing a valid \code{shiny.tag} which is a head tag with title and link tag. -This parameter is deprecated. Use \code{add_title()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} \item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the header of the app. -This parameter is deprecated. Use \code{add_header()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} \item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the footer of the app. -This parameter is deprecated. Use \code{add_footer()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} \item{data}{(\code{teal_data}, \code{teal_data_module}, or \code{reactive} returning \code{teal_data}) The data which application will depend on.} diff --git a/man/module_teal_with_splash.Rd b/man/module_teal_with_splash.Rd index 240ab8bb64..c8fb484c10 100644 --- a/man/module_teal_with_splash.Rd +++ b/man/module_teal_with_splash.Rd @@ -28,15 +28,15 @@ The data which application will depend on.} the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or by passing a valid \code{shiny.tag} which is a head tag with title and link tag. -This parameter is deprecated. Use \code{add_title()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} \item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the header of the app. -This parameter is deprecated. Use \code{add_header()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} \item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the footer of the app. -This parameter is deprecated. Use \code{add_footer()} on the teal app object instead.} +This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} \item{modules}{(\code{list} or \code{teal_modules} or \code{teal_module}) Nested list of \code{teal_modules} or \code{teal_module} objects or a single From aa60c141186820e470e61992a59c891637ffff56 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 13:28:01 +0530 Subject: [PATCH 06/50] chore: minor doc changes --- R/init.R | 4 ++-- man/init.Rd | 6 +++--- man/modify_footer.Rd | 2 +- man/modify_header.Rd | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/R/init.R b/R/init.R index e81f3bc5a9..e21cc9d549 100644 --- a/R/init.R +++ b/R/init.R @@ -315,7 +315,7 @@ modify_title <- function( #' ) #' #' shinyApp(app$ui, app$server) -modify_header <- function(app, header = "test") { +modify_header <- function(app, header = tags$p()) { res <- app res$ui <- function(request, ...) { args <- list(...) @@ -342,7 +342,7 @@ modify_header <- function(app, header = "test") { #' modify_footer("Custom footer") #' #' shinyApp(app$ui, app$server) -modify_footer <- function(app, footer = "test") { +modify_footer <- function(app, footer = tags$p()) { res <- app res$ui <- function(request, ...) { args <- list(...) diff --git a/man/init.Rd b/man/init.Rd index cdb3b09c50..33cee85f9f 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -8,9 +8,9 @@ init( data, modules, filter = teal_slices(), - title = build_app_title(), - header = tags$p(), - footer = tags$p(), + title = NULL, + header = NULL, + footer = NULL, id = character(0) ) } diff --git a/man/modify_footer.Rd b/man/modify_footer.Rd index b25a4d8d93..d7bf48d8bb 100644 --- a/man/modify_footer.Rd +++ b/man/modify_footer.Rd @@ -4,7 +4,7 @@ \alias{modify_footer} \title{Add a Footer to a \code{teal} App} \usage{ -modify_footer(app, footer = "test") +modify_footer(app, footer = tags$p()) } \arguments{ \item{app}{(\code{environment}) The \code{teal} app environment.} diff --git a/man/modify_header.Rd b/man/modify_header.Rd index 93ab8ada75..f4a13aeb55 100644 --- a/man/modify_header.Rd +++ b/man/modify_header.Rd @@ -4,7 +4,7 @@ \alias{modify_header} \title{Add a Header to a \code{teal} App} \usage{ -modify_header(app, header = "test") +modify_header(app, header = tags$p()) } \arguments{ \item{app}{(\code{environment}) The \code{teal} app environment.} From f5cf6a5d29f634a5f593546a8ff7192b34b6f7ec Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 14:05:13 +0530 Subject: [PATCH 07/50] docs: add news and update init --- NEWS.md | 5 ++++- R/init.R | 56 +++++++++++++++++++++++++++---------------------------- R/utils.R | 2 +- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/NEWS.md b/NEWS.md index ee1a092bc5..4c06c1ff8d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,13 +9,16 @@ * Introduced a function `set_datanames()` to change a `datanames` of the `teal_module`. * Datasets which name starts with `.` are ignored when `module`'s `datanames` is set as `"all"`. * Added warning when reserved `datanames`, such as `all` and `.raw_data` are being used. +* Added `add_custom_server()` to allow adding custom server logic to the main shiny server function of a teal app. ### Breaking changes * Setting `datanames()` on `data` passed to teal application no longer has effect. In order to change `teal_module`'s `datanames` one should modify `module$datanames`. -* The `landing_popup_module()` needs to be passed as the `landing_popup` argument of `init` instead of being passed as a module of the `modules` argument of `init`. +* `landing_popup_module()` is deprecated. Please use `add_landing_popup()` function to add a landing popup for your teal app. * `teal` no longer re-export `%>%`. Please load `library(magrittr)` instead or use `|>` from `base`. +* `build_app_title` will stop being exported in the future release. Please use the `modify_title()` function to change the title for your teal app. +* The `title`, `header`, and `footer` arguments of the `init()` function are deprecated. Please use the `modify_title`, `modify_header`, and `modify_footer` respectively. ### Enhancement diff --git a/R/init.R b/R/init.R index e21cc9d549..461b978caf 100644 --- a/R/init.R +++ b/R/init.R @@ -95,9 +95,9 @@ init <- function(data, modules, filter = teal_slices(), - title = build_app_title(), - header = tags$p(), - footer = tags$p(), + title = NULL, + header = NULL, + footer = NULL, id = character(0)) { logger::log_debug("init initializing teal app with: data ('{ class(data) }').") @@ -186,9 +186,6 @@ init <- function(data, ) } - res <- list() - res$landing_popup_server <- NULL - if (!is.null(title)) { checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) lifecycle::deprecate_warn( @@ -196,6 +193,8 @@ init <- function(data, what = "init(title)", details = "Use `modify_title()` on the teal app object instead." ) + } else { + title <- build_app_title() } if (!is.null(header)) { checkmate::assert_multi_class(header, c("shiny.tag", "shiny.tag.list", "html", "character")) @@ -206,6 +205,8 @@ init <- function(data, "Use `modify_header()` on the teal app object instead." ) ) + } else { + header <- tags$p() } if (!is.null(footer)) { checkmate::assert_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html", "character")) @@ -216,13 +217,15 @@ init <- function(data, "Use `modify_footer()` on the teal app object instead." ) ) + } else { + footer <- tags$p() } # argument transformations ## `modules` - landing module landing <- extract_module(modules, "teal_module_landing") if (length(landing) == 1L) { - res$landing_popup_server <- landing[[1L]]$server + landing_popup_server <- landing[[1L]]$server modules <- drop_module(modules, "teal_module_landing") lifecycle::deprecate_warn( when = "0.15.3", @@ -237,21 +240,23 @@ init <- function(data, } # Note: UI must be a function to support bookmarking. - res$ui <- function(request, ...) { - ui_teal( - id = "teal", - modules = modules, - title = title, - header = header, - footer = footer - ) - } - res$server <- function(input, output, session) { - if (!is.null(res$landing_popup_server)) { - do.call(res$landing_popup_server, c(list(id = "landing_module_shiny_id"))) + res <- list( + ui = function(request, ...) { + ui_teal( + id = "teal", + modules = modules, + title = title, + header = header, + footer = footer + ) + }, + server = function(input, output, session) { + if (!is.null(landing_popup_server)) { + do.call(landing_popup_server, c(list(id = "landing_module_shiny_id"))) + } + srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) } - srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) - } + ) logger::log_debug("init teal app has been initialized.") @@ -280,14 +285,7 @@ modify_title <- function( args <- list(...) args$title <- tags$div( id = "teal-title", - tags$head( - tags$title(title), - tags$link( - rel = "icon", - href = favicon, - sizes = "any" - ) - ) + build_app_title(title, favicon) ) ui_tq <- do.call(app$ui, c(list(request = request), args)) |> htmltools::tagQuery() diff --git a/R/utils.R b/R/utils.R index a5276e9563..e6a9fc4085 100644 --- a/R/utils.R +++ b/R/utils.R @@ -315,7 +315,7 @@ build_app_title <- function( favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { checkmate::assert_string(title, null.ok = TRUE) checkmate::assert_string(favicon, null.ok = TRUE) - lifecycle::deprecate_warn( + lifecycle::deprecate_soft( when = "0.15.3", what = "build_app_title()", details = "Use `modify_title()` on the teal app object instead." From 8f761d46bd3a9c8fa6e65bcf9149338ca6ca806d Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 14:27:15 +0530 Subject: [PATCH 08/50] @gogonzo comments --- R/init.R | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/R/init.R b/R/init.R index 461b978caf..d589307be9 100644 --- a/R/init.R +++ b/R/init.R @@ -287,8 +287,8 @@ modify_title <- function( id = "teal-title", build_app_title(title, favicon) ) - ui_tq <- do.call(app$ui, c(list(request = request), args)) |> - htmltools::tagQuery() + ui_tq <- htmltools::tagQuery(do.call(app$ui, c(list(request = request), args))) + ui_tq$find("#teal-title")$replaceWith(args$title)$allTags() } res @@ -318,8 +318,7 @@ modify_header <- function(app, header = tags$p()) { res$ui <- function(request, ...) { args <- list(...) args$header <- header - ui_tq <- do.call(app$ui, c(list(request = request), args)) |> - htmltools::tagQuery() + ui_tq <- htmltools::tagQuery(do.call(app$ui, c(list(request = request), args))) ui_tq$find("#teal-header")$replaceWith(tags$header(id = "teal-header", args$header))$allTags() } res @@ -345,8 +344,7 @@ modify_footer <- function(app, footer = tags$p()) { res$ui <- function(request, ...) { args <- list(...) args$footer <- footer - ui_tq <- do.call(app$ui, c(list(request = request), args)) |> - htmltools::tagQuery() + ui_tq <- htmltools::tagQuery(do.call(app$ui, c(list(request = request), args))) ui_tq$find("#teal-footer")$replaceWith(tags$div(id = "teal-footer", args$footer))$allTags() } res From 0af8a67c7ad5899d71103a565d3ecccefc425803 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 15:09:19 +0530 Subject: [PATCH 09/50] improvements from @gogonzo --- R/init.R | 63 +++++++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/R/init.R b/R/init.R index d589307be9..cab1f58175 100644 --- a/R/init.R +++ b/R/init.R @@ -224,24 +224,11 @@ init <- function(data, # argument transformations ## `modules` - landing module landing <- extract_module(modules, "teal_module_landing") - if (length(landing) == 1L) { - landing_popup_server <- landing[[1L]]$server - modules <- drop_module(modules, "teal_module_landing") - lifecycle::deprecate_warn( - when = "0.15.3", - what = "landing_popup_module()", - details = paste( - "landing_popup_module() is deprecated.", - "Use add_landing_popup() on the teal app object instead." - ) - ) - } else if (length(landing) > 1L) { - stop("Only one `landing_popup_module` can be used.") - } + modules <- drop_module(modules, "teal_module_landing") # Note: UI must be a function to support bookmarking. res <- list( - ui = function(request, ...) { + ui = function(request) { ui_teal( id = "teal", modules = modules, @@ -251,13 +238,26 @@ init <- function(data, ) }, server = function(input, output, session) { - if (!is.null(landing_popup_server)) { - do.call(landing_popup_server, c(list(id = "landing_module_shiny_id"))) - } srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) } ) + if (length(landing) == 1L) { + res <- add_custom_server(res, function(input, output, session) { + do.call(landing[[1L]]$server, c(list(id = "landing_module_shiny_id"))) + }) + lifecycle::deprecate_warn( + when = "0.15.3", + what = "landing_popup_module()", + details = paste( + "landing_popup_module() is deprecated.", + "Use add_landing_popup() on the teal app object instead." + ) + ) + } else if (length(landing) > 1L) { + stop("Only one `landing_popup_module` can be used.") + } + logger::log_debug("init teal app has been initialized.") res @@ -281,15 +281,13 @@ modify_title <- function( title = "teal app", favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { res <- app - res$ui <- function(request, ...) { - args <- list(...) - args$title <- tags$div( + res$ui <- function(request) { + title <- tags$div( id = "teal-title", build_app_title(title, favicon) ) - ui_tq <- htmltools::tagQuery(do.call(app$ui, c(list(request = request), args))) - - ui_tq$find("#teal-title")$replaceWith(args$title)$allTags() + ui_tq <- htmltools::tagQuery(app$ui(request = request)) + ui_tq$find("#teal-title")$replaceWith(title)$allTags() } res } @@ -315,11 +313,9 @@ modify_title <- function( #' shinyApp(app$ui, app$server) modify_header <- function(app, header = tags$p()) { res <- app - res$ui <- function(request, ...) { - args <- list(...) - args$header <- header - ui_tq <- htmltools::tagQuery(do.call(app$ui, c(list(request = request), args))) - ui_tq$find("#teal-header")$replaceWith(tags$header(id = "teal-header", args$header))$allTags() + res$ui <- function(request) { + ui_tq <- htmltools::tagQuery(app$ui(request = request)) + ui_tq$find("#teal-header")$replaceWith(tags$header(id = "teal-header", header))$allTags() } res } @@ -341,11 +337,9 @@ modify_header <- function(app, header = tags$p()) { #' shinyApp(app$ui, app$server) modify_footer <- function(app, footer = tags$p()) { res <- app - res$ui <- function(request, ...) { - args <- list(...) - args$footer <- footer - ui_tq <- htmltools::tagQuery(do.call(app$ui, c(list(request = request), args))) - ui_tq$find("#teal-footer")$replaceWith(tags$div(id = "teal-footer", args$footer))$allTags() + res$ui <- function(request) { + ui_tq <- htmltools::tagQuery(app$ui(request = request)) + ui_tq$find("#teal-footer")$replaceWith(tags$div(id = "teal-footer", footer))$allTags() } res } @@ -412,7 +406,6 @@ add_landing_popup <- function( #' modules = modules(example_module()) #' ) |> #' add_custom_server(function(input, output, session) { -#' print("injected server logic to the main shiny server function") #' }) #' #' shinyApp(app$ui, app$server) From e6acc983d7139df4fee342a291d415e4449d4d18 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 19:45:21 +0530 Subject: [PATCH 10/50] feat: modify the TealAppDriver args --- R/TealAppDriver.R | 16 ++++++++++++++-- R/init.R | 4 ++-- R/utils.R | 5 ----- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index af81466744..ad6e6ad396 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -26,7 +26,7 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. #' @description #' Initialize a `TealAppDriver` object for testing a `teal` application. #' - #' @param data,modules,filter,title,header,footer,landing_popup arguments passed to `init` + #' @param data,modules,filter arguments passed to `init` and title,header,footer,landing_popup modifiers #' @param timeout (`numeric`) Default number of milliseconds for any timeout or #' timeout_ parameter in the `TealAppDriver` class. #' Defaults to 20s. @@ -45,6 +45,10 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. initialize = function(data, modules, filter = teal_slices(), + title_args = list(title = "App title"), + header = tags$p(), + footer = tags$p(), + landing_popup_args = NULL, timeout = rlang::missing_arg(), load_timeout = rlang::missing_arg(), ...) { @@ -55,7 +59,15 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. data = data, modules = modules, filter = filter - ) + ) |> + modify_header(header) |> + modify_footer(footer) + + app <- do.call(modify_title, c(list(app = app), title_args)) + + if (!is.null(landing_popup_args)) { + app <- do.call(add_landing_popup, c(list(app = app), landing_popup_args)) + } # Default timeout is hardcoded to 4s in shinytest2:::resolve_timeout # It must be set as parameter to the AppDriver diff --git a/R/init.R b/R/init.R index cab1f58175..1105e2e983 100644 --- a/R/init.R +++ b/R/init.R @@ -282,12 +282,12 @@ modify_title <- function( favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { res <- app res$ui <- function(request) { - title <- tags$div( + title_tag <- tags$div( id = "teal-title", build_app_title(title, favicon) ) ui_tq <- htmltools::tagQuery(app$ui(request = request)) - ui_tq$find("#teal-title")$replaceWith(title)$allTags() + ui_tq$find("#teal-title")$replaceWith(title_tag)$allTags() } res } diff --git a/R/utils.R b/R/utils.R index e6a9fc4085..46fcddc1a3 100644 --- a/R/utils.R +++ b/R/utils.R @@ -315,11 +315,6 @@ build_app_title <- function( favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { checkmate::assert_string(title, null.ok = TRUE) checkmate::assert_string(favicon, null.ok = TRUE) - lifecycle::deprecate_soft( - when = "0.15.3", - what = "build_app_title()", - details = "Use `modify_title()` on the teal app object instead." - ) tags$head( tags$title(title), tags$link( From 66fb1aa1a4643444cb965fc3fef63ca2b693ad8f Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 20:07:47 +0530 Subject: [PATCH 11/50] fix: maks sure the chromote app runs --- R/TealAppDriver.R | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index ad6e6ad396..056bf9b6b2 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -45,7 +45,7 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. initialize = function(data, modules, filter = teal_slices(), - title_args = list(title = "App title"), + title_args, header = tags$p(), footer = tags$p(), landing_popup_args = NULL, @@ -55,18 +55,40 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. private$data <- data private$modules <- modules private$filter <- filter + + new_title <- modifyList( + list( + title = "Custom Teal App Title", + favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/teal.png" + ), + title_args + ) app <- init( data = data, modules = modules, filter = filter ) |> + modify_title(title = new_title$title, favicon = new_title$favicon) |> modify_header(header) |> modify_footer(footer) - app <- do.call(modify_title, c(list(app = app), title_args)) - if (!is.null(landing_popup_args)) { - app <- do.call(add_landing_popup, c(list(app = app), landing_popup_args)) + args <- modifyList( + list( + id = "landingpopup", + title = NULL, + content = NULL, + buttons = modalButton("Accept") + ), + landing_popup_args + ) + app <- app |> + add_landing_popup( + id = args$id, + title = args$title, + content = args$content, + buttons = args$button + ) } # Default timeout is hardcoded to 4s in shinytest2:::resolve_timeout From 9019c6e9a6d59e2ad542d3a790ffd2c42aa04f6d Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 20:27:12 +0530 Subject: [PATCH 12/50] fix: update the tests based on new TealAppDriver --- R/TealAppDriver.R | 2 +- tests/testthat/test-shinytest2-init.R | 23 ++--- .../testthat/test-shinytest2-landing_popup.R | 94 ++++++++----------- 3 files changed, 51 insertions(+), 68 deletions(-) diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index 056bf9b6b2..c187b38a3c 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -45,7 +45,7 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. initialize = function(data, modules, filter = teal_slices(), - title_args, + title_args = list(), header = tags$p(), footer = tags$p(), landing_popup_args = NULL, diff --git a/tests/testthat/test-shinytest2-init.R b/tests/testthat/test-shinytest2-init.R index e0672e7e53..854e087e33 100644 --- a/tests/testthat/test-shinytest2-init.R +++ b/tests/testthat/test-shinytest2-init.R @@ -68,39 +68,36 @@ testthat::test_that("e2e: teal app initializes with sessionInfo modal", { testthat::test_that("e2e: init creates UI containing specified title, favicon, header and footer", { skip_if_too_deep(5) - app_title <- "Custom Teal App Title" - app_favicon <- "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/teal.png" - app_header <- "Custom Teal App Header" - app_footer <- "Custom Teal App Footer" app <- TealAppDriver$new( data = simple_teal_data(), modules = example_module(label = "Example Module"), - title = build_app_title( - app_title, - app_favicon + title_args = list( + title = "Custom Teal App Title", + something_else = "asdfsdf", + favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/teal.png" ), - header = app_header, - footer = app_footer + header = "Custom Teal App Header", + footer = "Custom Teal App Footer" ) testthat::expect_equal( app$get_text("head > title")[1], - app_title + "Custom Teal App Title" ) testthat::expect_equal( rvest::html_attr( rvest::html_elements(app$get_html_rvest("head > link[rel='icon']"), "link"), "href" ), - app_favicon + "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/teal.png" ) testthat::expect_match( app$get_text("header"), - app_header + "Custom Teal App Header" ) testthat::expect_match( app$get_text("footer"), - app_footer + "Custom Teal App Footer" ) app$stop() }) diff --git a/tests/testthat/test-shinytest2-landing_popup.R b/tests/testthat/test-shinytest2-landing_popup.R index b402b60771..187f850252 100644 --- a/tests/testthat/test-shinytest2-landing_popup.R +++ b/tests/testthat/test-shinytest2-landing_popup.R @@ -8,7 +8,7 @@ testthat::test_that("e2e: teal app with landing_popup_module initializes with no modules = modules( example_module() ), - landing_popup = landing_popup_module( + landing_popup_args = list( title = "Welcome", content = tags$b("A welcome message!", style = "color: red;") ) @@ -26,9 +26,9 @@ testthat::test_that("e2e: app with default landing_popup_module creates modal co app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module(), - landing_popup_module() - ) + example_module() + ), + landing_popup_args = list() ) testthat::expect_equal( @@ -44,9 +44,9 @@ testthat::test_that("e2e: when default landing_popup_module is closed, it shows app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module(), - landing_popup_module() - ) + example_module() + ), + landing_popup_args = list() ) # Button is clicked. @@ -65,77 +65,64 @@ testthat::test_that( "e2e: app with customized landing_popup_module creates modal containing specified title, content and buttons", { skip_if_too_deep(5) - phash <- function(text) paste0("#", text) - - modal_title <- "Custom Landing Popup Module Title" - modal_content_message <- "A welcome message!" - modal_content <- tags$b(modal_content_message, style = "color: red;") - - modal_btns <- list( - go = list(text = "Proceed"), - more = list(text = "Read more", onclick = "window.open('http://google.com', '_blank')", id = "read"), - reject = list(text = "Reject", onclick = "window.close()", id = "close") - ) - modal_buttons <- - tagList( - shiny::modalButton(modal_btns$go$text), - shiny::actionButton( - modal_btns$more$id, - label = modal_btns$more$text, - onclick = modal_btns$more$onclick - ), - shiny::actionButton( - modal_btns$reject$id, - label = modal_btns$reject$text, - onclick = modal_btns$reject$onclick - ) - ) app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module(), - landing_popup_module( - title = modal_title, - content = modal_content, - buttons = modal_buttons + example_module() + ), + landing_popup_args = list( + title = "Custom Landing Popup Module Title", + content = tags$b("A welcome message!", style = "color: red;"), + buttons = tagList( + shiny::modalButton("Proceed"), + shiny::actionButton( + "read", + label = "Read more", + onclick = "window.open('http://google.com', '_blank')" + ), + shiny::actionButton( + "close", + label = "Reject", + onclick = "window.close()" + ) ) ) ) testthat::expect_equal( app$get_text(".modal-title"), - modal_title + "Custom Landing Popup Module Title" ) testthat::expect_equal( trimws(app$get_text(".modal-body")), - modal_content_message + "A welcome message!" ) testthat::expect_equal( app$get_text(".btn-default:nth-child(1)"), - modal_btns$go$text + "Proceed" ) testthat::expect_equal( - app$get_text(phash(modal_btns$more$id)), - modal_btns$more$text + app$get_text("#read"), + "Read more" ) testthat::expect_equal( - app$get_attr(phash(modal_btns$more$id), "onclick"), - modal_btns$more$onclick + app$get_attr("#read", "onclick"), + "window.open('http://google.com', '_blank')" ) testthat::expect_equal( - app$get_text(phash(modal_btns$reject$id)), - modal_btns$reject$text + app$get_text("#close"), + "Reject" ) testthat::expect_equal( - app$get_attr(phash(modal_btns$reject$id), "onclick"), - modal_btns$reject$onclick + app$get_attr("#close", "onclick"), + "window.close()" ) app$stop() @@ -144,20 +131,19 @@ testthat::test_that( testthat::test_that("e2e: when customized button in landing_popup_module is clicked, it redirects to a certain page", { skip_if_too_deep(5) - onclick_text <- "window.open('http://google.com', '_blank')" app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( - example_module(), - landing_popup_module( - buttons = actionButton("read", "Read more", onclick = onclick_text) - ) + example_module() + ), + landing_popup_args = list( + buttons = actionButton("read", "Read more", onclick = "window.open('http://google.com', '_blank')") ) ) testthat::expect_equal( app$get_attr("#read", "onclick"), - onclick_text + "window.open('http://google.com', '_blank')" ) app$stop() From c51ac219fe4969a311d79fb952c6465e797dac6b Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Fri, 10 Jan 2025 20:31:42 +0530 Subject: [PATCH 13/50] Update R/init.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: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/init.R b/R/init.R index 1105e2e983..d3caf7b757 100644 --- a/R/init.R +++ b/R/init.R @@ -379,8 +379,7 @@ add_landing_popup <- function( buttons = modalButton("Accept")) { old_server <- app$server - app$server <- function(input, output, session) { - old_server(input, output, session) + custom_server <- function(input, output, session() { showModal( modalDialog( id = id, @@ -390,6 +389,7 @@ add_landing_popup <- function( ) ) } + app$server <- add_custom_server(app, custom_server) app } From 2f4eb707bc29b7254c62c3713f867668ae0b3849 Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:35:42 +0530 Subject: [PATCH 14/50] Update R/init.R Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index d3caf7b757..0d855e08e5 100644 --- a/R/init.R +++ b/R/init.R @@ -265,7 +265,7 @@ init <- function(data, #' Add a custom title to the app #' -#' @param title The title to add +#' @param title (`shiny.tag` or `character(1)`) The title to be used #' @return The modified app object #' @export #' @examples From c6526930aa0f22bb1b3d3dcc108b30b5df0d131d Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:35:49 +0530 Subject: [PATCH 15/50] Update R/init.R Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index 0d855e08e5..88d4edea77 100644 --- a/R/init.R +++ b/R/init.R @@ -263,7 +263,7 @@ init <- function(data, res } -#' Add a custom title to the app +#' Add a custom Title to `teal` application #' #' @param title (`shiny.tag` or `character(1)`) The title to be used #' @return The modified app object From 858c1a7a9e5ea10b0b783745cc252cd77cc5b7fe Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:38:39 +0530 Subject: [PATCH 16/50] Update R/init.R Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index 88d4edea77..da57ea5407 100644 --- a/R/init.R +++ b/R/init.R @@ -279,7 +279,8 @@ init <- function(data, modify_title <- function( app, title = "teal app", - favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { + favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png" + ) { res <- app res$ui <- function(request) { title_tag <- tags$div( From 3d84da6c8c25c896cbc8f3d46d86014eeb767fd2 Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:39:43 +0530 Subject: [PATCH 17/50] Update R/init.R Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index da57ea5407..0c0adefb75 100644 --- a/R/init.R +++ b/R/init.R @@ -293,7 +293,7 @@ modify_title <- function( res } -#' Add a Header to a `teal` App +#' Add a Header to `teal` Application #' #' @description Adds a header to the `teal` app. #' From 104aa9c7fe983513e83087ed6dbba453601a8a96 Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:39:50 +0530 Subject: [PATCH 18/50] Update R/init.R Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index 0c0adefb75..63be0b7dda 100644 --- a/R/init.R +++ b/R/init.R @@ -394,7 +394,7 @@ add_landing_popup <- function( app } -#' Add a Custom Server Logic to a `teal` App +#' Add a Custom Server Logic to `teal` Application #' #' @description Adds a custom server function to the `teal` app. This function can define additional server logic. #' From daa4a3d519831f11a32d2f72e9cc9787c96242a2 Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:39:57 +0530 Subject: [PATCH 19/50] Update _pkgdown.yml Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- _pkgdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index f0eca2c428..fcc03e5d09 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -108,7 +108,7 @@ reference: - module - modules - teal_slices - - title: Teal app modifiers + - title: `teal` application modifiers desc: Functions to modify the `teal` app object contents: - modify_footer From 9c648f2428dfcc2738b4255e3849d9802b6325d5 Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:40:04 +0530 Subject: [PATCH 20/50] Update R/init.R Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index 63be0b7dda..b1f6e99aab 100644 --- a/R/init.R +++ b/R/init.R @@ -345,7 +345,7 @@ modify_footer <- function(app, footer = tags$p()) { res } -#' Add a Landing Popup to a `teal` App +#' Add a Landing Popup to `teal` Application #' #' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. #' From 4962e280ac78ec15b1001765ae7cb57aa077909a Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Mon, 13 Jan 2025 15:40:11 +0530 Subject: [PATCH 21/50] Update R/init.R Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Vedha Viyash <49812166+vedhav@users.noreply.github.com> --- R/init.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/init.R b/R/init.R index b1f6e99aab..263c81f20b 100644 --- a/R/init.R +++ b/R/init.R @@ -321,7 +321,7 @@ modify_header <- function(app, header = tags$p()) { res } -#' Add a Footer to a `teal` App +#' Add a Footer to `teal` Application #' #' @description Adds a footer to the `teal` app. #' From ab6e3fd24a3aefecfbd5488676496cd8ab43759c Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 17:18:33 +0530 Subject: [PATCH 22/50] fix: fix the broken test --- DESCRIPTION | 2 +- R/TealAppDriver.R | 32 +++++++++++++++++--------------- R/init.R | 10 ++++------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 397f7570cc..40ba52e96d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -35,7 +35,7 @@ URL: https://insightsengineering.github.io/teal/, https://github.com/insightsengineering/teal/ BugReports: https://github.com/insightsengineering/teal/issues Depends: - R (>= 4.0), + R (>= 4.1), shiny (>= 1.8.1), teal.data (>= 0.6.0.9017), teal.slice (>= 0.5.1.9015) diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index c187b38a3c..52c2b6ac07 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -73,22 +73,24 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. modify_footer(footer) if (!is.null(landing_popup_args)) { - args <- modifyList( - list( - id = "landingpopup", - title = NULL, - content = NULL, - buttons = modalButton("Accept") - ), - landing_popup_args + default_args <- list( + id = "landingpopup", + title = NULL, + content = NULL, + buttons = modalButton("Accept") + ) + landing_popup_args[names(default_args)] <- Map( + function(x, y) if (is.null(y)) x else y, + default_args, + landing_popup_args[names(default_args)] + ) + app <- add_landing_popup( + app, + id = landing_popup_args$id, + title = landing_popup_args$title, + content = landing_popup_args$content, + buttons = landing_popup_args$button ) - app <- app |> - add_landing_popup( - id = args$id, - title = args$title, - content = args$content, - buttons = args$button - ) } # Default timeout is hardcoded to 4s in shinytest2:::resolve_timeout diff --git a/R/init.R b/R/init.R index 263c81f20b..66fcde5be7 100644 --- a/R/init.R +++ b/R/init.R @@ -279,8 +279,7 @@ init <- function(data, modify_title <- function( app, title = "teal app", - favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png" - ) { + favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { res <- app res$ui <- function(request) { title_tag <- tags$div( @@ -378,9 +377,7 @@ add_landing_popup <- function( title = NULL, content = NULL, buttons = modalButton("Accept")) { - old_server <- app$server - - custom_server <- function(input, output, session() { + custom_server <- function(input, output, session) { showModal( modalDialog( id = id, @@ -390,7 +387,7 @@ add_landing_popup <- function( ) ) } - app$server <- add_custom_server(app, custom_server) + app <- add_custom_server(app, custom_server) app } @@ -407,6 +404,7 @@ add_landing_popup <- function( #' modules = modules(example_module()) #' ) |> #' add_custom_server(function(input, output, session) { +#' print("injected server logic") #' }) #' #' shinyApp(app$ui, app$server) From 863312723b0185b93aaaf45a8b756ebb4b49bc8d Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 17:30:32 +0530 Subject: [PATCH 23/50] chore: add missing args to deprecated args --- R/init.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/init.R b/R/init.R index 66fcde5be7..5b6dbee0ac 100644 --- a/R/init.R +++ b/R/init.R @@ -95,9 +95,9 @@ init <- function(data, modules, filter = teal_slices(), - title = NULL, - header = NULL, - footer = NULL, + title = lifecycle::deprecated(), + header = lifecycle::deprecated(), + footer = lifecycle::deprecated(), id = character(0)) { logger::log_debug("init initializing teal app with: data ('{ class(data) }').") @@ -186,7 +186,7 @@ init <- function(data, ) } - if (!is.null(title)) { + if (lifecycle::is_present(title)) { checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) lifecycle::deprecate_warn( when = "0.15.3", @@ -196,7 +196,7 @@ init <- function(data, } else { title <- build_app_title() } - if (!is.null(header)) { + if (lifecycle::is_present(header)) { checkmate::assert_multi_class(header, c("shiny.tag", "shiny.tag.list", "html", "character")) lifecycle::deprecate_warn( when = "0.15.3", @@ -208,7 +208,7 @@ init <- function(data, } else { header <- tags$p() } - if (!is.null(footer)) { + if (lifecycle::is_present(footer)) { checkmate::assert_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html", "character")) lifecycle::deprecate_warn( when = "0.15.3", From 08869e55ad58929e209733b1f79915b770cb9b19 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 17:38:53 +0530 Subject: [PATCH 24/50] chore: stop using `build_app_title` --- NEWS.md | 4 ++-- R/init.R | 18 ++++++++++++++++-- R/utils.R | 5 +++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index ed1cc2661e..b5a741452d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,9 +14,9 @@ * Setting `datanames()` on `data` passed to teal application no longer has effect. In order to change `teal_module`'s `datanames` one should modify `module$datanames`. -* `landing_popup_module()` is deprecated. Please use `add_landing_popup()` function to add a landing popup for your teal app. +* `landing_popup_module()` is deprecated. Please use `add_landing_popup()` function to add a landing popup for your teal application. * `teal` no longer re-export `%>%`. Please load `library(magrittr)` instead or use `|>` from `base`. -* `build_app_title` will stop being exported in the future release. Please use the `modify_title()` function to change the title for your teal app. +* `build_app_title` will be removed in the future release. Please use the `modify_title()` function to change the title for your teal application. * The `title`, `header`, and `footer` arguments of the `init()` function are deprecated. Please use the `modify_title`, `modify_header`, and `modify_footer` respectively. ### Enhancement diff --git a/R/init.R b/R/init.R index f173c5b451..5d7c8d38e9 100644 --- a/R/init.R +++ b/R/init.R @@ -193,7 +193,14 @@ init <- function(data, details = "Use `modify_title()` on the teal app object instead." ) } else { - title <- build_app_title() + title <- tags$head( + tags$title("teal app"), + tags$link( + rel = "icon", + href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", + sizes = "any" + ) + ) } if (lifecycle::is_present(header)) { checkmate::assert_multi_class(header, c("shiny.tag", "shiny.tag.list", "html", "character")) @@ -283,7 +290,14 @@ modify_title <- function( res$ui <- function(request) { title_tag <- tags$div( id = "teal-title", - build_app_title(title, favicon) + tags$head( + tags$title(title), + tags$link( + rel = "icon", + href = favicon, + sizes = "any" + ) + ) ) ui_tq <- htmltools::tagQuery(app$ui(request = request)) ui_tq$find("#teal-title")$replaceWith(title_tag)$allTags() diff --git a/R/utils.R b/R/utils.R index 46fcddc1a3..0f06e1e486 100644 --- a/R/utils.R +++ b/R/utils.R @@ -315,6 +315,11 @@ build_app_title <- function( favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { checkmate::assert_string(title, null.ok = TRUE) checkmate::assert_string(favicon, null.ok = TRUE) + lifecycle::deprecate_soft( + when = "0.15.3", + what = "build_app_title()", + details = "Use `modify_title()` on the object created using the `init`." + ) tags$head( tags$title(title), tags$link( From db3def827a7c7c0e550706386c73ae03f330f5e8 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:18:07 +0000 Subject: [PATCH 25/50] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/TealAppDriver.Rd | 6 +++++- man/add_custom_server.Rd | 4 ++-- man/add_landing_popup.Rd | 2 +- man/init.Rd | 6 +++--- man/modify_footer.Rd | 2 +- man/modify_header.Rd | 2 +- man/modify_title.Rd | 6 +++--- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/man/TealAppDriver.Rd b/man/TealAppDriver.Rd index 8a433d6263..e34d9334d1 100644 --- a/man/TealAppDriver.Rd +++ b/man/TealAppDriver.Rd @@ -98,6 +98,10 @@ Initialize a \code{TealAppDriver} object for testing a \code{teal} application. data, modules, filter = teal_slices(), + title_args = list(), + header = tags$p(), + footer = tags$p(), + landing_popup_args = NULL, timeout = rlang::missing_arg(), load_timeout = rlang::missing_arg(), ... @@ -107,7 +111,7 @@ Initialize a \code{TealAppDriver} object for testing a \code{teal} application. \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{data, modules, filter, title, header, footer, landing_popup}}{arguments passed to \code{init}} +\item{\code{data, modules, filter}}{arguments passed to \code{init} and title,header,footer,landing_popup modifiers} \item{\code{timeout}}{(\code{numeric}) Default number of milliseconds for any timeout or timeout_ parameter in the \code{TealAppDriver} class. diff --git a/man/add_custom_server.Rd b/man/add_custom_server.Rd index 8414de06f1..4c8bb352e4 100644 --- a/man/add_custom_server.Rd +++ b/man/add_custom_server.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/init.R \name{add_custom_server} \alias{add_custom_server} -\title{Add a Custom Server Logic to a \code{teal} App} +\title{Add a Custom Server Logic to \code{teal} Application} \usage{ add_custom_server(app, custom_server) } @@ -20,7 +20,7 @@ app <- init( modules = modules(example_module()) ) |> add_custom_server(function(input, output, session) { - print("injected server logic to the main shiny server function") + print("injected server logic") }) shinyApp(app$ui, app$server) diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd index 27656e38c7..0b2b701a88 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_popup.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/init.R \name{add_landing_popup} \alias{add_landing_popup} -\title{Add a Landing Popup to a \code{teal} App} +\title{Add a Landing Popup to \code{teal} Application} \usage{ add_landing_popup( app, diff --git a/man/init.Rd b/man/init.Rd index 3e21540cce..5b261f9e23 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -8,9 +8,9 @@ init( data, modules, filter = teal_slices(), - title = NULL, - header = NULL, - footer = NULL, + title = lifecycle::deprecated(), + header = lifecycle::deprecated(), + footer = lifecycle::deprecated(), id = character(0) ) } diff --git a/man/modify_footer.Rd b/man/modify_footer.Rd index d7bf48d8bb..9db57a4659 100644 --- a/man/modify_footer.Rd +++ b/man/modify_footer.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/init.R \name{modify_footer} \alias{modify_footer} -\title{Add a Footer to a \code{teal} App} +\title{Add a Footer to \code{teal} Application} \usage{ modify_footer(app, footer = tags$p()) } diff --git a/man/modify_header.Rd b/man/modify_header.Rd index f4a13aeb55..dcb111588f 100644 --- a/man/modify_header.Rd +++ b/man/modify_header.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/init.R \name{modify_header} \alias{modify_header} -\title{Add a Header to a \code{teal} App} +\title{Add a Header to \code{teal} Application} \usage{ modify_header(app, header = tags$p()) } diff --git a/man/modify_title.Rd b/man/modify_title.Rd index 9f6e845b40..796f8e7f7c 100644 --- a/man/modify_title.Rd +++ b/man/modify_title.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/init.R \name{modify_title} \alias{modify_title} -\title{Add a custom title to the app} +\title{Add a custom Title to \code{teal} application} \usage{ modify_title( app, @@ -12,13 +12,13 @@ modify_title( ) } \arguments{ -\item{title}{The title to add} +\item{title}{(\code{shiny.tag} or \code{character(1)}) The title to be used} } \value{ The modified app object } \description{ -Add a custom title to the app +Add a custom Title to \code{teal} application } \examples{ app <- init( From d35ed9258627a001933f7a399a6af4f25d8c05e3 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 20:23:51 +0530 Subject: [PATCH 26/50] docs: add a vignette and update docs --- R/TealAppDriver.R | 3 +- R/init.R | 20 +- _pkgdown.yml | 3 +- .../teal-app-init-with-modifiers.drawio | 199 ++++++++++++++++++ inst/design/teal-app-init.drawio | 187 ++++++++++++++++ man/TealAppDriver.Rd | 4 +- man/add_custom_server.Rd | 4 +- man/add_landing_popup.Rd | 4 +- man/modify_footer.Rd | 4 +- man/modify_header.Rd | 4 +- man/modify_title.Rd | 4 +- vignettes/adding-support-for-reporting.Rmd | 6 +- vignettes/bootstrap-themes-in-teal.Rmd | 3 +- vignettes/getting-started-with-teal.Rmd | 10 +- .../images/teal-app-init-with-modifiers.svg | 1 + vignettes/images/teal-app-init.svg | 1 + vignettes/quick-usage-guide-for-teal.Rmd | 101 +++++++++ 17 files changed, 535 insertions(+), 23 deletions(-) create mode 100644 inst/design/teal-app-init-with-modifiers.drawio create mode 100644 inst/design/teal-app-init.drawio create mode 100644 vignettes/images/teal-app-init-with-modifiers.svg create mode 100644 vignettes/images/teal-app-init.svg create mode 100644 vignettes/quick-usage-guide-for-teal.Rmd diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index 52c2b6ac07..d0fa211455 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -26,7 +26,8 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. #' @description #' Initialize a `TealAppDriver` object for testing a `teal` application. #' - #' @param data,modules,filter arguments passed to `init` and title,header,footer,landing_popup modifiers + #' @param data,modules,filter arguments passed to `init` + #' @param title_args,header,footer,landing_popup_args to pass into the modifier functions. #' @param timeout (`numeric`) Default number of milliseconds for any timeout or #' timeout_ parameter in the `TealAppDriver` class. #' Defaults to 20s. diff --git a/R/init.R b/R/init.R index 5d7c8d38e9..034f339ce5 100644 --- a/R/init.R +++ b/R/init.R @@ -281,7 +281,9 @@ init <- function(data, #' ) |> #' modify_title("Custom title") #' -#' shinyApp(app$ui, app$server) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } modify_title <- function( app, title = "teal app", @@ -323,7 +325,9 @@ modify_title <- function( #' ) #' ) #' -#' shinyApp(app$ui, app$server) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } modify_header <- function(app, header = tags$p()) { res <- app res$ui <- function(request) { @@ -347,7 +351,9 @@ modify_header <- function(app, header = tags$p()) { #' ) |> #' modify_footer("Custom footer") #' -#' shinyApp(app$ui, app$server) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } modify_footer <- function(app, footer = tags$p()) { res <- app res$ui <- function(request) { @@ -383,7 +389,9 @@ modify_footer <- function(app, footer = tags$p()) { #' buttons = modalButton("Accept") #' ) #' -#' shinyApp(app$ui, app$server) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } add_landing_popup <- function( app, id = "landingpopup", @@ -420,7 +428,9 @@ add_landing_popup <- function( #' print("injected server logic") #' }) #' -#' shinyApp(app$ui, app$server) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } add_custom_server <- function(app, custom_server) { old_server <- app$server diff --git a/_pkgdown.yml b/_pkgdown.yml index fcc03e5d09..302c3e4977 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -56,6 +56,7 @@ articles: navbar: Get started contents: - getting-started-with-teal + - quick-usage-guide-for-teal - title: Using `teal` navbar: Using `teal` contents: @@ -108,7 +109,7 @@ reference: - module - modules - teal_slices - - title: `teal` application modifiers + - title: Application modifiers for `teal` desc: Functions to modify the `teal` app object contents: - modify_footer diff --git a/inst/design/teal-app-init-with-modifiers.drawio b/inst/design/teal-app-init-with-modifiers.drawio new file mode 100644 index 0000000000..c00dbeb786 --- /dev/null +++ b/inst/design/teal-app-init-with-modifiers.drawio @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/inst/design/teal-app-init.drawio b/inst/design/teal-app-init.drawio new file mode 100644 index 0000000000..a31595b445 --- /dev/null +++ b/inst/design/teal-app-init.drawio @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/man/TealAppDriver.Rd b/man/TealAppDriver.Rd index e34d9334d1..438a124097 100644 --- a/man/TealAppDriver.Rd +++ b/man/TealAppDriver.Rd @@ -111,7 +111,9 @@ Initialize a \code{TealAppDriver} object for testing a \code{teal} application. \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{data, modules, filter}}{arguments passed to \code{init} and title,header,footer,landing_popup modifiers} +\item{\code{data, modules, filter}}{arguments passed to \code{init}} + +\item{\code{title_args, header, footer, landing_popup_args}}{to pass into the modifier functions.} \item{\code{timeout}}{(\code{numeric}) Default number of milliseconds for any timeout or timeout_ parameter in the \code{TealAppDriver} class. diff --git a/man/add_custom_server.Rd b/man/add_custom_server.Rd index 4c8bb352e4..7e53807797 100644 --- a/man/add_custom_server.Rd +++ b/man/add_custom_server.Rd @@ -23,5 +23,7 @@ app <- init( print("injected server logic") }) -shinyApp(app$ui, app$server) +if (interactive()) { + shinyApp(app$ui, app$server) +} } diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd index 0b2b701a88..971c7895d2 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_popup.Rd @@ -43,5 +43,7 @@ app <- init( buttons = modalButton("Accept") ) -shinyApp(app$ui, app$server) +if (interactive()) { + shinyApp(app$ui, app$server) +} } diff --git a/man/modify_footer.Rd b/man/modify_footer.Rd index 9db57a4659..8c0fa0e71a 100644 --- a/man/modify_footer.Rd +++ b/man/modify_footer.Rd @@ -21,5 +21,7 @@ app <- init( ) |> modify_footer("Custom footer") -shinyApp(app$ui, app$server) +if (interactive()) { + shinyApp(app$ui, app$server) +} } diff --git a/man/modify_header.Rd b/man/modify_header.Rd index dcb111588f..5298ec3d9f 100644 --- a/man/modify_header.Rd +++ b/man/modify_header.Rd @@ -25,5 +25,7 @@ app <- init( ) ) -shinyApp(app$ui, app$server) +if (interactive()) { + shinyApp(app$ui, app$server) +} } diff --git a/man/modify_title.Rd b/man/modify_title.Rd index 796f8e7f7c..3e3a617671 100644 --- a/man/modify_title.Rd +++ b/man/modify_title.Rd @@ -27,5 +27,7 @@ app <- init( ) |> modify_title("Custom title") -shinyApp(app$ui, app$server) +if (interactive()) { + shinyApp(app$ui, app$server) +} } diff --git a/vignettes/adding-support-for-reporting.Rmd b/vignettes/adding-support-for-reporting.Rmd index 9c3730610b..9ba72d4962 100644 --- a/vignettes/adding-support-for-reporting.Rmd +++ b/vignettes/adding-support-for-reporting.Rmd @@ -355,9 +355,9 @@ app <- init( example_reporter_module(label = "with Reporter"), my_module(label = "without Reporter") ), - filter = teal_slices(teal_slice(dataname = "AIR", varname = "Temp", selected = c(72, 85))), - header = "Example teal app with reporter" -) + filter = teal_slices(teal_slice(dataname = "AIR", varname = "Temp", selected = c(72, 85))) +) |> + modify_header("Example teal app with reporter") if (interactive()) shinyApp(app$ui, app$server) ``` diff --git a/vignettes/bootstrap-themes-in-teal.Rmd b/vignettes/bootstrap-themes-in-teal.Rmd index 8ea255e4c6..3812e1a76e 100644 --- a/vignettes/bootstrap-themes-in-teal.Rmd +++ b/vignettes/bootstrap-themes-in-teal.Rmd @@ -105,13 +105,12 @@ app <- init( data = teal_data(IRIS = iris), # nolint: line_length. filter = teal_slices(teal_slice("IRIS", "Sepal.Length", selected = c(5, 7))), modules = modules(example_module(), example_module()), - header = "My first teal application" ) bslib::run_with_themer(shinyApp(app$ui, app$server)) ``` -This gives us the following. +This gives us a `Theme Customizer` similar to what is shown below. diff --git a/vignettes/getting-started-with-teal.Rmd b/vignettes/getting-started-with-teal.Rmd index 342c7de059..921505cf9b 100644 --- a/vignettes/getting-started-with-teal.Rmd +++ b/vignettes/getting-started-with-teal.Rmd @@ -40,11 +40,11 @@ app <- init( ), filter = teal_slices( teal_slice(dataname = "IRIS", varname = "Species", selected = "setosa") - ), - title = build_app_title(title = "My first teal app"), - header = h3("My first teal application"), - footer = tags$div(a("Powered by teal", href = "https://insightsengineering.github.io/teal/latest-tag/")) -) + ) +) |> + modify_title("My first teal application") |> + modify_header(h3("My first teal application")) |> + modify_footer(tags$div(a("Powered by teal", href = "https://insightsengineering.github.io/teal/latest-tag/"))) if (interactive()) { shinyApp(app$ui, app$server) diff --git a/vignettes/images/teal-app-init-with-modifiers.svg b/vignettes/images/teal-app-init-with-modifiers.svg new file mode 100644 index 0000000000..eeb8a0cd26 --- /dev/null +++ b/vignettes/images/teal-app-init-with-modifiers.svg @@ -0,0 +1 @@ +
Custom title
Custom title
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Header with a button that can be observed!
Header with a button that can be observed!
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Powered by teal
Powered by te...
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Show notification
Show notification
   Yes, the button works!
   Yes, the button works!
x
x
Text is not SVG - cannot display
\ No newline at end of file diff --git a/vignettes/images/teal-app-init.svg b/vignettes/images/teal-app-init.svg new file mode 100644 index 0000000000..54970b4fbd --- /dev/null +++ b/vignettes/images/teal-app-init.svg @@ -0,0 +1 @@ +
teal app
teal app
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Powered by teal
Powered by te...
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd new file mode 100644 index 0000000000..30fa1d2910 --- /dev/null +++ b/vignettes/quick-usage-guide-for-teal.Rmd @@ -0,0 +1,101 @@ +--- +title: "Quick usage guide for teal" +author: "NEST CoreDev" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Quick usage guide for teal} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +## `init` to initialize your app + +The `init` function is the entrypoint to create a `teal` application. +The main arguments of `init` are `data` and `modules`, which are mandatory. Additionally, the `filter` argument allows the user to specify the default filter state of the filter panel. + +* The `data` argument takes a `teal_data` object, please refer to [Including Data in teal Applications vignette](including-data-in-teal-applications.html) to learn more. +* The `modules` argument takes a nested list of `teal_modules` or `teal_module` objects or a single `teal_modules` or `teal_module` object. The packages like [`teal.modules.general`](https://insightsengineering.github.io/teal.modules.general/) or [`teal.modules.clinical`](https://insightsengineering.github.io/teal.modules.clinical/) contain a collection of thess modules that can be used in your `teal` application. +* The `filter` argument takes an object of class `teal_slices`, please refer the [Filter Panel vignette](filter-panel.html) to learn more. + +## A simple `teal` application: + +This simple `teal` application takes the `iris` and `mtcars` datasets and displays their contents: + +```{r, message=FALSE} +library(teal) + +app <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Module 1") + ), + filter = teal_slices( + teal_slice(dataname = "IRIS", varname = "Species", selected = "setosa") + ) +) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +``` + + + Simple teal app to showcase `init` + + +## Extending the `teal` application + +The following functions can be used to extend the `teal` application: + +* `modify_title` to change the title of the application +* `modify_header` to change the header of the application +* `modify_footer` to change the footer of the application +* `add_landing_popup` to add a landing popup to the application +* `add_custom_server` to add custom server logic to the application + +Let's use these mopdifiers to customize the application we created earlier. + +```{r, message=FALSE} +library(teal) + +app <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Module 1") + ), + filter = teal_slices( + teal_slice(dataname = "IRIS", varname = "Species", selected = "setosa") + ) +) |> + modify_title(title = "Custom title") |> + modify_header( + header = tags$div( + h3("Header with a button that can be observed!"), + actionButton("notify", "Show notification") + ) + ) |> + modify_footer(footer = "Custom footer") |> + add_landing_popup(title = "Teal", content = "Welcome to my first teal application!") |> + add_custom_server(function(input, output, session) { + observeEvent(input$notify, { + showNotification("Yes, the button works!") + }) + }) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +``` + + + Simple teal app to showcase the modifier functions + + + +Note the changes made by these extensions: + +* `modify_title` changed the title that appears in the browser tab. +* `modify_header` adds a header with a button that shows a notification when clicked, but note that the observer for this button was added using the modifier `add_custom_server`. +* `modify_footer` adds a custom footer. +* `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note that the screenshot does not show the popup, but it will appear when the application is first loaded. +* `add_custom_server` observed the button created in the header and shows a notification when it is clicked. From 5e53f1f96e9850aa51def45e7b169ce97f64fc68 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 20:39:34 +0530 Subject: [PATCH 27/50] chore: fix spellcheck --- vignettes/quick-usage-guide-for-teal.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd index 30fa1d2910..9a330cecc0 100644 --- a/vignettes/quick-usage-guide-for-teal.Rmd +++ b/vignettes/quick-usage-guide-for-teal.Rmd @@ -10,18 +10,18 @@ vignette: > ## `init` to initialize your app -The `init` function is the entrypoint to create a `teal` application. +The `init` function is the entry point to create a `teal` application. The main arguments of `init` are `data` and `modules`, which are mandatory. Additionally, the `filter` argument allows the user to specify the default filter state of the filter panel. * The `data` argument takes a `teal_data` object, please refer to [Including Data in teal Applications vignette](including-data-in-teal-applications.html) to learn more. -* The `modules` argument takes a nested list of `teal_modules` or `teal_module` objects or a single `teal_modules` or `teal_module` object. The packages like [`teal.modules.general`](https://insightsengineering.github.io/teal.modules.general/) or [`teal.modules.clinical`](https://insightsengineering.github.io/teal.modules.clinical/) contain a collection of thess modules that can be used in your `teal` application. +* The `modules` argument takes a nested list of `teal_modules` or `teal_module` objects or a single `teal_modules` or `teal_module` object. The packages like [`teal.modules.general`](https://insightsengineering.github.io/teal.modules.general/) or [`teal.modules.clinical`](https://insightsengineering.github.io/teal.modules.clinical/) contain a collection of these modules that can be used in your `teal` application. * The `filter` argument takes an object of class `teal_slices`, please refer the [Filter Panel vignette](filter-panel.html) to learn more. ## A simple `teal` application: This simple `teal` application takes the `iris` and `mtcars` datasets and displays their contents: -```{r, message=FALSE} +```{r, eval=FALSE} library(teal) app <- init( @@ -53,9 +53,9 @@ The following functions can be used to extend the `teal` application: * `add_landing_popup` to add a landing popup to the application * `add_custom_server` to add custom server logic to the application -Let's use these mopdifiers to customize the application we created earlier. +Let's use these modifiers to customize the application we created earlier. -```{r, message=FALSE} +```{r, eval=FALSE} library(teal) app <- init( @@ -75,7 +75,7 @@ app <- init( ) ) |> modify_footer(footer = "Custom footer") |> - add_landing_popup(title = "Teal", content = "Welcome to my first teal application!") |> + add_landing_popup(title = "Te al", content = "Welcome to my first teal application!") |> add_custom_server(function(input, output, session) { observeEvent(input$notify, { showNotification("Yes, the button works!") From cfe628ede87845ffe6df9ab5aa06240fa0906444 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 21:28:58 +0530 Subject: [PATCH 28/50] @gogonzo comments + doc changes --- R/TealAppDriver.R | 2 -- R/init.R | 30 +++++++++++++------ .../teal-app-init-with-modifiers.drawio | 12 ++++---- inst/design/teal-app-init.drawio | 5 +--- man/add_custom_server.Rd | 4 +-- man/add_landing_popup.Rd | 7 ++--- man/modify_footer.Rd | 2 +- man/modify_header.Rd | 2 +- man/modify_title.Rd | 3 ++ .../images/teal-app-init-with-modifiers.svg | 2 +- vignettes/images/teal-app-init.svg | 2 +- vignettes/quick-usage-guide-for-teal.Rmd | 28 +++++------------ 12 files changed, 48 insertions(+), 51 deletions(-) diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index d0fa211455..48d8bd0ab6 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -75,7 +75,6 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. if (!is.null(landing_popup_args)) { default_args <- list( - id = "landingpopup", title = NULL, content = NULL, buttons = modalButton("Accept") @@ -87,7 +86,6 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. ) app <- add_landing_popup( app, - id = landing_popup_args$id, title = landing_popup_args$title, content = landing_popup_args$content, buttons = landing_popup_args$button diff --git a/R/init.R b/R/init.R index 034f339ce5..aeed660018 100644 --- a/R/init.R +++ b/R/init.R @@ -272,6 +272,8 @@ init <- function(data, #' Add a custom Title to `teal` application #' #' @param title (`shiny.tag` or `character(1)`) The title to be used +#' @param favicon (`character`) The path for the icon for the title. +#' The image/icon path can be remote or the static path accessible by `shiny`, like the `www/` #' @return The modified app object #' @export #' @examples @@ -288,6 +290,8 @@ modify_title <- function( app, title = "teal app", favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { + checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) + checkmate::assert_string(favicon) res <- app res$ui <- function(request) { title_tag <- tags$div( @@ -311,7 +315,7 @@ modify_title <- function( #' #' @description Adds a header to the `teal` app. #' -#' @param app (`environment`) The `teal` app environment. +#' @param app (`list`) The `teal` app environment. #' @param header (`shiny.tag` or `character(1)`) The header content to set. Defaults to an empty paragraph tag. #' @export #' @examples @@ -329,6 +333,7 @@ modify_title <- function( #' shinyApp(app$ui, app$server) #' } modify_header <- function(app, header = tags$p()) { + checkmate::assert_multi_class(header, c("shiny.tag", "shiny.tag.list", "html", "character")) res <- app res$ui <- function(request) { ui_tq <- htmltools::tagQuery(app$ui(request = request)) @@ -341,7 +346,7 @@ modify_header <- function(app, header = tags$p()) { #' #' @description Adds a footer to the `teal` app. #' -#' @param app (`environment`) The `teal` app environment. +#' @param app (`list`) The `teal` app environment. #' @param footer (`shiny.tag` or `character(1)`) The footer content to set. Defaults to an empty paragraph tag. #' @export #' @examples @@ -355,6 +360,7 @@ modify_header <- function(app, header = tags$p()) { #' shinyApp(app$ui, app$server) #' } modify_footer <- function(app, footer = tags$p()) { + checkmate::assert_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html", "character")) res <- app res$ui <- function(request) { ui_tq <- htmltools::tagQuery(app$ui(request = request)) @@ -370,7 +376,7 @@ modify_footer <- function(app, footer = tags$p()) { #' This modifier is used to display a popup dialog when the application starts. #' The dialog blocks access to the application and must be closed with a button before the application can be viewed. #' -#' @param app (`environment`) The `teal` app environment. +#' @param app (`list`) The `teal` app environment. #' @param id (`character(1)`) The ID of the modal dialog. #' @param label (`character(1)`) Label of the module. #' @param title (`character(1)`) Text to be displayed as popup title. @@ -394,30 +400,35 @@ modify_footer <- function(app, footer = tags$p()) { #' } add_landing_popup <- function( app, - id = "landingpopup", title = NULL, content = NULL, buttons = modalButton("Accept")) { custom_server <- function(input, output, session) { + checkmate::assert_string(id) + checkmate::assert_string(title, null.ok = TRUE) + checkmate::assert_multi_class( + content, + classes = c("character", "shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE + ) + checkmate::assert_multi_class(buttons, classes = c("shiny.tag", "shiny.tag.list")) showModal( modalDialog( - id = id, + id = "landingpopup", title = title, content, footer = buttons ) ) } - app <- add_custom_server(app, custom_server) - app + add_custom_server(app, custom_server) } #' Add a Custom Server Logic to `teal` Application #' #' @description Adds a custom server function to the `teal` app. This function can define additional server logic. #' -#' @param app (`environment`) The `teal` app environment. -#' @param custom_server (`function`) The custom server function to set. +#' @param app (`list`) The `teal` app environment. +#' @param custom_server (`function(input, output, session)`) The custom server function to set. #' @export #' @examples #' app <- init( @@ -432,6 +443,7 @@ add_landing_popup <- function( #' shinyApp(app$ui, app$server) #' } add_custom_server <- function(app, custom_server) { + checkmate::assert_function(custom_server, args = c("input", "output", "session")) old_server <- app$server app$server <- function(input, output, session) { diff --git a/inst/design/teal-app-init-with-modifiers.drawio b/inst/design/teal-app-init-with-modifiers.drawio index c00dbeb786..9045c5d4ed 100644 --- a/inst/design/teal-app-init-with-modifiers.drawio +++ b/inst/design/teal-app-init-with-modifiers.drawio @@ -1,6 +1,6 @@ - + @@ -142,8 +142,8 @@ - - + + @@ -184,13 +184,13 @@ - + - + - + diff --git a/inst/design/teal-app-init.drawio b/inst/design/teal-app-init.drawio index a31595b445..9e6a5c0cae 100644 --- a/inst/design/teal-app-init.drawio +++ b/inst/design/teal-app-init.drawio @@ -1,6 +1,6 @@ - + @@ -139,9 +139,6 @@ - - - diff --git a/man/add_custom_server.Rd b/man/add_custom_server.Rd index 7e53807797..758199113b 100644 --- a/man/add_custom_server.Rd +++ b/man/add_custom_server.Rd @@ -7,9 +7,9 @@ add_custom_server(app, custom_server) } \arguments{ -\item{app}{(\code{environment}) The \code{teal} app environment.} +\item{app}{(\code{list}) The \code{teal} app environment.} -\item{custom_server}{(\code{function}) The custom server function to set.} +\item{custom_server}{(\verb{function(input, output, session)}) The custom server function to set.} } \description{ Adds a custom server function to the \code{teal} app. This function can define additional server logic. diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd index 971c7895d2..2e9694d8b0 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_popup.Rd @@ -6,16 +6,13 @@ \usage{ add_landing_popup( app, - id = "landingpopup", title = NULL, content = NULL, buttons = modalButton("Accept") ) } \arguments{ -\item{app}{(\code{environment}) The \code{teal} app environment.} - -\item{id}{(\code{character(1)}) The ID of the modal dialog.} +\item{app}{(\code{list}) The \code{teal} app environment.} \item{title}{(\code{character(1)}) Text to be displayed as popup title.} @@ -24,6 +21,8 @@ Passed to \code{...} of \code{shiny::modalDialog}. See examples.} \item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}. See examples.} +\item{id}{(\code{character(1)}) The ID of the modal dialog.} + \item{label}{(\code{character(1)}) Label of the module.} } \description{ diff --git a/man/modify_footer.Rd b/man/modify_footer.Rd index 8c0fa0e71a..2a4153ee4a 100644 --- a/man/modify_footer.Rd +++ b/man/modify_footer.Rd @@ -7,7 +7,7 @@ modify_footer(app, footer = tags$p()) } \arguments{ -\item{app}{(\code{environment}) The \code{teal} app environment.} +\item{app}{(\code{list}) The \code{teal} app environment.} \item{footer}{(\code{shiny.tag} or \code{character(1)}) The footer content to set. Defaults to an empty paragraph tag.} } diff --git a/man/modify_header.Rd b/man/modify_header.Rd index 5298ec3d9f..a24d9d3756 100644 --- a/man/modify_header.Rd +++ b/man/modify_header.Rd @@ -7,7 +7,7 @@ modify_header(app, header = tags$p()) } \arguments{ -\item{app}{(\code{environment}) The \code{teal} app environment.} +\item{app}{(\code{list}) The \code{teal} app environment.} \item{header}{(\code{shiny.tag} or \code{character(1)}) The header content to set. Defaults to an empty paragraph tag.} } diff --git a/man/modify_title.Rd b/man/modify_title.Rd index 3e3a617671..26bfd0f67e 100644 --- a/man/modify_title.Rd +++ b/man/modify_title.Rd @@ -13,6 +13,9 @@ modify_title( } \arguments{ \item{title}{(\code{shiny.tag} or \code{character(1)}) The title to be used} + +\item{favicon}{(\code{character}) The path for the icon for the title. +The image/icon path can be remote or the static path accessible by \code{shiny}, like the \verb{www/}} } \value{ The modified app object diff --git a/vignettes/images/teal-app-init-with-modifiers.svg b/vignettes/images/teal-app-init-with-modifiers.svg index eeb8a0cd26..b7ac58b8d7 100644 --- a/vignettes/images/teal-app-init-with-modifiers.svg +++ b/vignettes/images/teal-app-init-with-modifiers.svg @@ -1 +1 @@ -
Custom title
Custom title
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Header with a button that can be observed!
Header with a button that can be observed!
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Powered by teal
Powered by te...
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Show notification
Show notification
   Yes, the button works!
   Yes, the button works!
x
x
Text is not SVG - cannot display
\ No newline at end of file +
Custom title
Custom title
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Header with a button that can be observed!
Header with a button that can be observed!
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Custom footer
Custom footer
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Show notification
Show notification
   Yes, the button works!
   Yes, the button works!
x
x
Text is not SVG - cannot display
\ No newline at end of file diff --git a/vignettes/images/teal-app-init.svg b/vignettes/images/teal-app-init.svg index 54970b4fbd..e0713bf493 100644 --- a/vignettes/images/teal-app-init.svg +++ b/vignettes/images/teal-app-init.svg @@ -1 +1 @@ -
teal app
teal app
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Powered by teal
Powered by te...
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Text is not SVG - cannot display
\ No newline at end of file +
teal app
teal app
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd index 9a330cecc0..f12705454e 100644 --- a/vignettes/quick-usage-guide-for-teal.Rmd +++ b/vignettes/quick-usage-guide-for-teal.Rmd @@ -39,21 +39,17 @@ if (interactive()) { } ``` - - Simple teal app to showcase `init` - +![](./images/teal-app-init.svg) ## Extending the `teal` application The following functions can be used to extend the `teal` application: -* `modify_title` to change the title of the application -* `modify_header` to change the header of the application -* `modify_footer` to change the footer of the application -* `add_landing_popup` to add a landing popup to the application -* `add_custom_server` to add custom server logic to the application - -Let's use these modifiers to customize the application we created earlier. +* `modify_title` changes the title that appears in the browser tab. +* `modify_header` adds a header with a button that shows a notification when clicked, also note that the observer for this button was added using the modifier `add_custom_server`. +* `modify_footer` adds a custom footer. +* `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note that the screenshot does not show the popup, but it will appear when the application is first loaded. +* `add_custom_server` adds custom server logic to the teal application, in this case the button created in the header is observed and a notification is shown. ```{r, eval=FALSE} library(teal) @@ -87,15 +83,7 @@ if (interactive()) { } ``` - - Simple teal app to showcase the modifier functions - +Here is what the app would look with those modifiers added. +![](./images/teal-app-init-with-modifiers.svg) -Note the changes made by these extensions: - -* `modify_title` changed the title that appears in the browser tab. -* `modify_header` adds a header with a button that shows a notification when clicked, but note that the observer for this button was added using the modifier `add_custom_server`. -* `modify_footer` adds a custom footer. -* `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note that the screenshot does not show the popup, but it will appear when the application is first loaded. -* `add_custom_server` observed the button created in the header and shows a notification when it is clicked. From d8eb34d65b01bd4c47b82126347a92ca1eb98826 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 21:31:17 +0530 Subject: [PATCH 29/50] chore: doc changes --- R/init.R | 2 -- man/add_landing_popup.Rd | 2 -- 2 files changed, 4 deletions(-) diff --git a/R/init.R b/R/init.R index aeed660018..3d53bbda27 100644 --- a/R/init.R +++ b/R/init.R @@ -372,8 +372,6 @@ modify_footer <- function(app, footer = tags$p()) { #' Add a Landing Popup to `teal` Application #' #' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. -#' -#' This modifier is used to display a popup dialog when the application starts. #' The dialog blocks access to the application and must be closed with a button before the application can be viewed. #' #' @param app (`list`) The `teal` app environment. diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd index 2e9694d8b0..29701919f0 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_popup.Rd @@ -27,8 +27,6 @@ Passed to \code{...} of \code{shiny::modalDialog}. See examples.} } \description{ Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. - -This modifier is used to display a popup dialog when the application starts. The dialog blocks access to the application and must be closed with a button before the application can be viewed. } \examples{ From 56896bc338dc45e27b63dff81f67094ba12d6cf9 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 21:34:38 +0530 Subject: [PATCH 30/50] @gogonzo comments --- R/init.R | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/R/init.R b/R/init.R index 3d53bbda27..0e74f1f6bc 100644 --- a/R/init.R +++ b/R/init.R @@ -402,7 +402,6 @@ add_landing_popup <- function( content = NULL, buttons = modalButton("Accept")) { custom_server <- function(input, output, session) { - checkmate::assert_string(id) checkmate::assert_string(title, null.ok = TRUE) checkmate::assert_multi_class( content, @@ -446,7 +445,11 @@ add_custom_server <- function(app, custom_server) { app$server <- function(input, output, session) { old_server(input, output, session) - custom_server(input, output, session) + if (all(c("input", "output", "session") %in% names(formals(custom_server)))) { + callModule(custom_server, "wrapper") + } else if ("id" %in% names(formals(custom_server))) { + custom_server("wrapper") + } } app } From 13b24b8b36d3a5bcca2d3524c08da65160cff0a1 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 22:12:38 +0530 Subject: [PATCH 31/50] fix: fix the module server call with name-space --- R/init.R | 33 ++++++++++++++++++++++++++++----- man/add_custom_server.Rd | 28 ++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/R/init.R b/R/init.R index 0e74f1f6bc..5967c9e038 100644 --- a/R/init.R +++ b/R/init.R @@ -425,7 +425,9 @@ add_landing_popup <- function( #' @description Adds a custom server function to the `teal` app. This function can define additional server logic. #' #' @param app (`list`) The `teal` app environment. -#' @param custom_server (`function(input, output, session)`) The custom server function to set. +#' @param custom_server (`function(input, output, session)` or `function(input, output, session)`) +#' The custom server function or server module to set. +#' @param module_id (`character(1)`) The ID of the module when a module server function is passed. #' @export #' @examples #' app <- init( @@ -439,16 +441,37 @@ add_landing_popup <- function( #' if (interactive()) { #' shinyApp(app$ui, app$server) #' } -add_custom_server <- function(app, custom_server) { - checkmate::assert_function(custom_server, args = c("input", "output", "session")) +#' +#' ns <- NS("custom_ns") +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' modify_header(actionButton(ns("button"), "Click me")) |> +#' add_custom_server( +#' function(id) { +#' moduleServer(id, function(input, output, session) { +#' observeEvent(input$button, { +#' showNotification("Button is clicked!") +#' }) +#' }) +#' }, +#' module_id = "custom_ns" +#' ) +#' +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +add_custom_server <- function(app, custom_server, module_id = "wrapper") { + checkmate::assert_function(custom_server) old_server <- app$server app$server <- function(input, output, session) { old_server(input, output, session) if (all(c("input", "output", "session") %in% names(formals(custom_server)))) { - callModule(custom_server, "wrapper") + custom_server(input, output, session) } else if ("id" %in% names(formals(custom_server))) { - custom_server("wrapper") + custom_server(module_id) } } app diff --git a/man/add_custom_server.Rd b/man/add_custom_server.Rd index 758199113b..f2666d4cb3 100644 --- a/man/add_custom_server.Rd +++ b/man/add_custom_server.Rd @@ -4,12 +4,15 @@ \alias{add_custom_server} \title{Add a Custom Server Logic to \code{teal} Application} \usage{ -add_custom_server(app, custom_server) +add_custom_server(app, custom_server, module_id = "wrapper") } \arguments{ \item{app}{(\code{list}) The \code{teal} app environment.} -\item{custom_server}{(\verb{function(input, output, session)}) The custom server function to set.} +\item{custom_server}{(\verb{function(input, output, session)} or \verb{function(input, output, session)}) +The custom server function or server module to set.} + +\item{module_id}{(\code{character(1)}) The ID of the module when a module server function is passed.} } \description{ Adds a custom server function to the \code{teal} app. This function can define additional server logic. @@ -23,6 +26,27 @@ app <- init( print("injected server logic") }) +if (interactive()) { + shinyApp(app$ui, app$server) +} + +ns <- NS("custom_ns") +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + modify_header(actionButton(ns("button"), "Click me")) |> + add_custom_server( + function(id) { + moduleServer(id, function(input, output, session) { + observeEvent(input$button, { + showNotification("Button is clicked!") + }) + }) + }, + module_id = "custom_ns" + ) + if (interactive()) { shinyApp(app$ui, app$server) } From 63065f03d878528c3595d6bb61d803933214399f Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 13 Jan 2025 22:14:56 +0530 Subject: [PATCH 32/50] chore: fix CI --- R/init.R | 3 +-- man/add_landing_popup.Rd | 4 ---- man/modify_title.Rd | 2 ++ 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/R/init.R b/R/init.R index 5967c9e038..ef494dce19 100644 --- a/R/init.R +++ b/R/init.R @@ -271,6 +271,7 @@ init <- function(data, #' Add a custom Title to `teal` application #' +#' @param app (`list`) The `teal` app environment. #' @param title (`shiny.tag` or `character(1)`) The title to be used #' @param favicon (`character`) The path for the icon for the title. #' The image/icon path can be remote or the static path accessible by `shiny`, like the `www/` @@ -375,8 +376,6 @@ modify_footer <- function(app, footer = tags$p()) { #' The dialog blocks access to the application and must be closed with a button before the application can be viewed. #' #' @param app (`list`) The `teal` app environment. -#' @param id (`character(1)`) The ID of the modal dialog. -#' @param label (`character(1)`) Label of the module. #' @param title (`character(1)`) Text to be displayed as popup title. #' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. #' Passed to `...` of `shiny::modalDialog`. See examples. diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd index 29701919f0..b2ec502e41 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_popup.Rd @@ -20,10 +20,6 @@ add_landing_popup( Passed to \code{...} of \code{shiny::modalDialog}. See examples.} \item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}. See examples.} - -\item{id}{(\code{character(1)}) The ID of the modal dialog.} - -\item{label}{(\code{character(1)}) Label of the module.} } \description{ Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. diff --git a/man/modify_title.Rd b/man/modify_title.Rd index 26bfd0f67e..db2b09cee7 100644 --- a/man/modify_title.Rd +++ b/man/modify_title.Rd @@ -12,6 +12,8 @@ modify_title( ) } \arguments{ +\item{app}{(\code{list}) The \code{teal} app environment.} + \item{title}{(\code{shiny.tag} or \code{character(1)}) The title to be used} \item{favicon}{(\code{character}) The path for the icon for the title. From 8e4ad576520fe7c788d2474cd145125559d49cea Mon Sep 17 00:00:00 2001 From: vedhav Date: Wed, 15 Jan 2025 13:25:36 +0530 Subject: [PATCH 33/50] feat: add `teal_replace_ui` and generalize the UI replacements --- DESCRIPTION | 4 +- NAMESPACE | 1 - R/init.R | 259 +++------------------------------ R/module_teal.R | 52 ++----- R/module_teal_with_splash.R | 9 +- R/show_rcode_modal.R | 2 +- R/tdata.R | 2 +- R/teal_modifiers.R | 236 ++++++++++++++++++++++++++++++ man/add_custom_server.Rd | 53 ------- man/add_landing_popup.Rd | 42 ------ man/modify_footer.Rd | 27 ---- man/modify_header.Rd | 31 ---- man/modify_title.Rd | 38 ----- man/module_teal.Rd | 22 +-- man/module_teal_with_splash.Rd | 14 -- man/teal_modifiers.Rd | 150 +++++++++++++++++++ 16 files changed, 430 insertions(+), 512 deletions(-) create mode 100644 R/teal_modifiers.R delete mode 100644 man/add_custom_server.Rd delete mode 100644 man/add_landing_popup.Rd delete mode 100644 man/modify_footer.Rd delete mode 100644 man/modify_header.Rd delete mode 100644 man/modify_title.Rd create mode 100644 man/teal_modifiers.Rd diff --git a/DESCRIPTION b/DESCRIPTION index d2b0081aae..f36879b3dd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -42,6 +42,7 @@ Depends: Imports: checkmate (>= 2.1.0), cli, + htmltools, jsonlite, lifecycle (>= 0.2.0), logger (>= 0.2.0), @@ -84,7 +85,7 @@ Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, rstudio/bslib, yihui/knitr, bioc::MultiAssayExperiment, r-lib/R6, rstudio/rmarkdown, tidyverse/rvest, rstudio/shinytest2, rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, - yaml=vubiostat/r-yaml + yaml=vubiostat/r-yaml, rstudio/htmltools Config/Needs/website: insightsengineering/nesttemplate Encoding: UTF-8 Language: en-US @@ -120,6 +121,7 @@ Collate: 'teal_data_module-eval_code.R' 'teal_data_module-within.R' 'teal_data_utils.R' + 'teal_modifiers.R' 'teal_reporter.R' 'teal_slices-store.R' 'teal_slices.R' diff --git a/NAMESPACE b/NAMESPACE index 78f31ed176..d5e792d55e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,7 +14,6 @@ S3method(ui_teal_module,teal_module) S3method(ui_teal_module,teal_modules) S3method(within,teal_data_module) export(TealReportCard) -export(add_custom_server) export(add_landing_popup) export(as.teal_slices) export(as_tdata) diff --git a/R/init.R b/R/init.R index ef494dce19..00b669bd05 100644 --- a/R/init.R +++ b/R/init.R @@ -185,6 +185,21 @@ init <- function(data, ) } + # argument transformations + ## `modules` - landing module + landing <- extract_module(modules, "teal_module_landing") + modules <- drop_module(modules, "teal_module_landing") + + # Note: UI must be a function to support bookmarking. + res <- list( + ui = function(request) { + ui_teal(id = character(0), modules = modules) + }, + server = function(input, output, session) { + srv_teal(id = character(0), data = data, modules = modules, filter = deep_copy_filter(filter)) + } + ) + if (lifecycle::is_present(title)) { checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) lifecycle::deprecate_warn( @@ -192,15 +207,7 @@ init <- function(data, what = "init(title)", details = "Use `modify_title()` on the teal app object instead." ) - } else { - title <- tags$head( - tags$title("teal app"), - tags$link( - rel = "icon", - href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", - sizes = "any" - ) - ) + res <- modify_title(res, title) } if (lifecycle::is_present(header)) { checkmate::assert_multi_class(header, c("shiny.tag", "shiny.tag.list", "html", "character")) @@ -211,8 +218,7 @@ init <- function(data, "Use `modify_header()` on the teal app object instead." ) ) - } else { - header <- tags$p() + res <- modify_header(res, header) } if (lifecycle::is_present(footer)) { checkmate::assert_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html", "character")) @@ -223,31 +229,9 @@ init <- function(data, "Use `modify_footer()` on the teal app object instead." ) ) - } else { - footer <- tags$p() + res <- modify_footer(res, footer) } - # argument transformations - ## `modules` - landing module - landing <- extract_module(modules, "teal_module_landing") - modules <- drop_module(modules, "teal_module_landing") - - # Note: UI must be a function to support bookmarking. - res <- list( - ui = function(request) { - ui_teal( - id = "teal", - modules = modules, - title = title, - header = header, - footer = footer - ) - }, - server = function(input, output, session) { - srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) - } - ) - if (length(landing) == 1L) { res <- add_custom_server(res, function(input, output, session) { do.call(landing[[1L]]$server, c(list(id = "landing_module_shiny_id"))) @@ -268,210 +252,3 @@ init <- function(data, res } - -#' Add a custom Title to `teal` application -#' -#' @param app (`list`) The `teal` app environment. -#' @param title (`shiny.tag` or `character(1)`) The title to be used -#' @param favicon (`character`) The path for the icon for the title. -#' The image/icon path can be remote or the static path accessible by `shiny`, like the `www/` -#' @return The modified app object -#' @export -#' @examples -#' app <- init( -#' data = teal_data(IRIS = iris, MTCARS = mtcars), -#' modules = modules(example_module()) -#' ) |> -#' modify_title("Custom title") -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -modify_title <- function( - app, - title = "teal app", - favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { - checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) - checkmate::assert_string(favicon) - res <- app - res$ui <- function(request) { - title_tag <- tags$div( - id = "teal-title", - tags$head( - tags$title(title), - tags$link( - rel = "icon", - href = favicon, - sizes = "any" - ) - ) - ) - ui_tq <- htmltools::tagQuery(app$ui(request = request)) - ui_tq$find("#teal-title")$replaceWith(title_tag)$allTags() - } - res -} - -#' Add a Header to `teal` Application -#' -#' @description Adds a header to the `teal` app. -#' -#' @param app (`list`) The `teal` app environment. -#' @param header (`shiny.tag` or `character(1)`) The header content to set. Defaults to an empty paragraph tag. -#' @export -#' @examples -#' app <- init( -#' data = teal_data(IRIS = iris), -#' modules = modules(example_module()) -#' ) |> -#' modify_header( -#' tags$div( -#' h3("Custom header") -#' ) -#' ) -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -modify_header <- function(app, header = tags$p()) { - checkmate::assert_multi_class(header, c("shiny.tag", "shiny.tag.list", "html", "character")) - res <- app - res$ui <- function(request) { - ui_tq <- htmltools::tagQuery(app$ui(request = request)) - ui_tq$find("#teal-header")$replaceWith(tags$header(id = "teal-header", header))$allTags() - } - res -} - -#' Add a Footer to `teal` Application -#' -#' @description Adds a footer to the `teal` app. -#' -#' @param app (`list`) The `teal` app environment. -#' @param footer (`shiny.tag` or `character(1)`) The footer content to set. Defaults to an empty paragraph tag. -#' @export -#' @examples -#' app <- init( -#' data = teal_data(IRIS = iris), -#' modules = modules(example_module()) -#' ) |> -#' modify_footer("Custom footer") -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -modify_footer <- function(app, footer = tags$p()) { - checkmate::assert_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html", "character")) - res <- app - res$ui <- function(request) { - ui_tq <- htmltools::tagQuery(app$ui(request = request)) - ui_tq$find("#teal-footer")$replaceWith(tags$div(id = "teal-footer", footer))$allTags() - } - res -} - -#' Add a Landing Popup to `teal` Application -#' -#' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. -#' The dialog blocks access to the application and must be closed with a button before the application can be viewed. -#' -#' @param app (`list`) The `teal` app environment. -#' @param title (`character(1)`) Text to be displayed as popup title. -#' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. -#' Passed to `...` of `shiny::modalDialog`. See examples. -#' @param buttons (`shiny.tag` or `shiny.tag.list`) Typically a `modalButton` or `actionButton`. See examples. -#' @export -#' @examples -#' app <- init( -#' data = teal_data(IRIS = iris, MTCARS = mtcars), -#' modules = modules(example_module()) -#' ) |> -#' add_landing_popup( -#' title = "Welcome", -#' content = "This is a landing popup.", -#' buttons = modalButton("Accept") -#' ) -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -add_landing_popup <- function( - app, - title = NULL, - content = NULL, - buttons = modalButton("Accept")) { - custom_server <- function(input, output, session) { - checkmate::assert_string(title, null.ok = TRUE) - checkmate::assert_multi_class( - content, - classes = c("character", "shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE - ) - checkmate::assert_multi_class(buttons, classes = c("shiny.tag", "shiny.tag.list")) - showModal( - modalDialog( - id = "landingpopup", - title = title, - content, - footer = buttons - ) - ) - } - add_custom_server(app, custom_server) -} - -#' Add a Custom Server Logic to `teal` Application -#' -#' @description Adds a custom server function to the `teal` app. This function can define additional server logic. -#' -#' @param app (`list`) The `teal` app environment. -#' @param custom_server (`function(input, output, session)` or `function(input, output, session)`) -#' The custom server function or server module to set. -#' @param module_id (`character(1)`) The ID of the module when a module server function is passed. -#' @export -#' @examples -#' app <- init( -#' data = teal_data(IRIS = iris), -#' modules = modules(example_module()) -#' ) |> -#' add_custom_server(function(input, output, session) { -#' print("injected server logic") -#' }) -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -#' -#' ns <- NS("custom_ns") -#' app <- init( -#' data = teal_data(IRIS = iris), -#' modules = modules(example_module()) -#' ) |> -#' modify_header(actionButton(ns("button"), "Click me")) |> -#' add_custom_server( -#' function(id) { -#' moduleServer(id, function(input, output, session) { -#' observeEvent(input$button, { -#' showNotification("Button is clicked!") -#' }) -#' }) -#' }, -#' module_id = "custom_ns" -#' ) -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -add_custom_server <- function(app, custom_server, module_id = "wrapper") { - checkmate::assert_function(custom_server) - old_server <- app$server - - app$server <- function(input, output, session) { - old_server(input, output, session) - if (all(c("input", "output", "session") %in% names(formals(custom_server)))) { - custom_server(input, output, session) - } else if ("id" %in% names(formals(custom_server))) { - custom_server(module_id) - } - } - app -} diff --git a/R/module_teal.R b/R/module_teal.R index 639d96900e..3ef993e84b 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -40,42 +40,18 @@ NULL #' @rdname module_teal #' @export -ui_teal <- function(id, - modules, - title = build_app_title(), - header = tags$p(), - footer = tags$p()) { +ui_teal <- function(id, modules) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) - checkmate::assert( - .var.name = "title", - checkmate::check_string(title), - checkmate::check_multi_class(title, c("shiny.tag", "shiny.tag.list", "html")) - ) - checkmate::assert( - .var.name = "header", - checkmate::check_string(header), - checkmate::check_multi_class(header, c("shiny.tag", "shiny.tag.list", "html")) - ) - checkmate::assert( - .var.name = "footer", - checkmate::check_string(footer), - checkmate::check_multi_class(footer, c("shiny.tag", "shiny.tag.list", "html")) - ) - - if (is.character(title)) { - title <- build_app_title(title) - } else { - validate_app_title_tag(title) - } - - if (checkmate::test_string(header)) { - header <- tags$p(header) - } - - if (checkmate::test_string(footer)) { - footer <- tags$p(footer) - } - + title <- tags$head( + tags$title("teal app"), + tags$link( + rel = "icon", + href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", + sizes = "any" + ) + ) + header <- tags$p() + footer <- tags$p() ns <- NS(id) # show busy icon when `shiny` session is busy computing stuff @@ -92,10 +68,10 @@ ui_teal <- function(id, fluidPage( id = id, - title = tags$div(id = "teal-title", title), + title = tags$div(id = ns("teal-title"), title), theme = get_teal_bs_theme(), include_teal_css_js(), - tags$header(id = "teal-header", header), + tags$header(id = ns("teal-header"), header), tags$hr(class = "my-2"), shiny_busy_message_panel, tags$div( @@ -133,7 +109,7 @@ ui_teal <- function(id, tags$hr(), tags$footer( tags$div( - tags$div(id = "teal-footer", footer), + tags$div(id = ns("teal-footer"), footer), teal.widgets::verbatim_popup_ui(ns("sessionInfo"), "Session Info", type = "link"), br(), ui_teal_lockfile(ns("lockfile")), diff --git a/R/module_teal_with_splash.R b/R/module_teal_with_splash.R index c4f29c0ae3..998c83fb95 100644 --- a/R/module_teal_with_splash.R +++ b/R/module_teal_with_splash.R @@ -20,18 +20,21 @@ ui_teal_with_splash <- function(id, header = tags$p(), footer = tags$p()) { lifecycle::deprecate_soft( - when = "0.16", + when = "0.15.3", what = "ui_teal_with_splash()", details = "Deprecated, please use `ui_teal` instead" ) - ui_teal(id = id, title = title, header = header, footer = footer) + res <- ui_teal(id = id) + res <- modify_title(res, title) + res <- modify_header(res, header) + modify_footer(res, footer) } #' @export #' @rdname module_teal_with_splash srv_teal_with_splash <- function(id, data, modules, filter = teal_slices()) { lifecycle::deprecate_soft( - when = "0.16", + when = "0.15.3", what = "srv_teal_with_splash()", details = "Deprecated, please use `srv_teal` instead" ) diff --git a/R/show_rcode_modal.R b/R/show_rcode_modal.R index 0f7fe88228..83bd248a98 100644 --- a/R/show_rcode_modal.R +++ b/R/show_rcode_modal.R @@ -15,7 +15,7 @@ #' @export show_rcode_modal <- function(title = NULL, rcode, session = getDefaultReactiveDomain()) { lifecycle::deprecate_soft( - when = "0.16", + when = "0.15.3", what = "show_rcode_modal()", details = "This function will be removed in the next release." ) diff --git a/R/tdata.R b/R/tdata.R index bb020c99f7..dc79693bfa 100644 --- a/R/tdata.R +++ b/R/tdata.R @@ -52,7 +52,7 @@ as_tdata <- function(...) { .deprecate_tdata_msg <- function() { lifecycle::deprecate_stop( - when = "0.16", + when = "0.15.3", what = "tdata()", details = paste( "tdata has been removed in favour of `teal_data`.\n", diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R new file mode 100644 index 0000000000..3379f68643 --- /dev/null +++ b/R/teal_modifiers.R @@ -0,0 +1,236 @@ +#' Replace UI Elements in `teal` UI objects +#' +#' @param x One of: +#' - List containing `ui` and `server` components (teal app/module) +#' - Function with `request` parameter (teal UI function) +#' - Function with `id` parameter (module UI function) +#' - Shiny tag object (`shiny.tag`, `shiny.tag.list`, `html`) +#' @param selector (`character(1)`) CSS selector to find elements to replace +#' @param element Replacement UI element (shiny tag or HTML) +#' @param asis (`logical(1)`) If TRUE, use the ID (selector prefixed with #) as-is even when inside a module. +#' Else use the module ID as a namespace. +#' @param title (`shiny.tag` or `character(1)`) The new title to be used. +#' @param favicon (`character`) The path for the icon for the title. +#' The image/icon path can be remote or the static path accessible by `shiny`, like the `www/` +#' @param ns_id (`character(1)`) The namespace ID to use when replacing elements in a module. +#' @name teal_modifiers +#' @rdname teal_modifiers +#' +#' @keywords internal +#' +NULL + + +#' @rdname teal_modifiers +#' @keywords internal +teal_replace_ui <- function(x, selector, element, asis = FALSE, ns_id = character(0)) { + if (checkmate::test_list(x) && all(c("ui", "server") %in% names(x))) { + # teal app, teal_module, teal_data_module, teal_transform_module. + x$ui <- teal_replace_ui(x$ui, selector, element, asis = asis) + x + } else if (checkmate::test_function(x, args = "request")) { + # teal ui function + function(request) { + ui_tq <- htmltools::tagQuery(x(request = request)) + ui_tq$find(selector)$children()$replaceWith(element)$allTags() + } + } else if (checkmate::test_function(x, args = "id")) { + # shiny module ui function + function(id, ...) { + ui_tq <- htmltools::tagQuery(x(id = id, ...)) + if (grepl("^#[a-zA-Z0-9_-]+$", selector) && !asis) { + selector <- paste0("#", NS(id, gsub("^#", "", selector))) + } + ui_tq$find(selector)$replaceWith(element)$allTags() + } + } else if (inherits(x, c("shiny.tag", "shiny.tag.list", "html"))) { + # shiny ui object + ui_tq <- htmltools::tagQuery(x) + if (grepl("^#[a-zA-Z0-9_-]+$", selector) && !asis) { + selector <- paste0("#", NS(ns_id, gsub("^#", "", selector))) + } + ui_tq$find(selector)$children()$replaceWith(element)$allTags() + } else { + stop("Invalid UI object") + } +} + +#' Add a custom Title to `teal` application +#' +#' @rdname teal_modifiers +#' @export +#' @examples +#' app <- init( +#' data = teal_data(IRIS = iris, MTCARS = mtcars), +#' modules = modules(example_module()) +#' ) |> +#' modify_title(title = "Custom title") +#' +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +modify_title <- function( + x, + title = "teal app", + favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { + checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) + checkmate::assert_string(favicon) + teal_replace_ui( + x, + "#teal-title", + tags$head( + tags$title(title), + tags$link( + rel = "icon", + href = favicon, + sizes = "any" + ) + ) + ) +} + +#' Add a Header to `teal` Application +#' +#' @rdname teal_modifiers +#' @export +#' @examples +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' modify_header(element = tags$div(h3("Custom header"))) +#' +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +modify_header <- function(x, element = tags$p()) { + checkmate::assert_multi_class(element, c("shiny.tag", "shiny.tag.list", "html", "character")) + teal_replace_ui(x, "#teal-header", element) +} + +#' Add a Footer to `teal` Application +#' +#' @rdname teal_modifiers +#' @export +#' @examples +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' modify_footer(element = "Custom footer") +#' +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +modify_footer <- function(x, element = tags$p()) { + checkmate::assert_multi_class(element, c("shiny.tag", "shiny.tag.list", "html", "character")) + teal_replace_ui(x, "#teal-footer", element) +} + +#' Add a Landing Popup to `teal` Application +#' +#' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. +#' The dialog blocks access to the application and must be closed with a button before the application can be viewed. +#' +#' @param app (`list`) The `teal` ui and server object created using `init`. +#' @param title (`character(1)`) Text to be displayed as popup title. +#' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. +#' Passed to `...` of `shiny::modalDialog`. +#' @param buttons (`shiny.tag` or `shiny.tag.list`) Typically a `modalButton` or `actionButton`. +#' @rdname teal_modifiers +#' @export +#' @examples +#' app <- init( +#' data = teal_data(IRIS = iris, MTCARS = mtcars), +#' modules = modules(example_module()) +#' ) |> +#' add_landing_popup( +#' title = "Welcome", +#' content = "This is a landing popup.", +#' buttons = modalButton("Accept") +#' ) +#' +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +add_landing_popup <- function( + app, + title = NULL, + content = NULL, + buttons = modalButton("Accept")) { + custom_server <- function(input, output, session) { + checkmate::assert_string(title, null.ok = TRUE) + checkmate::assert_multi_class( + content, + classes = c("character", "shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE + ) + checkmate::assert_multi_class(buttons, classes = c("shiny.tag", "shiny.tag.list")) + showModal( + modalDialog( + id = "landingpopup", + title = title, + content, + footer = buttons + ) + ) + } + add_custom_server(app, custom_server) +} + +#' Add a Custom Server Logic to `teal` Application +#' +#' @description Adds a custom server function to the `teal` app. This function can define additional server logic. +#' +#' @param app (`list`) The `teal` ui and server object created using `init`. +#' @param custom_server (`function(input, output, session)` or `function(input, output, session)`) +#' The custom server function or server module to set. +#' @param module_id (`character(1)`) The ID of the module when a module server function is passed. +#' @rdname teal_modifiers +#' @keywords internal +#' @examples +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' add_custom_server(function(input, output, session) { +#' print("injected server logic") +#' }) +#' +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +#' +#' ns <- NS("custom_ns") +#' app <- init( +#' data = teal_data(IRIS = iris), +#' modules = modules(example_module()) +#' ) |> +#' modify_header(actionButton(ns("button"), "Click me")) |> +#' add_custom_server( +#' function(id) { +#' moduleServer(id, function(input, output, session) { +#' observeEvent(input$button, { +#' showNotification("Button is clicked!") +#' }) +#' }) +#' }, +#' module_id = "custom_ns" +#' ) +#' +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } +add_custom_server <- function(app, custom_server, module_id = character(0)) { + checkmate::assert_function(custom_server) + old_server <- app$server + + app$server <- function(input, output, session) { + old_server(input, output, session) + if (all(c("input", "output", "session") %in% names(formals(custom_server)))) { + callModule(custom_server, module_id) + } else if ("id" %in% names(formals(custom_server))) { + custom_server(module_id) + } + } + app +} diff --git a/man/add_custom_server.Rd b/man/add_custom_server.Rd deleted file mode 100644 index f2666d4cb3..0000000000 --- a/man/add_custom_server.Rd +++ /dev/null @@ -1,53 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/init.R -\name{add_custom_server} -\alias{add_custom_server} -\title{Add a Custom Server Logic to \code{teal} Application} -\usage{ -add_custom_server(app, custom_server, module_id = "wrapper") -} -\arguments{ -\item{app}{(\code{list}) The \code{teal} app environment.} - -\item{custom_server}{(\verb{function(input, output, session)} or \verb{function(input, output, session)}) -The custom server function or server module to set.} - -\item{module_id}{(\code{character(1)}) The ID of the module when a module server function is passed.} -} -\description{ -Adds a custom server function to the \code{teal} app. This function can define additional server logic. -} -\examples{ -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - add_custom_server(function(input, output, session) { - print("injected server logic") - }) - -if (interactive()) { - shinyApp(app$ui, app$server) -} - -ns <- NS("custom_ns") -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - modify_header(actionButton(ns("button"), "Click me")) |> - add_custom_server( - function(id) { - moduleServer(id, function(input, output, session) { - observeEvent(input$button, { - showNotification("Button is clicked!") - }) - }) - }, - module_id = "custom_ns" - ) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -} diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd deleted file mode 100644 index b2ec502e41..0000000000 --- a/man/add_landing_popup.Rd +++ /dev/null @@ -1,42 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/init.R -\name{add_landing_popup} -\alias{add_landing_popup} -\title{Add a Landing Popup to \code{teal} Application} -\usage{ -add_landing_popup( - app, - title = NULL, - content = NULL, - buttons = modalButton("Accept") -) -} -\arguments{ -\item{app}{(\code{list}) The \code{teal} app environment.} - -\item{title}{(\code{character(1)}) Text to be displayed as popup title.} - -\item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. -Passed to \code{...} of \code{shiny::modalDialog}. See examples.} - -\item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}. See examples.} -} -\description{ -Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. -The dialog blocks access to the application and must be closed with a button before the application can be viewed. -} -\examples{ -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = modules(example_module()) -) |> - add_landing_popup( - title = "Welcome", - content = "This is a landing popup.", - buttons = modalButton("Accept") - ) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -} diff --git a/man/modify_footer.Rd b/man/modify_footer.Rd deleted file mode 100644 index 2a4153ee4a..0000000000 --- a/man/modify_footer.Rd +++ /dev/null @@ -1,27 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/init.R -\name{modify_footer} -\alias{modify_footer} -\title{Add a Footer to \code{teal} Application} -\usage{ -modify_footer(app, footer = tags$p()) -} -\arguments{ -\item{app}{(\code{list}) The \code{teal} app environment.} - -\item{footer}{(\code{shiny.tag} or \code{character(1)}) The footer content to set. Defaults to an empty paragraph tag.} -} -\description{ -Adds a footer to the \code{teal} app. -} -\examples{ -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - modify_footer("Custom footer") - -if (interactive()) { - shinyApp(app$ui, app$server) -} -} diff --git a/man/modify_header.Rd b/man/modify_header.Rd deleted file mode 100644 index a24d9d3756..0000000000 --- a/man/modify_header.Rd +++ /dev/null @@ -1,31 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/init.R -\name{modify_header} -\alias{modify_header} -\title{Add a Header to \code{teal} Application} -\usage{ -modify_header(app, header = tags$p()) -} -\arguments{ -\item{app}{(\code{list}) The \code{teal} app environment.} - -\item{header}{(\code{shiny.tag} or \code{character(1)}) The header content to set. Defaults to an empty paragraph tag.} -} -\description{ -Adds a header to the \code{teal} app. -} -\examples{ -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - modify_header( - tags$div( - h3("Custom header") - ) - ) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -} diff --git a/man/modify_title.Rd b/man/modify_title.Rd deleted file mode 100644 index db2b09cee7..0000000000 --- a/man/modify_title.Rd +++ /dev/null @@ -1,38 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/init.R -\name{modify_title} -\alias{modify_title} -\title{Add a custom Title to \code{teal} application} -\usage{ -modify_title( - app, - title = "teal app", - favicon = - "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png" -) -} -\arguments{ -\item{app}{(\code{list}) The \code{teal} app environment.} - -\item{title}{(\code{shiny.tag} or \code{character(1)}) The title to be used} - -\item{favicon}{(\code{character}) The path for the icon for the title. -The image/icon path can be remote or the static path accessible by \code{shiny}, like the \verb{www/}} -} -\value{ -The modified app object -} -\description{ -Add a custom Title to \code{teal} application -} -\examples{ -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = modules(example_module()) -) |> - modify_title("Custom title") - -if (interactive()) { - shinyApp(app$ui, app$server) -} -} diff --git a/man/module_teal.Rd b/man/module_teal.Rd index 7e34b653e6..91f4e8d805 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -6,13 +6,7 @@ \alias{srv_teal} \title{\code{teal} main module} \usage{ -ui_teal( - id, - modules, - title = build_app_title(), - header = tags$p(), - footer = tags$p() -) +ui_teal(id, modules) srv_teal(id, data, modules, filter = teal_slices()) } @@ -27,20 +21,6 @@ Nested list of \code{teal_modules} or \code{teal_module} objects or a single will be displayed in the \code{teal} application. See \code{\link[=modules]{modules()}} and \code{\link[=module]{module()}} for more details.} -\item{title}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, -the browser window title. Defaults to a title "teal app" with the icon of NEST. -Can be created using the \code{build_app_title()} or -by passing a valid \code{shiny.tag} which is a head tag with title and link tag. -This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} - -\item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, -the header of the app. -This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} - -\item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, -the footer of the app. -This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} - \item{data}{(\code{teal_data}, \code{teal_data_module}, or \code{reactive} returning \code{teal_data}) The data which application will depend on.} diff --git a/man/module_teal_with_splash.Rd b/man/module_teal_with_splash.Rd index c8fb484c10..18a24fe2f8 100644 --- a/man/module_teal_with_splash.Rd +++ b/man/module_teal_with_splash.Rd @@ -24,20 +24,6 @@ rather than a standalone \code{shiny} app. This is a legacy feature.} \item{data}{(\code{teal_data}, \code{teal_data_module}, or \code{reactive} returning \code{teal_data}) The data which application will depend on.} -\item{title}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, -the browser window title. Defaults to a title "teal app" with the icon of NEST. -Can be created using the \code{build_app_title()} or -by passing a valid \code{shiny.tag} which is a head tag with title and link tag. -This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} - -\item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, -the header of the app. -This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} - -\item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, -the footer of the app. -This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} - \item{modules}{(\code{list} or \code{teal_modules} or \code{teal_module}) Nested list of \code{teal_modules} or \code{teal_module} objects or a single \code{teal_modules} or \code{teal_module} object. These are the specific output modules which diff --git a/man/teal_modifiers.Rd b/man/teal_modifiers.Rd new file mode 100644 index 0000000000..c0c18d9e83 --- /dev/null +++ b/man/teal_modifiers.Rd @@ -0,0 +1,150 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/teal_modifiers.R +\name{teal_modifiers} +\alias{teal_modifiers} +\alias{teal_replace_ui} +\alias{modify_title} +\alias{modify_header} +\alias{modify_footer} +\alias{add_landing_popup} +\alias{add_custom_server} +\title{Replace UI Elements in \code{teal} UI objects} +\usage{ +teal_replace_ui(x, selector, element, asis = FALSE, ns_id = character(0)) + +modify_title( + x, + title = "teal app", + favicon = + "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png" +) + +modify_header(x, element = tags$p()) + +modify_footer(x, element = tags$p()) + +add_landing_popup( + app, + title = NULL, + content = NULL, + buttons = modalButton("Accept") +) + +add_custom_server(app, custom_server, module_id = character(0)) +} +\arguments{ +\item{x}{One of: +\itemize{ +\item List containing \code{ui} and \code{server} components (teal app/module) +\item Function with \code{request} parameter (teal UI function) +\item Function with \code{id} parameter (module UI function) +\item Shiny tag object (\code{shiny.tag}, \code{shiny.tag.list}, \code{html}) +}} + +\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} + +\item{element}{Replacement UI element (shiny tag or HTML)} + +\item{asis}{(\code{logical(1)}) If TRUE, use the ID (selector prefixed with #) as-is even when inside a module. +Else use the module ID as a namespace.} + +\item{ns_id}{(\code{character(1)}) The namespace ID to use when replacing elements in a module.} + +\item{title}{(\code{character(1)}) Text to be displayed as popup title.} + +\item{favicon}{(\code{character}) The path for the icon for the title. +The image/icon path can be remote or the static path accessible by \code{shiny}, like the \verb{www/}} + +\item{app}{(\code{list}) The \code{teal} ui and server object created using \code{init}.} + +\item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. +Passed to \code{...} of \code{shiny::modalDialog}.} + +\item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}.} + +\item{custom_server}{(\verb{function(input, output, session)} or \verb{function(input, output, session)}) +The custom server function or server module to set.} + +\item{module_id}{(\code{character(1)}) The ID of the module when a module server function is passed.} +} +\description{ +Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. +The dialog blocks access to the application and must be closed with a button before the application can be viewed. + +Adds a custom server function to the \code{teal} app. This function can define additional server logic. +} +\examples{ +app <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules(example_module()) +) |> + modify_title(title = "Custom title") + +if (interactive()) { + shinyApp(app$ui, app$server) +} +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + modify_header(element = tags$div(h3("Custom header"))) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + modify_footer(element = "Custom footer") + +if (interactive()) { + shinyApp(app$ui, app$server) +} +app <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules(example_module()) +) |> + add_landing_popup( + title = "Welcome", + content = "This is a landing popup.", + buttons = modalButton("Accept") + ) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + add_custom_server(function(input, output, session) { + print("injected server logic") + }) + +if (interactive()) { + shinyApp(app$ui, app$server) +} + +ns <- NS("custom_ns") +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + modify_header(actionButton(ns("button"), "Click me")) |> + add_custom_server( + function(id) { + moduleServer(id, function(input, output, session) { + observeEvent(input$button, { + showNotification("Button is clicked!") + }) + }) + }, + module_id = "custom_ns" + ) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +} +\keyword{internal} From ff480f948cf42fa4cf8f134388c0ce20c95993ef Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 07:57:48 +0000 Subject: [PATCH 34/50] [skip style] [skip vbump] Restyle files --- R/module_teal.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index 3ef993e84b..5ffd5a8d87 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -43,13 +43,13 @@ NULL ui_teal <- function(id, modules) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) title <- tags$head( - tags$title("teal app"), - tags$link( - rel = "icon", - href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", - sizes = "any" - ) + tags$title("teal app"), + tags$link( + rel = "icon", + href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", + sizes = "any" ) + ) header <- tags$p() footer <- tags$p() ns <- NS(id) From 160874c77d38f9dd3752fd742b84ca8fa285a4ee Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 13:23:42 +0530 Subject: [PATCH 35/50] feat: separate the sessioninfo module and remove unwanted ui from module_teal --- .pre-commit-config.yaml | 1 + DESCRIPTION | 1 + NAMESPACE | 2 + R/init.R | 43 +++++++-- R/module_session_info.R | 54 +++++++++++ R/module_teal.R | 39 +------- R/module_teal_with_splash.R | 24 ++++- R/teal_modifiers.R | 58 +++++------- man/add_landing_popup.Rd | 42 +++++++++ man/module_filter_data.Rd | 5 +- man/module_teal.Rd | 5 +- man/module_teal_module.Rd | 5 +- man/module_teal_with_splash.Rd | 5 +- man/session_info.Rd | 37 ++++++++ man/teal_extend_server.Rd | 54 +++++++++++ man/teal_modifiers.Rd | 92 ++----------------- tests/testthat/test-module_sessioninfo.R | 45 +++++++++ tests/testthat/test-module_teal.R | 54 ----------- tests/testthat/test-shinytest2-filter_panel.R | 1 + tests/testthat/test-shinytest2-init.R | 6 +- 20 files changed, 341 insertions(+), 232 deletions(-) create mode 100644 R/module_session_info.R create mode 100644 man/add_landing_popup.Rd create mode 100644 man/session_info.Rd create mode 100644 man/teal_extend_server.Rd create mode 100644 tests/testthat/test-module_sessioninfo.R diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cdab3ba284..81818efe03 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,6 +20,7 @@ repos: - mirai - checkmate - cli + - htmltools - jsonlite - lifecycle - logger diff --git a/DESCRIPTION b/DESCRIPTION index f36879b3dd..e2011476d8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -107,6 +107,7 @@ Collate: 'module_filter_manager.R' 'module_init_data.R' 'module_nested_tabs.R' + 'module_session_info.R' 'module_snapshot_manager.R' 'module_teal.R' 'module_teal_data.R' diff --git a/NAMESPACE b/NAMESPACE index d5e792d55e..ee7220014f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -33,6 +33,7 @@ export(new_tdata) export(report_card_template) export(reporter_previewer_module) export(show_rcode_modal) +export(srv_session_info) export(srv_teal) export(srv_teal_with_splash) export(srv_transform_teal_data) @@ -40,6 +41,7 @@ export(tdata2env) export(teal_data_module) export(teal_slices) export(teal_transform_module) +export(ui_session_info) export(ui_teal) export(ui_teal_with_splash) export(ui_transform_teal_data) diff --git a/R/init.R b/R/init.R index 00b669bd05..04b8d62d4c 100644 --- a/R/init.R +++ b/R/init.R @@ -191,13 +191,42 @@ init <- function(data, modules <- drop_module(modules, "teal_module_landing") # Note: UI must be a function to support bookmarking. - res <- list( - ui = function(request) { - ui_teal(id = character(0), modules = modules) - }, - server = function(input, output, session) { - srv_teal(id = character(0), data = data, modules = modules, filter = deep_copy_filter(filter)) - } + res <- structure( + list( + ui = function(request) { + fluidPage( + title = tags$div( + id = "teal-app-title", + tags$head( + tags$title("teal app"), + tags$link( + rel = "icon", + href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", + sizes = "any" + ) + ) + ), + tags$header( + id = "teal-header", + tags$div(id = "teal-header-content") + ), + ui_teal( + id = "teal", + modules = modules + ), + tags$footer( + id = "teal-footer", + tags$div(id = "teal-footer-content"), + ui_session_info("session_info") + ) + ) + }, + server = function(input, output, session) { + srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) + srv_session_info("session_info") + } + ), + class = "teal_app" ) if (lifecycle::is_present(title)) { diff --git a/R/module_session_info.R b/R/module_session_info.R new file mode 100644 index 0000000000..c0a13b565d --- /dev/null +++ b/R/module_session_info.R @@ -0,0 +1,54 @@ +#' `teal` user session info module +#' +#' Module to display the user session info popup and teal lockfile download. +#' +#' @rdname session_info +#' @name session_info +#' +#' @inheritParams module_teal +#' +#' @examples +#' ui <- fluidPage( +#' ui_session_info("session_info") +#' ) +#' +#' server <- function(input, output, session) { +#' srv_session_info("session_info") +#' } +#' +#' if (interactive()) { +#' shinyApp(ui, server) +#' } +#' +#' @return `NULL` invisibly +NULL + +#' @rdname session_info +#' @export +ui_session_info <- function(id) { + ns <- NS(id) + tags$div( + teal.widgets::verbatim_popup_ui(ns("sessionInfo"), "Session Info", type = "link"), + br(), + ui_teal_lockfile(ns("lockfile")), + textOutput(ns("identifier")) + ) +} + +#' @rdname session_info +#' @export +srv_session_info <- function(id) { + moduleServer(id, function(input, output, session) { + srv_teal_lockfile("lockfile") + + output$identifier <- renderText( + paste0("Pid:", Sys.getpid(), " Token:", substr(session$token, 25, 32)) + ) + + teal.widgets::verbatim_popup_srv( + "sessionInfo", + verbatim_content = utils::capture.output(utils::sessionInfo()), + title = "SessionInfo" + ) + }) +} diff --git a/R/module_teal.R b/R/module_teal.R index 5ffd5a8d87..eee64b4f14 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -42,16 +42,6 @@ NULL #' @export ui_teal <- function(id, modules) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) - title <- tags$head( - tags$title("teal app"), - tags$link( - rel = "icon", - href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", - sizes = "any" - ) - ) - header <- tags$p() - footer <- tags$p() ns <- NS(id) # show busy icon when `shiny` session is busy computing stuff @@ -68,10 +58,8 @@ ui_teal <- function(id, modules) { fluidPage( id = id, - title = tags$div(id = ns("teal-title"), title), theme = get_teal_bs_theme(), include_teal_css_js(), - tags$header(id = ns("teal-header"), header), tags$hr(class = "my-2"), shiny_busy_message_panel, tags$div( @@ -106,20 +94,15 @@ ui_teal <- function(id, modules) { ) ) ), - tags$hr(), - tags$footer( - tags$div( - tags$div(id = ns("teal-footer"), footer), - teal.widgets::verbatim_popup_ui(ns("sessionInfo"), "Session Info", type = "link"), - br(), - ui_teal_lockfile(ns("lockfile")), - textOutput(ns("identifier")) - ) - ) + tags$hr() ) } #' @rdname module_teal +#' @param modules (`teal_modules`) +#' `teal_modules` object. These are the specific output modules which +#' will be displayed in the `teal` application. See [modules()] and [module()] for +#' more details. #' @export srv_teal <- function(id, data, modules, filter = teal_slices()) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) @@ -134,18 +117,6 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { shinyjs::showLog() } - srv_teal_lockfile("lockfile") - - output$identifier <- renderText( - paste0("Pid:", Sys.getpid(), " Token:", substr(session$token, 25, 32)) - ) - - teal.widgets::verbatim_popup_srv( - "sessionInfo", - verbatim_content = utils::capture.output(utils::sessionInfo()), - title = "SessionInfo" - ) - # `JavaScript` code run_js_files(files = "init.js") diff --git a/R/module_teal_with_splash.R b/R/module_teal_with_splash.R index 998c83fb95..c563cef790 100644 --- a/R/module_teal_with_splash.R +++ b/R/module_teal_with_splash.R @@ -24,10 +24,25 @@ ui_teal_with_splash <- function(id, what = "ui_teal_with_splash()", details = "Deprecated, please use `ui_teal` instead" ) - res <- ui_teal(id = id) - res <- modify_title(res, title) - res <- modify_header(res, header) - modify_footer(res, footer) + fluidPage( + title = tags$div( + id = "teal-title", + tags$head( + tags$title("teal app"), + tags$link( + rel = "icon", + href = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png", + sizes = "any" + ) + ) + ), + tags$header(id = "teal-header-content"), + ui_teal(id = id), + tags$footer( + tags$div(id = "teal-footer-content"), + ui_session_info(ns("session_info")) + ) + ) } #' @export @@ -39,4 +54,5 @@ srv_teal_with_splash <- function(id, data, modules, filter = teal_slices()) { details = "Deprecated, please use `srv_teal` instead" ) srv_teal(id = id, data = data, modules = modules, filter = filter) + srv_session_info(NS(id, "session_info")) } diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R index 3379f68643..1391eec23c 100644 --- a/R/teal_modifiers.R +++ b/R/teal_modifiers.R @@ -4,15 +4,11 @@ #' - List containing `ui` and `server` components (teal app/module) #' - Function with `request` parameter (teal UI function) #' - Function with `id` parameter (module UI function) -#' - Shiny tag object (`shiny.tag`, `shiny.tag.list`, `html`) #' @param selector (`character(1)`) CSS selector to find elements to replace #' @param element Replacement UI element (shiny tag or HTML) -#' @param asis (`logical(1)`) If TRUE, use the ID (selector prefixed with #) as-is even when inside a module. -#' Else use the module ID as a namespace. #' @param title (`shiny.tag` or `character(1)`) The new title to be used. #' @param favicon (`character`) The path for the icon for the title. #' The image/icon path can be remote or the static path accessible by `shiny`, like the `www/` -#' @param ns_id (`character(1)`) The namespace ID to use when replacing elements in a module. #' @name teal_modifiers #' @rdname teal_modifiers #' @@ -21,35 +17,26 @@ NULL -#' @rdname teal_modifiers #' @keywords internal -teal_replace_ui <- function(x, selector, element, asis = FALSE, ns_id = character(0)) { - if (checkmate::test_list(x) && all(c("ui", "server") %in% names(x))) { - # teal app, teal_module, teal_data_module, teal_transform_module. - x$ui <- teal_replace_ui(x$ui, selector, element, asis = asis) +teal_replace_ui <- function(x, selector, element) { + if (inherits(x, c("teal_app", "teal_module", "teal_data_module", "teal_transform_module"))) { + x$ui <- teal_replace_ui(x$ui, selector, element) x } else if (checkmate::test_function(x, args = "request")) { - # teal ui function + # shiny ui function from teal_app function(request) { ui_tq <- htmltools::tagQuery(x(request = request)) - ui_tq$find(selector)$children()$replaceWith(element)$allTags() + ui_tq$find(selector)$empty()$append(element)$allTags() } } else if (checkmate::test_function(x, args = "id")) { # shiny module ui function function(id, ...) { ui_tq <- htmltools::tagQuery(x(id = id, ...)) - if (grepl("^#[a-zA-Z0-9_-]+$", selector) && !asis) { + if (grepl("^#[a-zA-Z0-9_-]+$", selector)) { selector <- paste0("#", NS(id, gsub("^#", "", selector))) } - ui_tq$find(selector)$replaceWith(element)$allTags() - } - } else if (inherits(x, c("shiny.tag", "shiny.tag.list", "html"))) { - # shiny ui object - ui_tq <- htmltools::tagQuery(x) - if (grepl("^#[a-zA-Z0-9_-]+$", selector) && !asis) { - selector <- paste0("#", NS(ns_id, gsub("^#", "", selector))) + ui_tq$find(selector)$empty()$append(element)$allTags() } - ui_tq$find(selector)$children()$replaceWith(element)$allTags() } else { stop("Invalid UI object") } @@ -73,11 +60,12 @@ modify_title <- function( x, title = "teal app", favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { + checkmate::assert_multi_class(x, "teal_app") checkmate::assert_multi_class(title, c("shiny.tag", "shiny.tag.list", "html", "character")) checkmate::assert_string(favicon) teal_replace_ui( x, - "#teal-title", + "#teal-app-title", tags$head( tags$title(title), tags$link( @@ -105,7 +93,7 @@ modify_title <- function( #' } modify_header <- function(x, element = tags$p()) { checkmate::assert_multi_class(element, c("shiny.tag", "shiny.tag.list", "html", "character")) - teal_replace_ui(x, "#teal-header", element) + teal_replace_ui(x, "#teal-header-content", element) } #' Add a Footer to `teal` Application @@ -124,7 +112,7 @@ modify_header <- function(x, element = tags$p()) { #' } modify_footer <- function(x, element = tags$p()) { checkmate::assert_multi_class(element, c("shiny.tag", "shiny.tag.list", "html", "character")) - teal_replace_ui(x, "#teal-footer", element) + teal_replace_ui(x, "#teal-footer-content", element) } #' Add a Landing Popup to `teal` Application @@ -132,12 +120,11 @@ modify_footer <- function(x, element = tags$p()) { #' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. #' The dialog blocks access to the application and must be closed with a button before the application can be viewed. #' -#' @param app (`list`) The `teal` ui and server object created using `init`. +#' @param x (`list`) The `teal` ui and server object created using `init`. #' @param title (`character(1)`) Text to be displayed as popup title. #' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. #' Passed to `...` of `shiny::modalDialog`. #' @param buttons (`shiny.tag` or `shiny.tag.list`) Typically a `modalButton` or `actionButton`. -#' @rdname teal_modifiers #' @export #' @examples #' app <- init( @@ -154,10 +141,11 @@ modify_footer <- function(x, element = tags$p()) { #' shinyApp(app$ui, app$server) #' } add_landing_popup <- function( - app, + x, title = NULL, content = NULL, buttons = modalButton("Accept")) { + checkmate::assert_class(x, "teal_app") custom_server <- function(input, output, session) { checkmate::assert_string(title, null.ok = TRUE) checkmate::assert_multi_class( @@ -174,25 +162,24 @@ add_landing_popup <- function( ) ) } - add_custom_server(app, custom_server) + teal_extend_server(x, custom_server) } #' Add a Custom Server Logic to `teal` Application #' #' @description Adds a custom server function to the `teal` app. This function can define additional server logic. #' -#' @param app (`list`) The `teal` ui and server object created using `init`. +#' @param x (`list`) The `teal` ui and server object created using `init`. #' @param custom_server (`function(input, output, session)` or `function(input, output, session)`) #' The custom server function or server module to set. #' @param module_id (`character(1)`) The ID of the module when a module server function is passed. -#' @rdname teal_modifiers #' @keywords internal #' @examples #' app <- init( #' data = teal_data(IRIS = iris), #' modules = modules(example_module()) #' ) |> -#' add_custom_server(function(input, output, session) { +#' teal_extend_server(function(input, output, session) { #' print("injected server logic") #' }) #' @@ -206,7 +193,7 @@ add_landing_popup <- function( #' modules = modules(example_module()) #' ) |> #' modify_header(actionButton(ns("button"), "Click me")) |> -#' add_custom_server( +#' teal_extend_server( #' function(id) { #' moduleServer(id, function(input, output, session) { #' observeEvent(input$button, { @@ -220,11 +207,12 @@ add_landing_popup <- function( #' if (interactive()) { #' shinyApp(app$ui, app$server) #' } -add_custom_server <- function(app, custom_server, module_id = character(0)) { +teal_extend_server <- function(x, custom_server, module_id = character(0)) { + checkmate::assert_class(x, "teal_app") checkmate::assert_function(custom_server) - old_server <- app$server + old_server <- x$server - app$server <- function(input, output, session) { + x$server <- function(input, output, session) { old_server(input, output, session) if (all(c("input", "output", "session") %in% names(formals(custom_server)))) { callModule(custom_server, module_id) @@ -232,5 +220,5 @@ add_custom_server <- function(app, custom_server, module_id = character(0)) { custom_server(module_id) } } - app + x } diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd new file mode 100644 index 0000000000..6df6d3ff9f --- /dev/null +++ b/man/add_landing_popup.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/teal_modifiers.R +\name{add_landing_popup} +\alias{add_landing_popup} +\title{Add a Landing Popup to \code{teal} Application} +\usage{ +add_landing_popup( + x, + title = NULL, + content = NULL, + buttons = modalButton("Accept") +) +} +\arguments{ +\item{x}{(\code{list}) The \code{teal} ui and server object created using \code{init}.} + +\item{title}{(\code{character(1)}) Text to be displayed as popup title.} + +\item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. +Passed to \code{...} of \code{shiny::modalDialog}.} + +\item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}.} +} +\description{ +Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. +The dialog blocks access to the application and must be closed with a button before the application can be viewed. +} +\examples{ +app <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules(example_module()) +) |> + add_landing_popup( + title = "Welcome", + content = "This is a landing popup.", + buttons = modalButton("Accept") + ) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +} diff --git a/man/module_filter_data.Rd b/man/module_filter_data.Rd index a98b9d6b95..ae47937d49 100644 --- a/man/module_filter_data.Rd +++ b/man/module_filter_data.Rd @@ -32,9 +32,8 @@ which implies in filter-panel to be "global". When \code{NULL} then filter-panel \item{data}{(\code{reactive} returning \code{teal_data})} -\item{modules}{(\code{list} or \code{teal_modules} or \code{teal_module}) -Nested list of \code{teal_modules} or \code{teal_module} objects or a single -\code{teal_modules} or \code{teal_module} object. These are the specific output modules which +\item{modules}{(\code{teal_modules}) +\code{teal_modules} object. These are the specific output modules which will be displayed in the \code{teal} application. See \code{\link[=modules]{modules()}} and \code{\link[=module]{module()}} for more details.} } diff --git a/man/module_teal.Rd b/man/module_teal.Rd index 91f4e8d805..14c315a9ef 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -15,9 +15,8 @@ srv_teal(id, data, modules, filter = teal_slices()) a string specifying the \code{shiny} module id in cases it is used as a \code{shiny} module rather than a standalone \code{shiny} app. This is a legacy feature.} -\item{modules}{(\code{list} or \code{teal_modules} or \code{teal_module}) -Nested list of \code{teal_modules} or \code{teal_module} objects or a single -\code{teal_modules} or \code{teal_module} object. These are the specific output modules which +\item{modules}{(\code{teal_modules}) +\code{teal_modules} object. These are the specific output modules which will be displayed in the \code{teal} application. See \code{\link[=modules]{modules()}} and \code{\link[=module]{module()}} for more details.} diff --git a/man/module_teal_module.Rd b/man/module_teal_module.Rd index 9c4d80361f..1c8cab10c8 100644 --- a/man/module_teal_module.Rd +++ b/man/module_teal_module.Rd @@ -69,9 +69,8 @@ srv_teal_module( a string specifying the \code{shiny} module id in cases it is used as a \code{shiny} module rather than a standalone \code{shiny} app. This is a legacy feature.} -\item{modules}{(\code{list} or \code{teal_modules} or \code{teal_module}) -Nested list of \code{teal_modules} or \code{teal_module} objects or a single -\code{teal_modules} or \code{teal_module} object. These are the specific output modules which +\item{modules}{(\code{teal_modules}) +\code{teal_modules} object. These are the specific output modules which will be displayed in the \code{teal} application. See \code{\link[=modules]{modules()}} and \code{\link[=module]{module()}} for more details.} diff --git a/man/module_teal_with_splash.Rd b/man/module_teal_with_splash.Rd index 18a24fe2f8..8d7f57043b 100644 --- a/man/module_teal_with_splash.Rd +++ b/man/module_teal_with_splash.Rd @@ -24,9 +24,8 @@ rather than a standalone \code{shiny} app. This is a legacy feature.} \item{data}{(\code{teal_data}, \code{teal_data_module}, or \code{reactive} returning \code{teal_data}) The data which application will depend on.} -\item{modules}{(\code{list} or \code{teal_modules} or \code{teal_module}) -Nested list of \code{teal_modules} or \code{teal_module} objects or a single -\code{teal_modules} or \code{teal_module} object. These are the specific output modules which +\item{modules}{(\code{teal_modules}) +\code{teal_modules} object. These are the specific output modules which will be displayed in the \code{teal} application. See \code{\link[=modules]{modules()}} and \code{\link[=module]{module()}} for more details.} diff --git a/man/session_info.Rd b/man/session_info.Rd new file mode 100644 index 0000000000..2fe30f7b5f --- /dev/null +++ b/man/session_info.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/module_session_info.R +\name{session_info} +\alias{session_info} +\alias{ui_session_info} +\alias{srv_session_info} +\title{\code{teal} user session info module} +\usage{ +ui_session_info(id) + +srv_session_info(id) +} +\arguments{ +\item{id}{(\code{character}) Optionally, +a string specifying the \code{shiny} module id in cases it is used as a \code{shiny} module +rather than a standalone \code{shiny} app. This is a legacy feature.} +} +\value{ +\code{NULL} invisibly +} +\description{ +Module to display the user session info popup and teal lockfile download. +} +\examples{ +ui <- fluidPage( + ui_session_info("session_info") +) + +server <- function(input, output, session) { + srv_session_info("session_info") +} + +if (interactive()) { + shinyApp(ui, server) +} + +} diff --git a/man/teal_extend_server.Rd b/man/teal_extend_server.Rd new file mode 100644 index 0000000000..8ea373518a --- /dev/null +++ b/man/teal_extend_server.Rd @@ -0,0 +1,54 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/teal_modifiers.R +\name{teal_extend_server} +\alias{teal_extend_server} +\title{Add a Custom Server Logic to \code{teal} Application} +\usage{ +teal_extend_server(x, custom_server, module_id = character(0)) +} +\arguments{ +\item{x}{(\code{list}) The \code{teal} ui and server object created using \code{init}.} + +\item{custom_server}{(\verb{function(input, output, session)} or \verb{function(input, output, session)}) +The custom server function or server module to set.} + +\item{module_id}{(\code{character(1)}) The ID of the module when a module server function is passed.} +} +\description{ +Adds a custom server function to the \code{teal} app. This function can define additional server logic. +} +\examples{ +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + teal_extend_server(function(input, output, session) { + print("injected server logic") + }) + +if (interactive()) { + shinyApp(app$ui, app$server) +} + +ns <- NS("custom_ns") +app <- init( + data = teal_data(IRIS = iris), + modules = modules(example_module()) +) |> + modify_header(actionButton(ns("button"), "Click me")) |> + teal_extend_server( + function(id) { + moduleServer(id, function(input, output, session) { + observeEvent(input$button, { + showNotification("Button is clicked!") + }) + }) + }, + module_id = "custom_ns" + ) + +if (interactive()) { + shinyApp(app$ui, app$server) +} +} +\keyword{internal} diff --git a/man/teal_modifiers.Rd b/man/teal_modifiers.Rd index c0c18d9e83..2202a8bbba 100644 --- a/man/teal_modifiers.Rd +++ b/man/teal_modifiers.Rd @@ -2,16 +2,11 @@ % Please edit documentation in R/teal_modifiers.R \name{teal_modifiers} \alias{teal_modifiers} -\alias{teal_replace_ui} \alias{modify_title} \alias{modify_header} \alias{modify_footer} -\alias{add_landing_popup} -\alias{add_custom_server} \title{Replace UI Elements in \code{teal} UI objects} \usage{ -teal_replace_ui(x, selector, element, asis = FALSE, ns_id = character(0)) - modify_title( x, title = "teal app", @@ -22,15 +17,6 @@ modify_title( modify_header(x, element = tags$p()) modify_footer(x, element = tags$p()) - -add_landing_popup( - app, - title = NULL, - content = NULL, - buttons = modalButton("Accept") -) - -add_custom_server(app, custom_server, module_id = character(0)) } \arguments{ \item{x}{One of: @@ -38,40 +24,25 @@ add_custom_server(app, custom_server, module_id = character(0)) \item List containing \code{ui} and \code{server} components (teal app/module) \item Function with \code{request} parameter (teal UI function) \item Function with \code{id} parameter (module UI function) -\item Shiny tag object (\code{shiny.tag}, \code{shiny.tag.list}, \code{html}) }} -\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} - -\item{element}{Replacement UI element (shiny tag or HTML)} - -\item{asis}{(\code{logical(1)}) If TRUE, use the ID (selector prefixed with #) as-is even when inside a module. -Else use the module ID as a namespace.} - -\item{ns_id}{(\code{character(1)}) The namespace ID to use when replacing elements in a module.} - -\item{title}{(\code{character(1)}) Text to be displayed as popup title.} +\item{title}{(\code{shiny.tag} or \code{character(1)}) The new title to be used.} \item{favicon}{(\code{character}) The path for the icon for the title. The image/icon path can be remote or the static path accessible by \code{shiny}, like the \verb{www/}} -\item{app}{(\code{list}) The \code{teal} ui and server object created using \code{init}.} - -\item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. -Passed to \code{...} of \code{shiny::modalDialog}.} - -\item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}.} - -\item{custom_server}{(\verb{function(input, output, session)} or \verb{function(input, output, session)}) -The custom server function or server module to set.} +\item{element}{Replacement UI element (shiny tag or HTML)} -\item{module_id}{(\code{character(1)}) The ID of the module when a module server function is passed.} +\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} } \description{ -Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. -The dialog blocks access to the application and must be closed with a button before the application can be viewed. +Replace UI Elements in \code{teal} UI objects + +Add a custom Title to \code{teal} application -Adds a custom server function to the \code{teal} app. This function can define additional server logic. +Add a Header to \code{teal} Application + +Add a Footer to \code{teal} Application } \examples{ app <- init( @@ -98,51 +69,6 @@ app <- init( ) |> modify_footer(element = "Custom footer") -if (interactive()) { - shinyApp(app$ui, app$server) -} -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = modules(example_module()) -) |> - add_landing_popup( - title = "Welcome", - content = "This is a landing popup.", - buttons = modalButton("Accept") - ) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - add_custom_server(function(input, output, session) { - print("injected server logic") - }) - -if (interactive()) { - shinyApp(app$ui, app$server) -} - -ns <- NS("custom_ns") -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - modify_header(actionButton(ns("button"), "Click me")) |> - add_custom_server( - function(id) { - moduleServer(id, function(input, output, session) { - observeEvent(input$button, { - showNotification("Button is clicked!") - }) - }) - }, - module_id = "custom_ns" - ) - if (interactive()) { shinyApp(app$ui, app$server) } diff --git a/tests/testthat/test-module_sessioninfo.R b/tests/testthat/test-module_sessioninfo.R new file mode 100644 index 0000000000..bff7f092e8 --- /dev/null +++ b/tests/testthat/test-module_sessioninfo.R @@ -0,0 +1,45 @@ +testthat::describe("srv_session_info 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_session_info, + args = list(id = "test"), + 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_session_info, + args = list(id = "test"), + expr = { + testthat::expect_false(file.exists(renv_filename)) + } + ) + } + ) + }) +}) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 12d0c1c0e1..351ede4772 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -65,60 +65,6 @@ transform_list <<- list( ) ) -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", { testthat::expect_no_error( diff --git a/tests/testthat/test-shinytest2-filter_panel.R b/tests/testthat/test-shinytest2-filter_panel.R index 22be061ab7..da8f3e9ca5 100644 --- a/tests/testthat/test-shinytest2-filter_panel.R +++ b/tests/testthat/test-shinytest2-filter_panel.R @@ -69,6 +69,7 @@ testthat::test_that("e2e: filtering a module-specific filter is reflected in oth testthat::test_that("e2e: filtering a module-specific filter is not refected in other unshared modules", { skip_if_too_deep(5) + devtools::load_all() app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( diff --git a/tests/testthat/test-shinytest2-init.R b/tests/testthat/test-shinytest2-init.R index 854e087e33..bc585e4617 100644 --- a/tests/testthat/test-shinytest2-init.R +++ b/tests/testthat/test-shinytest2-init.R @@ -19,7 +19,7 @@ testthat::test_that("e2e: teal app initializes with sessionInfo modal", { ) # Check if button exists. - button_selector <- "#teal-sessionInfo-button" + button_selector <- "#session_info-sessionInfo-button" testthat::expect_equal( app$get_text(button_selector), "Session Info" @@ -37,7 +37,7 @@ testthat::test_that("e2e: teal app initializes with sessionInfo modal", { testthat::expect_setequal( testthat::expect_length( app$get_text( - "#shiny-modal [id^='teal-sessionInfo-copy_button']" + "#shiny-modal [id^='session_info-sessionInfo-copy_button']" ), 2 ), @@ -53,7 +53,7 @@ testthat::test_that("e2e: teal app initializes with sessionInfo modal", { ) # Check session info output. - session_info <- app$get_text("#teal-sessionInfo-verbatim_content") + session_info <- app$get_text("#session_info-sessionInfo-verbatim_content") testthat::expect_match(session_info, "R version", fixed = TRUE) testthat::expect_match(session_info, "attached base packages:", fixed = TRUE) From 78b61a4462dfbbe90d1acf20c9bfd0ccd180a772 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 13:45:24 +0530 Subject: [PATCH 36/50] chore: doc changes --- R/init.R | 2 +- R/teal_modifiers.R | 14 ++++++++------ _pkgdown.yml | 2 +- man/add_landing_popup.Rd | 2 +- man/teal_extend_server.Rd | 2 +- man/teal_modifiers.Rd | 17 ++++++++++------- vignettes/quick-usage-guide-for-teal.Rmd | 6 +++--- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/R/init.R b/R/init.R index 071ac457db..0ad0b5670e 100644 --- a/R/init.R +++ b/R/init.R @@ -282,7 +282,7 @@ init <- function(data, } if (length(landing) == 1L) { - res <- add_custom_server(res, function(input, output, session) { + res <- teal_extend_server(res, function(input, output, session) { do.call(landing[[1L]]$server, c(list(id = "landing_module_shiny_id"))) }) lifecycle::deprecate_warn( diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R index 1391eec23c..72cd48dc8a 100644 --- a/R/teal_modifiers.R +++ b/R/teal_modifiers.R @@ -1,9 +1,6 @@ #' Replace UI Elements in `teal` UI objects #' -#' @param x One of: -#' - List containing `ui` and `server` components (teal app/module) -#' - Function with `request` parameter (teal UI function) -#' - Function with `id` parameter (module UI function) +#' @param x (`teal_app`) A `teal_app` object created using the `init` function. #' @param selector (`character(1)`) CSS selector to find elements to replace #' @param element Replacement UI element (shiny tag or HTML) #' @param title (`shiny.tag` or `character(1)`) The new title to be used. @@ -17,7 +14,12 @@ NULL +#' @rdname teal_modifiers #' @keywords internal +#' @param x One of: +#' - A `teal_app` object created using the `init` function. +#' - A `teal_module`, `teal_data_module`, or `teal_transform_module` object. +#' - A Shiny module UI function with `id` parameter teal_replace_ui <- function(x, selector, element) { if (inherits(x, c("teal_app", "teal_module", "teal_data_module", "teal_transform_module"))) { x$ui <- teal_replace_ui(x$ui, selector, element) @@ -120,7 +122,7 @@ modify_footer <- function(x, element = tags$p()) { #' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. #' The dialog blocks access to the application and must be closed with a button before the application can be viewed. #' -#' @param x (`list`) The `teal` ui and server object created using `init`. +#' @param x (`teal_app`) A `teal_app` object created using the `init` function. #' @param title (`character(1)`) Text to be displayed as popup title. #' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. #' Passed to `...` of `shiny::modalDialog`. @@ -169,7 +171,7 @@ add_landing_popup <- function( #' #' @description Adds a custom server function to the `teal` app. This function can define additional server logic. #' -#' @param x (`list`) The `teal` ui and server object created using `init`. +#' @param x (`teal_app`) A `teal_app` object created using the `init` function. #' @param custom_server (`function(input, output, session)` or `function(input, output, session)`) #' The custom server function or server module to set. #' @param module_id (`character(1)`) The ID of the module when a module server function is passed. diff --git a/_pkgdown.yml b/_pkgdown.yml index 302c3e4977..dac4899012 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -115,7 +115,7 @@ reference: - modify_footer - modify_header - modify_title - - add_custom_server + - teal_extend_server - add_landing_popup - title: Helper Functions desc: Helper functions for `teal` diff --git a/man/add_landing_popup.Rd b/man/add_landing_popup.Rd index 6df6d3ff9f..cd02af76f2 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_popup.Rd @@ -12,7 +12,7 @@ add_landing_popup( ) } \arguments{ -\item{x}{(\code{list}) The \code{teal} ui and server object created using \code{init}.} +\item{x}{(\code{teal_app}) A \code{teal_app} object created using the \code{init} function.} \item{title}{(\code{character(1)}) Text to be displayed as popup title.} diff --git a/man/teal_extend_server.Rd b/man/teal_extend_server.Rd index 8ea373518a..c1ea08bc11 100644 --- a/man/teal_extend_server.Rd +++ b/man/teal_extend_server.Rd @@ -7,7 +7,7 @@ teal_extend_server(x, custom_server, module_id = character(0)) } \arguments{ -\item{x}{(\code{list}) The \code{teal} ui and server object created using \code{init}.} +\item{x}{(\code{teal_app}) A \code{teal_app} object created using the \code{init} function.} \item{custom_server}{(\verb{function(input, output, session)} or \verb{function(input, output, session)}) The custom server function or server module to set.} diff --git a/man/teal_modifiers.Rd b/man/teal_modifiers.Rd index 2202a8bbba..0d3d95a316 100644 --- a/man/teal_modifiers.Rd +++ b/man/teal_modifiers.Rd @@ -2,11 +2,14 @@ % Please edit documentation in R/teal_modifiers.R \name{teal_modifiers} \alias{teal_modifiers} +\alias{teal_replace_ui} \alias{modify_title} \alias{modify_header} \alias{modify_footer} \title{Replace UI Elements in \code{teal} UI objects} \usage{ +teal_replace_ui(x, selector, element) + modify_title( x, title = "teal app", @@ -21,19 +24,19 @@ modify_footer(x, element = tags$p()) \arguments{ \item{x}{One of: \itemize{ -\item List containing \code{ui} and \code{server} components (teal app/module) -\item Function with \code{request} parameter (teal UI function) -\item Function with \code{id} parameter (module UI function) +\item A \code{teal_app} object created using the \code{init} function. +\item A \code{teal_module}, \code{teal_data_module}, or \code{teal_transform_module} object. +\item A Shiny module UI function with \code{id} parameter }} +\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} + +\item{element}{Replacement UI element (shiny tag or HTML)} + \item{title}{(\code{shiny.tag} or \code{character(1)}) The new title to be used.} \item{favicon}{(\code{character}) The path for the icon for the title. The image/icon path can be remote or the static path accessible by \code{shiny}, like the \verb{www/}} - -\item{element}{Replacement UI element (shiny tag or HTML)} - -\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} } \description{ Replace UI Elements in \code{teal} UI objects diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd index f12705454e..f20a0be8b2 100644 --- a/vignettes/quick-usage-guide-for-teal.Rmd +++ b/vignettes/quick-usage-guide-for-teal.Rmd @@ -46,10 +46,10 @@ if (interactive()) { The following functions can be used to extend the `teal` application: * `modify_title` changes the title that appears in the browser tab. -* `modify_header` adds a header with a button that shows a notification when clicked, also note that the observer for this button was added using the modifier `add_custom_server`. +* `modify_header` adds a header with a button that shows a notification when clicked, also note that the observer for this button was added using the modifier `teal_extend_server`. * `modify_footer` adds a custom footer. * `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note that the screenshot does not show the popup, but it will appear when the application is first loaded. -* `add_custom_server` adds custom server logic to the teal application, in this case the button created in the header is observed and a notification is shown. +* `teal_extend_server` adds custom server logic to the teal application, in this case the button created in the header is observed and a notification is shown. ```{r, eval=FALSE} library(teal) @@ -72,7 +72,7 @@ app <- init( ) |> modify_footer(footer = "Custom footer") |> add_landing_popup(title = "Te al", content = "Welcome to my first teal application!") |> - add_custom_server(function(input, output, session) { + teal_extend_server(function(input, output, session) { observeEvent(input$notify, { showNotification("Yes, the button works!") }) From 1f68edcdc843bbbdcc187f116fde5f6e0c7d74a6 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 14:00:49 +0530 Subject: [PATCH 37/50] chore: fix pkgdown render + remove server modification from vignette --- R/module_session_info.R | 8 ++++---- _pkgdown.yml | 1 + man/{session_info.Rd => module_session_info.Rd} | 4 ++-- ...st-module_sessioninfo.R => test-module_session_info.R} | 0 vignettes/quick-usage-guide-for-teal.Rmd | 8 +------- 5 files changed, 8 insertions(+), 13 deletions(-) rename man/{session_info.Rd => module_session_info.Rd} (92%) rename tests/testthat/{test-module_sessioninfo.R => test-module_session_info.R} (100%) diff --git a/R/module_session_info.R b/R/module_session_info.R index c0a13b565d..e0c237a9fc 100644 --- a/R/module_session_info.R +++ b/R/module_session_info.R @@ -2,8 +2,8 @@ #' #' Module to display the user session info popup and teal lockfile download. #' -#' @rdname session_info -#' @name session_info +#' @rdname module_session_info +#' @name module_session_info #' #' @inheritParams module_teal #' @@ -23,7 +23,7 @@ #' @return `NULL` invisibly NULL -#' @rdname session_info +#' @rdname module_session_info #' @export ui_session_info <- function(id) { ns <- NS(id) @@ -35,7 +35,7 @@ ui_session_info <- function(id) { ) } -#' @rdname session_info +#' @rdname module_session_info #' @export srv_session_info <- function(id) { moduleServer(id, function(input, output, session) { diff --git a/_pkgdown.yml b/_pkgdown.yml index dac4899012..72a7f518a1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -106,6 +106,7 @@ reference: - make_teal_transform_server - module_teal_with_splash - module_teal + - module_session_info - module - modules - teal_slices diff --git a/man/session_info.Rd b/man/module_session_info.Rd similarity index 92% rename from man/session_info.Rd rename to man/module_session_info.Rd index 079319f760..b7ff9ec3f7 100644 --- a/man/session_info.Rd +++ b/man/module_session_info.Rd @@ -1,7 +1,7 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/module_session_info.R -\name{session_info} -\alias{session_info} +\name{module_session_info} +\alias{module_session_info} \alias{ui_session_info} \alias{srv_session_info} \title{\code{teal} user session info module} diff --git a/tests/testthat/test-module_sessioninfo.R b/tests/testthat/test-module_session_info.R similarity index 100% rename from tests/testthat/test-module_sessioninfo.R rename to tests/testthat/test-module_session_info.R diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd index f20a0be8b2..4875ace4d6 100644 --- a/vignettes/quick-usage-guide-for-teal.Rmd +++ b/vignettes/quick-usage-guide-for-teal.Rmd @@ -49,7 +49,6 @@ The following functions can be used to extend the `teal` application: * `modify_header` adds a header with a button that shows a notification when clicked, also note that the observer for this button was added using the modifier `teal_extend_server`. * `modify_footer` adds a custom footer. * `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note that the screenshot does not show the popup, but it will appear when the application is first loaded. -* `teal_extend_server` adds custom server logic to the teal application, in this case the button created in the header is observed and a notification is shown. ```{r, eval=FALSE} library(teal) @@ -71,12 +70,7 @@ app <- init( ) ) |> modify_footer(footer = "Custom footer") |> - add_landing_popup(title = "Te al", content = "Welcome to my first teal application!") |> - teal_extend_server(function(input, output, session) { - observeEvent(input$notify, { - showNotification("Yes, the button works!") - }) - }) + add_landing_popup(title = "Te al", content = "Welcome to my first teal application!") if (interactive()) { shinyApp(app$ui, app$server) From a6d20bc6c1892719691f9734b5ebfd144bca0fd4 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 14:10:52 +0530 Subject: [PATCH 38/50] docs: update the app screenshot as custom server is not exported --- .../teal-app-init-with-modifiers.drawio | 99 +++++++++---------- .../images/teal-app-init-with-modifiers.svg | 2 +- vignettes/quick-usage-guide-for-teal.Rmd | 9 +- 3 files changed, 52 insertions(+), 58 deletions(-) diff --git a/inst/design/teal-app-init-with-modifiers.drawio b/inst/design/teal-app-init-with-modifiers.drawio index 9045c5d4ed..9c1ee7c389 100644 --- a/inst/design/teal-app-init-with-modifiers.drawio +++ b/inst/design/teal-app-init-with-modifiers.drawio @@ -1,6 +1,6 @@ - + @@ -17,97 +17,97 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -119,19 +119,19 @@ - + - + - + - + - + @@ -152,46 +152,43 @@ - + - + - + - + - + - + - + - + - + - - - - + - + - + diff --git a/vignettes/images/teal-app-init-with-modifiers.svg b/vignettes/images/teal-app-init-with-modifiers.svg index b7ac58b8d7..c30056e52d 100644 --- a/vignettes/images/teal-app-init-with-modifiers.svg +++ b/vignettes/images/teal-app-init-with-modifiers.svg @@ -1 +1 @@ -
Custom title
Custom title
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Header with a button that can be observed!
Header with a button that can be observed!
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Custom footer
Custom footer
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Show notification
Show notification
   Yes, the button works!
   Yes, the button works!
x
x
Text is not SVG - cannot display
\ No newline at end of file +
Custom title
Custom title
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Custom header
Custom header
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Custom footer
Custom footer
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
   Yes, the button works!
   Yes, the button works!
x
x
Text is not SVG - cannot display
\ No newline at end of file diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd index 4875ace4d6..483b6a6c1e 100644 --- a/vignettes/quick-usage-guide-for-teal.Rmd +++ b/vignettes/quick-usage-guide-for-teal.Rmd @@ -48,7 +48,7 @@ The following functions can be used to extend the `teal` application: * `modify_title` changes the title that appears in the browser tab. * `modify_header` adds a header with a button that shows a notification when clicked, also note that the observer for this button was added using the modifier `teal_extend_server`. * `modify_footer` adds a custom footer. -* `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note that the screenshot does not show the popup, but it will appear when the application is first loaded. +* `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note app illustration below does not show the popup, but it will appear as soon as the application loads. ```{r, eval=FALSE} library(teal) @@ -64,13 +64,10 @@ app <- init( ) |> modify_title(title = "Custom title") |> modify_header( - header = tags$div( - h3("Header with a button that can be observed!"), - actionButton("notify", "Show notification") - ) + header = tags$div(h3("Custom header")) ) |> modify_footer(footer = "Custom footer") |> - add_landing_popup(title = "Te al", content = "Welcome to my first teal application!") + add_landing_popup(title = "Teal", content = "Welcome to my first teal application!") if (interactive()) { shinyApp(app$ui, app$server) From 548daea742844cf0600d6a19b19e3720663edd29 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 14:32:07 +0530 Subject: [PATCH 39/50] docs: remove exmple --- R/teal_modifiers.R | 33 --------------------------------- man/teal_extend_server.Rd | 34 ---------------------------------- 2 files changed, 67 deletions(-) diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R index 72cd48dc8a..c7e76e7155 100644 --- a/R/teal_modifiers.R +++ b/R/teal_modifiers.R @@ -176,39 +176,6 @@ add_landing_popup <- function( #' The custom server function or server module to set. #' @param module_id (`character(1)`) The ID of the module when a module server function is passed. #' @keywords internal -#' @examples -#' app <- init( -#' data = teal_data(IRIS = iris), -#' modules = modules(example_module()) -#' ) |> -#' teal_extend_server(function(input, output, session) { -#' print("injected server logic") -#' }) -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } -#' -#' ns <- NS("custom_ns") -#' app <- init( -#' data = teal_data(IRIS = iris), -#' modules = modules(example_module()) -#' ) |> -#' modify_header(actionButton(ns("button"), "Click me")) |> -#' teal_extend_server( -#' function(id) { -#' moduleServer(id, function(input, output, session) { -#' observeEvent(input$button, { -#' showNotification("Button is clicked!") -#' }) -#' }) -#' }, -#' module_id = "custom_ns" -#' ) -#' -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } teal_extend_server <- function(x, custom_server, module_id = character(0)) { checkmate::assert_class(x, "teal_app") checkmate::assert_function(custom_server) diff --git a/man/teal_extend_server.Rd b/man/teal_extend_server.Rd index c1ea08bc11..2caf8cd7a4 100644 --- a/man/teal_extend_server.Rd +++ b/man/teal_extend_server.Rd @@ -17,38 +17,4 @@ The custom server function or server module to set.} \description{ Adds a custom server function to the \code{teal} app. This function can define additional server logic. } -\examples{ -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - teal_extend_server(function(input, output, session) { - print("injected server logic") - }) - -if (interactive()) { - shinyApp(app$ui, app$server) -} - -ns <- NS("custom_ns") -app <- init( - data = teal_data(IRIS = iris), - modules = modules(example_module()) -) |> - modify_header(actionButton(ns("button"), "Click me")) |> - teal_extend_server( - function(id) { - moduleServer(id, function(input, output, session) { - observeEvent(input$button, { - showNotification("Button is clicked!") - }) - }) - }, - module_id = "custom_ns" - ) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -} \keyword{internal} From 46af6aaca56a7ecc0b286cec7e41e3c6b51b74d3 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 15:15:41 +0530 Subject: [PATCH 40/50] chore: fix module_teal_with_splash --- R/module_teal_with_splash.R | 12 ++++++++---- man/module_teal_with_splash.Rd | 15 +++++++++++++++ tests/testthat/test-shinytest2-filter_panel.R | 1 - 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/R/module_teal_with_splash.R b/R/module_teal_with_splash.R index c563cef790..a3c5aa40a4 100644 --- a/R/module_teal_with_splash.R +++ b/R/module_teal_with_splash.R @@ -5,6 +5,7 @@ #' #' @inheritParams ui_teal #' @inheritParams srv_teal +#' @inheritParams init #' #' @return #' Returns a `reactive` expression containing a `teal_data` object when data is loaded or `NULL` when it is not. @@ -16,6 +17,7 @@ NULL #' @rdname module_teal_with_splash ui_teal_with_splash <- function(id, data, + modules, title = build_app_title(), header = tags$p(), footer = tags$p()) { @@ -24,9 +26,10 @@ ui_teal_with_splash <- function(id, what = "ui_teal_with_splash()", details = "Deprecated, please use `ui_teal` instead" ) + ns <- shiny::NS(id) fluidPage( title = tags$div( - id = "teal-title", + id = ns("teal-app-title"), tags$head( tags$title("teal app"), tags$link( @@ -36,9 +39,10 @@ ui_teal_with_splash <- function(id, ) ) ), - tags$header(id = "teal-header-content"), - ui_teal(id = id), + tags$header(id = ns("teal-header-content")), + ui_teal(id = id, modules = modules), tags$footer( + id = "teal-footer", tags$div(id = "teal-footer-content"), ui_session_info(ns("session_info")) ) @@ -54,5 +58,5 @@ srv_teal_with_splash <- function(id, data, modules, filter = teal_slices()) { details = "Deprecated, please use `srv_teal` instead" ) srv_teal(id = id, data = data, modules = modules, filter = filter) - srv_session_info(NS(id, "session_info")) + srv_session_info("session_info") } diff --git a/man/module_teal_with_splash.Rd b/man/module_teal_with_splash.Rd index 66b964603e..26f4a5e6f2 100644 --- a/man/module_teal_with_splash.Rd +++ b/man/module_teal_with_splash.Rd @@ -9,6 +9,7 @@ ui_teal_with_splash( id, data, + modules, title = build_app_title(), header = tags$p(), footer = tags$p() @@ -27,6 +28,20 @@ The data which application will depend on.} will be displayed in the \code{teal} application. See \code{\link[=modules]{modules()}} and \code{\link[=module]{module()}} for more details.} +\item{title}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the browser window title. Defaults to a title "teal app" with the icon of NEST. +Can be created using the \code{build_app_title()} or +by passing a valid \code{shiny.tag} which is a head tag with title and link tag. +This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} + +\item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the header of the app. +This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} + +\item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, +the footer of the app. +This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} + \item{filter}{(\code{teal_slices}) Optionally, specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} } diff --git a/tests/testthat/test-shinytest2-filter_panel.R b/tests/testthat/test-shinytest2-filter_panel.R index da8f3e9ca5..22be061ab7 100644 --- a/tests/testthat/test-shinytest2-filter_panel.R +++ b/tests/testthat/test-shinytest2-filter_panel.R @@ -69,7 +69,6 @@ testthat::test_that("e2e: filtering a module-specific filter is reflected in oth testthat::test_that("e2e: filtering a module-specific filter is not refected in other unshared modules", { skip_if_too_deep(5) - devtools::load_all() app <- TealAppDriver$new( data = simple_teal_data(), modules = modules( From a4422181dcb18bcdb88efa642a34f0d33ec0f0a4 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 15:40:57 +0530 Subject: [PATCH 41/50] chore: rearrange docs --- R/module_session_info.R | 2 +- R/teal_modifiers.R | 9 ++------- _pkgdown.yml | 1 - man/module_session_info.Rd | 2 +- man/teal_extend_server.Rd | 2 +- man/teal_modifiers.Rd | 24 +++++------------------- 6 files changed, 10 insertions(+), 30 deletions(-) diff --git a/R/module_session_info.R b/R/module_session_info.R index e0c237a9fc..cf391bebce 100644 --- a/R/module_session_info.R +++ b/R/module_session_info.R @@ -1,6 +1,6 @@ #' `teal` user session info module #' -#' Module to display the user session info popup and teal lockfile download. +#' Module to display the user session info popup and teal lockfile downloader. #' #' @rdname module_session_info #' @name module_session_info diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R index c7e76e7155..f07fe06ced 100644 --- a/R/teal_modifiers.R +++ b/R/teal_modifiers.R @@ -16,6 +16,7 @@ NULL #' @rdname teal_modifiers #' @keywords internal +#' @noRd #' @param x One of: #' - A `teal_app` object created using the `init` function. #' - A `teal_module`, `teal_data_module`, or `teal_transform_module` object. @@ -44,8 +45,6 @@ teal_replace_ui <- function(x, selector, element) { } } -#' Add a custom Title to `teal` application -#' #' @rdname teal_modifiers #' @export #' @examples @@ -79,8 +78,6 @@ modify_title <- function( ) } -#' Add a Header to `teal` Application -#' #' @rdname teal_modifiers #' @export #' @examples @@ -98,8 +95,6 @@ modify_header <- function(x, element = tags$p()) { teal_replace_ui(x, "#teal-header-content", element) } -#' Add a Footer to `teal` Application -#' #' @rdname teal_modifiers #' @export #' @examples @@ -172,7 +167,7 @@ add_landing_popup <- function( #' @description Adds a custom server function to the `teal` app. This function can define additional server logic. #' #' @param x (`teal_app`) A `teal_app` object created using the `init` function. -#' @param custom_server (`function(input, output, session)` or `function(input, output, session)`) +#' @param custom_server (`function(input, output, session)` or `function(id, ...)`) #' The custom server function or server module to set. #' @param module_id (`character(1)`) The ID of the module when a module server function is passed. #' @keywords internal diff --git a/_pkgdown.yml b/_pkgdown.yml index 72a7f518a1..383b18790a 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -116,7 +116,6 @@ reference: - modify_footer - modify_header - modify_title - - teal_extend_server - add_landing_popup - title: Helper Functions desc: Helper functions for `teal` diff --git a/man/module_session_info.Rd b/man/module_session_info.Rd index b7ff9ec3f7..ea1c479ecd 100644 --- a/man/module_session_info.Rd +++ b/man/module_session_info.Rd @@ -17,7 +17,7 @@ srv_session_info(id) \code{NULL} invisibly } \description{ -Module to display the user session info popup and teal lockfile download. +Module to display the user session info popup and teal lockfile downloader. } \examples{ ui <- fluidPage( diff --git a/man/teal_extend_server.Rd b/man/teal_extend_server.Rd index 2caf8cd7a4..365b7d27e0 100644 --- a/man/teal_extend_server.Rd +++ b/man/teal_extend_server.Rd @@ -9,7 +9,7 @@ teal_extend_server(x, custom_server, module_id = character(0)) \arguments{ \item{x}{(\code{teal_app}) A \code{teal_app} object created using the \code{init} function.} -\item{custom_server}{(\verb{function(input, output, session)} or \verb{function(input, output, session)}) +\item{custom_server}{(\verb{function(input, output, session)} or \verb{function(id, ...)}) The custom server function or server module to set.} \item{module_id}{(\code{character(1)}) The ID of the module when a module server function is passed.} diff --git a/man/teal_modifiers.Rd b/man/teal_modifiers.Rd index 0d3d95a316..a7f61db451 100644 --- a/man/teal_modifiers.Rd +++ b/man/teal_modifiers.Rd @@ -2,14 +2,11 @@ % Please edit documentation in R/teal_modifiers.R \name{teal_modifiers} \alias{teal_modifiers} -\alias{teal_replace_ui} \alias{modify_title} \alias{modify_header} \alias{modify_footer} \title{Replace UI Elements in \code{teal} UI objects} \usage{ -teal_replace_ui(x, selector, element) - modify_title( x, title = "teal app", @@ -22,30 +19,19 @@ modify_header(x, element = tags$p()) modify_footer(x, element = tags$p()) } \arguments{ -\item{x}{One of: -\itemize{ -\item A \code{teal_app} object created using the \code{init} function. -\item A \code{teal_module}, \code{teal_data_module}, or \code{teal_transform_module} object. -\item A Shiny module UI function with \code{id} parameter -}} - -\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} - -\item{element}{Replacement UI element (shiny tag or HTML)} +\item{x}{(\code{teal_app}) A \code{teal_app} object created using the \code{init} function.} \item{title}{(\code{shiny.tag} or \code{character(1)}) The new title to be used.} \item{favicon}{(\code{character}) The path for the icon for the title. The image/icon path can be remote or the static path accessible by \code{shiny}, like the \verb{www/}} + +\item{element}{Replacement UI element (shiny tag or HTML)} + +\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} } \description{ Replace UI Elements in \code{teal} UI objects - -Add a custom Title to \code{teal} application - -Add a Header to \code{teal} Application - -Add a Footer to \code{teal} Application } \examples{ app <- init( From f4218475f2211106ebff2bc0293a4cd0a040172b Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 15:50:27 +0530 Subject: [PATCH 42/50] chore: update news and pass spellcheck --- NEWS.md | 1 + R/module_session_info.R | 2 +- man/module_session_info.Rd | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index e0d1d9d81d..b44b9d9575 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ * Possible to call `ui_teal` and `srv_teal` directly in any application by delivering `data` argument as a `reactive` returning `teal_data` object. #669 * Since introduction of `ui_teal` and `srv_teal` functions `id` argument in `init` is being deprecated. #1438 +* Introduce `ui_session_info` and `srv_session_info` shiny module to create the user session info and teal app lockfile lockfile download button. * 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 diff --git a/R/module_session_info.R b/R/module_session_info.R index cf391bebce..a1c1f94ec9 100644 --- a/R/module_session_info.R +++ b/R/module_session_info.R @@ -1,6 +1,6 @@ #' `teal` user session info module #' -#' Module to display the user session info popup and teal lockfile downloader. +#' Module to display the user session info popup and teal lockfile download button #' #' @rdname module_session_info #' @name module_session_info diff --git a/man/module_session_info.Rd b/man/module_session_info.Rd index ea1c479ecd..80dfe67326 100644 --- a/man/module_session_info.Rd +++ b/man/module_session_info.Rd @@ -17,7 +17,7 @@ srv_session_info(id) \code{NULL} invisibly } \description{ -Module to display the user session info popup and teal lockfile downloader. +Module to display the user session info popup and teal lockfile download button } \examples{ ui <- fluidPage( From d49392b12c6d4437fd20363dc1d061b7044b8f4c Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 17:14:31 +0530 Subject: [PATCH 43/50] @gogonzo comments --- NAMESPACE | 2 +- NEWS.md | 2 +- R/TealAppDriver.R | 6 +++--- R/init.R | 6 +++--- R/module_session_info.R | 2 +- R/module_teal.R | 7 +++++-- R/module_teal_with_splash.R | 4 ++-- R/teal_modifiers.R | 21 ++++++++++++------- _pkgdown.yml | 2 +- ..._landing_popup.Rd => add_landing_modal.Rd} | 17 ++++++++------- man/module_session_info.Rd | 2 +- man/module_teal.Rd | 7 +++++-- man/teal_modifiers.Rd | 2 -- .../testthat/test-shinytest2-landing_popup.R | 4 ++-- vignettes/quick-usage-guide-for-teal.Rmd | 4 ++-- 15 files changed, 50 insertions(+), 38 deletions(-) rename man/{add_landing_popup.Rd => add_landing_modal.Rd} (70%) diff --git a/NAMESPACE b/NAMESPACE index ee7220014f..ac53893f97 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,7 +14,7 @@ S3method(ui_teal_module,teal_module) S3method(ui_teal_module,teal_modules) S3method(within,teal_data_module) export(TealReportCard) -export(add_landing_popup) +export(add_landing_modal) export(as.teal_slices) export(as_tdata) export(build_app_title) diff --git a/NEWS.md b/NEWS.md index b44b9d9575..9eaf0d0240 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,7 +16,7 @@ * Setting `datanames()` on `data` passed to teal application no longer has effect. In order to change `teal_module`'s `datanames` one should modify `module$datanames`. -* `landing_popup_module()` is deprecated. Please use `add_landing_popup()` function to add a landing popup for your teal application. +* `landing_popup_module()` is deprecated. Please use `add_landing_modal()` function to add a landing popup for your teal application. * `teal` no longer re-export `%>%`. Please load `library(magrittr)` instead or use `|>` from `base`. * `build_app_title` will be removed in the future release. Please use the `modify_title()` function to change the title for your teal application. * The `title`, `header`, and `footer` arguments of the `init()` function are deprecated. Please use the `modify_title`, `modify_header`, and `modify_footer` respectively. diff --git a/R/TealAppDriver.R b/R/TealAppDriver.R index 48d8bd0ab6..af78b87595 100644 --- a/R/TealAppDriver.R +++ b/R/TealAppDriver.R @@ -77,18 +77,18 @@ TealAppDriver <- R6::R6Class( # nolint: object_name. default_args <- list( title = NULL, content = NULL, - buttons = modalButton("Accept") + footer = modalButton("Accept") ) landing_popup_args[names(default_args)] <- Map( function(x, y) if (is.null(y)) x else y, default_args, landing_popup_args[names(default_args)] ) - app <- add_landing_popup( + app <- add_landing_modal( app, title = landing_popup_args$title, content = landing_popup_args$content, - buttons = landing_popup_args$button + footer = landing_popup_args$footer ) } diff --git a/R/init.R b/R/init.R index 0ad0b5670e..f1319828ff 100644 --- a/R/init.R +++ b/R/init.R @@ -237,13 +237,13 @@ init <- function(data, tags$footer( id = "teal-footer", tags$div(id = "teal-footer-content"), - ui_session_info("session_info") + ui_session_info("teal-footer-session_info") ) ) }, server = function(input, output, session) { srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) - srv_session_info("session_info") + srv_session_info("teal-footer-session_info") } ), class = "teal_app" @@ -290,7 +290,7 @@ init <- function(data, what = "landing_popup_module()", details = paste( "landing_popup_module() is deprecated.", - "Use add_landing_popup() on the teal app object instead." + "Use add_landing_modal() on the teal app object instead." ) ) } else if (length(landing) > 1L) { diff --git a/R/module_session_info.R b/R/module_session_info.R index a1c1f94ec9..682283adae 100644 --- a/R/module_session_info.R +++ b/R/module_session_info.R @@ -1,6 +1,6 @@ #' `teal` user session info module #' -#' Module to display the user session info popup and teal lockfile download button +#' Module to display the user session info popup and to download a lockfile. #' #' @rdname module_session_info #' @name module_session_info diff --git a/R/module_teal.R b/R/module_teal.R index f05ed244bc..511f63e923 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -2,9 +2,12 @@ #' #' @description #' `r lifecycle::badge("stable")` -#' Module to create a `teal` app. This module can be called directly instead of [init()] and -#' included in your custom application. Please note that [init()] adds `reporter_previewer_module` +#' Module to create a `teal` app as a Shiny Module. +#' This module can be used instead of [init()] and include in your custom Shiny application. +#' Please note that [init()] adds `reporter_previewer_module` and `module_session_info` #' automatically, which is not a case when calling `ui/srv_teal` directly. +#' Also note that the `header`, `footer`and `title` components added by `init` are not part of this module +#' as they can be easily added by in the shiny app which consumes this module. #' #' @details #' diff --git a/R/module_teal_with_splash.R b/R/module_teal_with_splash.R index a3c5aa40a4..7794f7ccb4 100644 --- a/R/module_teal_with_splash.R +++ b/R/module_teal_with_splash.R @@ -44,7 +44,7 @@ ui_teal_with_splash <- function(id, tags$footer( id = "teal-footer", tags$div(id = "teal-footer-content"), - ui_session_info(ns("session_info")) + ui_session_info(ns("teal-footer-session_info")) ) ) } @@ -58,5 +58,5 @@ srv_teal_with_splash <- function(id, data, modules, filter = teal_slices()) { details = "Deprecated, please use `srv_teal` instead" ) srv_teal(id = id, data = data, modules = modules, filter = filter) - srv_session_info("session_info") + srv_session_info("teal-footer-session_info") } diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R index f07fe06ced..2b9cb0673c 100644 --- a/R/teal_modifiers.R +++ b/R/teal_modifiers.R @@ -1,7 +1,6 @@ #' Replace UI Elements in `teal` UI objects #' #' @param x (`teal_app`) A `teal_app` object created using the `init` function. -#' @param selector (`character(1)`) CSS selector to find elements to replace #' @param element Replacement UI element (shiny tag or HTML) #' @param title (`shiny.tag` or `character(1)`) The new title to be used. #' @param favicon (`character`) The path for the icon for the title. @@ -21,6 +20,7 @@ NULL #' - A `teal_app` object created using the `init` function. #' - A `teal_module`, `teal_data_module`, or `teal_transform_module` object. #' - A Shiny module UI function with `id` parameter +#' @param selector (`character(1)`) CSS selector to find elements to replace teal_replace_ui <- function(x, selector, element) { if (inherits(x, c("teal_app", "teal_module", "teal_data_module", "teal_transform_module"))) { x$ui <- teal_replace_ui(x$ui, selector, element) @@ -91,6 +91,7 @@ modify_title <- function( #' shinyApp(app$ui, app$server) #' } modify_header <- function(x, element = tags$p()) { + checkmate::assert_multi_class(x, "teal_app") checkmate::assert_multi_class(element, c("shiny.tag", "shiny.tag.list", "html", "character")) teal_replace_ui(x, "#teal-header-content", element) } @@ -108,6 +109,7 @@ modify_header <- function(x, element = tags$p()) { #' shinyApp(app$ui, app$server) #' } modify_footer <- function(x, element = tags$p()) { + checkmate::assert_multi_class(x, "teal_app") checkmate::assert_multi_class(element, c("shiny.tag", "shiny.tag.list", "html", "character")) teal_replace_ui(x, "#teal-footer-content", element) } @@ -115,20 +117,21 @@ modify_footer <- function(x, element = tags$p()) { #' Add a Landing Popup to `teal` Application #' #' @description Adds a landing popup to the `teal` app. This popup will be shown when the app starts. -#' The dialog blocks access to the application and must be closed with a button before the application can be viewed. +#' The dialog must be closed by the app user to proceed to the main application. #' #' @param x (`teal_app`) A `teal_app` object created using the `init` function. #' @param title (`character(1)`) Text to be displayed as popup title. #' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. #' Passed to `...` of `shiny::modalDialog`. -#' @param buttons (`shiny.tag` or `shiny.tag.list`) Typically a `modalButton` or `actionButton`. +#' @param footer (`shiny.tag` or `shiny.tag.list`) Typically a `modalButton` or `actionButton`. +#' @param ... Additional arguments to `shiny::modalDialog`. #' @export #' @examples #' app <- init( #' data = teal_data(IRIS = iris, MTCARS = mtcars), #' modules = modules(example_module()) #' ) |> -#' add_landing_popup( +#' add_landing_modal( #' title = "Welcome", #' content = "This is a landing popup.", #' buttons = modalButton("Accept") @@ -137,11 +140,12 @@ modify_footer <- function(x, element = tags$p()) { #' if (interactive()) { #' shinyApp(app$ui, app$server) #' } -add_landing_popup <- function( +add_landing_modal <- function( x, title = NULL, content = NULL, - buttons = modalButton("Accept")) { + footer = modalButton("Accept"), + ...) { checkmate::assert_class(x, "teal_app") custom_server <- function(input, output, session) { checkmate::assert_string(title, null.ok = TRUE) @@ -149,13 +153,14 @@ add_landing_popup <- function( content, classes = c("character", "shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE ) - checkmate::assert_multi_class(buttons, classes = c("shiny.tag", "shiny.tag.list")) + checkmate::assert_multi_class(footer, classes = c("shiny.tag", "shiny.tag.list")) showModal( modalDialog( id = "landingpopup", title = title, content, - footer = buttons + footer = footer, + ... ) ) } diff --git a/_pkgdown.yml b/_pkgdown.yml index 383b18790a..a054d4d4d0 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -116,7 +116,7 @@ reference: - modify_footer - modify_header - modify_title - - add_landing_popup + - add_landing_modal - title: Helper Functions desc: Helper functions for `teal` contents: diff --git a/man/add_landing_popup.Rd b/man/add_landing_modal.Rd similarity index 70% rename from man/add_landing_popup.Rd rename to man/add_landing_modal.Rd index cd02af76f2..9170e479d7 100644 --- a/man/add_landing_popup.Rd +++ b/man/add_landing_modal.Rd @@ -1,14 +1,15 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/teal_modifiers.R -\name{add_landing_popup} -\alias{add_landing_popup} +\name{add_landing_modal} +\alias{add_landing_modal} \title{Add a Landing Popup to \code{teal} Application} \usage{ -add_landing_popup( +add_landing_modal( x, title = NULL, content = NULL, - buttons = modalButton("Accept") + footer = modalButton("Accept"), + ... ) } \arguments{ @@ -19,18 +20,20 @@ add_landing_popup( \item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. Passed to \code{...} of \code{shiny::modalDialog}.} -\item{buttons}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}.} +\item{footer}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}.} + +\item{...}{Additional arguments to \code{shiny::modalDialog}.} } \description{ Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. -The dialog blocks access to the application and must be closed with a button before the application can be viewed. +The dialog must be closed by the app user to proceed to the main application. } \examples{ app <- init( data = teal_data(IRIS = iris, MTCARS = mtcars), modules = modules(example_module()) ) |> - add_landing_popup( + add_landing_modal( title = "Welcome", content = "This is a landing popup.", buttons = modalButton("Accept") diff --git a/man/module_session_info.Rd b/man/module_session_info.Rd index 80dfe67326..1c15bd28b7 100644 --- a/man/module_session_info.Rd +++ b/man/module_session_info.Rd @@ -17,7 +17,7 @@ srv_session_info(id) \code{NULL} invisibly } \description{ -Module to display the user session info popup and teal lockfile download button +Module to display the user session info popup and to download a lockfile. } \examples{ ui <- fluidPage( diff --git a/man/module_teal.Rd b/man/module_teal.Rd index a5231944d0..8550fcc502 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -29,9 +29,12 @@ specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#stable}{\figure{lifecycle-stable.svg}{options: alt='[Stable]'}}}{\strong{[Stable]}} -Module to create a \code{teal} app. This module can be called directly instead of \code{\link[=init]{init()}} and -included in your custom application. Please note that \code{\link[=init]{init()}} adds \code{reporter_previewer_module} +Module to create a \code{teal} app as a Shiny Module. +This module can be used instead of \code{\link[=init]{init()}} and include in your custom Shiny application. +Please note that \code{\link[=init]{init()}} adds \code{reporter_previewer_module} and \code{module_session_info} automatically, which is not a case when calling \code{ui/srv_teal} directly. +Also note that the \code{header}, \code{footer}and \code{title} components added by \code{init} are not part of this module +as they can be easily added by in the shiny app which consumes this module. } \details{ Module is responsible for creating the main \code{shiny} app layout and initializing all the necessary diff --git a/man/teal_modifiers.Rd b/man/teal_modifiers.Rd index a7f61db451..7bafef0b4c 100644 --- a/man/teal_modifiers.Rd +++ b/man/teal_modifiers.Rd @@ -27,8 +27,6 @@ modify_footer(x, element = tags$p()) The image/icon path can be remote or the static path accessible by \code{shiny}, like the \verb{www/}} \item{element}{Replacement UI element (shiny tag or HTML)} - -\item{selector}{(\code{character(1)}) CSS selector to find elements to replace} } \description{ Replace UI Elements in \code{teal} UI objects diff --git a/tests/testthat/test-shinytest2-landing_popup.R b/tests/testthat/test-shinytest2-landing_popup.R index 187f850252..96c7597185 100644 --- a/tests/testthat/test-shinytest2-landing_popup.R +++ b/tests/testthat/test-shinytest2-landing_popup.R @@ -74,7 +74,7 @@ testthat::test_that( landing_popup_args = list( title = "Custom Landing Popup Module Title", content = tags$b("A welcome message!", style = "color: red;"), - buttons = tagList( + footer = tagList( shiny::modalButton("Proceed"), shiny::actionButton( "read", @@ -137,7 +137,7 @@ testthat::test_that("e2e: when customized button in landing_popup_module is clic example_module() ), landing_popup_args = list( - buttons = actionButton("read", "Read more", onclick = "window.open('http://google.com', '_blank')") + footer = actionButton("read", "Read more", onclick = "window.open('http://google.com', '_blank')") ) ) diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd index 483b6a6c1e..f6ac592a9a 100644 --- a/vignettes/quick-usage-guide-for-teal.Rmd +++ b/vignettes/quick-usage-guide-for-teal.Rmd @@ -48,7 +48,7 @@ The following functions can be used to extend the `teal` application: * `modify_title` changes the title that appears in the browser tab. * `modify_header` adds a header with a button that shows a notification when clicked, also note that the observer for this button was added using the modifier `teal_extend_server`. * `modify_footer` adds a custom footer. -* `add_landing_popup` adds a landing popup that appears when the application is first loaded. Note app illustration below does not show the popup, but it will appear as soon as the application loads. +* `add_landing_modal` adds a landing popup that appears when the application is first loaded. Note app illustration below does not show the popup, but it will appear as soon as the application loads. ```{r, eval=FALSE} library(teal) @@ -67,7 +67,7 @@ app <- init( header = tags$div(h3("Custom header")) ) |> modify_footer(footer = "Custom footer") |> - add_landing_popup(title = "Teal", content = "Welcome to my first teal application!") + add_landing_modal(title = "Teal", content = "Welcome to my first teal application!") if (interactive()) { shinyApp(app$ui, app$server) From e339218f5ee70203a5ed45c76b37327aa22feaf7 Mon Sep 17 00:00:00 2001 From: Vedha Viyash <49812166+vedhav@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:44:45 +0530 Subject: [PATCH 44/50] Update R/teal_modifiers.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dawid Kałędkowski --- R/teal_modifiers.R | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R index 2b9cb0673c..72933d0ab2 100644 --- a/R/teal_modifiers.R +++ b/R/teal_modifiers.R @@ -120,11 +120,8 @@ modify_footer <- function(x, element = tags$p()) { #' The dialog must be closed by the app user to proceed to the main application. #' #' @param x (`teal_app`) A `teal_app` object created using the `init` function. -#' @param title (`character(1)`) Text to be displayed as popup title. -#' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. -#' Passed to `...` of `shiny::modalDialog`. -#' @param footer (`shiny.tag` or `shiny.tag.list`) Typically a `modalButton` or `actionButton`. -#' @param ... Additional arguments to `shiny::modalDialog`. +#' @inheritParams shiny::modalDialog +#' @param ... Additional arguments to [shiny::modalDialog()]. #' @export #' @examples #' app <- init( From ed4bb5706bce38cc2f64c123600ae522c1e68e65 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 17:48:07 +0530 Subject: [PATCH 45/50] chore: fix ns in test --- tests/testthat/test-shinytest2-init.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-shinytest2-init.R b/tests/testthat/test-shinytest2-init.R index bc585e4617..9e0d6d4361 100644 --- a/tests/testthat/test-shinytest2-init.R +++ b/tests/testthat/test-shinytest2-init.R @@ -19,7 +19,7 @@ testthat::test_that("e2e: teal app initializes with sessionInfo modal", { ) # Check if button exists. - button_selector <- "#session_info-sessionInfo-button" + button_selector <- "#teal-footer-session_info-sessionInfo-button" testthat::expect_equal( app$get_text(button_selector), "Session Info" @@ -37,7 +37,7 @@ testthat::test_that("e2e: teal app initializes with sessionInfo modal", { testthat::expect_setequal( testthat::expect_length( app$get_text( - "#shiny-modal [id^='session_info-sessionInfo-copy_button']" + "#shiny-modal [id^='teal-footer-session_info-sessionInfo-copy_button']" ), 2 ), @@ -53,7 +53,7 @@ testthat::test_that("e2e: teal app initializes with sessionInfo modal", { ) # Check session info output. - session_info <- app$get_text("#session_info-sessionInfo-verbatim_content") + session_info <- app$get_text("#teal-footer-session_info-sessionInfo-verbatim_content") testthat::expect_match(session_info, "R version", fixed = TRUE) testthat::expect_match(session_info, "attached base packages:", fixed = TRUE) From 842c1899d43a5d3a945476742b3d0c7646503cb5 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 17:51:14 +0530 Subject: [PATCH 46/50] chore: minor doc change --- R/module_teal.R | 9 +++++---- man/add_landing_modal.Rd | 9 +++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index 511f63e923..b9f39b512f 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -39,6 +39,10 @@ #' @param id (`character(1)`) `shiny` module instance id. #' @param data (`teal_data`, `teal_data_module`, or `reactive` returning `teal_data`) #' The data which application will depend on. +#' @param modules (`teal_modules`) +#' `teal_modules` object. These are the specific output modules which +#' will be displayed in the `teal` application. See [modules()] and [module()] for +#' more details. #' #' @return `NULL` invisibly NULL @@ -47,6 +51,7 @@ NULL #' @export ui_teal <- function(id, modules) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) + checkmate::assert_class(modules, "teal_modules") ns <- NS(id) # show busy icon when `shiny` session is busy computing stuff @@ -104,10 +109,6 @@ ui_teal <- function(id, modules) { } #' @rdname module_teal -#' @param modules (`teal_modules`) -#' `teal_modules` object. These are the specific output modules which -#' will be displayed in the `teal` application. See [modules()] and [module()] for -#' more details. #' @export srv_teal <- function(id, data, modules, filter = teal_slices()) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) diff --git a/man/add_landing_modal.Rd b/man/add_landing_modal.Rd index 9170e479d7..2560f6564c 100644 --- a/man/add_landing_modal.Rd +++ b/man/add_landing_modal.Rd @@ -15,14 +15,11 @@ add_landing_modal( \arguments{ \item{x}{(\code{teal_app}) A \code{teal_app} object created using the \code{init} function.} -\item{title}{(\code{character(1)}) Text to be displayed as popup title.} +\item{title}{An optional title for the dialog.} -\item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup. -Passed to \code{...} of \code{shiny::modalDialog}.} +\item{footer}{UI for footer. Use \code{NULL} for no footer.} -\item{footer}{(\code{shiny.tag} or \code{shiny.tag.list}) Typically a \code{modalButton} or \code{actionButton}.} - -\item{...}{Additional arguments to \code{shiny::modalDialog}.} +\item{...}{Additional arguments to \code{\link[shiny:modalDialog]{shiny::modalDialog()}}.} } \description{ Adds a landing popup to the \code{teal} app. This popup will be shown when the app starts. From 0e86236320ea6d496a97d2f7ff81cd6f21a2ca2d Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 18:08:03 +0530 Subject: [PATCH 47/50] chore: update module_teal docs --- R/module_teal.R | 8 +++----- man/module_teal.Rd | 9 ++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index b9f39b512f..d78e7eb94c 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -3,13 +3,11 @@ #' @description #' `r lifecycle::badge("stable")` #' Module to create a `teal` app as a Shiny Module. -#' This module can be used instead of [init()] and include in your custom Shiny application. -#' Please note that [init()] adds `reporter_previewer_module` and `module_session_info` -#' automatically, which is not a case when calling `ui/srv_teal` directly. -#' Also note that the `header`, `footer`and `title` components added by `init` are not part of this module -#' as they can be easily added by in the shiny app which consumes this module. #' #' @details +#' This module can be used instead of [init()] in custom Shiny applications. Unlike [init()], it doesn't +#' automatically include `reporter_previewer_module`, `module_session_info`, or UI components like +#' `header`, `footer`, and `title` which can be added separately in the Shiny app consuming this module. #' #' Module is responsible for creating the main `shiny` app layout and initializing all the necessary #' components. This module establishes reactive connection between the input `data` and every other diff --git a/man/module_teal.Rd b/man/module_teal.Rd index 8550fcc502..c6ba7b206f 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -30,13 +30,12 @@ specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#stable}{\figure{lifecycle-stable.svg}{options: alt='[Stable]'}}}{\strong{[Stable]}} Module to create a \code{teal} app as a Shiny Module. -This module can be used instead of \code{\link[=init]{init()}} and include in your custom Shiny application. -Please note that \code{\link[=init]{init()}} adds \code{reporter_previewer_module} and \code{module_session_info} -automatically, which is not a case when calling \code{ui/srv_teal} directly. -Also note that the \code{header}, \code{footer}and \code{title} components added by \code{init} are not part of this module -as they can be easily added by in the shiny app which consumes this module. } \details{ +This module can be used instead of \code{\link[=init]{init()}} in custom Shiny applications. Unlike \code{\link[=init]{init()}}, it doesn't +automatically include \code{reporter_previewer_module}, \code{module_session_info}, or UI components like +\code{header}, \code{footer}, and \code{title} which can be added separately in the Shiny app consuming this module. + Module is responsible for creating the main \code{shiny} app layout and initializing all the necessary components. This module establishes reactive connection between the input \code{data} and every other component in the app. Reactive change of the \code{data} passed as an argument, reloads the app and From d50f5792c988411acf967df16134318d52f0878f Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 18:10:26 +0530 Subject: [PATCH 48/50] chore: update doc --- R/teal_modifiers.R | 1 + man/add_landing_modal.Rd | 2 ++ 2 files changed, 3 insertions(+) diff --git a/R/teal_modifiers.R b/R/teal_modifiers.R index 72933d0ab2..3f0fb90f19 100644 --- a/R/teal_modifiers.R +++ b/R/teal_modifiers.R @@ -121,6 +121,7 @@ modify_footer <- function(x, element = tags$p()) { #' #' @param x (`teal_app`) A `teal_app` object created using the `init` function. #' @inheritParams shiny::modalDialog +#' @param content (`character(1)`, `shiny.tag` or `shiny.tag.list`) with the content of the popup. #' @param ... Additional arguments to [shiny::modalDialog()]. #' @export #' @examples diff --git a/man/add_landing_modal.Rd b/man/add_landing_modal.Rd index 2560f6564c..be8d6339c6 100644 --- a/man/add_landing_modal.Rd +++ b/man/add_landing_modal.Rd @@ -17,6 +17,8 @@ add_landing_modal( \item{title}{An optional title for the dialog.} +\item{content}{(\code{character(1)}, \code{shiny.tag} or \code{shiny.tag.list}) with the content of the popup.} + \item{footer}{UI for footer. Use \code{NULL} for no footer.} \item{...}{Additional arguments to \code{\link[shiny:modalDialog]{shiny::modalDialog()}}.} From 489a28c40e87f992a8d2f535994a87d84a75d2e5 Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 18:34:47 +0530 Subject: [PATCH 49/50] chore: doc fixes --- R/init.R | 5 +--- R/landing_popup_module.R | 51 ++++++++----------------------------- man/init.Rd | 5 +--- man/landing_popup_module.Rd | 44 ++------------------------------ 4 files changed, 14 insertions(+), 91 deletions(-) diff --git a/R/init.R b/R/init.R index f1319828ff..417d0e5c4d 100644 --- a/R/init.R +++ b/R/init.R @@ -87,10 +87,7 @@ #' `Iris Sepal.Length histogram` = "new_iris Species", #' global_filters = "new_mtcars cyl" #' ) -#' ), -#' title = "App title", -#' header = tags$h1("Sample App"), -#' footer = tags$p("Sample footer") +#' ) #' ) #' if (interactive()) { #' shinyApp(app$ui, app$server) diff --git a/R/landing_popup_module.R b/R/landing_popup_module.R index 23b4c6606c..204c0e18e4 100644 --- a/R/landing_popup_module.R +++ b/R/landing_popup_module.R @@ -1,9 +1,10 @@ #' Landing popup module #' -#' @description Creates a landing welcome popup for `teal` applications. +#' @description `r lifecycle::badge("deprecated")` Creates a landing welcome popup for `teal` applications. #' #' This module is used to display a popup dialog when the application starts. #' The dialog blocks access to the application and must be closed with a button before the application can be viewed. +#' This function is deprecated, please use `add_landing_modal()` on the teal app object instead. #' #' @param label (`character(1)`) Label of the module. #' @param title (`character(1)`) Text to be displayed as popup title. @@ -13,51 +14,19 @@ #' #' @return A `teal_module` (extended with `teal_landing_module` class) to be used in `teal` applications. #' -#' @examples -#' app1 <- init( -#' data = teal_data(iris = iris), -#' modules = modules( -#' example_module(), -#' landing_popup_module( -#' content = "A place for the welcome message or a disclaimer statement.", -#' buttons = modalButton("Proceed") -#' ) -#' ) -#' ) -#' if (interactive()) { -#' shinyApp(app1$ui, app1$server) -#' } -#' -#' app2 <- init( -#' data = teal_data(iris = iris), -#' modules = modules( -#' example_module(), -#' landing_popup_module( -#' title = "Welcome", -#' content = tags$b( -#' "A place for the welcome message or a disclaimer statement.", -#' style = "color: red;" -#' ), -#' buttons = tagList( -#' modalButton("Proceed"), -#' actionButton("read", "Read more", -#' onclick = "window.open('http://google.com', '_blank')" -#' ), -#' actionButton("close", "Reject", onclick = "window.close()") -#' ) -#' ) -#' ) -#' ) -#' -#' if (interactive()) { -#' shinyApp(app2$ui, app2$server) -#' } -#' #' @export landing_popup_module <- function(label = "Landing Popup", title = NULL, content = NULL, buttons = modalButton("Accept")) { + lifecycle::deprecate_soft( + when = "0.15.3", + what = "landing_popup_module()", + details = paste( + "landing_popup_module() is deprecated.", + "Use add_landing_modal() on the teal app object instead." + ) + ) checkmate::assert_string(label) checkmate::assert_string(title, null.ok = TRUE) checkmate::assert_multi_class( diff --git a/man/init.Rd b/man/init.Rd index d646c16d85..f142e43b46 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -97,10 +97,7 @@ app <- init( `Iris Sepal.Length histogram` = "new_iris Species", global_filters = "new_mtcars cyl" ) - ), - title = "App title", - header = tags$h1("Sample App"), - footer = tags$p("Sample footer") + ) ) if (interactive()) { shinyApp(app$ui, app$server) diff --git a/man/landing_popup_module.Rd b/man/landing_popup_module.Rd index 43e15dd769..304c2cedb3 100644 --- a/man/landing_popup_module.Rd +++ b/man/landing_popup_module.Rd @@ -25,49 +25,9 @@ Passed to \code{...} of \code{shiny::modalDialog}. See examples.} A \code{teal_module} (extended with \code{teal_landing_module} class) to be used in \code{teal} applications. } \description{ -Creates a landing welcome popup for \code{teal} applications. +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Creates a landing welcome popup for \code{teal} applications. This module is used to display a popup dialog when the application starts. The dialog blocks access to the application and must be closed with a button before the application can be viewed. -} -\examples{ -app1 <- init( - data = teal_data(iris = iris), - modules = modules( - example_module(), - landing_popup_module( - content = "A place for the welcome message or a disclaimer statement.", - buttons = modalButton("Proceed") - ) - ) -) -if (interactive()) { - shinyApp(app1$ui, app1$server) -} - -app2 <- init( - data = teal_data(iris = iris), - modules = modules( - example_module(), - landing_popup_module( - title = "Welcome", - content = tags$b( - "A place for the welcome message or a disclaimer statement.", - style = "color: red;" - ), - buttons = tagList( - modalButton("Proceed"), - actionButton("read", "Read more", - onclick = "window.open('http://google.com', '_blank')" - ), - actionButton("close", "Reject", onclick = "window.close()") - ) - ) - ) -) - -if (interactive()) { - shinyApp(app2$ui, app2$server) -} - +This function is deprecated, please use \code{add_landing_modal()} on the teal app object instead. } From 62e836f896cda513669d5c4fd84630094d8a86cc Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 17 Jan 2025 18:58:07 +0530 Subject: [PATCH 50/50] docs: remove quick start guide vignette --- _pkgdown.yml | 1 - .../teal-app-init-with-modifiers.drawio | 196 ------------------ inst/design/teal-app-init.drawio | 184 ---------------- .../images/teal-app-init-with-modifiers.svg | 1 - vignettes/images/teal-app-init.svg | 1 - vignettes/quick-usage-guide-for-teal.Rmd | 80 ------- 6 files changed, 463 deletions(-) delete mode 100644 inst/design/teal-app-init-with-modifiers.drawio delete mode 100644 inst/design/teal-app-init.drawio delete mode 100644 vignettes/images/teal-app-init-with-modifiers.svg delete mode 100644 vignettes/images/teal-app-init.svg delete mode 100644 vignettes/quick-usage-guide-for-teal.Rmd diff --git a/_pkgdown.yml b/_pkgdown.yml index a054d4d4d0..1eea00f5f1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -56,7 +56,6 @@ articles: navbar: Get started contents: - getting-started-with-teal - - quick-usage-guide-for-teal - title: Using `teal` navbar: Using `teal` contents: diff --git a/inst/design/teal-app-init-with-modifiers.drawio b/inst/design/teal-app-init-with-modifiers.drawio deleted file mode 100644 index 9c1ee7c389..0000000000 --- a/inst/design/teal-app-init-with-modifiers.drawio +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/inst/design/teal-app-init.drawio b/inst/design/teal-app-init.drawio deleted file mode 100644 index 9e6a5c0cae..0000000000 --- a/inst/design/teal-app-init.drawio +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/vignettes/images/teal-app-init-with-modifiers.svg b/vignettes/images/teal-app-init-with-modifiers.svg deleted file mode 100644 index c30056e52d..0000000000 --- a/vignettes/images/teal-app-init-with-modifiers.svg +++ /dev/null @@ -1 +0,0 @@ -
Custom title
Custom title
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Custom header
Custom header
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Custom footer
Custom footer
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
   Yes, the button works!
   Yes, the button works!
x
x
Text is not SVG - cannot display
\ No newline at end of file diff --git a/vignettes/images/teal-app-init.svg b/vignettes/images/teal-app-init.svg deleted file mode 100644 index e0713bf493..0000000000 --- a/vignettes/images/teal-app-init.svg +++ /dev/null @@ -1 +0,0 @@ -
teal app
teal app
127.0.0.1:3838
127.0.0.1:3838
Module 1
Module 1
Choose a dataset 
Choose a dataset 
IRIS
IRIS
Show R code
Show R code
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species...
Active Filter Summary
Active Filter Summary
dataname
dataname
MTCARS
MTCARS
IRIS
IRIS
Obs
Obs
32/32
32/32
50/150
50/150
Active Filter Variables
Active Filter Variables
IRIS
IRIS
Species
Species
setosa
setosa
Add Filter Variables
Add Filter Variables
IRIS
IRIS
Add
Add
filter
filter
Select variable to filter
Select variable to fi...
MTCARS
MTCARS
MTCARS
MTCA...
Add
Add
filter
filter
Select variable to filter
Select variable to fil...
Session Info
Session In...
Pid:33902 Token:ed1901df
Pid:33902 Token:ed19...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/vignettes/quick-usage-guide-for-teal.Rmd b/vignettes/quick-usage-guide-for-teal.Rmd deleted file mode 100644 index f6ac592a9a..0000000000 --- a/vignettes/quick-usage-guide-for-teal.Rmd +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "Quick usage guide for teal" -author: "NEST CoreDev" -output: rmarkdown::html_vignette -vignette: > - %\VignetteIndexEntry{Quick usage guide for teal} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} ---- - -## `init` to initialize your app - -The `init` function is the entry point to create a `teal` application. -The main arguments of `init` are `data` and `modules`, which are mandatory. Additionally, the `filter` argument allows the user to specify the default filter state of the filter panel. - -* The `data` argument takes a `teal_data` object, please refer to [Including Data in teal Applications vignette](including-data-in-teal-applications.html) to learn more. -* The `modules` argument takes a nested list of `teal_modules` or `teal_module` objects or a single `teal_modules` or `teal_module` object. The packages like [`teal.modules.general`](https://insightsengineering.github.io/teal.modules.general/) or [`teal.modules.clinical`](https://insightsengineering.github.io/teal.modules.clinical/) contain a collection of these modules that can be used in your `teal` application. -* The `filter` argument takes an object of class `teal_slices`, please refer the [Filter Panel vignette](filter-panel.html) to learn more. - -## A simple `teal` application: - -This simple `teal` application takes the `iris` and `mtcars` datasets and displays their contents: - -```{r, eval=FALSE} -library(teal) - -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = modules( - example_module("Module 1") - ), - filter = teal_slices( - teal_slice(dataname = "IRIS", varname = "Species", selected = "setosa") - ) -) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -``` - -![](./images/teal-app-init.svg) - -## Extending the `teal` application - -The following functions can be used to extend the `teal` application: - -* `modify_title` changes the title that appears in the browser tab. -* `modify_header` adds a header with a button that shows a notification when clicked, also note that the observer for this button was added using the modifier `teal_extend_server`. -* `modify_footer` adds a custom footer. -* `add_landing_modal` adds a landing popup that appears when the application is first loaded. Note app illustration below does not show the popup, but it will appear as soon as the application loads. - -```{r, eval=FALSE} -library(teal) - -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = modules( - example_module("Module 1") - ), - filter = teal_slices( - teal_slice(dataname = "IRIS", varname = "Species", selected = "setosa") - ) -) |> - modify_title(title = "Custom title") |> - modify_header( - header = tags$div(h3("Custom header")) - ) |> - modify_footer(footer = "Custom footer") |> - add_landing_modal(title = "Teal", content = "Welcome to my first teal application!") - -if (interactive()) { - shinyApp(app$ui, app$server) -} -``` - -Here is what the app would look with those modifiers added. - -![](./images/teal-app-init-with-modifiers.svg) -