This purpose of this document is to build the package infrastucture.
To upgrade the versions of Vega-Lite that we support:
versions$value
in the yaml-header to this file.testthat::test()
); some snapshots will have changed.pkgdown::build_site()
), verify the visual-regression article (still to be built).These packages may not be listed in the Suggests
section of the DESCRIPTION
file. It's on you to make sure they are all up-to-date:
library("fs") library("glue") library("here") library("purrr") library("readr") library("dplyr") library("tibble") library("stringr") library("usethis") library("conflicted") library("vegawidget") conflict_prefer("filter", "dplyr")
There are two source of "truth" for this process: this document, and the contents of the directory data-raw/templates
.
(It may be useful to note this in a "contributing" document for this package.)
Each of the infrastructure elements is created anew when this document is run; so, between this document and data-raw/templates
, we need to be able to construct completely all the infrastructure.
Thus, we define this template directory and a function to delete and create
dir_templates <- here("data-raw", "templates") # create a create a clean directory, with a safety create_clean <- function(path, path_safe = here::here()) { # tests that path is within path_safe is_path_safe <- function(path, path_safe) { identical( unclass(path_safe), unclass(fs::path_common(c(path, path_safe))) ) } if (!is_path_safe(path, path_safe)) { stop( "Nothing done because ", " path: ", shQuote(path), " is not within path_safe: ", shQuote(path_safe) ) } if (fs::dir_exists(path)) { fs::dir_delete(path) } fs::dir_create(path) }
We need to know which versions of the libraries (vega, vega-lite, and vega-embed) to download. We do this by inspecting the manifest of a specific version of the vega-lite library. This package has an internal function, get_vega_version()
to help us do this:
tbl_versions <- enframe(unlist(params$versions), name = "widget", value = "vega_lite")
vega_version_all <- tbl_versions |> left_join( map_dfr(tbl_versions$vega_lite, vegawidget:::get_vega_version), by = "vega_lite" ) vega_version_all # we want to remove the "-rc.2" from the end of "4.0.0-rc.2" # "-\\w.*$" hyphen, followed by a letter, followed by anything, then end vega_version_short_all <- vega_version_all |> mutate(across(starts_with("vega"), ~sub("-\\w.*$", "", .x))) vega_version_short_all if (!identical(vega_version_all, vega_version_short_all)) { warning("You may be using a release-candidate; please check.") }
First, let's create a clean directory for the htmlwidget
dir_htmlwidgets <- here("inst", "htmlwidgets") dir_lib <- path(dir_htmlwidgets, "lib") dir_vegaembed <- path(dir_lib, "vega-embed") create_clean(dir_htmlwidgets) dir_create(dir_lib) dir_create(dir_vegaembed)
First, copy some files from our templates directory.
write_js <- function(x) { path(dir_templates, "vegawidget-all.js") %>% readr::read_lines() %>% purrr::map_chr( ~glue::glue_data(list(widget = x), .x, .open = "${") ) %>% readr::write_lines(path(dir_htmlwidgets, glue::glue("vegawidget-{x}.js"))) } walk(tbl_versions$widget, write_js)
The file vegawidget-all.yaml
requires the versions the JavaScript libraries; we interpolate these from vega_version_short
.
write_yaml <- function(x) { path(dir_templates, "vegawidget-all.yaml") %>% readr::read_lines() %>% purrr::map_chr( ~glue::glue_data(vega_version_all %>% filter(widget == !!x), .x) ) %>% readr::write_lines(path(dir_htmlwidgets, glue::glue("vegawidget-{x}.yaml"))) } walk(vega_version_all$widget, write_yaml)
The file vega-embed.css
adds some css for the (old-style) links that appeared below a rendered spec:
fs::file_copy( fs::path(dir_templates, "vega-embed.css"), fs::path(dir_vegaembed, "vega-embed.css") )
Here's where we download the libraries themselves, along with the licences.
license_downloads <- tribble( ~path_local, ~path_remote, "vega-lite/LICENSE", "https://raw.githubusercontent.com/vega/vega-lite/next/LICENSE", "vega/LICENSE", "https://raw.githubusercontent.com/vega/vega/master/LICENSE", "vega-embed/LICENSE", "https://raw.githubusercontent.com/vega/vega-embed/next/LICENSE" ) license_downloads
downloads_template <- tribble( ~path_local, ~path_remote, "vega-lite/vega-lite@{vega_lite}.min.js", "https://cdn.jsdelivr.net/npm/vega-lite@{vega_lite}", "vega/vega@{vega}.min.js", "https://cdn.jsdelivr.net/npm/vega@{vega}", "vega-embed/vega-embed@{vega_embed}.min.js", "https://cdn.jsdelivr.net/npm/vega-embed@{vega_embed}", ) downloads_local <- function(widget) { df_local <- dplyr::filter(vega_version_all, widget == !!widget) downloads_template |> dplyr::mutate( dplyr::across( dplyr::starts_with("path"), ~map_chr(.x, ~glue::glue_data(df_local, .x)) ) ) } htmlwidgets_downloads_all <- map_dfr(vega_version_all$widget, downloads_local) htmlwidgets_downloads_all
get_file <- function(path_local, path_remote, path_local_root) { path_local <- fs::path(path_local_root, path_local) # if directory does not yet exist, create it # TODO: swap out with create_clean() dir_local <- fs::path_dir(path_local) if (!fs::dir_exists(dir_local)) { dir_create(dir_local) } download.file(path_remote, destfile = path_local) invisible(NULL) }
Here, we create the lib
directory, then "walk" through each row of the downloads
data frame to get each of the files and put it into place.
pwalk(license_downloads, get_file, path_local_root = dir_lib)
pwalk(htmlwidgets_downloads_all, get_file, path_local_root = dir_lib)
We use this to support the vega_version()
function.
.vega_version_all <- as.data.frame(vega_version_all) .widget_default <- params$widget_default
.vw_handler_library <- list( event = .vw_handler_def( args = c("event", "item"), bodies = list( item = .vw_handler_body( params = list(), text = c( "// returns the item", "return item;" ) ), datum = .vw_handler_body( params = list(), text = c( "// if there is no item or no datum, return null", "if (item === null || item === undefined || item.datum === undefined) {", " return null;", "}", "", "// returns the datum behind the mark associated with the event", "return item.datum;" ) ) ) ), signal = .vw_handler_def( args = c("name", "value"), bodies = list( value = .vw_handler_body( params = list(), text = c( "// returns the value of the signal", "return value;" ) ) ) ), data = .vw_handler_def( args = c("name", "value"), bodies = list( value = .vw_handler_body( params = list(), text = c( "// returns a copy of the data", "return value.slice();" ) ) ) ), effect = .vw_handler_def( args = "x", bodies = list( shiny_input = .vw_handler_body( params = list(inputId = NULL, expr = "x"), text = c( "// sets the Shiny-input named `inputId` to `expr` (default \"x\")", "Shiny.setInputValue('${inputId}', ${expr});" ) ), console = .vw_handler_body( params = list(label = "", expr = "x"), text = c( "// if `label` is non-trivial, prints it to the JS console", "'${label}'.length > 0 ? console.log('${label}') : null;", "// prints `expr` (default \"x\") to the JS console", "console.log(${expr});" ) ), element_text = .vw_handler_body( params = list(selector = NULL, expr = "x"), text = c( "// to element `selector`, adds text `expr` (default \"x\")", "document.querySelector('${selector}').innerText = ${expr}" ) ) ) ) )
usethis::use_data( .vega_version_all, .widget_default, .vw_handler_library, internal = TRUE, overwrite = TRUE )
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.