diff --git a/NEWS.md b/NEWS.md index 228a98ae..5503ec83 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,10 @@ - Improve common chunk detection, output `na_group` and `row_in_group` when there are missing values. +# Changes in version 2025.10.5 (PR#244) + +- `geom(fill=NA)` now works like `geom(fill="transparent")`. + # Changes in version 2025.10.3 (PR#240) - `guide_legend(override.aes)` works in a plot with both color and fill legends. diff --git a/R/z_animintHelpers.R b/R/z_animintHelpers.R index f3686454..2662a4c3 100644 --- a/R/z_animintHelpers.R +++ b/R/z_animintHelpers.R @@ -680,9 +680,11 @@ getLegend <- function(mb){ if (nd != nk) warning("key and data have different number of rows") if (!".label" %in% names(key)) return(data.frame()); # if there are no labels, return an empty df. data$`.label` <- key$`.label` - data <- data[, which(colSums(!is.na(data)) > 0)] # remove cols that are entirely na + # Convert colour and fill to RGB BEFORE removing NA columns + # This ensures fill=NA gets converted to "transparent" and is not removed if("colour" %in% names(data)) data[["colour"]] <- toRGB(data[["colour"]]) # color hex values if("fill" %in% names(data)) data[["fill"]] <- toRGB(data[["fill"]]) # fill hex values + data <- data[, which(colSums(!is.na(data)) > 0)] # remove cols that are entirely na names(data) <- paste0(geom, names(data))# aesthetics by geom names(data) <- gsub(paste0(geom, "."), "", names(data), fixed=TRUE) # label isn't geom-specific data$label <- paste(data$label) # otherwise it is AsIs. diff --git a/tests/testthat/test-compiler-legend-fill-na.R b/tests/testthat/test-compiler-legend-fill-na.R new file mode 100644 index 00000000..a7d781c4 --- /dev/null +++ b/tests/testthat/test-compiler-legend-fill-na.R @@ -0,0 +1,68 @@ +library(testthat) +acontext("legend fill=NA rendering") + +test_that("fill=NA is converted to transparent in legend", { + viz <- animint( + fillNA=ggplot()+ + ggtitle("fill=NA")+ + geom_point(aes( + x, x, color=x), + fill=NA, + shape=21, + size=10, + data=data.frame(x=1)) + ) + info <- animint2dir(viz, open.browser = FALSE) + plot_json <- jsonlite::fromJSON(file.path(info$out.dir, "plot.json")) + legend_entries <- plot_json$plots$fillNA$legend$x$entries + expect_equal(nrow(legend_entries), 1) + expect_equal(legend_entries$pointfill[1], "transparent") +}) + +test_that("fill=NA works the same as fill='transparent'", { + viz <- animint( + fillNA=ggplot()+ + ggtitle("fill=NA")+ + geom_point(aes( + x, x, color=x), + fill=NA, + shape=21, + size=10, + data=data.frame(x=1)), + filltransparent=ggplot()+ + ggtitle("fill=transparent")+ + geom_point(aes( + x, x, color=x), + fill="transparent", + shape=21, + size=10, + data=data.frame(x=1)) + ) + info <- animint2dir(viz, open.browser = FALSE) + plot_json <- jsonlite::fromJSON(file.path(info$out.dir, "plot.json")) + fillNA_entry <- plot_json$plots$fillNA$legend$x$entries + filltransparent_entry <- plot_json$plots$filltransparent$legend$x$entries + expect_equal(fillNA_entry$pointfill[1], "transparent") + expect_equal(filltransparent_entry$pointfill[1], "transparent") + expect_equal(fillNA_entry$pointfill[1], filltransparent_entry$pointfill[1]) +}) + +test_that("legend with multiple entries and mixed fill values", { + test_data <- data.frame( + x = 1:3, + y = 1:3, + group = c("A", "B", "C") + ) + viz <- animint( + plot=ggplot(test_data, aes(x, y, color=group))+ + geom_point(shape=21, size=5, fill=NA) + ) + info <- animint2dir(viz, open.browser = FALSE) + plot_json <- jsonlite::fromJSON(file.path(info$out.dir, "plot.json")) + legend_entries <- plot_json$plots$plot$legend$group$entries + expect_equal(nrow(legend_entries), 3) + for (i in 1:3) { + expect_equal(legend_entries$pointfill[i], "transparent", + info=paste("Entry", i, "should have transparent fill")) + } +})