Skip to content

Commit 23efe22

Browse files
committed
Merge branch 'master' into fix-ggplotly-medium-issues
2 parents 0dfe79d + 386506d commit 23efe22

File tree

292 files changed

+527
-394
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

292 files changed

+527
-394
lines changed

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ See the [plotly.js releases page](https://github.com/plotly/plotly.js/releases)
3030
* `ggplotly()` now correctly uses custom legend labels from `scale_*_manual(labels = ...)`. (#2420)
3131
* `ggplotly()` with `dynamicTicks = TRUE` no longer errors on grouped `geom_line()` plots. (#2462)
3232
* `plot_ly()` with color mapping no longer resets Date/POSIXct x-axis values to 1970. (#2446)
33+
* Closed #2415: `ggplotly()` now shows variables named 'group' in tooltips when mapped to aesthetics like `colour`.
34+
* Closed #2455, #2460: `ggplotly()` no longer creates empty shapes when `panel.border` is `element_blank()` (ggplot2 4.0.0 compatibility).
35+
* Closed #2466: `ggplotly()` no longer errors when `scale_*_manual()` has unused aesthetics (e.g., `aesthetics = c("colour", "fill")` when only colour is used).
36+
* Closed #2305: `ggplotly()` now respects `geom_boxplot(outlier.shape = NA)` to hide outlier points.
37+
* Closed #2467: `ggplotly()` now correctly shows legends and splits traces when scales have multiple aesthetics.
38+
* Closed #2407, #2187: `ggplotly()` now translates `legend.position` theme element to plotly layout (supports "bottom", "top", "left", and numeric positions).
39+
* Closed #2281: `ggplotly()` no longer drops legends when `geom_blank()` is present in the plot.
3340

3441
# plotly 4.11.0
3542

R/ggplotly.R

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,9 @@ gg2list <- function(p, width = NULL, height = NULL,
411411
# of each non-positional scale for display in tooltips
412412
for (sc in npscales$scales) {
413413
data <- lapply(data, function(d) {
414-
# scale may not be relevant for every layer data
415-
if (any(names(d) %in% sc$aesthetics)) {
416-
d[paste0(sc$aesthetics, "_plotlyDomain")] <- d[sc$aesthetics]
414+
present_aes <- intersect(sc$aesthetics, names(d))
415+
if (length(present_aes) > 0) {
416+
d[paste0(present_aes, "_plotlyDomain")] <- d[present_aes]
417417
}
418418
d
419419
})
@@ -572,13 +572,15 @@ gg2list <- function(p, width = NULL, height = NULL,
572572
tr$hoverinfo <- tr$hoverinfo %||%"text"
573573
tr
574574
})
575-
# show only one legend entry per legendgroup
575+
# show only one legend entry per legendgroup (skip invisible traces for dedup)
576576
grps <- sapply(traces, "[[", "legendgroup")
577+
is_visible <- sapply(traces, function(tr) !isFALSE(tr$visible))
578+
grps_for_dedup <- ifelse(is_visible, grps, paste0(grps, "_invisible_", seq_along(grps)))
577579
traces <- Map(function(x, y) {
578580
if (!is.null(x[["frame"]])) return(x)
579581
x$showlegend <- isTRUE(x$showlegend) && y
580582
x
581-
}, traces, !duplicated(grps))
583+
}, traces, !duplicated(grps_for_dedup))
582584

583585
# ------------------------------------------------------------------------
584586
# axis/facet/margin conversion
@@ -980,12 +982,45 @@ gg2list <- function(p, width = NULL, height = NULL,
980982
bgcolor = toRGB(theme$legend.background$fill),
981983
bordercolor = toRGB(theme$legend.background$colour),
982984
borderwidth = unitConvert(
983-
theme$legend.background[[linewidth_or_size(theme$legend.background)]],
985+
theme$legend.background[[linewidth_or_size(theme$legend.background)]],
984986
"pixels", "width"
985987
),
986988
font = text2font(theme$legend.text)
987989
)
988-
990+
991+
# Translate legend.position to plotly layout
992+
legend_pos <- theme$legend.position %||% theme[["legend.position"]]
993+
if (!is.null(legend_pos) && !identical(legend_pos, "none")) {
994+
if (is.character(legend_pos)) {
995+
gglayout$legend <- switch(legend_pos,
996+
"bottom" = modifyList(gglayout$legend, list(
997+
orientation = "h", x = 0.5, y = -0.15, xanchor = "center", yanchor = "top"
998+
)),
999+
"top" = modifyList(gglayout$legend, list(
1000+
orientation = "h", x = 0.5, y = 1.02, xanchor = "center", yanchor = "bottom"
1001+
)),
1002+
"left" = modifyList(gglayout$legend, list(
1003+
x = -0.15, y = 0.5, xanchor = "right", yanchor = "middle"
1004+
)),
1005+
"inside" = {
1006+
inside_pos <- theme$legend.position.inside %||% theme[["legend.position.inside"]]
1007+
if (is.numeric(inside_pos) && length(inside_pos) == 2) {
1008+
modifyList(gglayout$legend, list(
1009+
x = inside_pos[1], y = inside_pos[2], xanchor = "left", yanchor = "bottom"
1010+
))
1011+
} else {
1012+
gglayout$legend
1013+
}
1014+
},
1015+
gglayout$legend
1016+
)
1017+
} else if (is.numeric(legend_pos) && length(legend_pos) == 2) {
1018+
gglayout$legend <- modifyList(gglayout$legend, list(
1019+
x = legend_pos[1], y = legend_pos[2], xanchor = "left", yanchor = "bottom"
1020+
))
1021+
}
1022+
}
1023+
9891024
# if theme(legend.position = "none") is used, don't show a legend _or_ guide
9901025
if (npscales$n() == 0 || identical(theme$legend.position, "none")) {
9911026
gglayout$showlegend <- FALSE
@@ -1391,7 +1426,12 @@ make_strip_rect <- function(xdom, ydom, theme, side = "top") {
13911426

13921427
# theme(panel.border) -> plotly.js rect shape
13931428
make_panel_border <- function(xdom, ydom, theme) {
1394-
rekt <- rect2shape(theme[["panel.border"]])
1429+
# Use calc_element to get fully resolved element with inherited values
1430+
border <- ggplot2::calc_element("panel.border", theme)
1431+
if (is.null(border) || is_blank(border)) {
1432+
return(list())
1433+
}
1434+
rekt <- rect2shape(border)
13951435
rekt$x0 <- xdom[1]
13961436
rekt$x1 <- xdom[2]
13971437
rekt$y0 <- ydom[1]

R/layers2traces.R

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ layers2traces <- function(data, prestats_data, layout, p) {
5656
if (!aesName %in% names(x)) next
5757
# TODO: should we be getting the name from scale_*(name) first?
5858
varName <- y[[i]]
59-
# "automatically" generated group aes is not informative
60-
if (identical("group", unique(varName, aesName))) next
59+
# Skip auto-generated group aesthetic, but keep explicit group mappings
60+
if (identical(aesName, "group") && identical(varName, "group")) next
6161
# add a line break if hovertext already exists
6262
if ("hovertext" %in% names(x)) x$hovertext <- paste0(x$hovertext, br())
6363
# text aestheic should be taken verbatim (for custom tooltips)
@@ -75,12 +75,13 @@ layers2traces <- function(data, prestats_data, layout, p) {
7575
x
7676
}, data, hoverTextAes)
7777

78-
# draw legends only for discrete scales
78+
# draw legends only for discrete scales (skip scales with guide = "none")
7979
discreteScales <- list()
8080
for (sc in p$scales$non_position_scales()$scales) {
81-
if (sc$is_discrete()) {
82-
nm <- paste(sc$aesthetics, collapse = "_")
83-
discreteScales[[nm]] <- sc
81+
if (sc$is_discrete() && !identical(sc$guide, "none")) {
82+
for (aes_name in sc$aesthetics) {
83+
discreteScales[[aes_name]] <- sc
84+
}
8485
}
8586
}
8687
# Convert "high-level" geoms to their "low-level" counterpart
@@ -726,7 +727,7 @@ geom2trace <- function(data, params, p) {
726727

727728
#' @export
728729
geom2trace.GeomBlank <- function(data, params, p) {
729-
list(visible = FALSE)
730+
list(visible = FALSE, showlegend = FALSE)
730731
}
731732

732733
#' @export
@@ -890,6 +891,8 @@ geom2trace.GeomBoxplot <- function(data, params, p) {
890891
# marker styling must inherit from GeomPoint$default_aes
891892
# https://github.com/hadley/ggplot2/blob/ab42c2ca81458b0cf78e3ba47ed5db21f4d0fc30/NEWS#L73-L7
892893
point_defaults <- GeomPoint$use_defaults(NULL)
894+
hide_outliers <- isFALSE(params$outliers) || isTRUE(is.na(params$outlier_gp$shape))
895+
893896
compact(list(
894897
x = data[["x"]],
895898
y = data[["y"]],
@@ -903,6 +906,7 @@ geom2trace.GeomBoxplot <- function(data, params, p) {
903906
aes2plotly(data, params, "fill"),
904907
aes2plotly(data, params, "alpha")
905908
),
909+
boxpoints = if (hide_outliers) FALSE,
906910
# markers/points
907911
marker = list(
908912
opacity = point_defaults$alpha,
@@ -1177,7 +1181,8 @@ linewidth_or_size.Geom <- function(x) {
11771181

11781182
#' @export
11791183
linewidth_or_size.element <- function(x) {
1180-
if ("linewidth" %in% names(x)) "linewidth" else "size"
1184+
# S7 objects (ggplot2 >= 4.0) don't have traditional names(), check for slot
1185+
if (!is.null(x$linewidth) || "linewidth" %in% names(x)) "linewidth" else "size"
11811186
}
11821187

11831188
#' @export

inst/examples/shiny/event_data/tests/testthat/_snaps/shinytest2/001.json

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@
239239
"ticks": "outside",
240240
"tickcolor": "rgba(51,51,51,1)",
241241
"ticklen": 3.6529680365296811,
242-
"tickwidth": 0,
242+
"tickwidth": 0.66417600664176002,
243243
"showticklabels": true,
244244
"tickfont": {
245245
"color": "rgba(77,77,77,1)",
@@ -252,7 +252,7 @@
252252
"linewidth": 0,
253253
"showgrid": true,
254254
"gridcolor": "rgba(255,255,255,1)",
255-
"gridwidth": 0,
255+
"gridwidth": 0.66417600664176002,
256256
"zeroline": false,
257257
"anchor": "y",
258258
"title": {
@@ -301,7 +301,7 @@
301301
"ticks": "outside",
302302
"tickcolor": "rgba(51,51,51,1)",
303303
"ticklen": 3.6529680365296811,
304-
"tickwidth": 0,
304+
"tickwidth": 0.66417600664176002,
305305
"showticklabels": true,
306306
"tickfont": {
307307
"color": "rgba(77,77,77,1)",
@@ -314,7 +314,7 @@
314314
"linewidth": 0,
315315
"showgrid": true,
316316
"gridcolor": "rgba(255,255,255,1)",
317-
"gridwidth": 0,
317+
"gridwidth": 0.66417600664176002,
318318
"zeroline": false,
319319
"anchor": "x",
320320
"title": {
@@ -328,30 +328,13 @@
328328
"hoverformat": ".2f"
329329
},
330330
"shapes": [
331-
{
332-
"type": "rect",
333-
"fillcolor": null,
334-
"line": {
335-
"color": null,
336-
"width": 0,
337-
"linetype": [
338331

339-
]
340-
},
341-
"yref": "paper",
342-
"xref": "paper",
343-
"layer": "below",
344-
"x0": 0,
345-
"x1": 1,
346-
"y0": 0,
347-
"y1": 1
348-
}
349332
],
350333
"showlegend": false,
351334
"legend": {
352335
"bgcolor": "rgba(255,255,255,1)",
353336
"bordercolor": "transparent",
354-
"borderwidth": 0,
337+
"borderwidth": 1.8897637795275593,
355338
"font": {
356339
"color": "rgba(0,0,0,1)",
357340
"family": "",

inst/examples/shiny/event_data/tests/testthat/_snaps/shinytest2/002.json

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"plotly_afterplot-A": "\"plot\"",
1919
"plotly_click-A": "[{\"curveNumber\":0,\"pointNumber\":7,\"x\":24.4,\"y\":3.19,\"customdata\":\"Merc 240D\"}]",
2020
"plotly_hover-A": null,
21-
"plotly_relayout-A": "{\"width\":962,\"height\":400}"
21+
"plotly_relayout-A": "{\"width\":947,\"height\":400}"
2222
},
2323
"output": {
2424
"brushed": "[1] \"Brush extents appear here (double-click to clear)\"",
@@ -242,7 +242,7 @@
242242
"ticks": "outside",
243243
"tickcolor": "rgba(51,51,51,1)",
244244
"ticklen": 3.6529680365296811,
245-
"tickwidth": 0,
245+
"tickwidth": 0.66417600664176002,
246246
"showticklabels": true,
247247
"tickfont": {
248248
"color": "rgba(77,77,77,1)",
@@ -255,7 +255,7 @@
255255
"linewidth": 0,
256256
"showgrid": true,
257257
"gridcolor": "rgba(255,255,255,1)",
258-
"gridwidth": 0,
258+
"gridwidth": 0.66417600664176002,
259259
"zeroline": false,
260260
"anchor": "y",
261261
"title": {
@@ -304,7 +304,7 @@
304304
"ticks": "outside",
305305
"tickcolor": "rgba(51,51,51,1)",
306306
"ticklen": 3.6529680365296811,
307-
"tickwidth": 0,
307+
"tickwidth": 0.66417600664176002,
308308
"showticklabels": true,
309309
"tickfont": {
310310
"color": "rgba(77,77,77,1)",
@@ -317,7 +317,7 @@
317317
"linewidth": 0,
318318
"showgrid": true,
319319
"gridcolor": "rgba(255,255,255,1)",
320-
"gridwidth": 0,
320+
"gridwidth": 0.66417600664176002,
321321
"zeroline": false,
322322
"anchor": "x",
323323
"title": {
@@ -331,30 +331,13 @@
331331
"hoverformat": ".2f"
332332
},
333333
"shapes": [
334-
{
335-
"type": "rect",
336-
"fillcolor": null,
337-
"line": {
338-
"color": null,
339-
"width": 0,
340-
"linetype": [
341334

342-
]
343-
},
344-
"yref": "paper",
345-
"xref": "paper",
346-
"layer": "below",
347-
"x0": 0,
348-
"x1": 1,
349-
"y0": 0,
350-
"y1": 1
351-
}
352335
],
353336
"showlegend": false,
354337
"legend": {
355338
"bgcolor": "rgba(255,255,255,1)",
356339
"bordercolor": "transparent",
357-
"borderwidth": 0,
340+
"borderwidth": 1.8897637795275593,
358341
"font": {
359342
"color": "rgba(0,0,0,1)",
360343
"family": "",

inst/examples/shiny/event_data/tests/testthat/_snaps/shinytest2/003.json

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"plotly_brushing-A": "{\"x\":[23.95978500551268,25.98332414553473],\"y\":[3.0020072289156627,3.5073743975903615]}",
2121
"plotly_click-A": "[{\"curveNumber\":0,\"pointNumber\":7,\"x\":24.4,\"y\":3.19,\"customdata\":\"Merc 240D\"}]",
2222
"plotly_hover-A": null,
23-
"plotly_relayout-A": "{\"width\":962,\"height\":400}",
23+
"plotly_relayout-A": "{\"width\":947,\"height\":400}",
2424
"plotly_selected-A": "[{\"curveNumber\":0,\"pointNumber\":7,\"x\":24.4,\"y\":3.19,\"customdata\":\"Merc 240D\"}]",
2525
"plotly_selecting-A": "[{\"curveNumber\":0,\"pointNumber\":7,\"x\":24.4,\"y\":3.19,\"customdata\":\"Merc 240D\"}]"
2626
},
@@ -246,7 +246,7 @@
246246
"ticks": "outside",
247247
"tickcolor": "rgba(51,51,51,1)",
248248
"ticklen": 3.6529680365296811,
249-
"tickwidth": 0,
249+
"tickwidth": 0.66417600664176002,
250250
"showticklabels": true,
251251
"tickfont": {
252252
"color": "rgba(77,77,77,1)",
@@ -259,7 +259,7 @@
259259
"linewidth": 0,
260260
"showgrid": true,
261261
"gridcolor": "rgba(255,255,255,1)",
262-
"gridwidth": 0,
262+
"gridwidth": 0.66417600664176002,
263263
"zeroline": false,
264264
"anchor": "y",
265265
"title": {
@@ -308,7 +308,7 @@
308308
"ticks": "outside",
309309
"tickcolor": "rgba(51,51,51,1)",
310310
"ticklen": 3.6529680365296811,
311-
"tickwidth": 0,
311+
"tickwidth": 0.66417600664176002,
312312
"showticklabels": true,
313313
"tickfont": {
314314
"color": "rgba(77,77,77,1)",
@@ -321,7 +321,7 @@
321321
"linewidth": 0,
322322
"showgrid": true,
323323
"gridcolor": "rgba(255,255,255,1)",
324-
"gridwidth": 0,
324+
"gridwidth": 0.66417600664176002,
325325
"zeroline": false,
326326
"anchor": "x",
327327
"title": {
@@ -335,30 +335,13 @@
335335
"hoverformat": ".2f"
336336
},
337337
"shapes": [
338-
{
339-
"type": "rect",
340-
"fillcolor": null,
341-
"line": {
342-
"color": null,
343-
"width": 0,
344-
"linetype": [
345338

346-
]
347-
},
348-
"yref": "paper",
349-
"xref": "paper",
350-
"layer": "below",
351-
"x0": 0,
352-
"x1": 1,
353-
"y0": 0,
354-
"y1": 1
355-
}
356339
],
357340
"showlegend": false,
358341
"legend": {
359342
"bgcolor": "rgba(255,255,255,1)",
360343
"bordercolor": "transparent",
361-
"borderwidth": 0,
344+
"borderwidth": 1.8897637795275593,
362345
"font": {
363346
"color": "rgba(0,0,0,1)",
364347
"family": "",

0 commit comments

Comments
 (0)