diff --git a/.circleci/config.yml b/.circleci/config.yml
index 00c63e0c..2d9fe5c5 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -39,7 +39,7 @@ jobs:
. venv/bin/activate
pip install -e git+https://github.com/plotly/dash.git#egg=dash[testing]
export PATH=$PATH:/home/circleci/.local/bin/
- pytest --log-cli-level DEBUG --nopercyfinalize --junitxml=test-reports/dashr.xml tests/integration/
+ pytest --nopercyfinalize --junitxml=test-reports/dashr.xml tests/integration/
- store_artifacts:
path: test-reports
- store_test_results:
@@ -55,7 +55,7 @@ jobs:
- run:
name: 🔎 Unit tests
command: |
- sudo Rscript -e 'testthat::test_dir("tests/")'
+ sudo Rscript -e 'res=devtools::test("tests/", reporter=default_reporter());df=as.data.frame(res);if(sum(df$failed) > 0 || any(df$error)) {q(status=1)}'
workflows:
version: 2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e4f728f..e9a4e546 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,22 @@
# Change Log for Dash for R
All notable changes to this project will be documented in this file.
+
+## [0.3.0] - 2020-02-12
+### Added
+- Support for config-aware relative paths [#172](https://github.com/plotly/dashR/pull/172)
+- Support index customization and index templates [#168](https://github.com/plotly/dashR/pull/168)
+- Application titles may be set using the `app$title()` method, for parity with Dash for Python's `app.title` syntax [#168](https://github.com/plotly/dashR/pull/168)
+
+### Changed
+- Dash for R now requires `dashCoreComponents` v1.8.0
+- Dash for R now requires `dashTable` v4.6.0
+- Automatically set routes and requests pathname prefixes if `DASH_APP_NAME` environment variable has been set [#165](https://github.com/plotly/dashR/pull/165)
+
+### Deprecated
+- Application titles can no longer be set using `name` parameter, which is now deprecated with a warning, for parity with Dash for Python [#168](https://github.com/plotly/dashR/pull/168)
+- Removed `DASH_HOST` and `DASH_PORT`, Dash for R now respects `HOST` and `PORT` [#167](https://github.com/plotly/dashR/pull/167)
+
## [0.2.0] - 2020-01-03
### Added
- Support for asynchronous/dynamic loading of dependencies, resource caching, and asset fingerprinting [#157](https://github.com/plotly/dashR/pull/157)
@@ -28,6 +44,7 @@ All notable changes to this project will be documented in this file.
- Fixes for hot reloading interval handling and refreshing apps within viewer pane [#148](https://github.com/plotly/dashR/pull/148)
- `get_asset_url` checks `getAppPath()` as well as `DASH_APP_ROOT_PATH` environment variable when invoked [#161](https://github.com/plotly/dashR/pull/161)
+
## [0.1.0] - 2019-07-10
### Added
- Initial release
diff --git a/DESCRIPTION b/DESCRIPTION
index 7a2d5289..e91fe991 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
-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"))
+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("Hammad", "Khan", role = c("aut"), email = "hammadkhan@plot.ly"), 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/R/dash.R b/R/dash.R
index ba122302..14fc024c 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,15 +117,92 @@
#' 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('DASH_HOST', "127.0.0.1"),
-#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
+#' \item{`get_relative_path(path, requests_pathname_prefix)`}{
+#' The `get_relative_path` method simplifies the handling of URLs and pathnames for apps
+#' running locally and on a deployment server such as Dash Enterprise. It handles the prefix
+#' for requesting assets similar to the `get_asset_url` method, but can also be used for URL handling
+#' in components such as `dccLink` or `dccLocation`. For example, `app$get_relative_url("/page/")`
+#' would return `/app/page/` for an app running on a deployment server. The path must be prefixed with
+#' a `/`.
+#' \describe{
+#' \item{path}{Character. A path string prefixed with a leading `/` which directs at a path or asset directory.}
+#' \item{requests_pathname_prefix}{Character. The pathname prefix for the app on a deployed application. Defaults to the environment variable set by the server, or `""` if run locally.}
+#' }
+#' \item{`strip_relative_path(path, requests_pathname_prefix)`}{
+#' The `strip_relative_path` method simplifies the handling of URLs and pathnames for apps
+#' running locally and on a deployment server such as Dash Enterprise. It acts almost opposite the `get_relative_path`
+#' method, by taking a `relative path` as an input, and returning the `path` stripped of the `requests_pathname_prefix`,
+#' and any leading or trailing `/`. For example, a path string `/app/homepage/`, would be returned as
+#' `homepage`. This is particularly useful for `dccLocation` URL routing.
+#' \describe{
+#' \item{path}{Character. A path string prefixed with a leading `/` and `requests_pathname_prefix` which directs at a path or asset directory.}
+#' \item{requests_pathname_prefix}{Character. The pathname prefix for the app on a deployed application. Defaults to the environment variable set by the server, or `""` if run locally.}
+#' }
+#' \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{
-#' \item{host}{Character. A string specifying a valid IPv4 address for the Fiery server, or `0.0.0.0` to listen on all addresses. Default is `127.0.0.1` Environment variable: `DASH_HOST`.}
-#' \item{port}{Integer. Specifies the port number on which the server should listen (default is `8050`). Environment variable: `DASH_PORT`.}
+#' \item{host}{Character. A string specifying a valid IPv4 address for the Fiery server, or `0.0.0.0` to listen on all addresses. Default is `127.0.0.1` Environment variable: `HOST`.}
+#' \item{port}{Integer. Specifies the port number on which the server should listen (default is `8050`). Environment variable: `PORT`.}
#' \item{block}{Logical. Start the server while blocking console input? Default is `TRUE`.}
#' \item{showcase}{Logical. Load the Dash application into the default web browser when server starts? Default is `FALSE`.}
-#' \item{use_viewer}{Logical. Load the Dash application into RStudio's viewer pane? Requires that `host` is either `127.0.0.1` or `localhost`, and that Dash application is started within RStudio; if `use_viewer = TRUE` and these conditions are not satsified, the user is warned and the app opens in the default browser instead. Default is `FALSE`.}
+#' \item{use_viewer}{Logical. Load the Dash application into RStudio's viewer pane? Requires that `host` is either `127.0.0.1` or `localhost`, and that Dash application is started within RStudio; if `use_viewer = TRUE` and these conditions are not satisfied, the user is warned and the app opens in the default browser instead. Default is `FALSE`.}
#' \item{debug}{Logical. Enable/disable all the dev tools unless overridden by the arguments or environment variables. Default is `FALSE` when called via `run_server`. Environment variable: `DASH_DEBUG`.}
#' \item{dev_tools_ui}{Logical. Show Dash's dev tools UI? Default is `TRUE` if `debug == TRUE`, `FALSE` otherwise. Environment variable: `DASH_UI`.}
#' \item{dev_tools_hot_reload}{Logical. Activate hot reloading when app, assets, and component files change? Default is `TRUE` if `debug == TRUE`, `FALSE` otherwise. Requires that the Dash application is loaded using `source()`, so that `srcref` attributes are available for executed code. Environment variable: `DASH_HOT_RELOAD`.}
@@ -174,7 +254,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 +271,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
@@ -213,8 +298,8 @@ Dash <- R6::R6Class(
private$in_viewer <- FALSE
# config options
- self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX", url_base_pathname)
- self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX", url_base_pathname)
+ self$config$routes_pathname_prefix <- resolvePrefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX", url_base_pathname)
+ self$config$requests_pathname_prefix <- resolvePrefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX", url_base_pathname)
self$config$external_scripts <- external_scripts
self$config$external_stylesheets <- external_stylesheets
@@ -763,11 +848,61 @@ Dash <- R6::R6Class(
sep="/")))
},
+ # ------------------------------------------------------------------------
+ # return relative asset URLs
+ # ------------------------------------------------------------------------
+
+ get_relative_path = function(path, requests_pathname_prefix = self$config$requests_pathname_prefix) {
+ asset = get_relative_path(requests_pathname = requests_pathname_prefix, path = path)
+ return(asset)
+ },
+
+
+ # ------------------------------------------------------------------------
+ # return relative asset URLs
+ # ------------------------------------------------------------------------
+
+ strip_relative_path = function(path, requests_pathname_prefix = self$config$requests_pathname_prefix) {
+ asset = strip_relative_path(requests_pathname = requests_pathname_prefix, path = path)
+ return(asset)
+ },
+
+ # 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('DASH_HOST', "127.0.0.1"),
- port = Sys.getenv('DASH_PORT', 8050),
+ run_server = function(host = Sys.getenv('HOST', "127.0.0.1"),
+ port = Sys.getenv('PORT', 8050),
block = TRUE,
showcase = FALSE,
use_viewer = FALSE,
@@ -827,7 +962,7 @@ Dash <- R6::R6Class(
# set the modtime to track state of the Dash app directory
# this calls getAppPath, which will try three approaches to
# identifying the local app path (depending on whether the app
- # is invoked via script, source(), or executed dire ctly from console)
+ # is invoked via script, source(), or executed directly from console)
self$config$ui <- dev_tools_ui
if (dev_tools_hot_reload) {
@@ -848,7 +983,6 @@ Dash <- R6::R6Class(
self$server$on('cycle-end', function(server, ...) {
# handle case where assets are not present, since we can still hot reload the app itself
#
- # private$last_refresh is set after the asset_map is refreshed
# private$last_reload stores the time of the last hard or soft reload event
# private$last_cycle will be set when the cycle-end handler terminates
#
@@ -1013,7 +1147,7 @@ Dash <- R6::R6Class(
# assuming private$layout is either a function or a list of components...
layout_ <- if (is.function(private$layout_)) private$layout_() else private$layout_
- # accomodate functions that return a single component
+ # accommodate functions that return a single component
if (is.component(layout_)) layout_ <- list(layout_)
# make sure we are working with a list of components
@@ -1266,6 +1400,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() {
@@ -1315,7 +1467,7 @@ Dash <- R6::R6Class(
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]
- # styleheets always go in header
+ # stylesheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
if (is.null(dep$stylesheet)) return(NULL)
dep$script <- NULL
@@ -1434,13 +1586,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 450040c0..31a4e215 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -457,22 +457,25 @@ valid_seq <- function(params) {
}
}
-resolve_prefix <- function(prefix, environment_var, base_pathname) {
+resolvePrefix <- function(prefix, environment_var, base_pathname) {
if (!(is.null(prefix))) {
assertthat::assert_that(is.character(prefix))
return(prefix)
} else {
+ # Check environment variables
prefix_env <- Sys.getenv(environment_var)
- if (prefix_env != "") {
+ env_base_pathname <- Sys.getenv("DASH_URL_BASE_PATHNAME")
+ app_name <- Sys.getenv("DASH_APP_NAME")
+
+ if (prefix_env != "")
return(prefix_env)
- } else {
- env_base_pathname <- Sys.getenv("DASH_URL_BASE_PATHNAME")
- if (env_base_pathname != "")
- return(env_base_pathname)
- else
- return(base_pathname)
- }
+ else if (app_name != "")
+ return(sprintf("/%s/", app_name))
+ else if (env_base_pathname != "")
+ return(env_base_pathname)
+ else
+ return(base_pathname)
}
}
@@ -1267,3 +1270,75 @@ tryCompress <- function(request, response) {
}
return(response$compress())
}
+
+get_relative_path <- function(requests_pathname, path) {
+ # Returns a path with the config setting 'requests_pathname_prefix' prefixed to
+ # it. This is particularly useful for apps deployed on Dash Enterprise, which makes
+ # it easier to serve apps under both URL prefixes and localhost.
+
+ if (requests_pathname == "/" && path == "") {
+ return("/")
+ }
+ else if (requests_pathname != "/" && path == "") {
+ return(requests_pathname)
+ }
+ else if (!startsWith(path, "/")) {
+ stop(sprintf(paste0("Unsupported relative path! Paths that aren't prefixed" ,
+ "with a leading '/' are not supported. You supplied '%s'."),
+ path))
+ }
+ else {
+ return(paste(gsub("/$", "", requests_pathname), gsub("^/", "", path), sep = "/"))
+ }
+}
+
+strip_relative_path <- function(requests_pathname, path) {
+ # Returns a relative path with the `requests_pathname_prefix` and leadings and trailing
+ # slashes stripped from it. This function is particularly relevant to dccLocation pathname routing.
+
+ if (is.null(path)) {
+ return(NULL)
+ }
+ else if ((requests_pathname != "/" && !startsWith(path, gsub("/$", "", requests_pathname)))
+ || (requests_pathname == "/" && !startsWith(path, "/"))) {
+ stop(sprintf(paste0("Unsupported relative path! Path's that are not prefixed ",
+ "with a leading 'requests_pathname_prefix` are not suported. ",
+ "You supplied '%s', and requests_pathname_prefix was '%s'."),
+ path, requests_pathname
+ ))
+ }
+ else if (requests_pathname != "/" && startsWith(path, gsub("/$", "", requests_pathname))) {
+ path = sub(gsub("/$", "", requests_pathname), "", path)
+ }
+ return(trimws(gsub("/", "", path)))
+}
+
+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/man/Dash.Rd b/man/Dash.Rd
index a93a5191..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,11 +131,67 @@ 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{run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"), port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)}}{
+\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{
-\item{host}{Character. A string specifying a valid IPv4 address for the Fiery server, or \code{0.0.0.0} to listen on all addresses. Default is \code{127.0.0.1} Environment variable: \code{DASH_HOST}.}
-\item{port}{Integer. Specifies the port number on which the server should listen (default is \code{8050}). Environment variable: \code{DASH_PORT}.}
+\item{host}{Character. A string specifying a valid IPv4 address for the Fiery server, or \code{0.0.0.0} to listen on all addresses. Default is \code{127.0.0.1} Environment variable: \code{HOST}.}
+\item{port}{Integer. Specifies the port number on which the server should listen (default is \code{8050}). Environment variable: \code{PORT}.}
\item{block}{Logical. Start the server while blocking console input? Default is \code{TRUE}.}
\item{showcase}{Logical. Load the Dash application into the default web browser when server starts? Default is \code{FALSE}.}
\item{use_viewer}{Logical. Load the Dash application into RStudio's viewer pane? Requires that \code{host} is either \code{127.0.0.1} or \code{localhost}, and that Dash application is started within RStudio; if \code{use_viewer = TRUE} and these conditions are not satsified, the user is warned and the app opens in the default browser instead. Default is \code{FALSE}.}
diff --git a/man/dash-package.Rd b/man/dash-package.Rd
index f17d0269..0f816519 100644
--- a/man/dash-package.Rd
+++ b/man/dash-package.Rd
@@ -34,6 +34,7 @@ Authors:
\item Chris Parmer \email{chris@plot.ly}
\item Ryan Patrick Kyle \email{ryan@plot.ly}
\item Carson Sievert
+ \item Hammad Khan \email{hammadkhan@plot.ly}
}
Other contributors:
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"
diff --git a/tests/testthat/test-index.R b/tests/testthat/test-index.R
new file mode 100644
index 00000000..4a5ccd47
--- /dev/null
+++ b/tests/testthat/test-index.R
@@ -0,0 +1,52 @@
+context("customindex")
+
+test_that("Omitting required template keys produces warnings", {
+ string <-
+ "
+
+
+ {%meta_tags%}
+ Testing Again
+ {%favicon%}
+ {%css_tags%}
+
+
+ {%app_entry%}
+
+
+ "
+
+ app <- Dash$new()
+
+ expect_error(
+ app$index_string(gsub("\\{\\%config\\%\\}|\\{\\%scripts\\%\\}|\\{\\%app_entry\\%\\}", "", string)),
+ "Did you forget to include app_entry, config, scripts in your index string?"
+ )
+
+ expect_error(
+ app$index_string(gsub("\\{\\%scripts\\%\\}", "", string)),
+ "Did you forget to include scripts in your index string?"
+ )
+
+ expect_error(
+ app$index_string(gsub("\\{\\%app_entry\\%\\}", "", string)),
+ "Did you forget to include app_entry in your index string?"
+ )
+
+ expect_error(
+ app$index_string(gsub("\\{\\%config\\%\\}", "", string)),
+ "Did you forget to include config in your index string?"
+ )
+})
+
+test_that("Customizing title using `name` produces a warning", {
+
+ expect_warning(
+ Dash$new(name="Testing"),
+ "The supplied application title, 'Testing', 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.",
+ fixed=TRUE
+ )
+})