diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index fd1195d138..275487bf57 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -26,17 +26,17 @@ jobs: fail-fast: false matrix: config: + # vdiffr & shinytest only runs on mac r-release since the results aren't cross-platform - {os: macOS-latest, r: 'release', visual_tests: true, node: "14.x", shinytest: true} - {os: windows-latest, r: 'release'} - {os: windows-latest, r: '4.1'} - {os: windows-latest, r: '3.6'} - - {os: ubuntu-18.04, r: 'devel'} - # vdiffr & shinytest only runs on linux r-release since the results aren't cross-platform - - {os: ubuntu-18.04, r: 'release'} - - {os: ubuntu-18.04, r: 'oldrel-1'} - - {os: ubuntu-18.04, r: 'oldrel-2'} - - {os: ubuntu-18.04, r: 'oldrel-3'} - - {os: ubuntu-18.04, r: 'oldrel-4'} + - {os: ubuntu-latest, r: 'devel'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} + - {os: ubuntu-latest, r: 'oldrel-2'} + - {os: ubuntu-latest, r: 'oldrel-3'} + - {os: ubuntu-latest, r: 'oldrel-4'} env: VISUAL_TESTS: ${{ matrix.config.visual_tests }} diff --git a/DESCRIPTION b/DESCRIPTION index f2982b5555..5985c2a79c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -85,3 +85,5 @@ Config/Needs/check: rcmdcheck, devtools, reshape2 +Remotes: + tidyverse/ggplot2 diff --git a/R/ggplotly.R b/R/ggplotly.R index 4f4117098a..8ac3cd7ed8 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -314,7 +314,7 @@ gg2list <- function(p, width = NULL, height = NULL, }) # Transform all scales - data <- lapply(data, ggfun("scales_transform_df"), scales = scales) + data <- lapply(data, scales_transform_df, scales = scales) # Map and train positions so that statistics have access to ranges # and all positions are numeric @@ -368,7 +368,7 @@ gg2list <- function(p, width = NULL, height = NULL, data <- by_layer(function(l, d) l$map_statistic(d, plot)) # Make sure missing (but required) aesthetics are added - ggfun("scales_add_missing")(plot, c("x", "y"), plot$plot_env) + scales_add_missing(plot, c("x", "y")) # Reparameterise geoms from (e.g.) y and width to ymin and ymax data <- by_layer(function(l, d) l$compute_geom_1(d)) @@ -401,7 +401,7 @@ gg2list <- function(p, width = NULL, height = NULL, # Train and map non-position scales npscales <- scales$non_position_scales() if (npscales$n() > 0) { - lapply(data, ggfun("scales_train_df"), scales = npscales) + lapply(data, scales_train_df, scales = npscales) # this for loop is unique to plotly -- it saves the "domain" # of each non-positional scale for display in tooltips for (sc in npscales$scales) { @@ -413,7 +413,7 @@ gg2list <- function(p, width = NULL, height = NULL, d }) } - data <- lapply(data, ggfun("scales_map_df"), scales = npscales) + data <- lapply(data, scales_map_df, scales = npscales) } # Fill in defaults etc. @@ -1004,12 +1004,12 @@ gg2list <- function(p, width = NULL, height = NULL, # justification of legend boxes theme$legend.box.just <- theme$legend.box.just %||% c("center", "center") # scales -> data for guides - gdefs <- ggfun("guides_train")(scales, theme, plot$guides, plot$labels) - if (length(gdefs) > 0) { - gdefs <- ggfun("guides_merge")(gdefs) - gdefs <- ggfun("guides_geom")(gdefs, layers, plot$mapping) + gdefs <- if (inherits(plot$guides, "ggproto")) { + get_gdefs_ggproto(npscales$scales, theme, plot, layers) + } else { + get_gdefs(scales, theme, plot, layers) } - + # colourbar -> plotly.js colorbar colorbar <- compact(lapply(gdefs, gdef2trace, theme, gglayout)) nguides <- length(colorbar) + gglayout$showlegend @@ -1403,12 +1403,21 @@ gdef2trace <- function(gdef, theme, gglayout) { if (inherits(gdef, "colorbar")) { # sometimes the key has missing values, which we can ignore gdef$key <- gdef$key[!is.na(gdef$key$.value), ] - rng <- range(gdef$bar$value) - gdef$bar$value <- scales::rescale(gdef$bar$value, from = rng) - gdef$key$.value <- scales::rescale(gdef$key$.value, from = rng) + + # Put values on a 0-1 scale + # N.B. ggplot2 >v3.4.2 (specifically #4879) renamed bar to decor and also + # started returning normalized values for the key field + decor <- gdef$decor %||% gdef$bar + rng <- range(decor$value) + decor$value <- scales::rescale(decor$value, from = rng) + if (!"decor" %in% names(gdef)) { + gdef$key$.value <- scales::rescale(gdef$key$.value, from = rng) + } + vals <- lapply(gglayout[c("xaxis", "yaxis")], function(ax) { if (identical(ax$tickmode, "auto")) ax$ticktext else ax$tickvals }) + list( x = vals[[1]][[1]], y = vals[[2]][[1]], @@ -1422,7 +1431,7 @@ gdef2trace <- function(gdef, theme, gglayout) { # do everything on a 0-1 scale marker = list( color = c(0, 1), - colorscale = setNames(gdef$bar[c("value", "colour")], NULL), + colorscale = setNames(decor[c("value", "colour")], NULL), colorbar = list( bgcolor = toRGB(theme$legend.background$fill), bordercolor = toRGB(theme$legend.background$colour), @@ -1459,3 +1468,72 @@ getAesMap <- function(plot, layer) { layer$mapping } } + +# ------------------------------------------------------------------ +# Handle compatibility for changes in ggplot2 >v3.4.2 (specifically #5144), +# which moved away from scales_transform_df(), scales_train_df(), etc +# towards ggproto methods attached to `scales` +# ------------------------------------------------------------------ +scales_transform_df <- function(scales, df) { + if (is.function(scales$transform_df)) { + scales$transform_df(df) + } else { + ggfun("scales_transform_df")(df, scales = scales) + } +} + +scales_train_df <- function(scales, df) { + if (is.function(scales$train_df)) { + scales$train_df(df) + } else { + ggfun("scales_train_df")(df, scales = scales) + } +} + +scales_map_df <- function(scales, df) { + if (is.function(scales$map_df)) { + scales$map_df(df) + } else { + ggfun("scales_map_df")(df, scales = scales) + } +} + +scales_add_missing <- function(plot, aesthetics) { + if (is.function(plot$scales$add_missing)) { + plot$scales$add_missing(c("x", "y"), plot$plot_env) + } else { + ggfun("scales_add_missing")(plot, aesthetics, plot$plot_env) + } +} + +# ------------------------------------------------------------------------- +# Handle compatibility for changes in ggplot2 >v3.4.2 (specifically #4879), +# which away from guides_train(), guides_merge(), guides_geom() +# towards ggproto methods attached to `plot$guides` +# ------------------------------------------------------------------------- +get_gdefs_ggproto <- function(scales, theme, plot, layers) { + guides <- plot$guides$setup(scales) + guides$train(scales, theme$legend.direction, plot$labels) + if (length(guides$guides) > 0) { + guides$merge() + guides$process_layers(layers) + } + # Add old legend/colorbar classes to guide params so that ggplotly() code + # can continue to work the same way it always has + for (i in which(vapply(guides$guides, inherits, logical(1), "GuideColourbar"))) { + guides$params[[i]] <- prefix_class(guides$params[[i]], "colorbar") + } + for (i in which(vapply(guides$guides, inherits, logical(1), "GuideLegend"))) { + guides$params[[i]] <- prefix_class(guides$params[[i]], "legend") + } + guides$params +} + +get_gdefs <- function(scales, theme, plot, layers) { + gdefs <- ggfun("guides_train")(scales, theme, plot$guides, plot$labels) + if (length(gdefs) > 0) { + gdefs <- ggfun("guides_merge")(gdefs) + gdefs <- ggfun("guides_geom")(gdefs, layers, plot$mapping) + } + gdefs +} diff --git a/R/layers2traces.R b/R/layers2traces.R index 5b110cd56e..e246e85146 100644 --- a/R/layers2traces.R +++ b/R/layers2traces.R @@ -103,7 +103,8 @@ layers2traces <- function(data, prestats_data, layout, p) { # now to the actual layer -> trace conversion trace.list <- list() - aes_no_guide <- names(p$guides)[vapply(p$guides, identical, logical(1), "none")] + guides <- if (inherits(p$guides, "ggproto")) p$guides$guides else p$guides + aes_no_guide <- names(guides)[vapply(guides, identical, logical(1), "none")] for (i in seq_along(datz)) { d <- datz[[i]] diff --git a/tests/testthat/_snaps/ggplot-heatmap/heatmap-discrete.svg b/tests/testthat/_snaps/ggplot-heatmap/heatmap-discrete.svg index 78366c2ee6..12ac197a96 100644 --- a/tests/testthat/_snaps/ggplot-heatmap/heatmap-discrete.svg +++ b/tests/testthat/_snaps/ggplot-heatmap/heatmap-discrete.svg @@ -1 +1 @@ -amcarbcyldispdratgearhpmpgqsecvswtamcarbcyldispdratgearhpmpgqsecvswt-0.50.00.51.0corvar1var2 +amcarbcyldispdratgearhpmpgqsecvswtamcarbcyldispdratgearhpmpgqsecvswt-0.50.00.51.0corvar1var2 diff --git a/tests/testthat/_snaps/ggplot-heatmap/heatmap-midpoint.svg b/tests/testthat/_snaps/ggplot-heatmap/heatmap-midpoint.svg index 9f0074863c..7a2fb04789 100644 --- a/tests/testthat/_snaps/ggplot-heatmap/heatmap-midpoint.svg +++ b/tests/testthat/_snaps/ggplot-heatmap/heatmap-midpoint.svg @@ -1 +1 @@ -0.000.250.500.751.000.000.250.500.751.000.000.250.500.751.00zxy +0.000.250.500.751.000.000.250.500.751.000.000.250.500.751.00zxy diff --git a/tests/testthat/_snaps/ggplot-heatmap/heatmap.svg b/tests/testthat/_snaps/ggplot-heatmap/heatmap.svg index 53c7493737..d6881103cb 100644 --- a/tests/testthat/_snaps/ggplot-heatmap/heatmap.svg +++ b/tests/testthat/_snaps/ggplot-heatmap/heatmap.svg @@ -1 +1 @@ -MondayTuesdayWednesdayThursdayFridayMorningAfternoonEvening020406080valuedaytime +MondayTuesdayWednesdayThursdayFridayMorningAfternoonEvening020406080valuedaytime diff --git a/tests/testthat/_snaps/ggplot-hex/hex-basic.svg b/tests/testthat/_snaps/ggplot-hex/hex-basic.svg index 5ca957f81a..0a6cbd6985 100644 --- a/tests/testthat/_snaps/ggplot-hex/hex-basic.svg +++ b/tests/testthat/_snaps/ggplot-hex/hex-basic.svg @@ -1 +1 @@ -01234505000100001500010002000300040005000countcaratprice +01234505000100001500010002000300040005000countcaratprice diff --git a/tests/testthat/_snaps/ggplot-hex/hex-bins.svg b/tests/testthat/_snaps/ggplot-hex/hex-bins.svg index 768fc21ffa..ba07778dc8 100644 --- a/tests/testthat/_snaps/ggplot-hex/hex-bins.svg +++ b/tests/testthat/_snaps/ggplot-hex/hex-bins.svg @@ -1 +1 @@ -01234505000100001500020000250050007500countcaratprice +01234505000100001500020000250050007500countcaratprice diff --git a/tests/testthat/_snaps/ggplot-hex/hex-binwidth.svg b/tests/testthat/_snaps/ggplot-hex/hex-binwidth.svg index 01433f0f4f..0faa47f44a 100644 --- a/tests/testthat/_snaps/ggplot-hex/hex-binwidth.svg +++ b/tests/testthat/_snaps/ggplot-hex/hex-binwidth.svg @@ -1 +1 @@ -0240500010000150002000050001000015000countcaratprice +0240500010000150002000050001000015000countcaratprice diff --git a/tests/testthat/_snaps/ggplot-histogram/histogram-fill.svg b/tests/testthat/_snaps/ggplot-histogram/histogram-fill.svg index 8ef8074014..9a6b65a8e9 100644 --- a/tests/testthat/_snaps/ggplot-histogram/histogram-fill.svg +++ b/tests/testthat/_snaps/ggplot-histogram/histogram-fill.svg @@ -1 +1 @@ -234502460246countwtcount +234502460246countwtcount diff --git a/tests/testthat/_snaps/ggplot-map/map-facet.svg b/tests/testthat/_snaps/ggplot-map/map-facet.svg index b375480779..010be0473b 100644 --- a/tests/testthat/_snaps/ggplot-map/map-facet.svg +++ b/tests/testthat/_snaps/ggplot-map/map-facet.svg @@ -1 +1 @@ -253035404550-120-100-80253035404550-120-100-80100200300valuexyAssaultMurderRapeUrbanPop +253035404550-120-100-80253035404550-120-100-80100200300valuexyAssaultMurderRapeUrbanPop diff --git a/tests/testthat/_snaps/ggplot-path/path-colors.svg b/tests/testthat/_snaps/ggplot-path/path-colors.svg index fa7349a704..6683900020 100644 --- a/tests/testthat/_snaps/ggplot-path/path-colors.svg +++ b/tests/testthat/_snaps/ggplot-path/path-colors.svg @@ -1 +1 @@ -1.001.251.501.752.001.001.251.501.752.001.001.251.501.752.00yxy +1.001.251.501.752.001.001.251.501.752.001.001.251.501.752.00yxy diff --git a/tests/testthat/_snaps/ggplot-segment/segment-multiple-non-numeric.svg b/tests/testthat/_snaps/ggplot-segment/segment-multiple-non-numeric.svg index 79d8ce82d2..50e1168d0e 100644 --- a/tests/testthat/_snaps/ggplot-segment/segment-multiple-non-numeric.svg +++ b/tests/testthat/_snaps/ggplot-segment/segment-multiple-non-numeric.svg @@ -1 +1 @@ -0.81.21.62.0901001101201301.001.251.501.752.00campaigncampaigndonation +0.81.21.62.0901001101201301.001.251.501.752.00campaigncampaigndonation diff --git a/tests/testthat/_snaps/ggplot-sf/sf-fill-text.svg b/tests/testthat/_snaps/ggplot-sf/sf-fill-text.svg index 2eb8628863..644ffacdb0 100644 --- a/tests/testthat/_snaps/ggplot-sf/sf-fill-text.svg +++ b/tests/testthat/_snaps/ggplot-sf/sf-fill-text.svg @@ -1 +1 @@ -84 ° W82 ° W80 ° W78 ° W76 ° W34.0 ° N34.5 ° N35.0 ° N35.5 ° N36.0 ° N36.5 ° N0.050.100.150.20AREA +84 ° W82 ° W80 ° W78 ° W76 ° W34.0 ° N34.5 ° N35.0 ° N35.5 ° N36.0 ° N36.5 ° N0.050.100.150.20AREA diff --git a/tests/testthat/_snaps/ggplot-tooltip/heatmap-discrete-tooltip.svg b/tests/testthat/_snaps/ggplot-tooltip/heatmap-discrete-tooltip.svg index 8b41c92703..6471d3ab3b 100644 --- a/tests/testthat/_snaps/ggplot-tooltip/heatmap-discrete-tooltip.svg +++ b/tests/testthat/_snaps/ggplot-tooltip/heatmap-discrete-tooltip.svg @@ -1 +1 @@ -1020304680.20.40.6densitympgfactor(cyl) +1020304680.20.40.6densitympgfactor(cyl) diff --git a/tests/testthat/_snaps/plotly-linetype/plotly-linetype-manual.new.svg b/tests/testthat/_snaps/plotly-linetype/plotly-linetype-manual.new.svg deleted file mode 100644 index 0bd90c87e9..0000000000 --- a/tests/testthat/_snaps/plotly-linetype/plotly-linetype-manual.new.svg +++ /dev/null @@ -1 +0,0 @@ -1015202530355010015020025030035040045001mpgdisp diff --git a/tests/testthat/_snaps/plotly-linetype/plotly-linetype-manual.svg b/tests/testthat/_snaps/plotly-linetype/plotly-linetype-manual.svg index b7dd154a44..0bd90c87e9 100644 --- a/tests/testthat/_snaps/plotly-linetype/plotly-linetype-manual.svg +++ b/tests/testthat/_snaps/plotly-linetype/plotly-linetype-manual.svg @@ -1 +1 @@ -1015202530355010015020025030035040045001mpgdisp +1015202530355010015020025030035040045001mpgdisp diff --git a/tests/testthat/_snaps/plotly-subplot/ggally-ggcorr.svg b/tests/testthat/_snaps/plotly-subplot/ggally-ggcorr.svg index f19e29a9f3..c47cd60883 100644 --- a/tests/testthat/_snaps/plotly-subplot/ggally-ggcorr.svg +++ b/tests/testthat/_snaps/plotly-subplot/ggally-ggcorr.svg @@ -1 +1 @@ -v1v2v3v4v5012345612345-1.0-0.50.00.51.0 +v1v2v3v4v5012345612345-1.0-0.50.00.51.0 diff --git a/tests/testthat/_snaps/plotly-subplot/subplot-reposition-shape-fixed.new.svg b/tests/testthat/_snaps/plotly-subplot/subplot-reposition-shape-fixed.new.svg deleted file mode 100644 index 1f986bbaf8..0000000000 --- a/tests/testthat/_snaps/plotly-subplot/subplot-reposition-shape-fixed.new.svg +++ /dev/null @@ -1 +0,0 @@ -0246−1012340246−101234 diff --git a/tests/testthat/_snaps/plotly-subplot/subplot-reposition-shape-fixed.svg b/tests/testthat/_snaps/plotly-subplot/subplot-reposition-shape-fixed.svg index 16316e8b7b..1f986bbaf8 100644 --- a/tests/testthat/_snaps/plotly-subplot/subplot-reposition-shape-fixed.svg +++ b/tests/testthat/_snaps/plotly-subplot/subplot-reposition-shape-fixed.svg @@ -1 +1 @@ -0246−1012340246−101234 +0246−1012340246−101234 diff --git a/tests/testthat/test-cookbook-axes.R b/tests/testthat/test-cookbook-axes.R index db50825360..5c5344ed01 100644 --- a/tests/testthat/test-cookbook-axes.R +++ b/tests/testthat/test-cookbook-axes.R @@ -34,13 +34,13 @@ test_that("factor levels determine tick order", { bp.ylim.hide <- bp + ylim(5, 7.5) test_that("ylim hides points", { info <- expect_warning(expect_traces(bp.ylim.hide, 1, "ylim.hide"), - regexp = "non-finite values") + regexp = "non-finite") }) bp.scale.hide <- bp + scale_y_continuous(limits = c(5, 7.5)) test_that("scale_y(limits) hides points", { info <- expect_warning(expect_traces(bp.scale.hide, 1, "scale.hide"), - regexp = "non-finite values") + regexp = "non-finite") expect_equivalent(range(info$layout$yaxis$tickvals), c(5, 7.5)) y <- unlist(lapply(info$data, "[[", "y")) expect_true(all(5 <= y & y <= 7.5, na.rm = TRUE)) diff --git a/tests/testthat/test-ggplot-dynamicTicks.R b/tests/testthat/test-ggplot-dynamicTicks.R index 28459e48a6..f50a6b750b 100644 --- a/tests/testthat/test-ggplot-dynamicTicks.R +++ b/tests/testthat/test-ggplot-dynamicTicks.R @@ -33,7 +33,7 @@ test_that("Categorical axis reflects custom scale mapping", { scale_x_discrete(limits = lims) expect_warning(p <- ggplotly(g, dynamicTicks = "x"), - regexp = "non-finite values") + regexp = "non-finite") axisActual <- with( p$x$layout$xaxis, list(type, tickmode, categoryorder, categoryarray) @@ -49,7 +49,7 @@ test_that("Categorical axis reflects custom scale mapping", { geom_bar() + scale_x_discrete(limits = lims, labels = labs) expect_warning(p <- ggplotly(g, dynamicTicks = "x"), - regexp = "non-finite values") + regexp = "non-finite") axisActual <- with( p$x$layout$xaxis, list(type, tickmode, categoryorder, categoryarray)