diff --git a/DESCRIPTION b/DESCRIPTION index 290c0770..ee485f08 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -11,17 +11,17 @@ BugReports: https://github.com/insightsengineering/teal.reporter/issues Imports: checkmate, grid, + knitr, R6, + rmarkdown, shiny, shinyWidgets, yaml, zip Suggests: ggplot2, - knitr, lattice, png, - rmarkdown, rtables, testthat VignetteBuilder: diff --git a/NAMESPACE b/NAMESPACE index 917af506..b5a7be28 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,6 +9,8 @@ export(add_card_button_ui) export(as_yaml_auto) export(download_report_button_srv) export(download_report_button_ui) +export(reporter_previewer_srv) +export(reporter_previewer_ui) export(reset_report_button_srv) export(reset_report_button_ui) export(rmd_output_arguments) diff --git a/R/DownloadModule.R b/R/DownloadModule.R index 7e407ce7..a1964750 100644 --- a/R/DownloadModule.R +++ b/R/DownloadModule.R @@ -25,7 +25,6 @@ download_report_button_ui <- function(id) { #' For more details see the vignette: `vignette("simpleReporter", "teal.reporter")`. #' @param id `character` #' @param reporter `Reporter` instance. -#' @param notification `logical` whether to add a shiny notification about the download process. Default `TRUE`. #' @param rmd_output `character` vector with `rmarkdown` output types, #' by default all possible `c("pdf_document", "html_document", "powerpoint_presentation", "word_document")`. #' @param rmd_yaml_args `named list` vector with `Rmd` `yaml` header fields and their default values. @@ -35,7 +34,6 @@ download_report_button_ui <- function(id) { #' @export download_report_button_srv <- function(id, reporter, - notification = TRUE, rmd_output = c( "html_document", "pdf_document", "powerpoint_presentation", "word_document" @@ -45,7 +43,6 @@ download_report_button_srv <- function(id, date = as.character(Sys.Date()), output = "html_document" )) { checkmate::assert_class(reporter, "Reporter") - checkmate::assert_flag(notification) checkmate::assert_subset(rmd_output, c( "html_document", "pdf_document", "powerpoint_presentation", "word_document" @@ -122,9 +119,7 @@ download_report_button_srv <- function(id, paste("report_", format(Sys.time(), "%y%m%d%H%M%S"), ".zip", sep = "") }, content = function(file) { - if (notification) { - shiny::showNotification(sprintf("Rendering and Downloading a document.")) - } + shiny::showNotification("Rendering and Downloading the document.") input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) names(input_list) <- names(rmd_yaml_args) report_render_and_compress(reporter, input_list, file) diff --git a/R/Previewer.R b/R/Previewer.R new file mode 100644 index 00000000..90c1887b --- /dev/null +++ b/R/Previewer.R @@ -0,0 +1,282 @@ +#' Reporter Previewer User Interface +#' @description reporter previewer user interface to visualize and manipulate the already added report Cards +#' @param id `character` +#' @param rmd_output `character` vector with `rmarkdown` output types, +#' by default all possible `c("pdf_document", "html_document", "powerpoint_presentation", "word_document")`. +#' @param rmd_yaml_args `named list` vector with `Rmd` `yaml` header fields and their default values. +#' Default `list(author = "NEST", title = "Report", date = Sys.Date(), output = "html_document")`. +#' Please update only values at this moment. +#' @export +reporter_previewer_ui <- function(id, rmd_output = c( + "html_document", "pdf_document", + "powerpoint_presentation", "word_document" + ), + rmd_yaml_args = list( + author = "NEST", title = "Report", + date = as.character(Sys.Date()), output = "html_document" + )) { + ns <- shiny::NS(id) + encoding <- shiny::tagList( + shiny::tags$h3("Download the Report"), + shiny::tags$hr(), + shiny::textInput(ns("author"), label = "Author:", value = rmd_yaml_args$author), + shiny::textInput(ns("title"), label = "Title:", value = rmd_yaml_args$title), + shiny::dateInput(ns("date"), "Date:", value = rmd_yaml_args$date), + shiny::tags$div( + shinyWidgets::pickerInput( + inputId = ns("output"), + label = "Choose a document type: ", + choices = rmd_output, + selected = rmd_yaml_args$output + ) + ), + shiny::tags$a( + id = ns("download_data_prev"), + class = "btn btn-primary shiny-download-link", + href = "", + target = "_blank", + download = NA, + shiny::icon("download"), + "Download Report" + ), + teal.reporter::reset_report_button_ui(ns("resetButtonPreviewer")) + ) + + shiny::fluidRow( + add_previewer_js(ns), + add_previewer_css(), + shiny::tags$div( + shiny::tags$div( + class = "col-md-3", + shiny::tags$div(class = "well", encoding) + ), + shiny::tags$div( + class = "col-md-9", + shiny::tags$div( + id = "reporter_previewer_panel", + shiny::uiOutput(ns("pcards")) + ) + ) + ) + ) +} + +#' Reporter Previewer Server +#' @description server supporting the functionalities of the reporter previewer +#' @param id `character` +#' @param reporter `Reporter` instance +#' @param rmd_yaml_args `named list` vector with `Rmd` `yaml` header fields and their default values. +#' Default `list(author = "NEST", title = "Report", date = Sys.Date(), output = "html_document")`. +#' Please update only values at this moment. +#' @export +reporter_previewer_srv <- function(id, reporter, rmd_yaml_args = list( + author = "NEST", title = "Report", + date = as.character(Sys.Date()), output = "html_document" + )) { + checkmate::assert_class(reporter, "Reporter") + shiny::moduleServer( + id, + function(input, output, session) { + ns <- session$ns + + teal.reporter::reset_report_button_srv("resetButtonPreviewer", reporter) + + output$pcards <- shiny::renderUI({ + reporter$get_reactive_add_card() + input$card_remove_id + input$card_down_id + input$card_up_id + + cards <- reporter$get_cards() + + if (length(cards)) { + shiny::tags$div( + class = "panel-group", id = "accordion", + lapply(seq_along(cards), function(ic) { + shiny::tags$div( + id = paste0("panel_card_", ic), + class = "panel panel-default", + previewer_collapse_head(ic, cards[[ic]]$get_name()), + previewer_collapse_body(ic, cards[[ic]]$get_content()) + ) + }) + ) + } else { + shiny::tags$div( + id = "reporter_previewer_panel_no_cards", + shiny::tags$p(style = "color:red;", shiny::tags$strong("No Cards added")) + ) + } + }) + + shiny::observeEvent(input$card_remove_id, { + reporter$remove_cards(input$card_remove_id) + }) + + shiny::observeEvent(input$card_up_id, { + if (input$card_up_id > 1) { + reporter$swap_cards( + as.integer(input$card_up_id), + as.integer(input$card_up_id - 1) + ) + } + }) + + shiny::observeEvent(input$card_down_id, { + if (input$card_down_id < length(reporter$get_cards())) { + reporter$swap_cards( + as.integer(input$card_down_id), + as.integer(input$card_down_id + 1) + ) + } + }) + + output$download_data_prev <- shiny::downloadHandler( + filename = function() { + paste("report_", format(Sys.time(), "%y%m%d%H%M%S"), ".zip", sep = "") + }, + content = function(file) { + shiny::showNotification("Rendering and Downloading the document.") + input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) + names(input_list) <- names(rmd_yaml_args) + report_render_and_compress(reporter, input_list, file) + }, + contentType = "application/zip" + ) + } + ) +} + +block_to_html <- function(b) { + block_class <- class(b)[1] + b_content <- b$get_content() + switch(block_class, + TextBlock = { + switch(b$get_style(), + header1 = shiny::tags$h1(b_content), + header2 = shiny::tags$h2(b_content), + header3 = shiny::tags$h3(b_content), + header4 = shiny::tags$h4(b_content), + verbatim = shiny::tags$pre(b_content), + b_content + ) + }, + PictureBlock = shiny::tags$img(src = knitr::image_uri(b_content)), + TableBlock = { + b_table <- readRDS(b_content) + shiny::tags$pre( + paste(utils::capture.output(print(b_table)), collapse = "\n") + ) + }, + NewpageBlock = shiny::tags$br(), + "" + ) +} + +add_previewer_css <- function() { + shiny::tags$head(shiny::tags$style(" + span.preview_card_control i:hover { + color: blue; + } + + .isDisabled { + color: currentColor; + cursor: not-allowed; + pointer-events: none; + opacity: 0.5; + text-decoration: none; + } + ")) +} + +add_previewer_js <- function(ns) { + shiny::tags$head(shiny::tags$script( + shiny::HTML(sprintf(' + $(document).ready(function(event) { + $("body").on("click", "span.card_remove_id", function() { + let val = $(this).data("cardid"); + let msg_confirm = "Do you really want to remove the card " + val + " from the Report?"; + let answer = confirm(msg_confirm); + if (answer) { + Shiny.setInputValue("%s", val, {priority: "event"}); + $("#panel_card_" + val).remove(); + } + }); + + $("body").on("click", "span.card_up_id", function() { + let val = $(this).data("cardid"); + Shiny.setInputValue("%s", val, {priority: "event"}); + }); + + $("body").on("click", "span.card_down_id", function() { + let val = $(this).data("cardid"); + Shiny.setInputValue("%s", val, {priority: "event"}); + }); + + $("body").on("DOMSubtreeModified", "#reporter_previewer_panel", function() { + let accor = $(this).find("#accordion"); + let down_button = $("#%s"); + if (accor && (accor.length === 0)) { + down_button.addClass("isDisabled"); + } else { + down_button.removeClass("isDisabled"); + } + }); + + }); + ', ns("card_remove_id"), ns("card_up_id"), ns("card_down_id"), ns("download_data_prev"))) + )) +} + +nav_previewer_icon <- function(name, icon_name, idx, size = 1L) { + checkmate::assert_string(name) + checkmate::assert_string(icon_name) + checkmate::assert_int(size) + + shiny::tags$span( + class = name, `data-cardid` = idx, + style = "float:right;margin-left:10px;margin-right:10px;margin-top:10px;color:#337ab7;", + shiny::icon(icon_name, sprintf("fa-%sx", size)) + ) +} + +previewer_collapse_body <- function(idx, card_blocks) { + shiny::tags$div( + id = paste0("collapse", idx), class = "panel-collapse collapse out", + shiny::tags$div( + class = "panel-body", + shiny::tags$div( + id = paste0("card", idx), + lapply( + card_blocks, + function(b) { + block_to_html(b) + } + ) + ) + ) + ) +} + +previewer_collapse_head <- function(idx, card_name) { + shiny::tags$div( + class = "panel-heading", style = "overflow:auto;", + shiny::tags$h4( + class = "panel-title", + shiny::tags$span( + shiny::tags$span( + class = "preview_card_control", + nav_previewer_icon(name = "card_remove_id", icon_name = "times", idx = idx, size = 1), + nav_previewer_icon(name = "card_up_id", icon_name = "arrow-up", idx = idx, size = 1), + nav_previewer_icon(name = "card_down_id", icon_name = "arrow-down", idx = idx, size = 1) + ), + shiny::tags$a( + class = "accordion-toggle", + style = "display: block;padding: 10px 15px;margin: -10px -15px;", + `data-toggle` = "collapse", `data-parent` = "#accordion", href = paste0("#collapse", idx), + shiny::tags$h4(paste0("Card ", idx, ": ", card_name), shiny::icon("caret-down")) + ) + ) + ) + ) +} diff --git a/R/ReportCard.R b/R/ReportCard.R index 935036b1..57a4d2f5 100644 --- a/R/ReportCard.R +++ b/R/ReportCard.R @@ -129,12 +129,31 @@ ReportCard <- R6::R6Class( # nolint: object_name_linter. #' get_chr_converters = function() { private$chr_converters + }, + #' @description get the Card name + #' + #' @return `character` a Card name + #' @examples + #' ReportCard$new()$set_name("NAME")$get_name() + get_name = function() { + private$name + }, + #' @description set the Card name + #' + #' @param name `character` a Card name + #' @return invisibly self + #' @examples + #' ReportCard$new()$set_name("NAME")$get_name() + set_name = function(name) { + checkmate::assert_string(name) + private$name <- name + invisible(self) } ), private = list( content = list(), chr_converters = list(), - + name = character(0), # @description The copy constructor. # # @param name the name of the field diff --git a/R/Reporter.R b/R/Reporter.R index ff260570..eed03b31 100644 --- a/R/Reporter.R +++ b/R/Reporter.R @@ -12,6 +12,7 @@ Reporter <- R6::R6Class( # nolint: object_name_linter. #' initialize = function() { private$cards <- list() + private$reactive_add_card <- shiny::reactiveVal(0) invisible(self) }, #' @description Appends a table to this `Reporter`. @@ -42,6 +43,7 @@ Reporter <- R6::R6Class( # nolint: object_name_linter. append_cards = function(cards) { checkmate::assert_list(cards, "ReportCard") private$cards <- append(private$cards, cards) + private$reactive_add_card(length(private$cards)) invisible(self) }, #' @description Returns cards of this `Reporter`. @@ -114,11 +116,58 @@ Reporter <- R6::R6Class( # nolint: object_name_linter. #' reset = function() { private$cards <- list() + private$reactive_add_card(0) invisible(self) + }, + #' @description remove a specific Card in the Reporter + #' + #' @param ids `integer` the indexes of cards + #' @return invisibly self + remove_cards = function(ids = NULL) { + checkmate::assert( + checkmate::check_null(ids), + checkmate::check_integer(ids, min.len = 1, max.len = length(private$cards)) + ) + if (!is.null(ids)) { + private$cards <- private$cards[-ids] + } + private$reactive_add_card(length(private$cards)) + invisible(self) + }, + #' @description swap two cards in the Reporter + #' + #' @param start `integer` the index of the first card + #' @param end `integer` the index of the second card + #' @return invisibly self + swap_cards = function(start, end) { + checkmate::assert( + checkmate::check_integer(start, + min.len = 1, max.len = 1, lower = 1, upper = length(private$cards) + ), + checkmate::check_integer(end, + min.len = 1, max.len = 1, lower = 1, upper = length(private$cards) + ), + combine = "and" + ) + start_val <- private$cards[[start]]$clone() + end_val <- private$cards[[end]]$clone() + private$cards[[start]] <- end_val + private$cards[[end]] <- start_val + invisible(self) + }, + #' @description get a value for the reactive value for the add card + #' + #' @return `reactive_add_card` filed value + #' @note The function has to be used in the shiny reactive context. + #' @examples + #' shiny::isolate(Reporter$new()$get_reactive_add_card()) + get_reactive_add_card = function() { + private$reactive_add_card() } ), private = list( cards = list(), + reactive_add_card = NULL, # @description The copy constructor. # # @param name the name of the field diff --git a/R/SimpleReporter.R b/R/SimpleReporter.R index 388084bf..391f5816 100644 --- a/R/SimpleReporter.R +++ b/R/SimpleReporter.R @@ -24,15 +24,14 @@ simple_reporter_ui <- function(id) { #' @param reporter `Reporter` instance. #' @param card_fun `function` which returns a `ReportCard` instance, #' the function have at`card`argument and optional `comment`. -#' @param notification logical if to add shiny notification about the download process. #' @return `shiny::moduleServer` #' @export -simple_reporter_srv <- function(id, reporter, card_fun, notification = TRUE) { +simple_reporter_srv <- function(id, reporter, card_fun) { shiny::moduleServer( id, function(input, output, session) { add_card_button_srv("add_report_card_simple", reporter = reporter, card_fun = card_fun) - download_report_button_srv("download_button_simple", reporter = reporter, notification = notification) + download_report_button_srv("download_button_simple", reporter = reporter) reset_report_button_srv("reset_button_simple", reporter = reporter) } ) diff --git a/R/TableBlock.R b/R/TableBlock.R index c6a93bc1..19a6ebec 100644 --- a/R/TableBlock.R +++ b/R/TableBlock.R @@ -20,7 +20,7 @@ TableBlock <- R6::R6Class( # nolint: object_name_linter. #' #' @details throws if argument is not a table-like object. #' - #' @param content (`data.frame`, `DT`, `rtables`) a table assigned to this `TableBlock` + #' @param content (`data.frame`, `rtables`) a table assigned to this `TableBlock` #' @return invisibly self #' @examples #' block <- teal.reporter:::TableBlock$new() @@ -42,7 +42,7 @@ TableBlock <- R6::R6Class( # nolint: object_name_linter. } ), private = list( - supported_tables = c("data.frame", "DT", "rtables", "TableTree") + supported_tables = c("data.frame", "rtables", "TableTree") ), lock_objects = TRUE, lock_class = TRUE diff --git a/R/yaml_utils.R b/R/yaml_utils.R index 775f33d9..abe5c007 100644 --- a/R/yaml_utils.R +++ b/R/yaml_utils.R @@ -48,7 +48,6 @@ md_header <- function(x) { #' teal.reporter:::conv_str_logi("n") #' #' teal.reporter:::conv_str_logi("sth") -#' conv_str_logi <- function(input, name = "", pos_logi = c("TRUE", "true", "True", "yes", "y", "Y", "on"), @@ -78,7 +77,6 @@ conv_str_logi <- function(input, #' @export #' @examples #' rmd_outputs() -#' rmd_outputs <- function() { rmarkdown_namespace <- asNamespace("rmarkdown") ls(rmarkdown_namespace)[grep("_document|_presentation", ls(rmarkdown_namespace))] @@ -93,7 +91,6 @@ rmd_outputs <- function() { #' @examples #' rmd_output_arguments("pdf_document") #' rmd_output_arguments("pdf_document", TRUE) -#' rmd_output_arguments <- function(output_name, default_values = FALSE) { checkmate::assert_string(output_name) checkmate::assert_subset(output_name, rmd_outputs()) @@ -164,7 +161,6 @@ rmd_output_arguments <- function(output_name, default_values = FALSE) { #' ), #' multi_output = TRUE #' ) -#' as_yaml_auto <- function(input_list, as_header = TRUE, convert_logi = TRUE, @@ -260,7 +256,6 @@ as_yaml_auto <- function(input_list, #' out <- as_yaml_auto(input) #' out #' print(out) -#' print.rmd_yaml_header <- function(x, ...) { cat(x, ...) } diff --git a/man/ReportCard.Rd b/man/ReportCard.Rd index 74c94c7b..b0e6ed62 100644 --- a/man/ReportCard.Rd +++ b/man/ReportCard.Rd @@ -77,6 +77,18 @@ card <- ReportCard$new()$append_text("Some text")$append_plot( )$append_metadata(key = "code", value = lm(Ozone ~ Solar.R, airquality)) card$get_chr_converters() + +## ------------------------------------------------ +## Method `ReportCard$get_name` +## ------------------------------------------------ + +ReportCard$new()$set_name("NAME")$get_name() + +## ------------------------------------------------ +## Method `ReportCard$set_name` +## ------------------------------------------------ + +ReportCard$new()$set_name("NAME")$get_name() } \section{Methods}{ \subsection{Public methods}{ @@ -88,6 +100,8 @@ card$get_chr_converters() \item \href{#method-get_content}{\code{ReportCard$get_content()}} \item \href{#method-append_metadata}{\code{ReportCard$append_metadata()}} \item \href{#method-get_chr_converters}{\code{ReportCard$get_chr_converters()}} +\item \href{#method-get_name}{\code{ReportCard$get_name()}} +\item \href{#method-set_name}{\code{ReportCard$set_name()}} \item \href{#method-clone}{\code{ReportCard$clone()}} } } @@ -309,6 +323,55 @@ card$get_chr_converters() } +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_name}{}}} +\subsection{Method \code{get_name()}}{ +get the Card name +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ReportCard$get_name()}\if{html}{\out{
}} +} + +\subsection{Returns}{ +\code{character} a Card name +} +\subsection{Examples}{ +\if{html}{\out{
}} +\preformatted{ReportCard$new()$set_name("NAME")$get_name() +} +\if{html}{\out{
}} + +} + +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-set_name}{}}} +\subsection{Method \code{set_name()}}{ +set the Card name +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{ReportCard$set_name(name)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{name}}{\code{character} a Card name} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +invisibly self +} +\subsection{Examples}{ +\if{html}{\out{
}} +\preformatted{ReportCard$new()$set_name("NAME")$get_name() +} +\if{html}{\out{
}} + +} + } \if{html}{\out{
}} \if{html}{\out{}} diff --git a/man/Reporter.Rd b/man/Reporter.Rd index 43aecb02..c38ecb3b 100644 --- a/man/Reporter.Rd +++ b/man/Reporter.Rd @@ -8,6 +8,9 @@ \code{Reporter} } +\note{ +The function has to be used in the shiny reactive context. +} \examples{ ## ------------------------------------------------ @@ -92,6 +95,12 @@ reporter <- teal.reporter:::Reporter$new() reporter$append_cards(list(card1, card2)) reporter$get_blocks() + +## ------------------------------------------------ +## Method `Reporter$get_reactive_add_card` +## ------------------------------------------------ + +shiny::isolate(Reporter$new()$get_reactive_add_card()) } \section{Methods}{ \subsection{Public methods}{ @@ -101,6 +110,9 @@ reporter$get_blocks() \item \href{#method-get_cards}{\code{Reporter$get_cards()}} \item \href{#method-get_blocks}{\code{Reporter$get_blocks()}} \item \href{#method-reset}{\code{Reporter$reset()}} +\item \href{#method-remove_cards}{\code{Reporter$remove_cards()}} +\item \href{#method-swap_cards}{\code{Reporter$swap_cards()}} +\item \href{#method-get_reactive_add_card}{\code{Reporter$get_reactive_add_card()}} \item \href{#method-clone}{\code{Reporter$clone()}} } } @@ -274,6 +286,69 @@ Removes all \code{ReportCard} objects added to this \code{Reporter}. \subsection{Returns}{ invisibly self } +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-remove_cards}{}}} +\subsection{Method \code{remove_cards()}}{ +remove a specific Card in the Reporter +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{Reporter$remove_cards(ids = NULL)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{ids}}{\code{integer} the indexes of cards} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +invisibly self +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-swap_cards}{}}} +\subsection{Method \code{swap_cards()}}{ +swap two cards in the Reporter +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{Reporter$swap_cards(start, end)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{start}}{\code{integer} the index of the first card} + +\item{\code{end}}{\code{integer} the index of the second card} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +invisibly self +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-get_reactive_add_card}{}}} +\subsection{Method \code{get_reactive_add_card()}}{ +get a value for the reactive value for the add card +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{Reporter$get_reactive_add_card()}\if{html}{\out{
}} +} + +\subsection{Returns}{ +\code{reactive_add_card} filed value +} +\subsection{Examples}{ +\if{html}{\out{
}} +\preformatted{shiny::isolate(Reporter$new()$get_reactive_add_card()) +} +\if{html}{\out{
}} + +} + } \if{html}{\out{
}} \if{html}{\out{}} diff --git a/man/TableBlock.Rd b/man/TableBlock.Rd index e272eff6..248319a0 100644 --- a/man/TableBlock.Rd +++ b/man/TableBlock.Rd @@ -70,7 +70,7 @@ Sets content of this \code{TableBlock}. \subsection{Arguments}{ \if{html}{\out{
}} \describe{ -\item{\code{content}}{(\code{data.frame}, \code{DT}, \code{rtables}) a table assigned to this \code{TableBlock}} +\item{\code{content}}{(\code{data.frame}, \code{rtables}) a table assigned to this \code{TableBlock}} } \if{html}{\out{
}} } diff --git a/man/TealReportCard.Rd b/man/TealReportCard.Rd index 317a414f..3e939469 100644 --- a/man/TealReportCard.Rd +++ b/man/TealReportCard.Rd @@ -59,7 +59,9 @@ card$get_content()[[1]]$get_content() \item \out{}\href{../../teal.reporter/html/ReportCard.html#method-append_text}{\code{teal.reporter::ReportCard$append_text()}}\out{} \item \out{}\href{../../teal.reporter/html/ReportCard.html#method-get_chr_converters}{\code{teal.reporter::ReportCard$get_chr_converters()}}\out{} \item \out{}\href{../../teal.reporter/html/ReportCard.html#method-get_content}{\code{teal.reporter::ReportCard$get_content()}}\out{} +\item \out{}\href{../../teal.reporter/html/ReportCard.html#method-get_name}{\code{teal.reporter::ReportCard$get_name()}}\out{} \item \out{}\href{../../teal.reporter/html/ReportCard.html#method-initialize}{\code{teal.reporter::ReportCard$initialize()}}\out{} +\item \out{}\href{../../teal.reporter/html/ReportCard.html#method-set_name}{\code{teal.reporter::ReportCard$set_name()}}\out{} } \out{} } diff --git a/man/as_yaml_auto.Rd b/man/as_yaml_auto.Rd index d62eeee6..565975ea 100644 --- a/man/as_yaml_auto.Rd +++ b/man/as_yaml_auto.Rd @@ -77,5 +77,4 @@ as_yaml_auto( ), multi_output = TRUE ) - } diff --git a/man/conv_str_logi.Rd b/man/conv_str_logi.Rd index 22cfae02..36b5ec05 100644 --- a/man/conv_str_logi.Rd +++ b/man/conv_str_logi.Rd @@ -37,6 +37,5 @@ teal.reporter:::conv_str_logi("off") teal.reporter:::conv_str_logi("n") teal.reporter:::conv_str_logi("sth") - } \keyword{internal} diff --git a/man/download_report_button_srv.Rd b/man/download_report_button_srv.Rd index 80c38935..e83ad106 100644 --- a/man/download_report_button_srv.Rd +++ b/man/download_report_button_srv.Rd @@ -7,7 +7,6 @@ download_report_button_srv( id, reporter, - notification = TRUE, rmd_output = c("html_document", "pdf_document", "powerpoint_presentation", "word_document"), rmd_yaml_args = list(author = "NEST", title = "Report", date = @@ -19,8 +18,6 @@ download_report_button_srv( \item{reporter}{\code{Reporter} instance.} -\item{notification}{\code{logical} whether to add a shiny notification about the download process. Default \code{TRUE}.} - \item{rmd_output}{\code{character} vector with \code{rmarkdown} output types, by default all possible \code{c("pdf_document", "html_document", "powerpoint_presentation", "word_document")}.} diff --git a/man/print.rmd_yaml_header.Rd b/man/print.rmd_yaml_header.Rd index b4b8b5f0..a42dde80 100644 --- a/man/print.rmd_yaml_header.Rd +++ b/man/print.rmd_yaml_header.Rd @@ -19,5 +19,4 @@ input <- list(author = "", output = "pdf_document", toc = TRUE, keep_tex = TRUE) out <- as_yaml_auto(input) out print(out) - } diff --git a/man/reporter_previewer_srv.Rd b/man/reporter_previewer_srv.Rd new file mode 100644 index 00000000..f172e6b6 --- /dev/null +++ b/man/reporter_previewer_srv.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Previewer.R +\name{reporter_previewer_srv} +\alias{reporter_previewer_srv} +\title{Reporter Previewer Server} +\usage{ +reporter_previewer_srv( + id, + reporter, + rmd_yaml_args = list(author = "NEST", title = "Report", date = + as.character(Sys.Date()), output = "html_document") +) +} +\arguments{ +\item{id}{\code{character}} + +\item{reporter}{\code{Reporter} instance} + +\item{rmd_yaml_args}{\verb{named list} vector with \code{Rmd} \code{yaml} header fields and their default values. +Default \code{list(author = "NEST", title = "Report", date = Sys.Date(), output = "html_document")}. +Please update only values at this moment.} +} +\description{ +server supporting the functionalities of the reporter previewer +} diff --git a/man/reporter_previewer_ui.Rd b/man/reporter_previewer_ui.Rd new file mode 100644 index 00000000..d250a662 --- /dev/null +++ b/man/reporter_previewer_ui.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Previewer.R +\name{reporter_previewer_ui} +\alias{reporter_previewer_ui} +\title{Reporter Previewer User Interface} +\usage{ +reporter_previewer_ui( + id, + rmd_output = c("html_document", "pdf_document", "powerpoint_presentation", + "word_document"), + rmd_yaml_args = list(author = "NEST", title = "Report", date = + as.character(Sys.Date()), output = "html_document") +) +} +\arguments{ +\item{id}{\code{character}} + +\item{rmd_output}{\code{character} vector with \code{rmarkdown} output types, +by default all possible \code{c("pdf_document", "html_document", "powerpoint_presentation", "word_document")}.} + +\item{rmd_yaml_args}{\verb{named list} vector with \code{Rmd} \code{yaml} header fields and their default values. +Default \code{list(author = "NEST", title = "Report", date = Sys.Date(), output = "html_document")}. +Please update only values at this moment.} +} +\description{ +reporter previewer user interface to visualize and manipulate the already added report Cards +} diff --git a/man/rmd_output_arguments.Rd b/man/rmd_output_arguments.Rd index fb7517f9..7b295df6 100644 --- a/man/rmd_output_arguments.Rd +++ b/man/rmd_output_arguments.Rd @@ -17,5 +17,4 @@ get document output arguments from the \code{rmarkdown} package \examples{ rmd_output_arguments("pdf_document") rmd_output_arguments("pdf_document", TRUE) - } diff --git a/man/rmd_outputs.Rd b/man/rmd_outputs.Rd index bae6a93f..a10e2f17 100644 --- a/man/rmd_outputs.Rd +++ b/man/rmd_outputs.Rd @@ -14,5 +14,4 @@ get document output types from the \code{rmarkdown} package. } \examples{ rmd_outputs() - } diff --git a/man/simple_reporter_srv.Rd b/man/simple_reporter_srv.Rd index f9ba7174..13d80e0f 100644 --- a/man/simple_reporter_srv.Rd +++ b/man/simple_reporter_srv.Rd @@ -4,7 +4,7 @@ \alias{simple_reporter_srv} \title{Simple Reporter Server} \usage{ -simple_reporter_srv(id, reporter, card_fun, notification = TRUE) +simple_reporter_srv(id, reporter, card_fun) } \arguments{ \item{id}{\code{character}} @@ -13,8 +13,6 @@ simple_reporter_srv(id, reporter, card_fun, notification = TRUE) \item{card_fun}{\code{function} which returns a \code{ReportCard} instance, the function have at\code{card}argument and optional \code{comment}.} - -\item{notification}{logical if to add shiny notification about the download process.} } \value{ \code{shiny::moduleServer} diff --git a/tests/testthat/test-DownloadReportModule.R b/tests/testthat/test-DownloadReportModule.R index 76fddd41..66b72570 100644 --- a/tests/testthat/test-DownloadReportModule.R +++ b/tests/testthat/test-DownloadReportModule.R @@ -12,7 +12,7 @@ reporter$append_cards(list(card1)) testthat::test_that("download_report_button_srv - render and downlaod a document", { shiny::testServer( download_report_button_srv, - args = list(reporter = reporter, notification = FALSE), + args = list(reporter = reporter), expr = { session$setInputs(`download_button` = 0) session$setInputs(`output` = "html_document") @@ -29,6 +29,7 @@ testthat::test_that("download_report_button_srv - render and downlaod a document files <- list.files(output_dir, recursive = TRUE) testthat::expect_true(any(grepl("[.]Rmd", files))) testthat::expect_true(any(grepl("[.]html", files))) + unlink(output_dir, recursive = TRUE) } ) }) diff --git a/tests/testthat/test-PreviewerReportModule.R b/tests/testthat/test-PreviewerReportModule.R new file mode 100644 index 00000000..94b5d078 --- /dev/null +++ b/tests/testthat/test-PreviewerReportModule.R @@ -0,0 +1,125 @@ +card1 <- ReportCard$new() +card1$append_text("Header 2 text", "header2") +card1$append_text("A paragraph of default text", "header2") +card1$append_plot( + ggplot2::ggplot(iris, ggplot2::aes(x = Petal.Length)) + + ggplot2::geom_histogram() +) + +reporter <- Reporter$new() +reporter$append_cards(list(card1)) + +testthat::test_that("reporter_previewer_srv - render and downlaod a document", { + shiny::testServer( + reporter_previewer_srv, + args = list(reporter = reporter), + expr = { + session$setInputs(`output` = "html_document") + session$setInputs(`title` = "TITLE") + session$setInputs(`author` = "AUTHOR") + session$setInputs(`download_data_prev` = 0) + + f <- output$download_data_prev + testthat::expect_true(file.exists(f)) + tmp_dir <- tempdir() + output_dir <- file.path(tmp_dir, sprintf("report_test_%s", gsub("[.]", "", format(Sys.time(), "%Y%m%d%H%M%OS4")))) + dir.create(path = output_dir) + zip::unzip(f, exdir = output_dir) + files <- list.files(output_dir, recursive = TRUE) + testthat::expect_true(any(grepl("[.]Rmd", files))) + testthat::expect_true(any(grepl("[.]html", files))) + unlink(output_dir, recursive = TRUE) + } + ) +}) + +reporter <- Reporter$new() +reporter$append_cards(list(card1)) +testthat::test_that("reporter_previewer_srv - remove a card", { + shiny::testServer( + reporter_previewer_srv, + args = list(reporter = reporter), + expr = { + len_prior <- length(reporter$get_cards()) + session$setInputs(`card_remove_id` = 1L) + len_post <- length(reporter$get_cards()) + + testthat::expect_identical(len_prior, len_post + 1L) + } + ) +}) + +card2 <- ReportCard$new() +card2$append_text("Header 2 text 2", "header2") +card2$append_text("A paragraph of default text 2", "header2") +card2$append_plot( + ggplot2::ggplot(iris, ggplot2::aes(x = Petal.Width)) + + ggplot2::geom_histogram() +) + +reporter <- Reporter$new() +reporter$append_cards(list(card1, card2)) + +testthat::test_that("reporter_previewer_srv - up with first card and down with last card does not induce change", { + shiny::testServer( + reporter_previewer_srv, + args = list(reporter = reporter), + expr = { + cards_pre <- reporter$get_cards() + session$setInputs(`card_up_id` = 1L) + cards_post <- reporter$get_cards() + testthat::expect_identical(cards_pre, cards_post) + + cards_pre <- reporter$get_cards() + session$setInputs(`card_down_id` = 2L) + cards_post <- reporter$get_cards() + testthat::expect_identical(cards_pre, cards_post) + } + ) +}) + +testthat::test_that("reporter_previewer_srv - card up and down compensate", { + shiny::testServer( + reporter_previewer_srv, + args = list(reporter = reporter), + expr = { + cards_pre <- reporter$get_cards() + session$setInputs(`card_up_id` = 2L) + session$setInputs(`card_down_id` = 1L) + cards_post <- reporter$get_cards() + testthat::expect_equal(cards_pre, cards_post) + } + ) +}) + +testthat::test_that("reporter_previewer_srv - card down", { + shiny::testServer( + reporter_previewer_srv, + args = list(reporter = reporter), + expr = { + cards_pre <- reporter$get_cards() + session$setInputs(`card_down_id` = 1L) + cards_post <- reporter$get_cards() + testthat::expect_equivalent(cards_pre, cards_post[2:1]) + } + ) +}) + +testthat::test_that("reporter_previewer_srv - card up", { + shiny::testServer( + reporter_previewer_srv, + args = list(reporter = reporter), + expr = { + cards_pre <- reporter$get_cards() + session$setInputs(`card_up_id` = 2L) + cards_post <- reporter$get_cards() + testthat::expect_equivalent(cards_pre, cards_post[2:1]) + } + ) +}) + +testthat::test_that("reporter_previewer_ui - returns a tagList", { + testthat::expect_true( + inherits(reporter_previewer_ui("sth"), c("shiny.tag")) + ) +}) diff --git a/tests/testthat/test-Renderer.R b/tests/testthat/test-Renderer.R index f7de35bc..92dabc6d 100644 --- a/tests/testthat/test-Renderer.R +++ b/tests/testthat/test-Renderer.R @@ -10,8 +10,9 @@ text_block1 <- TextBlock$new()$set_content("text")$set_style("header2") text_block2 <- TextBlock$new()$set_content("text") png_path <- system.file("img", "Rlogo.png", package = "png") picture_block <- PictureBlock$new()$set_content(ggplot2::ggplot(iris)) +table_block <- TableBlock$new()$set_content(iris) newpage_block <- NewpageBlock$new() -blocks <- list(text_block1, text_block2, picture_block, newpage_block) +blocks <- list(text_block1, text_block2, picture_block, table_block, newpage_block) testthat::test_that("renderRmd asserts the argument is a list of TextBlocks/PictureBlock/NewpageBlock/TableBlock", { renderer <- Renderer$new() diff --git a/tests/testthat/test-ReportCard.R b/tests/testthat/test-ReportCard.R index 0d12033d..8a9ebbf8 100644 --- a/tests/testthat/test-ReportCard.R +++ b/tests/testthat/test-ReportCard.R @@ -136,3 +136,8 @@ testthat::test_that("The deep copy constructor copies the non ContentBlock objec testthat::expect_equal(card_copy$get_content()[[1]], card$get_content()[[1]]) testthat::expect_equal(card_copy$get_content()[[3]], card$get_content()[[3]]) }) + +testthat::test_that("setting and getting a name to the ReportCard", { + testthat::expect_identical(ReportCard$new()$set_name("NAME")$get_name(), "NAME") + testthat::expect_identical(ReportCard$new()$get_name(), character(0)) +}) diff --git a/tests/testthat/test-Reporter.R b/tests/testthat/test-Reporter.R index 5cfb90f3..3c23fa8f 100644 --- a/tests/testthat/test-Reporter.R +++ b/tests/testthat/test-Reporter.R @@ -43,11 +43,11 @@ testthat::test_that("get_blocks by default adds NewpageBlock$new() between cards expect_equal(reporter$get_blocks(), reporter_blocks2) }) -reporter <- Reporter$new() +reporter2 <- Reporter$new() testthat::test_that("get_blocks and get_cards return empty list by default", { - expect_identical(reporter$get_blocks(), list()) - expect_identical(reporter$get_cards(), list()) + expect_identical(reporter2$get_blocks(), list()) + expect_identical(reporter2$get_cards(), list()) }) testthat::test_that("The deep copy constructor copies the content files to new files", { @@ -59,3 +59,18 @@ testthat::test_that("The deep copy constructor copies the content files to new f testthat::expect_false(original_content_file == copied_content_file) }) + + +testthat::test_that("swap_cards", { + reporter1a <- reporter$clone() + reporter1b <- reporter$clone() + testthat::expect_equal(reporter1a$swap_cards(1L, 2L), reporter1b$swap_cards(2L, 1L)) +}) + +testthat::test_that("reactive_add_card", { + reporter <- Reporter$new() + testthat::expect_error(reporter$get_reactive_add_card()) + testthat::expect_identical(isolate(reporter$get_reactive_add_card()), 0) + reporter$append_cards(list(card1)) + testthat::expect_identical(isolate(reporter$get_reactive_add_card()), 1L) +}) diff --git a/tests/testthat/test-ResetModule.R b/tests/testthat/test-ResetModule.R index 0d7ed9f1..2111e90d 100644 --- a/tests/testthat/test-ResetModule.R +++ b/tests/testthat/test-ResetModule.R @@ -15,7 +15,7 @@ reporter$append_cards(list(card1)) testthat::test_that("simple_reporter_srv - reset a reporter", { shiny::testServer( simple_reporter_srv, - args = list(reporter = reporter, notification = FALSE, card_fun = card_fun), + args = list(reporter = reporter, card_fun = card_fun), expr = { testthat::expect_identical(reporter$get_cards(), list(card1)) session$setInputs(`reset_button_simple-reset_reporter` = 0) diff --git a/tests/testthat/test-SimpleReporter.R b/tests/testthat/test-SimpleReporter.R index 525f7dd8..61077455 100644 --- a/tests/testthat/test-SimpleReporter.R +++ b/tests/testthat/test-SimpleReporter.R @@ -15,7 +15,7 @@ reporter$append_cards(list(card_fun0())) testthat::test_that("simple_reporter_srv - render and downlaod a document", { shiny::testServer( simple_reporter_srv, - args = list(reporter = reporter, notification = FALSE, card_fun = card_fun0), + args = list(reporter = reporter, card_fun = card_fun0), expr = { session$setInputs(`download_button_simple` = 0) session$setInputs(`download_button_simple-output` = "html_document") @@ -42,7 +42,7 @@ reporter <- Reporter$new() testthat::test_that("simple_reporter_srv - add a Card to Reporter", { shiny::testServer( simple_reporter_srv, - args = list(reporter = reporter, notification = FALSE, card_fun = card_fun0), + args = list(reporter = reporter, card_fun = card_fun0), expr = { card_len <- length(card_fun0()$get_content()) session$setInputs(`add_report_card_simple-add_report_card_button` = 0) diff --git a/tests/testthat/test-TableBlock.R b/tests/testthat/test-TableBlock.R index c0649f6c..9ba1ea01 100644 --- a/tests/testthat/test-TableBlock.R +++ b/tests/testthat/test-TableBlock.R @@ -13,7 +13,7 @@ testthat::test_that("set_content accepts a table object", { testthat::test_that("set_content asserts the argument is a plot", { block <- TableBlock$new() - testthat::expect_error(block$set_content(7), regexp = "Must inherit from class 'data.frame'/'DT'/'rtables'") + testthat::expect_error(block$set_content(7), regexp = "Must inherit from class 'data.frame'/'rtables'") }) testthat::test_that("set_content returns the TableBlock object", { diff --git a/vignettes/previewerReporter.Rmd b/vignettes/previewerReporter.Rmd new file mode 100644 index 00000000..ef2283a3 --- /dev/null +++ b/vignettes/previewerReporter.Rmd @@ -0,0 +1,197 @@ +--- +title: "Reporter Previewer" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Reporter Previewer} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```r +library(shiny) +library(teal.reporter) +library(ggplot2) +library(rtables) +library(DT) +``` + +Reporter Previewer is a shiny module to visualize and manipulate the already added report Cards, and eventually downloading a report document. +Reporter Previewer is extended by the base modules introduced in the `simpleReporter` vignette. + +The code added to introduce the reporter is wrapped in the `### REPORTER` code blocks. + +The implementation should consist of 5 steps: + +1. Create a `tabsetPanel` with the main app and the Previewer. +2. Add modules user interface to the user interface of the app. +3. Initialize Reporter instance. +4. Create the Report Card function with two arguments: card and comment. +The function should build the Card step by step and assuming it is empty at the beginning, +the optional comment argument is a string provided by the user when the card is added. +If the comment argument is not specified then it is added automatically at the end of the Card. +This part requires the developer to use their imagination on how the document page should look like. +5. Invoke the servers with the reporter instance and the function to create the report card instance. + +```r +ui <- fluidPage( + titlePanel(""), + tabsetPanel( + tabPanel( + "main App", + tags$br(), + tags$br(), + sidebarLayout( + uiOutput("encoding"), + mainPanel( + ### REPORTER + teal.reporter::add_card_button_ui("addReportCard"), + teal.reporter::download_report_button_ui("downloadButton"), + teal.reporter::reset_report_button_ui("resetButton"), + ### + tags$br(), + tags$br(), + tabsetPanel( + id = "tabs", + tabPanel("Plot", plotOutput("dist_plot")), + tabPanel("Table", verbatimTextOutput("table")), + tabPanel("Table DataFrame", verbatimTextOutput("table2")), + tabPanel("Table DataTable", dataTableOutput("table3")) + ) + ) + ) + ), + ### REPORTER + tabPanel( + "Previewer", + reporter_previewer_ui("prev") + ) + ### + ) +) + +server <- function(input, output, session) { + output$encoding <- renderUI({ + if (input$tabs == "Plot") { + sidebarPanel( + sliderInput( + "binwidth", + "binwidth", + min = 2, + max = 10, + value = 8 + ) + ) + } else if (input$tabs %in% c("Table", "Table DataFrame", "Table DataTable")) { + sidebarPanel( + selectInput( + "stat", + label = "Statistic", + choices = c("mean", "median", "sd"), + "mean" + ) + ) + } else { + NULL + } + }) + + plot <- reactive({ + req(input$binwidth) + x <- mtcars$mpg + ggplot2::ggplot(data = mtcars, ggplot2::aes(x = mpg)) + + ggplot2::geom_histogram(binwidth = input$binwidth) + }) + + output$dist_plot <- renderPlot({ + plot() + }) + + table <- reactive({ + req(input$stat) + lyt <- basic_table() %>% + split_rows_by("Month", label_pos = "visible") %>% + analyze("Ozone", afun = eval(str2expression(input$stat))) + build_table(lyt, airquality) + }) + + output$table <- renderPrint({ + table() + }) + + table2 <- reactive({ + req(input$stat) + data <- aggregate(airquality[, c("Ozone"), drop = FALSE], list(Month = airquality$Month), get(input$stat), na.rm = TRUE) + colnames(data) <- c("Month", input$stat) + data + }) + + output$table2 <- renderPrint({ + print.data.frame(table2()) + }) + + output$table3 <- renderDataTable({ + DT::datatable(table2()) + }) + + ### REPORTER + reporter <- teal.reporter::Reporter$new() + card_fun <- function(card = ReportCard$new(), comment) { + if (input$tabs == "Plot") { + card$set_name("Plot Module") + card$append_text("My plot", "header2") + card$append_plot(plot()) + card$append_text( + paste( + c( + "x <- mtcars$mpg", + "ggplot2::ggplot(data = mtcars, ggplot2::aes(x = mpg)) +", + paste0("ggplot2::geom_histogram(binwidth = ", input$binwidth, ")") + ), + collapse = "\n" + ), + "verbatim") + } else if (input$tabs == "Table") { + card$set_name("Table Module rtables") + card$append_text("My rtables", "header2") + card$append_table(table()) + card$append_text( + paste( + c( + 'lyt <- rtables::basic_table() %>%', + 'rtables::split_rows_by("Month", label_pos = "visible") %>%', + paste0('rtables::analyze("Ozone", afun = ', input$stat, ')'), + 'rtables::build_table(lyt, airquality)' + ), + collapse = "\n" + ), "verbatim") + } else if (input$tabs %in% c("Table DataFrame", "Table DataTable")) { + card$set_name("Table Module DF") + card$append_text("My Table DF", "header2") + card$append_table(table2()) + card$append_text( + paste0( + c( + 'data <- aggregate(airquality[, c("Ozone"), drop = FALSE], list(Month = airquality$Month), ', + input$stat, + ', na.rm = TRUE)\n', + 'colnames(data) <- c("Month", ', paste0('"', input$stat, '"'), ')\n', + 'data' + ), collapse = "" + ), "verbatim") + } + if (!comment == "") { + card$append_text("Comment", "header3") + card$append_text(comment) + } + card + } + + teal.reporter::add_card_button_srv("addReportCard", reporter = reporter, card_fun = card_fun) + teal.reporter::download_report_button_srv("downloadButton", reporter = reporter) + teal.reporter::reset_report_button_srv("resetButton", reporter) + teal.reporter::reporter_previewer_srv("prev", reporter) + ### +} + +shinyApp(ui = ui, server = server) +``` diff --git a/vignettes/simpleReporter.Rmd b/vignettes/simpleReporter.Rmd index 5945f471..8615c7ee 100644 --- a/vignettes/simpleReporter.Rmd +++ b/vignettes/simpleReporter.Rmd @@ -26,7 +26,7 @@ The Simple Reporter module consists of two separate modules one for each of the The code added to introduce the simple reporter is wrapped in the `### REPORTER` code blocks. -The implementation should consist of 5 steps: +The implementation should consist of 4 steps: 1. Add modules user interface to the user interface of the app. 2. Initialize Reporter instance. @@ -50,6 +50,7 @@ ui <- fluidPage( ### REPORTER teal.reporter::add_card_button_ui("addReportCard"), teal.reporter::download_report_button_ui("downloadButton"), + teal.reporter::reset_report_button_ui("resetButton"), ### tags$br(), tags$br(), @@ -125,6 +126,7 @@ server <- function(input, output, session) { teal.reporter::add_card_button_srv("addReportCard", reporter = reporter, card_fun = card_fun) teal.reporter::download_report_button_srv("downloadButton", reporter = reporter) + teal.reporter::reset_report_button_srv("resetButton", reporter) ### }