--- title: "Custom `ggplot2` arguments module" author: "Maciej NasiƄski" output: rmarkdown::html_vignette runtime: shiny vignette: > %\VignetteIndexEntry{Custom `ggplot2` arguments module} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- This vignette will guide you through implementation of custom `ggplot2::labs` and `ggplot2::theme` for `ggplot2` graphics based modules. We will enable 2 ways of updating `ggplot2::labs` and `ggplot2::theme` by the end users. The `ggplot2` specification could be updated with the `teal.ggplot2_args` `options` variable or a `ggplot2_args` argument in a `tm_g_*` module. We still take into account default specification set up by the module creator in the server function, which has the lowest priority. The implementation should consist of 5 steps: 1. Adding a `ggplot2_args` argument to the `tm_g_*` function and then its server function. The default should be set to the `ggplot2_args(labs = list(), theme = list())` function for single plot. and `list(default = ggplot2_args(labs = list(), theme = list()))` multi-plot modules. 2. Adding a validation (e.g. `stopifnot` or `checkmate`) of the `ggplot2_args` argument to the `tm_*` function. The validation is more complex for multi-plot modules, where the `ggplot2_args` could be a `list`. The module creator has to provide a list of plots names, which should be validated at this step and added to the `param` field in `roxygen2`. For multi-plot modules the step `if (is_ggplot2_args) ggplot2_args <- list(default = ggplot2_args)` is recommended. 3. Aggregating and reducing all `ggplot2_args` sources with `resolve_ggplot2_args()`. 4. Usage of the `parse_ggplot2_args()` function which will parse inputs to list of expressions. 5. Adding the created expression to the last chunk of a plot. `Reduce(function(x, y) call("+", x, y), list(...)` function could be helpful at this step. The `resolve_ggplot2_args()` function picks the first non NULL value for each argument, checking in order: 1. `ggplot2_args` argument provided by the end user. For multi-plot case, per plot (`user_plot`) and then default (`user_default`) setup. 2. Global R variable (`options`), `teal.ggplot2_args`. 3. `module_plot` which is a developer setup. ### Additional topics When a more complex `ggplot2` object has to be used inside the `teal.widgets::ggplot2_args` function, then a `base::quote` function would prevent an object expansion in Show R Code. For example the `ggplot2::element_text` function returns a complex object, then we should use code like `teal.widgets::ggplot2_args(theme = list(plot.title = quote(ggplot2::element_text(size = 20))))` to prevent Show R Code expansion. If you get a `promise already under evaluation: recursive default argument reference or earlier problems?` error, then probably your function argument has the same name as a function which is an input for it. To solve the problem please use `::` to prefix it directly to a specific package, like `new_fun <- function(ggplot2_args = teal.widgets::ggplot2_args())`. ## Loading libraries and data ```{r echo=TRUE, warning=FALSE, message=FALSE} library(shiny) library(ggplot2) library(teal.widgets) options("teal.ggplot2_args" = teal.widgets::ggplot2_args(labs = list(caption = "Caption from options"))) user_ggplot2_args <- list( default = ggplot2_args( labs = list(title = "User default title"), theme = list(legend.position = "right", legend.direction = "vertical") ), plot1 = ggplot2_args( labs = list(title = "User title"), theme = list(legend.position = "right", legend.direction = "vertical") ) ) app <- shinyApp( ui = fluidPage( shinyjs::useShinyjs(), div(plotOutput("plot1")) ), server = function(input, output, session) { dev_ggplot2_args <- ggplot2_args( labs = list(subtitle = "Dev substitle"), theme = list(legend.position = "none") ) f_ggplot2_expr <- parse_ggplot2_args( resolve_ggplot2_args( user_plot = user_ggplot2_args$plot1, user_default = user_ggplot2_args$default, module_plot = dev_ggplot2_args ) ) plot_expr <- substitute( expr = { gg <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Length, color = Species)) + geom_point() + ggplot_expr_labs + ggplot_expr_theme print(gg) }, env = list(ggplot_expr_labs = f_ggplot2_expr$labs, ggplot_expr_theme = f_ggplot2_expr$theme) ) print(plot_expr) output$plot1 <- renderPlot(eval(plot_expr)) } ) ``` ```{r echo=TRUE, eval = FALSE} shinyApp(app$ui, app$server) ```