1111# ' Specifies variable(s) to be analyzed for outliers.
1212# ' @param categorical_var (`data_extract_spec` or `list` of multiple `data_extract_spec`) optional,
1313# ' specifies the categorical variable(s) to split the selected outlier variables on.
14+ # ' @param table_decorator (`teal_transform_module`, `language` or `function`) optional,
15+ # ' decorator for the table.
16+ # ' @param boxplot_decorator (`teal_transform_module`, `language` or `function`) optional,
17+ # ' decorator for the box plot.
18+ # ' @param violin_decorator (`teal_transform_module`, `language` or `function`) optional,
19+ # ' decorator for the violing plot.
20+ # ' @param density_decorator (`teal_transform_module`, `language` or `function`) optional,
21+ # ' decorator for the density plot.
22+ # ' @param cum_dist_decorator (`teal_transform_module`, `language` or `function`) optional,
23+ # ' decorator for the cumulative distribution plot.
1424# '
1525# ' @templateVar ggnames "Boxplot","Density Plot","Cumulative Distribution Plot"
1626# ' @template ggplot2_args_multi
1727# '
1828# ' @inherit shared_params return
1929# '
30+ # ' @section Decorating the tables and plots:
31+ # ' The act of decoration means to modify the tables and plots output by this module.
32+ # ' The module lets app developers do it by allowing them to execute arbitrary R code
33+ # ' that modifies the objects displayed by the module.
34+ # '
35+ # ' The decorating parameters accept one of the three types of objects as decorators:
36+ # ' * `teal_transform_module`
37+ # ' * `language`
38+ # ' * `function` of the output object.
39+ # '
40+ # ' The module will execute the code contained in either of the objects just before
41+ # ' rendering the outputs in the application. This lets app developers modify
42+ # ' features like: titles, labels, sizes, limits, etc. of the rendered tables
43+ # ' and plots.
44+ # '
45+ # ' The app developer can pass either of the types. See examples for the proper
46+ # ' use of each of the type of the decorator.
47+ # '
48+ # ' IMPORTANT
49+ # ' The `language` and `teal_transform_module` decorators are required by the module
50+ # ' to overwrite the binding of the output, otherwise the effect of the decorator
51+ # ' is not going to be visible. E.g.:
52+ # '
53+ # ' ```{r}
54+ # ' # The module uses `g` variable for the plot
55+ # '
56+ # ' # Will work
57+ # ' lang_dec <- quote({
58+ # ' g <- g + ggplot2::ggtitle("A new title")
59+ # ' })
60+ # '
61+ # ' # Will not work because the decorater overwrites `plot` instead of `g`
62+ # ' lang_dec <- quote({
63+ # ' plot <- g + ggplot2::ggtitle("A new title")
64+ # ' })
65+ # ' ```
66+ # '
67+ # ' The app developer can discover the bindings used for the outputs by inspecting
68+ # ' the R code generated by the module.
69+ # '
2070# ' @examples
2171# ' library(teal.widgets)
2272# '
120170# ' shinyApp(app$ui, app$server)
121171# ' }
122172# '
173+ # ' # Decorators
174+ # ' function_decorator <- function(p) {
175+ # ' p <- p + ggplot2::ggtitle("A new title")
176+ # ' }
177+ # '
178+ # ' quote_decorator <- quote({
179+ # ' g <- g + ggplot2::ggtitle("A new title")
180+ # ' })
181+ # '
182+ # ' module_decorator <- teal_transform_module(
183+ # ' ui = function(id) NULL,
184+ # ' srv = function(id, data) {
185+ # ' within(
186+ # ' data,
187+ # ' {
188+ # ' g <- g + ggplot2::ggtitle("A new title")
189+ # ' }
190+ # ' )
191+ # ' }
192+ # ' )
193+ # '
123194# ' @export
124195# '
125196tm_outliers <- function (label = " Outliers Module" ,
@@ -130,13 +201,24 @@ tm_outliers <- function(label = "Outliers Module",
130201 plot_height = c(600 , 200 , 2000 ),
131202 plot_width = NULL ,
132203 pre_output = NULL ,
133- post_output = NULL ) {
204+ post_output = NULL ,
205+ table_decorator = teal_transform_module(),
206+ boxplot_decorator = teal_transform_module(),
207+ violin_decorator = teal_transform_module(),
208+ density_decorator = teal_transform_module(),
209+ cum_dist_decorator = teal_transform_module()) {
134210 message(" Initializing tm_outliers" )
135211
136212 # Normalize the parameters
137213 if (inherits(outlier_var , " data_extract_spec" )) outlier_var <- list (outlier_var )
138214 if (inherits(categorical_var , " data_extract_spec" )) categorical_var <- list (categorical_var )
139215 if (inherits(ggplot2_args , " ggplot2_args" )) ggplot2_args <- list (default = ggplot2_args )
216+
217+ table_decorator <- decorate_teal_data(table_decorator , output_name = " summary_table" )
218+ boxplot_decorator <- decorate_teal_data(boxplot_decorator , output_name = " g" )
219+ violin_decorator <- decorate_teal_data(violin_decorator , output_name = " g" )
220+ density_decorator <- decorate_teal_data(density_decorator , output_name = " g" )
221+ cum_dist_decorator <- decorate_teal_data(cum_dist_decorator , output_name = " g" )
140222
141223 # Start of assertions
142224 checkmate :: assert_string(label )
@@ -177,15 +259,18 @@ tm_outliers <- function(label = "Outliers Module",
177259 categorical_var = categorical_var
178260 )
179261
262+
180263 ans <- module(
181264 label = label ,
182265 server = srv_outliers ,
183266 server_args = c(
184267 data_extract_list ,
185- list (plot_height = plot_height , plot_width = plot_width , ggplot2_args = ggplot2_args )
268+ list (
269+ plot_height = plot_height , plot_width = plot_width , ggplot2_args = ggplot2_args ,
270+ decorators = list (table = table_decorator , boxplot = boxplot_decorator , violin = violin_decorator , density = density_decorator , cum_dist = cum_dist_decorator ))
186271 ),
187272 ui = ui_outliers ,
188- ui_args = args ,
273+ ui_args = c( args ) ,
189274 datanames = teal.transform :: get_extract_datanames(data_extract_list )
190275 )
191276 attr(ans , " teal_bookmarkable" ) <- TRUE
@@ -198,24 +283,36 @@ ui_outliers <- function(id, ...) {
198283 ns <- NS(id )
199284 is_single_dataset_value <- teal.transform :: is_single_dataset(args $ outlier_var , args $ categorical_var )
200285
286+
201287 teal.widgets :: standard_layout(
202288 output = teal.widgets :: white_small_well(
203289 uiOutput(ns(" total_outliers" )),
204290 DT :: dataTableOutput(ns(" summary_table" )),
291+ ui_teal_data(ns(" table_decorator" ), args $ table_decorator ),
205292 uiOutput(ns(" total_missing" )),
206293 tags $ br(), tags $ hr(),
207294 tabsetPanel(
208295 id = ns(" tabs" ),
209296 tabPanel(
210297 " Boxplot" ,
298+ conditionalPanel(
299+ condition = sprintf(" input['%s'] == 'Box plot'" , ns(" boxplot_alts" )),
300+ ui_teal_data(ns(" boxplot_decorator" ), args $ boxplot_decorator )
301+ ),
302+ conditionalPanel(
303+ condition = sprintf(" input['%s'] == 'Violin plot'" , ns(" boxplot_alts" )),
304+ ui_teal_data(ns(" violin_decorator" ), args $ violin_decorator )
305+ ),
211306 teal.widgets :: plot_with_settings_ui(id = ns(" box_plot" ))
212307 ),
213308 tabPanel(
214309 " Density Plot" ,
310+ ui_teal_data(ns(" density_decorator" ), args $ density_decorator ),
215311 teal.widgets :: plot_with_settings_ui(id = ns(" density_plot" ))
216312 ),
217313 tabPanel(
218314 " Cumulative Distribution Plot" ,
315+ ui_teal_data(ns(" cum_dist_decorator" ), args $ cum_dist_decorator ),
219316 teal.widgets :: plot_with_settings_ui(id = ns(" cum_density_plot" ))
220317 )
221318 ),
@@ -326,7 +423,7 @@ ui_outliers <- function(id, ...) {
326423
327424# Server function for the outliers module
328425srv_outliers <- function (id , data , reporter , filter_panel_api , outlier_var ,
329- categorical_var , plot_height , plot_width , ggplot2_args ) {
426+ categorical_var , plot_height , plot_width , ggplot2_args , decorators ) {
330427 with_reporter <- ! missing(reporter ) && inherits(reporter , " Reporter" )
331428 with_filter <- ! missing(filter_panel_api ) && inherits(filter_panel_api , " FilterPanelAPI" )
332429 checkmate :: assert_class(data , " reactive" )
@@ -669,13 +766,14 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var,
669766 qenv
670767 })
671768
769+ decorated_table_q <- srv_teal_data(id = " table_decorator" , data = common_code_q , data_module = decorators $ table , modules = module())
672770 output $ summary_table <- DT :: renderDataTable(
673771 expr = {
674772 if (iv_r()$ is_valid()) {
675773 categorical_var <- as.vector(merged $ anl_input_r()$ columns_source $ categorical_var )
676774 if (! is.null(categorical_var )) {
677775 DT :: datatable(
678- common_code_q ()[[" summary_table" ]],
776+ decorated_table_q ()[[" summary_table" ]],
679777 options = list (
680778 dom = " t" ,
681779 autoWidth = TRUE ,
@@ -1024,18 +1122,30 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var,
10241122 }
10251123 })
10261124
1027- boxplot_r <- reactive({
1125+ undecorated_boxplot_r <- reactive({
10281126 teal :: validate_inputs(iv_r())
10291127 boxplot_q()[[" g" ]]
10301128 })
1031- density_plot_r <- reactive({
1129+ decorated_boxplot_r <- srv_teal_data(id = " boxplot_decorator" , data = undecorated_boxplot_r , data_module = decorators $ boxplot , modules = module())
1130+ decorated_violin_r <- srv_teal_data(id = " violin_decorator" , data = undecorated_boxplot_r , data_module = decorators $ violin , modules = module())
1131+ boxplot_r <- reactive({
1132+ req(input $ boxplot_alts )
1133+ if (input $ boxplot_alts == " Box plot" ) {
1134+ decorated_boxplot_r()
1135+ } else {
1136+ decorated_violin_r()
1137+ }
1138+ })
1139+ undecorated_density_plot_r <- reactive({
10321140 teal :: validate_inputs(iv_r())
10331141 density_plot_q()[[" g" ]]
10341142 })
1035- cumulative_plot_r <- reactive({
1143+ density_plot_r <- srv_teal_data(id = " density_decorator" , data = undecorated_density_plot_r , data_module = decorators $ density , modules = module())
1144+ undecorated_cumulative_plot_r <- reactive({
10361145 teal :: validate_inputs(iv_r())
10371146 cumulative_plot_q()[[" g" ]]
10381147 })
1148+ cumulative_plot_r <- srv_teal_data(id = " cum_dist_decorator" , data = undecorated_cumulative_plot_r , data_module = decorators $ cum_dist , modules = module())
10391149
10401150 box_pws <- teal.widgets :: plot_with_settings_srv(
10411151 id = " box_plot" ,
0 commit comments