From 404dbd6e4a79530cca5b9229a711b1a8f100b63e Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 10 Jan 2025 13:23:50 +0530 Subject: [PATCH] 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 634dbfc9e..7dab02589 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 654a4ced0..e81f3bc5a 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 7834d1d8a..639d96900 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 46fcddc1a..a5276e956 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 61b602050..f0eca2c42 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 961ac2b3c..27656e38c 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 1976b5428..cdb3b09c5 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 4d487d9cc..b25a4d8d9 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 c3f66d3e7..93ab8ada7 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 5a3eec9bd..9f6e845b4 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 dbf514949..7e34b653e 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 240ab8bb6..c8fb484c1 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