diff --git a/DESCRIPTION b/DESCRIPTION
index 7a2d5289..9dea1e61 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,14 +1,14 @@
Package: dash
Title: An Interface to the Dash Ecosystem for Authoring Reactive Web Applications
-Version: 0.2.0
+Version: 0.3.0
Authors@R: c(person("Chris", "Parmer", role = c("aut"), email = "chris@plot.ly"), person("Ryan Patrick", "Kyle", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-5829-9867"), email = "ryan@plot.ly"), person("Carson", "Sievert", role = c("aut"), comment = c(ORCID = "0000-0002-4958-2844")), person(family = "Plotly Technologies", role = "cph"))
Description: A framework for building analytical web applications, Dash offers a pleasant and productive development experience. No JavaScript required.
Depends:
R (>= 3.0.2)
Imports:
dashHtmlComponents (== 1.0.2),
- dashCoreComponents (== 1.6.0),
- dashTable (== 4.5.1),
+ dashCoreComponents (== 1.8.0),
+ dashTable (== 4.6.0),
R6,
fiery (> 1.0.0),
routr (> 0.2.0),
@@ -32,8 +32,8 @@ Collate:
'print.R'
'internal.R'
Remotes: plotly/dash-html-components@55c3884,
- plotly/dash-core-components@c107e0f,
- plotly/dash-table@3058bd5
+ plotly/dash-core-components@fc153b4,
+ plotly/dash-table@79d46ca
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
diff --git a/NAMESPACE b/NAMESPACE
index 6daedb64..e5c7d9ec 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -27,4 +27,4 @@ importFrom(routr,RouteStack)
importFrom(routr,ressource_route)
importFrom(stats,setNames)
importFrom(tools,file_ext)
-importFrom(utils,getFromNamespace)
\ No newline at end of file
+importFrom(utils,getFromNamespace)
diff --git a/R/dash.R b/R/dash.R
index 9c86c5a0..6f403cdd 100644
--- a/R/dash.R
+++ b/R/dash.R
@@ -5,7 +5,7 @@
#' @usage Dash
#'
#' @section Constructor: Dash$new(
-#' name = "dash",
+#' name = NULL,
#' server = fiery::Fire$new(),
#' assets_folder = 'assets',
#' assets_url_path = '/assets',
@@ -24,7 +24,7 @@
#' @section Arguments:
#' \tabular{lll}{
#' `name` \tab \tab Character. The name of the Dash application (placed in the `
`
-#' of the HTML page).\cr
+#' of the HTML page). DEPRECATED; please use `index_string()` or `interpolate_index()` instead.\cr
#' `server` \tab \tab The web server used to power the application.
#' Must be a [fiery::Fire] object.\cr
#' `assets_folder` \tab \tab Character. A path, relative to the current working directory,
@@ -100,6 +100,9 @@
#' from the Dash backend. The latter may offer improved performance relative
#' to callbacks written in R.
#' }
+#' \item{`title("dash")`}{
+#' The title of the app. If no title is supplied, Dash for R will use 'dash'.
+#' }
#' \item{`callback_context()`}{
#' The `callback_context` method permits retrieving the inputs which triggered
#' the firing of a given callback, and allows introspection of the input/state
@@ -114,7 +117,63 @@
#' present a warning and return `NULL` if the Dash app was not loaded via `source()`
#' if the `DASH_APP_PATH` environment variable is undefined.
#' }
-#' \item{`run_server(host = Sys.getenv('HOST', "127.0.0.1"),
+#' \item{`index_string(string)`}{
+#' The `index_string` method allows the specification of a custom index by changing
+#' the default `HTML` template that is generated by the Dash UI. Meta tags, CSS, Javascript,
+#' are some examples of features that can be modified.
+#' This method will present a warning if your HTML template is missing any necessary elements
+#' and return an error if a valid index is not defined. The following interpolation keys are
+#' currently supported:
+#' \describe{
+#' \item{`{%metas%}`}{Optional - The registered meta tags.}
+#' \item{`{%favicon%}`}{Optional - A favicon link tag if found in assets.}
+#' \item{`{%css%}`}{Optional - Link tags to css resources.}
+#' \item{`{%config%}`}{Required - Config generated by dash for the renderer.}
+#' \item{`{%app_entry%}`}{Required - The container where dash react components are rendered.}
+#' \item{`{%scripts%}`}{Required - Collected dependencies scripts tags.}
+#' }
+#' \describe{
+#' \item{Example of a basic HTML index string:}{
+#' \preformatted{
+#' "
+#'
+#'
+#' \{\%meta_tags\%\}
+#' \{\{%css\%\}\}
+#' \{\%favicon\%\}
+#' \{\%css_tags\%\}
+#'
+#'
+#' \{\%app_entry\%\}
+#'
+#'
+#' "
+#' }
+#' }
+#' }
+#' }
+#' \item{`interpolate_index(template_index, ...)`}{
+#' With the `interpolate_index` method, we can pass a custom index with template string
+#' variables that are already evaluated. We can directly pass arguments to the `template_index`
+#' by assigning them to variables present in the template. This is similar to the `index_string` method
+#' but offers the ability to change the default components of the Dash index as seen in the example below:
+#' \preformatted{
+#' app$interpolate_index(
+#' template_index,
+#' metas = "",
+#' renderer = renderer,
+#' config = config)
+#' }
+#' \describe{
+#' \item{template_index}{Character. A formatted string with the HTML index string. Defaults to the initial template}
+#' \item{...}{Named List. The unnamed arguments can be passed as individual named lists corresponding to the components
+#' of the Dash html index. These include the same arguments as those found in the `index_string()` template.}
+#' }
+#' }
+#' \item{`run_server(host = Sys.getenv('HOST', "127.0.0.1"),
#' port = Sys.getenv('PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' The `run_server` method has 13 formal arguments, several of which are optional:
#' \describe{
@@ -174,7 +233,7 @@ Dash <- R6::R6Class(
config = list(),
# i.e., the Dash$new() method
- initialize = function(name = "dash",
+ initialize = function(name = NULL,
server = fiery::Fire$new(),
assets_folder = 'assets',
assets_url_path = '/assets',
@@ -191,13 +250,18 @@ Dash <- R6::R6Class(
suppress_callback_exceptions = FALSE) {
# argument type checking
- assertthat::assert_that(is.character(name))
assertthat::assert_that(inherits(server, "Fire"))
assertthat::assert_that(is.logical(serve_locally))
assertthat::assert_that(is.logical(suppress_callback_exceptions))
# save relevant args as private fields
- private$name <- name
+ if (!is.null(name)) {
+ warning(sprintf(
+ "The supplied application title, '%s', should be set using the title() method, or passed via index_string() or interpolate_index(); it has been ignored, and 'dash' will be used instead.",
+ name),
+ call. = FALSE
+ )
+ }
private$serve_locally <- serve_locally
private$eager_loading <- eager_loading
# remove leading and trailing slash(es) if present
@@ -763,11 +827,44 @@ Dash <- R6::R6Class(
sep="/")))
},
+ # ------------------------------------------------------------------------
+ # specify a custom index string
+ # ------------------------------------------------------------------------
+ index_string = function(string) {
+ private$custom_index <- validate_keys(string)
+ },
+
+ # ------------------------------------------------------------------------
+ # modify the templated variables by using the `interpolate_index` method.
+ # ------------------------------------------------------------------------
+ interpolate_index = function(template_index = private$template_index[[1]], ...) {
+ template = template_index
+ kwargs <- list(...)
+
+ for (name in names(kwargs)) {
+ key = paste0('\\{\\%', name, '\\%\\}')
+ template = sub(key, kwargs[[name]], template)
+ }
+
+ invisible(validate_keys(names(kwargs)))
+
+ private$template_index <- template
+ },
+
+ # ------------------------------------------------------------------------
+ # specify a custom title
+ # ------------------------------------------------------------------------
+ title = function(string = "dash") {
+ assertthat::assert_that(is.character(string))
+ private$name <- string
+ },
+
# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = Sys.getenv('HOST', "127.0.0.1"),
port = Sys.getenv('PORT', 8050),
+
block = TRUE,
showcase = FALSE,
use_viewer = FALSE,
@@ -1266,6 +1363,24 @@ Dash <- R6::R6Class(
# akin to https://github.com/plotly/dash/blob/d2ebc837/dash/dash.py#L338
# note discussion here https://github.com/plotly/dash/blob/d2ebc837/dash/dash.py#L279-L284
+ custom_index = NULL,
+ template_index = c(
+ "
+
+
+ {%meta_tags%}
+ {%title%}
+ {%favicon%}
+ {%css_tags%}
+
+
+ {%app_entry%}
+
+
+ ", NA),
.index = NULL,
generateReloadHash = function() {
@@ -1434,13 +1549,32 @@ Dash <- R6::R6Class(
css_tags <- all_tags[["css_tags"]]
# retrieve script tags for serving in the index
- scripts_tags <- all_tags[["scripts_tags"]]
+ scripts <- all_tags[["scripts_tags"]]
# insert meta tags if present
meta_tags <- all_tags[["meta_tags"]]
+
+ # define the react-entry-point
+ app_entry <- "
-
+ %s
',
- meta_tags,
- private$name,
- favicon,
- css_tags,
- to_JSON(self$config),
- scripts_tags
- )
+ meta_tags,
+ private$name,
+ favicon,
+ css_tags,
+ app_entry,
+ config,
+ scripts
+ )
+ }
}
)
)
diff --git a/R/utils.R b/R/utils.R
index ae0ed9ef..0735bb52 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -1270,3 +1270,33 @@ tryCompress <- function(request, response) {
}
return(response$compress())
}
+
+interpolate_str <- function(index_template, ...) {
+ # This function takes an index string, along with
+ # user specified keys for the html keys of the index
+ # and sets the default values of the keys to the
+ # ones specified by the keys themselves, returning
+ # the custom index template.
+ template = index_template
+ kwargs <- list(...)
+
+ for (name in names(kwargs)) {
+ key = paste0('\\{', name, '\\}')
+
+ template = sub(key, kwargs[[name]], template)
+ }
+ return(template)
+}
+
+validate_keys <- function(string) {
+ required_keys <- c("app_entry", "config", "scripts")
+
+ keys_present <- vapply(required_keys, function(x) grepl(x, string), logical(1))
+
+ if (!all(keys_present)) {
+ stop(sprintf("Did you forget to include %s in your index string?",
+ paste(names(keys_present[keys_present==FALSE]), collapse = ", ")))
+ } else {
+ return(string)
+ }
+}
diff --git a/README.md b/README.md
index eed95c5b..e20210a8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[](https://circleci.com/gh/plotly/dashR/tree/dev)
+[](https://circleci.com/gh/plotly/dashR/tree/master)
[](https://github.com/plotly/dashR/blob/master/LICENSE)
[](https://github.com/plotly/dashR/graphs/contributors)
diff --git a/man/Dash.Rd b/man/Dash.Rd
index 0e2e9a00..bf7e9aa4 100644
--- a/man/Dash.Rd
+++ b/man/Dash.Rd
@@ -13,7 +13,7 @@ A framework for building analytical web applications, Dash offers a pleasant and
}
\section{Constructor}{
Dash$new(
-name = "dash",
+name = NULL,
server = fiery::Fire$new(),
assets_folder = 'assets',
assets_url_path = '/assets',
@@ -34,7 +34,7 @@ suppress_callback_exceptions = FALSE
\tabular{lll}{
\code{name} \tab \tab Character. The name of the Dash application (placed in the \code{}
-of the HTML page).\cr
+of the HTML page). DEPRECATED; please use \code{index_string()} or \code{interpolate_index()} instead.\cr
\code{server} \tab \tab The web server used to power the application.
Must be a \link[fiery:Fire]{fiery::Fire} object.\cr
\code{assets_folder} \tab \tab Character. A path, relative to the current working directory,
@@ -114,6 +114,9 @@ describes a locally served JavaScript function instead. The latter defines a
from the Dash backend. The latter may offer improved performance relative
to callbacks written in R.
}
+\item{\code{title("dash")}}{
+The title of the app. If no title is supplied, Dash for R will use 'dash'.
+}
\item{\code{callback_context()}}{
The \code{callback_context} method permits retrieving the inputs which triggered
the firing of a given callback, and allows introspection of the input/state
@@ -128,6 +131,62 @@ but this is configurable via the \code{prefix} parameter. Note: this method will
present a warning and return \code{NULL} if the Dash app was not loaded via \code{source()}
if the \code{DASH_APP_PATH} environment variable is undefined.
}
+\item{\code{index_string(string)}}{
+The \code{index_string} method allows the specification of a custom index by changing
+the default \code{HTML} template that is generated by the Dash UI. Meta tags, CSS, Javascript,
+are some examples of features that can be modified.
+This method will present a warning if your HTML template is missing any necessary elements
+and return an error if a valid index is not defined. The following interpolation keys are
+currently supported:
+\describe{
+\item{\code{{\%metas\%}}}{Optional - The registered meta tags.}
+\item{\code{{\%favicon\%}}}{Optional - A favicon link tag if found in assets.}
+\item{\code{{\%css\%}}}{Optional - Link tags to css resources.}
+\item{\code{{\%config\%}}}{Required - Config generated by dash for the renderer.}
+\item{\code{{\%app_entry\%}}}{Required - The container where dash react components are rendered.}
+\item{\code{{\%scripts\%}}}{Required - Collected dependencies scripts tags.}
+}
+\describe{
+\item{Example of a basic HTML index string:}{
+\preformatted{
+"
+
+
+ \{\%meta_tags\%\}
+ \{\{%css\%\}\}
+ \{\%favicon\%\}
+ \{\%css_tags\%\}
+
+
+ \{\%app_entry\%\}
+
+
+"
+ }
+}
+}
+}
+\item{\code{interpolate_index(template_index, ...)}}{
+With the \code{interpolate_index} method, we can pass a custom index with template string
+variables that are already evaluated. We can directly pass arguments to the \code{template_index}
+by assigning them to variables present in the template. This is similar to the \code{index_string} method
+but offers the ability to change the default components of the Dash index as seen in the example below:
+\preformatted{
+ app$interpolate_index(
+ template_index,
+ metas = "",
+ renderer = renderer,
+ config = config)
+ }
+\describe{
+\item{template_index}{Character. A formatted string with the HTML index string. Defaults to the initial template}
+\item{...}{Named List. The unnamed arguments can be passed as individual named lists corresponding to the components
+of the Dash html index. These include the same arguments as those found in the \code{index_string()} template.}
+}
+}
\item{\code{run_server(host = Sys.getenv('HOST', "127.0.0.1"), port = Sys.getenv('PORT', 8050), block = TRUE, showcase = FALSE, ...)}}{
The \code{run_server} method has 13 formal arguments, several of which are optional:
\describe{
diff --git a/tests/integration/test_name.py b/tests/integration/test_name.py
new file mode 100644
index 00000000..5000bf92
--- /dev/null
+++ b/tests/integration/test_name.py
@@ -0,0 +1,55 @@
+named_app = """
+library(dash)
+library(dashHtmlComponents)
+app <- Dash$new()
+
+app$title("Testing")
+
+app$layout(htmlDiv(list(htmlDiv(id='container',children='Hello Dash for R testing'))))
+app$run_server()
+"""
+
+app_with_template = """
+library(dash)
+library(dashHtmlComponents)
+app <- Dash$new()
+
+string <-
+ "
+
+
+ {%meta_tags%}
+ Testing Again
+ {%favicon%}
+ {%css_tags%}
+
+
+ {%app_entry%}
+
+
+ "
+
+app$index_string(string)
+
+app$layout(htmlDiv(list(htmlDiv(id='container',children='Hello Dash for R testing'))))
+app$run_server()
+"""
+
+
+def test_rapp001r_with_appname(dashr):
+ dashr.start_server(named_app)
+ dashr.wait_for_text_to_equal(
+ "#container", "Hello Dash for R testing", timeout=1
+ )
+ assert dashr.find_element("title").get_attribute("text") == "Testing"
+
+
+def test_rapp002_r_with_template(dashr):
+ dashr.start_server(app_with_template)
+ dashr.wait_for_text_to_equal(
+ "#container", "Hello Dash for R testing", timeout=1
+ )
+ assert dashr.find_element("title").get_attribute("text") == "Testing Again"