diff --git a/NEWS.md b/NEWS.md index f4815f325d..643e11bc65 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # ggplot2 (development version) +* Position adjustments can now have auxiliary aesthetics (@teunbrand). + * `position_nudge()` gains `nudge_x` and `nudge_y` aesthetics (#3026, #5445). + * `position_dodge()` gains `order` aesthetic (#3022, #3345) * More stability for vctrs-based palettes (@teunbrand, #6117). * Fixed regression in `guide_bins(reverse = TRUE)` (@teunbrand, #6183). * New function family for setting parts of a theme. For example, you can now use diff --git a/R/geom-label.R b/R/geom-label.R index 6f21478da0..68f4549b6e 100644 --- a/R/geom-label.R +++ b/R/geom-label.R @@ -4,11 +4,9 @@ #' @param label.r Radius of rounded corners. Defaults to 0.15 lines. #' @param label.size Size of label border, in mm. geom_label <- function(mapping = NULL, data = NULL, - stat = "identity", position = "identity", + stat = "identity", position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, label.padding = unit(0.25, "lines"), label.r = unit(0.15, "lines"), label.size = 0.25, @@ -16,16 +14,6 @@ geom_label <- function(mapping = NULL, data = NULL, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { - if (!missing(nudge_x) || !missing(nudge_y)) { - if (!missing(position)) { - cli::cli_abort(c( - "Both {.arg position} and {.arg nudge_x}/{.arg nudge_y} are supplied.", - "i" = "Choose one approach to alter the position." - )) - } - - position <- position_nudge(nudge_x, nudge_y) - } layer( data = data, diff --git a/R/geom-sf.R b/R/geom-sf.R index 1d53d67499..4b61300108 100644 --- a/R/geom-sf.R +++ b/R/geom-sf.R @@ -314,11 +314,9 @@ geom_sf <- function(mapping = aes(), data = NULL, stat = "sf", #' @inheritParams geom_label #' @inheritParams stat_sf_coordinates geom_sf_label <- function(mapping = aes(), data = NULL, - stat = "sf_coordinates", position = "identity", + stat = "sf_coordinates", position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, label.padding = unit(0.25, "lines"), label.r = unit(0.15, "lines"), label.size = 0.25, @@ -327,17 +325,6 @@ geom_sf_label <- function(mapping = aes(), data = NULL, inherit.aes = TRUE, fun.geometry = NULL) { - if (!missing(nudge_x) || !missing(nudge_y)) { - if (!missing(position)) { - cli::cli_abort(c( - "Both {.arg position} and {.arg nudge_x}/{.arg nudge_y} are supplied.", - "i" = "Only use one approach to alter the position." - )) - } - - position <- position_nudge(nudge_x, nudge_y) - } - layer_sf( data = data, mapping = mapping, @@ -363,28 +350,15 @@ geom_sf_label <- function(mapping = aes(), data = NULL, #' @inheritParams geom_text #' @inheritParams stat_sf_coordinates geom_sf_text <- function(mapping = aes(), data = NULL, - stat = "sf_coordinates", position = "identity", + stat = "sf_coordinates", position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, check_overlap = FALSE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, fun.geometry = NULL) { - if (!missing(nudge_x) || !missing(nudge_y)) { - if (!missing(position)) { - cli::cli_abort(c( - "Both {.arg position} and {.arg nudge_x}/{.arg nudge_y} are supplied.", - "i" = "Only use one approach to alter the position." - )) - } - - position <- position_nudge(nudge_x, nudge_y) - } - layer_sf( data = data, mapping = mapping, diff --git a/R/geom-text.R b/R/geom-text.R index 78e601f8f9..7e7a1b8f81 100644 --- a/R/geom-text.R +++ b/R/geom-text.R @@ -41,19 +41,6 @@ #' @inheritParams geom_point #' @param parse If `TRUE`, the labels will be parsed into expressions and #' displayed as described in `?plotmath`. -#' @param nudge_x,nudge_y Horizontal and vertical adjustment to nudge labels by. -#' Useful for offsetting text from points, particularly on discrete scales. -#' Cannot be jointly specified with `position`. -#' @param position A position adjustment to use on the data for this layer. -#' Cannot be jointy specified with `nudge_x` or `nudge_y`. This -#' can be used in various ways, including to prevent overplotting and -#' improving the display. The `position` argument accepts the following: -#' * The result of calling a position function, such as `position_jitter()`. -#' * A string naming the position adjustment. To give the position as a -#' string, strip the function name of the `position_` prefix. For example, -#' to use `position_jitter()`, give the position as `"jitter"`. -#' * For more information and other ways to specify the position, see the -#' [layer position][layer_positions] documentation. #' @param check_overlap If `TRUE`, text that overlaps previous text in the #' same layer will not be plotted. `check_overlap` happens at draw time and in #' the order of the data. Therefore data should be arranged by the label @@ -166,28 +153,15 @@ #' geom_text(aes(label = text), vjust = "inward", hjust = "inward") #' } geom_text <- function(mapping = NULL, data = NULL, - stat = "identity", position = "identity", + stat = "identity", position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, check_overlap = FALSE, size.unit = "mm", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { - if (!missing(nudge_x) || !missing(nudge_y)) { - if (!missing(position)) { - cli::cli_abort(c( - "Both {.arg position} and {.arg nudge_x}/{.arg nudge_y} are supplied.", - "i" = "Only use one approach to alter the position." - )) - } - - position <- position_nudge(nudge_x, nudge_y) - } - layer( data = data, mapping = mapping, diff --git a/R/layer.R b/R/layer.R index a915763e3c..6be74b5d72 100644 --- a/R/layer.R +++ b/R/layer.R @@ -128,12 +128,12 @@ layer <- function(geom = NULL, stat = NULL, # Split up params between aesthetics, geom, and stat params <- rename_aes(params) - aes_params <- params[intersect(names(params), geom$aesthetics())] + aes_params <- params[intersect(names(params), union(geom$aesthetics(), position$aesthetics()))] geom_params <- params[intersect(names(params), geom$parameters(TRUE))] stat_params <- params[intersect(names(params), stat$parameters(TRUE))] ignore <- c("key_glyph", "name") - all <- c(geom$parameters(TRUE), stat$parameters(TRUE), geom$aesthetics(), ignore) + all <- c(geom$parameters(TRUE), stat$parameters(TRUE), geom$aesthetics(), position$aesthetics(), ignore) # Take care of plain patterns provided as aesthetic pattern <- vapply(aes_params, is_pattern, logical(1)) @@ -164,7 +164,7 @@ layer <- function(geom = NULL, stat = NULL, extra_aes <- setdiff( mapped_aesthetics(mapping), - c(geom$aesthetics(), stat$aesthetics()) + c(geom$aesthetics(), stat$aesthetics(), position$aesthetics()) ) # Take care of size->linewidth aes renaming if (geom$rename_size && "size" %in% extra_aes && !"linewidth" %in% mapped_aesthetics(mapping)) { @@ -415,6 +415,7 @@ Layer <- ggproto("Layer", NULL, compute_position = function(self, data, layout) { if (empty(data)) return(data_frame0()) + data <- self$position$use_defaults(data, self$aes_params) params <- self$position$setup_params(data) data <- self$position$setup_data(data, params) diff --git a/R/position-.R b/R/position-.R index 88d6f914a9..c78ca0fc4c 100644 --- a/R/position-.R +++ b/R/position-.R @@ -46,6 +46,8 @@ Position <- ggproto("Position", required_aes = character(), + default_aes = aes(), + setup_params = function(self, data) { list() }, @@ -66,6 +68,36 @@ Position <- ggproto("Position", compute_panel = function(self, data, params, scales) { cli::cli_abort("Not implemented.") + }, + + aesthetics = function(self) { + required_aes <- self$required_aes + if (!is.null(required_aes)) { + required_aes <- unlist(strsplit(self$required_aes, "|", fixed = TRUE)) + } + c(union(required_aes, names(self$default_aes))) + }, + + use_defaults = function(self, data, params = list()) { + + aes <- self$aesthetics() + defaults <- self$default_aes + + params <- params[intersect(names(params), aes)] + params <- params[setdiff(names(params), names(data))] + defaults <- defaults[setdiff(names(defaults), c(names(params), names(data)))] + + if ((length(params) + length(defaults)) < 1) { + return(data) + } + + new <- compact(lapply(defaults, eval_tidy, data = data)) + new[names(params)] <- params + check_aesthetics(new, nrow(data)) + + data[names(new)] <- new + data + } ) diff --git a/R/position-dodge.R b/R/position-dodge.R index 78d4a9a45f..bd816eecc9 100644 --- a/R/position-dodge.R +++ b/R/position-dodge.R @@ -19,6 +19,8 @@ #' @param reverse If `TRUE`, will reverse the default stacking order. #' This is useful if you're rotating both the plot and legend. #' @family position adjustments +#' @eval rd_aesthetics("position", "dodge") +#' #' @export #' @examples #' ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + @@ -104,7 +106,10 @@ PositionDodge <- ggproto("PositionDodge", Position, preserve = "total", orientation = "x", reverse = NULL, + default_aes = aes(order = NULL), + setup_params = function(self, data) { + flipped_aes <- has_flipped_aes(data, default = self$orientation == "y") check_required_aesthetics( if (flipped_aes) "y|ymin" else "x|xmin", @@ -139,9 +144,22 @@ PositionDodge <- ggproto("PositionDodge", Position, setup_data = function(self, data, params) { data <- flip_data(data, params$flipped_aes) + if (!"x" %in% names(data) && all(c("xmin", "xmax") %in% names(data))) { data$x <- (data$xmin + data$xmax) / 2 } + + data$order <- xtfrm( # xtfrm makes anything 'sortable' + data$order %||% ave(data$group, data$x, data$PANEL, FUN = match_sorted) + ) + if (params$reverse) { + data$order <- -data$order + } + if (is.null(params$n)) { # preserve = "total" + data$order <- ave(data$order, data$x, data$PANEL, FUN = match_sorted) + } else { # preserve = "single" + data$order <- match_sorted(data$order) + } flip_data(data, params$flipped_aes) }, @@ -179,7 +197,7 @@ pos_dodge <- function(df, width, n = NULL) { # Have a new group index from 1 to number of groups. # This might be needed if the group numbers in this set don't include all of 1:n - groupidx <- match(df$group, unique0(df$group)) + groupidx <- df$order %||% match_sorted(df$group) # Find the center for each group, then use that to calculate xmin and xmax df$x <- df$x + width * ((groupidx - 0.5) / n - 0.5) @@ -188,3 +206,7 @@ pos_dodge <- function(df, width, n = NULL) { df } + +match_sorted <- function(x, y = x, ...) { + vec_match(x, vec_sort(unique0(y), ...)) +} diff --git a/R/position-nudge.R b/R/position-nudge.R index 56e4e8fe4d..6bf8a81f01 100644 --- a/R/position-nudge.R +++ b/R/position-nudge.R @@ -8,6 +8,7 @@ #' @family position adjustments #' @param x,y Amount of vertical and horizontal distance to move. #' @export +#' @eval rd_aesthetics("position", "nudge") #' @examples #' df <- data.frame( #' x = c(1,3,2,5), @@ -26,7 +27,7 @@ #' ggplot(df, aes(x, y)) + #' geom_point() + #' geom_text(aes(label = y), nudge_y = -0.1) -position_nudge <- function(x = 0, y = 0) { +position_nudge <- function(x = NULL, y = NULL) { ggproto(NULL, PositionNudge, x = x, y = y @@ -38,25 +39,21 @@ position_nudge <- function(x = 0, y = 0) { #' @usage NULL #' @export PositionNudge <- ggproto("PositionNudge", Position, - x = 0, - y = 0, + x = NULL, + y = NULL, + + default_aes = aes(nudge_x = 0, nudge_y = 0), setup_params = function(self, data) { - list(x = self$x, y = self$y) + list( + x = self$x %||% data$nudge_x, + y = self$y %||% data$nudge_y + ) }, compute_layer = function(self, data, params, layout) { - # transform only the dimensions for which non-zero nudging is requested - if (any(params$x != 0)) { - if (any(params$y != 0)) { - transform_position(data, function(x) x + params$x, function(y) y + params$y) - } else { - transform_position(data, function(x) x + params$x, NULL) - } - } else if (any(params$y != 0)) { - transform_position(data, NULL, function(y) y + params$y) - } else { - data # if both x and y are 0 we don't need to transform - } + trans_x <- if (any(params$x != 0)) function(x) x + params$x + trans_y <- if (any(params$y != 0)) function(y) y + params$y + transform_position(data, trans_x, trans_y) } ) diff --git a/R/utilities-help.R b/R/utilities-help.R index 22bddc7dcd..e97e7ad50e 100644 --- a/R/utilities-help.R +++ b/R/utilities-help.R @@ -3,7 +3,8 @@ rd_aesthetics <- function(type, name, extra_note = NULL) { obj <- switch(type, geom = validate_subclass(name, "Geom", env = globalenv()), - stat = validate_subclass(name, "Stat", env = globalenv()) + stat = validate_subclass(name, "Stat", env = globalenv()), + position = validate_subclass(name, "Position", env = globalenv()) ) aes <- rd_aesthetics_item(obj) diff --git a/R/utilities.R b/R/utilities.R index 3b0e9ec806..3bcdaacedc 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -940,7 +940,7 @@ compute_data_size <- function(data, size, default = 0.9, res <- vapply(res, resolution, FUN.VALUE = numeric(1), ...) res <- min(res, na.rm = TRUE) } else if (panels == "by") { - res <- ave(data[[var]], data$PANEL, FUN = function(x) resolution(x, ...)) + res <- stats::ave(data[[var]], data$PANEL, FUN = function(x) resolution(x, ...)) } else { res <- resolution(data[[var]], ...) } diff --git a/man/geom_boxplot.Rd b/man/geom_boxplot.Rd index d5026c013f..5760caa71a 100644 --- a/man/geom_boxplot.Rd +++ b/man/geom_boxplot.Rd @@ -240,6 +240,7 @@ See McGill et al. (1978) for more details. \item \code{\link[=aes_linetype_size_shape]{shape}} \item \code{\link[=aes_linetype_size_shape]{size}} \item \code{weight} +\item \code{width} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_dotplot.Rd b/man/geom_dotplot.Rd index ff7f30a4fc..5dbf0614db 100644 --- a/man/geom_dotplot.Rd +++ b/man/geom_dotplot.Rd @@ -171,6 +171,7 @@ to match the number of dots. \item \code{\link[=aes_linetype_size_shape]{linetype}} \item \code{stroke} \item \code{weight} +\item \code{width} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/geom_text.Rd b/man/geom_text.Rd index e88e45a0e1..e92e3fe3d8 100644 --- a/man/geom_text.Rd +++ b/man/geom_text.Rd @@ -9,11 +9,9 @@ geom_label( mapping = NULL, data = NULL, stat = "identity", - position = "identity", + position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, label.padding = unit(0.25, "lines"), label.r = unit(0.15, "lines"), label.size = 0.25, @@ -27,11 +25,9 @@ geom_text( mapping = NULL, data = NULL, stat = "identity", - position = "identity", + position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, check_overlap = FALSE, size.unit = "mm", na.rm = FALSE, @@ -73,12 +69,12 @@ give the stat as \code{"count"}. \link[=layer_stats]{layer stat} documentation. }} -\item{position}{A position adjustment to use on the data for this layer. -Cannot be jointy specified with \code{nudge_x} or \code{nudge_y}. This +\item{position}{A position adjustment to use on the data for this layer. This can be used in various ways, including to prevent overplotting and improving the display. The \code{position} argument accepts the following: \itemize{ \item The result of calling a position function, such as \code{position_jitter()}. +This method allows for passing extra arguments to the position. \item A string naming the position adjustment. To give the position as a string, strip the function name of the \code{position_} prefix. For example, to use \code{position_jitter()}, give the position as \code{"jitter"}. @@ -117,10 +113,6 @@ lists which parameters it can accept. \item{parse}{If \code{TRUE}, the labels will be parsed into expressions and displayed as described in \code{?plotmath}.} -\item{nudge_x, nudge_y}{Horizontal and vertical adjustment to nudge labels by. -Useful for offsetting text from points, particularly on discrete scales. -Cannot be jointly specified with \code{position}.} - \item{label.padding}{Amount of padding around label. Defaults to 0.25 lines.} \item{label.r}{Radius of rounded corners. Defaults to 0.15 lines.} diff --git a/man/geom_violin.Rd b/man/geom_violin.Rd index 244a7ac7ea..7b923b68ab 100644 --- a/man/geom_violin.Rd +++ b/man/geom_violin.Rd @@ -196,6 +196,7 @@ This geom treats each axis differently and, thus, can thus have two orientations \item \code{\link[=aes_linetype_size_shape]{linetype}} \item \code{\link[=aes_linetype_size_shape]{linewidth}} \item \code{weight} +\item \code{width} } Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. } diff --git a/man/ggsf.Rd b/man/ggsf.Rd index 1fee9f59bb..1ab15f232a 100644 --- a/man/ggsf.Rd +++ b/man/ggsf.Rd @@ -44,11 +44,9 @@ geom_sf_label( mapping = aes(), data = NULL, stat = "sf_coordinates", - position = "identity", + position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, label.padding = unit(0.25, "lines"), label.r = unit(0.15, "lines"), label.size = 0.25, @@ -62,11 +60,9 @@ geom_sf_text( mapping = aes(), data = NULL, stat = "sf_coordinates", - position = "identity", + position = "nudge", ..., parse = FALSE, - nudge_x = 0, - nudge_y = 0, check_overlap = FALSE, na.rm = FALSE, show.legend = NA, @@ -277,10 +273,6 @@ lists which parameters it can accept. \item{parse}{If \code{TRUE}, the labels will be parsed into expressions and displayed as described in \code{?plotmath}.} -\item{nudge_x, nudge_y}{Horizontal and vertical adjustment to nudge labels by. -Useful for offsetting text from points, particularly on discrete scales. -Cannot be jointly specified with \code{position}.} - \item{label.padding}{Amount of padding around label. Defaults to 0.25 lines.} \item{label.r}{Radius of rounded corners. Defaults to 0.15 lines.} diff --git a/man/position_dodge.Rd b/man/position_dodge.Rd index e4f9211110..5706e93e02 100644 --- a/man/position_dodge.Rd +++ b/man/position_dodge.Rd @@ -46,6 +46,15 @@ be specified in the global or \verb{geom_*} layer. Unlike \code{position_dodge() particularly useful for arranging box plots, which can have variable widths. } +\section{Aesthetics}{ + +\code{position_dodge()} understands the following aesthetics (required aesthetics are in bold): +\itemize{ +\item \code{order} +} +Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. +} + \examples{ ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) + geom_bar(position = "dodge2") diff --git a/man/position_nudge.Rd b/man/position_nudge.Rd index 3b2b2573cb..0db66c8035 100644 --- a/man/position_nudge.Rd +++ b/man/position_nudge.Rd @@ -4,7 +4,7 @@ \alias{position_nudge} \title{Nudge points a fixed distance} \usage{ -position_nudge(x = 0, y = 0) +position_nudge(x = NULL, y = NULL) } \arguments{ \item{x, y}{Amount of vertical and horizontal distance to move.} @@ -15,6 +15,16 @@ items on discrete scales by a small amount. Nudging is built in to \code{\link[=geom_text]{geom_text()}} because it's so useful for moving labels a small distance from what they're labelling. } +\section{Aesthetics}{ + +\code{position_nudge()} understands the following aesthetics (required aesthetics are in bold): +\itemize{ +\item \code{nudge_x} +\item \code{nudge_y} +} +Learn more about setting these aesthetics in \code{vignette("ggplot2-specs")}. +} + \examples{ df <- data.frame( x = c(1,3,2,5), diff --git a/man/theme.Rd b/man/theme.Rd index d28c10b149..51f92e1f96 100644 --- a/man/theme.Rd +++ b/man/theme.Rd @@ -384,7 +384,7 @@ differently when added to a ggplot object. Also, when setting \code{complete = TRUE} all elements will be set to inherit from blank elements.} -\item{validate}{\code{TRUE} to run \code{validate_element()}, \code{FALSE} to bypass checks.} +\item{validate}{\code{TRUE} to run \code{check_element()}, \code{FALSE} to bypass checks.} } \description{ Themes are a powerful way to customize the non-data components of your plots: diff --git a/tests/testthat/_snaps/geom-label.md b/tests/testthat/_snaps/geom-label.md index 2ea8c33c06..68ab4ebba4 100644 --- a/tests/testthat/_snaps/geom-label.md +++ b/tests/testthat/_snaps/geom-label.md @@ -1,7 +1,6 @@ # geom_label() throws meaningful errors - Both `position` and `nudge_x`/`nudge_y` are supplied. - i Choose one approach to alter the position. + Ignoring unknown parameters: `nudge_x` --- diff --git a/tests/testthat/_snaps/geom-sf.md b/tests/testthat/_snaps/geom-sf.md index 2d5217dd4f..74edd268e1 100644 --- a/tests/testthat/_snaps/geom-sf.md +++ b/tests/testthat/_snaps/geom-sf.md @@ -19,13 +19,11 @@ --- - Both `position` and `nudge_x`/`nudge_y` are supplied. - i Only use one approach to alter the position. + Ignoring unknown parameters: `nudge_x` --- - Both `position` and `nudge_x`/`nudge_y` are supplied. - i Only use one approach to alter the position. + Ignoring unknown parameters: `nudge_x` --- diff --git a/tests/testthat/_snaps/geom-text.md b/tests/testthat/_snaps/geom-text.md index e86cc9c905..917a4ca707 100644 --- a/tests/testthat/_snaps/geom-text.md +++ b/tests/testthat/_snaps/geom-text.md @@ -1,7 +1,6 @@ # geom_text() checks input - Both `position` and `nudge_x`/`nudge_y` are supplied. - i Only use one approach to alter the position. + Ignoring unknown parameters: `nudge_x` # geom_text() drops missing angles diff --git a/tests/testthat/_snaps/prohibited-functions.md b/tests/testthat/_snaps/prohibited-functions.md index 007e6521c4..4612a484bf 100644 --- a/tests/testthat/_snaps/prohibited-functions.md +++ b/tests/testthat/_snaps/prohibited-functions.md @@ -43,17 +43,11 @@ $geom_density_2d_filled [1] "contour_var" - $geom_label - [1] "nudge_x" "nudge_y" - - $geom_sf_label - [1] "nudge_x" "nudge_y" - $geom_sf_text - [1] "nudge_x" "nudge_y" "check_overlap" + [1] "check_overlap" $geom_text - [1] "nudge_x" "nudge_y" "check_overlap" + [1] "check_overlap" $geom_violin [1] "draw_quantiles" diff --git a/tests/testthat/test-geom-label.R b/tests/testthat/test-geom-label.R index 028c3c4980..c80be5e38b 100644 --- a/tests/testthat/test-geom-label.R +++ b/tests/testthat/test-geom-label.R @@ -1,5 +1,5 @@ test_that("geom_label() throws meaningful errors", { - expect_snapshot_error(geom_label(position = "jitter", nudge_x = 0.5)) + expect_snapshot_warning(geom_label(position = "jitter", nudge_x = 0.5)) expect_snapshot_error(labelGrob(label = 1:3)) }) diff --git a/tests/testthat/test-geom-sf.R b/tests/testthat/test-geom-sf.R index ed6914ba61..29f5da8323 100644 --- a/tests/testthat/test-geom-sf.R +++ b/tests/testthat/test-geom-sf.R @@ -137,8 +137,8 @@ test_that("errors are correctly triggered", { ) p <- ggplot(pts) + geom_sf() + coord_cartesian() expect_snapshot_error(ggplotGrob(p)) - expect_snapshot_error(geom_sf_label(position = "jitter", nudge_x = 0.5)) - expect_snapshot_error(geom_sf_text(position = "jitter", nudge_x = 0.5)) + expect_snapshot_warning(geom_sf_label(position = "jitter", nudge_x = 0.5)) + expect_snapshot_warning(geom_sf_text(position = "jitter", nudge_x = 0.5)) # #5204: missing linewidth should be dropped pts <- sf::st_sf( diff --git a/tests/testthat/test-geom-text.R b/tests/testthat/test-geom-text.R index a6fe3359d9..8fe509e724 100644 --- a/tests/testthat/test-geom-text.R +++ b/tests/testthat/test-geom-text.R @@ -1,5 +1,5 @@ test_that("geom_text() checks input", { - expect_snapshot_error(geom_text(position = "jitter", nudge_x = 0.5)) + expect_snapshot_warning(geom_text(position = "jitter", nudge_x = 0.5)) }) test_that("geom_text() drops missing angles", { diff --git a/tests/testthat/test-position_dodge.R b/tests/testthat/test-position_dodge.R index 14b79d3cad..878ee6d155 100644 --- a/tests/testthat/test-position_dodge.R +++ b/tests/testthat/test-position_dodge.R @@ -38,6 +38,21 @@ test_that("position_dodge() can reverse the dodge order", { expect_equal(ld$label[order(ld$x)], c("A", "A", "B", "B", "C")) }) +test_that("position_dodge() can use the order aesthetic", { + + major <- c(1,1,1,2,2,3,3,4,4,5,6,7) + minor <- c(1:3, 1:2, 1, 3, 2:3, 1:3) + df <- data_frame0( + x = LETTERS[major], + g = c("X", "Y", "Z")[minor] + ) + ld <- layer_data( + ggplot(df, aes(x, 1, colour = g, order = g)) + + geom_point(position = position_dodge(preserve = "single", width = 0.6)) + ) + expect_equal(ld$x, major + c(-0.2, 0, 0.2)[minor], ignore_attr = TRUE) +}) + test_that("position_dodge warns about missing required aesthetics", { # Bit of a contrived geom to not have a required 'x' aesthetic