Skip to content

Commit 879a118

Browse files
committed
Merge branch 'master' of https://github.com/ropensci/plotly into feature/hovermode
2 parents 679f990 + 24d3c5c commit 879a118

24 files changed

+313
-151
lines changed

CONTRIBUTING.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Contributing to plotly.js
2+
3+
## Opening issues
4+
5+
Search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/ropensci/plotly/issues/new)
6+
7+
Bug reports __must__ have a [reproducible example](http://adv-r.had.co.nz/Reproducibility.html) and include the output of `devtools::session_info()` (instead of `sessionInfo()`).
8+
9+
GitHub issues are great for reporting bugs and requesting new features, but implementation questions are better suited for Stack Overflow (tagged
10+
[`plotly`](https://stackoverflow.com/questions/tagged/plotly)) or on
11+
community.plot.ly (tagged [`plotly-js`](http://community.plot.ly/c/plotly-js)).
12+
13+
## Development guidelines
14+
15+
If you'd like to contribute changes to plotly, we use [the GitHub flow](https://guides.github.com/introduction/flow/index.html) for proposing, submitting, reviewing, and accepting changes. If you haven't done this before, Hadley Wickham provides a nice overview of git (<http://r-pkgs.had.co.nz/git.html>), as well as best practices for submitting pull requests (<http://r-pkgs.had.co.nz/git.html#pr-make>). We also recommend using his style guide when writing code (<http://adv-r.had.co.nz/Style.html>).
16+
17+
If your pull request fixes a bug, or implements a new feature, it's a good idea to write a test (<http://r-pkgs.had.co.nz/tests.html>) to demonstrate it's working. If you'd like to closely simulate the tests that run when you submit your pull request, open R under your local plotly git repo, then do the following:
18+
19+
```r
20+
# the pull request number is arbitrary when running locally
21+
Sys.setenv('TRAVIS_PULL_REQUEST' = '1')
22+
Sys.setenv('TRAVIS_COMMIT' = substr(system('git rev-parse HEAD', intern = T), 1, 7))
23+
devtools::load_all(); source('tests/testthat.R', chdir = TRUE)
24+
```
25+
26+
You can also build a ggplot2/plotly comparison table:
27+
28+
```r
29+
Sys.setenv('PLOTLY_TEST' = 'TRUE')
30+
devtools::load_all(); source('tests/testthat.R', chdir = TRUE)
31+
```
32+
33+
## Code of Conduct
34+
35+
We want to encourage a warm, welcoming, and safe environment for contributing to this project. See the [code of conduct](https://github.com/ropensci/plotly/blob/master/CONDUCT.md) for more information.

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: plotly
22
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
3-
Version: 2.0.18
3+
Version: 2.1.3
44
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
55
email = "[email protected]"),
66
person("Chris", "Parmer", role = c("aut", "cph"),

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ S3method(print,figure)
44
S3method(print,plotly)
55
export("%>%")
66
export(add_trace)
7+
export(as.widget)
78
export(config)
89
export(embed_notebook)
910
export(get_figure)

NEWS

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
2.1.3 -- 12 Jan 2015
2+
3+
Fix size and alpha translation for geom_point. Fixes #386
4+
5+
2.1.2 -- 11 Jan 2015
6+
7+
Upgraded to plotlyjs 1.4.1. For a list of changes, see https://github.com/plotly/plotly.js/releases/tag/v1.4.1
8+
9+
2.1.1 -- 11 Jan 2015
10+
11+
Upgraded to plotlyjs 1.4. For a list of changes, see https://github.com/plotly/plotly.js/releases/tag/v1.4.0
12+
13+
2.1.0 -- 29 Dec 2015
14+
15+
plot_ly() now defaults to inherit=FALSE and plotly_build() is now idempotent. Fixes #280 and #277. See #368 for details.
16+
17+
2.0.19 -- 23 Dec 2015
18+
19+
Added as.widget() function for conveniency in converting plotly object to htmlwidget objects. See #294.
20+
121
2.0.18 -- 22 Dec 2015
222

323
Fix #365

R/plotly.R

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
#'
6666
plot_ly <- function(data = data.frame(), ..., type = "scatter",
6767
group, color, colors, symbol, symbols, size,
68-
width = NULL, height = NULL, inherit = TRUE,
68+
width = NULL, height = NULL, inherit = FALSE,
6969
evaluate = FALSE) {
7070
# "native" plotly arguments
7171
argz <- substitute(list(...))
@@ -170,7 +170,7 @@ layout <- function(p = last_plot(), ...,
170170
enclos = parent.frame()
171171
)
172172
p <- last_plot(p)
173-
p$layout <- c(p$layout, list(layout))
173+
p$layout <- c(p$layout, list(layout = layout))
174174
if (evaluate) p <- plotly_build(p)
175175
hash_plot(data, p)
176176
}
@@ -265,9 +265,6 @@ plotly_build <- function(l = last_plot()) {
265265
# ggplot objects don't need any special type of handling
266266
if (is.ggplot(l)) return(gg2list(l))
267267
l <- get_plot(l)
268-
# plots without NSE don't need it either
269-
nmz <- c(lapply(l$data, names), lapply(l$layout, names), lapply(l$style, names))
270-
if (!all(c("args", "env") %in% unlist(nmz))) return(structure(l, class = unique("plotly", class(l))))
271268
# assume unnamed list elements are data/traces
272269
nms <- names(l)
273270
idx <- nms %in% ""
@@ -276,14 +273,22 @@ plotly_build <- function(l = last_plot()) {
276273
} else if (any(idx)) {
277274
c(data = c(l$data, l[idx]), l[!idx])
278275
} else l
279-
dats <- list()
276+
# carry over properties, if necessary (but don't carry over evaluation envir)
277+
if (length(l$data) > 1 && isTRUE(l$data[[1]]$inherit)) {
278+
d <- l$data[[1]]
279+
d <- d[!names(d) %in% c("env", "enclos")]
280+
for (i in seq.int(2, length(l$data))) {
281+
l$data[[i]] <- modifyList(l$data[[i]], d)
282+
}
283+
}
284+
# 'x' is the same as 'l', but with arguments evaluated
285+
# this is ugly, but I think it is necessary, since we don't know how many
286+
# traces we have until we evaluate args and call traceify() (or similar)
287+
x <- list()
280288
for (i in seq_along(l$data)) {
281289
d <- l$data[[i]]
282-
# if appropriate, evaluate trace arguments in a suitable environment
283-
idx <- names(d) %in% c("args", "env", "enclos")
284-
if (sum(idx) == 3) {
285-
dat <- c(d[!idx], eval(d$args, as.list(d$env, all.names = TRUE), d$enclos))
286-
dat[c("args", "env", "enclos")] <- NULL
290+
if (should_eval(d)) {
291+
dat <- do_eval(d)
287292
# start processing specially named arguments
288293
s <- dat[["size"]]
289294
if (!is.null(s)) {
@@ -309,49 +314,30 @@ plotly_build <- function(l = last_plot()) {
309314
has_group <- !is.null(dat[["group"]])
310315
if (has_color) {
311316
title <- as.list(d$args)[["color"]] %||% as.list(d$args)[["z"]] %||% ""
312-
dats <- c(dats, colorize(dat, title))
317+
x$data <- c(x$data, colorize(dat, title))
313318
}
314319
# TODO: add a legend title (is this only possible via annotations?!?)
315-
if (has_symbol) dats <- c(dats, symbolize(dat))
316-
if (has_group) dats <- c(dats, traceify(dat, "group"))
317-
if (!has_color && !has_symbol && !has_group) dats <- c(dats, list(dat))
320+
if (has_symbol) x$data <- c(x$data, symbolize(dat))
321+
if (has_group) x$data <- c(x$data, traceify(dat, "group"))
322+
if (!has_color && !has_symbol && !has_group) x$data <- c(x$data, list(dat))
318323
} else {
319-
dats <- c(dats, list(d))
320-
}
321-
}
322-
x <- list(data = dats)
323-
# carry over properties/data from first trace (if appropriate)
324-
if (length(x$data) > 1 && isTRUE(l$data[[1]]$inherit)) {
325-
for (i in seq.int(2, length(x$data))) {
326-
x$data[[i]] <- modifyList(x$data[[1]], x$data[[i]])
324+
x$data <- c(x$data, list(d))
327325
}
328326
}
329-
# layout() tacks on an unnamed list element to potentially pre-existing
330-
# layout(s). Note that ggplotly() will return a named list
331-
# of length n >= 1 (so we need to carefully merge them ).
327+
# it's possible have nested layouts (e.g., plot_ly() %>% layout() %>% layout())
332328
nms <- names(l$layout)
333-
if (!is.null(nms) && any(idx <- nms %in% "")) {
334-
# TODO: does this always preserve the correct order to layouts?
335-
# (important since we use modifyList at a later point)
336-
l$layout <- c(list(l$layout[!idx]), l$layout[idx])
337-
}
329+
idx <- nms %in% "layout"
330+
l$layout <- c(list(l$layout[!idx]), setNames(l$layout[idx], NULL))
338331
for (i in seq_along(l$layout)) {
339-
layout <- l$layout[[i]]
340-
idx <- names(layout) %in% c("args", "env", "enclos")
341-
x$layout[[i]] <- if (sum(idx) == 3) {
342-
c(layout[!idx], eval(layout$args, as.list(layout$env, all.names = TRUE), layout$enclos))
343-
} else {
344-
layout
345-
}
332+
x$layout[[i]] <- perform_eval(l$layout[[i]])
346333
}
347334
x$layout <- Reduce(modifyList, x$layout)
348335
# if style is not null, use it to modify existing traces
349336
if (!is.null(l$style)) {
350337
for (i in seq_along(l$style)) {
351-
sty <- l$style[[i]]
352-
idx <- names(sty) %in% c("args", "env", "enclos")
353-
new_sty <- if (sum(idx) == 3) c(sty[!idx], eval(sty$args, as.list(sty$env, all.names = TRUE), sty$enclos)) else sty
354-
for (k in sty$traces) x$data[[k]] <- modifyList(x$data[[k]], new_sty)
338+
sty <- perform_eval(l$style[[i]])
339+
for (k in l$style[[i]]$traces)
340+
x$data[[k]] <- modifyList(x$data[[k]], sty)
355341
}
356342
}
357343
# add appropriate axis title (if they don't already exist)
@@ -365,26 +351,6 @@ plotly_build <- function(l = last_plot()) {
365351
if (!is.null(a) && !is.null(names(a))) {
366352
x$layout$annotations <- list(x$layout$annotations)
367353
}
368-
# search for keyword args in traces and place them at the top level
369-
kwargs <- lapply(x$data, function(z) z[get_kwargs()])
370-
# 'top-level' keywords args take precedence
371-
kwargs <- Reduce(modifyList, c(kwargs, list(x[get_kwargs()])))
372-
# empty keyword arguments can cause problems
373-
kwargs <- kwargs[sapply(kwargs, length) > 0]
374-
# try our damndest to assign a sensible filename
375-
if (is.null(kwargs$filename)) {
376-
kwargs$filename <-
377-
as.character(kwargs$layout$title) %||%
378-
paste(
379-
c(kwargs$layout$xaxis$title,
380-
kwargs$layout$yaxis$title,
381-
kwargs$layout$zaxis$title),
382-
collapse = " vs. "
383-
) %||%
384-
"plot from api"
385-
}
386-
# tack on keyword arguments
387-
x <- c(x, kwargs)
388354
# traces shouldn't have any names
389355
x$data <- setNames(x$data, NULL)
390356
# if this is a non-line scatter trace and no hovermode exists,

R/plotly_POST.R

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@
2626

2727
plotly_POST <- function(x, filename, fileopt = "new", world_readable = TRUE) {
2828
x <- plotly_build(x)
29-
if (!missing(filename)) x$filename <- filename
29+
x$filename <- if (!missing(filename)) {
30+
filename
31+
} else {
32+
# try our damndest to assign a sensible filename
33+
x$filename %||% as.character(x$layout$title) %||%
34+
paste(c(x$layout$xaxis$title, x$layout$yaxis$title, x$layout$zaxis$title),
35+
collapse = " vs. ") %||% "plot from api"
36+
}
3037
if (!is.null(x$fileopt))
3138
warning("fileopt was specified in the wrong place. Please specify in plotly_POST()")
3239
x$fileopt <- fileopt

R/print.R

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,37 @@
11
#' Print a plotly object
22
#'
33
#' @param x a plotly object
4-
#' @param ... additional arguments (currently ignored)
4+
#' @param ... additional arguments
55
#' @export
66
#' @importFrom htmlwidgets createWidget
77
#' @importFrom htmlwidgets sizingPolicy
88
print.plotly <- function(x, ...) {
9-
w <- toWidget(x)
10-
get("print.htmlwidget", envir = asNamespace("htmlwidgets"))(w)
9+
if (!inherits(x, "htmlwidget")) x <- as.widget(x)
10+
get("print.htmlwidget", envir = asNamespace("htmlwidgets"))(x, ...)
1111
}
1212

1313
#' Print a plotly object in a knitr doc
1414
#'
1515
#' @param x a plotly object
1616
#' @param options knitr options.
17-
#' @param ... additional arguments (currently ignored)
17+
#' @param ... additional arguments
1818
#' @export
1919
knit_print.plotly <- function(x, options, ...) {
20-
w <- toWidget(x)
21-
get("knit_print.htmlwidget", envir = asNamespace("htmlwidgets"))(w, options = options)
20+
if (!inherits(x, "htmlwidget")) x <- as.widget(x)
21+
get("knit_print.htmlwidget", envir = asNamespace("htmlwidgets"))(x, options = options, ...)
2222
}
2323

2424
#' Convert a plotly object to an htmlwidget object
2525
#'
26-
#' Users shouldn't need to use this function. It's exported for internal reasons.
27-
#'
2826
#' @param x a plotly object.
27+
#' @param ... other options passed onto \code{htmlwidgets::createWidget}
28+
#' @export
29+
#' @examples \dontrun{
30+
#' p <- plot_ly(mtcars, x = mpg, y = disp, mode = "markers")
31+
#' htmlwidgets::saveWidget(as.widget(p), "index.html")
32+
#' }
2933
#'
30-
toWidget <- function(x) {
34+
as.widget <- function(x, ...) {
3135
p <- plotly_build(x)
3236
# set some margin defaults if none are provided
3337
p$layout$margin <- modifyList(
@@ -41,13 +45,17 @@ toWidget <- function(x) {
4145
x = p,
4246
width = p$width,
4347
height = p$height,
44-
htmlwidgets::sizingPolicy(
48+
sizingPolicy = htmlwidgets::sizingPolicy(
4549
padding = 5,
4650
browser.fill = TRUE
47-
)
51+
),
52+
...
4853
)
4954
}
5055

56+
# for legacy reasons
57+
toWidget <- as.widget
58+
5159
#' Print a plotly figure object
5260
#'
5361
#' @param x a plotly figure object

R/process.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ process.figure <- function(resp) {
2828
fig <- con$payload$figure
2929
fig$url <- sub("apigetfile/", "~", resp$url)
3030
fig <- add_boxed(fig)
31+
fig$data[[1]]$inherit <- FALSE
3132
# any reasonable way to return a data frame?
3233
hash_plot(data.frame(), fig)
3334
}

R/shiny.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ plotlyOutput <- function(outputId, width = "100%", height = "400px") {
2626
renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
2727
if (!quoted) { expr <- substitute(expr) } # force quoted
2828
# https://github.com/ramnathv/htmlwidgets/issues/166#issuecomment-153000306
29-
expr <- as.call(list(call(":::", quote("plotly"), quote("toWidget")), expr))
29+
expr <- call("as.widget", expr)
3030
shinyRenderWidget(expr, plotlyOutput, env, quoted = TRUE)
3131
}

R/trace_generation.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,10 @@ geom2trace <- list(
590590
)
591591
},
592592
point=function(data, params){
593+
# params contains unique values, but we need _all_ values from the data
594+
for (i in names(params)) {
595+
if (length(params[[i]]) > 1) params[[i]] <- data[[i]]
596+
}
593597
L <- list(
594598
x = data$x,
595599
y = data$y,

R/utils.R

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,25 @@ plotly_headers <- function(type = "main") {
190190
httr::add_headers(.headers = h)
191191
}
192192

193+
194+
perform_eval <- function(x) {
195+
if (should_eval(x)) do_eval(x) else x
196+
}
197+
198+
# env/enclos are special properties specific to the R API
199+
# if they appear _and_ are environments, then evaluate arguments
200+
# (sometimes figures return these properties but evaluation doesn't make sense)
201+
should_eval <- function(x) {
202+
any(vapply(x[c("env", "enclos")], is.environment, logical(1)))
203+
}
204+
205+
# perform evaluation of arguments, keeping other list elements
206+
do_eval <- function(x) {
207+
y <- c(x, eval(x$args, as.list(x$env, all.names = TRUE), x$enclos))
208+
y[c("args", "env", "enclos")] <- NULL
209+
y
210+
}
211+
193212
# try to write environment variables to an .Rprofile
194213
cat_profile <- function(key, value, path = "~") {
195214
r_profile <- file.path(normalizePath(path, mustWork = TRUE),

README.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ devtools::install_github("ropensci/plotly")
2020

2121
## Introduction
2222

23-
If you use [ggplot2](http://cran.r-project.org/package=ggplot2), simply call `ggplotly()` to convert your ggplot to an interactive, web-based version!
23+
If you use [ggplot2](http://cran.r-project.org/package=ggplot2), use `ggplotly()` to convert your ggplot to an interactive, web-based version!
2424

2525
```r
2626
library(plotly)
@@ -50,19 +50,15 @@ The `ggplotly()` function converts a ggplot object to a plotly object, so if you
5050
layout(gg, hovermode = "closest")
5151
```
5252

53-
## Learn more
53+
## Documentation
5454

55-
* [An overview of plotly's R API](https://cdn.rawgit.com/ropensci/plotly/master/vignettes/intro.html)
56-
* Peruse the examples on plotly's [R homepage](https://plot.ly/r) and [ggplot2 homepage](https://plot.ly/ggplot2)
55+
* [An introduction to plotly's R API](https://cran.r-project.org/web/packages/plotly/vignettes/intro.html)
56+
* Examples and vignettes on plotly's R homepage - <https://plot.ly/r>
57+
* The complete figure reference guide - <https://plot.ly/r/reference>
5758

5859
## Contributing
5960

60-
- We love collaboration! See the [wiki](https://github.com/ropensci/plotly/wiki/Development-guidelines) and the [code of conduct](https://github.com/ropensci/plotly/blob/master/CONDUCT.md) for more information.
61-
62-
## Stay in touch
63-
64-
65-
- [@plotlygraphs](https://twitter.com/plotlygraphs)
61+
Please read through our [contributing guidelines](https://github.com/ropensci/plotly/blob/master/CONTRIBUTING.md). Included are directions for opening issues, asking questions, contributing changes to plotly, and our code of conduct.
6662

6763
---
6864

0 commit comments

Comments
 (0)