From 22691310814e29d3aa2dd77739d8ba39f187ef2d Mon Sep 17 00:00:00 2001 From: Jake Tufts <137207796+JT-39@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:53:01 +0100 Subject: [PATCH] Shiny template module (#5) * New helper function to add css to UI * Removed shiny-template cookie logic and replaced with dfeshiny cookie functions. Added cookie page to app. Removed use_shinydashboard. * renv updated --- R/helper_functions.R | 184 +++++++++++++++++++-------------- global.R | 1 + renv.lock | 2 +- server.R | 103 +++---------------- ui.R | 235 +++++++++++++++++++++---------------------- 5 files changed, 238 insertions(+), 287 deletions(-) diff --git a/R/helper_functions.R b/R/helper_functions.R index f9f3391e..ba36aab1 100644 --- a/R/helper_functions.R +++ b/R/helper_functions.R @@ -1,74 +1,110 @@ -# ----------------------------------------------------------------------------- -# This is the helper file, filled with lots of helpful functions! -# -# It is commonly used as an R script to store custom functions used through the -# app to keep the rest of the app code easier to read. -# ----------------------------------------------------------------------------- - -# Expandable function --------------------------------------------------------- -expandable <- function(input_id, label, contents) { - gov_details <- shiny::tags$details( - class = "govuk-details", id = input_id, - shiny::tags$summary( - class = "govuk-details__summary", - shiny::tags$span( - class = "govuk-details__summary-text", - label - ) - ), - shiny::tags$div(contents) - ) -} - -# Value box function ---------------------------------------------------------- -# fontsize: can be small, medium or large -value_box <- function(value, subtitle, icon = NULL, - color = "blue", width = 4, - href = NULL, fontsize = "medium") { - validate_color(color) - if (!is.null(icon)) tagAssert(icon, type = "i") - - box_content <- div( - class = paste0("small-box bg-", color), - div( - class = "inner", - p(value, id = paste0("vboxhead-", fontsize)), - p(subtitle, id = paste0("vboxdetail-", fontsize)) - ), - if (!is.null(icon)) div(class = "icon-large", icon) - ) - - if (!is.null(href)) { - box_content <- a(href = href, box_content) - } - - div( - class = if (!is.null(width)) paste0("col-sm-", width), - box_content - ) -} - -# Valid colours for value box ------------------------------------------------- -valid_colors <- c("blue", "dark-blue", "green", "orange", "purple", "white") - -# Validate that only valid colours are used ----------------------------------- -validate_color <- function(color) { - if (color %in% valid_colors) { - return(TRUE) - } - - stop( - "Invalid color: ", color, ". Valid colors are: ", - paste(valid_colors, collapse = ", "), "." - ) -} - -# GSS colours ----------------------------------------------------------------- -# Current GSS colours for use in charts. These are taken from the current -# guidance here: -# https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/ -# Note the advice on trying to keep to a maximum of 4 series in a single plot -# AF colours package guidance here: https://best-practice-and-impact.github.io/afcolours/ -suppressMessages( - gss_colour_pallette <- afcolours::af_colours("categorical", colour_format = "hex", n = 4) -) +# ----------------------------------------------------------------------------- +# This is the helper file, filled with lots of helpful functions! +# +# It is commonly used as an R script to store custom functions used through the +# app to keep the rest of the app code easier to read. +# ----------------------------------------------------------------------------- + +# Expandable function --------------------------------------------------------- +expandable <- function(input_id, label, contents) { + gov_details <- shiny::tags$details( + class = "govuk-details", id = input_id, + shiny::tags$summary( + class = "govuk-details__summary", + shiny::tags$span( + class = "govuk-details__summary-text", + label + ) + ), + shiny::tags$div(contents) + ) +} + +# Value box function ---------------------------------------------------------- +# fontsize: can be small, medium or large +value_box <- function(value, subtitle, icon = NULL, + color = "blue", width = 4, + href = NULL, fontsize = "medium") { + validate_color(color) + if (!is.null(icon)) tagAssert(icon, type = "i") + + box_content <- div( + class = paste0("small-box bg-", color), + div( + class = "inner", + p(value, id = paste0("vboxhead-", fontsize)), + p(subtitle, id = paste0("vboxdetail-", fontsize)) + ), + if (!is.null(icon)) div(class = "icon-large", icon) + ) + + if (!is.null(href)) { + box_content <- a(href = href, box_content) + } + + div( + class = if (!is.null(width)) paste0("col-sm-", width), + box_content + ) +} + +# Valid colours for value box ------------------------------------------------- +valid_colors <- c("blue", "dark-blue", "green", "orange", "purple", "white") + +# Validate that only valid colours are used ----------------------------------- +validate_color <- function(color) { + if (color %in% valid_colors) { + return(TRUE) + } + + stop( + "Invalid color: ", color, ". Valid colors are: ", + paste(valid_colors, collapse = ", "), "." + ) +} + +# GSS colours ----------------------------------------------------------------- +# Current GSS colours for use in charts. These are taken from the current +# guidance here: +# https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/ +# Note the advice on trying to keep to a maximum of 4 series in a single plot +# AF colours package guidance here: https://best-practice-and-impact.github.io/afcolours/ +suppressMessages( + gss_colour_pallette <- afcolours::af_colours("categorical", colour_format = "hex", n = 4) +) + + +#' Set CSS Style Sheet -------------------------------------------------------- +#' +#' This function generates an HTML `head` tag that includes a +#' link to a specified CSS stylesheet. +#' It can be used to dynamically set or include a CSS file in a Shiny +#' application or any other HTML-based interface that utilizes R. +#' +#' @param css_filename A character string specifying the path or +#' URL to the CSS file. +#' This should be a relative or absolute path to a `.css` file or a URL +#' pointing to an external stylesheet. +#' +#' @return A `tags$head` object containing a `link` tag that references +#' the specified CSS file. +#' This object can be directly included in the UI definition of a +#' Shiny application. +#' +#' @details +#' When included in the UI of a Shiny app, it instructs the web browser +#' to load and apply the specified CSS stylesheet. +#' +#' This function is useful when you need to modularise the +#' inclusion of stylesheets, especially in applications where the CSS file +#' might change dynamically or needs to be set programmatically. +#' +set_css_style_sheet <- function(css_filename) { + tags$head( + tags$link( + rel = "stylesheet", + type = "text/css", + href = css_filename + ) + ) +} diff --git a/global.R b/global.R index b19e08cd..0ecf48a9 100644 --- a/global.R +++ b/global.R @@ -46,6 +46,7 @@ source(here::here("R/helper_functions.R")) # Source all files in the ui_panels folder lapply(list.files(here::here("R/ui_panels/"), full.names = TRUE), source) +lapply(list.files(here::here("R/general_modules/"), full.names = TRUE), source) # Set global variables -------------------------------------------------------- diff --git a/renv.lock b/renv.lock index e19b20d8..8fc7422b 100644 --- a/renv.lock +++ b/renv.lock @@ -1306,7 +1306,7 @@ "Package": "shinyWidgets", "Version": "0.8.6", "Source": "Repository", - "Repository": "RSPM", + "Repository": "CRAN", "Requirements": [ "R", "anytime", diff --git a/server.R b/server.R index 178a2575..0ff939ff 100644 --- a/server.R +++ b/server.R @@ -68,99 +68,20 @@ server <- function(input, output, session) { # }) # Cookies logic ------------------------------------------------------------- - shiny::observeEvent(input$cookies, { - if (!is.null(input$cookies)) { - if (!("dfe_analytics" %in% names(input$cookies))) { - shinyjs::show(id = "cookieMain") - } else { - shinyjs::hide(id = "cookieMain") - msg <- list( - name = "dfe_analytics", - value = input$cookies$dfe_analytics - ) - session$sendCustomMessage("analytics-consent", msg) - if ("cookies" %in% names(input)) { - if ("dfe_analytics" %in% names(input$cookies)) { - if (input$cookies$dfe_analytics == "denied") { - ga_msg <- list(name = paste0("_ga_", google_analytics_key)) - session$sendCustomMessage("cookie-remove", ga_msg) - } - } - } - } - } else { - shinyjs::hide(id = "cookieMain") - } - }) - - # Need these set of observeEvent to create a path through the cookie banner - shiny::observeEvent(input$cookieAccept, { - msg <- list( - name = "dfe_analytics", - value = "granted" - ) - session$sendCustomMessage("cookie-set", msg) - session$sendCustomMessage("analytics-consent", msg) - shinyjs::show(id = "cookieAcceptDiv") - shinyjs::hide(id = "cookieMain") - }) - - shiny::observeEvent(input$cookieReject, { - msg <- list( - name = "dfe_analytics", - value = "denied" - ) - session$sendCustomMessage("cookie-set", msg) - session$sendCustomMessage("analytics-consent", msg) - shinyjs::show(id = "cookieRejectDiv") - shinyjs::hide(id = "cookieMain") - }) - - shiny::observeEvent(input$hideAccept, { - shinyjs::toggle(id = "cookieDiv") - }) - - shiny::observeEvent(input$hideReject, { - shinyjs::toggle(id = "cookieDiv") - }) - - shiny::observeEvent(input$remove, { - shinyjs::toggle(id = "cookieMain") - msg <- list(name = "dfe_analytics", value = "denied") - session$sendCustomMessage("cookie-remove", msg) - session$sendCustomMessage("analytics-consent", msg) - print(input$cookies) - }) - - cookies_data <- shiny::reactive({ - input$cookies - }) + output$cookie_status <- dfeshiny::cookie_banner_server( + "cookie-banner", + input_cookies = shiny::reactive(input$cookies), + parent_session = session, + google_analytics_key = google_analytics_key, + cookie_link_panel = "cookies_panel_ui" + ) - output$cookie_status <- shiny::renderText({ - cookie_text_stem <- "To better understand the reach of our dashboard tools, - this site uses cookies to identify numbers of unique users as part of Google - Analytics. You have chosen to" - cookie_text_tail <- "the use of cookies on this website." - if ("cookies" %in% names(input)) { - if ("dfe_analytics" %in% names(input$cookies)) { - if (input$cookies$dfe_analytics == "granted") { - paste(cookie_text_stem, "accept", cookie_text_tail) - } else { - paste(cookie_text_stem, "reject", cookie_text_tail) - } - } - } else { - "Cookies consent has not been confirmed." - } - }) + dfeshiny::cookies_panel_server( + id = "cookie-panel", + input_cookies = shiny::reactive(input$cookies), + google_analytics_key = google_analytics_key + ) - shiny::observeEvent(input$cookieLink, { - # Need to link here to where further info is located. You can - # updateTabsetPanel to have a cookie page for instance - shiny::updateTabsetPanel(session, "navlistPanel", - selected = "Support and feedback" - ) - }) # Dataset with timeseries data ---------------------------------------------- reactive_rev_bal <- shiny::reactive({ diff --git a/ui.R b/ui.R index 4f091ae9..2b0f3929 100644 --- a/ui.R +++ b/ui.R @@ -1,121 +1,114 @@ -# ----------------------------------------------------------------------------- -# This is the ui file. Use it to call elements created in your server file into -# the app, and define where they are placed, and define any user inputs. -# -# Other elements like charts, navigation bars etc. are completely up to you to -# decide what goes in. However, every element should meet accessibility -# requirements and user needs. -# -# This is the user-interface definition of a Shiny web application. You can -# run the application by clicking 'Run App' above. -# -# Find out more about building applications with Shiny here: -# -# http://shiny.rstudio.com/ -# -# The documentation for GOV.UK components can be found at: -# -# https://github.com/moj-analytical-services/shinyGovstyle -# -# ----------------------------------------------------------------------------- -ui <- function(input, output, session) { - shiny::fluidPage( - # Set application metadata ------------------------------------------------ - tags$head(HTML("