diff --git a/.lintr b/.lintr index 34473d27..0a0bb22f 100644 --- a/.lintr +++ b/.lintr @@ -1,5 +1,6 @@ linters: linters_with_defaults( line_length_linter = line_length_linter(120), cyclocomp_linter = NULL, - object_usage_linter = NULL + object_usage_linter = NULL, + indentation_linter = NULL ) diff --git a/NEWS.md b/NEWS.md index 17127b47..7471c1be 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # teal.reporter 0.3.1.9001 +### Enhancements + +* Report cards are now included in bookmarks. When using the `shiny` bookmarking mechanism, present report cards will be available in the restored application. + # teal.reporter 0.3.1 ### Enhancements diff --git a/R/AddCardModule.R b/R/AddCardModule.R index d5646d55..91825da2 100644 --- a/R/AddCardModule.R +++ b/R/AddCardModule.R @@ -88,125 +88,129 @@ add_card_button_srv <- function(id, reporter, card_fun) { checkmate::assert_class(reporter, "Reporter") checkmate::assert_subset(names(formals(card_fun)), c("card", "comment", "label"), empty.ok = TRUE) - shiny::moduleServer( - id, - function(input, output, session) { - ns <- session$ns - add_modal <- function() { - shiny::modalDialog( - easyClose = TRUE, - shiny::tags$h3("Add a Card to the Report"), - shiny::tags$hr(), - shiny::textInput( - ns("label"), - "Card Name", - value = "", - placeholder = "Add the card title here", - width = "100%" - ), - shiny::textAreaInput( - ns("comment"), - "Comment", - value = "", - placeholder = "Add a comment here...", - width = "100%" - ), - shiny::tags$script( - shiny::HTML( - sprintf( - " + shiny::moduleServer(id, function(input, output, session) { + shiny::setBookmarkExclude(c( + "add_report_card_button", "download_button", "reset_reporter", + "add_card_ok", "download_data", "reset_reporter_ok", + "label", "comment" + )) + + ns <- session$ns + + add_modal <- function() { + shiny::modalDialog( + easyClose = TRUE, + shiny::tags$h3("Add a Card to the Report"), + shiny::tags$hr(), + shiny::textInput( + ns("label"), + "Card Name", + value = "", + placeholder = "Add the card title here", + width = "100%" + ), + shiny::textAreaInput( + ns("comment"), + "Comment", + value = "", + placeholder = "Add a comment here...", + width = "100%" + ), + shiny::tags$script( + shiny::HTML( + sprintf( + " $('#shiny-modal').on('shown.bs.modal', () => { $('#%s').focus() }) ", - ns("label") - ) + ns("label") ) + ) + ), + footer = shiny::div( + shiny::tags$button( + type = "button", + class = "btn btn-secondary", + `data-dismiss` = "modal", + `data-bs-dismiss` = "modal", + NULL, + "Cancel" ), - footer = shiny::div( - shiny::tags$button( - type = "button", - class = "btn btn-secondary", - `data-dismiss` = "modal", - `data-bs-dismiss` = "modal", - NULL, - "Cancel" - ), - shiny::tags$button( - id = ns("add_card_ok"), - type = "button", - class = "btn btn-primary action-button", - `data-val` = shiny::restoreInput(id = ns("add_card_ok"), default = NULL), - NULL, - "Add Card" - ) + shiny::tags$button( + id = ns("add_card_ok"), + type = "button", + class = "btn btn-primary action-button", + `data-val` = shiny::restoreInput(id = ns("add_card_ok"), default = NULL), + NULL, + "Add Card" ) ) - } - - shiny::observeEvent(input$add_report_card_button, { - shiny::showModal(add_modal()) - }) + ) + } - # the add card button is disabled when clicked to prevent multi-clicks - # please check the ui part for more information - shiny::observeEvent(input$add_card_ok, { - card_fun_args_nams <- names(formals(card_fun)) - has_card_arg <- "card" %in% card_fun_args_nams - has_comment_arg <- "comment" %in% card_fun_args_nams - has_label_arg <- "label" %in% card_fun_args_nams + shiny::observeEvent(input$add_report_card_button, { + shiny::showModal(add_modal()) + }) - arg_list <- list() + # the add card button is disabled when clicked to prevent multi-clicks + # please check the ui part for more information + shiny::observeEvent(input$add_card_ok, { + card_fun_args_nams <- names(formals(card_fun)) + has_card_arg <- "card" %in% card_fun_args_nams + has_comment_arg <- "comment" %in% card_fun_args_nams + has_label_arg <- "label" %in% card_fun_args_nams - if (has_comment_arg) { - arg_list <- c(arg_list, list(comment = input$comment)) - } - if (has_label_arg) { - arg_list <- c(arg_list, list(label = input$label)) - } + arg_list <- list() - if (has_card_arg) { - # The default_card is defined here because formals() returns a pairedlist object - # of formal parameter names and their default values. The values are missing - # if not defined and the missing check does not work if supplied formals(card_fun)[[1]] - default_card <- formals(card_fun)$card - card <- `if`( - missing(default_card), - ReportCard$new(), - eval(default_card, envir = environment(card_fun)) - ) - arg_list <- c(arg_list, list(card = card)) - } + if (has_comment_arg) { + arg_list <- c(arg_list, list(comment = input$comment)) + } + if (has_label_arg) { + arg_list <- c(arg_list, list(label = input$label)) + } - card <- try(do.call(card_fun, arg_list)) + if (has_card_arg) { + # The default_card is defined here because formals() returns a pairedlist object + # of formal parameter names and their default values. The values are missing + # if not defined and the missing check does not work if supplied formals(card_fun)[[1]] + default_card <- formals(card_fun)$card + card <- `if`( + missing(default_card), + ReportCard$new(), + eval(default_card, envir = environment(card_fun)) + ) + arg_list <- c(arg_list, list(card = card)) + } - if (inherits(card, "try-error")) { - msg <- paste0( - "The card could not be added to the report. ", - "Have the outputs for the report been created yet? If not please try again when they ", - "are ready. Otherwise contact your application developer" - ) - warning(msg) - shiny::showNotification( - msg, - type = "error" - ) - } else { - checkmate::assert_class(card, "ReportCard") - if (!has_comment_arg && length(input$comment) > 0 && input$comment != "") { - card$append_text("Comment", "header3") - card$append_text(input$comment) - } + card <- try(do.call(card_fun, arg_list)) - if (!has_label_arg && length(input$label) == 1 && input$label != "") { - card$set_name(input$label) - } + if (inherits(card, "try-error")) { + msg <- paste0( + "The card could not be added to the report. ", + "Have the outputs for the report been created yet? If not please try again when they ", + "are ready. Otherwise contact your application developer" + ) + warning(msg) + shiny::showNotification( + msg, + type = "error" + ) + } else { + checkmate::assert_class(card, "ReportCard") + if (!has_comment_arg && length(input$comment) > 0 && input$comment != "") { + card$append_text("Comment", "header3") + card$append_text(input$comment) + } - reporter$append_cards(list(card)) - shiny::showNotification(sprintf("The card added successfully."), type = "message") - shiny::removeModal() + if (!has_label_arg && length(input$label) == 1 && input$label != "") { + card$set_name(input$label) } - }) - } - ) + + reporter$append_cards(list(card)) + shiny::showNotification(sprintf("The card added successfully."), type = "message") + shiny::removeModal() + } + }) + }) } diff --git a/R/DownloadModule.R b/R/DownloadModule.R index c0e8f3b3..578fc777 100644 --- a/R/DownloadModule.R +++ b/R/DownloadModule.R @@ -73,85 +73,84 @@ download_report_button_srv <- function(id, ) checkmate::assert_true(rmd_yaml_args[["output"]] %in% rmd_output) - shiny::moduleServer( - id, - function(input, output, session) { - ns <- session$ns + shiny::moduleServer(id, function(input, output, session) { + shiny::setBookmarkExclude(c("download_button")) - download_modal <- function() { - nr_cards <- length(reporter$get_cards()) - downb <- shiny::tags$a( - id = ns("download_data"), - class = paste("btn btn-primary shiny-download-link", if (nr_cards) NULL else "disabled"), - style = if (nr_cards) NULL else "pointer-events: none;", - href = "", - target = "_blank", - download = NA, - shiny::icon("download"), - "Download" - ) - shiny::modalDialog( - easyClose = TRUE, - shiny::tags$h3("Download the Report"), - shiny::tags$hr(), - if (length(reporter$get_cards()) == 0) { - shiny::tags$div( - class = "mb-4", - shiny::tags$p( - class = "text-danger", - shiny::tags$strong("No Cards Added") - ) - ) - } else { - shiny::tags$div( - class = "mb-4", - shiny::tags$p( - class = "text-success", - shiny::tags$strong(paste("Number of cards: ", nr_cards)) - ), + ns <- session$ns + + download_modal <- function() { + nr_cards <- length(reporter$get_cards()) + downb <- shiny::tags$a( + id = ns("download_data"), + class = paste("btn btn-primary shiny-download-link", if (nr_cards) NULL else "disabled"), + style = if (nr_cards) NULL else "pointer-events: none;", + href = "", + target = "_blank", + download = NA, + shiny::icon("download"), + "Download" + ) + shiny::modalDialog( + easyClose = TRUE, + shiny::tags$h3("Download the Report"), + shiny::tags$hr(), + if (length(reporter$get_cards()) == 0) { + shiny::tags$div( + class = "mb-4", + shiny::tags$p( + class = "text-danger", + shiny::tags$strong("No Cards Added") ) - }, - reporter_download_inputs( - rmd_yaml_args = rmd_yaml_args, - rmd_output = rmd_output, - showrcode = any_rcode_block(reporter), - session = session - ), - footer = shiny::tagList( - shiny::tags$button( - type = "button", - class = "btn btn-secondary", - `data-dismiss` = "modal", - `data-bs-dismiss` = "modal", - NULL, - "Cancel" + ) + } else { + shiny::tags$div( + class = "mb-4", + shiny::tags$p( + class = "text-success", + shiny::tags$strong(paste("Number of cards: ", nr_cards)) ), - downb ) - ) - } - - shiny::observeEvent(input$download_button, { - shiny::showModal(download_modal()) - }) - - output$download_data <- 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.") - shinybusy::block(id = ns("download_data"), text = "", type = "dots") - input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) - names(input_list) <- names(rmd_yaml_args) - if (is.logical(input$showrcode)) global_knitr[["echo"]] <- input$showrcode - report_render_and_compress(reporter, input_list, global_knitr, file) - shinybusy::unblock(id = ns("download_data")) - }, - contentType = "application/zip" + reporter_download_inputs( + rmd_yaml_args = rmd_yaml_args, + rmd_output = rmd_output, + showrcode = any_rcode_block(reporter), + session = session + ), + footer = shiny::tagList( + shiny::tags$button( + type = "button", + class = "btn btn-secondary", + `data-dismiss` = "modal", + `data-bs-dismiss` = "modal", + NULL, + "Cancel" + ), + downb + ) ) } - ) + + shiny::observeEvent(input$download_button, { + shiny::showModal(download_modal()) + }) + + output$download_data <- 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.") + shinybusy::block(id = ns("download_data"), text = "", type = "dots") + input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) + names(input_list) <- names(rmd_yaml_args) + if (is.logical(input$showrcode)) global_knitr[["echo"]] <- input$showrcode + report_render_and_compress(reporter, input_list, global_knitr, file) + shinybusy::unblock(id = ns("download_data")) + }, + contentType = "application/zip" + ) + }) } #' Render the report diff --git a/R/Previewer.R b/R/Previewer.R index 5e0a81f4..45fce4c7 100644 --- a/R/Previewer.R +++ b/R/Previewer.R @@ -6,6 +6,8 @@ #' and interact with report cards that have been added to a report. #' It includes a previewer interface to see the cards and options to modify the report before downloading. #' +#' Cards are saved by the `shiny` bookmarking mechanism. +#' #' For more details see the vignette: `vignette("previewerReporter", "teal.reporter")`. #' #' @details `r global_knitr_details()` @@ -77,133 +79,140 @@ reporter_previewer_srv <- function(id, ) checkmate::assert_true(rmd_yaml_args[["output"]] %in% rmd_output) - shiny::moduleServer( - id, - function(input, output, session) { - ns <- session$ns + shiny::moduleServer(id, function(input, output, session) { + shiny::setBookmarkExclude(c( + "card_remove_id", "card_down_id", "card_up_id", "remove_card_ok", "showrcode", "download_data_prev" + )) + session$onBookmark(function(state) { + state$values$report_cards <- reporter$get_cards() + }) + session$onRestored(function(state) { + reporter$append_cards(state$values$report_cards) + }) - reset_report_button_srv("resetButtonPreviewer", reporter) + ns <- session$ns - output$encoding <- shiny::renderUI({ - reporter$get_reactive_add_card() - shiny::tagList( - shiny::tags$h3("Download the Report"), - shiny::tags$hr(), - reporter_download_inputs( - rmd_yaml_args = rmd_yaml_args, - rmd_output = rmd_output, - showrcode = any_rcode_block(reporter), - session = session - ), - htmltools::tagAppendAttributes( - shiny::tags$a( - id = ns("download_data_prev"), - class = "btn btn-primary shiny-download-link", - href = "", - target = "_blank", - download = NA, - shiny::tags$span("Download Report", shiny::icon("download")) - ), - class = if (length(reporter$get_cards())) "" else "disabled" + reset_report_button_srv("resetButtonPreviewer", reporter) + + output$encoding <- shiny::renderUI({ + reporter$get_reactive_add_card() + shiny::tagList( + shiny::tags$h3("Download the Report"), + shiny::tags$hr(), + reporter_download_inputs( + rmd_yaml_args = rmd_yaml_args, + rmd_output = rmd_output, + showrcode = any_rcode_block(reporter), + session = session + ), + htmltools::tagAppendAttributes( + shiny::tags$a( + id = ns("download_data_prev"), + class = "btn btn-primary shiny-download-link", + href = "", + target = "_blank", + download = NA, + shiny::tags$span("Download Report", shiny::icon("download")) ), - reset_report_button_ui(ns("resetButtonPreviewer"), label = "Reset Report") - ) - }) + class = if (length(reporter$get_cards())) "" else "disabled" + ), + reset_report_button_ui(ns("resetButtonPreviewer"), label = "Reset Report") + ) + }) - output$pcards <- shiny::renderUI({ - reporter$get_reactive_add_card() - input$card_remove_id - input$card_down_id - input$card_up_id + 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() + cards <- reporter$get_cards() - if (length(cards)) { - shiny::tags$div( - class = "panel-group accordion", - id = "reporter_previewer_panel", - lapply(seq_along(cards), function(ic) { - previewer_collapse_item(ic, cards[[ic]]$get_name(), cards[[ic]]$get_content()) - }) - ) - } else { - shiny::tags$div( - id = "reporter_previewer_panel_no_cards", - shiny::tags$p( - class = "text-danger mt-4", - shiny::tags$strong("No Cards added") - ) + if (length(cards)) { + shiny::tags$div( + class = "panel-group accordion", + id = "reporter_previewer_panel", + lapply(seq_along(cards), function(ic) { + previewer_collapse_item(ic, cards[[ic]]$get_name(), cards[[ic]]$get_content()) + }) + ) + } else { + shiny::tags$div( + id = "reporter_previewer_panel_no_cards", + shiny::tags$p( + class = "text-danger mt-4", + shiny::tags$strong("No Cards added") ) - } - }) + ) + } + }) - shiny::observeEvent(input$card_remove_id, { - shiny::showModal( - shiny::modalDialog( - title = "Remove the Report Card", - shiny::tags$p( - shiny::HTML( - sprintf( - "Do you really want to remove the card %s from the Report?", - input$card_remove_id - ) + shiny::observeEvent(input$card_remove_id, { + shiny::showModal( + shiny::modalDialog( + title = "Remove the Report Card", + shiny::tags$p( + shiny::HTML( + sprintf( + "Do you really want to remove the card %s from the Report?", + input$card_remove_id ) - ), - footer = shiny::tagList( - shiny::tags$button( - type = "button", - class = "btn btn-secondary", - `data-dismiss` = "modal", - `data-bs-dismiss` = "modal", - NULL, - "Cancel" - ), - shiny::actionButton(ns("remove_card_ok"), "OK", class = "btn-danger") ) + ), + footer = shiny::tagList( + shiny::tags$button( + type = "button", + class = "btn btn-secondary", + `data-dismiss` = "modal", + `data-bs-dismiss` = "modal", + NULL, + "Cancel" + ), + shiny::actionButton(ns("remove_card_ok"), "OK", class = "btn-danger") ) ) - }) + ) + }) - shiny::observeEvent(input$remove_card_ok, { - reporter$remove_cards(input$card_remove_id) - shiny::removeModal() - }) + shiny::observeEvent(input$remove_card_ok, { + reporter$remove_cards(input$card_remove_id) + shiny::removeModal() + }) - 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_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) - ) - } - }) + 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.") - shinybusy::block(id = ns("download_data_prev"), text = "", type = "dots") - input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) - names(input_list) <- names(rmd_yaml_args) - if (is.logical(input$showrcode)) global_knitr[["echo"]] <- input$showrcode - report_render_and_compress(reporter, input_list, global_knitr, file) - shinybusy::unblock(id = ns("download_data_prev")) - }, - contentType = "application/zip" - ) - } - ) + 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.") + shinybusy::block(id = ns("download_data_prev"), text = "", type = "dots") + input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) + names(input_list) <- names(rmd_yaml_args) + if (is.logical(input$showrcode)) global_knitr[["echo"]] <- input$showrcode + report_render_and_compress(reporter, input_list, global_knitr, file) + shinybusy::unblock(id = ns("download_data_prev")) + }, + contentType = "application/zip" + ) + }) } #' @noRd diff --git a/R/ResetModule.R b/R/ResetModule.R index 59a74afc..d7f214ae 100644 --- a/R/ResetModule.R +++ b/R/ResetModule.R @@ -45,42 +45,41 @@ reset_report_button_ui <- function(id, label = NULL) { reset_report_button_srv <- function(id, reporter) { checkmate::assert_class(reporter, "Reporter") - shiny::moduleServer( - id, - function(input, output, session) { - ns <- session$ns - nr_cards <- length(reporter$get_cards()) + shiny::moduleServer(id, function(input, output, session) { + shiny::setBookmarkExclude(c("reset_reporter")) + ns <- session$ns + nr_cards <- length(reporter$get_cards()) - shiny::observeEvent(input$reset_reporter, { - shiny::showModal( - shiny::modalDialog( - shiny::tags$h3("Reset the Report"), - shiny::tags$hr(), - shiny::tags$strong( - shiny::tags$p( - "Are you sure you want to reset the report? (This will remove ALL previously added cards)." - ) - ), - footer = shiny::tagList( - shiny::tags$button( - type = "button", - class = "btn btn-secondary", - `data-dismiss` = "modal", - `data-bs-dismiss` = "modal", - NULL, - "Cancel" - ), - shiny::actionButton(ns("reset_reporter_ok"), "Reset", class = "btn-danger") + + shiny::observeEvent(input$reset_reporter, { + shiny::showModal( + shiny::modalDialog( + shiny::tags$h3("Reset the Report"), + shiny::tags$hr(), + shiny::tags$strong( + shiny::tags$p( + "Are you sure you want to reset the report? (This will remove ALL previously added cards)." ) + ), + footer = shiny::tagList( + shiny::tags$button( + type = "button", + class = "btn btn-secondary", + `data-dismiss` = "modal", + `data-bs-dismiss` = "modal", + NULL, + "Cancel" + ), + shiny::actionButton(ns("reset_reporter_ok"), "Reset", class = "btn-danger") ) ) - }) + ) + }) - shiny::observeEvent(input$reset_reporter_ok, { - reporter$reset() - shiny::removeModal() - }) - } - ) + shiny::observeEvent(input$reset_reporter_ok, { + reporter$reset() + shiny::removeModal() + }) + }) } diff --git a/man/reporter_previewer.Rd b/man/reporter_previewer.Rd index 1d9a9024..c4667414 100644 --- a/man/reporter_previewer.Rd +++ b/man/reporter_previewer.Rd @@ -46,6 +46,8 @@ Module offers functionalities to visualize, manipulate, and interact with report cards that have been added to a report. It includes a previewer interface to see the cards and options to modify the report before downloading. +Cards are saved by the \code{shiny} bookmarking mechanism. + For more details see the vignette: \code{vignette("previewerReporter", "teal.reporter")}. } \details{