Skip to content

Commit 5c919be

Browse files
authored
Merge pull request #1343 from ropensci/style-partial
add support for partial updates in style(), fixes #1342
2 parents 2bf7aa8 + 07e711b commit 5c919be

File tree

4 files changed

+145
-14
lines changed

4 files changed

+145
-14
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
* Upgraded to plotly.js v1.41.3.
1111
* The `orca()` function now supports conversion of much larger figures (#1322) and works without a mapbox api token (#1314).
12+
* The `style()` function now supports "partial updates" (i.e. modification of a particular property of an object, rather than the entire object). For example, notice how the first plot retains the original marker shape (a square): `p <- plot_ly(x = 1:10, y = 1:10, symbol = I(15)); subplot(style(p, marker.color = "red"), style(p, marker = list(color = "red")))` (#1342).
1213

1314
## BUG FIXES
1415

R/style.R

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,64 @@
1313
#' @export
1414
#' @examples
1515
#'
16-
#' p <- qplot(data = mtcars, wt, mpg, geom = c("point", "smooth"))
17-
#' # keep the hover info for points, but remove it for the line/ribbon
16+
#' # style() is especially useful in conjunction with ggplotly()
17+
#' # It allows you to leverage the underlying plotly.js library to change
18+
#' # the return result of ggplotly()
19+
#' (p <- ggplotly(qplot(data = mtcars, wt, mpg, geom = c("point", "smooth"))))
20+
#'
21+
#' # removes hoverinfo for the line/ribbon traces (use `plotly_json()` to verify!)
1822
#' style(p, hoverinfo = "none", traces = c(2, 3))
1923
#'
24+
#' # another example with plot_ly() instead of ggplotly()
25+
#' marker <- list(
26+
#' color = "red",
27+
#' line = list(
28+
#' width = 20,
29+
#' color = "black"
30+
#' )
31+
#' )
32+
#' (p <- plot_ly(x = 1:10, y = 1:10, marker = marker))
33+
#'
34+
#' # note how the entire (marker) object is replaced if a list is provided
35+
#' style(p, marker = list(line = list(color = "blue")))
36+
#'
37+
#' # similar to plotly.js, you can update a particular attribute like so
38+
#' # https://github.com/plotly/plotly.js/issues/1866#issuecomment-314115744
39+
#' style(p, marker.line.color = "blue")
40+
#' # this clobbers the previously supplied marker.line.color
41+
#' style(p, marker.line = list(width = 2.5), marker.size = 10)
42+
#'
2043
style <- function(p, ..., traces = NULL) {
2144
p <- plotly_build(p)
22-
nTraces <- length(p$x$data)
23-
traces <- traces %||% seq_len(nTraces)
24-
idx <- traces > nTraces
25-
traces <- traces[!idx]
26-
if (any(idx)) warning("You've referenced non-existent traces", call. = FALSE)
27-
argz <- list(...)
28-
for (i in traces) {
29-
for (j in names(argz)) {
30-
p$x$data[[i]][[j]] <- argz[[j]]
31-
}
45+
n_traces <- length(p$x$data)
46+
trace_idx <- traces %||% seq_len(n_traces)
47+
if (any(trace_idx > n_traces)) {
48+
warning("You've referenced non-existent traces", call. = FALSE)
3249
}
50+
51+
values <- list(...)
52+
paths <- strsplit(names(values), "\\.")
53+
54+
p$x$data[trace_idx] <- lapply(p$x$data[trace_idx], function(trace) {
55+
for (i in seq_along(paths)) {
56+
trace <- trace_replace(trace, paths[[i]], values[[i]])
57+
}
58+
trace
59+
})
60+
3361
p
3462
}
63+
64+
#' @param trace a single plotly trace
65+
#' @param path character vector of path elements pointing to a trace property: c("marker", "line", "size")
66+
#' @param value a value to assign to that trace property
67+
trace_replace <- function(trace, path, value) {
68+
if (length(path) == 0) return(trace)
69+
if (length(path) == 1) {
70+
trace[[path]] <- value
71+
return(trace)
72+
}
73+
trace[[path[1]]] <- trace[[path[1]]] %||% setNames(list(NULL), path[2])
74+
trace[[path[1]]] <- trace_replace(trace[[path[1]]], path[-1], value)
75+
trace
76+
}

man/style.Rd

Lines changed: 25 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-style.R

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
context("style/restyle functionality")
2+
3+
p1 <- plot_ly(x = 1:10, y = 1:10, symbol = I(15))
4+
marker1 <- plotly_build(p1)$x$data[[1]]$marker
5+
6+
test_that("Whole update works as expected", {
7+
p2 <- style(p1, marker = list(color = "red"))
8+
marker2 <- plotly_build(p2)$x$data[[1]]$marker
9+
expect_equal(marker2, list(color = "red"))
10+
11+
p3 <- style(p1, marker = list(line = list(color = "red", width = 10)))
12+
marker3 <- plotly_build(p3)$x$data[[1]]$marker
13+
expect_equal(marker3, list(line = list(color = "red", width = 10)))
14+
})
15+
16+
17+
test_that("Partial update works as expected", {
18+
p4 <- style(p1, marker.color = "red")
19+
marker4 <- plotly_build(p4)$x$data[[1]]$marker
20+
expect_equal(marker4, modifyList(marker4, list(color = "red")))
21+
22+
p5 <- style(p1, marker.line.color = "red")
23+
marker5 <- plotly_build(p5)$x$data[[1]]$marker
24+
expect_equal(marker5, modifyList(marker5, list(line = list(color = "red"))))
25+
})
26+
27+
test_that("Partial update works as expected", {
28+
p4 <- style(p1, marker.color = "red")
29+
marker4 <- plotly_build(p4)$x$data[[1]]$marker
30+
expect_equal(marker4, modifyList(marker4, list(color = "red")))
31+
32+
p5 <- style(p1, marker.line.color = "red")
33+
marker5 <- plotly_build(p5)$x$data[[1]]$marker
34+
expect_equal(marker5, modifyList(marker5, list(line = list(color = "red"))))
35+
})
36+
37+
38+
test_that("Partial update works as expected", {
39+
trace <- list(
40+
x = 1:5,
41+
y = 6:10,
42+
marker = list(line = list(color = "red", width = 20))
43+
)
44+
45+
trace_new <- trace_replace(trace, c("marker", "line"), list(width = 10))
46+
trace$marker$line <- list(width = 10)
47+
expect_equal(trace_new, trace)
48+
49+
trace <- list(
50+
x = 1:5,
51+
y = 6:10,
52+
marker = list(line = list(color = "red", width = 20))
53+
)
54+
trace_new <- trace_replace(trace, c("marker", "line", "width"), 10)
55+
trace$marker$line$width <- 10
56+
expect_equal(trace_new, trace)
57+
58+
trace <- list(
59+
x = 1:5,
60+
y = 6:10
61+
)
62+
trace_new <- trace_replace(trace, c("marker", "line", "width"), 10)
63+
trace$marker$line$width <- 10
64+
expect_equal(trace_new, trace)
65+
})

0 commit comments

Comments
 (0)