Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Provide support for arbitrary file extensions within Dash for R component libraries by rpkyle · Pull Request #186 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Provide support for arbitrary file extensions within Dash for R component libraries by rpkyle · Pull Request #186 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Provide support for arbitrary file extensions within Dash for R component libraries by rpkyle · Pull Request #186 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Provide support for arbitrary file extensions within Dash for R component libraries by rpkyle · Pull Request #186 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Provide support for arbitrary file extensions within Dash for R component libraries by rpkyle · Pull Request #186 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Provide support for arbitrary file extensions within Dash for R component libraries by rpkyle · Pull Request #186 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Provide support for arbitrary file extensions within Dash for R component libraries by rpkyle · Pull Request #186 · plotly/dashR · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,7 +37,7 @@ jobs:
command: |
python -m venv venv
. venv/bin/activate
git clone --depth 1 https://github.com/plotly/dash.git dash-main
git clone -b 481-arbitrary-extensions --depth 1 https://github.com/plotly/dash.git dash-main

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Temporary, for (1) the modified standard test component, (2) the modified generator

cd dash-main && pip install -e .[dev,testing] --progress-bar off && cd ..
cd dash-main/\@plotly/dash-generator-test-component-nested && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
cd dash-main/\@plotly/dash-generator-test-component-standard && npm ci && npm run build && sudo R CMD INSTALL . && cd ../../..
Expand Down
15 changes: 8 additions & 7 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased
### Added
- Support for inline clientside callbacks in JavaScript [#140](https://github.com/plotly/dashR/pull/140)
- Support for arbitrary file extensions for assets within component libraries [#186](https://github.com/plotly/dashR/pull/186)

## [0.3.0] - 2020-02-12
### Added
Expand DownExpand Up@@ -53,8 +54,8 @@ All notable changes to this project will be documented in this file.
- Initial release
- Support for `plot_ly` and `ggplotly` "subplots" [#84](https://github.com/plotly/dashR/pull/84)
- Improved debugging support [#87](https://github.com/plotly/dashR/pull/87), including Dash Dev Tools and `debug` mode
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Provide a useful warning message when JS dependencies cannot be found [#81](https://github.com/plotly/dashR/pull/81)
- Support for externalized PropTypes introduced
- Support for `callback_context` added
- Options to set `dev_tools_ui` and `dev_tools_props_check` added

Expand All@@ -69,11 +70,11 @@ All notable changes to this project will be documented in this file.

### Fixed
- CSS dependencies are now properly loaded [#94](https://github.com/plotly/dashR/pull/94)


## [0.0.7] - 2019-04-09
### Removed
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package
- `dependencies_set`, `dependencies_get`, and `dependencies_get_internal` methods removed from package


### [0.0.6] - 2019-04-08
Expand DownExpand Up@@ -107,9 +108,9 @@ All notable changes to this project will be documented in this file.

### [0.0.3] - 2019-03-08
### Added
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
- `assert_valid_callbacks` to validate callback handler definitions and ordering of `input` and `state` using
new `valid_seq` function

### Changed
- Callback method and handling refactored to match current Dash for Python API [#51](https://github.com/plotly/dashR/pull/51)
- Handler function for callbacks now passed via `func` argument to `app$callback()`
Expand Down
32 changes: 21 additions & 11 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -560,11 +560,27 @@ Dash <- R6::R6Class(
# if debug mode is not active
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$type <- get_mimetype(filename)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (grepl("text|javascript", response$type)) {
response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")

if (private$compress && length(response$body) > 0) {
response <- tryCompress(request, response)
}
} else {
file_handle <- file(dep_path, "rb")
file_size <- file.size(dep_path)

response$body <- readBin(dep_path,
raw(),
file_size)
close(file_handle)
}

if (!private$debug && has_fingerprint) {
response$status <- 200L
response$set_header('Cache-Control',
Expand All@@ -587,13 +603,8 @@ Dash <- R6::R6Class(
} else {
response$status <- 200L
}

response$type <- get_mimetype(filename)
}

if (private$compress && length(response$body) > 0)
response <- tryCompress(request, response)

TRUE
})

Expand DownExpand Up@@ -629,8 +640,7 @@ Dash <- R6::R6Class(
# and opens/closes a file handle if the type is assumed to be binary
if (!(is.null(asset_path)) && file.exists(asset_path)) {
response$type <- request$headers[["Content-Type"]] %||%
mime::guess_type(asset_to_match,
empty = "application/octet-stream")
get_mimetype(asset_to_match)

if (grepl("text|javascript", response$type)) {
response$body <- readLines(asset_path,
Expand Down
16 changes: 9 additions & 7 deletions R/utils.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -315,9 +315,9 @@ assert_no_names <- function (x)
# filtered out by the subsequent vapply statement
clean_dependencies <- function(deps) {
dep_list <- lapply(deps, function(x) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet)) | (is.null(x$package))) {
if (is.null(x$src$file) | (is.null(x$script) & is.null(x$stylesheet) & is.null(x$other)) | (is.null(x$package))) {
if (is.null(x$src$href))
stop(sprintf("Scriptor CSS dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
stop(sprintf("Script, CSS, or other dependencies with NULL href fields must include a file path, dependency name, and R package name."), call. = FALSE)
else
return(NULL)
}
Expand DownExpand Up@@ -504,7 +504,9 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
dep_path <- file.path(x$src$file, x$script)
else if (!is.null(x$stylesheet))
dep_path <- file.path(x$src$file, x$stylesheet)

else if (!is.null(x$other))
dep_path <- file.path(x$src$file, x$other)

# remove n>1 slashes and replace with / if present;
# htmltools seems to permit // in pathnames, but
# this complicates string matching unless they're
Expand All@@ -531,17 +533,17 @@ get_package_mapping <- function(script_name, url_package, dependencies) {
}

get_mimetype <- function(filename) {
# the tools package is available to all
filename_ext <- file_ext(filename)
filename_ext <- getFileExt(filename)

if (filename_ext == 'js')
return('application/JavaScript')
else if (filename_ext == 'css')
return('text/css')
else if (filename_ext == 'map')
else if (filename_ext %in% c('js.map', 'map'))
return('application/json')
else
return(NULL)
return(mime::guess_type(filename,
empty = "application/octet-stream"))
}

generate_css_dist_html <- function(href,
Expand Down
49 changes: 46 additions & 3 deletions tests/integration/test_generation.py
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
from selenium.webdriver.support.select import Select
import time, os

from selenium.webdriver.support.ui import WebDriverWait

app = """
library(dash)
Expand All@@ -17,9 +16,53 @@
app$run_server()
"""

styled_app = """
library(dash)
library(dashHtmlComponents)
library(dashGeneratorTestComponentStandard)

app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton(id='btn', list('Click')),
htmlDiv(id='container')
)))

app$callback(output(id = 'container', property = 'children'),
list(input(id = 'btn', property = 'n_clicks')),
function(n_clicks) {
if (is.null(unlist(n_clicks))) {
return(dashNoUpdate())
} else {
return(list(dgtc_standardMyStandardComponent(id="standard", value="Standard", style=list(fontFamily="godfather"))))
}
})

app$run_server()
"""


def test_gene001_simple_callback(dashr):
dashr.start_server(app)

assert dashr.wait_for_element("#standard").text == "Standard"
assert dashr.wait_for_element("#nested").text == "Nested"
assert dashr.wait_for_element("#nested").text == "Nested"

dashr.percy_snapshot("gene001-simple-callback")


def test_gene002_arbitrary_resources(dashr):
dashr.start_server(styled_app)

assert (
dashr.driver.execute_script("return document.fonts.check('1em godfather')")
is False
)

dashr.wait_for_element("#btn").click()
assert dashr.wait_for_element("#standard").text == "Standard"

WebDriverWait(dashr.driver, 10).until(
lambda _: dashr.driver.execute_script("return document.fonts.check('1em godfather')") is True,
)

dashr.percy_snapshot("gene002-arbitrary-resource")