diff --git a/R/z_animint.R b/R/z_animint.R index 3beb7dd7c..0f6c371ef 100644 --- a/R/z_animint.R +++ b/R/z_animint.R @@ -21,6 +21,9 @@ parsePlot <- function(meta, plot, plot.name){ } } + ## Validate facet variables before ggplot_build (Issue #168) + checkFacetVariables(plot) + built <- ggplot_build(plot) plot.info <- list() diff --git a/R/z_animintHelpers.R b/R/z_animintHelpers.R index f36864547..a0844e246 100644 --- a/R/z_animintHelpers.R +++ b/R/z_animintHelpers.R @@ -1066,3 +1066,40 @@ selectSSandCS <- function(aesthetics_list){ ## TODO: how to handle showSelected$ignored in prev animint code?? return(aes.list) } + +checkFacetVariables <- function(plot){ + facet <- plot$facet + if(is.null(facet)) return(invisible()) + facet_class <- class(facet)[1] + facet_vars <- character(0) + if(facet_class == "wrap" && length(facet$facets) > 0){ + facet_vars <- names(facet$facets) + facet_vars <- facet_vars[facet_vars != "."] + }else if(facet_class == "grid"){ + row_vars <- names(facet$rows) + col_vars <- names(facet$cols) + facet_vars <- c(row_vars, col_vars) + facet_vars <- facet_vars[facet_vars != "."] + } + if(length(facet_vars) == 0) return(invisible()) + all_data_vars <- character(0) + for(layer in plot$layers){ + if(length(layer$data) > 0){ + all_data_vars <- c(all_data_vars, names(layer$data)) + } + } + if(length(plot$data) > 0){ + all_data_vars <- c(all_data_vars, names(plot$data)) + } + all_data_vars <- unique(all_data_vars) + missing_vars <- setdiff(facet_vars, all_data_vars) + if(length(missing_vars) > 0){ + stop(sprintf( + "Facet variable%s not found in data: %s\nAvailable columns: %s\nUse string notation like facet_wrap(\"var\") instead of formula notation facet_wrap(. ~ var)", + if(length(missing_vars) > 1) "s" else "", + paste(missing_vars, collapse = ", "), + paste(all_data_vars, collapse = ", ") + )) + } + invisible() +} diff --git a/tests/testthat/test-renderer-facet-error-messages.R b/tests/testthat/test-renderer-facet-error-messages.R new file mode 100644 index 000000000..4add970e7 --- /dev/null +++ b/tests/testthat/test-renderer-facet-error-messages.R @@ -0,0 +1,32 @@ +acontext("Facet error messages") +test_that("facet_wrap formula with missing variable gives clear error", { + viz <- list( + scatter = ggplot() + + facet_wrap(. ~ NonExistentColumn) + + geom_point(aes(Sepal.Length, Petal.Length), data = iris) + ) + expect_error( + animint2dir(viz, out.dir = tempfile(), open.browser = FALSE), + "NonExistentColumn" + ) +}) +test_that("facet_grid formula with missing variable gives clear error", { + viz <- list( + scatter = ggplot() + + facet_grid(. ~ MissingVar) + + geom_point(aes(Sepal.Length, Petal.Length), data = iris) + ) + expect_error( + animint2dir(viz, out.dir = tempfile(), open.browser = FALSE), + "MissingVar" + ) +}) +test_that("facet_wrap string notation works", { + viz <- list( + scatter = ggplot() + + facet_wrap("Species") + + geom_point(aes(Sepal.Length, Petal.Length), data = iris) + ) + info <- animint2dir(viz, out.dir = tempfile(), open.browser = FALSE) + expect_true(file.exists(file.path(info$out.dir, "index.html"))) +})