Skip to content

Commit e6d8ae9

Browse files
committed
Add validation for selector names to prevent browser rendering failures(#232).
1 parent 9746ed1 commit e6d8ae9

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
<<<<<<< HEAD
12
# Changes in version 2025.10.9 (PR#242)
23

34
- Improve common chunk detection, output `na_group` and `row_in_group` when there are missing values.
5+
=======
6+
# Changes in version 2025.10.6 (Issue #232)
7+
8+
- Added validation for selector names to prevent browser rendering failures. Selector names (from data values used in `clickSelects` and `showSelected`) cannot contain CSS special characters like `#`, `@`, `!`, `$`, etc., as these interfere with JavaScript DOM selectors and cause blank visualizations in the browser. The compiler now stops with a clear error message identifying problematic selector names, helping users fix data issues before attempting to render.
9+
>>>>>>> 32e15c5d (Add validation for selector names to prevent browser rendering failures(#232).)
410
511
# Changes in version 2025.10.3 (PR#240)
612

R/z_animint.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ animint2dir <- function
457457
## For a static data viz with no interactive aes, no need to check
458458
## for trivial showSelected variables with only 1 level.
459459
checkSingleShowSelectedValue(meta$selectors)
460+
461+
## Check selector names for CSS compatibility (no special characters like #)
462+
checkSelectorNames(meta$selectors)
460463

461464
## Go through options and add to the list.
462465
for(v.name in names(meta$duration)){

R/z_animintHelpers.R

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,54 @@ checkSingleShowSelectedValue <- function(selectors){
638638
}
639639

640640

641+
#' Validate selector names for CSS compatibility
642+
#' @param selectors selectors to validate
643+
#' @return \code{NULL}. Throws error if invalid characters found.
644+
checkSelectorNames <- function(selectors){
645+
if(length(selectors) == 0){
646+
return(NULL)
647+
}
648+
649+
selector.names <- names(selectors)
650+
651+
## Characters that are invalid in CSS selectors and cause issues in browser
652+
## CSS selector special characters: # . : [ ] , + > ~ ( ) @ ! $ % ^ & * = | \ / ' " ` ? < >
653+
## Most problematic: # (hash/id selector), . (class selector), : (pseudo-class)
654+
## We check for these common problematic characters
655+
invalid.chars <- c("#", "!", "@", "$", "%", "^", "&", "*", "=", "|", "\\", "/", "'", "\"", "`", "?", "<", ">", "(", ")", "[", "]")
656+
657+
## Check each selector name for invalid characters
658+
has.invalid <- sapply(selector.names, function(name) {
659+
any(sapply(invalid.chars, function(char) {
660+
grepl(char, name, fixed = TRUE)
661+
}))
662+
})
663+
664+
if(any(has.invalid)){
665+
invalid.names <- selector.names[has.invalid]
666+
## Find which character(s) are problematic for each name
667+
problematic <- sapply(invalid.names, function(name) {
668+
found.chars <- invalid.chars[sapply(invalid.chars, function(char) {
669+
grepl(char, name, fixed = TRUE)
670+
})]
671+
paste0("'", found.chars, "'", collapse = ", ")
672+
})
673+
674+
error.msg <- paste0(
675+
"Invalid character(s) in selector name(s). ",
676+
"Selector names cannot contain special characters that interfere with CSS selectors.\n",
677+
"The following selector(s) contain invalid characters:\n",
678+
paste0(" - '", invalid.names, "' contains ", problematic, collapse = "\n"),
679+
"\n\nPlease remove or replace these characters in your variable names."
680+
)
681+
682+
stop(error.msg)
683+
}
684+
685+
return(NULL)
686+
}
687+
688+
641689
#' Set plot width and height for all plots
642690
#' @param meta meta object with all information
643691
#' @param AllPlotsInfo plot info list
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
acontext("Invalid selector character validation")
2+
3+
# Test that selector names with special characters cause errors
4+
# Note: Selector names come from the VALUES in the data when using .variable/.value pattern
5+
test_that("selector name with # causes error in clickSelects", {
6+
viz <- list(
7+
plot1 = ggplot() +
8+
geom_point(
9+
aes(Sepal.Length, Petal.Length),
10+
data = data.frame(
11+
Sepal.Length = 1:10,
12+
Petal.Length = rnorm(10),
13+
regularization = "# nearest neighbors", # This VALUE becomes the selector name
14+
parameter = 1:10
15+
),
16+
clickSelects = c(regularization = "parameter")
17+
)
18+
)
19+
20+
expect_error(
21+
animint2dir(viz, open.browser = FALSE),
22+
"Invalid character\\(s\\) in selector name\\(s\\)",
23+
info = "Selector names with '#' should cause an error"
24+
)
25+
26+
expect_error(
27+
animint2dir(viz, open.browser = FALSE),
28+
"# nearest neighbors",
29+
info = "Error message should mention the problematic selector name"
30+
)
31+
})
32+
33+
test_that("selector name with @ causes error in clickSelects", {
34+
viz <- list(
35+
plot1 = ggplot() +
36+
geom_point(
37+
aes(Sepal.Length, Petal.Length),
38+
data = data.frame(
39+
Sepal.Length = 1:10,
40+
Petal.Length = rnorm(10),
41+
regularization = "model@version1", # This VALUE becomes selector name
42+
parameter = 1:10
43+
),
44+
clickSelects = c(regularization = "parameter")
45+
)
46+
)
47+
48+
expect_error(
49+
animint2dir(viz, open.browser = FALSE),
50+
"Invalid character\\(s\\) in selector name\\(s\\)",
51+
info = "Selector names with '@' should cause an error"
52+
)
53+
54+
expect_error(
55+
animint2dir(viz, open.browser = FALSE),
56+
"model@version",
57+
info = "Error message should mention the problematic selector"
58+
)
59+
})
60+
61+
test_that("selector name with ! causes error", {
62+
viz <- list(
63+
plot1 = ggplot() +
64+
geom_point(
65+
aes(x=1:10, y=rnorm(10)),
66+
data = data.frame(
67+
model = "model!important",
68+
parameter = 1:10
69+
),
70+
clickSelects = c(model = "parameter")
71+
)
72+
)
73+
74+
expect_error(
75+
animint2dir(viz, open.browser = FALSE),
76+
"Invalid character\\(s\\) in selector name\\(s\\)",
77+
info = "Selector names with '!' should cause an error"
78+
)
79+
})
80+
81+
test_that("valid selector names work with clickSelects", {
82+
viz <- list(
83+
plot1 = ggplot() +
84+
geom_point(
85+
aes(x=1:10, y=rnorm(10)),
86+
data = data.frame(
87+
regularization = "polynomial_degree", # Valid name
88+
parameter = 0:9
89+
),
90+
clickSelects = c(regularization = "parameter")
91+
)
92+
)
93+
94+
# Should NOT error
95+
info <- animint2dir(viz, open.browser = FALSE)
96+
expect_true(TRUE, info = "Valid selector names should not cause errors")
97+
})
98+
99+
test_that("selector names with spaces work", {
100+
viz <- list(
101+
plot1 = ggplot() +
102+
geom_point(
103+
aes(x=1:10, y=rnorm(10)),
104+
data = data.frame(
105+
regularization = "nearest neighbors", # spaces are OK
106+
parameter = 1:10
107+
),
108+
clickSelects = c(regularization = "parameter")
109+
)
110+
)
111+
112+
# Should NOT error - spaces are fine
113+
info <- animint2dir(viz, open.browser = FALSE)
114+
expect_true(TRUE, info = "Selector names with spaces should work")
115+
})
116+
117+
test_that("multiple values with invalid characters all reported", {
118+
viz <- list(
119+
plot1 = ggplot() +
120+
geom_point(
121+
aes(x=1:10, y=rnorm(10)),
122+
data = data.frame(
123+
regularization = rep(c("#bad", "!worse"), 5), # Both have invalid chars
124+
parameter = 1:10
125+
),
126+
clickSelects = c(regularization = "parameter")
127+
)
128+
)
129+
130+
error_msg <- tryCatch(
131+
animint2dir(viz, open.browser = FALSE),
132+
error = function(e) as.character(e$message)
133+
)
134+
135+
# Should catch both bad selector names
136+
expect_match(error_msg, "Invalid character", info = "Should report invalid characters")
137+
expect_match(error_msg, "#bad|!worse", info = "Should mention at least one problematic selector")
138+
})

0 commit comments

Comments
 (0)