Skip to content

Commit 219f037

Browse files
committed
Fix legend rendering for fill=NA aesthetic (#227)
1 parent d6e41e6 commit 219f037

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Changes in version 2025.10.5 (Issue #227)
2+
3+
- Fixed legend rendering for `fill=NA` aesthetic. Previously, legend symbols would incorrectly show solid fill when `fill=NA` was specified. Now `fill=NA` is properly converted to "transparent" before legend compilation, ensuring legend symbols appear hollow as expected. This makes `fill=NA` behave identically to `fill="transparent"`.
4+
15
# Changes in version 2025.10.3 (PR#240)
26

37
- `guide_legend(override.aes)` works in a plot with both color and fill legends.

R/z_animintHelpers.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,9 +711,11 @@ getLegend <- function(mb){
711711
if (nd != nk) warning("key and data have different number of rows")
712712
if (!".label" %in% names(key)) return(data.frame()); # if there are no labels, return an empty df.
713713
data$`.label` <- key$`.label`
714-
data <- data[, which(colSums(!is.na(data)) > 0)] # remove cols that are entirely na
714+
# Convert colour and fill to RGB BEFORE removing NA columns
715+
# This ensures fill=NA gets converted to "transparent" and is not removed
715716
if("colour" %in% names(data)) data[["colour"]] <- toRGB(data[["colour"]]) # color hex values
716717
if("fill" %in% names(data)) data[["fill"]] <- toRGB(data[["fill"]]) # fill hex values
718+
data <- data[, which(colSums(!is.na(data)) > 0)] # remove cols that are entirely na
717719
names(data) <- paste0(geom, names(data))# aesthetics by geom
718720
names(data) <- gsub(paste0(geom, "."), "", names(data), fixed=TRUE) # label isn't geom-specific
719721
data$label <- paste(data$label) # otherwise it is AsIs.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
library(testthat)
2+
acontext("legend fill=NA rendering")
3+
4+
test_that("fill=NA is converted to transparent in legend", {
5+
# Create a simple plot with fill=NA
6+
viz <- animint(
7+
fillNA=ggplot()+
8+
ggtitle("fill=NA")+
9+
geom_point(aes(
10+
x, x, color=x),
11+
fill=NA,
12+
shape=21,
13+
size=10,
14+
data=data.frame(x=1))
15+
)
16+
17+
# Compile the visualization
18+
info <- animint2dir(viz, open.browser = FALSE)
19+
20+
# Read the generated plot.json
21+
plot_json <- jsonlite::fromJSON(file.path(info$out.dir, "plot.json"))
22+
23+
# Check that the legend entry has pointfill="transparent"
24+
legend_entries <- plot_json$plots$fillNA$legend$x$entries
25+
expect_equal(nrow(legend_entries), 1)
26+
expect_equal(legend_entries$pointfill[1], "transparent")
27+
})
28+
29+
test_that("fill=NA works the same as fill='transparent'", {
30+
# Create two plots - one with fill=NA, one with fill="transparent"
31+
viz <- animint(
32+
fillNA=ggplot()+
33+
ggtitle("fill=NA")+
34+
geom_point(aes(
35+
x, x, color=x),
36+
fill=NA,
37+
shape=21,
38+
size=10,
39+
data=data.frame(x=1)),
40+
filltransparent=ggplot()+
41+
ggtitle("fill=transparent")+
42+
geom_point(aes(
43+
x, x, color=x),
44+
fill="transparent",
45+
shape=21,
46+
size=10,
47+
data=data.frame(x=1))
48+
)
49+
50+
# Compile the visualization
51+
info <- animint2dir(viz, open.browser = FALSE)
52+
53+
# Read the generated plot.json
54+
plot_json <- jsonlite::fromJSON(file.path(info$out.dir, "plot.json"))
55+
56+
# Check that both legend entries have the same pointfill value
57+
fillNA_entry <- plot_json$plots$fillNA$legend$x$entries
58+
filltransparent_entry <- plot_json$plots$filltransparent$legend$x$entries
59+
60+
expect_equal(fillNA_entry$pointfill[1], "transparent")
61+
expect_equal(filltransparent_entry$pointfill[1], "transparent")
62+
expect_equal(fillNA_entry$pointfill[1], filltransparent_entry$pointfill[1])
63+
})
64+
65+
test_that("legend with multiple entries and mixed fill values", {
66+
# Create a plot with multiple legend entries, some with NA fill
67+
test_data <- data.frame(
68+
x = 1:3,
69+
y = 1:3,
70+
group = c("A", "B", "C")
71+
)
72+
73+
viz <- animint(
74+
plot=ggplot(test_data, aes(x, y, color=group))+
75+
geom_point(shape=21, size=5, fill=NA)
76+
)
77+
78+
# Compile the visualization
79+
info <- animint2dir(viz, open.browser = FALSE)
80+
81+
# Read the generated plot.json
82+
plot_json <- jsonlite::fromJSON(file.path(info$out.dir, "plot.json"))
83+
84+
# Check that all legend entries have pointfill="transparent"
85+
legend_entries <- plot_json$plots$plot$legend$group$entries
86+
expect_equal(nrow(legend_entries), 3)
87+
88+
for (i in 1:3) {
89+
expect_equal(legend_entries$pointfill[i], "transparent",
90+
info=paste("Entry", i, "should have transparent fill"))
91+
}
92+
})

0 commit comments

Comments
 (0)