diff --git a/.Rbuildignore b/.Rbuildignore index f4e8e2ba..22ad5a67 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -8,3 +8,4 @@ ^pkgdown$ ^.lintr$ ^data-raw$ +^init_test$ diff --git a/.gitignore b/.gitignore index 4841380b..b9be6031 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ google-analytics.html tests/testthat/google-analytics.html docs inst/doc +init_test/ diff --git a/DESCRIPTION b/DESCRIPTION index 0e1b57cc..4c012760 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,6 +16,7 @@ Depends: R (>= 2.10) Imports: dplyr, + dfeR, checkmate, glue, htmltools, diff --git a/NAMESPACE b/NAMESPACE index e8ffb6ea..c3125ecd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,14 +6,18 @@ export(cookies_banner_server) export(cookies_banner_ui) export(cookies_panel_server) export(cookies_panel_ui) +export(create_dashboard) export(custom_disconnect_message) +export(dfe_content_links) export(dfe_cookies_script) export(dfe_reactable) export(external_link) export(header) export(init_analytics) +export(init_commit_hooks) export(init_cookies) export(init_hooks) +export(init_workflow) export(section_tags) export(set_bookmark_include) export(support_panel) diff --git a/R/a11y_panel.R b/R/a11y_panel.R index 0e4c1cd6..cc7015d2 100644 --- a/R/a11y_panel.R +++ b/R/a11y_panel.R @@ -9,7 +9,6 @@ #' [GDS model accessibility statement](https://www.gov.uk/guidance/model-accessibility-statement) #' #' @param dashboard_title Title of the host dashboard -#' @param dashboard_url URL for the host dashboard #' @param issues_contact URL for the GitHub Issues log or contact e-mail address #' for users to flag accessibility issues #' @param publication_name The parent publication name (optional) @@ -23,6 +22,7 @@ #' @param date_reviewed Date the statement was last reviewed #' @param date_template_reviewed Date the underlying template was reviewed #' (default: 12th March 2024) +#' @inheritParams create_dashboard #' #' @return shiny$tags$div element containing the HTML tags and content for the standard #' accessibility statement @@ -40,25 +40,26 @@ #' publication_name = "LA and school expenditure" #' ) a11y_panel <- function( - dashboard_title, - dashboard_url, - date_tested, - date_prepared, - date_reviewed, - date_template_reviewed = "12 March 2024", - issues_contact = NULL, - publication_name = NULL, - publication_slug = NULL, - non_accessible_components = c( - "Keyboard navigation through the interactive charts is currently limited", - "Alternative text in interactive charts is limited to titles" - ), - specific_issues = c( - "Charts have non-accessible components that are inaccessible for keyboard users.", - "Chart tooltips are not compatible with screen reader use.", - "Some decorative images are not labelled appropriately as yet.", - "Some links are not appropriately labelled." - )) { + dashboard_title, + dashboard_url, + date_tested, + date_prepared, + date_reviewed, + date_template_reviewed = "12 March 2024", + issues_contact = NULL, + publication_name = NULL, + publication_slug = NULL, + non_accessible_components = c( + "Keyboard navigation through the interactive charts is currently limited", + "Alternative text in interactive charts is limited to titles" + ), + specific_issues = c( + "Charts have non-accessible components that are inaccessible for keyboard users.", + "Chart tooltips are not compatible with screen reader use.", + "Some decorative images are not labelled appropriately as yet.", + "Some links are not appropriately labelled." + ) +) { # Validate inputs date_tested <- validate_date(date_tested) date_prepared <- validate_date(date_prepared) @@ -67,22 +68,31 @@ a11y_panel <- function( validate_dashboard_url(dashboard_url) if ( lubridate::interval( - lubridate::dmy(date_prepared), lubridate::dmy(date_reviewed) - ) / lubridate::days(1) < 0 + lubridate::dmy(date_prepared), + lubridate::dmy(date_reviewed) + ) / + lubridate::days(1) < + 0 ) { stop("date_reviewed should be later than date_prepared") } if ( lubridate::interval( - lubridate::dmy(date_tested), lubridate::dmy(date_reviewed) - ) / lubridate::days(1) < 0 + lubridate::dmy(date_tested), + lubridate::dmy(date_reviewed) + ) / + lubridate::days(1) < + 0 ) { stop("date_reviewed should be later than date_tested") } if ( lubridate::interval( - lubridate::dmy(date_template_reviewed), lubridate::dmy(date_reviewed) - ) / lubridate::days(1) < 0 + lubridate::dmy(date_template_reviewed), + lubridate::dmy(date_reviewed) + ) / + lubridate::days(1) < + 0 ) { warning( "The template has been through a review more recently than your dashboard, please get in ", @@ -101,10 +111,14 @@ a11y_panel <- function( } } if (is.null(publication_name) && !is.null(publication_slug)) { - stop("Error: If publication_name is provided, then so should publication_slug.") + stop( + "Error: If publication_name is provided, then so should publication_slug." + ) } if (!is.null(publication_name) && is.null(publication_slug)) { - stop("Error: If publication_slug is provided, then so should publication_name.") + stop( + "Error: If publication_slug is provided, then so should publication_name." + ) } shiny::tags$div( style = "margin-top: 50px; margin-bottom: 50px", @@ -140,17 +154,23 @@ a11y_panel <- function( (including the most recent versions of JAWS, NVDA and VoiceOver)" ) )), - shiny::tags$p("We've also made the website text as simple as possible to understand."), + shiny::tags$p( + "We've also made the website text as simple as possible to understand." + ), shiny::tags$p( external_link(href = "https://mcmw.abilitynet.org.uk/", "AbilityNet"), " has advice on making your device easier to use if you have a disability." ), shiny::tags$h2("How accessible this website is"), if (all(is.null(non_accessible_components))) { - shiny::tags$p("This website is fully compliant with accessibility standards.") + shiny::tags$p( + "This website is fully compliant with accessibility standards." + ) } else { shiny::tagList( - shiny::tags$p("We know some parts of this website are not fully accessible:"), + shiny::tags$p( + "We know some parts of this website are not fully accessible:" + ), shiny::tags$div(tags$ol( tagList(lapply(non_accessible_components, shiny::tags$li)) )) @@ -177,9 +197,11 @@ a11y_panel <- function( ) ) }, - shiny::tags$p("We're always looking to improve the accessibility of this website. + shiny::tags$p( + "We're always looking to improve the accessibility of this website. If you find any problems not listed on this page or think we're not meeting - accessibility requirements, contact us:"), + accessibility requirements, contact us:" + ), shiny::tags$ul(tags$li( shiny::tags$a( href = "mailto:explore.statistics@education.gov.uk", @@ -227,8 +249,10 @@ a11y_panel <- function( " due to the non-compliances listed below." ), shiny::tags$h3("Non accessible content"), - shiny::tags$p("The content listed below is non-accessible for the following reasons. - We will address these issues to ensure our content is accessible."), + shiny::tags$p( + "The content listed below is non-accessible for the following reasons. + We will address these issues to ensure our content is accessible." + ), shiny::tags$div(tags$ol( tagList(lapply(specific_issues, shiny::tags$li)) )) @@ -261,7 +285,9 @@ a11y_panel <- function( shiny::tags$li("charts, maps, and tables") )), shiny::tags$p( - "This specific website was was last tested on ", date_tested, " against ", + "This specific website was was last tested on ", + date_tested, + " against ", external_link( href = "https://www.w3.org/TR/WCAG22/", "Accessibility Guidelines WCAG2.2" diff --git a/R/analytics.R b/R/analytics.R index c5238f2d..9c026f9f 100644 --- a/R/analytics.R +++ b/R/analytics.R @@ -8,6 +8,7 @@ #' @param ga_code The Google Analytics code for the dashboard #' @param create_file Boolean TRUE or FALSE, default is TRUE, false will return #' the HTML in the console and is used mainly for testing or comparisons +#' @inheritParams create_dashboard #' #' @importFrom magrittr %>% #' @return NULL @@ -17,7 +18,11 @@ #' if (interactive()) { #' init_analytics(ga_code = "0123456789") #' } -init_analytics <- function(ga_code, create_file = TRUE) { +init_analytics <- function( + ga_code, + path = "./", + create_file = TRUE +) { if (!is.logical(create_file)) { stop("create_file must always be TRUE or FALSE") } @@ -117,8 +122,10 @@ dashboard. } else { if (file.exists("google-analytics.html")) { message("Analytics file already exists.") - message("If you have any customisations in that file, make sure you've - backed those up before over-writing.") + message( + "If you have any customisations in that file, make sure you've + backed those up before over-writing." + ) user_input <- readline( prompt = "Are you happy to overwrite the existing analytics script (y/N) " ) |> @@ -132,10 +139,16 @@ dashboard. write_out <- TRUE } if (write_out) { - cat(html_script_with_id, file = "google-analytics.html", sep = "\n") + cat( + html_script_with_id, + file = file.path(path, "google-analytics.html"), + sep = "\n" + ) message("") message("Google analytics script created as google-analytics.html.") - message("You'll need to add the following line to your ui.R script to start using analytics:") + message( + "You'll need to add the following line to your ui.R script to start using analytics:" + ) message("") message("tags$head(includeHTML((google-analytics.html))),") } else { diff --git a/R/cookies.R b/R/cookies.R index b2cacc55..c6058ad2 100644 --- a/R/cookies.R +++ b/R/cookies.R @@ -85,17 +85,25 @@ dfe_cookies_script <- function() { #' #' @param id Shiny tag shared with cookies_banner_server(), can be any string set #' by the user as long as it matches the id in the cookies_banner_server() +#' @inheritParams create_dashboard #' @param name Name of the dashboard on which the cookie authorisation is being -#' applied +#' applied (deprecated, replaced by site_title) #' #' @family cookies #' @return shiny::tags$div() #' @export #' @inherit cookies examples cookies_banner_ui <- function( - id = "cookies_banner", - name = "DfE R-Shiny dashboard template" -) { + id = "cookies_banner", + site_title = "DfE R-Shiny dashboard template", + name = NULL + ) { + if(!is.null(name) & site_title == "DfE R-Shiny dashboard template"){ + warning( + "The use of name as a parameter in cookies_banner_ui is deprecated. Please use site_title." + ) + site_title <- name + } # Attach CSS from inst/www/css/cookie-banner.css dependency <- htmltools::htmlDependency( name = "cookie-banner", @@ -117,10 +125,10 @@ cookies_banner_ui <- function( class = "govuk-grid-column-two-thirds", shiny::tags$div( class = "govuk-cookie-banner__content", - shiny::tags$h2( - class = "govuk-cookie-banner__heading govuk-heading-m", - name - ), + shiny::tags$h2( + class = "govuk-cookie-banner__heading govuk-heading-m", + site_title + ), shiny::tags$p( class = "govuk-body", "We use some essential cookies to make this service work." @@ -171,12 +179,11 @@ cookies_banner_ui <- function( #' be `reactive(input$cookies)`) #' @param parent_session This should be the R Shiny app session, expect it to #' always be `parent_session = session` -#' @param google_analytics_key Provide the GA 10 digit key of the form -#' "ABCDE12345" #' @param cookies_link_panel name of the navigation panel that the cookie banner #' provides a link to, usually "cookies_panel_ui" #' @param cookies_nav_id ID of the navigation panel the cookie panel page is #' within, defaults to "navlistPanel" +#' @inheritParams create_dashboard #' #' @family cookies #' @return NULL @@ -356,8 +363,7 @@ Shiny.addCustomMessageHandler('analytics-consent', function(msg){ #' #' @param id Shiny tag shared with cookies_panel_server(), can be any string set by #' the user as long as it matches the id in the cookies_panel_server() -#' @param google_analytics_key Provide the GA 10 digit key of the form -#' "ABCDE12345" +#' @inheritParams create_dashboard #' #' @family cookies #' @return a HTML div, containing standard cookies content for a public R @@ -365,9 +371,9 @@ Shiny.addCustomMessageHandler('analytics-consent', function(msg){ #' @export #' @inherit cookies examples cookies_panel_ui <- function( - id = "cookies_panel", - google_analytics_key = NULL -) { + id = "cookies_panel", + google_analytics_key = NULL + ) { shiny::tags$div( style = "margin-top: 50px; margin-bottom: 50px;", shiny::tags$h1("Cookies"), @@ -474,8 +480,7 @@ cookies_panel_ui <- function( #' the user as long as it matches the id in the cookies_panel_ui() #' @param input_cookies The cookie input passed from cookies.js (should always #' be `reactive(input$cookies)`) -#' @param google_analytics_key Provide the GA 10 digit key of the form -#' "ABCDE12345" +#' @inheritParams create_dashboard #' #' @family cookies #' @export diff --git a/R/create_dashboard.R b/R/create_dashboard.R new file mode 100644 index 00000000..7dbdbafb --- /dev/null +++ b/R/create_dashboard.R @@ -0,0 +1,368 @@ +#' Initialize the DFE Shiny Template Setup +#' +#' This function sets up a complete DFE Shiny template by: +#' 1. Cloning the Shiny template repository. +#' 2. Installing a Git pre-commit hook. +#' 3. Setting up a GitHub Actions workflow for deployment. +#' +#' @param path The root directory where the app should be created. String, default: `"."`. +#' @param google_analytics_key 10 digit Google Analytics key if available. String, default: +#' `"XXXXXXXXXX"` +#' @param site_title Site title for the dashboard. String, default: `"dfeshiny template"` +#' @param team_email Email address for support contact. String, default: +#' `"explore.statistics@@education.gov.uk"` +#' @param repo_name The repository URL, must be a valid URL for the dfe-analytical-services GitHub +#' area or the dfe-gov-uk Azure DevOps. String, default: +#' `"https://github.com/dfe-analytical-services/dfeshiny"` +#' @param form_url URL for a feedback form for the dashboard +#' @param dashboard_url URL for the host dashboard +#' @param publication_name The parent publication name +#' @param publication_link URL link to the parent publication +#' @param verbose RUn in verbose mode +#' +#' @details This function runs `init_app()`, `init_commit_hooks()`, and +#' `init_workflow()` +#' in sequence to set up a working environment for a Shiny application. +#' +#' @return No return value. The function initializes the complete template. +#' @examples +#' \dontrun{ +#' create_dashboard() +#' } +#' @export +create_dashboard <- function( + path = "./", + site_title = "dfeshiny template", + google_analytics_key = "XXXXXXXXXX", + dashboard_url = "https://department-for-education.shinyapps.io/dfeshiny-template/", + publication_name = "Publication name", + publication_link = paste0( + "https://explore-education-statistics.service.gov.uk/find-statistics/", + "publication-name" + ), + team_email = "explore.statistics@education.gov.uk", + repo_name = "https://github.com/dfe-analytical-services/dfeshiny", + form_url = "", + verbose = FALSE +) { + message("Initializing the DFE Shiny template...") + + # Set up basic app structure + init_base_app( + path = path, + google_analytics_key = google_analytics_key, + dashboard_url = dashboard_url, + site_title = site_title, + publication_name = publication_name, + publication_link = publication_link, + team_email = team_email, + repo_name = repo_name, + form_url = form_url, + verbose = verbose + ) + + # Install Git pre-commit hooks + init_commit_hooks(path = path) + + # Set up GitHub Actions workflow + init_workflow( + path = path, + site_title = site_title + ) + + # Set up Google Analytics + init_analytics( + path = path, + ga_code = google_analytics_key + ) + + message("DFE Shiny template setup completed successfully.") +} + +#' Initialize a Shiny App Project Structure +#' +#' Creates the basic file and directory structure for a Shiny application, +#' including:`global.R`, `ui.R`, `server.R`, a `data/` folder, +#' and a `tests/testthat/` folder. +#' +#' @inheritParams create_dashboard +#' +#' @return No return value. The function is called for +#' its side effects (i.e., file and folder creation). +#' @keywords internal +#' +#' @examples +#' \dontrun{ +#' init_base_app() # Creates structure in current directory +#' } +#' +init_base_app <- function( + path = "./", + google_analytics_key = "XXXXXXXXX", + dashboard_url = "https://department-for-education.shinyapps.io/dashboard-name/", + site_title = "Dashboard name", + publication_name = "Publication name", + publication_link = paste0( + "https://explore-education-statistics.service.gov.uk/find-statistics/", + "publication-name" + ), + team_email = "team.name@education.gov.uk", + repo_name = "https://github.com/dfe-analytical-services/dashboard-name", + form_url = "", + verbose = FALSE +) { + # Create directories + dirs <- c("data", "R/footer_pages", "tests/testthat") + for (dir in dirs) { + dir_path <- file.path(path, dir) + if (!dir.exists(dir_path)) { + dir.create(dir_path, recursive = TRUE) + dfeR::toggle_message("Created directory: ", dir_path, verbose = verbose) + } else { + dfeR::toggle_message( + "Directory already exists: ", + dir_path, + verbose = verbose + ) + } + } + + init_global( + path = path, + google_analytics_key = google_analytics_key, + dashboard_url = dashboard_url, + site_title = site_title, + publication_name = publication_name, + publication_link = publication_link, + team_email = team_email, + repo_name = repo_name, + form_url = form_url, + verbose = verbose + ) + init_ui( + path = path, + verbose = verbose + ) + init_server( + path = path, + verbose = verbose + ) +} + +#' Initialise global.R +#' +#' @inheritParams create_dashboard +#' +#' @returns NULL +#' @keywords internal +#' +#' @examples +#' \dontrun{ +#' init_global() +#' } +init_global <- function( + path = "./", + google_analytics_key = "XXXXXXXXX", + dashboard_url = "https://department-for-education.shinyapps.io/dashboard-name/", + site_title = "Dashboard name", + publication_name = "Publication name", + publication_link = paste0( + "https://explore-education-statistics.service.gov.uk/find-statistics/", + "publication-name" + ), + team_email = "team.name@education.gov.uk", + repo_name = "https://github.com/dfe-analytical-services/dashboard-name", + form_url = "", + verbose = FALSE +) { + globalr_script <- paste( + c( + readLines("inst/global_template.r"), + paste0("site_title <- \"", site_title, "\""), + paste0("publication_name <- \"", publication_name, "\""), + paste0("publication_link <- \"", publication_link, "\""), + paste0("team_email <- \"", team_email, "\""), + paste0("repo_name <- \"", repo_name, "\""), + paste0("form_url <- \"", form_url, "\""), + + "## Set the URLs that the site will be published to", + paste0("site_primary <- \"", dashboard_url, "\""), + + ## Google Analytics tracking + paste0("google_analytics_key <- \"", google_analytics_key, "\"") + ), + collapse = "\n" + ) + globalr_script |> + cat(file = file.path(path, "global.R")) + dfeR::toggle_message( + "create_dashboard: global.R created", + verbose = verbose + ) + return(NULL) +} + +#' Initialise ui.R +#' +#' @inheritParams create_dashboard +#' +#' @returns NULL +#' @keywords internal +#' +#' @examples +#' \dontrun{ +#' init_ui() +#' } +init_ui <- function( + path = ".", + verbose = FALSE +) { + uir_script <- readLines("inst/ui_template.r") |> + paste(collapse = "\n") + uir_script |> cat(file = file.path(path, "ui.R")) + dfeR::toggle_message( + "create_dashboard: ui.R created", + verbose = verbose + ) + return(NULL) +} + +#' Initialise server.R +#' +#' @inheritParams create_dashboard +#' +#' @returns NULL +#' @keywords internal +#' +#' @examples +#' \dontrun{ +#' init_server() +#' } +init_server <- function( + path = ".", + verbose = FALSE +) { + serverr_script <- readLines("inst/server_template.r") |> + paste(collapse = "\n") + serverr_script |> cat(file = file.path(path, "server.R")) + dfeR::toggle_message( + "create_dashboard: server.R created", + verbose = verbose + ) + return(serverr_script) +} + + +#' Initialize Git Pre-Commit Hook for a Shiny App +#' +#' This function checks if the current directory is a Git repository, then +#' downloads a pre-commit hook script from the +#' "dfe-analytical-services/shiny-template" repository and +#' places it in `.git/hooks/pre-commit`, overwriting if necessary. +#' +#' @details The function verifies that the working directory is part of a +#' Git repository before proceeding. If Git is not installed or the directory +#' is not a Git repository, it stops with an error message. +#' The downloaded script ensures best practices before committing changes. +#' +#' @inheritParams create_dashboard +#' +#' @return No return value. The function installs the pre-commit hook. +#' @examples +#' \dontrun{ +#' init_commit_hooks() +#' } +#' @export +init_commit_hooks <- function(path = "./") { + # Define the Git pre-commit hook file URL + hook_url <- "https://raw.githubusercontent.com/dfe-analytical-services/shiny-template/main/.hooks/pre-commit.R" + + # Check if this is a Git repository + if (system("git rev-parse --is-inside-work-tree", intern = TRUE) != "true") { + stop( + "This is not a Git repository. Please initialize a Git repository first." + ) + } + + # Define the destination path for the pre-commit hook + hook_path <- file.path(path, ".git/hooks/pre-commit") + if (!dir.exists(hook_path)) { + dir.create(hook_path, recursive = TRUE) + message(hook_path, " directory created.") + } + + # Download and write the file + tryCatch( + { + utils::download.file(hook_url, hook_path, mode = "wb") + Sys.chmod(hook_path, mode = "755") # Make it executable + message("Pre-commit hook installed successfully.") + }, + error = function(e) { + stop("Failed to download and install the pre-commit hook: ", e$message) + } + ) +} + +#' Initialize GitHub Actions Workflow for Shiny App Deployment +#' +#' This function sets up a GitHub Actions workflow for deploying a Shiny app. +#' It ensures that the `.github/workflows/` directory exists, downloads a +#' deployment workflow file, and creates a configuration YAML file. +#' +#' @inheritParams create_dashboard +#' +#' @details If the `.github/workflows/` directory does not exist, it is created. +#' The function then downloads the deployment workflow file and places it inside +#' `.github/workflows/`. Additionally, it generates a YAML file containing +#' deployment settings, allowing customization of the `dashboard_name`. +#' +#' @return No return value. The function sets up the workflow and configuration. +#' @examples +#' \dontrun{ +#' init_workflow() +#' init_workflow(dashboard_name = "my-custom-dashboard") +#' } +#' @export +init_workflow <- function(site_title, path = "./") { + # Define paths + workflows_dir <- file.path(path, ".github/workflows") + workflow_file <- file.path(workflows_dir, "deploy-shiny.yaml") + config_file <- file.path(path, "deployment-config.yaml") + + # Define GitHub workflow file URL + workflow_url <- "https://raw.githubusercontent.com/dfe-analytical-services/shiny-template/main/.github/workflows/deploy-shiny.yaml" + + # Ensure the .github/workflows directory exists + if (!dir.exists(workflows_dir)) { + dir.create(workflows_dir, recursive = TRUE) + message(workflows_dir, " directory created.") + } + + # Download and save the workflow file + tryCatch( + { + utils::download.file(workflow_url, workflow_file, mode = "wb") + message("GitHub Actions workflow file downloaded successfully.") + }, + error = function(e) { + stop("Failed to download the workflow file: ", e$message) + } + ) + + # Create the deployment configuration YAML file + yaml_content <- sprintf( + "dashboard_name: %s\ndeploy_target: shinyapps\n", + site_title + ) + + tryCatch( + { + writeLines(yaml_content, config_file) + message("Deployment configuration file created successfully.") + }, + error = function(e) { + stop("Failed to create the deployment configuration file: ", e$message) + } + ) +} diff --git a/R/custom_disconnect_message.R b/R/custom_disconnect_message.R index 43e569b2..fd5e1c52 100644 --- a/R/custom_disconnect_message.R +++ b/R/custom_disconnect_message.R @@ -51,24 +51,32 @@ #' custom_refresh = "https://department-for-education.shinyapps.io/my-dashboard" #' ) custom_disconnect_message <- function( - refresh = "refresh page (attempting to keep your last known selections)", - reset = "reset page (removing any previous selections)", - dashboard_title = NULL, - links = NULL, - publication_name = NULL, - publication_link = NULL, - support_contact = "explore.statistics@education.gov.uk", - custom_refresh = NULL, - custom_reset = NULL) { + refresh = "refresh page (attempting to keep your last known selections)", + reset = "reset page (removing any previous selections)", + dashboard_title = NULL, + links = NULL, + publication_name = NULL, + publication_link = NULL, + support_contact = "explore.statistics@education.gov.uk", + custom_refresh = NULL, + custom_reset = NULL +) { # Check links are valid - if (FALSE %in% validate_dashboard_url(links) || - "https://department-for-education.shinyapps.io/" %in% links) { # nolint: [indentation_linter] + if ( + FALSE %in% + validate_dashboard_url(links) || + "https://department-for-education.shinyapps.io/" %in% links + ) { + # nolint: [indentation_linter] stop("You have entered an invalid site link in the links argument.") } if (!is.null(custom_refresh)) { is_valid_refresh <- function(refresh) { - startsWith(stringr::str_trim(refresh), "https://department-for-education.shinyapps.io/") + startsWith( + stringr::str_trim(refresh), + "https://department-for-education.shinyapps.io/" + ) } if (is_valid_refresh(custom_refresh) == FALSE) { @@ -83,7 +91,10 @@ custom_disconnect_message <- function( if (!is.null(custom_reset)) { is_valid_reset <- function(reset) { - startsWith(stringr::str_trim(reset), "https://department-for-education.shinyapps.io/") + startsWith( + stringr::str_trim(reset), + "https://department-for-education.shinyapps.io/" + ) } if (is_valid_reset(custom_reset) == FALSE) { @@ -109,10 +120,15 @@ custom_disconnect_message <- function( startsWith(stringr::str_trim(link), pub_prefix) } - if (TRUE %in% is_valid_publication_link(publication_link) == FALSE || - publication_link %in% pub_prefix) { # nolint: [indentation_linter] - stop("You have entered an invalid publication link in the publication_link - argument.") + if ( + TRUE %in% is_valid_publication_link(publication_link) == FALSE || + publication_link %in% pub_prefix + ) { + # nolint: [indentation_linter] + stop( + "You have entered an invalid publication link in the publication_link + argument." + ) } } @@ -204,7 +220,10 @@ custom_disconnect_message <- function( ". Apologies for the inconvenience." ) }, - if (!is.null(publication_name) && grepl("explore-education-statistics", publication_link)) { + if ( + !is.null(publication_name) && + grepl("explore-education-statistics", publication_link) + ) { tags$p( "The data used in this dashboard can also be viewed or downloaded via the ", dfeshiny::external_link( @@ -236,7 +255,8 @@ custom_disconnect_message <- function( tags$div(id = "ss-overlay", style = "display: none;"), tags$head(htmltools::tags$style( glue::glue( - .open = "{{", .close = "}}", + .open = "{{", + .close = "}}", "#custom-disconnect-dialog a { display: {{ if (refresh == '') 'none' else 'inline' }} !important; color: #1d70b8 !important; diff --git a/R/dfe_content_links.R b/R/dfe_content_links.R new file mode 100644 index 00000000..f6e60e06 --- /dev/null +++ b/R/dfe_content_links.R @@ -0,0 +1,37 @@ +#' Content links +#' +#' @description +#' Content links for the right hand navigation list panel +#' +#' @param links_list List of links for the navigation panel +#' +#' @returns NULL +#' @export +#' +#' @examples +#' dfe_content_links(c("Data", "Information")) +dfe_content_links <- function(links_list) { + # Add the HTML around the link and make an id by snake casing + create_sidelink <- function(link_text) { + tags$li( + "-", + shiny::actionLink( + tolower(gsub(" ", "_", link_text)), + link_text, + `data-value` = link_text, + class = "contents_link" + ) + ) + } + + # The HTML div to be returned + tags$div( + style = "position: sticky; top: 0.5rem; padding: 0.25rem;", # Make it stick! + shiny::tags$h2("Contents"), + tags$ul( + id = "contents_links", + style = "list-style-type: none; padding-left: 0; font-size: 1rem;", # remove the circle bullets + lapply(links_list, create_sidelink) + ) + ) +} diff --git a/R/support_panel.R b/R/support_panel.R index 97ee7368..50992a83 100644 --- a/R/support_panel.R +++ b/R/support_panel.R @@ -8,17 +8,16 @@ #' dfe-analytical-services GitHub area or the dfe-gov-uk Azure DevOps #' @param ees_publication Whether the parent publication is hosted on Explore #' Education Statistics -#' @param publication_name The parent publication name #' @param publication_slug The parent publication slug on Explore Education #' Statistics #' @param alt_href Alternative link to the parent publication (if not hosted on #' Explore Education Statistics) -#' @param form_url URL for a feedback form for the dashboard #' @param custom_data_info A single text string or a `shiny::tagList()` object #' for custom text to go under the "Find out more information on the data" heading. #' @param extra_text Add extra paragraphs to the page before the "Contact us" section. #' Use `dfeshiny::section_tags()` to specify the heading and body. #' Look at examples to see how to add one or multiple sections. +#' @inheritParams create_dashboard #' #' @return a HTML div, containing standard support content for a public R Shiny #' dashboard in DfE @@ -132,15 +131,16 @@ #' ) #' ) support_panel <- function( - team_email = "", - repo_name = "", - ees_publication = TRUE, - publication_name = NULL, - publication_slug = "", - alt_href = NULL, - form_url = NULL, - custom_data_info = NULL, - extra_text = NULL) { + team_email = "", + repo_name = "", + ees_publication = TRUE, + publication_name = NULL, + publication_slug = "", + alt_href = NULL, + form_url = NULL, + custom_data_info = NULL, + extra_text = NULL +) { if (is_valid_dfe_email(team_email) == FALSE) { stop( "You have entered an invalid email in the team_email argument. @@ -210,7 +210,8 @@ support_panel <- function( href = paste0("mailto:", team_email), link_text = team_email, add_warning = FALSE - ), "." + ), + "." ) ), shiny::tags$h2("Find more information on the data"), @@ -240,7 +241,8 @@ support_panel <- function( dfeshiny::external_link( href = paste0( "https://explore-education-statistics.service.gov.uk/find-statistics/", # nolint: [line_length_linter] - publication_slug, "/data-guidance" + publication_slug, + "/data-guidance" ), link_text = "data guidance" ), @@ -248,7 +250,8 @@ support_panel <- function( dfeshiny::external_link( href = paste0( "https://explore-education-statistics.service.gov.uk/find-statistics/", # nolint: [line_length_linter] - publication_slug, "#explore-data-and-files" + publication_slug, + "#explore-data-and-files" ), link_text = "tools to access and interrogate the underlying data" ), diff --git a/_pkgdown.yml b/_pkgdown.yml index 41b467cb..e4ad0656 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -6,6 +6,10 @@ template: pkgdown-nav-height: 81.4468px reference: +- title: Creating a dashboard template + contents: + - create_dashboard + - starts_with("init") - title: Bookmarks contents: - set_bookmark_include @@ -15,10 +19,6 @@ reference: - title: Connectivity contents: - custom_disconnect_message -- title: Initialisation functions - desc: One time functions used to set up or update standardised scripts and workflows needed for your dashboard - contents: - - starts_with("init") - title: Links contents: - external_link diff --git a/inst/global_template.r b/inst/global_template.r new file mode 100644 index 00000000..d89c8ffa --- /dev/null +++ b/inst/global_template.r @@ -0,0 +1,53 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is the global file. +# Use it to store functions, library calls, source files etc. +# Moving these out of the server file and into here improves performance as the +# global file is run only once when the app launches and stays consistent +# across users whereas the server and UI files are constantly interacting and +# responsive to user input. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Library calls =============================================================== +shhh <- suppressPackageStartupMessages # It's a library, so shhh! + +## Core shiny and R packages -------------------------------------------------- +shhh(library(shiny)) +shhh(library(bslib)) +shhh(library(shinytitle)) +shhh(library(metathis)) + +## Custom packages ------------------------------------------------------------ +shhh(library(dfeR)) +shhh(library(dfeshiny)) +shhh(library(shinyGovstyle)) +shhh(library(afcolours)) + +## Creating charts and tables-------------------------------------------------- +shhh(library(ggplot2)) +shhh(library(ggiraph)) + +## Data and string manipulation ----------------------------------------------- +shhh(library(dplyr)) +shhh(library(stringr)) + +## Data downloads ------------------------------------------------------------- +shhh(library(data.table)) + +## Testing dependencies ------------------------------------------------------- +# These are not needed for the app itself but including them here keeps them in +# renv but avoids the app needlessly loading them, saving on load time. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +if (FALSE) { + shhh(library(shinytest2)) + shhh(library(testthat)) + shhh(library(lintr)) + shhh(library(styler)) +} + +# Source R scripts ============================================================ +# Source any scripts here. Scripts may be needed to process data before it gets +# to the server file or to hold custom functions to keep the main files shorter. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +lapply(list.files("R/footer_pages/", full.names = TRUE), source) + +# Set global variables ======================================================== diff --git a/inst/server_template.r b/inst/server_template.r new file mode 100644 index 00000000..1e733a4b --- /dev/null +++ b/inst/server_template.r @@ -0,0 +1,68 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# This is the server file. +# Use it to create interactive elements like tables, charts and text for your +# app. +# +# Anything you create in the server file won't appear in your app until you call +# it in the UI file. This server script gives examples of plots and value boxes +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +server <- function(input, output, session) { + # Manage cookie consent + output$cookies_status <- dfeshiny::cookies_banner_server( + input_cookies = shiny::reactive(input$cookies), + parent_session = session, + google_analytics_key = google_analytics_key + ) + + dfeshiny::cookies_panel_server( + input_cookies = shiny::reactive(input$cookies), + google_analytics_key = google_analytics_key + ) + + # Navigation ================================================================ + ## Main content left navigation --------------------------------------------- + observeEvent( + input$panel_1, + nav_select("left_nav", "panel_1") + ) + observeEvent(input$user_guide, nav_select("left_nav", "user_guide")) + + ## Footer links ------------------------------------------------------------- + observeEvent(input$dashboard, nav_select("pages", "dashboard")) + observeEvent(input$footnotes, nav_select("pages", "footnotes")) + observeEvent(input$support, nav_select("pages", "support")) + observeEvent( + input$accessibility_statement, + nav_select("pages", "accessibility_statement") + ) + observeEvent( + input$cookies_statement, + nav_select("pages", "cookies_statement") + ) + + ## Back links to main dashboard --------------------------------------------- + observeEvent(input$footnotes_to_dashboard, nav_select("pages", "dashboard")) + observeEvent(input$support_to_dashboard, nav_select("pages", "dashboard")) + observeEvent(input$cookies_to_dashboard, nav_select("pages", "dashboard")) + observeEvent( + input$accessibility_to_dashboard, + nav_select("pages", "dashboard") + ) + + # Update title ============================================================== + # This changes the title based on the tab selections and is important for accessibility + # On the main dashboard it uses the active tab from left_nav, else it uses the page input + observe({ + if (input$pages == "dashboard") { + change_window_title( + title = paste0(site_title, " - ", gsub("_", " ", input$left_nav)) + ) + } else { + change_window_title( + title = paste0(site_title, " - ", gsub("_", " ", input$pages)) + ) + } + }) +} diff --git a/inst/ui_template.r b/inst/ui_template.r new file mode 100644 index 00000000..1fcc622a --- /dev/null +++ b/inst/ui_template.r @@ -0,0 +1,120 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ui <- function(input, output, session) { + page_fluid( + # Metadata for app ======================================================== + tags$html(lang = "en"), + tags$head(HTML(paste0("", site_title, ""))), # set in global.R + tags$head(tags$link(rel = "shortcut icon", href = "dfefavicon.png")), + # Add meta description for search engines + metathis::meta() %>% + meta_general( + application_name = site_title, + description = "Interactive tool for exploring data", + robots = "index,follow", + generator = "R Shiny", + subject = "education", + rating = "General", + referrer = "no-referrer" + ), + + # Required to make the title update based on tab changes + shinytitle::use_shiny_title(), + + ## Custom CSS ------------------------------------------------------------- + tags$head( + tags$link( + rel = "stylesheet", + type = "text/css", + href = "dfe_shiny_gov_style.css" + ) + ), + + ## Custom disconnect function --------------------------------------------- + dfeshiny::custom_disconnect_message( + links = site_primary, + publication_name = parent_pub_name, + publication_link = parent_publication + ), + tags$head(includeHTML(("google-analytics.html"))), + shinyjs::useShinyjs(), + dfeshiny::dfe_cookies_script(), + dfeshiny::cookies_banner_ui( + name = site_title + ), + + ## Header ----------------------------------------------------------------- + dfeshiny::header( + header = site_title + ), + + ## Beta banner ------------------------------------------------------------ + shinyGovstyle::banner( + "gds_phase_banner", + "Alpha", + paste0( + "This dashboard is being developed, contact", + team_email, + " with any feedback" + ) + ), + + # Page navigation ========================================================= + # This switches between the supporting pages in the footer and the main dashboard + gov_main_layout( + bslib::navset_hidden( + id = "pages", + nav_panel( + "dashboard", + ## Main dashboard --------------------------------------------------- + layout_columns( + # Override default wrapping breakpoints to avoid text overlap + col_widths = breakpoints(sm = c(4, 8), md = c(3, 9), lg = c(2, 9)), + ## Left navigation ------------------------------------------------ + dfe_content_links( + links_list = c( + "Panel 1", + "User guide" + ) + ), + ## Dashboard panels ----------------------------------------------- + bslib::navset_hidden( + id = "left_nav", + nav_panel( + "panel_1", + dashboard_page(id = "panel_1") + ), + nav_panel("user_guide", user_guide()) + ) + ) + ), + ## Footer pages ------------------------------------------------------- + nav_panel("footnotes", footnotes_page()), + nav_panel("support", support_page()), + nav_panel("accessibility_statement", accessibility_page()), + nav_panel("cookies_statement", cookies_page()) + ) + ), + + # Footer ================================================================== + dfe_footer( + links_list = c( + "Footnotes", + "Support", + "Accessibility statement", + "Cookies statement" + ) + ) + ) +} diff --git a/man/cookies_banner_server.Rd b/man/cookies_banner_server.Rd index 52188cd0..fac4b95c 100644 --- a/man/cookies_banner_server.Rd +++ b/man/cookies_banner_server.Rd @@ -23,8 +23,8 @@ be \code{reactive(input$cookies)})} \item{parent_session}{This should be the R Shiny app session, expect it to always be \code{parent_session = session}} -\item{google_analytics_key}{Provide the GA 10 digit key of the form -"ABCDE12345"} +\item{google_analytics_key}{10 digit Google Analytics key if available. String, default: +\code{"XXXXXXXXXX"}} \item{cookies_link_panel}{name of the navigation panel that the cookie banner provides a link to, usually "cookies_panel_ui"} diff --git a/man/cookies_banner_ui.Rd b/man/cookies_banner_ui.Rd index cbae8af7..c06e69ba 100644 --- a/man/cookies_banner_ui.Rd +++ b/man/cookies_banner_ui.Rd @@ -6,15 +6,18 @@ \usage{ cookies_banner_ui( id = "cookies_banner", - name = "DfE R-Shiny dashboard template" + site_title = "DfE R-Shiny dashboard template", + name = NULL ) } \arguments{ \item{id}{Shiny tag shared with cookies_banner_server(), can be any string set by the user as long as it matches the id in the cookies_banner_server()} +\item{site_title}{Site title for the dashboard. String, default: \code{"dfeshiny template"}} + \item{name}{Name of the dashboard on which the cookie authorisation is being -applied} +applied (deprecated, replaced by site_title)} } \value{ shiny::tags$div() diff --git a/man/cookies_panel_server.Rd b/man/cookies_panel_server.Rd index 223b7206..43879b04 100644 --- a/man/cookies_panel_server.Rd +++ b/man/cookies_panel_server.Rd @@ -17,8 +17,8 @@ the user as long as it matches the id in the cookies_panel_ui()} \item{input_cookies}{The cookie input passed from cookies.js (should always be \code{reactive(input$cookies)})} -\item{google_analytics_key}{Provide the GA 10 digit key of the form -"ABCDE12345"} +\item{google_analytics_key}{10 digit Google Analytics key if available. String, default: +\code{"XXXXXXXXXX"}} } \description{ Create the server module of DfE R-Shiny cookies dashboard panel to be used diff --git a/man/cookies_panel_ui.Rd b/man/cookies_panel_ui.Rd index 11345b9f..03c7cfbd 100644 --- a/man/cookies_panel_ui.Rd +++ b/man/cookies_panel_ui.Rd @@ -10,8 +10,8 @@ cookies_panel_ui(id = "cookies_panel", google_analytics_key = NULL) \item{id}{Shiny tag shared with cookies_panel_server(), can be any string set by the user as long as it matches the id in the cookies_panel_server()} -\item{google_analytics_key}{Provide the GA 10 digit key of the form -"ABCDE12345"} +\item{google_analytics_key}{10 digit Google Analytics key if available. String, default: +\code{"XXXXXXXXXX"}} } \value{ a HTML div, containing standard cookies content for a public R diff --git a/man/create_dashboard.Rd b/man/create_dashboard.Rd new file mode 100644 index 00000000..56c7ebba --- /dev/null +++ b/man/create_dashboard.Rd @@ -0,0 +1,67 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_dashboard.R +\name{create_dashboard} +\alias{create_dashboard} +\title{Initialize the DFE Shiny Template Setup} +\usage{ +create_dashboard( + path = "./", + site_title = "dfeshiny template", + google_analytics_key = "XXXXXXXXXX", + dashboard_url = "https://department-for-education.shinyapps.io/dfeshiny-template/", + publication_name = "Publication name", + publication_link = + paste0("https://explore-education-statistics.service.gov.uk/find-statistics/", + "publication-name"), + team_email = "explore.statistics@education.gov.uk", + repo_name = "https://github.com/dfe-analytical-services/dfeshiny", + form_url = "", + verbose = FALSE +) +} +\arguments{ +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} + +\item{site_title}{Site title for the dashboard. String, default: \code{"dfeshiny template"}} + +\item{google_analytics_key}{10 digit Google Analytics key if available. String, default: +\code{"XXXXXXXXXX"}} + +\item{dashboard_url}{URL for the host dashboard} + +\item{publication_name}{The parent publication name} + +\item{publication_link}{URL link to the parent publication} + +\item{team_email}{Email address for support contact. String, default: +\code{"explore.statistics@education.gov.uk"}} + +\item{repo_name}{The repository URL, must be a valid URL for the dfe-analytical-services GitHub +area or the dfe-gov-uk Azure DevOps. String, default: +\code{"https://github.com/dfe-analytical-services/dfeshiny"}} + +\item{form_url}{URL for a feedback form for the dashboard} + +\item{verbose}{RUn in verbose mode} +} +\value{ +No return value. The function initializes the complete template. +} +\description{ +This function sets up a complete DFE Shiny template by: +\enumerate{ +\item Cloning the Shiny template repository. +\item Installing a Git pre-commit hook. +\item Setting up a GitHub Actions workflow for deployment. +} +} +\details{ +This function runs \code{init_app()}, \code{init_commit_hooks()}, and +\code{init_workflow()} +in sequence to set up a working environment for a Shiny application. +} +\examples{ +\dontrun{ +create_dashboard() +} +} diff --git a/man/dfe_content_links.Rd b/man/dfe_content_links.Rd new file mode 100644 index 00000000..593daf19 --- /dev/null +++ b/man/dfe_content_links.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dfe_content_links.R +\name{dfe_content_links} +\alias{dfe_content_links} +\title{Content links} +\usage{ +dfe_content_links(links_list) +} +\arguments{ +\item{links_list}{List of links for the navigation panel} +} +\description{ +Content links for the right hand navigation list panel +} +\examples{ +dfe_content_links(c("Data", "Information")) +} diff --git a/man/init_analytics.Rd b/man/init_analytics.Rd index 01dcdc05..e0bb219f 100644 --- a/man/init_analytics.Rd +++ b/man/init_analytics.Rd @@ -4,11 +4,13 @@ \alias{init_analytics} \title{init_analytics} \usage{ -init_analytics(ga_code, create_file = TRUE) +init_analytics(ga_code, path = "./", create_file = TRUE) } \arguments{ \item{ga_code}{The Google Analytics code for the dashboard} +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} + \item{create_file}{Boolean TRUE or FALSE, default is TRUE, false will return the HTML in the console and is used mainly for testing or comparisons} } diff --git a/man/init_base_app.Rd b/man/init_base_app.Rd new file mode 100644 index 00000000..0d0bb0de --- /dev/null +++ b/man/init_base_app.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_dashboard.R +\name{init_base_app} +\alias{init_base_app} +\title{Initialize a Shiny App Project Structure} +\usage{ +init_base_app( + path = "./", + google_analytics_key = "XXXXXXXXX", + dashboard_url = "https://department-for-education.shinyapps.io/dashboard-name/", + site_title = "Dashboard name", + publication_name = "Publication name", + publication_link = + paste0("https://explore-education-statistics.service.gov.uk/find-statistics/", + "publication-name"), + team_email = "team.name@education.gov.uk", + repo_name = "https://github.com/dfe-analytical-services/dashboard-name", + form_url = "", + verbose = FALSE +) +} +\arguments{ +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} + +\item{google_analytics_key}{10 digit Google Analytics key if available. String, default: +\code{"XXXXXXXXXX"}} + +\item{dashboard_url}{URL for the host dashboard} + +\item{site_title}{Site title for the dashboard. String, default: \code{"dfeshiny template"}} + +\item{publication_name}{The parent publication name} + +\item{publication_link}{URL link to the parent publication} + +\item{team_email}{Email address for support contact. String, default: +\code{"explore.statistics@education.gov.uk"}} + +\item{repo_name}{The repository URL, must be a valid URL for the dfe-analytical-services GitHub +area or the dfe-gov-uk Azure DevOps. String, default: +\code{"https://github.com/dfe-analytical-services/dfeshiny"}} + +\item{form_url}{URL for a feedback form for the dashboard} + +\item{verbose}{RUn in verbose mode} +} +\value{ +No return value. The function is called for +its side effects (i.e., file and folder creation). +} +\description{ +Creates the basic file and directory structure for a Shiny application, +including:\code{global.R}, \code{ui.R}, \code{server.R}, a \verb{data/} folder, +and a \verb{tests/testthat/} folder. +} +\examples{ +\dontrun{ +init_base_app() # Creates structure in current directory +} + +} +\keyword{internal} diff --git a/man/init_commit_hooks.Rd b/man/init_commit_hooks.Rd new file mode 100644 index 00000000..7417e94d --- /dev/null +++ b/man/init_commit_hooks.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_dashboard.R +\name{init_commit_hooks} +\alias{init_commit_hooks} +\title{Initialize Git Pre-Commit Hook for a Shiny App} +\usage{ +init_commit_hooks(path = "./") +} +\arguments{ +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} +} +\value{ +No return value. The function installs the pre-commit hook. +} +\description{ +This function checks if the current directory is a Git repository, then +downloads a pre-commit hook script from the +"dfe-analytical-services/shiny-template" repository and +places it in \code{.git/hooks/pre-commit}, overwriting if necessary. +} +\details{ +The function verifies that the working directory is part of a +Git repository before proceeding. If Git is not installed or the directory +is not a Git repository, it stops with an error message. +The downloaded script ensures best practices before committing changes. +} +\examples{ +\dontrun{ +init_commit_hooks() +} +} diff --git a/man/init_global.Rd b/man/init_global.Rd new file mode 100644 index 00000000..509f25a0 --- /dev/null +++ b/man/init_global.Rd @@ -0,0 +1,55 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_dashboard.R +\name{init_global} +\alias{init_global} +\title{Initialise global.R} +\usage{ +init_global( + path = "./", + google_analytics_key = "XXXXXXXXX", + dashboard_url = "https://department-for-education.shinyapps.io/dashboard-name/", + site_title = "Dashboard name", + publication_name = "Publication name", + publication_link = + paste0("https://explore-education-statistics.service.gov.uk/find-statistics/", + "publication-name"), + team_email = "team.name@education.gov.uk", + repo_name = "https://github.com/dfe-analytical-services/dashboard-name", + form_url = "", + verbose = FALSE +) +} +\arguments{ +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} + +\item{google_analytics_key}{10 digit Google Analytics key if available. String, default: +\code{"XXXXXXXXXX"}} + +\item{dashboard_url}{URL for the host dashboard} + +\item{site_title}{Site title for the dashboard. String, default: \code{"dfeshiny template"}} + +\item{publication_name}{The parent publication name} + +\item{publication_link}{URL link to the parent publication} + +\item{team_email}{Email address for support contact. String, default: +\code{"explore.statistics@education.gov.uk"}} + +\item{repo_name}{The repository URL, must be a valid URL for the dfe-analytical-services GitHub +area or the dfe-gov-uk Azure DevOps. String, default: +\code{"https://github.com/dfe-analytical-services/dfeshiny"}} + +\item{form_url}{URL for a feedback form for the dashboard} + +\item{verbose}{RUn in verbose mode} +} +\description{ +Initialise global.R +} +\examples{ +\dontrun{ +init_global() +} +} +\keyword{internal} diff --git a/man/init_server.Rd b/man/init_server.Rd new file mode 100644 index 00000000..4c50a576 --- /dev/null +++ b/man/init_server.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_dashboard.R +\name{init_server} +\alias{init_server} +\title{Initialise server.R} +\usage{ +init_server(path = ".", verbose = FALSE) +} +\arguments{ +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} + +\item{verbose}{RUn in verbose mode} +} +\description{ +Initialise server.R +} +\examples{ +\dontrun{ +init_server() +} +} +\keyword{internal} diff --git a/man/init_ui.Rd b/man/init_ui.Rd new file mode 100644 index 00000000..baebaae1 --- /dev/null +++ b/man/init_ui.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_dashboard.R +\name{init_ui} +\alias{init_ui} +\title{Initialise ui.R} +\usage{ +init_ui(path = ".", verbose = FALSE) +} +\arguments{ +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} + +\item{verbose}{RUn in verbose mode} +} +\description{ +Initialise ui.R +} +\examples{ +\dontrun{ +init_ui() +} +} +\keyword{internal} diff --git a/man/init_workflow.Rd b/man/init_workflow.Rd new file mode 100644 index 00000000..afca33ff --- /dev/null +++ b/man/init_workflow.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/create_dashboard.R +\name{init_workflow} +\alias{init_workflow} +\title{Initialize GitHub Actions Workflow for Shiny App Deployment} +\usage{ +init_workflow(site_title, path = "./") +} +\arguments{ +\item{site_title}{Site title for the dashboard. String, default: \code{"dfeshiny template"}} + +\item{path}{The root directory where the app should be created. String, default: \code{"."}.} +} +\value{ +No return value. The function sets up the workflow and configuration. +} +\description{ +This function sets up a GitHub Actions workflow for deploying a Shiny app. +It ensures that the \verb{.github/workflows/} directory exists, downloads a +deployment workflow file, and creates a configuration YAML file. +} +\details{ +If the \verb{.github/workflows/} directory does not exist, it is created. +The function then downloads the deployment workflow file and places it inside +\verb{.github/workflows/}. Additionally, it generates a YAML file containing +deployment settings, allowing customization of the \code{dashboard_name}. +} +\examples{ +\dontrun{ +init_workflow() +init_workflow(dashboard_name = "my-custom-dashboard") +} +}