From 0c5dab7515ad9cd231d3535f7112eb590fc75bc0 Mon Sep 17 00:00:00 2001 From: "Dr. Connie Brett" Date: Thu, 28 Oct 2021 10:39:17 -0700 Subject: [PATCH] CRAN 1.0.0 release (#58) --- DESCRIPTION | 12 +- NEWS.md | 9 + R/appReset.R | 55 ++- R/bodyFooter.R | 51 ++- R/downloadFile.R | 196 +++++++---- R/downloadablePlot.R | 122 +++++-- R/downloadableTable.R | 321 ++++++++++++++---- R/fw_helpers_external.R | 121 ++++++- R/fw_helpers_internal.R | 3 +- R/generate_template.R | 125 +++++-- README.md | 11 +- cran-comments.md | 20 +- inst/fw_templ/p_blank/global.R | 3 + inst/fw_templ/p_example/global.R | 1 + inst/fw_templ/p_example/program_helpers.R | 10 +- inst/fw_templ/p_example/server_global.R | 4 + inst/fw_templ/p_example/server_local.R | 241 ++++++++++--- .../fw_templ/p_example/server_local_no_left.R | 242 ++++++++++--- inst/fw_templ/p_example/server_local_plus.R | 240 ++++++++++--- .../p_example/server_local_plus_no_left.R | 238 ++++++++++--- inst/fw_templ/p_example/ui_body.R | 12 +- inst/fw_templ/www/periscope_style.yaml | 47 +++ man/create_new_application.Rd | 10 +- man/downloadFile.Rd | 44 +-- man/downloadFileButton.Rd | 2 +- man/downloadablePlot.Rd | 29 +- man/downloadablePlotUI.Rd | 6 +- man/downloadableTable.Rd | 78 +++-- man/downloadableTableUI.Rd | 6 +- tests/testthat/_snaps/downloadable_table.md | 81 +++++ tests/testthat/_snaps/ui_functions.md | 195 +++++++++++ .../sample_app/program/data/.gitignore | 1 - .../sample_app/program/fxn/program_helpers.R | 10 +- .../sample_app/program/server_local.R | 218 ++++++++++-- tests/testthat/sample_app/program/ui_body.R | 12 +- tests/testthat/sample_app/ui.R | 2 +- .../sample_app/www/periscope_style.yaml | 47 +++ .../program/data/.gitignore | 1 - .../program/fxn/program_helpers.R | 10 +- .../program/server_local.R | 226 ++++++++++-- .../sample_app_both_sidebar/program/ui_body.R | 12 +- .../program/ui_sidebar_right.R | 63 +++- tests/testthat/sample_app_both_sidebar/ui.R | 18 +- .../www/periscope_style.yaml | 47 +++ .../program/data/.gitignore | 1 - .../program/fxn/program_helpers.R | 10 +- .../program/server_local.R | 221 ++++++++++-- .../sample_app_no_sidebar/program/ui_body.R | 12 +- tests/testthat/sample_app_no_sidebar/ui.R | 2 +- .../www/periscope_style.yaml | 47 +++ .../program/data/.gitignore | 1 - .../program/fxn/program_helpers.R | 10 +- .../program/server_local.R | 221 ++++++++++-- .../program/ui_body.R | 12 +- .../sample_app_no_sidebar_no_resetbutton/ui.R | 2 +- .../www/periscope_style.yaml | 47 +++ .../program/data/.gitignore | 1 - .../program/fxn/program_helpers.R | 10 +- .../program/server_local.R | 226 ++++++++++-- .../sample_app_r_sidebar/program/ui_body.R | 12 +- .../program/ui_sidebar_right.R | 63 +++- tests/testthat/sample_app_r_sidebar/ui.R | 18 +- .../www/periscope_style.yaml | 47 +++ tests/testthat/setup.R | 1 + tests/testthat/test_app_reset.R | 68 ++-- tests/testthat/test_body_footer.R | 29 +- tests/testthat/test_convert_application.R | 2 +- tests/testthat/test_create_new_application.R | 41 +-- tests/testthat/test_download_file.R | 80 ++++- tests/testthat/test_downloadable_plot.R | 27 +- tests/testthat/test_downloadable_table.R | 156 ++++++++- tests/testthat/test_ui_functions.R | 286 ++-------------- tests/testthat/test_ui_misc_functions.R | 5 + vignettes/downloadFile-module.Rmd | 48 +-- vignettes/downloadablePlot-module.Rmd | 34 +- vignettes/downloadableTable-module.Rmd | 86 +++-- vignettes/figures/downloadableTable-2.jpg | Bin 0 -> 82394 bytes vignettes/figures/periscope_style.jpg | Bin 0 -> 218125 bytes vignettes/figures/sample_app_styling.jpg | Bin 0 -> 71237 bytes vignettes/new-application.Rmd | 33 +- 80 files changed, 3933 insertions(+), 1128 deletions(-) create mode 100644 inst/fw_templ/www/periscope_style.yaml create mode 100644 tests/testthat/_snaps/ui_functions.md create mode 100644 tests/testthat/sample_app/www/periscope_style.yaml create mode 100644 tests/testthat/sample_app_both_sidebar/www/periscope_style.yaml create mode 100644 tests/testthat/sample_app_no_sidebar/www/periscope_style.yaml create mode 100644 tests/testthat/sample_app_no_sidebar_no_resetbutton/www/periscope_style.yaml create mode 100644 tests/testthat/sample_app_r_sidebar/www/periscope_style.yaml create mode 100644 vignettes/figures/downloadableTable-2.jpg create mode 100644 vignettes/figures/periscope_style.jpg create mode 100644 vignettes/figures/sample_app_styling.jpg diff --git a/DESCRIPTION b/DESCRIPTION index d3d9d9c..27888a1 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: periscope Type: Package Title: Enterprise Streamlined 'Shiny' Application Framework -Version: 0.6.3 +Version: 1.0.0 Authors@R: c( person("Constance", "Brett", email="connie@aggregate-genius.com", role = c("aut", "cre")), person("Isaac", "Neuhaus", role = "aut", comment = "canvasXpress JavaScript Library Maintainer"), @@ -20,7 +20,7 @@ Language: en-US Depends: R (>= 3.5) Imports: - shiny (>= 1.1), + shiny (>= 1.5), shinydashboard (>= 0.5), shinyBS (>= 0.61), lubridate (>= 1.6), @@ -28,7 +28,10 @@ Imports: writexl (>= 1.3), ggplot2 (>= 2.2), methods, - utils + utils, + fresh, + yaml, + grDevices RoxygenNote: 7.1.1 Suggests: knitr, @@ -36,5 +39,6 @@ Suggests: shinydashboardPlus, testthat (>= 3.0), canvasXpress, - openxlsx (>= 3.0) + openxlsx (>= 3.0), + colourpicker VignetteBuilder: knitr diff --git a/NEWS.md b/NEWS.md index 7d923e9..4721301 100755 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,14 @@ #Revisions and Change Log +### v1.0.0 **major version release** +* Updated to the latest shiny modules paradigm from the old one +* Support for downloadableTable DT options +* Styling changed to use the fresh package +* Updated example applications, documentation, vignettes, etc. +* Ensured apps created with the older version of this package will work when the package is upgraded + +--- + ### v0.6.3 * Bugfix for the framework to not require shinydashboardPlus unless a right sidebar is in use * Bugfixes for the sample applications diff --git a/R/appReset.R b/R/appReset.R index fe99f89..03bbf84 100755 --- a/R/appReset.R +++ b/R/appReset.R @@ -25,21 +25,57 @@ } # Module Server Function -.appReset <- function(input, output, session, logger) { +.appReset <- function(..., logger) { + call <- match.call() + params <- list(...) + param_index <- 1 + params_length <- length(params) + old_style_call <- call[[1]] == "module" || "periscope" %in% as.character(call[[1]]) + + if (old_style_call) { + input <- params[[param_index]] + param_index <- param_index + 1 + output <- params[[param_index]] + param_index <- param_index + 1 + session <- params[[param_index]] + param_index <- param_index + 1 + } else { + id <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(logger) && params_length >= param_index) { + logger <- params[[param_index]] + } + + if (old_style_call) { + app_reset(input, output, session, logger) + } + else { + shiny::moduleServer( + id, + function(input, output, session) { + app_reset(input, output, session, logger) + }) + } +} + + +app_reset <- function(input, output, session, logger) { shiny::observe({ pending <- shiny::isolate(input$resetPending) waittime <- shiny::isolate(.g_opts$reset_wait) - + if (is.null(pending)) { return() # there is no reset button on the UI for the app } - + if (input$resetButton && !(pending)) { # reset initially requested logwarn(paste("Application Reset requested by user. ", - "Resetting in ", (waittime / 1000), - "seconds."), - logger = logger) + "Resetting in ", (waittime / 1000), + "seconds."), + logger = logger) shinyBS::createAlert( session, "sidebarAdvancedAlert", style = "danger", @@ -60,8 +96,8 @@ else if (!input$resetButton && pending) { # reset cancelled by pushing the button again loginfo("Application Reset cancelled by user.", - logger = logger) - + logger = logger) + shinyBS::createAlert( session, "sidebarAdvancedAlert", style = "success", @@ -82,4 +118,5 @@ session$reload() } }) -} + +} diff --git a/R/bodyFooter.R b/R/bodyFooter.R index d413fad..0c2684d 100755 --- a/R/bodyFooter.R +++ b/R/bodyFooter.R @@ -19,27 +19,62 @@ # Module Server Function -.bodyFooter <- function(input, output, session, logdata) { - output$dt_userlog <- shiny::renderTable({ +.bodyFooter <- function(..., logdata) { + call <- match.call() + params <- list(...) + param_index <- 1 + params_length <- length(params) + old_style_call <- call[[1]] == "module" || "periscope" %in% as.character(call[[1]]) + + if (old_style_call) { + input <- params[[param_index]] + param_index <- param_index + 1 + output <- params[[param_index]] + param_index <- param_index + 1 + session <- params[[param_index]] + param_index <- param_index + 1 + } else { + id <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(logdata) && params_length >= param_index) { + logdata <- params[[param_index]] + } + + if (old_style_call) { + body_footer(input, output, session, logdata) + } + else { + shiny::moduleServer( + id, + function(input, output, session) { + body_footer(input, output, session, logdata) + }) + } +} +body_footer <- function(input, output, session, logdata) { + output$dt_userlog <- shiny::renderTable({ + lines <- logdata() if (is.null(lines) || length(lines) == 0) { return() } - + out1 <- data.frame(orig = lines, stringsAsFactors = F) loc1 <- regexpr("\\[", out1$orig) loc2 <- regexpr("\\]", out1$orig) - + out1$logname <- substr(out1$orig, 1, loc1 - 1) - + out1$timestamp <- substr(out1$orig, loc1 + 1, loc2 - 1) out1$timestamp <- lubridate::parse_date_time(out1$timestamp, "YmdHMS") - + out1$action <- substring(out1$orig, loc2 + 1) out1$action <- trimws(out1$action, "both") - + data.frame(action = out1$action, time = format(out1$timestamp, format = .g_opts$datetime.fmt)) - }) + }) } diff --git a/R/downloadFile.R b/R/downloadFile.R index ff3ff5d..ff652d9 100755 --- a/R/downloadFile.R +++ b/R/downloadFile.R @@ -26,7 +26,7 @@ #' @section Shiny Usage: #' Call this function at the place in ui.R where the button should be placed. #' -#' It is paired with a call to \code{shiny::callModule(downloadFile, id, ...)} +#' It is paired with a call to \code{downloadFile(id, ...)} #' in server.R #' #' @seealso \link[periscope]{downloadFile} @@ -52,7 +52,7 @@ downloadFileButton <- function(id, hovertext = NULL) { ns <- shiny::NS(id) output <- "" - + if (length(downloadtypes) > 1) { # create dropdown list dropdown <- list() @@ -60,30 +60,30 @@ downloadFileButton <- function(id, dropdown <- list(dropdown, shiny::tags$li( shiny::downloadLink( - ns(item), - label = item, - class = "periscope-download-choice"))) + ns(item), + label = item, + class = "periscope-download-choice"))) } dropdown <- shiny::tagList(dropdown) - + # button with dropdown list output <- shiny::span( - class = "btn-group", - shinyBS::bsButton( - inputId = ns("downloadFileList"), - label = NULL, - icon = shiny::icon("files-o", lib = "font-awesome"), - type = "action", - class = "dropdown-toggle periscope-download-btn", - `data-toggle` = "dropdown", - `aria-haspopup` = "true", - `aria-expanded` = "false"), - shiny::tags$ul(class = "dropdown-menu", - id = ns("testList"), - dropdown), - shinyBS::bsTooltip(id = ns("downloadFileList"), - hovertext, - placement = "top")) + class = "btn-group", + shinyBS::bsButton( + inputId = ns("downloadFileList"), + label = NULL, + icon = shiny::icon("files-o", lib = "font-awesome"), + type = "action", + class = "dropdown-toggle periscope-download-btn", + `data-toggle` = "dropdown", + `aria-haspopup` = "true", + `aria-expanded` = "false"), + shiny::tags$ul(class = "dropdown-menu", + id = ns("testList"), + dropdown), + shinyBS::bsTooltip(id = ns("downloadFileList"), + hovertext, + placement = "top")) } else { # single button - no dropdown @@ -103,11 +103,8 @@ downloadFileButton <- function(id, #' Server-side function for the downloadFileButton. This is a custom #' high-functionality button for file downloads supporting single or multiple #' download types. The server function is used to provide the data for download. -#' -#' @param input provided by \code{shiny::callModule} -#' @param output provided by \code{shiny::callModule} -#' @param session provided by \code{shiny::callModule} -#' \cr \cr +#' @param ... free parameters list for shiny to pass session variables based on the module call(session, input, output) +#' variables. \emph{Note}: The first argument of this function must be the ID of the Module's UI element #' @param logger logger to use #' @param filenameroot the base text used for user-downloaded file - can be #' either a character string or a reactive expression that returns a character @@ -123,72 +120,139 @@ downloadFileButton <- function(id, #' This function is not called directly by consumers - it is accessed in #' server.R using the same id provided in \code{downloadFileButton}: #' -#' \strong{\code{callModule(downloadFile, id, logger, filenameroot, datafxns)}} +#' \strong{\code{downloadFile(id, logger, filenameroot, datafxns)}} #' #' @seealso \link[periscope]{downloadFileButton} #' @seealso \link[periscope]{downloadFile_ValidateTypes} #' @seealso \link[periscope]{downloadFile_AvailableTypes} -#' @seealso \link[shiny]{callModule} #' #' @examples #' # Inside server_local.R #' #' #single download type -#' # callModule(downloadFile, -#' # "object_id1", -#' # logger = ss_userAction.Log, -#' # filenameroot = "mydownload1", -#' # datafxns = list(csv = mydatafxn1), -#' # aspectratio = 1) +#' # downloadFile("object_id1", +#' # logger = ss_userAction.Log, +#' # filenameroot = "mydownload1", +#' # datafxns = list(csv = mydatafxn1), +#' # aspectratio = 1) #' #' #multiple download types -#' # callModule(downloadFile, -#' # "object_id2", -#' # logger = ss_userAction.Log, -#' # filenameroot = "mytype2", -#' # datafxns = list(csv = mydatafxn1, xlsx = mydatafxn2), -#' # aspectratio = 1) +#' # downloadFile("object_id2", +#' # logger = ss_userAction.Log, +#' # filenameroot = "mytype2", +#' # datafxns = list(csv = mydatafxn1, xlsx = mydatafxn2), +#' # aspectratio = 1) #' #' @export -downloadFile <- function(input, output, session, logger, - filenameroot, datafxns = list(), +downloadFile <- function(..., + logger, + filenameroot, + datafxns = list(), aspectratio = 1) { + call <- match.call() + params <- list(...) + param_index <- 1 + params_length <- length(params) + old_style_call <- call[[1]] == "module" || "periscope" %in% as.character(call[[1]]) + + if (old_style_call) { + input <- params[[param_index]] + param_index <- param_index + 1 + output <- params[[param_index]] + param_index <- param_index + 1 + session <- params[[param_index]] + param_index <- param_index + 1 + } else { + id <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(logger) && params_length >= param_index) { + logger <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(filenameroot) && params_length >= param_index) { + filenameroot <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(datafxns) && params_length >= param_index) { + datafxns <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(aspectratio) && params_length >= param_index) { + aspectratio <- params[[param_index]] + param_index <- param_index + 1 + } + + if (old_style_call) { + download_file(input, + output, + session, + logger, + filenameroot, + datafxns, + aspectratio) + } + else { + shiny::moduleServer( + id, + function(input, output, session) { + download_file(input, + output, + session, + logger, + filenameroot, + datafxns, + aspectratio) + }) + } +} +download_file <- function(input, + output, + session, + logger, + filenameroot, + datafxns = list(), + aspectratio = 1) { rootname <- filenameroot if ("character" %in% class(filenameroot)) { rootname <- shiny::reactive({filenameroot}) } - + # --- DATA processing - + output$csv <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "csv", sep = ".")}), content = function(file) { writeFile("csv", datafxns$csv(), file, logger, shiny::reactive({paste(rootname(), "csv", sep = ".")})) - }) - + }) + output$xlsx <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "xlsx", sep = ".")}), content = function(file) { writeFile("xlsx", datafxns$xlsx(), file, logger, shiny::reactive({paste(rootname(), "xlsx", sep = ".")})) - }) - + }) + output$tsv <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "tsv", sep = ".")}), content = function(file) { writeFile("tsv", datafxns$tsv(), file, logger, shiny::reactive({paste(rootname(), "tsv", sep = ".")})) - }) - + }) + output$txt <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "txt", sep = ".")}), content = function(file) { writeFile("txt", datafxns$txt(), file, logger, shiny::reactive({paste(rootname(), "txt", sep = ".")})) - }) - + }) + # filename is expected to be a reactive expression writeFile <- function(type, data, file, logger, filename) { # tabular values @@ -214,9 +278,9 @@ downloadFile <- function(input, output, session, logger, openxlsx::saveWorkbook(data, file) } else { show_rownames <- attr(data, "show_rownames") - openxlsx::write.xlsx(data, file, - asTable = TRUE, - row.names = !is.null(show_rownames) && show_rownames) + openxlsx::write.xlsx(data, file, + asTable = TRUE, + row.names = !is.null(show_rownames) && show_rownames) } } else { writexl::write_xlsx(data, file) @@ -243,42 +307,42 @@ downloadFile <- function(input, output, session, logger, warning(msg) } loginfo(paste("File downloaded in browser: <", - filename(), ">"), logger = logger) + filename(), ">"), logger = logger) } - + # --- IMAGE processing - + output$png <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "png", sep = ".")}), content = function(file) { writeImage("png", datafxns$png(), file, aspectratio, logger, shiny::reactive({paste(rootname(), "png", sep = ".")})) }) - + output$jpeg <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "jpeg", sep = ".")}), content = function(file) { writeImage("jpeg", datafxns$jpeg(), file, aspectratio, logger, shiny::reactive({paste(rootname(), "jpeg", sep = ".")})) }) - + output$tiff <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "tiff", sep = ".")}), content = function(file) { writeImage("tiff", datafxns$tiff(), file, aspectratio, logger, shiny::reactive({paste(rootname(), "tiff", sep = ".")})) }) - + output$bmp <- shiny::downloadHandler( filename = shiny::reactive({paste(rootname(), "bmp", sep = ".")}), content = function(file) { writeImage("bmp", datafxns$bmp(), file, aspectratio, logger, shiny::reactive({paste(rootname(), "bmp", sep = ".")})) }) - + writeImage <- function(type, data, file, aspectratio, logger, filename) { dim <- list(width = 7, height = 7/aspectratio, units = "in") - + #ggplot processing if (inherits(data, c("ggplot", "ggmatrix", "grob"))) { if (type %in% c("png", "jpeg", "tiff", "bmp")) { @@ -322,7 +386,7 @@ downloadFile <- function(input, output, session, logger, warning(msg) } loginfo(paste("File downloaded in browser: <", - filename(), ">"), logger = logger) + filename(), ">"), logger = logger) } } @@ -347,7 +411,7 @@ downloadFile_ValidateTypes <- function(types) { if ( !(type %in% shiny::isolate(.g_opts$data_download_types)) && !(type %in% shiny::isolate(.g_opts$plot_download_types)) ) { warning(paste0("file download list contains an invalid type <", - type, ">")) + type, ">")) } } types diff --git a/R/downloadablePlot.R b/R/downloadablePlot.R index a3448ba..a5baed9 100755 --- a/R/downloadablePlot.R +++ b/R/downloadablePlot.R @@ -28,9 +28,7 @@ #' #' @section Notes: #' When there is nothing to download in any of the linked downloadfxns the -#' button will be hidden as there is nothing to download. The linked -#' downloadfxns are set in the paired callModule (see the \strong{Shiny Usage} -#' section) +#' button will be hidden as there is nothing to download. #' #' This module is NOT compatible with the built-in (base) graphics \emph{(such as #' basic plot, etc.)} because they cannot be saved into an object and are directly @@ -39,7 +37,7 @@ #' @section Shiny Usage: #' Call this function at the place in ui.R where the plot should be placed. #' -#' Paired with a call to \code{shiny::callModule(downloadablePlot, id, ...)} +#' Paired with a call to \code{downloadablePlot(id, ...)} #' in server.R #' #' @seealso \link[periscope]{downloadablePlot} @@ -132,10 +130,8 @@ downloadablePlotUI <- function(id, #' Server-side function for the downloadablePlotUI. This is a custom #' plot output paired with a linked downloadFile button. #' -#' @param input provided by \code{shiny::callModule} -#' @param output provided by \code{shiny::callModule} -#' @param session provided by \code{shiny::callModule} -#' \cr \cr +#' @param ... free parameters list for shiny to pass session variables based on the module call(session, input, output) +#' variables. \emph{Note}: The first argument of this function must be the ID of the Module's UI element #' @param logger logger to use #' @param filenameroot the base text used for user-downloaded file - can be #' either a character string or a reactive expression returning a character @@ -157,36 +153,111 @@ downloadablePlotUI <- function(id, #' This function is not called directly by consumers - it is accessed in #' server.R using the same id provided in \code{downloadablePlotUI}: #' -#' \strong{\code{callModule(downloadablePlot, id, logger, filenameroot, +#' \strong{\code{downloadablePlot(id, logger, filenameroot, #' downloadfxns, visibleplot)}} #' #' @seealso \link[periscope]{downloadablePlotUI} -#' @seealso \link[shiny]{callModule} #' #' @examples #' # Inside server_local.R #' -#' # callModule(downloadablePlot, -#' # "object_id1", -#' # logger = ss_userAction.Log, -#' # filenameroot = "mydownload1", -#' # aspectratio = 1.33, -#' # downloadfxns = list(png = myplotfxn, tsv = mydatafxn), -#' # visibleplot = myplotfxn) +#' # downloadablePlot("object_id1", +#' # logger = ss_userAction.Log, +#' # filenameroot = "mydownload1", +#' # aspectratio = 1.33, +#' # downloadfxns = list(png = myplotfxn, tsv = mydatafxn), +#' # visibleplot = myplotfxn) #' #' @export -downloadablePlot <- function(input, output, session, logger, +downloadablePlot <- function(..., + logger, filenameroot, aspectratio = 1, downloadfxns = list(), visibleplot) { + call <- match.call() + params <- list(...) + param_index <- 1 + params_length <- length(params) + + old_style_call <- call[[1]] == "module" || "periscope" %in% as.character(call[[1]]) + + if (old_style_call) { + input <- params[[param_index]] + param_index <- param_index + 1 + output <- params[[param_index]] + param_index <- param_index + 1 + session <- params[[param_index]] + param_index <- param_index + 1 + } else { + id <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(logger) && params_length >= param_index) { + logger <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(filenameroot) && params_length >= param_index) { + filenameroot <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(aspectratio) && params_length >= param_index) { + aspectratio <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(downloadfxns) && params_length >= param_index) { + downloadfxns <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(visibleplot) && params_length >= param_index) { + visibleplot <- params[[param_index]] + param_index <- param_index + 1 + } + + if (old_style_call) { + download_plot(input, + output, + session, + logger, + filenameroot, + aspectratio, + downloadfxns, + visibleplot) + } + else { + shiny::moduleServer( + id, + function(input, output, session) { + download_plot(input, + output, + session, + logger, + filenameroot, + aspectratio, + downloadfxns, + visibleplot) + }) + } +} - shiny::callModule(downloadFile, "dplotButtonID", - logger, filenameroot, downloadfxns, aspectratio) - +download_plot <- function(input, + output, + session, + logger, + filenameroot, + aspectratio = 1, + downloadfxns = list(), + visibleplot) { + downloadFile("dplotButtonID", logger, filenameroot, downloadfxns, aspectratio) + dpInfo <- shiny::reactiveValues(visibleplot = NULL, downloadfxns = NULL) - + shiny::observe({ dpInfo$visibleplot <- visibleplot() output$dplotOutputID <- shiny::renderPlot({ @@ -197,17 +268,16 @@ downloadablePlot <- function(input, output, session, logger, plot }) }) - + shiny::observe({ if (!is.null(downloadfxns) && (length(downloadfxns) > 0)) { dpInfo$downloadfxns <- lapply(downloadfxns, do.call, list()) - + rowct <- lapply(dpInfo$downloadfxns, is.null) session$sendCustomMessage( "downloadbutton_toggle", message = list(btn = session$ns("dplotButtonDiv"), rows = sum(unlist(rowct) == FALSE)) ) } - }) - + }) } diff --git a/R/downloadableTable.R b/R/downloadableTable.R index edf77af..650bfc4 100755 --- a/R/downloadableTable.R +++ b/R/downloadableTable.R @@ -32,14 +32,12 @@ #' #' @section Notes: #' When there are no rows to download in any of the linked downloaddatafxns the -#' button will be hidden as there is nothing to download. The linked -#' downloaddatafxns are set in the paired callModule (see the \strong{Shiny Usage} -#' section) +#' button will be hidden as there is nothing to download. #' #' @section Shiny Usage: #' Call this function at the place in ui.R where the table should be placed. #' -#' Paired with a call to \code{shiny::callModule(downloadableTable, id, ...)} +#' Paired with a call to \code{downloadableTable(id, ...)} #' in server.R #' #' @seealso \link[periscope]{downloadableTable} @@ -89,11 +87,22 @@ downloadableTableUI <- function(id, #' Server-side function for the downloadableTableUI. This is a custom #' high-functionality table paired with a linked downloadFile #' button. +#' +#' Generated table can highly customized using function \code{?DT::datatable} same arguments +#' except for `options` and `selection` parameters. +#' +#' For `options` user can pass the same \code{?DT::datatable} options using the same names and +#' values one by one separated by comma. +#' +#' For `selection` parameter it can be either a function or reactive expression providing the row_ids of the +#' rows that should be selected. +#' +#' Also, user can apply the same provided \code{?DT::formatCurrency} columns formats on passed +#' dataset using format functions names as keys and their options as a list. +#' #' -#' @param input provided by \code{shiny::callModule} -#' @param output provided by \code{shiny::callModule} -#' @param session provided by \code{shiny::callModule} -#' \cr \cr +#' @param ... free parameters list to pass table customization options. See example below. +#' \emph{Note}: The first argument of this function must be the ID of the Module's UI element #' @param logger logger to use #' @param filenameroot the base text used for user-downloaded file - can be #' either a character string or a reactive expression returning a character @@ -103,64 +112,158 @@ downloadableTableUI <- function(id, #' when the table UI was created. #' @param tabledata function or reactive expression providing the table display #' data as a return value. This function should require no input parameters. -#' @param rownames whether or not to show the rownames in the table -#' @param caption table caption #' @param selection function or reactive expression providing the row_ids of the -#' rows that should be selected. +#' rows that should be selected #' #' @return Reactive expression containing the currently selected rows in the #' display table #' #' @section Notes: -#' When there are no rows to download in any of the linked downloaddatafxns the -#' button will be hidden as there is nothing to download. +#' \itemize{ +#' \item When there are no rows to download in any of the linked downloaddatafxns +#' the button will be hidden as there is nothing to download. +#' \item \code{selection} parameter has different usage than DT::datatable \code{selection} option. +#' See parameters usage section. +#' \item DT::datatable options \code{editable}, \code{width} and \code{height} are not supported +#' } #' #' @section Shiny Usage: #' This function is not called directly by consumers - it is accessed in #' server.R using the same id provided in \code{downloadableTableUI}: #' -#' \strong{\code{callModule(downloadableTable, id, logger, filenameroot, +#' \strong{\code{downloadableTable(id, logger, filenameroot, #' downloaddatafxns, tabledata, rownames, caption, selection)}} #' -#' \emph{Note}: callModule returns the reactive expression containing the +#' \emph{Note}: calling module server returns the reactive expression containing the #' currently selected rows in the display table. #' #' @seealso \link[periscope]{downloadableTableUI} -#' @seealso \link[shiny]{callModule} #' #' @examples #' # Inside server_local.R #' -#' # selectedrows <- callModule(downloadableTable, -#' # "object_id1", -#' # logger = ss_userAction.Log, -#' # filenameroot = "mydownload1", -#' # downloaddatafxns = list(csv = mydatafxn1, tsv = mydatafxn2), -#' # tabledata = mydatafxn3, -#' # rownames = FALSE, -#' # caption = "This is a great table! By: Me", -#' # selection = mydataRowIds) +#' # selectedrows <- downloadableTable( +#' # "object_id1", +#' # logger = ss_userAction.Log, +#' # filenameroot = "mydownload1", +#' # downloaddatafxns = list(csv = mydatafxn1, tsv = mydatafxn2), +#' # tabledata = mydatafxn3, +#' # rownames = FALSE, +#' # caption = "This is a great table! By: Me", +#' # selection = mydataRowIds, +#' # colnames = c("Area", "Delta", "Increase"), +#' # filter = "bottom", +#' # width = "150px", +#' # height = "50px", +#' # extensions = 'Buttons', +#' # plugins = 'natural', +#' # editable = TRUE, +#' # dom = 'Bfrtip', +#' # buttons = c('copy', 'csv', 'excel', 'pdf', 'print'), +#' # formatStyle = list(columns = c('Area'), color = 'red'), +#' # formatStyle = list(columns = c('Increase'), color = DT::styleInterval(0, c('red', 'green'))), +#' # formatCurrency = list(columns = c('Delta'))) #' #' # selectedrows is the reactive return value, captured for later use #' #' @export -downloadableTable <- function(input, output, session, logger, - filenameroot, downloaddatafxns = list(), - tabledata, rownames = TRUE, caption = NULL, +downloadableTable <- function(..., + logger, + filenameroot, + downloaddatafxns = list(), + tabledata, selection = NULL) { + call <- match.call() + params <- list(...) + param_index <- 1 + params_length <- length(params) + old_style_call <- call[[1]] == "module" || "periscope" %in% as.character(call[[1]]) + + if (old_style_call) { + input <- params[[param_index]] + param_index <- param_index + 1 + output <- params[[param_index]] + param_index <- param_index + 1 + session <- params[[param_index]] + param_index <- param_index + 1 + } + else { + id <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(logger) && params_length >= param_index) { + logger <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(filenameroot) && params_length >= param_index) { + filenameroot <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(downloaddatafxns) && params_length >= param_index) { + downloaddatafxns <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(tabledata) && params_length >= param_index) { + tabledata <- params[[param_index]] + param_index <- param_index + 1 + } + + if (missing(selection)) { + selection <- params[["selection"]] + params[["selection"]] <- NULL + } + + if (old_style_call) { + download_table(input, output, session, + logger, + filenameroot, + downloaddatafxns, + tabledata, + selection, + params[param_index:params_length]) + } + else { + shiny::moduleServer(id = params[[1]], + function(input, output, session) { + download_table(input, output, session, + logger, + filenameroot, + downloaddatafxns, + tabledata, + selection, + params[param_index:params_length]) + }) + } +} - shiny::callModule(downloadFile, "dtableButtonID", - logger, filenameroot, downloaddatafxns) +download_table <- function(input, output, session, + logger, + filenameroot, + downloaddatafxns = list(), + tabledata, + selection, + table_options) { + if (all(!is.null(selection), + is.character(selection))) { + message("'selection' parameter must be a function or reactive expression. Setting default value NULL.") + selection <- NULL + } + + downloadFile("dtableButtonID", logger, filenameroot, downloaddatafxns) session$sendCustomMessage("downloadbutton_toggle", message = list(btn = session$ns("dtableButtonDiv"), rows = -1)) - + dtInfo <- shiny::reactiveValues(selection = NULL, selected = NULL, tabledata = NULL, downloaddatafxns = NULL) - + shiny::observe({ result <- list(mode = ifelse(input$dtableSingleSelect == "TRUE", "single", "multiple")) if (!is.null(selection)) { @@ -177,61 +280,145 @@ downloadableTable <- function(input, output, session, logger, shiny::observe({ dtInfo$selected <- input$dtableOutputID_rows_selected }) - + shiny::observe({ dtInfo$tabledata <- tabledata() }) - + shiny::observe({ dtInfo$downloaddatafxns <- lapply(downloaddatafxns, do.call, list()) - + rowct <- lapply(dtInfo$downloaddatafxns, nrow) session$sendCustomMessage("downloadbutton_toggle", message = list(btn = session$ns("dtableButtonDiv"), rows = sum(unlist(rowct)))) }) - + output$dtableOutputID <- DT::renderDataTable({ sourcedata <- dtInfo$tabledata - + if (!is.null(sourcedata) && nrow(sourcedata) > 0) { row.names <- rownames(sourcedata) row.ids <- as.character(seq(1:nrow(sourcedata))) if (is.null(row.names) || identical(row.names, row.ids)) { DT_RowId <- paste0("rowid_", row.ids) - sourcedata <- cbind(DT_RowId, sourcedata) - } else { - col.names <- colnames(sourcedata) - sourcedata <- cbind(row.names, sourcedata) - colnames(sourcedata) <- c(" ", col.names) + rownames(sourcedata) <- DT_RowId } } - DT::datatable(data = sourcedata, - options = list( - deferRender = FALSE, - scrollY = input$dtableOutputHeight, - paging = FALSE, - scrollX = TRUE, - dom = '<"periscope-downloadable-table-header"f>tr', - processing = TRUE, - rowId = 1, - columnDefs = list(list(targets = 0, - visible = FALSE, - searchable = FALSE)), - searchHighlight = TRUE ), - class = paste("periscope-downloadable-table table-condensed", - "table-striped table-responsive"), - rownames = rownames, - selection = dtInfo$selection, - caption = caption, - escape = FALSE, - style = "bootstrap") + + if (is.null(table_options[["scrollY"]])) { + table_options[["scrollY"]] <- input$dtableOutputHeight + } + + table_options[["selection"]] <- dtInfo$selection + + if (is.null(table_options[["escape"]])) { + table_options[["escape"]] <- FALSE + } + + if (is.null(table_options[["rownames"]])) { + table_options[["rownames"]] <- FALSE + } + + # get format functions + format_options_idx <- which(startsWith(names(table_options), "format")) + format_options <- table_options[format_options_idx] + if (length(format_options_idx) > 0) { + dt_args <- build_datatable_arguments(table_options[-format_options_idx]) + } else { + dt_args <- build_datatable_arguments(table_options) + } + + if (is.null(sourcedata)) { + sourcedata <- data.frame() + } + + dt_args[["data"]] <- sourcedata + + tryCatch({ + dt <- do.call(DT::datatable, dt_args) + + if (length(format_options) > 0) { + dt <- format_columns(dt, format_options) + } + dt + }, + error = function(e) { + message("Could not apply DT options due to: ", e$message) + DT::datatable(sourcedata) + }) }) - - - selectedrows <- shiny::reactive({ + + + shiny::reactive({ return(shiny::isolate(dtInfo$tabledata)[dtInfo$selected, ]) - }) + }) +} + +build_datatable_arguments <- function(table_options) { + dt_args <- list() + formal_dt_args <- methods::formalArgs(DT::datatable) + dt_args[["rownames"]] <- TRUE + dt_args[["class"]] <- paste("periscope-downloadable-table table-condensed", + "table-striped table-responsive") + options <- list() + for (option in names(table_options)) { + if (option %in% c("editable", "width", "height")) { + message("DT option '", option ,"' is not supported. Ignoring it.") + next + } + + if (option %in% formal_dt_args) { + dt_args[[option]] <- table_options[[option]] + } else { + options[[option]] <- table_options[[option]] + } + } + + if (is.null(options[["deferRender"]])) { + options[["deferRender"]] <- FALSE + } + + if (is.null(options[["paging"]]) && is.null(table_options[["pageLength"]])) { + options[["paging"]] <- FALSE + } + + if (is.null(options[["scrollX"]])) { + options[["scrollX"]] <- TRUE + } + + if (is.null(options[["dom"]]) && is.null(table_options[["pageLength"]])) { + options[["dom"]] <- '<"periscope-downloadable-table-header"f>tr' + } + + if (is.null(options[["processing"]])) { + options[["processing"]] <- TRUE + } + + if (is.null(options[["rowId"]])) { + options[["rowId"]] <- 1 + } + + if (is.null(options[["searchHighlight"]])) { + options[["searchHighlight"]] <- TRUE + } + dt_args[["options"]] <- options + dt_args +} - return(selectedrows) +format_columns <- function(dt, format_options) { + for (format_idx in 1:length(format_options)) { + format_args <- format_options[[format_idx]] + format_args[["table"]] <- dt + format <- tolower(names(format_options)[format_idx]) + dt <- switch(format, + "formatstyle" = do.call(DT::formatStyle, format_args), + "formatdate" = do.call(DT::formatDate, format_args), + "formatsignif" = do.call(DT::formatSignif, format_args), + "formatround" = do.call(DT::formatRound, format_args), + "formatpercentage" = do.call(DT::formatPercentage, format_args), + "formatstring" = do.call(DT::formatString, format_args), + "formatcurrency" = do.call(DT::formatCurrency, format_args)) + } + dt } diff --git a/R/fw_helpers_external.R b/R/fw_helpers_external.R index b520cbc..15c46cb 100755 --- a/R/fw_helpers_external.R +++ b/R/fw_helpers_external.R @@ -5,9 +5,9 @@ # Framework Server Setup fw_server_setup <- function(input, output, session, logger) { logfile <- shiny::isolate(.setup_logging(session, logger)) - shiny::callModule(.bodyFooter, "footerId", logfile) + .bodyFooter("footerId", logfile) if (shiny::isolate(.g_opts$reset_button)) { - shiny::callModule(.appReset, "appResetId", logger) + .appReset("appResetId", logger) } } @@ -145,6 +145,22 @@ fw_create_right_sidebar <- function() { # Framework UI Body Creation fw_create_body <- function() { + header_color_style <- "$('.logo').css('background-color', $('.navbar').css('background-color'))" + update_right_side_bar_width <- "$('.navbar-custom-menu').on('click', + function() { + main_width = $('.main-sidebar').css('width'); + if ($('.control-sidebar-open').length != 0) { + $('.control-sidebar-open').css('width', main_width); + $('.control-sidebar-bg').css('width', main_width); + $('.control-sidebar-bg').css('right', '0px' ); + $('.control-sidebar').css('right', '0px'); + } else { + $('.control-sidebar-bg').css('right', '-' + main_width); + $('.control-sidebar').css('right', '-' + main_width); + $('.control-sidebar').css('width', '-' + main_width); + } + });" + app_info <- shiny::isolate(.g_opts$app_info) info_content <- NULL @@ -157,16 +173,97 @@ fw_create_body <- function() { app_info) } - return( - shinydashboard::dashboardBody( - shiny::tags$head( - shiny::tags$style(.framework_css()), - shiny::tags$script(.framework_js())), - info_content, - shiny::isolate(.g_opts$body_elements), - if (shiny::isolate(.g_opts$show_userlog)) { - .bodyFooterOutput("footerId") } - else {NULL} + shinydashboard::dashboardBody( + fresh::use_theme(create_theme()), + shiny::tags$head( + shiny::tags$style(.framework_css()), + shiny::tags$script(.framework_js())), + shiny::tags$script(update_right_side_bar_width), + shiny::tags$script(header_color_style), + info_content, + shiny::isolate(.g_opts$body_elements), + if (shiny::isolate(.g_opts$show_userlog)) { + .bodyFooterOutput("footerId") + } else { + NULL + } + ) + +} + +create_theme <- function() { + theme_settings <- NULL + primary_color <- NULL + sidebar_width <- NULL + sidebar_background_color <- NULL + sidebar_hover_color <- NULL + sidebar_text_color <- NULL + body_background_color <- NULL + box_color <- NULL + infobox_color <- NULL + theme_colors_keys <- c("primary_color", "sidebar_background_color", "sidebar_hover_color", + "sidebar_text_color", "body_background_color", "box_color", + "infobox_color") + + if (file.exists("www/periscope_style.yaml")) { + theme_settings <- tryCatch({ + yaml::read_yaml("www/periscope_style.yaml") + }, + error = function(e){ + warning("Could not parse 'periscope_style.yaml' due to: ", e$message) + NULL + }) + + if (!is.null(theme_settings) && is.list(theme_settings)) { + for (color in theme_colors_keys) { + if (!is_valid_color(theme_settings[[color]])) { + warning(color, " has invalid color value. Setting default color.") + theme_settings[[color]] <- NULL + } + } + + primary_color <- theme_settings[["primary_color"]] + sidebar_width <- theme_settings[["sidebar_width"]] + sidebar_background_color <- theme_settings[["sidebar_background_color"]] + sidebar_hover_color <- theme_settings[["sidebar_hover_color"]] + sidebar_text_color <- theme_settings[["sidebar_text_color"]] + body_background_color <- theme_settings[["body_background_color"]] + box_color <- theme_settings[["box_color"]] + infobox_color <- theme_settings[["infobox_color"]] + if (!is.null(sidebar_width)) { + if (any(!is.numeric(sidebar_width), sidebar_width <= 0)) { + warning("'sidebar_width' must be positive value. Setting default value.") + } else { + sidebar_width <- paste0(sidebar_width, "px") + } + } + } + } + + fresh::create_theme( + fresh::adminlte_color( + light_blue = primary_color + ), + fresh::adminlte_sidebar( + width = sidebar_width, + dark_bg = sidebar_background_color, + dark_hover_bg = sidebar_hover_color, + dark_color = sidebar_text_color + ), + fresh::adminlte_global( + content_bg = body_background_color, + box_bg = box_color, + info_box_bg = infobox_color ) ) } + +is_valid_color <- function(color) { + tryCatch({ + grDevices::col2rgb(color) + TRUE + }, + error = function(e) { + FALSE + }) +} diff --git a/R/fw_helpers_internal.R b/R/fw_helpers_internal.R index 68f9b5d..edb0c33 100755 --- a/R/fw_helpers_internal.R +++ b/R/fw_helpers_internal.R @@ -69,8 +69,7 @@ } .remove_sidebar_toggle <- function() { - shiny::tags$script(shiny::HTML("$('[class~=\"sidebar-toggle\"]').remove(); - $('[class~=\"logo\"]').css('background-color', '#3c8dbc');")) + shiny::tags$script(shiny::HTML("$('[class~=\"sidebar-toggle\"]').remove();")) } # Returns the custom css as HTML diff --git a/R/generate_template.R b/R/generate_template.R index d75e3df..9c1ddde 100755 --- a/R/generate_template.R +++ b/R/generate_template.R @@ -16,7 +16,7 @@ #' @param rightsidebar parameter to set the right sidebar. It can be TRUE/FALSE or a character #' containing the name of a shiny::icon(). #' @param leftsidebar whether the left sidebar should be enabled. -#' @param style list containing application styling properties. By default the skin is blue. +#' @param custom_theme_file location of custom theme settings yaml file. Default value is NULL. #' #' @section Name: #' The \code{name} directory must not exist in \code{location}. If the code @@ -76,6 +76,10 @@ #' inside of the call to \code{shinyServer(...)}. Anything placed in this #' file will be accessible only within a single user session.\cr #' \cr +#' \strong{\emph{name}/www/periscope_style.yaml} :\cr +#' This is the application custom styling yaml file. User can update +#' application different parts style using this file.\cr +#' \cr #' \cr #' \strong{Do not modify the following files}: \cr #' \preformatted{ @@ -108,12 +112,18 @@ #' # blank app named 'myblankapp' created in a temp dir #' create_new_application(name = 'myblankapp', location = tempdir()) #' # blank app named 'myblankapp' with a green skin created in a temp dir -#' create_new_application(name = 'myblankapp', location = tempdir(), style = list(skin = "green")) +#' create_new_application(name = 'myblankapp', location = tempdir()) #' # blank app named 'myblankapp' without a left sidebar created in a temp dir #' create_new_application(name = 'myblankapp', location = tempdir(), leftsidebar = FALSE) #' #' @export -create_new_application <- function(name, location, sampleapp = FALSE, resetbutton = TRUE, rightsidebar = FALSE, leftsidebar = TRUE, style = list(skin = "blue")) { +create_new_application <- function(name, + location, + sampleapp = FALSE, + resetbutton = TRUE, + rightsidebar = FALSE, + leftsidebar = TRUE, + custom_theme_file = NULL) { usersep <- .Platform$file.sep newloc <- paste(location, name, sep = usersep) @@ -138,22 +148,22 @@ create_new_application <- function(name, location, sampleapp = FALSE, resetbutto stop("Framework creation could not proceed, invalid type for rightsidebar, only logical or character allowed") } } - if (!is.null(style)) { - if (class(style) == "list") { - if (!identical(intersect("skin", names(style)), character(0)) && !identical(class(style$skin), "character")) { - stop("Framework creation could not proceed, invalid type for skin, only character allowed. See ?shinydashboard::dashboardPage for supported colors.") - } - } else { - stop("Framework creation could not proceed, invalid type for style, only list allowed") - } - } if (!(.g_sdp_installed) && dashboard_plus) { stop('shinyDashboardPlus is not currently installed -- it is required to generate an application with a right sidebar.') } .create_dirs(newloc, usersep) - .copy_fw_files(newloc, usersep, resetbutton, dashboard_plus, leftsidebar, right_sidebar_icon, style) + if (!is.null(custom_theme_file)) { + if (any(!is.character(custom_theme_file), + length(custom_theme_file) != 1, + custom_theme_file == "", + !file.exists(custom_theme_file))) { + warning("'custom_theme_file' must be single character value pointing to valid yaml file location. Using default values.") + custom_theme_file <- NULL + } + } + .copy_fw_files(newloc, usersep, resetbutton, dashboard_plus, leftsidebar, right_sidebar_icon, custom_theme_file, sampleapp) .copy_program_files(newloc, usersep, sampleapp, resetbutton, leftsidebar, dashboard_plus) message("Framework creation was successful.") @@ -185,7 +195,14 @@ create_new_application <- function(name, location, sampleapp = FALSE, resetbutto } # Create Framework Files ---------------------------- -.copy_fw_files <- function(newloc, usersep, resetbutton = TRUE, dashboard_plus = FALSE, leftsidebar = TRUE, right_sidebar_icon = NULL, style = list(skin = "blue")) { +.copy_fw_files <- function(newloc, + usersep, + resetbutton = TRUE, + dashboard_plus = FALSE, + leftsidebar = TRUE, + right_sidebar_icon = NULL, + custom_theme_file, + sampleapp = FALSE) { files <- c("global.R", "server.R") if (dashboard_plus) { @@ -233,19 +250,20 @@ create_new_application <- function(name, location, sampleapp = FALSE, resetbutto writeLines(ui_content, con = ui_file) close(ui_file) } - # styling - if (!is.null(style) && identical(class(style), "list") && length(style) > 0 && - !identical(intersect("skin", names(style)), character(0)) && !identical(style, list(skin = "blue"))) { - skin_value <- style$skin - ui_file <- file(paste(newloc, "ui.R", sep = usersep), open = "r+") - ui_content <- readLines(con = ui_file) - ui_content[length(ui_content)] <- paste0(substr(ui_content[length(ui_content)], 1, nchar(ui_content[length(ui_content)]) - 1), ",") - white_space <- paste(rep(" ", ifelse(dashboard_plus, nchar("dashboardPagePlus"), nchar("dashboardPage"))), collapse = "") - ui_content[length(ui_content) + 1] <- sprintf("%s skin = '%s')", white_space, skin_value) + + if (sampleapp) { + ui_file <- file(paste(newloc, "ui.R", sep = usersep), open = "r") + ui_content <- readLines(con = ui_file) + close(ui_file) + ui_content <- gsub("periscope:::fw_create_body()", + "uiOutput('body')", + ui_content, + fixed = TRUE) + ui_file <- file(paste(newloc, "ui.R", sep = usersep), open = "w") writeLines(ui_content, con = ui_file) close(ui_file) } - + #subdir copies imgs <- c("loader.gif", "tooltip.png") for (file in imgs) { @@ -256,7 +274,14 @@ create_new_application <- function(name, location, sampleapp = FALSE, resetbutto con = paste(newloc, "www", "img", file, sep = usersep)) } - return() + if (!is.null(custom_theme_file)) { + file.copy(custom_theme_file, paste(newloc, "www", "periscope_style.yaml", sep = usersep)) + } else if (sampleapp) { + file.copy(system.file("fw_templ", "www", "periscope_style.yaml", package = "periscope"), + paste(newloc, "www", "periscope_style.yaml", sep = usersep)) + } else { + create_default_theme_file(paste(newloc, "www", "periscope_style.yaml", sep = usersep)) + } } # Create Program Files ---------------------------- @@ -310,6 +335,54 @@ create_new_application <- function(name, location, sampleapp = FALSE, resetbutto con = paste(targetdir, unlist(supporting_files[file], use.names = F), file, sep = usersep)) } } +} - return() +create_default_theme_file <- function(theme_file) { + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "primary_color: \n\n", + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + "sidebar_width: \n", + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_background_color: \n", + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "body_background_color: \n", + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "box_color: \n", + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: \n") + writeLines(lines, theme_file) } diff --git a/README.md b/README.md index 23399d4..91e678a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ output: [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/periscope?color=9bc2cf)](https://cran.r-project.org/package=periscope) [![CRAN_Downloads_Badge](https://cranlogs.r-pkg.org/badges/grand-total/periscope?color=9bc2cf)](https://cran.r-project.org/package=periscope) -[![Travis-CI Build Status](https://travis-ci.com/cb4ds/periscope.svg?branch=master)](https://travis-ci.com/cb4ds/periscope) +[![Travis-CI Build Status](https://app.travis-ci.com/cb4ds/periscope.svg?branch=master)](https://app.travis-ci.com/cb4ds/periscope) [![Coverage Status](https://img.shields.io/codecov/c/github/cb4ds/periscope/master.svg)](https://codecov.io/github/cb4ds/periscope?branch=master) @@ -64,12 +64,3 @@ create_new_application("sampleapp2", location = tempdir(), sampleapp = TRUE, rig runApp('sampleapp2') ``` - -#### Sample application - custom styling - -```r -library(periscope) -create_new_application("sampleapp3", location = tempdir(), sampleapp = TRUE, style = list(skin = "green")) -runApp('sampleapp3') - -``` diff --git a/cran-comments.md b/cran-comments.md index 5e412ec..ce03cef 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,10 +1,11 @@ ## Comments from Maintainer -* LazyData removed from Description to fix Note on CRAN check page +Resubmission comments: +Updated travis link in Readme file -* Bugfix for situation where the user wants to generate an app but does not have shinydashboardPlus installed and does not need a right sidebar (which requires sdp) - -* There may be a NOTE due to the short time between submissions, this is an important bugfix release and I kindly ask for your exception to allow this release. +Initial comments: +This is a major functionality update including changing the shiny module paradigm, supporting additional DT options in downloadableTables and updating the styling paradigm to allow more flexibility when customizing periscope applications. This release is compatible with apps created with the 0.x version of the package and +documentation including the sample applications, examples, vignettes, tests, etc. were also updated. --- @@ -14,13 +15,14 @@ RStudio Server Pro (Ubuntu 18.04.2) * R 3.6.3 -* R 4.0.4 +* R 4.0.5 +* R 4.1.1 Travis-CI (Ubuntu 16.04.6) * R 3.6.3 * R 4.0.2 -* R devel (2021-03-29 r80130) +* R devel (2021-09-29 r80990) WinBuilder @@ -29,7 +31,7 @@ WinBuilder RHub -* devtools::check_rhub(interactive = F) +* devtools::check_rhub(interactive = F, env_vars = c("R_CHECK_FORCE_SUGGESTS" = "false")) --- @@ -49,9 +51,9 @@ devtools::check() **NONE** ``` -pdb <- available.packages() tools::package_dependencies(packages = c('periscope'), - db = pdb, reverse = TRUE) + db = available.packages(), + reverse = TRUE) $periscope character(0) diff --git a/inst/fw_templ/p_blank/global.R b/inst/fw_templ/p_blank/global.R index 7f73254..212e893 100755 --- a/inst/fw_templ/p_blank/global.R +++ b/inst/fw_templ/p_blank/global.R @@ -9,6 +9,9 @@ # to server, UI and session scopes # ---------------------------------------- +library(shiny) +library(periscope) + # -- Setup your Application -- set_app_parameters(title = "Set title in global.R using set_app_parameters()", diff --git a/inst/fw_templ/p_example/global.R b/inst/fw_templ/p_example/global.R index 52cfc13..9e74719 100755 --- a/inst/fw_templ/p_example/global.R +++ b/inst/fw_templ/p_example/global.R @@ -12,6 +12,7 @@ # -- IMPORTS -- library(canvasXpress) + # -- Setup your Application -- set_app_parameters(title = "Sample Title (click for an info pop-up)", titleinfo = HTML("

Application Information Pop-Up

", diff --git a/inst/fw_templ/p_example/program_helpers.R b/inst/fw_templ/p_example/program_helpers.R index 209068e..2140772 100755 --- a/inst/fw_templ/p_example/program_helpers.R +++ b/inst/fw_templ/p_example/program_helpers.R @@ -23,7 +23,13 @@ load_data2 <- function() { load_data3 <- function() { ldf <- df %>% - select(1:3) - + select(1:3) %>% + mutate(Total.Population.Change = as.numeric(gsub(",", "", Total.Population.Change)), + Natural.Increase = as.numeric(gsub(",", "", Natural.Increase))) + as.data.frame(ldf) } + +read_themes <- function() { + yaml::read_yaml("www/periscope_style.yaml") +} diff --git a/inst/fw_templ/p_example/server_global.R b/inst/fw_templ/p_example/server_global.R index bcc8a3d..571c128 100755 --- a/inst/fw_templ/p_example/server_global.R +++ b/inst/fw_templ/p_example/server_global.R @@ -23,6 +23,10 @@ # ---------------------------------------- # -- IMPORTS -- +library(colourpicker) +library(DT) +library(htmltools) +library(htmlwidgets) # -- VARIABLES -- diff --git a/inst/fw_templ/p_example/server_local.R b/inst/fw_templ/p_example/server_local.R index d4fd8e0..0da3686 100755 --- a/inst/fw_templ/p_example/server_local.R +++ b/inst/fw_templ/p_example/server_local.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -54,7 +54,7 @@ output$proginfo <- renderUI({ "application-wide functionality is useful across all users that ", "should be added into server_global.R. Scoping information is in ", "the top comment of all program example files.") ) - }) +}) output$tooltips <- renderUI({ list(hr(), @@ -63,7 +63,7 @@ output$tooltips <- renderUI({ text = "Example tooltip text"), "can be added with the following code in the UI:"), p(pre("U: ui_tooltip('tooltipID', 'label text (optional)', 'text content')")) ) - }) +}) output$busyind <- renderUI({ list(hr(), @@ -73,7 +73,7 @@ output$busyind <- renderUI({ bsButton("showWorking", label = "Show application busy indicator for 5 seconds", style = "primary")) ) - }) +}) output$download <- renderUI({ list( @@ -83,13 +83,13 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", downloadFileButton("exampleDownload2", c("csv", "xlsx", "tsv"), "Download options")) ) - }) +}) output$alerts <- renderUI({ list(hr(), @@ -112,7 +112,7 @@ output$alerts <- renderUI({ label = "Body", style = "info", width = "25%")) ) - }) +}) output$loginfo <- renderUI({ list(p("The collapsed ", @@ -129,7 +129,7 @@ output$loginfo <- renderUI({ "the log is kept as 'actions.log.last"), p("See the ", em("logging"), "documentation for more information ", "on functions and other options") ) - }) +}) output$hover_info <- renderUI({ hover <- input$examplePlot2_hover @@ -142,21 +142,68 @@ output$hover_info <- renderUI({ else { left_pct <- (hover$x - hover$domain$left) / (hover$domain$right - hover$domain$left) left_px <- hover$range$left + left_pct * (hover$range$right - hover$range$left) - + top_pct <- (hover$domain$top - hover$y) / (hover$domain$top - hover$domain$bottom) top_px <- hover$range$top + top_pct * (hover$range$bottom - hover$range$top) - + style <- paste0("position:absolute;", "z-index:100;", "background-color: rgba(245, 245, 245, 0.85); ", "left:", left_px + 2, "px; top:", top_px + 2, "px;") - + return(wellPanel(class = "well-sm", style = style, HTML(" Car: ", rownames(point))) ) } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -167,33 +214,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2ggplot, - csv = plot2ggplot_data), - aspectratio = 1.5, - visibleplot = plot2ggplot) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3lattice, - tiff = plot3lattice, - txt = plot3lattice_data, - tsv = plot3lattice_data), - visibleplot = plot3lattice) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2ggplot, + csv = plot2ggplot_data), + aspectratio = 1.5, + visibleplot = plot2ggplot) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3lattice, + tiff = plot3lattice, + txt = plot3lattice_data, + tsv = plot3lattice_data), + visibleplot = plot3lattice) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -210,7 +298,7 @@ observeEvent(input$exampleAdvancedAlert, { createAlert(session, "sidebarAdvancedAlert", style = "warning", content = "Example Advanced Sidebar Alert") - + }) observeEvent(input$exampleBodyAlert, { @@ -226,3 +314,78 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list(periscope:::fw_create_body(), + init_js_command()) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + init_js_command()) + }) +}) + +init_js_command <- function() { + list(shiny::tags$script("setTimeout(function() {$('div.navbar-custom-menu').click()}, 1000);"), + shiny::tags$script("$('div.navbar-custom-menu').click();"), + shiny::tags$script("$('#examplePlot2-dplotButtonDiv').css('display', 'inherit')"), + shiny::tags$script("$('#examplePlot3-dplotButtonDiv').css('display', 'inherit')")) +} diff --git a/inst/fw_templ/p_example/server_local_no_left.R b/inst/fw_templ/p_example/server_local_no_left.R index ed5d67c..b75b4d2 100755 --- a/inst/fw_templ/p_example/server_local_no_left.R +++ b/inst/fw_templ/p_example/server_local_no_left.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -54,7 +54,7 @@ output$proginfo <- renderUI({ "application-wide functionality is useful across all users that ", "should be added into server_global.R. Scoping information is in ", "the top comment of all program example files.") ) - }) +}) output$tooltips <- renderUI({ list(hr(), @@ -63,7 +63,7 @@ output$tooltips <- renderUI({ text = "Example tooltip text"), "can be added with the following code in the UI:"), p(pre("U: ui_tooltip('tooltipID', 'label text (optional)', 'text content')")) ) - }) +}) output$busyind <- renderUI({ list(hr(), @@ -73,7 +73,7 @@ output$busyind <- renderUI({ bsButton("showWorking", label = "Show application busy indicator for 5 seconds", style = "primary")) ) - }) +}) output$download <- renderUI({ list( @@ -83,13 +83,13 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", downloadFileButton("exampleDownload2", c("csv", "xlsx", "tsv"), "Download options")) ) - }) +}) output$alerts <- renderUI({ list(hr(), @@ -103,7 +103,7 @@ output$alerts <- renderUI({ label = "Body", style = "info", width = "25%")) ) - }) +}) output$loginfo <- renderUI({ list(p("The collapsed ", @@ -120,7 +120,7 @@ output$loginfo <- renderUI({ "the log is kept as 'actions.log.last"), p("See the ", em("logging"), "documentation for more information ", "on functions and other options") ) - }) +}) output$hover_info <- renderUI({ hover <- input$examplePlot2_hover @@ -133,21 +133,69 @@ output$hover_info <- renderUI({ else { left_pct <- (hover$x - hover$domain$left) / (hover$domain$right - hover$domain$left) left_px <- hover$range$left + left_pct * (hover$range$right - hover$range$left) - + top_pct <- (hover$domain$top - hover$y) / (hover$domain$top - hover$domain$bottom) top_px <- hover$range$top + top_pct * (hover$range$bottom - hover$range$top) - + style <- paste0("position:absolute;", "z-index:100;", "background-color: rgba(245, 245, 245, 0.85); ", "left:", left_px + 2, "px; top:", top_px + 2, "px;") - + return(wellPanel(class = "well-sm", style = style, HTML(" Car: ", rownames(point))) ) } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) + # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -158,33 +206,75 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2ggplot, - csv = plot2ggplot_data), - aspectratio = 1.5, - visibleplot = plot2ggplot) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3lattice, - tiff = plot3lattice, - txt = plot3lattice_data, - tsv = plot3lattice_data), - visibleplot = plot3lattice) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) + +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2ggplot, + csv = plot2ggplot_data), + aspectratio = 1.5, + visibleplot = plot2ggplot) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3lattice, + tiff = plot3lattice, + txt = plot3lattice_data, + tsv = plot3lattice_data), + visibleplot = plot3lattice) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -201,7 +291,6 @@ observeEvent(input$exampleAdvancedAlert, { createAlert(session, "sidebarAdvancedAlert", style = "warning", content = "Example Advanced Sidebar Alert") - }) observeEvent(input$exampleBodyAlert, { @@ -217,3 +306,78 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list(periscope:::fw_create_body(), + init_js_command()) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + init_js_command()) + }) +}) + +init_js_command <- function() { + list(shiny::tags$script("setTimeout(function() {$('div.navbar-custom-menu').click()}, 1000);"), + shiny::tags$script("$('div.navbar-custom-menu').click();"), + shiny::tags$script("$('#examplePlot2-dplotButtonDiv').css('display', 'inherit')"), + shiny::tags$script("$('#examplePlot3-dplotButtonDiv').css('display', 'inherit')")) +} diff --git a/inst/fw_templ/p_example/server_local_plus.R b/inst/fw_templ/p_example/server_local_plus.R index 963d96d..2f422ce 100755 --- a/inst/fw_templ/p_example/server_local_plus.R +++ b/inst/fw_templ/p_example/server_local_plus.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -85,7 +85,7 @@ output$proginfo <- renderUI({ "application-wide functionality is useful across all users that ", "should be added into server_global.R. Scoping information is in ", "the top comment of all program example files.") ) - }) +}) output$tooltips <- renderUI({ list(hr(), @@ -94,7 +94,7 @@ output$tooltips <- renderUI({ text = "Example tooltip text"), "can be added with the following code in the UI:"), p(pre("U: ui_tooltip('tooltipID', 'label text (optional)', 'text content')")) ) - }) +}) output$busyind <- renderUI({ list(hr(), @@ -104,7 +104,7 @@ output$busyind <- renderUI({ bsButton("showWorking", label = "Show application busy indicator for 5 seconds", style = "primary")) ) - }) +}) output$download <- renderUI({ list( @@ -114,13 +114,13 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", downloadFileButton("exampleDownload2", c("csv", "xlsx", "tsv"), "Download options")) ) - }) +}) output$alerts <- renderUI({ list(hr(), @@ -147,7 +147,7 @@ output$alerts <- renderUI({ label = "Sidebar - Right", style = "danger", width = "20%") ) ) - }) +}) output$loginfo <- renderUI({ list(p("The collapsed ", @@ -164,7 +164,7 @@ output$loginfo <- renderUI({ "the log is kept as 'actions.log.last"), p("See the ", em("logging"), "documentation for more information ", "on functions and other options") ) - }) +}) output$hover_info <- renderUI({ hover <- input$examplePlot2_hover @@ -177,21 +177,68 @@ output$hover_info <- renderUI({ else { left_pct <- (hover$x - hover$domain$left) / (hover$domain$right - hover$domain$left) left_px <- hover$range$left + left_pct * (hover$range$right - hover$range$left) - + top_pct <- (hover$domain$top - hover$y) / (hover$domain$top - hover$domain$bottom) top_px <- hover$range$top + top_pct * (hover$range$bottom - hover$range$top) - + style <- paste0("position:absolute;", "z-index:100;", "background-color: rgba(245, 245, 245, 0.85); ", "left:", left_px + 2, "px; top:", top_px + 2, "px;") - + return(wellPanel(class = "well-sm", style = style, HTML(" Car: ", rownames(point))) ) } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -206,33 +253,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2, - csv = plot2_data), - aspectratio = 1.5, - visibleplot = plot2) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3, - tiff = plot3, - txt = plot3_data, - tsv = plot3_data), - visibleplot = plot3) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2, + csv = plot2_data), + aspectratio = 1.5, + visibleplot = plot2) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3, + tiff = plot3, + txt = plot3_data, + tsv = plot3_data), + visibleplot = plot3) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -249,7 +337,6 @@ observeEvent(input$exampleAdvancedAlert, { createAlert(session, "sidebarAdvancedAlert", style = "warning", content = "Example Advanced Sidebar Alert") - }) observeEvent(input$exampleRightAlert, { @@ -274,3 +361,78 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list(periscope:::fw_create_body(), + init_js_command()) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + init_js_command()) + }) +}) + +init_js_command <- function() { + list(shiny::tags$script("setTimeout(function() {$('div.navbar-custom-menu').click()}, 1000);"), + shiny::tags$script("$('div.navbar-custom-menu').click();"), + shiny::tags$script("$('#examplePlot2-dplotButtonDiv').css('display', 'inherit')"), + shiny::tags$script("$('#examplePlot3-dplotButtonDiv').css('display', 'inherit')")) +} diff --git a/inst/fw_templ/p_example/server_local_plus_no_left.R b/inst/fw_templ/p_example/server_local_plus_no_left.R index 6dc6840..f2088bf 100755 --- a/inst/fw_templ/p_example/server_local_plus_no_left.R +++ b/inst/fw_templ/p_example/server_local_plus_no_left.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -85,7 +85,7 @@ output$proginfo <- renderUI({ "application-wide functionality is useful across all users that ", "should be added into server_global.R. Scoping information is in ", "the top comment of all program example files.") ) - }) +}) output$tooltips <- renderUI({ list(hr(), @@ -94,7 +94,7 @@ output$tooltips <- renderUI({ text = "Example tooltip text"), "can be added with the following code in the UI:"), p(pre("U: ui_tooltip('tooltipID', 'label text (optional)', 'text content')")) ) - }) +}) output$busyind <- renderUI({ list(hr(), @@ -104,7 +104,7 @@ output$busyind <- renderUI({ bsButton("showWorking", label = "Show application busy indicator for 5 seconds", style = "primary")) ) - }) +}) output$download <- renderUI({ list( @@ -114,13 +114,13 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", downloadFileButton("exampleDownload2", c("csv", "xlsx", "tsv"), "Download options")) ) - }) +}) output$alerts <- renderUI({ list(hr(), @@ -155,7 +155,7 @@ output$loginfo <- renderUI({ "the log is kept as 'actions.log.last"), p("See the ", em("logging"), "documentation for more information ", "on functions and other options") ) - }) +}) output$hover_info <- renderUI({ hover <- input$examplePlot2_hover @@ -168,21 +168,68 @@ output$hover_info <- renderUI({ else { left_pct <- (hover$x - hover$domain$left) / (hover$domain$right - hover$domain$left) left_px <- hover$range$left + left_pct * (hover$range$right - hover$range$left) - + top_pct <- (hover$domain$top - hover$y) / (hover$domain$top - hover$domain$bottom) top_px <- hover$range$top + top_pct * (hover$range$bottom - hover$range$top) - + style <- paste0("position:absolute;", "z-index:100;", "background-color: rgba(245, 245, 245, 0.85); ", "left:", left_px + 2, "px; top:", top_px + 2, "px;") - + return(wellPanel(class = "well-sm", style = style, HTML(" Car: ", rownames(point))) ) } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -197,33 +244,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2, - csv = plot2_data), - aspectratio = 1.5, - visibleplot = plot2) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3, - tiff = plot3, - txt = plot3_data, - tsv = plot3_data), - visibleplot = plot3) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) + )) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2, + csv = plot2_data), + aspectratio = 1.5, + visibleplot = plot2) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3, + tiff = plot3, + txt = plot3_data, + tsv = plot3_data), + visibleplot = plot3) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -240,7 +328,6 @@ observeEvent(input$exampleAdvancedAlert, { createAlert(session, "sidebarAdvancedAlert", style = "warning", content = "Example Advanced Sidebar Alert") - }) observeEvent(input$exampleRightAlert, { @@ -265,3 +352,78 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list(periscope:::fw_create_body(), + init_js_command()) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + init_js_command()) + }) +}) + +init_js_command <- function() { + list(shiny::tags$script("setTimeout(function() {$('div.navbar-custom-menu').click()}, 1000);"), + shiny::tags$script("$('div.navbar-custom-menu').click();"), + shiny::tags$script("$('#examplePlot2-dplotButtonDiv').css('display', 'inherit')"), + shiny::tags$script("$('#examplePlot3-dplotButtonDiv').css('display', 'inherit')")) +} diff --git a/inst/fw_templ/p_example/ui_body.R b/inst/fw_templ/p_example/ui_body.R index 8a15e22..6f13fc1 100755 --- a/inst/fw_templ/p_example/ui_body.R +++ b/inst/fw_templ/p_example/ui_body.R @@ -40,12 +40,22 @@ body2 <- shinydashboard::box( id = "bodyElement2", collapsed = TRUE, htmlOutput("proginfo") ) +app_styling <- shinydashboard::box(id = "app_styling", + title = "Application Styling", + width = 12, + status = "primary", + collapsible = TRUE, + collapsed = TRUE, + htmlOutput("styles")) + body3 <- shinydashboard::box( id = "bodyElement3", title = "Downloadable Table", width = 12, status = "primary", collapsible = TRUE, collapsed = TRUE, + htmlOutput("table_info"), + hr(), downloadableTableUI("exampleDT1", list("csv", "tsv"), "Download table data") ) @@ -90,4 +100,4 @@ body6 <- shinydashboard::box( id = "bodyElement6", # -- Register Elements in the ORDER SHOWN in the UI # -- Note: Will be added before the standard framework footer -add_ui_body(list(body1, body2, body3, body4, body5, body6), append = FALSE) +add_ui_body(list(body1, body2, app_styling, body3, body4, body5, body6), append = FALSE) diff --git a/inst/fw_templ/www/periscope_style.yaml b/inst/fw_templ/www/periscope_style.yaml new file mode 100644 index 0000000..03ab446 --- /dev/null +++ b/inst/fw_templ/www/periscope_style.yaml @@ -0,0 +1,47 @@ +### primary_color +# Sets the primary status color that affects the color of the header, valueBox, infoBox and box. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +primary_color: "#4F718F" + + +# Sidebar variables: change the default sidebar width, colors: +### sidebar_width +# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only. +# Valid possible value are 200, 350, 425, ... +# Blank/empty value will use default value +sidebar_width: 300 + +### sidebar_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_background_color: "#A0B89E" + +### sidebar_hover_color +# The color of sidebar menu item upon hovring with mouse. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_hover_color: + +### sidebar_text_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_text_color: + + +# body variables +### body_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +body_background_color: "#EDECE8" + +# boxes variables +### box_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +box_color: "#DAE0D9" + +### infobox_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +infobox_color: diff --git a/man/create_new_application.Rd b/man/create_new_application.Rd index 042b202..3a66101 100644 --- a/man/create_new_application.Rd +++ b/man/create_new_application.Rd @@ -11,7 +11,7 @@ create_new_application( resetbutton = TRUE, rightsidebar = FALSE, leftsidebar = TRUE, - style = list(skin = "blue") + custom_theme_file = NULL ) } \arguments{ @@ -28,7 +28,7 @@ containing the name of a shiny::icon().} \item{leftsidebar}{whether the left sidebar should be enabled.} -\item{style}{list containing application styling properties. By default the skin is blue.} +\item{custom_theme_file}{location of custom theme settings yaml file. Default value is NULL.} } \description{ Creates ready-to-use templated application files using the periscope @@ -101,6 +101,10 @@ Use this location for code that would have previously resided in server.R inside of the call to \code{shinyServer(...)}. Anything placed in this file will be accessible only within a single user session.\cr \cr +\strong{\emph{name}/www/periscope_style.yaml} :\cr +This is the application custom styling yaml file. User can update +application different parts style using this file.\cr +\cr \cr \strong{Do not modify the following files}: \cr \preformatted{ @@ -133,7 +137,7 @@ rightsidebar = "table") # blank app named 'myblankapp' created in a temp dir create_new_application(name = 'myblankapp', location = tempdir()) # blank app named 'myblankapp' with a green skin created in a temp dir -create_new_application(name = 'myblankapp', location = tempdir(), style = list(skin = "green")) +create_new_application(name = 'myblankapp', location = tempdir()) # blank app named 'myblankapp' without a left sidebar created in a temp dir create_new_application(name = 'myblankapp', location = tempdir(), leftsidebar = FALSE) diff --git a/man/downloadFile.Rd b/man/downloadFile.Rd index 3517e71..1b8c6d6 100644 --- a/man/downloadFile.Rd +++ b/man/downloadFile.Rd @@ -4,23 +4,11 @@ \alias{downloadFile} \title{downloadFile Module} \usage{ -downloadFile( - input, - output, - session, - logger, - filenameroot, - datafxns = list(), - aspectratio = 1 -) +downloadFile(..., logger, filenameroot, datafxns = list(), aspectratio = 1) } \arguments{ -\item{input}{provided by \code{shiny::callModule}} - -\item{output}{provided by \code{shiny::callModule}} - -\item{session}{provided by \code{shiny::callModule} -\cr \cr} +\item{...}{free parameters list for shiny to pass session variables based on the module call(session, input, output) +variables. \emph{Note}: The first argument of this function must be the ID of the Module's UI element} \item{logger}{logger to use} @@ -46,27 +34,25 @@ download types. The server function is used to provide the data for download. This function is not called directly by consumers - it is accessed in server.R using the same id provided in \code{downloadFileButton}: -\strong{\code{callModule(downloadFile, id, logger, filenameroot, datafxns)}} +\strong{\code{downloadFile(id, logger, filenameroot, datafxns)}} } \examples{ # Inside server_local.R #single download type -# callModule(downloadFile, -# "object_id1", -# logger = ss_userAction.Log, -# filenameroot = "mydownload1", -# datafxns = list(csv = mydatafxn1), -# aspectratio = 1) +# downloadFile("object_id1", +# logger = ss_userAction.Log, +# filenameroot = "mydownload1", +# datafxns = list(csv = mydatafxn1), +# aspectratio = 1) #multiple download types -# callModule(downloadFile, -# "object_id2", -# logger = ss_userAction.Log, -# filenameroot = "mytype2", -# datafxns = list(csv = mydatafxn1, xlsx = mydatafxn2), -# aspectratio = 1) +# downloadFile("object_id2", +# logger = ss_userAction.Log, +# filenameroot = "mytype2", +# datafxns = list(csv = mydatafxn1, xlsx = mydatafxn2), +# aspectratio = 1) } \seealso{ @@ -75,6 +61,4 @@ server.R using the same id provided in \code{downloadFileButton}: \link[periscope]{downloadFile_ValidateTypes} \link[periscope]{downloadFile_AvailableTypes} - -\link[shiny]{callModule} } diff --git a/man/downloadFileButton.Rd b/man/downloadFileButton.Rd index eeffd92..a7be35d 100644 --- a/man/downloadFileButton.Rd +++ b/man/downloadFileButton.Rd @@ -38,7 +38,7 @@ for the button. Call this function at the place in ui.R where the button should be placed. -It is paired with a call to \code{shiny::callModule(downloadFile, id, ...)} +It is paired with a call to \code{downloadFile(id, ...)} in server.R } diff --git a/man/downloadablePlot.Rd b/man/downloadablePlot.Rd index f8525e7..46b7f41 100644 --- a/man/downloadablePlot.Rd +++ b/man/downloadablePlot.Rd @@ -5,9 +5,7 @@ \title{downloadablePlot Module} \usage{ downloadablePlot( - input, - output, - session, + ..., logger, filenameroot, aspectratio = 1, @@ -16,12 +14,8 @@ downloadablePlot( ) } \arguments{ -\item{input}{provided by \code{shiny::callModule}} - -\item{output}{provided by \code{shiny::callModule}} - -\item{session}{provided by \code{shiny::callModule} -\cr \cr} +\item{...}{free parameters list for shiny to pass session variables based on the module call(session, input, output) +variables. \emph{Note}: The first argument of this function must be the ID of the Module's UI element} \item{logger}{logger to use} @@ -55,24 +49,21 @@ button will be hidden as there is nothing to download. This function is not called directly by consumers - it is accessed in server.R using the same id provided in \code{downloadablePlotUI}: -\strong{\code{callModule(downloadablePlot, id, logger, filenameroot, +\strong{\code{downloadablePlot(id, logger, filenameroot, downloadfxns, visibleplot)}} } \examples{ # Inside server_local.R -# callModule(downloadablePlot, -# "object_id1", -# logger = ss_userAction.Log, -# filenameroot = "mydownload1", -# aspectratio = 1.33, -# downloadfxns = list(png = myplotfxn, tsv = mydatafxn), -# visibleplot = myplotfxn) +# downloadablePlot("object_id1", +# logger = ss_userAction.Log, +# filenameroot = "mydownload1", +# aspectratio = 1.33, +# downloadfxns = list(png = myplotfxn, tsv = mydatafxn), +# visibleplot = myplotfxn) } \seealso{ \link[periscope]{downloadablePlotUI} - -\link[shiny]{callModule} } diff --git a/man/downloadablePlotUI.Rd b/man/downloadablePlotUI.Rd index b8effec..58bf661 100644 --- a/man/downloadablePlotUI.Rd +++ b/man/downloadablePlotUI.Rd @@ -58,9 +58,7 @@ produced graphics. \section{Notes}{ When there is nothing to download in any of the linked downloadfxns the -button will be hidden as there is nothing to download. The linked -downloadfxns are set in the paired callModule (see the \strong{Shiny Usage} -section) +button will be hidden as there is nothing to download. This module is NOT compatible with the built-in (base) graphics \emph{(such as basic plot, etc.)} because they cannot be saved into an object and are directly @@ -71,7 +69,7 @@ output by the system at the time of creation. Call this function at the place in ui.R where the plot should be placed. -Paired with a call to \code{shiny::callModule(downloadablePlot, id, ...)} +Paired with a call to \code{downloadablePlot(id, ...)} in server.R } diff --git a/man/downloadableTable.Rd b/man/downloadableTable.Rd index 2b3e406..84b94ec 100644 --- a/man/downloadableTable.Rd +++ b/man/downloadableTable.Rd @@ -5,25 +5,17 @@ \title{downloadableTable Module} \usage{ downloadableTable( - input, - output, - session, + ..., logger, filenameroot, downloaddatafxns = list(), tabledata, - rownames = TRUE, - caption = NULL, selection = NULL ) } \arguments{ -\item{input}{provided by \code{shiny::callModule}} - -\item{output}{provided by \code{shiny::callModule}} - -\item{session}{provided by \code{shiny::callModule} -\cr \cr} +\item{...}{free parameters list to pass table customization options. See example below. +\emph{Note}: The first argument of this function must be the ID of the Module's UI element} \item{logger}{logger to use} @@ -38,12 +30,8 @@ when the table UI was created.} \item{tabledata}{function or reactive expression providing the table display data as a return value. This function should require no input parameters.} -\item{rownames}{whether or not to show the rownames in the table} - -\item{caption}{table caption} - \item{selection}{function or reactive expression providing the row_ids of the -rows that should be selected.} +rows that should be selected} } \value{ Reactive expression containing the currently selected rows in the @@ -54,10 +42,28 @@ Server-side function for the downloadableTableUI. This is a custom high-functionality table paired with a linked downloadFile button. } +\details{ +Generated table can highly customized using function \code{?DT::datatable} same arguments + except for `options` and `selection` parameters. + +For `options` user can pass the same \code{?DT::datatable} options using the same names and +values one by one separated by comma. + +For `selection` parameter it can be either a function or reactive expression providing the row_ids of the +rows that should be selected. + +Also, user can apply the same provided \code{?DT::formatCurrency} columns formats on passed +dataset using format functions names as keys and their options as a list. +} \section{Notes}{ -When there are no rows to download in any of the linked downloaddatafxns the -button will be hidden as there is nothing to download. + \itemize{ + \item When there are no rows to download in any of the linked downloaddatafxns + the button will be hidden as there is nothing to download. + \item \code{selection} parameter has different usage than DT::datatable \code{selection} option. + See parameters usage section. + \item DT::datatable options \code{editable}, \code{width} and \code{height} are not supported +} } \section{Shiny Usage}{ @@ -65,31 +71,41 @@ button will be hidden as there is nothing to download. This function is not called directly by consumers - it is accessed in server.R using the same id provided in \code{downloadableTableUI}: -\strong{\code{callModule(downloadableTable, id, logger, filenameroot, +\strong{\code{downloadableTable(id, logger, filenameroot, downloaddatafxns, tabledata, rownames, caption, selection)}} -\emph{Note}: callModule returns the reactive expression containing the +\emph{Note}: calling module server returns the reactive expression containing the currently selected rows in the display table. } \examples{ # Inside server_local.R -# selectedrows <- callModule(downloadableTable, -# "object_id1", -# logger = ss_userAction.Log, -# filenameroot = "mydownload1", -# downloaddatafxns = list(csv = mydatafxn1, tsv = mydatafxn2), -# tabledata = mydatafxn3, -# rownames = FALSE, -# caption = "This is a great table! By: Me", -# selection = mydataRowIds) +# selectedrows <- downloadableTable( +# "object_id1", +# logger = ss_userAction.Log, +# filenameroot = "mydownload1", +# downloaddatafxns = list(csv = mydatafxn1, tsv = mydatafxn2), +# tabledata = mydatafxn3, +# rownames = FALSE, +# caption = "This is a great table! By: Me", +# selection = mydataRowIds, +# colnames = c("Area", "Delta", "Increase"), +# filter = "bottom", +# width = "150px", +# height = "50px", +# extensions = 'Buttons', +# plugins = 'natural', +# editable = TRUE, +# dom = 'Bfrtip', +# buttons = c('copy', 'csv', 'excel', 'pdf', 'print'), +# formatStyle = list(columns = c('Area'), color = 'red'), +# formatStyle = list(columns = c('Increase'), color = DT::styleInterval(0, c('red', 'green'))), +# formatCurrency = list(columns = c('Delta'))) # selectedrows is the reactive return value, captured for later use } \seealso{ \link[periscope]{downloadableTableUI} - -\link[shiny]{callModule} } diff --git a/man/downloadableTableUI.Rd b/man/downloadableTableUI.Rd index b0443ba..c0a1f37 100644 --- a/man/downloadableTableUI.Rd +++ b/man/downloadableTableUI.Rd @@ -52,16 +52,14 @@ sorting by columns and returns a reactive dataset of selected items. \section{Notes}{ When there are no rows to download in any of the linked downloaddatafxns the -button will be hidden as there is nothing to download. The linked -downloaddatafxns are set in the paired callModule (see the \strong{Shiny Usage} -section) +button will be hidden as there is nothing to download. } \section{Shiny Usage}{ Call this function at the place in ui.R where the table should be placed. -Paired with a call to \code{shiny::callModule(downloadableTable, id, ...)} +Paired with a call to \code{downloadableTable(id, ...)} in server.R } diff --git a/tests/testthat/_snaps/downloadable_table.md b/tests/testthat/_snaps/downloadable_table.md index 61468e7..fb28b22 100644 --- a/tests/testthat/_snaps/downloadable_table.md +++ b/tests/testthat/_snaps/downloadable_table.md @@ -20,3 +20,84 @@ +# build_datatable_arguments + + Code + build_datatable_arguments(table_options) + Message + DT option 'width' is not supported. Ignoring it. + DT option 'height' is not supported. Ignoring it. + DT option 'editable' is not supported. Ignoring it. + Output + $rownames + [1] FALSE + + $class + [1] "periscope-downloadable-table table-condensed table-striped table-responsive" + + $callback + [1] "table.order([2, 'asc']).draw();" + + $caption + [1] " Very Important Information" + + $colnames + [1] "Area" "Delta" "Increase" + + $filter + [1] "bottom" + + $extensions + [1] "Buttons" + + $plugins + [1] "natural" + + $options + $options$order + $options$order[[1]] + $options$order[[1]][[1]] + [1] 2 + + $options$order[[1]][[2]] + [1] "asc" + + + $options$order[[2]] + $options$order[[2]][[1]] + [1] 3 + + $options$order[[2]][[2]] + [1] "desc" + + + + $options$deferRender + [1] FALSE + + $options$paging + [1] FALSE + + $options$scrollX + [1] TRUE + + $options$dom + [1] "<\"periscope-downloadable-table-header\"f>tr" + + $options$processing + [1] TRUE + + $options$rowId + [1] 1 + + $options$searchHighlight + [1] TRUE + + + +# format_columns + + Code + format_columns(DT::datatable(dt), list(formatCurrency = list(columns = c("A", + "C")), formatPercentage = list(columns = c("D"), 2))) + diff --git a/tests/testthat/_snaps/ui_functions.md b/tests/testthat/_snaps/ui_functions.md new file mode 100644 index 0000000..6843671 --- /dev/null +++ b/tests/testthat/_snaps/ui_functions.md @@ -0,0 +1,195 @@ +# fw_create_header + +
+ + +
+ +# fw_create_sidebar no sidebar + + + +# fw_create_sidebar empty + + + +# fw_create_sidebar only basic + + + +# fw_create_sidebar only advanced + + + +# fw_create_body app_info + +
+
+ + + +
+
+
+

User Action Log

+
+ +
+
+
+
+
+
+
+
+
+ +# fw_create_body no log + +
+
+ + +
+
+ +# ui_tooltip + + + mylabel + + + + +# fw_create_header_plus + +
+ + +
+ +# fw_create_right_sidebar + + +
+ +# fw_create_right_sidebar SDP>=2 + + +
+ diff --git a/tests/testthat/sample_app/program/data/.gitignore b/tests/testthat/sample_app/program/data/.gitignore index 0a4bafe..94548af 100644 --- a/tests/testthat/sample_app/program/data/.gitignore +++ b/tests/testthat/sample_app/program/data/.gitignore @@ -1,4 +1,3 @@ * */ !.gitignore -!example.csv diff --git a/tests/testthat/sample_app/program/fxn/program_helpers.R b/tests/testthat/sample_app/program/fxn/program_helpers.R index 209068e..2140772 100644 --- a/tests/testthat/sample_app/program/fxn/program_helpers.R +++ b/tests/testthat/sample_app/program/fxn/program_helpers.R @@ -23,7 +23,13 @@ load_data2 <- function() { load_data3 <- function() { ldf <- df %>% - select(1:3) - + select(1:3) %>% + mutate(Total.Population.Change = as.numeric(gsub(",", "", Total.Population.Change)), + Natural.Increase = as.numeric(gsub(",", "", Natural.Increase))) + as.data.frame(ldf) } + +read_themes <- function() { + yaml::read_yaml("www/periscope_style.yaml") +} diff --git a/tests/testthat/sample_app/program/server_local.R b/tests/testthat/sample_app/program/server_local.R index d4fd8e0..4d2c34e 100644 --- a/tests/testthat/sample_app/program/server_local.R +++ b/tests/testthat/sample_app/program/server_local.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -83,7 +83,7 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", @@ -157,6 +157,53 @@ output$hover_info <- renderUI({ } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -167,33 +214,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2ggplot, - csv = plot2ggplot_data), - aspectratio = 1.5, - visibleplot = plot2ggplot) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3lattice, - tiff = plot3lattice, - txt = plot3lattice_data, - tsv = plot3lattice_data), - visibleplot = plot3lattice) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2ggplot, + csv = plot2ggplot_data), + aspectratio = 1.5, + visibleplot = plot2ggplot) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3lattice, + tiff = plot3lattice, + txt = plot3lattice_data, + tsv = plot3lattice_data), + visibleplot = plot3lattice) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -226,3 +314,75 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list( + periscope:::fw_create_body(), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();")) + ) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();"))) + }) +}) diff --git a/tests/testthat/sample_app/program/ui_body.R b/tests/testthat/sample_app/program/ui_body.R index 8a15e22..6f13fc1 100644 --- a/tests/testthat/sample_app/program/ui_body.R +++ b/tests/testthat/sample_app/program/ui_body.R @@ -40,12 +40,22 @@ body2 <- shinydashboard::box( id = "bodyElement2", collapsed = TRUE, htmlOutput("proginfo") ) +app_styling <- shinydashboard::box(id = "app_styling", + title = "Application Styling", + width = 12, + status = "primary", + collapsible = TRUE, + collapsed = TRUE, + htmlOutput("styles")) + body3 <- shinydashboard::box( id = "bodyElement3", title = "Downloadable Table", width = 12, status = "primary", collapsible = TRUE, collapsed = TRUE, + htmlOutput("table_info"), + hr(), downloadableTableUI("exampleDT1", list("csv", "tsv"), "Download table data") ) @@ -90,4 +100,4 @@ body6 <- shinydashboard::box( id = "bodyElement6", # -- Register Elements in the ORDER SHOWN in the UI # -- Note: Will be added before the standard framework footer -add_ui_body(list(body1, body2, body3, body4, body5, body6), append = FALSE) +add_ui_body(list(body1, body2, app_styling, body3, body4, body5, body6), append = FALSE) diff --git a/tests/testthat/sample_app/ui.R b/tests/testthat/sample_app/ui.R index 3864fcd..207ed89 100644 --- a/tests/testthat/sample_app/ui.R +++ b/tests/testthat/sample_app/ui.R @@ -19,4 +19,4 @@ source(paste("program", "ui_body.R", sep = .Platform$file.sep), dashboardPage(periscope:::fw_create_header(), periscope:::fw_create_sidebar(), - periscope:::fw_create_body()) + uiOutput('body')) diff --git a/tests/testthat/sample_app/www/periscope_style.yaml b/tests/testthat/sample_app/www/periscope_style.yaml new file mode 100644 index 0000000..334f142 --- /dev/null +++ b/tests/testthat/sample_app/www/periscope_style.yaml @@ -0,0 +1,47 @@ +### primary_color +# Sets the primary status color that affects the color of the header, valueBox, infoBox and box. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +primary_color: "#31A5CC" + + +# Sidebar variables: change the default sidebar width, colors: +### sidebar_width +# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only. +# Valid possible value are 200, 350, 425, ... +# Blank/empty value will use default value +sidebar_width: 300 + +### sidebar_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_background_color: "#00FF00" + +### sidebar_hover_color +# The color of sidebar menu item upon hovring with mouse. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_hover_color: + +### sidebar_text_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_text_color: + + +# body variables +### body_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +body_background_color: "#C7DFE8" + +# boxes variables +### box_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +box_color: "#FDFFF5" + +### infobox_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +infobox_color: diff --git a/tests/testthat/sample_app_both_sidebar/program/data/.gitignore b/tests/testthat/sample_app_both_sidebar/program/data/.gitignore index 0a4bafe..94548af 100644 --- a/tests/testthat/sample_app_both_sidebar/program/data/.gitignore +++ b/tests/testthat/sample_app_both_sidebar/program/data/.gitignore @@ -1,4 +1,3 @@ * */ !.gitignore -!example.csv diff --git a/tests/testthat/sample_app_both_sidebar/program/fxn/program_helpers.R b/tests/testthat/sample_app_both_sidebar/program/fxn/program_helpers.R index 209068e..2140772 100644 --- a/tests/testthat/sample_app_both_sidebar/program/fxn/program_helpers.R +++ b/tests/testthat/sample_app_both_sidebar/program/fxn/program_helpers.R @@ -23,7 +23,13 @@ load_data2 <- function() { load_data3 <- function() { ldf <- df %>% - select(1:3) - + select(1:3) %>% + mutate(Total.Population.Change = as.numeric(gsub(",", "", Total.Population.Change)), + Natural.Increase = as.numeric(gsub(",", "", Natural.Increase))) + as.data.frame(ldf) } + +read_themes <- function() { + yaml::read_yaml("www/periscope_style.yaml") +} diff --git a/tests/testthat/sample_app_both_sidebar/program/server_local.R b/tests/testthat/sample_app_both_sidebar/program/server_local.R index 2354393..514b773 100644 --- a/tests/testthat/sample_app_both_sidebar/program/server_local.R +++ b/tests/testthat/sample_app_both_sidebar/program/server_local.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -114,7 +114,7 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", @@ -135,18 +135,18 @@ output$alerts <- renderUI({ label = "Sidebar - Basic", style = "success", width = "20%"), - bsButton( "exampleAdvancedAlert", - label = "Sidebar - Advanced", - style = "warning", - width = "20%"), bsButton( "exampleBodyAlert", label = "Body", style = "info", width = "20%"), + bsButton( "exampleAdvancedAlert", + label = "Sidebar - Advanced", + style = "warning", + width = "20%"), bsButton( "exampleRightAlert", label = "Sidebar - Right", style = "danger", - width = "20%")) ) + width = "20%") ) ) }) output$loginfo <- renderUI({ @@ -192,6 +192,53 @@ output$hover_info <- renderUI({ } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -206,33 +253,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2, - csv = plot2_data), - aspectratio = 1.5, - visibleplot = plot2) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3, - tiff = plot3, - txt = plot3_data, - tsv = plot3_data), - visibleplot = plot3) +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2, + csv = plot2_data), + aspectratio = 1.5, + visibleplot = plot2) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3, + tiff = plot3, + txt = plot3_data, + tsv = plot3_data), + visibleplot = plot3) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -274,3 +362,75 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list( + periscope:::fw_create_body(), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();")) + ) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();"))) + }) +}) diff --git a/tests/testthat/sample_app_both_sidebar/program/ui_body.R b/tests/testthat/sample_app_both_sidebar/program/ui_body.R index 8a15e22..6f13fc1 100644 --- a/tests/testthat/sample_app_both_sidebar/program/ui_body.R +++ b/tests/testthat/sample_app_both_sidebar/program/ui_body.R @@ -40,12 +40,22 @@ body2 <- shinydashboard::box( id = "bodyElement2", collapsed = TRUE, htmlOutput("proginfo") ) +app_styling <- shinydashboard::box(id = "app_styling", + title = "Application Styling", + width = 12, + status = "primary", + collapsible = TRUE, + collapsed = TRUE, + htmlOutput("styles")) + body3 <- shinydashboard::box( id = "bodyElement3", title = "Downloadable Table", width = 12, status = "primary", collapsible = TRUE, collapsed = TRUE, + htmlOutput("table_info"), + hr(), downloadableTableUI("exampleDT1", list("csv", "tsv"), "Download table data") ) @@ -90,4 +100,4 @@ body6 <- shinydashboard::box( id = "bodyElement6", # -- Register Elements in the ORDER SHOWN in the UI # -- Note: Will be added before the standard framework footer -add_ui_body(list(body1, body2, body3, body4, body5, body6), append = FALSE) +add_ui_body(list(body1, body2, app_styling, body3, body4, body5, body6), append = FALSE) diff --git a/tests/testthat/sample_app_both_sidebar/program/ui_sidebar_right.R b/tests/testthat/sample_app_both_sidebar/program/ui_sidebar_right.R index 6742ad0..e804c40 100644 --- a/tests/testthat/sample_app_both_sidebar/program/ui_sidebar_right.R +++ b/tests/testthat/sample_app_both_sidebar/program/ui_sidebar_right.R @@ -23,23 +23,50 @@ # -- Create Elements -tab1 <- rightSidebarTabContent( - id = 1, - icon = "desktop", - title = "Tab 1 - Plots", - active = TRUE, - checkboxInput("enableGGPlot", "Enable GGPlot", value = TRUE), - checkboxInput("enableLatticePlot", "Enable Lattice Plot", value = TRUE), - checkboxInput("enableCXPlot", "Enable CanvasXpress Plot", value = TRUE)) - -tab2 <- rightSidebarTabContent( - id = 2, - title = "Tab 2 - Datatable") - -tab3 <- rightSidebarTabContent( - id = 3, - title = "Tab 3 - Other", - icon = "paint-brush") +if (utils::packageVersion('shinydashboardPlus') < 2) { + tab1 <- rightSidebarTabContent( + id = 1, + icon = "desktop", + title = "Tab 1 - Plots", + active = TRUE, + checkboxInput("enableGGPlot", "Enable GGPlot", value = TRUE), + checkboxInput("enableLatticePlot", "Enable Lattice Plot", value = TRUE), + checkboxInput("enableCXPlot", "Enable CanvasXpress Plot", value = TRUE)) + + tab2 <- rightSidebarTabContent( + id = 2, + title = "Tab 2 - Datatable") + + tab3 <- rightSidebarTabContent( + id = 3, + title = "Tab 3 - Other", + icon = "paint-brush") + + plus_fxn <- list(tab1, tab2, tab3) +} else { + tab1 <- controlbarItem( + id = 1, + title = icon("desktop"), + "Tab 1 - Plots", + checkboxInput("enableGGPlot", "Enable GGPlot", value = TRUE), + checkboxInput("enableLatticePlot", "Enable Lattice Plot", value = TRUE), + checkboxInput("enableCXPlot", "Enable CanvasXpress Plot", value = TRUE) + ) + + tab2 <- controlbarItem( + id = 2, + title = icon("database"), + "Tab 2 - Datatable", + ) + + tab3 <- controlbarItem( + id = 3, + title = icon("paint-brush"), + "Tab 3 - Other", + ) + + plus_fxn <- controlbarMenu(tab1, tab2, tab3) +} # -- Register Basic Elements in the ORDER SHOWN in the UI -add_ui_sidebar_right(list(tab1, tab2, tab3)) +add_ui_sidebar_right(plus_fxn) diff --git a/tests/testthat/sample_app_both_sidebar/ui.R b/tests/testthat/sample_app_both_sidebar/ui.R index c5227de..af8ae0e 100644 --- a/tests/testthat/sample_app_both_sidebar/ui.R +++ b/tests/testthat/sample_app_both_sidebar/ui.R @@ -19,8 +19,16 @@ source(paste("program", "ui_body.R", sep = .Platform$file.sep), local = TRUE) -dashboardPagePlus(periscope:::fw_create_header_plus(), - periscope:::fw_create_sidebar(), - periscope:::fw_create_body(), - periscope:::fw_create_right_sidebar(), - sidebar_fullCollapse = TRUE) +addl_opts <- list() +if (utils::packageVersion('shinydashboardPlus') < 2) { + plus_fxn <- getExportedValue("shinydashboardPlus", "dashboardPagePlus") + addl_opts <- list(sidebar_fullCollapse = TRUE) +} else { + plus_fxn <- getExportedValue("shinydashboardPlus", "dashboardPage") +} + +do.call(plus_fxn, c(list(periscope:::fw_create_header_plus(), + periscope:::fw_create_sidebar(), + uiOutput('body'), + periscope:::fw_create_right_sidebar()), + addl_opts)) diff --git a/tests/testthat/sample_app_both_sidebar/www/periscope_style.yaml b/tests/testthat/sample_app_both_sidebar/www/periscope_style.yaml new file mode 100644 index 0000000..334f142 --- /dev/null +++ b/tests/testthat/sample_app_both_sidebar/www/periscope_style.yaml @@ -0,0 +1,47 @@ +### primary_color +# Sets the primary status color that affects the color of the header, valueBox, infoBox and box. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +primary_color: "#31A5CC" + + +# Sidebar variables: change the default sidebar width, colors: +### sidebar_width +# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only. +# Valid possible value are 200, 350, 425, ... +# Blank/empty value will use default value +sidebar_width: 300 + +### sidebar_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_background_color: "#00FF00" + +### sidebar_hover_color +# The color of sidebar menu item upon hovring with mouse. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_hover_color: + +### sidebar_text_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_text_color: + + +# body variables +### body_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +body_background_color: "#C7DFE8" + +# boxes variables +### box_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +box_color: "#FDFFF5" + +### infobox_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +infobox_color: diff --git a/tests/testthat/sample_app_no_sidebar/program/data/.gitignore b/tests/testthat/sample_app_no_sidebar/program/data/.gitignore index 0a4bafe..94548af 100644 --- a/tests/testthat/sample_app_no_sidebar/program/data/.gitignore +++ b/tests/testthat/sample_app_no_sidebar/program/data/.gitignore @@ -1,4 +1,3 @@ * */ !.gitignore -!example.csv diff --git a/tests/testthat/sample_app_no_sidebar/program/fxn/program_helpers.R b/tests/testthat/sample_app_no_sidebar/program/fxn/program_helpers.R index 209068e..2140772 100644 --- a/tests/testthat/sample_app_no_sidebar/program/fxn/program_helpers.R +++ b/tests/testthat/sample_app_no_sidebar/program/fxn/program_helpers.R @@ -23,7 +23,13 @@ load_data2 <- function() { load_data3 <- function() { ldf <- df %>% - select(1:3) - + select(1:3) %>% + mutate(Total.Population.Change = as.numeric(gsub(",", "", Total.Population.Change)), + Natural.Increase = as.numeric(gsub(",", "", Natural.Increase))) + as.data.frame(ldf) } + +read_themes <- function() { + yaml::read_yaml("www/periscope_style.yaml") +} diff --git a/tests/testthat/sample_app_no_sidebar/program/server_local.R b/tests/testthat/sample_app_no_sidebar/program/server_local.R index 06920ce..0656ace 100644 --- a/tests/testthat/sample_app_no_sidebar/program/server_local.R +++ b/tests/testthat/sample_app_no_sidebar/program/server_local.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -83,7 +83,7 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", @@ -93,7 +93,7 @@ output$download <- renderUI({ output$alerts <- renderUI({ list(hr(), - p("There is one standardized location for alerts. To create ", + p("There is one standardized location for alerts in this app. To create ", "an alert call the following on the server: ", pre('S: createAlert(session, location, content = "Alert Text", ...)'), 'LOCATION can be: "bodyAlert", See the ', em("alertBS"), @@ -148,6 +148,54 @@ output$hover_info <- renderUI({ } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) + # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -158,33 +206,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2ggplot, - csv = plot2ggplot_data), - aspectratio = 1.5, - visibleplot = plot2ggplot) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3lattice, - tiff = plot3lattice, - txt = plot3lattice_data, - tsv = plot3lattice_data), - visibleplot = plot3lattice) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) + +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2ggplot, + csv = plot2ggplot_data), + aspectratio = 1.5, + visibleplot = plot2ggplot) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3lattice, + tiff = plot3lattice, + txt = plot3lattice_data, + tsv = plot3lattice_data), + visibleplot = plot3lattice) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -217,3 +306,75 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list( + periscope:::fw_create_body(), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();")) + ) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();"))) + }) +}) diff --git a/tests/testthat/sample_app_no_sidebar/program/ui_body.R b/tests/testthat/sample_app_no_sidebar/program/ui_body.R index 8a15e22..6f13fc1 100644 --- a/tests/testthat/sample_app_no_sidebar/program/ui_body.R +++ b/tests/testthat/sample_app_no_sidebar/program/ui_body.R @@ -40,12 +40,22 @@ body2 <- shinydashboard::box( id = "bodyElement2", collapsed = TRUE, htmlOutput("proginfo") ) +app_styling <- shinydashboard::box(id = "app_styling", + title = "Application Styling", + width = 12, + status = "primary", + collapsible = TRUE, + collapsed = TRUE, + htmlOutput("styles")) + body3 <- shinydashboard::box( id = "bodyElement3", title = "Downloadable Table", width = 12, status = "primary", collapsible = TRUE, collapsed = TRUE, + htmlOutput("table_info"), + hr(), downloadableTableUI("exampleDT1", list("csv", "tsv"), "Download table data") ) @@ -90,4 +100,4 @@ body6 <- shinydashboard::box( id = "bodyElement6", # -- Register Elements in the ORDER SHOWN in the UI # -- Note: Will be added before the standard framework footer -add_ui_body(list(body1, body2, body3, body4, body5, body6), append = FALSE) +add_ui_body(list(body1, body2, app_styling, body3, body4, body5, body6), append = FALSE) diff --git a/tests/testthat/sample_app_no_sidebar/ui.R b/tests/testthat/sample_app_no_sidebar/ui.R index c0206da..6b96e75 100644 --- a/tests/testthat/sample_app_no_sidebar/ui.R +++ b/tests/testthat/sample_app_no_sidebar/ui.R @@ -17,4 +17,4 @@ source(paste("program", "ui_body.R", sep = .Platform$file.sep), dashboardPage(periscope:::fw_create_header(), periscope:::fw_create_sidebar(showsidebar = FALSE), - periscope:::fw_create_body()) + uiOutput('body')) diff --git a/tests/testthat/sample_app_no_sidebar/www/periscope_style.yaml b/tests/testthat/sample_app_no_sidebar/www/periscope_style.yaml new file mode 100644 index 0000000..334f142 --- /dev/null +++ b/tests/testthat/sample_app_no_sidebar/www/periscope_style.yaml @@ -0,0 +1,47 @@ +### primary_color +# Sets the primary status color that affects the color of the header, valueBox, infoBox and box. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +primary_color: "#31A5CC" + + +# Sidebar variables: change the default sidebar width, colors: +### sidebar_width +# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only. +# Valid possible value are 200, 350, 425, ... +# Blank/empty value will use default value +sidebar_width: 300 + +### sidebar_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_background_color: "#00FF00" + +### sidebar_hover_color +# The color of sidebar menu item upon hovring with mouse. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_hover_color: + +### sidebar_text_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_text_color: + + +# body variables +### body_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +body_background_color: "#C7DFE8" + +# boxes variables +### box_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +box_color: "#FDFFF5" + +### infobox_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +infobox_color: diff --git a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/data/.gitignore b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/data/.gitignore index 0a4bafe..94548af 100644 --- a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/data/.gitignore +++ b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/data/.gitignore @@ -1,4 +1,3 @@ * */ !.gitignore -!example.csv diff --git a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/fxn/program_helpers.R b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/fxn/program_helpers.R index 209068e..2140772 100644 --- a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/fxn/program_helpers.R +++ b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/fxn/program_helpers.R @@ -23,7 +23,13 @@ load_data2 <- function() { load_data3 <- function() { ldf <- df %>% - select(1:3) - + select(1:3) %>% + mutate(Total.Population.Change = as.numeric(gsub(",", "", Total.Population.Change)), + Natural.Increase = as.numeric(gsub(",", "", Natural.Increase))) + as.data.frame(ldf) } + +read_themes <- function() { + yaml::read_yaml("www/periscope_style.yaml") +} diff --git a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/server_local.R b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/server_local.R index 06920ce..0656ace 100644 --- a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/server_local.R +++ b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/server_local.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -83,7 +83,7 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", @@ -93,7 +93,7 @@ output$download <- renderUI({ output$alerts <- renderUI({ list(hr(), - p("There is one standardized location for alerts. To create ", + p("There is one standardized location for alerts in this app. To create ", "an alert call the following on the server: ", pre('S: createAlert(session, location, content = "Alert Text", ...)'), 'LOCATION can be: "bodyAlert", See the ', em("alertBS"), @@ -148,6 +148,54 @@ output$hover_info <- renderUI({ } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) + # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -158,33 +206,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2ggplot, - csv = plot2ggplot_data), - aspectratio = 1.5, - visibleplot = plot2ggplot) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3lattice, - tiff = plot3lattice, - txt = plot3lattice_data, - tsv = plot3lattice_data), - visibleplot = plot3lattice) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) + +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2ggplot, + csv = plot2ggplot_data), + aspectratio = 1.5, + visibleplot = plot2ggplot) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3lattice, + tiff = plot3lattice, + txt = plot3lattice_data, + tsv = plot3lattice_data), + visibleplot = plot3lattice) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -217,3 +306,75 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list( + periscope:::fw_create_body(), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();")) + ) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();"))) + }) +}) diff --git a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/ui_body.R b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/ui_body.R index 8a15e22..6f13fc1 100644 --- a/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/ui_body.R +++ b/tests/testthat/sample_app_no_sidebar_no_resetbutton/program/ui_body.R @@ -40,12 +40,22 @@ body2 <- shinydashboard::box( id = "bodyElement2", collapsed = TRUE, htmlOutput("proginfo") ) +app_styling <- shinydashboard::box(id = "app_styling", + title = "Application Styling", + width = 12, + status = "primary", + collapsible = TRUE, + collapsed = TRUE, + htmlOutput("styles")) + body3 <- shinydashboard::box( id = "bodyElement3", title = "Downloadable Table", width = 12, status = "primary", collapsible = TRUE, collapsed = TRUE, + htmlOutput("table_info"), + hr(), downloadableTableUI("exampleDT1", list("csv", "tsv"), "Download table data") ) @@ -90,4 +100,4 @@ body6 <- shinydashboard::box( id = "bodyElement6", # -- Register Elements in the ORDER SHOWN in the UI # -- Note: Will be added before the standard framework footer -add_ui_body(list(body1, body2, body3, body4, body5, body6), append = FALSE) +add_ui_body(list(body1, body2, app_styling, body3, body4, body5, body6), append = FALSE) diff --git a/tests/testthat/sample_app_no_sidebar_no_resetbutton/ui.R b/tests/testthat/sample_app_no_sidebar_no_resetbutton/ui.R index 4f070d1..6ccf32b 100644 --- a/tests/testthat/sample_app_no_sidebar_no_resetbutton/ui.R +++ b/tests/testthat/sample_app_no_sidebar_no_resetbutton/ui.R @@ -17,4 +17,4 @@ source(paste("program", "ui_body.R", sep = .Platform$file.sep), dashboardPage(periscope:::fw_create_header(), periscope:::fw_create_sidebar(showsidebar = FALSE, resetbutton = FALSE), - periscope:::fw_create_body()) + uiOutput('body')) diff --git a/tests/testthat/sample_app_no_sidebar_no_resetbutton/www/periscope_style.yaml b/tests/testthat/sample_app_no_sidebar_no_resetbutton/www/periscope_style.yaml new file mode 100644 index 0000000..b17949b --- /dev/null +++ b/tests/testthat/sample_app_no_sidebar_no_resetbutton/www/periscope_style.yaml @@ -0,0 +1,47 @@ +### primary_color +# Sets the primary status color that affects the color of the header, valueBox, infoBox and box. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +primary_color: '#CC316F' + + +# Sidebar variables: change the default sidebar width, colors: +### sidebar_width +# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only. +# Valid possible value are 200, 350, 425, ... +# Blank/empty value will use default value +sidebar_width: 300 + +### sidebar_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_background_color: '#2200FF' + +### sidebar_hover_color +# The color of sidebar menu item upon hovring with mouse. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_hover_color: + +### sidebar_text_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_text_color: + + +# body variables +### body_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +body_background_color: '#C7DFE8' + +# boxes variables +### box_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +box_color: '#FDFFF5' + +### infobox_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +infobox_color: diff --git a/tests/testthat/sample_app_r_sidebar/program/data/.gitignore b/tests/testthat/sample_app_r_sidebar/program/data/.gitignore index 0a4bafe..94548af 100644 --- a/tests/testthat/sample_app_r_sidebar/program/data/.gitignore +++ b/tests/testthat/sample_app_r_sidebar/program/data/.gitignore @@ -1,4 +1,3 @@ * */ !.gitignore -!example.csv diff --git a/tests/testthat/sample_app_r_sidebar/program/fxn/program_helpers.R b/tests/testthat/sample_app_r_sidebar/program/fxn/program_helpers.R index 209068e..2140772 100644 --- a/tests/testthat/sample_app_r_sidebar/program/fxn/program_helpers.R +++ b/tests/testthat/sample_app_r_sidebar/program/fxn/program_helpers.R @@ -23,7 +23,13 @@ load_data2 <- function() { load_data3 <- function() { ldf <- df %>% - select(1:3) - + select(1:3) %>% + mutate(Total.Population.Change = as.numeric(gsub(",", "", Total.Population.Change)), + Natural.Increase = as.numeric(gsub(",", "", Natural.Increase))) + as.data.frame(ldf) } + +read_themes <- function() { + yaml::read_yaml("www/periscope_style.yaml") +} diff --git a/tests/testthat/sample_app_r_sidebar/program/server_local.R b/tests/testthat/sample_app_r_sidebar/program/server_local.R index b88be81..e4a11d6 100644 --- a/tests/testthat/sample_app_r_sidebar/program/server_local.R +++ b/tests/testthat/sample_app_r_sidebar/program/server_local.R @@ -25,8 +25,8 @@ # -- IMPORTS -- - # -- VARIABLES -- +load_themes <- reactiveValues(themes = NULL) # -- FUNCTIONS -- @@ -114,7 +114,7 @@ output$download <- renderUI({ "extensions and corresponding data functions with the ", "following code:"), p(pre("U: downloadFileButton('uiID', list(extensions))"), - pre("S: callModule(downloadFile, 'uiID', logger, 'filenameroot', list(datafxns)"), + pre("S: downloadFile('uiID', logger, 'filenameroot', list(datafxns)"), "Single Download: ", downloadFileButton("exampleDownload1", c("csv"), "csv"), "Multiple-choice Download: ", @@ -124,15 +124,19 @@ output$download <- renderUI({ output$alerts <- renderUI({ list(hr(), - p("There is one standardized location for alerts. To create ", + p("There are two standardized locations for alerts in this app. To create ", "an alert call the following on the server: ", pre('S: createAlert(session, location, content = "Alert Text", ...)'), - 'LOCATION can be: "bodyAlert", See the ', em("alertBS"), + 'LOCATION can be: "bodyAlert" and "sidebarRightAlert", See the ', em("alertBS"), "documentation for more information on styles and other options"), div(align = "center", bsButton( "exampleBodyAlert", label = "Body", style = "info", + width = "25%"), + bsButton( "exampleRightAlert", + label = "Sidebar - Right", + style = "danger", width = "25%")) ) }) @@ -179,6 +183,53 @@ output$hover_info <- renderUI({ } }) +output$styles <- renderUI({ + load_themes$themes <- read_themes() + list(p("User can control primary aspects of the application's styles by modifying the www/periscope_style.yaml file.\n This interactive example can be used to explore those parameters."), + p("Color values can be specified as:", + tags$ul(tags$li("Hex Value:", HTML(" "), tags$b(tags$i("i.e. '#31A5CC'"))), + tags$li("RGB Value:", HTML(" "), tags$b(tags$i("i.e. 'rgb(49, 165, 204)'"))), + tags$li("Color Name:", HTML(" "), tags$b(tags$i("i.e. 'green', 'red', ..."))))), + fluidRow( + column(width = 6, + colourpicker::colourInput("primary_color", + ui_tooltip("primary_tip", + "Primary Color", + "Sets the primary status color that affects the color of the header, valueBox, infoBox and box."), + load_themes$themes[["primary_color"]])), + column(width = 6, + numericInput("sidebar_width", + ui_tooltip("sidebar_width_tip", + "Sidebar Width", + "Change the default sidebar width"), + load_themes$themes[["sidebar_width"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("sidebar_background_color", + ui_tooltip("sidebar_background_color_tip", + "Sidebar Background Color", + "Change the default sidebar background color"), + load_themes$themes[["sidebar_background_color"]])), + column(width = 6, + colourpicker::colourInput("body_background_color", + ui_tooltip("body_background_color_tip", + "Body Background Color", + "Change body background color"), + load_themes$themes[["body_background_color"]]))), + fluidRow( + column(width = 6, + colourpicker::colourInput("box_color", + ui_tooltip("box_color_tip", + "Box Color", + "Change box default color"), + load_themes$themes[["box_color"]])), + column(width = 6, + br(), + bsButton("updateStyles", + label = "Update Application Theme"), + style = "margin-top: 5px;"))) + +}) # -- CanvasXpress Plot Example output$examplePlot1 <- renderCanvasXpress({ @@ -193,33 +244,74 @@ loginfo("Be Sure to Remember to Log ALL user actions", logger = ss_userAction.Log) # -- Setup Download Modules with Functions we want called -callModule(downloadFile, "exampleDownload1", ss_userAction.Log, - "examplesingle", - list(csv = load_data1)) -callModule(downloadFile, "exampleDownload2", ss_userAction.Log, - "examplemulti", - list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) -callModule(downloadableTable, "exampleDT1", ss_userAction.Log, - "exampletable", - list(csv = load_data3, tsv = load_data3), - load_data3, - rownames = FALSE) - -callModule(downloadablePlot, "examplePlot2", ss_userAction.Log, - filenameroot = "plot2_ggplot", - downloadfxns = list(jpeg = plot2, - csv = plot2_data), - aspectratio = 1.5, - visibleplot = plot2) - -callModule(downloadablePlot, "examplePlot3", ss_userAction.Log, - filenameroot = "plot3_lattice", - aspectratio = 2, - downloadfxns = list(png = plot3, - tiff = plot3, - txt = plot3_data, - tsv = plot3_data), - visibleplot = plot3) +downloadFile("exampleDownload1", + ss_userAction.Log, + "examplesingle", + list(csv = load_data1)) +downloadFile("exampleDownload2", + ss_userAction.Log, + "examplemulti", + list(csv = load_data2, xlsx = load_data2, tsv = load_data2)) +sketch <- htmltools::withTags( + table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics")), + tr( + th("Change"), + th("Increase"))) +)) + +downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval(c(7614, 15914, 34152), + c("lightgray", "gray", "cadetblue", "#808000")))) + +output$table_info <- renderUI({ + list( + tags$ul(tags$li("User can customize downloadableTable modules using DT options such as:", + tags$ul(tags$li("labels:", HTML(" "), + tags$b(tags$i("i.e. 'colnames', 'caption', ..."))), + tags$li("layout and columns styles:", HTML(" "), + tags$b(tags$i("i.e. 'container', 'formatStyle', ..."))), + tags$li("other addons:", HTML(" "), + tags$b(tags$i("i.e. 'filter', 'callback', ..."))))), + tags$li("For more information about table options please visit the", + tags$a("DT documentation", target = "_blank", href = "https://rstudio.github.io/DT/"), + "site") + )) +}) + + +downloadablePlot("examplePlot2", + ss_userAction.Log, + filenameroot = "plot2_ggplot", + downloadfxns = list(jpeg = plot2, + csv = plot2_data), + aspectratio = 1.5, + visibleplot = plot2) + +downloadablePlot("examplePlot3", + ss_userAction.Log, + filenameroot = "plot3_lattice", + aspectratio = 2, + downloadfxns = list(png = plot3, + tiff = plot3, + txt = plot3_data, + tsv = plot3_data), + visibleplot = plot3) # -- Observe UI Changes observeEvent(input$exampleBasicAlert, { @@ -261,3 +353,75 @@ observeEvent(input$showWorking, { logger = ss_userAction.Log) Sys.sleep(5) }) + +output$body <- renderUI({ + list( + periscope:::fw_create_body(), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();")) + ) +}) + +observeEvent(input$updateStyles, { + req(input$primary_color) + req(input$sidebar_width) + req(input$sidebar_background_color) + req(input$body_background_color) + req(input$box_color) + + lines <- c("### primary_color", + "# Sets the primary status color that affects the color of the header, valueBox, infoBox and box.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("primary_color: '", input$primary_color, "'\n\n"), + + + "# Sidebar variables: change the default sidebar width, colors:", + "### sidebar_width", + "# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only.", + "# Valid possible value are 200, 350, 425, ...", + "# Blank/empty value will use default value", + paste0("sidebar_width: ", input$sidebar_width, "\n"), + + "### sidebar_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("sidebar_background_color: '", input$sidebar_background_color, "'\n"), + + "### sidebar_hover_color", + "# The color of sidebar menu item upon hovring with mouse.", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_hover_color: \n", + + "### sidebar_text_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "sidebar_text_color: \n\n", + + "# body variables", + "### body_background_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("body_background_color: '", input$body_background_color, "'\n"), + + "# boxes variables", + "### box_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + paste0("box_color: '", input$box_color, "'\n"), + + "### infobox_color", + "# Valid values are names of the color or hex-decimal value of the color (i.e,: \"blue\", \"#086A87\").", + "# Blank/empty value will use default value", + "infobox_color: ") + + write(lines, "www/periscope_style.yaml", append = F) + load_themes$themes <- read_themes() + output$body <- renderUI({ + list(periscope:::fw_create_body(), + shiny::tags$script("$('#app_styling').closest('.box').find('[data-widget=collapse]').click();"), + shiny::tags$script(shiny::HTML("setTimeout(function (){$('div.navbar-custom-menu').click()}, 1000);")), + shiny::tags$script(shiny::HTML("$('div.navbar-custom-menu').click();"))) + }) +}) diff --git a/tests/testthat/sample_app_r_sidebar/program/ui_body.R b/tests/testthat/sample_app_r_sidebar/program/ui_body.R index 8a15e22..6f13fc1 100644 --- a/tests/testthat/sample_app_r_sidebar/program/ui_body.R +++ b/tests/testthat/sample_app_r_sidebar/program/ui_body.R @@ -40,12 +40,22 @@ body2 <- shinydashboard::box( id = "bodyElement2", collapsed = TRUE, htmlOutput("proginfo") ) +app_styling <- shinydashboard::box(id = "app_styling", + title = "Application Styling", + width = 12, + status = "primary", + collapsible = TRUE, + collapsed = TRUE, + htmlOutput("styles")) + body3 <- shinydashboard::box( id = "bodyElement3", title = "Downloadable Table", width = 12, status = "primary", collapsible = TRUE, collapsed = TRUE, + htmlOutput("table_info"), + hr(), downloadableTableUI("exampleDT1", list("csv", "tsv"), "Download table data") ) @@ -90,4 +100,4 @@ body6 <- shinydashboard::box( id = "bodyElement6", # -- Register Elements in the ORDER SHOWN in the UI # -- Note: Will be added before the standard framework footer -add_ui_body(list(body1, body2, body3, body4, body5, body6), append = FALSE) +add_ui_body(list(body1, body2, app_styling, body3, body4, body5, body6), append = FALSE) diff --git a/tests/testthat/sample_app_r_sidebar/program/ui_sidebar_right.R b/tests/testthat/sample_app_r_sidebar/program/ui_sidebar_right.R index 6742ad0..e804c40 100644 --- a/tests/testthat/sample_app_r_sidebar/program/ui_sidebar_right.R +++ b/tests/testthat/sample_app_r_sidebar/program/ui_sidebar_right.R @@ -23,23 +23,50 @@ # -- Create Elements -tab1 <- rightSidebarTabContent( - id = 1, - icon = "desktop", - title = "Tab 1 - Plots", - active = TRUE, - checkboxInput("enableGGPlot", "Enable GGPlot", value = TRUE), - checkboxInput("enableLatticePlot", "Enable Lattice Plot", value = TRUE), - checkboxInput("enableCXPlot", "Enable CanvasXpress Plot", value = TRUE)) - -tab2 <- rightSidebarTabContent( - id = 2, - title = "Tab 2 - Datatable") - -tab3 <- rightSidebarTabContent( - id = 3, - title = "Tab 3 - Other", - icon = "paint-brush") +if (utils::packageVersion('shinydashboardPlus') < 2) { + tab1 <- rightSidebarTabContent( + id = 1, + icon = "desktop", + title = "Tab 1 - Plots", + active = TRUE, + checkboxInput("enableGGPlot", "Enable GGPlot", value = TRUE), + checkboxInput("enableLatticePlot", "Enable Lattice Plot", value = TRUE), + checkboxInput("enableCXPlot", "Enable CanvasXpress Plot", value = TRUE)) + + tab2 <- rightSidebarTabContent( + id = 2, + title = "Tab 2 - Datatable") + + tab3 <- rightSidebarTabContent( + id = 3, + title = "Tab 3 - Other", + icon = "paint-brush") + + plus_fxn <- list(tab1, tab2, tab3) +} else { + tab1 <- controlbarItem( + id = 1, + title = icon("desktop"), + "Tab 1 - Plots", + checkboxInput("enableGGPlot", "Enable GGPlot", value = TRUE), + checkboxInput("enableLatticePlot", "Enable Lattice Plot", value = TRUE), + checkboxInput("enableCXPlot", "Enable CanvasXpress Plot", value = TRUE) + ) + + tab2 <- controlbarItem( + id = 2, + title = icon("database"), + "Tab 2 - Datatable", + ) + + tab3 <- controlbarItem( + id = 3, + title = icon("paint-brush"), + "Tab 3 - Other", + ) + + plus_fxn <- controlbarMenu(tab1, tab2, tab3) +} # -- Register Basic Elements in the ORDER SHOWN in the UI -add_ui_sidebar_right(list(tab1, tab2, tab3)) +add_ui_sidebar_right(plus_fxn) diff --git a/tests/testthat/sample_app_r_sidebar/ui.R b/tests/testthat/sample_app_r_sidebar/ui.R index cfdc2ad..6f610ee 100644 --- a/tests/testthat/sample_app_r_sidebar/ui.R +++ b/tests/testthat/sample_app_r_sidebar/ui.R @@ -17,8 +17,16 @@ source(paste("program", "ui_body.R", sep = .Platform$file.sep), local = TRUE) -dashboardPagePlus(periscope:::fw_create_header_plus(), - periscope:::fw_create_sidebar(showsidebar = FALSE), - periscope:::fw_create_body(), - periscope:::fw_create_right_sidebar(), - sidebar_fullCollapse = TRUE) +addl_opts <- list() +if (utils::packageVersion('shinydashboardPlus') < 2) { + plus_fxn <- getExportedValue("shinydashboardPlus", "dashboardPagePlus") + addl_opts <- list(sidebar_fullCollapse = TRUE) +} else { + plus_fxn <- getExportedValue("shinydashboardPlus", "dashboardPage") +} + +do.call(plus_fxn, c(list(periscope:::fw_create_header_plus(), + periscope:::fw_create_sidebar(showsidebar = FALSE, resetbutton = FALSE), + uiOutput('body'), + periscope:::fw_create_right_sidebar()), + addl_opts)) diff --git a/tests/testthat/sample_app_r_sidebar/www/periscope_style.yaml b/tests/testthat/sample_app_r_sidebar/www/periscope_style.yaml new file mode 100644 index 0000000..334f142 --- /dev/null +++ b/tests/testthat/sample_app_r_sidebar/www/periscope_style.yaml @@ -0,0 +1,47 @@ +### primary_color +# Sets the primary status color that affects the color of the header, valueBox, infoBox and box. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +primary_color: "#31A5CC" + + +# Sidebar variables: change the default sidebar width, colors: +### sidebar_width +# Width is to be specified as a numeric value in pixels. Must be greater than 0 and include numbers only. +# Valid possible value are 200, 350, 425, ... +# Blank/empty value will use default value +sidebar_width: 300 + +### sidebar_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_background_color: "#00FF00" + +### sidebar_hover_color +# The color of sidebar menu item upon hovring with mouse. +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_hover_color: + +### sidebar_text_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +sidebar_text_color: + + +# body variables +### body_background_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +body_background_color: "#C7DFE8" + +# boxes variables +### box_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +box_color: "#FDFFF5" + +### infobox_color +# Valid values are names of the color or hex-decimal value of the color (i.e,: "blue", "#086A87"). +# Blank/empty value will use default value +infobox_color: diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 4cca672..a913d95 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -2,6 +2,7 @@ require(testthat) require(shiny) require(periscope) require(shinydashboardPlus) +require(ggplot2) if (interactive()) { test_source_path <- "periscope/R" diff --git a/tests/testthat/test_app_reset.R b/tests/testthat/test_app_reset.R index 9d557cc..2ee9407 100755 --- a/tests/testthat/test_app_reset.R +++ b/tests/testthat/test_app_reset.R @@ -5,37 +5,61 @@ test_that(".appResetButton", { expect_snapshot_output(.appResetButton("myid")) }) -test_that(".appReset - no reset button", { - # there is no reset button on the UI for the app - testServer(.appReset, - {session$setInputs(resetPending = NULL) - expect_silent(.appReset)}) +test_that("app_reset - no reset button", { + testServer(app_reset, + expr = { + session$setInputs(resetPending = NULL, logger = periscope:::fw_get_user_log()) + expect_null(session$getReturned()) + }) }) -test_that(".appReset - reset button - no pending", { - # there is no reset button on the UI for the app - suppressWarnings(testServer(.appReset, - {session$setInputs(resetButton = TRUE, resetPending = FALSE) - expect_silent(.appReset)})) +test_that("app_reset - reset button - no pending", { + expect_silent(app_reset(input = list(resetButton = TRUE, resetPending = FALSE), + output = list(), + session = MockShinySession$setInputs(resetButton = TRUE, + resetPending = FALSE), + logger = periscope:::fw_get_user_log())) }) -test_that(".appReset - no reset button - with pending", { - # there is no reset button on the UI for the app - suppressWarnings(testServer(.appReset, - {session$setInputs(resetButton = FALSE, resetPending = TRUE) - expect_silent(.appReset)})) +test_that("app_reset - no reset button - with pending", { + expect_silent(app_reset(input = list(resetButton = FALSE, resetPending = TRUE), + output = list(), + session = MockShinySession$setInputs(resetButton = TRUE, + resetPending = FALSE), + logger = periscope:::fw_get_user_log())) }) -test_that(".appReset - reset button - with pending", { - suppressWarnings(testServer(.appReset, - {session$setInputs(resetButton = TRUE, resetPending = TRUE) - expect_silent(.appReset)})) +test_that("app_reset - reset button - with pending", { + expect_silent(app_reset(input = list(resetButton = TRUE, resetPending = TRUE), + output = list(), + session = MockShinySession$setInputs(resetButton = TRUE, + resetPending = FALSE), + logger = periscope:::fw_get_user_log())) }) -test_that(".appReset", { - expect_silent(.appReset(input = list(resetButton = TRUE, resetPending = FALSE), - output = list(), +test_that("app_reset", { + expect_silent(app_reset(input = list(resetButton = FALSE, resetPending = FALSE), + output = list(), session = MockShinySession$setInputs(resetButton = TRUE, resetPending = FALSE), logger = periscope:::fw_get_user_log())) }) + +test_that(".appReset", { + reset <- shiny::callModule(.appReset, + "reset", + input = list(), + output = list(), + session = MockShinySession$new(), + periscope:::fw_get_user_log()) + expect_equal(class(reset)[[1]], "Observer") + expect_equal(class(reset)[[2]], "R6") +}) + +test_that(".appReset - new call", { + expect_error(.appReset("reset", + input = list(), + output = list(), + session = MockShinySession$new(), + logger = periscope:::fw_get_user_log())) +}) diff --git a/tests/testthat/test_body_footer.R b/tests/testthat/test_body_footer.R index ec2d180..0be54e7 100755 --- a/tests/testthat/test_body_footer.R +++ b/tests/testthat/test_body_footer.R @@ -1,10 +1,37 @@ context("periscope - Body footer") +# Helper functions +data <- function(){ + c("line 1", "line 2", "line 3") +} +data2 <- function(){ + NULL +} + +# UI unit tests test_that(".bodyFooterOutput", { local_edition(3) expect_snapshot_output(.bodyFooterOutput("myid")) }) + +# Server unit tests test_that(".bodyFooter", { - testServer(.bodyFooter, {expect_silent(.bodyFooter)}) + footer <- shiny::callModule(.bodyFooter, "footer", input = list(), + output = list(), + session = MockShinySession$new(), + logdata = data) + expect_equal(class(footer)[[1]], "shiny.render.function") +}) + +test_that("body_footer ", { + expect_silent(body_footer(input = list(), + output = list(), + session = MockShinySession$new(), + logdata = data)) + + expect_silent(body_footer(input = list(), + output = list(), + session = MockShinySession$new(), + logdata = data2)) }) diff --git a/tests/testthat/test_convert_application.R b/tests/testthat/test_convert_application.R index 91a02f4..555ae23 100644 --- a/tests/testthat/test_convert_application.R +++ b/tests/testthat/test_convert_application.R @@ -223,7 +223,7 @@ test_that("remove_reset_button both sidebar", { test_that("remove_reset_button r sidebar", { app_location <- create_app_tmp_dir(left_sidebar = FALSE, right_sidebar = TRUE) - expect_message(remove_reset_button(location = app_location), "Left sidebar not available, reset button cannot be removed") + expect_message(remove_reset_button(location = app_location), "Reset button already removed, no conversion needed") }) ## add_reset_button tests diff --git a/tests/testthat/test_create_new_application.R b/tests/testthat/test_create_new_application.R index f122b81..91023a5 100755 --- a/tests/testthat/test_create_new_application.R +++ b/tests/testthat/test_create_new_application.R @@ -1,7 +1,7 @@ context("periscope create new application") - -expect_cleanup_create_new_application <- function(fullname, sampleapp = FALSE, dashboard_plus = FALSE, leftsidebar = TRUE, skin = NULL) { +expect_cleanup_create_new_application <- function(fullname, sampleapp = FALSE, dashboard_plus = FALSE, leftsidebar = TRUE) { + local_edition(3) expect_true(dir.exists(fullname)) expect_true(file.exists(paste0(fullname, "/global.R"))) expect_true(file.exists(paste0(fullname, "/server.R"))) @@ -9,6 +9,7 @@ expect_cleanup_create_new_application <- function(fullname, sampleapp = FALSE, d expect_true(dir.exists(paste0(fullname, "/www"))) expect_true(dir.exists(paste0(fullname, "/www/css"))) expect_true(dir.exists(paste0(fullname, "/www/js"))) + expect_true(file.exists(paste0(fullname, "/www/periscope_style.yaml"))) expect_true(dir.exists(paste0(fullname, "/www/img"))) expect_true(file.exists(paste0(fullname, "/www/img/loader.gif"))) expect_true(file.exists(paste0(fullname, "/www/img/tooltip.png"))) @@ -37,13 +38,6 @@ expect_cleanup_create_new_application <- function(fullname, sampleapp = FALSE, d } else { expect_true(!file.exists(paste0(fullname, "/program/ui_sidebar_right.R"))) } - if (!is.null(skin)) { - ui_file <- file(paste0(fullname, "/ui.R"), open = "r") - ui_content <- readLines(con = ui_file) - close(ui_file) - expect_true(any(grepl(skin, ui_content))) - } - # clean up unlink(fullname, TRUE) } @@ -141,43 +135,26 @@ test_that("create_new_application no reset button, no left sidebar", { expect_cleanup_create_new_application(appTemp, sampleapp = TRUE, leftsidebar = FALSE) }) -test_that("create_new_application custom style", { - appTemp.dir <- tempdir() - appTemp <- tempfile(pattern = "TestThatApp", tmpdir = appTemp.dir) - appTemp.name <- gsub('\\\\|/', '', (gsub(appTemp.dir, "", appTemp, fixed = T))) - - expect_message(create_new_application(name = appTemp.name, location = appTemp.dir, sampleapp = FALSE, rightsidebar = NULL, style = list(skin = "green")), - "Framework creation was successful.") - expect_cleanup_create_new_application(appTemp, skin = "green") -}) -test_that("create_new_application bad style", { +test_that("create_new_application invalid yaml file", { appTemp.dir <- tempdir() appTemp <- tempfile(pattern = "TestThatApp", tmpdir = appTemp.dir) appTemp.name <- gsub('\\\\|/', '', (gsub(appTemp.dir, "", appTemp, fixed = T))) - expect_error(create_new_application(name = appTemp.name, location = appTemp.dir, sampleapp = FALSE, rightsidebar = NULL, style = list("green")), - "Framework creation could not proceed, invalid type for skin, only character allowed") + expect_warning(create_new_application(name = appTemp.name, location = appTemp.dir, sampleapp = FALSE, rightsidebar = NULL, custom_theme_file = ""), + "'custom_theme_file' must be single character value pointing to valid yaml file location. Using default values.") }) -test_that("create_new_application custom style right sidebar", { +test_that("create_new_application with valid yaml file", { appTemp.dir <- tempdir() appTemp <- tempfile(pattern = "TestThatApp", tmpdir = appTemp.dir) appTemp.name <- gsub('\\\\|/', '', (gsub(appTemp.dir, "", appTemp, fixed = T))) + yaml_loc <- "sample_app/www/periscope_style.yaml" - expect_message(create_new_application(name = appTemp.name, location = appTemp.dir, sampleapp = FALSE, rightsidebar = TRUE, style = list(skin = "green")), + expect_message(create_new_application(name = appTemp.name, location = appTemp.dir, sampleapp = FALSE, rightsidebar = NULL, custom_theme_file = yaml_loc), "Framework creation was successful.") - expect_cleanup_create_new_application(appTemp, dashboard_plus = TRUE, skin = "green") }) -test_that("create_new_application invalid style", { - appTemp.dir <- tempdir() - appTemp <- tempfile(pattern = "TestThatApp", tmpdir = appTemp.dir) - appTemp.name <- gsub('\\\\|/', '', (gsub(appTemp.dir, "", appTemp, fixed = T))) - - expect_error(create_new_application(name = appTemp.name, location = appTemp.dir, sampleapp = FALSE, rightsidebar = NULL, style = mtcars), - "Framework creation could not proceed, invalid type for style, only list allowed") -}) test_that("create_new_application invalid location", { expect_warning(create_new_application(name = "Invalid", location = tempfile(), sampleapp = FALSE), diff --git a/tests/testthat/test_download_file.R b/tests/testthat/test_download_file.R index db442c0..98a82c1 100755 --- a/tests/testthat/test_download_file.R +++ b/tests/testthat/test_download_file.R @@ -1,6 +1,31 @@ context("periscope - download file") +# helper functions +download_plot <- function() { + ggplot2::ggplot(data = mtcars, aes(x = wt, y = mpg)) + + geom_point(aes(color = cyl)) + + theme(legend.justification = c(1, 1), + legend.position = c(1, 1), + legend.title = element_blank()) + + ggtitle("GGPlot Example w/Hover") + + xlab("wt") + + ylab("mpg") +} +download_data <- function() { + mtcars +} + +download_data_show_row_names <- function() { + attr(mtcars, "show_rownames") <- TRUE + mtcars +} + +download_string_list <- function() { + c("test1", "test2", "tests") +} + +# UI Testing test_that("downloadFileButton", { local_edition(3) expect_snapshot_output(downloadFileButton(id = "myid", @@ -15,6 +40,7 @@ test_that("downloadFileButton multiple types", { hovertext = "myhovertext")) }) +# Server Testing test_that("downloadFile_ValidateTypes invalid", { result <- downloadFile_ValidateTypes(types = "csv") @@ -31,10 +57,52 @@ test_that("downloadFile_AvailableTypes", { expect_equal(result, c("csv", "xlsx", "tsv", "txt", "png", "jpeg", "tiff", "bmp")) }) -test_that("downloadFile", { - expect_silent(downloadFile(input = list(), - output = list(), - session = MockShinySession$new(), - logger = periscope:::fw_get_user_log(), - filenameroot = "mydownload1")) +test_that("download_file", { + session <- MockShinySession$new() + session$env$filenameroot <- "mydownload1" + expect_silent( + periscope:::download_file( + input = list(), + output = list(), + session = session, + logger = periscope:::fw_get_user_log(), + filenameroot = "mydownload1", + datafxns = list(csv = download_data, + xlsx = download_data, + tsv = download_data, + txt = download_data, + png = download_plot, + jpeg = download_plot, + tiff = download_plot, + bmp = download_plot)) + ) + +}) + +test_that("downloadFile_callModule", { + session <- MockShinySession$new() + session$env$filenameroot <- "mydownload1" + session$env$datafxns = list(csv = download_data, + xlsx = download_data, + tsv = download_data, + txt = download_data, + png = download_plot, + jpeg = download_plot, + tiff = download_plot, + bmp = download_plot) + expect_silent(shiny::callModule(downloadFile, + "download", + input = list(), + output = list(), + session = session, + logger = periscope:::fw_get_user_log(), + filenameroot = "mydownload1", + datafxns = list(csv = download_data, + xlsx = download_data, + tsv = download_data, + txt = download_data, + png = download_plot, + jpeg = download_plot, + tiff = download_plot, + bmp = download_plot))) }) diff --git a/tests/testthat/test_downloadable_plot.R b/tests/testthat/test_downloadable_plot.R index 0ce1946..8900049 100755 --- a/tests/testthat/test_downloadable_plot.R +++ b/tests/testthat/test_downloadable_plot.R @@ -63,11 +63,32 @@ test_that("downloadablePlotUI invalid btn_valign", { }) test_that("downloadablePlot", { - expect_error(downloadablePlot(input = list(), + download_plot <- function() { + ggplot2::ggplot(data = mtcars, aes(x = wt, y = mpg)) + + geom_point(aes(color = cyl)) + + theme(legend.justification = c(1, 1), + legend.position = c(1, 1), + legend.title = element_blank()) + + ggtitle("GGPlot Example w/Hover") + + xlab("wt") + + ylab("mpg") + } + + download_data <- function() { + mtcars + } + + expect_silent(shiny::callModule(downloadablePlot, + "download", + input = list(), output = list(), session = MockShinySession$new(), logger = periscope:::fw_get_user_log(), filenameroot = "mydownload1", - visibleplot = NULL)) + aspectratio = 2, + downloadfxns = list(png = download_plot, + tiff = download_plot, + txt = download_data, + tsv = download_data), + visibleplot = download_plot)) }) - diff --git a/tests/testthat/test_downloadable_table.R b/tests/testthat/test_downloadable_table.R index d48deac..f3ce299 100755 --- a/tests/testthat/test_downloadable_table.R +++ b/tests/testthat/test_downloadable_table.R @@ -8,11 +8,153 @@ test_that("downloadableTableUI", { hovertext = "myHoverText")) }) -test_that("downloadableTable", { - expect_error(downloadableTable(input = list(), - output = list(), - session = MockShinySession$new(), - logger = periscope:::fw_get_user_log(), - filenameroot = "mydownload1", - tabledata = NULL)) +# helper functions +data <- reactive({ + c(1,2) +}) + +mydataRowIds <- function(){ + rownames(mtcars) +} + +test_that("downloadableTable - singleSelect_FALSE_selection_enabled", { + suppressWarnings({ + session <- MockShinySession$new() + session$setInputs(dtableSingleSelect = FALSE) + session$env$filenameroot <- "mydownload1" + session$env$downloaddatafxns = list(csv = data, tsv = data) + expect_silent(shiny::callModule(downloadableTable, + "download", + input = list(dtableSingleSelect = "FALSE"), + output = list(), + session = session, + logger = periscope:::fw_get_user_log(), + filenameroot = "mydownload1", + downloaddatafxns = list(csv = data, tsv = data), + tabledata = data, + selection = mydataRowIds)) + }) +}) + +test_that("downloadableTable - free_parameters", { + suppressWarnings({ + session <- MockShinySession$new() + session$setInputs(dtableSingleSelect = FALSE) + session$env$filenameroot <- "mydownload1" + session$env$downloaddatafxns = list(csv = data, tsv = data) + expect_silent(shiny::callModule(downloadableTable, + "download", + input = list(dtableSingleSelect = "FALSE"), + output = list(), + session = session, + periscope:::fw_get_user_log(), + "mydownload1", + list(csv = data, tsv = data), + data, + selection = mydataRowIds)) + }) +}) + +test_that("downloadableTable - new module call", { + suppressWarnings({ + session <- MockShinySession$new() + session$setInputs(dtableSingleSelect = FALSE) + session$env$filenameroot <- "mydownload1" + session$env$downloaddatafxns = list(csv = data, tsv = data) + expect_error(downloadableTable("download", + input = list(dtableSingleSelect = "FALSE"), + output = list(), + session = session, + logger = periscope:::fw_get_user_log(), + filenameroot = "mydownload1", + downloaddatafxns = list(csv = data, tsv = data), + tabledata = data, + selection = mydataRowIds)) + + }) +}) + +test_that("downloadableTable - singleSelect_TRUE_selection_enabled", { + suppressWarnings({ + session <- MockShinySession$new() + session$setInputs(dtableSingleSelect = TRUE) + session$env$filenameroot <- "mydownload1" + session$env$downloaddatafxns = list(csv = data, tsv = data) + expect_silent(shiny::callModule(downloadableTable, + "download", + input = list(dtableSingleSelect = "FALSE"), + output = list(), + session = session, + logger = periscope:::fw_get_user_log(), + filenameroot = "mydownload1", + downloaddatafxns = list(csv = data, tsv = data), + tabledata = data, + selection = mydataRowIds)) + }) +}) + +test_that("downloadableTable - singleSelect and selection disabled", { + suppressWarnings({ + session <- MockShinySession$new() + session$setInputs(dtableSingleSelect = TRUE) + session$env$filenameroot <- "mydownload1" + session$env$downloaddatafxns = list(csv = data, tsv = data) + expect_silent(shiny::callModule(downloadableTable, + "download", + input = list(dtableSingleSelect = "FALSE"), + output = list(), + session = session, + logger = periscope:::fw_get_user_log(), + filenameroot = "mydownload1", + downloaddatafxns = list(csv = data, tsv = data), + tabledata = data)) + }) +}) + +test_that("downloadableTable - invalid_selection", { + suppressWarnings({ + session <- MockShinySession$new() + session$setInputs(dtableSingleSelect = TRUE) + session$env$filenameroot <- "mydownload1" + session$env$downloaddatafxns = list(csv = data, tsv = data) + expect_message(shiny::callModule(downloadableTable, + "download", + input = list(dtableSingleSelect = "FALSE"), + output = list(), + session = session, + logger = periscope:::fw_get_user_log(), + filenameroot = "mydownload1", + downloaddatafxns = list(csv = data, tsv = data), + tabledata = data, + selection = "single")) + }) +}) + +test_that("build_datatable_arguments", { + local_edition(3) + table_options <- list(rownames = FALSE, + callback = "table.order([2, 'asc']).draw();", + caption = " Very Important Information", + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + width = "150px", + height = "50px", + extensions = 'Buttons', + plugins = 'natural', + editable = TRUE, + order = list(list(2, 'asc'), list(3, 'desc'))) + expect_snapshot(build_datatable_arguments(table_options)) +}) + + +test_that("format_columns", { + local_edition(3) + set.seed(123) + dt <- cbind(matrix(rnorm(60, 1e5, 1e6), 20), runif(20), rnorm(20, 100)) + dt[, 1:3] = round(dt[, 1:3]) + dt[, 4:5] = round(dt[, 4:5], 7) + colnames(dt) = head(LETTERS, ncol(dt)) + expect_snapshot(format_columns(DT::datatable(dt), + list(formatCurrency = list(columns = c("A", "C")), + formatPercentage = list(columns = c("D"), 2)))) }) diff --git a/tests/testthat/test_ui_functions.R b/tests/testthat/test_ui_functions.R index bb7d188..49dd79d 100755 --- a/tests/testthat/test_ui_functions.R +++ b/tests/testthat/test_ui_functions.R @@ -1,24 +1,8 @@ context("periscope - UI functionality") - +local_edition(3) test_that("fw_create_header", { - result <- periscope:::fw_create_header() - expect_equal(result$name, "header") - expect_equal(result$attribs, list(class = "main-header")) - - result.children <- result$children - expect_equal(length(result.children), 3) - expect_equal(result.children[[1]], NULL) ## ? - - expect_equal(result.children[[2]]$name, "span") - expect_equal(result.children[[2]]$attribs$class, "logo") - expect_equal(length(result.children[[2]]$children), 1) - - expect_equal(result.children[[2]]$children[[1]]$name, "div") - expect_equal(result.children[[2]]$children[[1]]$attribs, list(class = "periscope-busy-ind")) - - expect_equal(length(result.children[[2]]$children[[1]]$children), 2) - expect_equal(result.children[[2]]$children[[1]]$children[[1]], "Working") + expect_snapshot_output(periscope:::fw_create_header()) }) check_sidebar_result <- function(result, showsidebar = TRUE, basic_existing = FALSE, advanced_existing = FALSE) { @@ -32,7 +16,7 @@ check_sidebar_result <- function(result, showsidebar = TRUE, basic_existing = F expect_equal(result$attribs, list(id = "sidebarCollapsed", class = "main-sidebar", 'data-collapsed' = "true")) } } - + result.children <- result$children expect_equal(length(result.children), 2) if (showsidebar) { @@ -43,14 +27,14 @@ check_sidebar_result <- function(result, showsidebar = TRUE, basic_existing = F expect_equal(class(result.children[[1]][[2]]), "list") expect_equal(class(result.children[[1]][[3]]), "list") } - + expect_equal(result.children[[2]]$name, "section") expect_equal(result.children[[2]]$attribs$class, "sidebar") expect_equal(result.children[[2]][[2]]$id, "sidebarItemExpanded") - + result.subchilds <- result.children[[2]]$children[[1]] expect_equal(length(result.subchilds), 3) - + expect_equal(result.subchilds[[1]][[1]]$name, "script") expect_true(grepl("Set using set_app_parameters\\() in program/global.R", result.subchilds[[1]][[1]]$children[[1]])) @@ -65,16 +49,13 @@ check_sidebar_result <- function(result, showsidebar = TRUE, basic_existing = F } } + test_that("fw_create_sidebar no sidebar", { - result <- periscope:::fw_create_sidebar(showsidebar = F, resetbutton = F) - - check_sidebar_result(result, showsidebar = FALSE) + expect_snapshot_output(periscope:::fw_create_sidebar(showsidebar = F, resetbutton = F)) }) test_that("fw_create_sidebar empty", { - result <- periscope:::fw_create_sidebar(showsidebar = T, resetbutton = F) - - check_sidebar_result(result, showsidebar = TRUE) + expect_snapshot_output(periscope:::fw_create_sidebar(showsidebar = T, resetbutton = F)) }) test_that("fw_create_sidebar only basic", { @@ -83,11 +64,9 @@ test_that("fw_create_sidebar only basic", { .g_opts$side_basic <- list(tags$p()) side_advanced <- shiny::isolate(.g_opts$side_advanced) .g_opts$side_advanced <- NULL - - result <- periscope:::fw_create_sidebar(showsidebar = T, resetbutton = F) - - check_sidebar_result(result, showsidebar = TRUE, basic_existing = TRUE, advanced_existing = FALSE) - + + expect_snapshot_output(periscope:::fw_create_sidebar(showsidebar = T, resetbutton = F)) + # teardown .g_opts$side_basic <- side_basic .g_opts$side_advanced <- side_advanced @@ -99,11 +78,9 @@ test_that("fw_create_sidebar only advanced", { .g_opts$side_basic <- NULL side_advanced <- shiny::isolate(.g_opts$side_advanced) .g_opts$side_advanced <- list(tags$p()) - - result <- periscope:::fw_create_sidebar() - - check_sidebar_result(result, showsidebar = TRUE, basic_existing = FALSE, advanced_existing = TRUE) - + + expect_snapshot_output(periscope:::fw_create_sidebar()) + # teardown .g_opts$side_basic <- side_basic .g_opts$side_advanced <- side_advanced @@ -115,100 +92,34 @@ test_that("fw_create_sidebar basic and advanced", { .g_opts$side_basic <- list(tags$p()) side_advanced <- shiny::isolate(.g_opts$side_advanced) .g_opts$side_advanced <- list(tags$p()) - + result <- periscope:::fw_create_sidebar() - + check_sidebar_result(result, showsidebar = TRUE, basic_existing = TRUE, advanced_existing = TRUE) - + # teardown .g_opts$side_basic <- side_basic .g_opts$side_advanced <- side_advanced }) -check_body_result <- function(result, logging = TRUE) { - expect_equal(result$name, "div") - expect_equal(result$attribs, list(class = "content-wrapper")) - - result.children <- result$children - expect_equal(length(result.children), 1) - - expect_equal(result.children[[1]]$name, "section") - expect_equal(result.children[[1]]$attribs$class, "content") - - result.subchilds <- result.children[[1]]$children - expect_equal(length(result.subchilds), 4) - - expect_equal(result.subchilds[[1]]$name, "head") - # check if tab title is set in javascript - expect_true(grepl("document.title = 'Set using set_app_parameters\\() in program/global.R'", result.subchilds[[1]]$children[[2]]$children)) - - if (logging) { - expect_equal(class(result.subchilds[[2]]), "shiny.tag") - expect_equal(result.subchilds[[2]]$name, "div") - expect_equal(result.subchilds[[2]]$attribs$class, "modal sbs-modal fade") - expect_equal(result.subchilds[[2]]$attribs$id, "titleinfobox") - expect_equal(result.subchilds[[2]]$attribs$tabindex, "-1") - expect_equal(result.subchilds[[2]]$attribs$`data-sbs-trigger`, "titleinfobox_trigger") - - expect_equal(length(result.subchilds[[4]]), 3) - - expect_equal(result.subchilds[[4]]$name, "div") - expect_equal(result.subchilds[[4]]$attribs$class, "col-sm-12") - result.subsubchilds <- result.subchilds[[4]]$children - - expect_equal(result.subsubchilds[[1]]$name, "div") - expect_equal(result.subsubchilds[[1]]$attribs$class, "box collapsed-box") - - result.subsubsubchilds <- result.subsubchilds[[1]]$children - expect_equal(length(result.subsubsubchilds), 3) - expect_equal(result.subsubsubchilds[[1]]$name, "div") - expect_equal(result.subsubsubchilds[[1]]$attribs$class, "box-header") - - result.subsubsubsubchilds <- result.subsubsubchilds[[1]]$children - expect_equal(length(result.subsubsubsubchilds), 2) - expect_equal(result.subsubsubsubchilds[[1]]$name, "h3") - expect_equal(result.subsubsubsubchilds[[1]]$attribs$class, "box-title") - - result.subsubsubsubsubchilds <- result.subsubsubsubchilds[[1]]$children - expect_equal(result.subsubsubsubsubchilds[[1]], "User Action Log") - - result.subsubsubsubsubchilds <- result.subsubsubsubchilds[[2]]$children - expect_equal(result.subsubsubsubsubchilds[[1]]$name, "button") - expect_equal(result.subsubsubsubsubchilds[[1]]$attribs, list(class = "btn btn-box-tool", 'data-widget' = "collapse")) - expect_equal(length(result.subsubsubsubsubchilds[[1]]$children), 1) - - expect_equal(result.subsubsubsubsubchilds[[1]]$children[[1]]$name, "i") - expect_equal(result.subsubsubsubsubchilds[[1]]$children[[1]]$attribs$class, "fa fa-plus") - expect_equal(result.subsubsubsubsubchilds[[1]]$children[[1]]$children, list()) - } else { - expect_equal(result.subchilds[[2]], NULL) - expect_equal(result.subchilds[[3]], NULL) - expect_equal(result.subchilds[[4]], NULL) - } -} - test_that("fw_create_body app_info", { - # setup app_info <- shiny::isolate(.g_opts$app_info) .g_opts$app_info <- HTML("app_info") - - result <- periscope:::fw_create_body() - check_body_result(result) - + + expect_snapshot_output(periscope:::fw_create_body()) + # teardown .g_opts$app_info <- app_info }) test_that("fw_create_body no log", { - # setup show_userlog <- shiny::isolate(.g_opts$show_userlog) .g_opts$show_userlog <- FALSE - - result <- periscope:::fw_create_body() - check_body_result(result, logging = FALSE) - + + expect_snapshot_output(periscope:::fw_create_body()) + # teardown .g_opts$show_userlog <- show_userlog }) @@ -244,12 +155,7 @@ test_that("add_ui_body", { }) test_that("ui_tooltip", { - result <- ui_tooltip(id = "id", label = "mylabel", text = "mytext") - expect_equal(result$name, "span") - expect_equal(result$attribs, list(class = "periscope-input-label-with-tt")) - result.children <- result$children - expect_equal(length(result.children), 3) - expect_equal(result.children[[1]], "mylabel") + expect_snapshot_output(ui_tooltip(id = "id", label = "mylabel", text = "mytext")) }) test_that("ui_tooltip no text", { @@ -257,152 +163,26 @@ test_that("ui_tooltip no text", { }) test_that("fw_create_header_plus", { - result <- periscope:::fw_create_header_plus() - expect_equal(result$name, "header") - expect_equal(result$attribs, list(class = "main-header")) - - result.children <- result$children - expect_equal(length(result.children), 3) - expect_equal(result.children[[1]], NULL) ## ? - - expect_equal(result.children[[2]]$name, "span") - expect_equal(result.children[[2]]$attribs$class, "logo") - expect_equal(length(result.children[[2]]$children), 1) - - expect_equal(result.children[[2]]$children[[1]]$name, "div") - expect_equal(result.children[[2]]$children[[1]]$attribs, list(class = "periscope-busy-ind")) - - expect_equal(length(result.children[[2]]$children[[1]]$children), 2) - expect_equal(result.children[[2]]$children[[1]]$children[[1]], "Working") - - expect_equal(result.children[[3]]$name, "nav") - expect_equal(result.children[[3]]$attribs$class, "navbar navbar-static-top") - expect_equal(length(result.children[[3]]$children), 4) - - expect_equal(result.children[[3]]$children[[1]]$name, "span") - expect_equal(result.children[[3]]$children[[1]]$attribs, list(style = "display:none;")) - - expect_equal(result.children[[3]]$children[[2]]$name, "a") - expect_equal(result.children[[3]]$children[[2]]$attribs, list(href = "#", class = "sidebar-toggle", `data-toggle` = "offcanvas", role = "button")) - - expect_equal(result.children[[3]]$children[[3]]$name, "div") - expect_equal(result.children[[3]]$children[[3]]$attribs, list(class = "navbar-custom-menu", style = "float: left; margin-left: 10px;")) - - expect_equal(result.children[[3]]$children[[4]]$name, "div") - expect_equal(result.children[[3]]$children[[4]]$attribs, list(class = "navbar-custom-menu")) + expect_snapshot_output(periscope:::fw_create_header_plus()) }) test_that("fw_create_right_sidebar", { - result <- periscope:::fw_create_right_sidebar() - - expect_equal(length(result), 2) - expect_equal(result[[1]]$name, "head") - expect_equal(length(result[[1]]$attribs), 0) - expect_equal(length(result[[1]]$children), 1) - - result1.children <- result[[1]]$children[[1]] - - expect_equal(result1.children$name, "style") - expect_equal(length(result1.children$attribs), 0) + expect_snapshot_output(periscope:::fw_create_right_sidebar()) }) test_that("fw_create_right_sidebar SDP<2", { skip_if_not(t_sdp_old) - result <- periscope:::fw_create_right_sidebar() - - expect_equal(result[[2]]$name, "div") - expect_equal(result[[2]]$attribs, list(id = "controlbar")) - expect_equal(length(result[[2]]$children), 2) - - result2.children <- result[[2]]$children - - expect_equal(result2.children[[1]]$name, "aside") - expect_equal(length(result2.children[[1]]$children), 2) - - expect_equal(result2.children[[1]]$children[[1]]$name, "ul") - expect_equal(result2.children[[1]]$children[[1]]$attribs, list(class = "nav nav-tabs nav-justified control-sidebar-tabs")) - - expect_equal(result2.children[[1]]$children[[2]]$name, "div") - expect_equal(result2.children[[1]]$children[[2]]$attribs, list(class = "controlbar tab-content")) - - expect_equal(result2.children[[2]]$name, "div") - expect_equal(result2.children[[2]]$attribs, list(class = "control-sidebar-bg", style = "width: 230px;")) - - add_ui_sidebar_right(elementlist = list(selectInput(inputId = "id", choices = 1:3, label = "Input widget"))) - result <- periscope:::fw_create_right_sidebar() - - expect_equal(length(result), 2) - expect_equal(result[[1]]$name, "head") - expect_equal(length(result[[1]]$attribs), 0) - expect_equal(length(result[[1]]$children), 1) - - result1.children <- result[[1]]$children[[1]] - - expect_equal(result1.children$name, "style") - expect_equal(length(result1.children$attribs), 0) - - expect_equal(result[[2]]$name, "div") - expect_equal(result[[2]]$attribs, list(id = "controlbar")) - expect_equal(length(result[[2]]$children), 2) - - result2.children <- result[[2]]$children - - expect_equal(result2.children[[1]]$name, "aside") - expect_equal(length(result2.children[[1]]$children), 2) - - expect_equal(result2.children[[1]]$children[[1]]$name, "ul") - expect_equal(result2.children[[1]]$children[[1]]$attribs, list(class = "nav nav-tabs nav-justified control-sidebar-tabs")) - - expect_equal(result2.children[[1]]$children[[2]]$name, "div") - expect_equal(result2.children[[1]]$children[[2]]$attribs, list(class = "controlbar tab-content")) - - result2.1.2.children <- result2.children[[1]]$children[[2]]$children - - expect_equal(result2.1.2.children[[1]]$name, "div") - expect_equal(length(result2.1.2.children[[1]]$children), 1) - - expect_equal(result2.1.2.children[[2]]$name, "div") - expect_equal(result2.1.2.children[[2]]$attribs, list(class = "form-group shiny-input-container")) - expect_equal(length(result2.1.2.children[[2]]$children), 2) - - expect_equal(result2.1.2.children[[2]]$children[[1]]$name, "label") - expect_equal(result2.1.2.children[[2]]$children[[1]]$attribs$class, "control-label") - - expect_equal(result2.1.2.children[[2]]$children[[2]]$name, "div") - expect_equal(length(result2.1.2.children[[2]]$children[[2]]$children), 2) - - expect_equal(result2.1.2.children[[2]]$children[[2]]$children[[1]]$name, "select") - expect_equal(result2.1.2.children[[2]]$children[[2]]$children[[1]]$attribs, list(id = "id")) - - expect_equal(result2.1.2.children[[2]]$children[[2]]$children[[2]]$name, "script") - expect_equal(result2.1.2.children[[2]]$children[[2]]$children[[2]]$attribs, list(type = "application/json", `data-for` = "id", `data-nonempty` = "")) - - expect_equal(result2.children[[2]]$name, "div") - expect_equal(result2.children[[2]]$attribs, list(class = "control-sidebar-bg", style = "width: 230px;")) + expect_snapshot_output(periscope:::fw_create_right_sidebar()) + expect_snapshot_output(add_ui_sidebar_right(elementlist = list(selectInput(inputId = "id", choices = 1:3, label = "Input widget")))) + expect_snapshot_output(periscope:::fw_create_right_sidebar()) }) test_that("fw_create_right_sidebar SDP>=2", { skip_if(t_sdp_old) - result <- periscope:::fw_create_right_sidebar() - result2 <- result[[2]] - - expect_equal(length(result2), 2) - expect_equal(result2[[1]]$name, "aside") - expect_equal(result2[[1]]$attribs$id, "controlbarId") - - result2.1child <- result2[[1]]$children - - expect_equal(length(result2.1child), 1) - - expect_equal(length(result2.1child[[1]][[1]]), 3) - expect_equal(result2.1child[[1]][[1]]$name, "div") - expect_equal(result2.1child[[1]][[1]]$attribs$id, "sidebarRightAlert") - - result2.2child <- result2[[2]]$children - expect_equal(length(result2.2child), 0) - }) + expect_snapshot_output(periscope:::fw_create_right_sidebar()) +}) test_that("add_ui_sidebar_right", { result <- add_ui_sidebar_right(elementlist = NULL) @@ -412,7 +192,7 @@ test_that("add_ui_sidebar_right", { test_that("add_ui_sidebar_right with append", { result <- add_ui_sidebar_right(elementlist = NULL, append = TRUE) expect_null(result, "add_ui_sidebar_right") - + result <- add_ui_sidebar_right(elementlist = NULL, append = FALSE) expect_null(result, "add_ui_sidebar_right") }) diff --git a/tests/testthat/test_ui_misc_functions.R b/tests/testthat/test_ui_misc_functions.R index 6153165..2eb8ab0 100755 --- a/tests/testthat/test_ui_misc_functions.R +++ b/tests/testthat/test_ui_misc_functions.R @@ -63,5 +63,10 @@ test_that("fw_server_setup", { logger = periscope:::fw_get_user_log())) }) +test_that("is_valid_color", { + expect_true(is_valid_color("green")) + expect_false(is_valid_color("not color")) +}) + # clean up unlink("log", TRUE) diff --git a/vignettes/downloadFile-module.Rmd b/vignettes/downloadFile-module.Rmd index 8e04442..df3d341 100755 --- a/vignettes/downloadFile-module.Rmd +++ b/vignettes/downloadFile-module.Rmd @@ -14,7 +14,7 @@ vignette: > # Overview -## Purpose +## Purpose This *Shiny Module* was created in order to provide a consistent-looking and easy-to-use button that facilitates one or multiple types of file downloads. @@ -36,21 +36,16 @@ easy-to-use button that facilitates one or multiple types of file downloads. Shiny modules consist of a pair of functions that modularize, or package, a small piece of reusable functionality. The UI function is called directly by the user to place the UI in the correct location (as with other shiny UI -objects). The server function is not called directly by the user of the module. -Instead the module server function is called only once to set it up using the -shiny::callModule function inside the server function (i.e. user-local session -scope. The callModule function supplies the first three arguments of the -Shiny Module's function inputs - the input, output, and session. Additional -arguments supplied by the user in the callModule function are passed to the -specific shiny module that is called. There can be additional helper functions -that are a part of a shiny module. +objects). The module server function that is called only once to set it up using the +module name as a function inside the server function (i.e. user-local session +scope. The function first arguments is string represents the module id (the same id used in module UI function). Additional arguments can be supplied by the user based on the specific shiny module that is called. There can be additional helper functions that are a part of a shiny module. The **downloadFile** Shiny Module is a part of the *periscope* package and consists of the following functions: * **downloadFileButton** - the UI function to place the button in the -application -* **downloadFile** - the Server function supplied to callModule. +application. +* **downloadFile** - the server function to be called inside server_local.R. * **downloadFile_ValidateTypes** - a helper function that will check a given list of file types and warn the caller if the list contains an invalid or unsupported type. @@ -101,13 +96,8 @@ downloadFileButton("object_id2", ## downloadFile -The **downloadFile** function is not called directly - instead a call to -shiny::callModule is made inside the server.R (or equivalent) file to initialize -the module. +The **downloadFile** function is called directly. The call consists of the following: -The call consists of the following: - -* the name of the module - unquoted * the unique object ID that was provided to downloadFileButton when creating the UI object * the logging logger to be used @@ -142,20 +132,18 @@ to the user from the application. # Inside server_local.R #single download type -callModule(downloadFile, - "object_id1", - logger = ss_userAction.Log, - filenameroot = "mydownload1", - datafxns = list(csv = mydatafxn1), - aspectratio = 1) +downloadFile("object_id1", + logger = ss_userAction.Log, + filenameroot = "mydownload1", + datafxns = list(csv = mydatafxn1), + aspectratio = 1) #multiple download types -callModule(downloadFile, - "object_id2", - logger = ss_userAction.Log, - filenameroot = "mytype2", - datafxns = list(csv = mydatafxn1, xlsx = mydatafxn2), - aspectratio = 1) +downloadFile("object_id2", + logger = ss_userAction.Log, + filenameroot = "mytype2", + datafxns = list(csv = mydatafxn1, xlsx = mydatafxn2), + aspectratio = 1) ``` @@ -169,7 +157,7 @@ library(periscope) app_dir = tempdir() create_new_application('mysampleapp', location = app_dir, sampleapp = TRUE) -runApp('mysampleapp', appDir = app_dir) +runApp(paste(app_dir, 'mysampleapp', sep = .Platform$file.sep)) ```
diff --git a/vignettes/downloadablePlot-module.Rmd b/vignettes/downloadablePlot-module.Rmd index 56a58c1..9a017c0 100755 --- a/vignettes/downloadablePlot-module.Rmd +++ b/vignettes/downloadablePlot-module.Rmd @@ -15,7 +15,7 @@ vignette: > # Overview -## Purpose +## Purpose This *Shiny Module* was created in order to provide an easy-to-use downloadFileButton for a plot that is automatically created, linked @@ -47,14 +47,9 @@ button Shiny modules consist of a pair of functions that modularize, or package, a small piece of reusable functionality. The UI function is called directly by the user to place the UI in the correct location (as with other shiny UI -objects). The server function is not called directly by the user of the module. -Instead the module server function is called only once to set it up using the -shiny::callModule function inside the server function (i.e. user-local session -scope. The callModule function supplies the first three arguments of the -Shiny Module's function inputs - the input, output, and session. Additional -arguments supplied by the user in the callModule function are passed to the -specific shiny module that is called. There can be additional helper functions -that are a part of a shiny module. +objects). The module server function that is called only once to set it up using the +module name as a function inside the server function (i.e. user-local session +scope. The function first arguments is string represents the module id (the same id used in module UI function). Additional arguments can be supplied by the user based on the specific shiny module that is called. There can be additional helper functions that are a part of a shiny module. ## downloadablePlotUI @@ -93,13 +88,8 @@ downloadablePlotUI("object_id1", ## downloadablePlot -The **downloadablePlot** function is not called directly - instead a call to -shiny::callModule is made inside the server.R (or equivalent) file to initialize -the module. +The **downloadablePlot** function is also called directly. The call consists of the following: -The call consists of the following: - -* the name of the module - unquoted * the unique object ID that was provided to downloadablePlotUI when creating the UI object * the logging logger to be used @@ -139,13 +129,13 @@ to the user from the application. All the above requirements apply. ```{r, eval = F} # Inside server_local.R -callModule(downloadablePlot, - "object_id1", - logger = ss_userAction.Log, - filenameroot = "mydownload1", - aspectratio = 1.33, - downloadfxns = list(png = myplotfxn, tsv = mydatafxn), - visibleplot = myplotfxn) +downloadablePlot("object_id1", + logger = ss_userAction.Log, + filenameroot = "mydownload1", + aspectratio = 1.33, + downloadfxns = list(png = myplotfxn, tsv = mydatafxn), + visibleplot = myplotfxn) + ```
diff --git a/vignettes/downloadableTable-module.Rmd b/vignettes/downloadableTable-module.Rmd index e74be09..dbab953 100755 --- a/vignettes/downloadableTable-module.Rmd +++ b/vignettes/downloadableTable-module.Rmd @@ -15,7 +15,7 @@ vignette: > # Overview -## Purpose +## Purpose This *Shiny Module* was created in order to provide a consistent-looking and easy-to-use table including a downloadFileButton that is automatically created, @@ -48,21 +48,16 @@ scrolling (no paging) Shiny modules consist of a pair of functions that modularize, or package, a small piece of reusable functionality. The UI function is called directly by the user to place the UI in the correct location (as with other shiny UI -objects). The server function is not called directly by the user of the module. -Instead the module server function is called only once to set it up using the -shiny::callModule function inside the server function (i.e. user-local session -scope. The callModule function supplies the first three arguments of the -Shiny Module's function inputs - the input, output, and session. Additional -arguments supplied by the user in the callModule function are passed to the -specific shiny module that is called. There can be additional helper functions -that are a part of a shiny module. +objects). The module server function that is called only once to set it up using the +module name as a function inside the server function (i.e. user-local session +scope. The function first arguments is string represents the module id (the same id used in module UI function). Additional arguments can be supplied by the user based on the specific shiny module that is called. There can be additional helper functions that are a part of a shiny module. The **downloadableTable** Shiny Module is a part of the *periscope* package and consists of the following functions: * **downloadableTableUI** - the UI function to place the table in the application -* **downloadableTable** - the Server function supplied to callModule. +* **downloadableTable** - the server function to be called inside server_local.R. ## downloadableTableUI @@ -99,13 +94,8 @@ downloadableTableUI("object_id1", ## downloadableTable -The **downloadableTable** function is not called directly - instead a call to -shiny::callModule is made inside the server.R (or equivalent) file to initialize -the module. +The **downloadableTable** function is called directly. The call consists of the following: -The call consists of the following: - -* the name of the module - unquoted * the unique object ID that was provided to downloadableTableUI when creating the UI object * the logging logger to be used @@ -119,8 +109,8 @@ initiates a download *(see requirements below)*. * a data function providing the data for the visible table. It can be the same, or different, data as that provided by the download data functions. This allows finer control over what the user can view vs. download if desired. -* whether or not to show rownames on the table -* a table caption, if desired. +* ... free parameters **named list** to pass table customization options. +It supports most of DT table options customization. See example below. **Data Function Requirements** @@ -138,26 +128,60 @@ to the user from the application. All the above requirements apply. **Reactive Return Value** -The callModule function returns a reactive expression containing the selected +The server function returns a reactive expression containing the selected rows (data, not references, rownumbers, etc - the actual table row data). This allows the user to capture this to update another table, chart, etc. as desired. It is acceptable to ignore the return value as well if this functionality is not needed. +**Customization Options** -```{r, eval = F} -# Inside server_local.R +*downloadableTable* module can be customized using the same `?DT::datatable` arguments. options or format functions. These options can be sent as a named options via the server function, see example below. +*Notes*: + +* `selection` parameter in the server function has different usage than `DT::datatable` `selection` option as it should be a function or reactive expression providing the row_ids of the rows that should be selected. Its default value is `NULL` +* `editable`, `width`, `height` options in `DT::datatable` are not supported -selectedrows <- callModule(downloadableTable, - "object_id1", - logger = ss_userAction.Log, - filenameroot = "mydownload1", - downloaddatafxns = list(csv = mydatafxn1, tsv = mydatafxn2), - tabledata = mydatafxn3, - rownames = FALSE, - caption = "This is a great table! By: Me" ) +The following is an example of a customized downloadableTable: -# selectedrows is the reactive return value, captured for later use +
+ +It is generated using the following code: + +```{r, eval = F} +# Inside server_local.R +sketch <- htmltools::withTags(table( + class = "display", + thead( + tr( + th(rowspan = 2, "Location"), + th(colspan = 2, "Statistics") + ), + tr( + th("Change"), + th("Increase") + ) + + ) +)) + +selectedrows <- downloadableTable("exampleDT1", + ss_userAction.Log, + "exampletable", + list(csv = load_data3, tsv = load_data3), + load_data3, + colnames = c("Area", "Delta", "Increase"), + filter = "bottom", + callback = htmlwidgets::JS("table.order([1, 'asc']).draw();"), + container = sketch, + formatStyle = list(columns = c("Total.Population.Change"), + color = DT::styleInterval(0, c("red", "green"))), + formatStyle = list(columns = c("Natural.Increase"), + backgroundColor = DT::styleInterval( + c(7614, 15914, 34152), + c("blue", "lightblue", "#FF7F7F", "red")))) + +# NOTE: selectedrows is the reactive return value, captured for later use ``` @@ -171,7 +195,7 @@ library(periscope) app_dir = tempdir() create_new_application('mysampleapp', location = app_dir, sampleapp = TRUE) -runApp('mysampleapp', appDir = app_dir) +runApp(paste(app_dir, 'mysampleapp', sep = .Platform$file.sep)) ```
diff --git a/vignettes/figures/downloadableTable-2.jpg b/vignettes/figures/downloadableTable-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..986a9efa541be91b6a8609bbfd75e4a7a310a98b GIT binary patch literal 82394 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 )_(!unijMJzDUGMG0)>5L>7ZR7=)=iZ2v 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

t4&5l%d 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 WD8SO-A`E|?(QC(;O>xxuOtNS%D?yi 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%0ABSs(td@Vn2_)-Y}}U%?V5Qn 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 literal 0 HcmV?d00001 diff --git a/vignettes/new-application.Rmd b/vignettes/new-application.Rmd index 39e2f38..cdd25e0 100755 --- a/vignettes/new-application.Rmd +++ b/vignettes/new-application.Rmd @@ -151,9 +151,20 @@ createAlert(session, "sidebarRightAlert", ``` #### Styling +##### Overview +Different parts of the generated application can be customized with a custom yaml file called *periscope_style.yaml* located under *www* folder as follow: -The application can be created with a custom style. For now, only the color of the application header bar can be changed, -but more options will be added later on. The bar color (aka the 'skin') is by default blue, but it can also be set to "black", "purple", "green", "red" or "yellow". +

+ +##### Usage + +* User can update the values for **periscope_style.yaml** then restart the application so new changes can take affect. +* User can pass an existing **periscope_style.yaml** from an existing app to new one through passing its location to `custom_theme_file` parameter in `create_new_application` method. +* The sample applications contain a section to explore updating some styles interactively: + +
+ +* The generated yaml file for blank applications will contain no values for the properties -- blank application will use default style options unless they are customized. *See the Creating a Sample Application and Creating your Application sections for an example* @@ -199,8 +210,8 @@ create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, rightsidebar = TRUE) # application with a right sidebar using a custom icon create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, rightsidebar = "table") -# application with a custom header bar color (skin) -create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, style = list(skin = "green")) +# application with a custom style file +create_new_application(name = 'mytestapp', location = app_dir, sampleapp = TRUE, custom_theme_file = "periscope_style.yaml") ``` This generates a default sample application optionally with a left/right sidebar in a subdirectory named *mytestapp* @@ -213,7 +224,8 @@ user's system. ## Step 2: Run ```{r, eval=F} -runApp('mytestapp', appDir = app_dir) +runApp(paste(app_dir, 'mytestapp', sep = .Platform$file.sep)) + ``` The application should run in either the viewer or browser (depending on system @@ -239,8 +251,8 @@ create_new_application(name = 'mytestapp', location = app_dir, resetbutton = FAL create_new_application(name = 'mytestapp', location = app_dir, rightsidebar = TRUE) # application with a right sidebar using a custom icon create_new_application(name = 'mytestapp', location = app_dir, rightsidebar = "table") -# application with a custom header bar color (skin) -create_new_application(name = 'mytestapp', location = app_dir, style = list(skin = "green")) +# application with a custom style file +create_new_application(name = 'mytestapp', location = app_dir, custom_theme_file = "periscope_style.yaml") ``` This generates a default blank application optionally with a left/right sidebar in a subdirectory named *mytestapp* @@ -410,8 +422,7 @@ output$example1 <- renderUI({ p("Some great explanatory text in my application")) }) -callModule(downloadFile, "ex_d1", ss_userAction.Log, "mydownload", - list(csv=get_ref_data)) +downloadFile("ex_d1", ss_userAction.Log, "mydownload", list(csv=get_ref_data)) observeEvent(input$exButton, { loginfo("exButton Pressed!", logger = ss_userAction.Log) @@ -456,6 +467,10 @@ needs. *(i.e. you would source a file in server_local.R to scope by user session, server_global.R to scope across all sessions, and global.R to scope across all sessions and UI)* +#### www/periscope_style.yaml + +Updated this file values and restart app to customize application different parts styles. +
# Additional Resources