From 96e3945a08cb96ff230d49de0672fb9d8c9298b9 Mon Sep 17 00:00:00 2001 From: vedhav Date: Tue, 17 Sep 2024 15:43:24 +0200 Subject: [PATCH 001/108] push local changes --- DESCRIPTION | 2 +- R/module_data_summary.R | 30 +++-------------- R/module_nested_tabs.R | 34 ++++++++++++------- R/module_teal.R | 17 ++++------ R/module_transform_data.R | 28 +++++----------- R/show_rcode_modal.R | 3 +- R/utils.R | 6 ++-- inst/css/custom.css | 69 +++------------------------------------ inst/css/sidebar.css | 40 +++++++++++++++++++---- inst/css/tooltip.css | 37 --------------------- inst/css/validation.css | 3 +- inst/js/sidebar.js | 34 ------------------- 12 files changed, 86 insertions(+), 217 deletions(-) delete mode 100644 inst/css/tooltip.css delete mode 100644 inst/js/sidebar.js diff --git a/DESCRIPTION b/DESCRIPTION index f668bd7eec..d26a257cbc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,6 +40,7 @@ Depends: teal.data (> 0.6.0.9007), teal.slice (>= 0.5.1.9009) Imports: + bslib (>= 0.8.0), checkmate (>= 2.1.0), future (>= 1.33.2), jsonlite, @@ -57,7 +58,6 @@ Imports: teal.widgets (>= 0.4.0), utils Suggests: - bslib, knitr (>= 1.42), MultiAssayExperiment, R6, diff --git a/R/module_data_summary.R b/R/module_data_summary.R index 407edc0aba..5f11bdc578 100644 --- a/R/module_data_summary.R +++ b/R/module_data_summary.R @@ -23,31 +23,11 @@ NULL ui_data_summary <- function(id) { ns <- NS(id) content_id <- ns("filters_overview_contents") - tags$div( - id = id, - class = "well", - tags$div( - class = "row", - tags$div( - class = "col-sm-9", - tags$label("Active Filter Summary", class = "text-primary mb-4") - ), - tags$div( - class = "col-sm-3", - tags$i( - class = "remove pull-right fa fa-angle-down", - style = "cursor: pointer;", - title = "fold/expand data summary panel", - onclick = sprintf("togglePanelItems(this, '%s', 'fa-angle-right', 'fa-angle-down');", content_id) - ) - ) - ), - tags$div( - id = content_id, - tags$div( - class = "teal_active_summary_filter_panel", - tableOutput(ns("table")) - ) + bslib::accordion( + bslib::accordion_panel( + "Active Filter Summary", + icon = icon("fas fa-list"), + tableOutput(ns("table")) ) ) } diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 7bc3a72fbf..bd576b1ea2 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -58,7 +58,7 @@ ui_teal_module.teal_modules <- function(id, modules, depth = 0L) { function(module_id) { module_label <- modules$children[[module_id]]$label if (is.null(module_label)) { - module_label <- icon("fas fa-database") + module_label <- icon("fas fa-list") } tabPanel( title = module_label, @@ -100,18 +100,28 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { class = "teal_module", uiOutput(ns("data_reactive"), inline = TRUE), tagList( - if (depth >= 2L) tags$div(style = "mt-6"), + if (depth >= 2L) tags$div(), if (!is.null(modules$datanames)) { - fluidRow( - column(width = 9, ui_teal, class = "teal_primary_col"), - column( - width = 3, - ui_data_summary(ns("data_summary")), - ui_filter_data(ns("filter_panel")), - if (length(modules$transformers) > 0 && !isTRUE(attr(modules$transformers, "custom_ui"))) { - ui_transform_data(ns("data_transform"), transforms = modules$transformers, class = "well") - }, - class = "teal_secondary_col" + div( + bslib::layout_sidebar( + sidebar = bslib::sidebar( + class = "teal-sidebar", + width = 350, + tags$div( + ui_data_summary(ns("data_summary")), + ui_filter_data(ns("filter_panel")), + if (length(modules$transformers) > 0 && !isTRUE(attr(modules$transformers, "custom_ui"))) { + bslib::accordion( + bslib::accordion_panel( + "Transform Data", + icon = icon("fas fa-pen-to-square"), + ui_transform_data(ns("data_transform"), transforms = modules$transformers) + ) + ) + } + ) + ), + ui_teal ) ) } else { diff --git a/R/module_teal.R b/R/module_teal.R index 391782cb9d..72f8b91f14 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -80,8 +80,10 @@ ui_teal <- function(id, tags$div( icon("arrows-rotate", class = "fa-spin", prefer_type = "solid"), "Computing ...", - # CSS defined in `custom.css` - class = "shinybusymessage" + style = "position: fixed; bottom: 0; right: 0; + width: 140px; margin: 15px; padding: 5px 0 5px 10px; + text-align: left; font-weight: bold; font-size: 100%; + color: #ffffff; background-color: #347ab7; z-index: 105;" ) ) @@ -92,13 +94,13 @@ ui_teal <- function(id, } tabs_elem <- ui_teal_module(id = ns("teal_modules"), modules = modules) - fluidPage( + bslib::page_fluid( id = id, title = title, theme = get_teal_bs_theme(), include_teal_css_js(), tags$header(header), - tags$hr(class = "my-2"), + tags$hr(style = "margin-top: 0.5rem; margin-bottom: 0.5rem;"), shiny_busy_message_panel, tags$div( id = ns("tabpanel_wrapper"), @@ -109,13 +111,6 @@ ui_teal <- function(id, id = ns("options_buttons"), style = "position: absolute; right: 10px;", bookmark_panel_ui, - tags$button( - class = "btn action-button filter_hamburger", # see sidebar.css for style filter_hamburger - href = "javascript:void(0)", - onclick = sprintf("toggleFilterPanel('%s');", ns("tabpanel_wrapper")), - title = "Toggle filter panel", - icon("fas fa-bars") - ), ui_snapshot_manager_panel(ns("snapshot_manager_panel")), ui_filter_manager_panel(ns("filter_manager_panel")) ), diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 30107ef88d..5ea347bb73 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -26,25 +26,15 @@ ui_transform_data <- function(id, transforms, class = "well") { function(name) { data_mod <- transforms[[name]] wrapper_id <- ns(sprintf("wrapper_%s", name)) - div( # todo: accordion? - # class .teal_validated changes the color of the boarder on error in ui_validate_reactive_teal_data - # For details see tealValidate.js file. - class = c(class, "teal_validated"), - title = attr(data_mod, "label"), - tags$span( - class = "text-primary mb-4", - icon("fas fa-square-pen"), - attr(data_mod, "label") - ), - tags$i( - class = "remove pull-right fa fa-angle-down", - style = "cursor: pointer;", - title = "fold/expand transform panel", - onclick = sprintf("togglePanelItems(this, '%s', 'fa-angle-right', 'fa-angle-down');", wrapper_id) - ), - div( - id = wrapper_id, - ui_teal_data(id = ns(name), data_module = transforms[[name]]) + bslib::accordion( + class = "teal-transform-accordian", # todo: make transform accordian + bslib::accordion_panel( + attr(data_mod, "label"), + icon = icon("fas fa-square-pen"), + div( + id = wrapper_id, + ui_teal_data(id = ns(name), data_module = transforms[[name]]) + ) ) ) } diff --git a/R/show_rcode_modal.R b/R/show_rcode_modal.R index 0f7fe88228..55ee8b1975 100644 --- a/R/show_rcode_modal.R +++ b/R/show_rcode_modal.R @@ -27,8 +27,7 @@ show_rcode_modal <- function(title = NULL, rcode, session = getDefaultReactiveDo tagList( tags$div( actionButton(ns("copyRCode"), "Copy to Clipboard", `data-clipboard-target` = paste0("#", ns("r_code"))), - modalButton("Dismiss"), - style = "mb-4" + modalButton("Dismiss") ), tags$div(tags$pre(id = ns("r_code"), rcode)), ), diff --git a/R/utils.R b/R/utils.R index 36cd81f564..fed86d3e41 100644 --- a/R/utils.R +++ b/R/utils.R @@ -26,16 +26,16 @@ get_teal_bs_theme <- function() { bs_theme <- getOption("teal.bs_theme") if (is.null(bs_theme)) { - return(NULL) + bs_theme <- bslib::bs_theme() } if (!checkmate::test_class(bs_theme, "bs_theme")) { warning( "Assertion on 'teal.bs_theme' option value failed: ", checkmate::check_class(bs_theme, "bs_theme"), - ". The default Shiny Bootstrap theme will be used." + ". The default bslib Bootstrap theme will be used." ) - return(NULL) + bs_theme <- bslib::bs_theme() } bs_theme diff --git a/inst/css/custom.css b/inst/css/custom.css index bf2e8ad95b..e471d63a06 100644 --- a/inst/css/custom.css +++ b/inst/css/custom.css @@ -1,71 +1,7 @@ /* teal custom css */ -.flex-grow { - flex-grow: 1; -} - -.my-2 { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mt-6 { - margin-top: 1.5rem; -} - -.overflow-auto { - overflow: auto; -} - -/* display waiting box at bottom right corner when Shiny is computing */ -.shinybusymessage { - position: fixed; - bottom: 0; - right: 0; - width: 140px; - margin: 15px; - padding: 5px 0 5px 10px; - text-align: left; - font-weight: bold; - font-size: 100%; - color: #ffffff; - background-color: #347ab7; - z-index: 105; -} - -/* DataTable connected if any exists */ -.dataTables_wrapper { - overflow: auto; -} - -/* to set the alignment of pickerInput dropdowns */ -.dropdown-menu.open { - width: 100%; -} - /* per tag */ -.nav { - display: flex; - flex-wrap: wrap; - width: calc(100% - 160px); -} - -header { - display: flow-root; -} - -footer { - text-align: left; - padding: 5px; - font-size: 0.8em; - opacity: 0.6; -} - #teal_secondary_col .well { margin: 0 0 15px 0; } @@ -95,3 +31,8 @@ body > div:has(~ #shiny-modal-wrapper .blur_background) { #shiny-modal:has(.hide_background) { background-color: white; } + +.btn { + --bs-btn-padding-x: 0.7em; + --bs-btn-padding-y: 0.4em; +} diff --git a/inst/css/sidebar.css b/inst/css/sidebar.css index d0e21ff7ca..fca7dbe235 100644 --- a/inst/css/sidebar.css +++ b/inst/css/sidebar.css @@ -1,6 +1,5 @@ /* teal sidebar css */ - .filter_hamburger, .wunder_bar_button { font-size: 16px; @@ -8,10 +7,6 @@ background-color: transparent !important; } -.filter_hamburger { - float: right !important; -} - .badge-count { padding-left: 1em; padding-right: 1em; @@ -19,7 +14,7 @@ -moz-border-radius: 1em; border-radius: 1em; font-size: 0.7em; - padding: 0 .5em; + padding: 0 0.5em; vertical-align: top; margin-left: -1em; } @@ -47,7 +42,7 @@ a.disabled { flex: 1 1 80%; } -.manager_table_row *+* { +.manager_table_row * + * { flex: 0 0 0px; padding: 0em 1.5em; } @@ -55,3 +50,34 @@ a.disabled { .manager_placeholder { margin-top: 1em; } + +.teal-sidebar { + --_padding: 0px; +} + +.teal-sidebar .accordion-button { + --bs-accordion-btn-bg: #ededed; + --bs-accordion-active-bg: #ededed; + --bs-accordion-btn-padding-x: 1rem; + --bs-accordion-btn-padding-y: 0.8rem; +} + +.teal-sidebar .accordion { + --bs-accordion-body-padding-x: 1rem; + --bs-accordion-body-padding-y: 0.5rem; +} + +.teal-sidebar .accordion-button:hover { + --bs-accordion-btn-bg: #c5c5c5; + --bs-accordion-active-bg: #c5c5c5; +} + +.teal-transform-accordian .accordion-button { + --bs-accordion-btn-padding-y: 0.5rem; + --bs-accordion-btn-padding-x: 0.5rem; +} + +.teal-transform-accordian .accordion-button:not(.collapsed)::after, +.teal-transform-accordian .accordion-button::after { + display: none; +} diff --git a/inst/css/tooltip.css b/inst/css/tooltip.css deleted file mode 100644 index 1540a72253..0000000000 --- a/inst/css/tooltip.css +++ /dev/null @@ -1,37 +0,0 @@ -/* teal tooltip css */ -/* -Thanks to this css all teal applications can use div.teal-tooltip with -icon.tooltiptext inside to make a hoover info. Search for for "teal-tooltip" in the -organization to find example usage -*/ - -/* Tooltip container */ -.teal-tooltip { - position: relative; - display: inline-block; - border-bottom: 1px dotted black; -} - -/* Tooltip text */ -.teal-tooltip .tooltiptext { - visibility: hidden; - background-color: black; - color: #fff; - text-align: center; - padding: 10px; - border-radius: 6px; - opacity: 0; - z-index: 10; - position: absolute; - width: 400px; - left: 100%; - top: -5px; - transition: all 0s ease 0s; -} - -/* Show the tooltip text when you mouse over the tooltip container */ -.teal-tooltip:hover .tooltiptext { - visibility: visible; - opacity: 1; - transition: opacity 0.3s ease 0.4s; -} diff --git a/inst/css/validation.css b/inst/css/validation.css index 965b982eae..665fac2d73 100644 --- a/inst/css/validation.css +++ b/inst/css/validation.css @@ -9,7 +9,6 @@ border-radius: 4px; } - .teal_validated .teal-output-warning { color: #888; } @@ -38,7 +37,7 @@ .teal_primary_col .teal_validated:has(.shiny-output-error), .teal_primary_col .teal_validated:has(.teal-output-warning) { margin: 1em 0 1em 0; - padding: .5em 0 .5em .5em; + padding: 0.5em 0 0.5em 0.5em; } .teal_primary_col > .teal_validated:has(.teal-output-warning), diff --git a/inst/js/sidebar.js b/inst/js/sidebar.js deleted file mode 100644 index 20095c2468..0000000000 --- a/inst/js/sidebar.js +++ /dev/null @@ -1,34 +0,0 @@ -// used to collapse and expand the filter panel in teal apps -/* -resize is placed at end of functions -b/c in embedded apps it will throw errors that cause the function to exit early -*/ -var filter_open = {}; -const hideSidebar = (tabpanel_wrapper) => { - $(`#${tabpanel_wrapper} .teal_secondary_col`).fadeOut(1); - $(`#${tabpanel_wrapper} .teal_primary_col`) - .removeClass("col-sm-9") - .addClass("col-sm-12"); -}; -const showSidebar = (tabpanel_wrapper) => { - $(`#${tabpanel_wrapper} .teal_primary_col`) - .removeClass("col-sm-12") - .addClass("col-sm-9"); - $(`#${tabpanel_wrapper} .teal_secondary_col`).fadeIn(650); - $(`#${tabpanel_wrapper} .teal_secondary_col`).trigger("shown"); -}; -const toggleFilterPanel = (tabpanel_wrapper) => { - if (filter_open[tabpanel_wrapper] === undefined) { - filter_open[tabpanel_wrapper] = true; - } - if ( - filter_open[tabpanel_wrapper] && - !$(`#${tabpanel_wrapper} .teal_secondary_col`).is(":visible") - ) { - showSidebar(tabpanel_wrapper); - return; - } - filter_open[tabpanel_wrapper] = !filter_open[tabpanel_wrapper]; - if (filter_open[tabpanel_wrapper]) showSidebar(tabpanel_wrapper); - else hideSidebar(tabpanel_wrapper); -}; From 20bb0f095df27414bd2366cb5b40a6208f4a34f5 Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 26 Sep 2024 07:13:30 +0530 Subject: [PATCH 002/108] feat: add additional sidebar toggle buttons --- R/module_data_summary.R | 9 +----- R/module_nested_tabs.R | 70 ++++++++++++++++++++++++++++++++++++++--- inst/css/sidebar.css | 28 +++++++++++++++++ 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/R/module_data_summary.R b/R/module_data_summary.R index 5f11bdc578..896b9eadd9 100644 --- a/R/module_data_summary.R +++ b/R/module_data_summary.R @@ -22,14 +22,7 @@ NULL #' @rdname module_data_summary ui_data_summary <- function(id) { ns <- NS(id) - content_id <- ns("filters_overview_contents") - bslib::accordion( - bslib::accordion_panel( - "Active Filter Summary", - icon = icon("fas fa-list"), - tableOutput(ns("table")) - ) - ) + tableOutput(ns("table")) } #' @rdname module_data_summary diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index bd576b1ea2..e8b954a805 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -46,12 +46,11 @@ ui_teal_module.default <- function(id, modules, depth = 0L) { ui_teal_module.teal_modules <- function(id, modules, depth = 0L) { ns <- NS(id) do.call( - tabsetPanel, + ifelse(modules$label == "root", navset_card_pill, navset_card_underline), c( # by giving an id, we can reactively respond to tab changes list( - id = ns("active_tab"), - type = if (modules$label == "root") "pills" else "tabs" + id = ns("active_tab") ), lapply( names(modules$children), @@ -60,7 +59,7 @@ ui_teal_module.teal_modules <- function(id, modules, depth = 0L) { if (is.null(module_label)) { module_label <- icon("fas fa-list") } - tabPanel( + nav_panel( title = module_label, value = module_id, # when clicked this tab value changes input$ ui_teal_module( @@ -105,13 +104,22 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { div( bslib::layout_sidebar( sidebar = bslib::sidebar( + id = ns("teal_module_sidebar"), class = "teal-sidebar", width = 350, tags$div( - ui_data_summary(ns("data_summary")), + bslib::accordion( + id = ns("data_summary_accordion"), + bslib::accordion_panel( + "Active Filter Summary", + icon = icon("fas fa-list"), + ui_data_summary(ns("data_summary")) + ) + ), ui_filter_data(ns("filter_panel")), if (length(modules$transformers) > 0 && !isTRUE(attr(modules$transformers, "custom_ui"))) { bslib::accordion( + id = ns("data_transform_accordion"), bslib::accordion_panel( "Transform Data", icon = icon("fas fa-pen-to-square"), @@ -122,6 +130,37 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ) ), ui_teal + ), + div( + id = ns("sidebar_toggle_buttons"), + class = "sidebar-toggle-buttons", + actionButton( + ns("data_summary_toggle"), + icon("fas fa-list") + ), + actionButton( + ns("data_filters_toggle"), + icon("fas fa-filter") + ), + if (length(modules$transformers) > 0) { + actionButton( + ns("data_transforms_toggle"), + icon("fas fa-pen-to-square") + ) + } + ), + tags$script( + HTML( + sprintf( + " + $(document).ready(function() { + $('#%s').insertAfter('#%s button.collapse-toggle'); + }); + ", + ns("sidebar_toggle_buttons"), + id + ) + ) ) ) } else { @@ -250,6 +289,27 @@ srv_teal_module.teal_module <- function(id, summary_table <- srv_data_summary("data_summary", module_teal_data) + observeEvent(input$data_summary_toggle, { + bslib::toggle_sidebar(id = "teal_module_sidebar", open = TRUE) + bslib::accordion_panel_open(id = "data_summary_accordion", values = TRUE) + bslib::accordion_panel_close(id = "filter_panel-filters-main_filter_accordian", values = TRUE) + bslib::accordion_panel_close(id = "data_transform_accordion", values = TRUE) + }) + + observeEvent(input$data_filters_toggle, { + bslib::toggle_sidebar(id = "teal_module_sidebar", open = TRUE) + bslib::accordion_panel_close(id = "data_summary_accordion", values = TRUE) + bslib::accordion_panel_open(id = "filter_panel-filters-main_filter_accordian", values = TRUE) + bslib::accordion_panel_close(id = "data_transform_accordion", values = TRUE) + }) + + observeEvent(input$data_transforms_toggle, { + bslib::toggle_sidebar(id = "teal_module_sidebar", open = TRUE) + bslib::accordion_panel_close(id = "data_summary_accordion", values = TRUE) + bslib::accordion_panel_close(id = "filter_panel-filters-main_filter_accordian", values = TRUE) + bslib::accordion_panel_open(id = "data_transform_accordion", values = TRUE) + }) + # Call modules. module_out <- reactiveVal(NULL) if (!inherits(modules, "teal_module_previewer")) { diff --git a/inst/css/sidebar.css b/inst/css/sidebar.css index fca7dbe235..10cd0c29a3 100644 --- a/inst/css/sidebar.css +++ b/inst/css/sidebar.css @@ -81,3 +81,31 @@ a.disabled { .teal-transform-accordian .accordion-button::after { display: none; } + +.bslib-sidebar-layout > .sidebar-toggle-buttons { + grid-column: 1 / 2; + z-index: 1000; + display: inline-flex; + position: absolute; + right: calc(var(--_icon-size) + 3rem); + top: calc(var(--_icon-size, 1rem) / 2); + height: var(--_icon-button-size, 2rem); + width: 80%; + align-items: center; + justify-content: space-between; + transition: width 0.3s ease, top 0.3s ease, right 0.3s ease; +} + +.bslib-sidebar-layout > .sidebar-toggle-buttons > button { + width: 80%; +} + +.bslib-sidebar-layout.sidebar-collapsed > .sidebar-toggle-buttons { + width: 3rem; + top: 3rem; + right: auto; + display: flex; + justify-content: space-between; + flex-direction: column; + transition: width 0.3s ease, top 0.3s ease, right 0.3s ease; +} From 2c36c7bc940373ac5cb05ccdc25e55669ef1c28f Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 26 Sep 2024 17:49:21 +0530 Subject: [PATCH 003/108] feat: show three different tabs types for different module depths --- R/module_nested_tabs.R | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index e8b954a805..8cfbea963f 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -45,8 +45,13 @@ ui_teal_module.default <- function(id, modules, depth = 0L) { #' @export ui_teal_module.teal_modules <- function(id, modules, depth = 0L) { ns <- NS(id) + first_level_modules <- sapply(modules$children, \(x) x$label) do.call( - ifelse(modules$label == "root", navset_card_pill, navset_card_underline), + switch(as.character(depth), + "0" = navset_card_pill, + "1" = navset_card_tab, + navset_card_underline + ), c( # by giving an id, we can reactively respond to tab changes list( From 45d0f1e94ec9aed394ef6e41fb62dc3623f6fb37 Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 26 Sep 2024 19:22:09 +0530 Subject: [PATCH 004/108] chore: add bslib package ns --- R/module_nested_tabs.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 8cfbea963f..8681214e2a 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -48,9 +48,9 @@ ui_teal_module.teal_modules <- function(id, modules, depth = 0L) { first_level_modules <- sapply(modules$children, \(x) x$label) do.call( switch(as.character(depth), - "0" = navset_card_pill, - "1" = navset_card_tab, - navset_card_underline + "0" = bslib::navset_card_pill, + "1" = bslib::navset_card_tab, + bslib:: navset_card_underline ), c( # by giving an id, we can reactively respond to tab changes @@ -64,7 +64,7 @@ ui_teal_module.teal_modules <- function(id, modules, depth = 0L) { if (is.null(module_label)) { module_label <- icon("fas fa-list") } - nav_panel( + bslib::nav_panel( title = module_label, value = module_id, # when clicked this tab value changes input$ ui_teal_module( From 85622dfb84dea24ca9d379bdc35d80586f74784f Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 3 Oct 2024 17:17:17 +0530 Subject: [PATCH 005/108] push local style changes --- R/module_nested_tabs.R | 15 +++++++-------- inst/css/custom.css | 8 ++++++++ inst/css/sidebar.css | 30 ++++++++++++++++++++++++++---- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 4e61c5c91c..f0dddd4e89 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -48,9 +48,9 @@ ui_teal_module.teal_modules <- function(id, modules, depth = 0L) { first_level_modules <- sapply(modules$children, \(x) x$label) do.call( switch(as.character(depth), - "0" = bslib::navset_card_pill, - "1" = bslib::navset_card_tab, - bslib:: navset_card_underline + "0" = bslib::navset_pill, + "1" = bslib::navset_tab, + bslib::navset_underline ), c( # by giving an id, we can reactively respond to tab changes @@ -106,18 +106,18 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { tagList( if (depth >= 2L) tags$div(), if (!is.null(modules$datanames)) { - div( + tagList( bslib::layout_sidebar( + class = "teal-sidebar-layout", sidebar = bslib::sidebar( id = ns("teal_module_sidebar"), class = "teal-sidebar", - width = 350, + width = 250, tags$div( bslib::accordion( id = ns("data_summary_accordion"), bslib::accordion_panel( "Active Filter Summary", - icon = icon("fas fa-list"), ui_data_summary(ns("data_summary")) ) ), @@ -127,7 +127,6 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { id = ns("data_transform_accordion"), bslib::accordion_panel( "Transform Data", - icon = icon("fas fa-pen-to-square"), ui_transform_data(ns("data_transform"), transforms = modules$transformers) ) ) @@ -159,7 +158,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { sprintf( " $(document).ready(function() { - $('#%s').insertAfter('#%s button.collapse-toggle'); + $('#%s').insertAfter('#%s > .bslib-sidebar-layout > button.collapse-toggle'); }); ", ns("sidebar_toggle_buttons"), diff --git a/inst/css/custom.css b/inst/css/custom.css index e471d63a06..c03f120f23 100644 --- a/inst/css/custom.css +++ b/inst/css/custom.css @@ -36,3 +36,11 @@ body > div:has(~ #shiny-modal-wrapper .blur_background) { --bs-btn-padding-x: 0.7em; --bs-btn-padding-y: 0.4em; } + +:root { + --bs-body-font-size: 0.875rem; +} + +.accordion-button { + font-size: 0.875rem; +} diff --git a/inst/css/sidebar.css b/inst/css/sidebar.css index 10cd0c29a3..1deedc9c6a 100644 --- a/inst/css/sidebar.css +++ b/inst/css/sidebar.css @@ -56,6 +56,7 @@ a.disabled { } .teal-sidebar .accordion-button { + height: 2.5rem; --bs-accordion-btn-bg: #ededed; --bs-accordion-active-bg: #ededed; --bs-accordion-btn-padding-x: 1rem; @@ -84,8 +85,8 @@ a.disabled { .bslib-sidebar-layout > .sidebar-toggle-buttons { grid-column: 1 / 2; - z-index: 1000; - display: inline-flex; + z-index: -1; + opacity: 0; position: absolute; right: calc(var(--_icon-size) + 3rem); top: calc(var(--_icon-size, 1rem) / 2); @@ -93,7 +94,6 @@ a.disabled { width: 80%; align-items: center; justify-content: space-between; - transition: width 0.3s ease, top 0.3s ease, right 0.3s ease; } .bslib-sidebar-layout > .sidebar-toggle-buttons > button { @@ -101,11 +101,33 @@ a.disabled { } .bslib-sidebar-layout.sidebar-collapsed > .sidebar-toggle-buttons { + z-index: 1000; width: 3rem; top: 3rem; + opacity: 1; right: auto; display: flex; justify-content: space-between; flex-direction: column; - transition: width 0.3s ease, top 0.3s ease, right 0.3s ease; + transition: 0.3s ease, top 0.3s ease, right 0.3s ease; +} + +.teal_module > .bslib-sidebar-layout > .main { + --_padding: 0; +} + +.bslib-sidebar-layout.sidebar-collapsed > .main { + padding-right: 0; +} + +.teal-sidebar-layout > div > .container-fluid { + padding-right: 0; +} + +.teal-sidebar-layout > div > .container-fluid > .bslib-sidebar-layout > .main { + padding: 0; +} + +a.remove { + padding: 0; } From 8a12408bbb9d0f90c1293a6a812329126d871500 Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 3 Oct 2024 17:47:07 +0530 Subject: [PATCH 006/108] reposition the validated message div --- R/module_nested_tabs.R | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 565745b246..c3678fcc99 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -99,18 +99,18 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { id = ns("validate_datanames"), ui_validate_reactive_teal_data(ns("validate_datanames")) ), - shinyjs::hidden( - tags$div( - id = ns("transformer_failure_info"), - class = "teal_validated", - div( - class = "teal-output-warning", - "One of transformers failed. Please fix and continue." - ) - ) - ), tags$div( id = ns("teal_module_ui"), + shinyjs::hidden( + tags$div( + id = ns("transformer_failure_info"), + class = "teal_validated", + div( + class = "teal-output-warning", + "One of transformers failed. Please fix and continue." + ) + ) + ), do.call(modules$ui, args) ) ) From 910c26dd09fea50479b77ae0249455200cbb511b Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 3 Oct 2024 18:17:44 +0530 Subject: [PATCH 007/108] bold accordion titles --- inst/css/custom.css | 1 + 1 file changed, 1 insertion(+) diff --git a/inst/css/custom.css b/inst/css/custom.css index c03f120f23..e3152bbb2d 100644 --- a/inst/css/custom.css +++ b/inst/css/custom.css @@ -43,4 +43,5 @@ body > div:has(~ #shiny-modal-wrapper .blur_background) { .accordion-button { font-size: 0.875rem; + font-weight: bold; } From 3279b6ed9c32ef4bd9473827aed54cc21c69bcea Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 3 Oct 2024 18:38:12 +0530 Subject: [PATCH 008/108] fix unwanted padding created by hidden validation message div --- R/module_nested_tabs.R | 2 +- inst/css/sidebar.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index c3678fcc99..28959a50e1 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -94,7 +94,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ns <- NS(id) args <- c(list(id = ns("module")), modules$ui_args) - ui_teal <- tagList( + ui_teal <- tags$div( div( id = ns("validate_datanames"), ui_validate_reactive_teal_data(ns("validate_datanames")) diff --git a/inst/css/sidebar.css b/inst/css/sidebar.css index 1deedc9c6a..2660ff89da 100644 --- a/inst/css/sidebar.css +++ b/inst/css/sidebar.css @@ -120,6 +120,10 @@ a.disabled { padding-right: 0; } +.bslib-sidebar-layout > .main { + padding: 0; +} + .teal-sidebar-layout > div > .container-fluid { padding-right: 0; } From e7346fdab9a21a15e225b301fb4ed9116b4ce86d Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 4 Oct 2024 23:45:05 +0530 Subject: [PATCH 009/108] make sidebar width customizable using options --- R/module_nested_tabs.R | 2 +- R/module_teal.R | 2 +- inst/css/custom.css | 4 ++++ vignettes/teal-options.Rmd | 5 +++++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 28959a50e1..7733e81bfe 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -128,7 +128,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { sidebar = bslib::sidebar( id = ns("teal_module_sidebar"), class = "teal-sidebar", - width = 250, + width = getOption("teal.sidebar.width", 250), tags$div( bslib::accordion( id = ns("data_summary_accordion"), diff --git a/R/module_teal.R b/R/module_teal.R index 61fcf4fa74..9255970aad 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -248,7 +248,7 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { insertUI( selector = sprintf("#%s", session$ns("tabpanel_wrapper")), where = "beforeBegin", - ui = tags$div(ui_validate_reactive_teal_data(session$ns("validate")), tags$br()) + ui = tags$div(ui_validate_reactive_teal_data(session$ns("validate"))) ) } diff --git a/inst/css/custom.css b/inst/css/custom.css index e3152bbb2d..8a08118886 100644 --- a/inst/css/custom.css +++ b/inst/css/custom.css @@ -45,3 +45,7 @@ body > div:has(~ #shiny-modal-wrapper .blur_background) { font-size: 0.875rem; font-weight: bold; } + +.teal-body .tabbable > .nav-pills { + width: calc(100% - 5rem); +} \ No newline at end of file diff --git a/vignettes/teal-options.Rmd b/vignettes/teal-options.Rmd index 3ab894456e..89aebf87cf 100644 --- a/vignettes/teal-options.Rmd +++ b/vignettes/teal-options.Rmd @@ -98,6 +98,11 @@ Default: `"auto"`. To read more about lockfile usage creation check `?teal::module_teal_lockfile`. +### `teal.sidebar.width` (`numeric`) + +This sets the sidebar width in pixels in the teal app. + +Default: `250` # Deprecated options From a616392e7acf3f367c30f653c63dd7cfbdb2df96 Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 7 Oct 2024 19:21:33 +0530 Subject: [PATCH 010/108] icon changes --- R/module_bookmark_manager.R | 14 +++++++++----- inst/css/custom.css | 6 +++++- inst/css/sidebar.css | 5 +++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/R/module_bookmark_manager.R b/R/module_bookmark_manager.R index 6f1af5b630..2862ffda6c 100644 --- a/R/module_bookmark_manager.R +++ b/R/module_bookmark_manager.R @@ -128,12 +128,16 @@ srv_bookmark_panel <- function(id, modules) { ) } + showModal( - modalDialog( - id = ns("bookmark_modal"), - title = "Bookmarked teal app url", - modal_content, - easyClose = TRUE + div( + class = "teal bookmark-popup", + modalDialog( + id = ns("bookmark_modal"), + title = "Bookmarked teal app url", + modal_content, + easyClose = TRUE + ) ) ) }) diff --git a/inst/css/custom.css b/inst/css/custom.css index 8a08118886..aa81841846 100644 --- a/inst/css/custom.css +++ b/inst/css/custom.css @@ -48,4 +48,8 @@ body > div:has(~ #shiny-modal-wrapper .blur_background) { .teal-body .tabbable > .nav-pills { width: calc(100% - 5rem); -} \ No newline at end of file +} + +.teal.bookmark-popup .modal-body { + padding: 0 1.5rem; +} diff --git a/inst/css/sidebar.css b/inst/css/sidebar.css index 2660ff89da..571238c6b7 100644 --- a/inst/css/sidebar.css +++ b/inst/css/sidebar.css @@ -135,3 +135,8 @@ a.disabled { a.remove { padding: 0; } + +.teal-sidebar-layout .accordion-button:not(.collapsed)::after, +.accordion-button::after { + scale: 0.8; +} From 0716caf229bcc6062dc5094c8772fa003697ac50 Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 17 Oct 2024 18:10:31 +0530 Subject: [PATCH 011/108] fix: make the filter mapping popup concise --- R/module_filter_manager.R | 2 +- inst/css/custom.css | 12 ++++-------- tests/testthat/test-shinytest2-wunder_bar.R | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/R/module_filter_manager.R b/R/module_filter_manager.R index 99575dea6f..6db664ea54 100644 --- a/R/module_filter_manager.R +++ b/R/module_filter_manager.R @@ -74,7 +74,7 @@ srv_filter_manager_panel <- function(id, slices_global) { showModal( modalDialog( ui_filter_manager(session$ns("filter_manager")), - class = "filter_manager_modal", + class = "teal-filter-manager-modal", size = "l", footer = NULL, easyClose = TRUE diff --git a/inst/css/custom.css b/inst/css/custom.css index aa81841846..357b2aa7b8 100644 --- a/inst/css/custom.css +++ b/inst/css/custom.css @@ -2,14 +2,6 @@ /* per tag */ -#teal_secondary_col .well { - margin: 0 0 15px 0; -} - -#shiny-modal:has(> .modal-dialog > .modal-content > #landingpopup) { - backdrop-filter: blur(10px); -} - body:not(.modal-open) { padding-right: 0 !important; overflow: auto !important; @@ -53,3 +45,7 @@ body > div:has(~ #shiny-modal-wrapper .blur_background) { .teal.bookmark-popup .modal-body { padding: 0 1.5rem; } + +.modal-dialog:has(.teal-filter-manager-modal) { + width: fit-content; +} diff --git a/tests/testthat/test-shinytest2-wunder_bar.R b/tests/testthat/test-shinytest2-wunder_bar.R index cf177194fc..3482bd1404 100644 --- a/tests/testthat/test-shinytest2-wunder_bar.R +++ b/tests/testthat/test-shinytest2-wunder_bar.R @@ -14,9 +14,9 @@ testthat::test_that("wunder_bar_srv clicking filter icon opens filter-manager mo value = TRUE ) - testthat::expect_true(is.null(app$get_text(".filter_manager_modal"))) + testthat::expect_true(is.null(app$get_text(".teal-filter-manager-modal"))) app$click(filter_manager_btn_id) - testthat::expect_true(!is.null(app$get_text(".filter_manager_modal"))) + testthat::expect_true(!is.null(app$get_text(".teal-filter-manager-modal"))) }) From e705f36d58d9534cf60ed29020607819c5b3935f Mon Sep 17 00:00:00 2001 From: vedhav Date: Fri, 18 Oct 2024 19:48:00 +0530 Subject: [PATCH 012/108] improve the sidebar distinction --- R/module_nested_tabs.R | 41 ++++++++++++++++------ R/module_transform_data.R | 19 ++++++----- inst/css/sidebar.css | 72 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 19 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 3c8a4138f0..2bfc6a7546 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -130,20 +130,36 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { class = "teal-sidebar", width = getOption("teal.sidebar.width", 250), tags$div( - bslib::accordion( - id = ns("data_summary_accordion"), - bslib::accordion_panel( - "Active Filter Summary", - ui_data_summary(ns("data_summary")) + tags$div( + class = "teal-active-data-summary-panel", + bslib::accordion( + id = ns("data_summary_accordion"), + bslib::accordion_panel( + "Active Data Summary", + tags$div( + class = "teal-active-data-summary", + ui_data_summary(ns("data_summary")) + ) + ) ) ), - ui_filter_data(ns("filter_panel")), + tags$br(), + tags$div( + class = "teal-filter-panel", + ui_filter_data(ns("filter_panel")) + ), if (length(modules$transformers) > 0 && !isTRUE(attr(modules$transformers, "custom_ui"))) { - bslib::accordion( - id = ns("data_transform_accordion"), - bslib::accordion_panel( - "Transform Data", - ui_transform_data(ns("data_transform"), transforms = modules$transformers) + tags$div( + tags$br(), + tags$div( + class = "teal-transform-panel", + bslib::accordion( + id = ns("data_transform_accordion"), + bslib::accordion_panel( + "Transform Data", + ui_transform_data(ns("data_transform"), transforms = modules$transformers) + ) + ) ) ) } @@ -155,15 +171,18 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { id = ns("sidebar_toggle_buttons"), class = "sidebar-toggle-buttons", actionButton( + class = "data-summary-toggle", ns("data_summary_toggle"), icon("fas fa-list") ), actionButton( + class = "data-filters-toggle", ns("data_filters_toggle"), icon("fas fa-filter") ), if (length(modules$transformers) > 0) { actionButton( + class = "data-transforms-toggle", ns("data_transforms_toggle"), icon("fas fa-pen-to-square") ) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index d3f3882082..56317ad9fc 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -26,14 +26,17 @@ ui_transform_data <- function(id, transforms, class = "well") { function(name) { data_mod <- transforms[[name]] wrapper_id <- ns(sprintf("wrapper_%s", name)) - bslib::accordion( - class = "teal-transform-accordian", # todo: make transform accordian - bslib::accordion_panel( - attr(data_mod, "label"), - icon = icon("fas fa-square-pen"), - div( - id = wrapper_id, - ui_teal_data(id = ns(name), data_module = transforms[[name]]$ui) + tags$div( + class = "teal-trasform-component", + bslib::accordion( + class = "teal-transform-accordian", # todo: make transform accordian + bslib::accordion_panel( + attr(data_mod, "label"), + icon = icon("fas fa-square-pen"), + div( + id = wrapper_id, + ui_teal_data(id = ns(name), data_module = transforms[[name]]$ui) + ) ) ) ) diff --git a/inst/css/sidebar.css b/inst/css/sidebar.css index 571238c6b7..36b915830c 100644 --- a/inst/css/sidebar.css +++ b/inst/css/sidebar.css @@ -140,3 +140,75 @@ a.remove { .accordion-button::after { scale: 0.8; } + +.teal-active-data-summary { + overflow-x: scroll; +} + +.teal-active-data-summary::-webkit-scrollbar-thumb:hover { + background-color: rgba(var(--bs-primary-rgb), 0.5); +} + +.teal-active-data-summary::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + border-radius: 10px; +} + +.teal-active-data-summary::-webkit-scrollbar { + height: 6px; +} + +.teal-active-data-summary::-webkit-scrollbar-thumb { + border-radius: 10px; + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); +} + +.teal-transform-panel .accordion .accordion-button, +.teal-active-data-summary-panel .accordion .accordion-button { + background-color: rgba(var(--bs-primary-rgb), 0.75); + color: #fff; +} +.teal-transform-panel .accordion-body .accordion .accordion-button { + background-color: rgba(var(--bs-primary-rgb), 0.35); + color: #000; +} +.teal-transform-panel .accordion .accordion-button:hover, +.teal-active-data-summary-panel .accordion .accordion-button:hover { + background-color: rgba(var(--bs-primary-rgb), 1); + color: #fff; +} +.teal-transform-panel .accordion-body .accordion .accordion-button:hover { + background-color: rgba(var(--bs-primary-rgb), 0.6); + color: #000; +} +/* .teal-transform-panel .accordion .accordion-body, +.teal-active-data-summary-panel .accordion .accordion-body { + background-color: rgba(var(--bs-primary-rgb), 0.05); +} */ + +/* .teal-transform-panel .accordion-body, +.teal-active-data-summary-panel .accordion-body { + border: solid 1.5px var(--bs-primary); +} + +.teal-filter-panel .accordion-body { + border: solid 1.5px var(--bs-gray); +} */ + +.sidebar-toggle-buttons .data-summary-toggle { + color: rgba(var(--bs-primary-rgb), 1); +} + +.sidebar-toggle-buttons .data-summary-toggle:hover { + background: rgba(var(--bs-primary-rgb), 1); + color: white; +} + +.sidebar-toggle-buttons .data-transforms-toggle { + color: rgba(var(--bs-primary-rgb), 1); +} + +.sidebar-toggle-buttons .data-transforms-toggle:hover { + background: rgba(var(--bs-primary-rgb), 1); + color: white; +} From 21e39555febfb4cb7ed18afa046ac57420d5b1df Mon Sep 17 00:00:00 2001 From: vedhav Date: Tue, 22 Oct 2024 19:25:39 +0530 Subject: [PATCH 013/108] user feedback changes --- R/module_data_summary.R | 18 +++++++++++++----- R/module_nested_tabs.R | 6 +++--- inst/css/sidebar.css | 15 ++++++++------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/R/module_data_summary.R b/R/module_data_summary.R index fb8ece3c31..63f24d362e 100644 --- a/R/module_data_summary.R +++ b/R/module_data_summary.R @@ -68,13 +68,16 @@ srv_data_summary <- function(id, teal_data) { } else if (is.null(summary_table_out)) { "no datasets to show" } else { - body_html <- apply( - summary_table_out, - 1, - function(x) { + body_html <- lapply( + seq_len(nrow(summary_table_out)), + function(row_index) { + x <- summary_table_out[row_index, ] + row_color <- ifelse(row_index %% 2 == 0, "#f2f2f2", "#ffffff") + tags$tr( tagList( tags$td( + style = paste0("background-color:", row_color, ";"), if (all(x[-1] == "")) { icon( name = "fas fa-exclamation-triangle", @@ -86,7 +89,12 @@ srv_data_summary <- function(id, teal_data) { }, x[1] ), - lapply(x[-1], tags$td) + lapply(x[-1], function(cell) { + tags$td( + style = paste0("background-color:", row_color, ";"), + cell + ) + }) ) ) } diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 2bfc6a7546..5d02bb08b2 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -171,18 +171,18 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { id = ns("sidebar_toggle_buttons"), class = "sidebar-toggle-buttons", actionButton( - class = "data-summary-toggle", + class = "data-summary-toggle btn-outline-primary", ns("data_summary_toggle"), icon("fas fa-list") ), actionButton( - class = "data-filters-toggle", + class = "data-filters-toggle btn-outline-secondary", ns("data_filters_toggle"), icon("fas fa-filter") ), if (length(modules$transformers) > 0) { actionButton( - class = "data-transforms-toggle", + class = "data-transforms-toggle btn-outline-primary", ns("data_transforms_toggle"), icon("fas fa-pen-to-square") ) diff --git a/inst/css/sidebar.css b/inst/css/sidebar.css index 36b915830c..d710b12ff9 100644 --- a/inst/css/sidebar.css +++ b/inst/css/sidebar.css @@ -165,21 +165,22 @@ a.remove { .teal-transform-panel .accordion .accordion-button, .teal-active-data-summary-panel .accordion .accordion-button { - background-color: rgba(var(--bs-primary-rgb), 0.75); + background-color: rgba(var(--bs-primary-rgb), 1); color: #fff; } -.teal-transform-panel .accordion-body .accordion .accordion-button { - background-color: rgba(var(--bs-primary-rgb), 0.35); - color: #000; -} .teal-transform-panel .accordion .accordion-button:hover, .teal-active-data-summary-panel .accordion .accordion-button:hover { + background-color: rgba(var(--bs-primary-rgb), 0.75); + color: #fff; +} + +.teal-transform-panel .accordion-body .accordion .accordion-button { background-color: rgba(var(--bs-primary-rgb), 1); color: #fff; } .teal-transform-panel .accordion-body .accordion .accordion-button:hover { - background-color: rgba(var(--bs-primary-rgb), 0.6); - color: #000; + background-color: rgba(var(--bs-primary-rgb), 0.75); + color: #fff; } /* .teal-transform-panel .accordion .accordion-body, .teal-active-data-summary-panel .accordion .accordion-body { From 36c2561c00d113da4b4ab733ce6c1df8e5918e98 Mon Sep 17 00:00:00 2001 From: vedhav Date: Thu, 13 Feb 2025 20:44:13 +0530 Subject: [PATCH 014/108] fix: remove unused component --- R/module_teal.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index 90079e4912..e73254832c 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -71,8 +71,6 @@ ui_teal <- function(id, modules) { id = id, theme = get_teal_bs_theme(), include_teal_css_js(), - tags$header(header), - tags$hr(style = "margin-top: 0.5rem; margin-bottom: 0.5rem;"), shiny_busy_message_panel, tags$div( id = ns("tabpanel_wrapper"), From 2d5d3a63a59b9734ebce0ae9b77d8e384c0af618 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Mon, 24 Feb 2025 09:35:39 +0100 Subject: [PATCH 015/108] report card handled by teal --- R/module_nested_tabs.R | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 0d823ce619..4d3029d80e 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -207,7 +207,11 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ) } else { ui_teal - } + }, + div( # todo: fix the position to the top right of the tab content? + uiOutput(ns("reporter_add_container")) + # uiOutput(ns("show_rcode_container")) # todo: same mechanism as for the reporter + ) ) ) } @@ -410,6 +414,27 @@ srv_teal_module.teal_module <- function(id, } }) + reporter_card_out <- reactive({ + card <- if (is.list(module_out())) { + unlist( + Filter( + function(x) is.reactive(x) && inherits(x(), "ReportCard"), + module_out() + ), + recursive = FALSE + ) + } else if (is.reactive(module_out()) && inherits(module_out()(), "ReportCard")) { + module_out() + } + }) + + output$reporter_add_container <- renderUI({ + req(reporter_card_out()) + teal.reporter::add_card_button_ui(session$ns("reporter_add")) + }) + + teal.reporter::add_card_button_srv("reporter_add", reporter = reporter, card_fun = reporter_card_out) + module_out }) } From fe0545ebb6bc1b61243587619b13a43c0a277d8f Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Mon, 24 Feb 2025 10:41:16 +0100 Subject: [PATCH 016/108] simplify --- R/module_nested_tabs.R | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 4d3029d80e..ef9fb40eb3 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -416,15 +416,7 @@ srv_teal_module.teal_module <- function(id, reporter_card_out <- reactive({ card <- if (is.list(module_out())) { - unlist( - Filter( - function(x) is.reactive(x) && inherits(x(), "ReportCard"), - module_out() - ), - recursive = FALSE - ) - } else if (is.reactive(module_out()) && inherits(module_out()(), "ReportCard")) { - module_out() + module_out()$report_card() } }) From 333a87c4950657a529b879281d7b3713f7555417 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:38:00 +0100 Subject: [PATCH 017/108] Merge main to test@bslib@main (#1497) Hey @vedhav @gogonzo just letting you know that I am merging `main` into `test@bslib@main` branch, so that on other repositories, like `tmg`, we can install `teal` from this branch and also we can satisfy condition for `teal` to be `>= 0.16.0`. https://github.com/insightsengineering/teal.modules.general/blob/report_redesign_poc%40main/DESCRIPTION#L83 https://github.com/insightsengineering/teal.modules.general/blob/report_redesign_poc%40main/DESCRIPTION#L30 --------- Co-authored-by: Dony Unardi Co-authored-by: insights-engineering-bot <68416928+insights-engineering-bot@users.noreply.github.com> --- DESCRIPTION | 8 ++++---- NEWS.md | 18 ++++++++++-------- R/dummy_functions.R | 2 ++ R/init.R | 24 ++++++++++-------------- R/landing_popup_module.R | 2 +- R/module_teal_with_splash.R | 6 +++--- R/show_rcode_modal.R | 2 +- R/tdata.R | 2 +- R/utils.R | 2 +- man/init.Rd | 10 +++++----- man/module_teal_with_splash.Rd | 6 +++--- tests/testthat.R | 2 ++ 12 files changed, 43 insertions(+), 41 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5b1b2b1123..68e1acba98 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9131 -Date: 2025-02-12 +Version: 0.16.0.9000 +Date: 2025-02-24 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), @@ -51,8 +51,8 @@ Imports: rlang (>= 1.0.0), shinyjs, stats, - teal.code (>= 0.6.0), - teal.logger (>= 0.3.1), + teal.code (>= 0.6.1), + teal.logger (>= 0.3.2), teal.reporter (>= 0.4.0), teal.widgets (>= 0.4.3), tools, diff --git a/NEWS.md b/NEWS.md index de6c44f330..e0608021df 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,20 +1,22 @@ -# teal 0.15.2.9131 +# teal 0.16.0.9000 + +# teal 0.16.0 ### New features -* Possible to call `ui_teal` and `srv_teal` directly in any application by delivering `data` argument as a `reactive` returning `teal_data` object. #669 -* Since introduction of `ui_teal` and `srv_teal` functions `id` argument in `init` is being deprecated. #1438 +* Possible to call `ui_teal` and `srv_teal` directly in any application by delivering `data` argument as a `reactive` returning `teal_data` object (#669). +* Since introduction of `ui_teal` and `srv_teal` functions, the `id` argument in `init` is being deprecated (#1438). * Introduce `ui_session_info` and `srv_session_info` shiny module to create the user session info and teal app lockfile download button. -* Introduced `teal_transform_module` to provide a way to interactively modify data delivered to `teal_module`'s `server` and to decorate module outputs. #1228 #1384 +* Introduced `teal_transform_module` to provide a way to interactively modify data delivered to `teal_module`'s `server` and to decorate module outputs, along with a vignette to demonstrate its usage (#1228, #1384). * Introduced a new argument `once = FALSE` in `teal_data_module` to possibly reload data during a run time. -* Possibility to download lockfile to restore app session for reproducibility. #479 +* Possibility to download lockfile to restore app session for reproducibility (#479). * Datasets which name starts with `.` are ignored when `module`'s `datanames` is set as `"all"`. -* Added warning when reserved `datanames`, such as `all` and `.raw_data` are being used. +* Added warning when reserved `datanames`, such as `all` and `.raw_data` are being used. * Added `add_custom_server()` to allow adding custom server logic to the main shiny server function of a teal app. ### Breaking changes -* Setting `datanames()` on `data` passed to teal application no longer has effect. In order to change `teal_module`'s +* Setting `datanames()` on `data` passed to teal application no longer has effect. In order to change `teal_module`'s `datanames` one should modify `module$datanames`. * `landing_popup_module()` is deprecated. Please use `add_landing_modal()` function to add a landing popup for your teal application. * `teal` no longer re-export `%>%`. Please load `library(magrittr)` instead or use `|>` from `base`. @@ -24,7 +26,7 @@ ### Enhancement * Enhanced a system of data validation and a display of error messages. -* Easier way of to call `javascript` events by setting `$(document).ready(function() { ... })`. #1114 +* Easier way of to call `javascript` events by setting `$(document).ready(function() { ... })` (#1114). * Provided progress bar for modules loading and data filtering during teal app startup. * Filter mapping display has a separate icon in the tab. * Environment of the `data` passed to the `teal_module`'s server consists unfiltered datasets contained in `.raw_data`. diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 3d447daf3e..becb2706de 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -105,3 +105,5 @@ example_module <- function(label = "example teal module", attr(ans, "teal_bookmarkable") <- TRUE ans } + +globalVariables("dataname") diff --git a/R/init.R b/R/init.R index f365ea4e9c..44cce38982 100644 --- a/R/init.R +++ b/R/init.R @@ -23,21 +23,17 @@ #' the browser window title. Defaults to a title "teal app" with the icon of NEST. #' Can be created using the `build_app_title()` or #' by passing a valid `shiny.tag` which is a head tag with title and link tag. -#' This parameter is deprecated. Use `modify_title()` on the teal app object instead. +#' This parameter is no longer supported. Use `modify_title()` on the teal app object instead. #' @param header (`shiny.tag` or `character(1)`) `r lifecycle::badge("deprecated")` Optionally, #' the header of the app. -#' This parameter is deprecated. Use `modify_header()` on the teal app object instead. +#' This parameter is no longer supported. Use `modify_header()` on the teal app object instead. #' @param footer (`shiny.tag` or `character(1)`) `r lifecycle::badge("deprecated")` Optionally, #' the footer of the app. -#' This parameter is deprecated. Use `modify_footer()` on the teal app object instead. +#' This parameter is no longer supported. Use `modify_footer()` on the teal app object instead. #' @param id `r lifecycle::badge("deprecated")` (`character`) Optionally, #' a string specifying the `shiny` module id in cases it is used as a `shiny` module -#' rather than a standalone `shiny` app. This is a legacy feature. Deprecated since v0.15.3 -#' please use [ui_teal()] and [srv_teal()] instead. -#' @param id `r lifecycle::badge("deprecated")` (`character`) Optionally, -#' a string specifying the `shiny` module id in cases it is used as a `shiny` module -#' rather than a standalone `shiny` app. This is a legacy feature. Deprecated since v0.15.3 -#' please use [ui_teal()] and [srv_teal()] instead. +#' rather than a standalone `shiny` app. +#' This parameter is no longer supported. Use [ui_teal()] and [srv_teal()] instead. #' #' @return Named list containing server and UI functions. #' @@ -190,7 +186,7 @@ init <- function(data, if (lifecycle::is_present(id)) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "init(id)", details = paste( "To wrap `teal` application within other shiny application please use", @@ -244,7 +240,7 @@ init <- function(data, if (lifecycle::is_present(title)) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "init(title)", details = paste( "Use `modify_title()` on the teal app object instead.", @@ -256,7 +252,7 @@ init <- function(data, } if (lifecycle::is_present(header)) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "init(header)", details = paste( "Use `modify_header()` on the teal app object instead.", @@ -268,7 +264,7 @@ init <- function(data, } if (lifecycle::is_present(footer)) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "init(footer)", details = paste( "Use `modify_footer()` on the teal app object instead.", @@ -281,7 +277,7 @@ init <- function(data, if (length(landing) == 1L) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "landing_popup_module()", details = paste( "`landing_popup_module()` is deprecated.", diff --git a/R/landing_popup_module.R b/R/landing_popup_module.R index 204c0e18e4..3b2ebf68c9 100644 --- a/R/landing_popup_module.R +++ b/R/landing_popup_module.R @@ -20,7 +20,7 @@ landing_popup_module <- function(label = "Landing Popup", content = NULL, buttons = modalButton("Accept")) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "landing_popup_module()", details = paste( "landing_popup_module() is deprecated.", diff --git a/R/module_teal_with_splash.R b/R/module_teal_with_splash.R index d390b65d59..76b89d6920 100644 --- a/R/module_teal_with_splash.R +++ b/R/module_teal_with_splash.R @@ -22,9 +22,9 @@ ui_teal_with_splash <- function(id, header = tags$p(), footer = tags$p()) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "ui_teal_with_splash()", - details = "Deprecated, please use `?ui_teal` instead" + details = "Please use `?ui_teal` instead" ) ns <- shiny::NS(id) fluidPage( @@ -53,7 +53,7 @@ ui_teal_with_splash <- function(id, #' @rdname module_teal_with_splash srv_teal_with_splash <- function(id, data, modules, filter = teal_slices()) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "srv_teal_with_splash()", details = "Deprecated, please use `?srv_teal` instead" ) diff --git a/R/show_rcode_modal.R b/R/show_rcode_modal.R index 8e9456a773..8e0848c6d2 100644 --- a/R/show_rcode_modal.R +++ b/R/show_rcode_modal.R @@ -15,7 +15,7 @@ #' @export show_rcode_modal <- function(title = NULL, rcode, session = getDefaultReactiveDomain()) { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "show_rcode_modal()", details = "This function will be removed in the next release." ) diff --git a/R/tdata.R b/R/tdata.R index dc79693bfa..b0185501f9 100644 --- a/R/tdata.R +++ b/R/tdata.R @@ -52,7 +52,7 @@ as_tdata <- function(...) { .deprecate_tdata_msg <- function() { lifecycle::deprecate_stop( - when = "0.15.3", + when = "0.16.0", what = "tdata()", details = paste( "tdata has been removed in favour of `teal_data`.\n", diff --git a/R/utils.R b/R/utils.R index 260ed49537..6dd53dc119 100644 --- a/R/utils.R +++ b/R/utils.R @@ -318,7 +318,7 @@ build_app_title <- function( title = "teal app", favicon = "https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/PNG/nest.png") { lifecycle::deprecate_soft( - when = "0.15.3", + when = "0.16.0", what = "build_app_title()", details = "Use `modify_title()` on the object created using the `init`." ) diff --git a/man/init.Rd b/man/init.Rd index 62c64a6da8..fcf18d116d 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -31,20 +31,20 @@ specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or by passing a valid \code{shiny.tag} which is a head tag with title and link tag. -This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} +This parameter is no longer supported. Use \code{modify_title()} on the teal app object instead.} \item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the header of the app. -This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} +This parameter is no longer supported. Use \code{modify_header()} on the teal app object instead.} \item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the footer of the app. -This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} +This parameter is no longer supported. Use \code{modify_footer()} on the teal app object instead.} \item{id}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} (\code{character}) Optionally, a string specifying the \code{shiny} module id in cases it is used as a \code{shiny} module -rather than a standalone \code{shiny} app. This is a legacy feature. Deprecated since v0.15.3 -please use \code{\link[=ui_teal]{ui_teal()}} and \code{\link[=srv_teal]{srv_teal()}} instead.} +rather than a standalone \code{shiny} app. +This parameter is no longer supported. Use \code{\link[=ui_teal]{ui_teal()}} and \code{\link[=srv_teal]{srv_teal()}} instead.} } \value{ Named list containing server and UI functions. diff --git a/man/module_teal_with_splash.Rd b/man/module_teal_with_splash.Rd index 26f4a5e6f2..39ac07ae2f 100644 --- a/man/module_teal_with_splash.Rd +++ b/man/module_teal_with_splash.Rd @@ -32,15 +32,15 @@ more details.} the browser window title. Defaults to a title "teal app" with the icon of NEST. Can be created using the \code{build_app_title()} or by passing a valid \code{shiny.tag} which is a head tag with title and link tag. -This parameter is deprecated. Use \code{modify_title()} on the teal app object instead.} +This parameter is no longer supported. Use \code{modify_title()} on the teal app object instead.} \item{header}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the header of the app. -This parameter is deprecated. Use \code{modify_header()} on the teal app object instead.} +This parameter is no longer supported. Use \code{modify_header()} on the teal app object instead.} \item{footer}{(\code{shiny.tag} or \code{character(1)}) \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Optionally, the footer of the app. -This parameter is deprecated. Use \code{modify_footer()} on the teal app object instead.} +This parameter is no longer supported. Use \code{modify_footer()} on the teal app object instead.} \item{filter}{(\code{teal_slices}) Optionally, specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} diff --git a/tests/testthat.R b/tests/testthat.R index 46e87bf96d..a60ee5ea0b 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,3 +1,5 @@ pkg_name <- "teal" library(pkg_name, character.only = TRUE) testthat::test_check(pkg_name) +unlink(".renv") +unlink("BiocManager") From 86391e43cf36df64701a92fe138f4a49e7df515a Mon Sep 17 00:00:00 2001 From: vedhav Date: Tue, 25 Feb 2025 21:36:59 +0530 Subject: [PATCH 018/108] feat: add remote dependencies --- DESCRIPTION | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 68e1acba98..0969489ef9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -72,6 +72,10 @@ Suggests: testthat (>= 3.2.0), withr (>= 2.1.0), yaml (>= 1.1.0) +Remotes: + insightsengineering/teal.slice@test@bslib@main, + insightsengineering/teal.widgets@test@bslib@main, + insightsengineering/teal.reporter@test@bslib@main VignetteBuilder: knitr, rmarkdown From 454d49ecf73571f97df49a11c158ea7a27624a50 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 26 Feb 2025 12:49:54 +0100 Subject: [PATCH 019/108] revert teal.reporter redesign changes --- R/module_nested_tabs.R | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index ef9fb40eb3..0d823ce619 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -207,11 +207,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ) } else { ui_teal - }, - div( # todo: fix the position to the top right of the tab content? - uiOutput(ns("reporter_add_container")) - # uiOutput(ns("show_rcode_container")) # todo: same mechanism as for the reporter - ) + } ) ) } @@ -414,19 +410,6 @@ srv_teal_module.teal_module <- function(id, } }) - reporter_card_out <- reactive({ - card <- if (is.list(module_out())) { - module_out()$report_card() - } - }) - - output$reporter_add_container <- renderUI({ - req(reporter_card_out()) - teal.reporter::add_card_button_ui(session$ns("reporter_add")) - }) - - teal.reporter::add_card_button_srv("reporter_add", reporter = reporter, card_fun = reporter_card_out) - module_out }) } From 620e5ba1a5beba4fb9b8c05d2f29ec74e62b4b74 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 26 Feb 2025 12:51:15 +0100 Subject: [PATCH 020/108] bring back teal.reporter redesign changes --- R/module_nested_tabs.R | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 0d823ce619..ef9fb40eb3 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -207,7 +207,11 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ) } else { ui_teal - } + }, + div( # todo: fix the position to the top right of the tab content? + uiOutput(ns("reporter_add_container")) + # uiOutput(ns("show_rcode_container")) # todo: same mechanism as for the reporter + ) ) ) } @@ -410,6 +414,19 @@ srv_teal_module.teal_module <- function(id, } }) + reporter_card_out <- reactive({ + card <- if (is.list(module_out())) { + module_out()$report_card() + } + }) + + output$reporter_add_container <- renderUI({ + req(reporter_card_out()) + teal.reporter::add_card_button_ui(session$ns("reporter_add")) + }) + + teal.reporter::add_card_button_srv("reporter_add", reporter = reporter, card_fun = reporter_card_out) + module_out }) } From 17f5a4a0cee7b496d8ce7463676e589c170a468d Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 26 Feb 2025 12:51:36 +0100 Subject: [PATCH 021/108] update Remotes branch names --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0969489ef9..4ef7386d4f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -75,7 +75,7 @@ Suggests: Remotes: insightsengineering/teal.slice@test@bslib@main, insightsengineering/teal.widgets@test@bslib@main, - insightsengineering/teal.reporter@test@bslib@main + insightsengineering/teal.reporter@redesign@main VignetteBuilder: knitr, rmarkdown From df3a62aa08cd02640a2cf75324df70aaca1a220d Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 26 Feb 2025 12:57:14 +0100 Subject: [PATCH 022/108] change add_card_button_srv -> add_document_button_srv --- R/module_nested_tabs.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index ef9fb40eb3..bd09a00c3f 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -425,7 +425,7 @@ srv_teal_module.teal_module <- function(id, teal.reporter::add_card_button_ui(session$ns("reporter_add")) }) - teal.reporter::add_card_button_srv("reporter_add", reporter = reporter, card_fun = reporter_card_out) + teal.reporter::add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) module_out }) From 9f9126056fef451323bfe8d221cd95c179cc8dfd Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 26 Feb 2025 15:59:43 +0100 Subject: [PATCH 023/108] add dev versions to teal dev dependencies --- DESCRIPTION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0969489ef9..7eae923063 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -38,7 +38,7 @@ Depends: R (>= 4.1), shiny (>= 1.8.1), teal.data (>= 0.7.0), - teal.slice (>= 0.6.0) + teal.slice (>= 0.6.0.9000) Imports: bslib (>= 0.8.0), checkmate (>= 2.1.0), @@ -53,8 +53,8 @@ Imports: stats, teal.code (>= 0.6.1), teal.logger (>= 0.3.2), - teal.reporter (>= 0.4.0), - teal.widgets (>= 0.4.3), + teal.reporter (>= 0.4.0.9000), + teal.widgets (>= 0.4.3.9000), tools, utils Suggests: From 2bf6481cfa92651d75f6a5cbb4fd8e8e4dceacab Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 26 Feb 2025 16:00:44 +0100 Subject: [PATCH 024/108] add dev versions to dependencies --- DESCRIPTION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4ef7386d4f..dab4acea96 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -38,7 +38,7 @@ Depends: R (>= 4.1), shiny (>= 1.8.1), teal.data (>= 0.7.0), - teal.slice (>= 0.6.0) + teal.slice (>= 0.6.0.9000) Imports: bslib (>= 0.8.0), checkmate (>= 2.1.0), @@ -53,8 +53,8 @@ Imports: stats, teal.code (>= 0.6.1), teal.logger (>= 0.3.2), - teal.reporter (>= 0.4.0), - teal.widgets (>= 0.4.3), + teal.reporter (>= 0.4.0.9000), + teal.widgets (>= 0.4.3.9000), tools, utils Suggests: From 06f2898285b15dbbb2c30f4405159ae93fb191ef Mon Sep 17 00:00:00 2001 From: vedhav Date: Mon, 3 Mar 2025 02:59:14 -0500 Subject: [PATCH 025/108] fix: bslib changes --- R/init.R | 2 +- R/module_teal.R | 6 +++--- inst/js/init.js | 5 +---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/R/init.R b/R/init.R index 44cce38982..c8d9265e22 100644 --- a/R/init.R +++ b/R/init.R @@ -203,7 +203,7 @@ init <- function(data, res <- structure( list( ui = function(request) { - fluidPage( + bslib::page_fluid( title = tags$div( id = "teal-app-title", tags$head( diff --git a/R/module_teal.R b/R/module_teal.R index e73254832c..18d5c8fde2 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -185,11 +185,11 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { if (inherits(data, "teal_data_module")) { setBookmarkExclude(c("teal_modules-active_tab")) - shiny::insertTab( - inputId = "teal_modules-active_tab", + bslib::nav_insert( + id = "teal_modules-active_tab", position = "before", select = TRUE, - tabPanel( + bslib::nav_panel( title = icon("fas fa-database"), value = "teal_data_module", tags$div( diff --git a/inst/js/init.js b/inst/js/init.js index 2bf5a1f179..69e9b7a27b 100644 --- a/inst/js/init.js +++ b/inst/js/init.js @@ -2,7 +2,4 @@ // not included in the original HTML // this code alows the show R code "copy to clipbaord" button to work -var clipboard = new ClipboardJS('.btn[data-clipboard-target]'); - - - +var clipboard = new ClipboardJS(".btn[data-clipboard-target]"); From 59b3a15f870dfde3dc436c78fe83d53a7ebb73c9 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 4 Mar 2025 12:12:48 +0100 Subject: [PATCH 026/108] bring add_document_button_srv to teal --- R/module_nested_tabs.R | 2 +- R/teal_reporter.R | 96 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index bd09a00c3f..d621134032 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -425,7 +425,7 @@ srv_teal_module.teal_module <- function(id, teal.reporter::add_card_button_ui(session$ns("reporter_add")) }) - teal.reporter::add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) + add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) module_out }) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 48fd5dd71d..3b7621d3ce 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -194,3 +194,99 @@ TealSlicesBlock <- R6::R6Class( # nolint: object_name_linter. teal_slices = NULL # teal_slices ) ) + +#' Server function for `ReportDocument` card class. +#' @keywords internal +add_document_button_srv <- function(id, reporter, r_card_fun) { + checkmate::assert_class(r_card_fun, "reactive") + checkmate::assert_class(reporter, "Reporter") + + shiny::moduleServer(id, function(input, output, session) { + shiny::setBookmarkExclude(c( + "add_report_card_button", "download_button", "reset_reporter", + "add_card_ok", "download_data", "reset_reporter_ok", + "label", "comment" + )) + + ns <- session$ns + + add_modal <- function() { + div( + class = "teal-widgets reporter-modal", + shiny::modalDialog( + easyClose = TRUE, + shiny::tags$h3("Add a Card to the Report"), + shiny::tags$hr(), + shiny::textInput( + ns("label"), + "Card Name", + value = "", + placeholder = "Add the card title here", + width = "100%" + ), + shiny::tags$script( + shiny::HTML( + sprintf( + " + $('#shiny-modal').on('shown.bs.modal', () => { + $('#%s').focus() + }) + ", + ns("label") + ) + ) + ), + footer = shiny::div( + shiny::tags$button( + type = "button", + class = "btn btn-secondary", + `data-dismiss` = "modal", + `data-bs-dismiss` = "modal", + NULL, + "Cancel" + ), + shiny::tags$button( + id = ns("add_card_ok"), + type = "button", + class = "btn btn-primary action-button", + `data-val` = shiny::restoreInput(id = ns("add_card_ok"), default = NULL), + NULL, + "Add Card" + ) + ) + ) + ) + } + + shiny::observeEvent(input$add_report_card_button, { + shiny::showModal(add_modal()) + }) + + # the add card button is disabled when clicked to prevent multi-clicks + # please check the ui part for more information + shiny::observeEvent(input$add_card_ok, { + if (inherits(r_card_fun, "try-error")) { + msg <- paste0( + "The card could not be added to the report. ", + "Have the outputs for the report been created yet? If not please try again when they ", + "are ready. Otherwise contact your application developer" + ) + warning(msg) + shiny::showNotification( + msg, + type = "error" + ) + } else { + card <- r_card_fun() + checkmate::assert_class(card, "ReportDocument") + names(card) <- input$label + + # todo card <- to_markdown(card) + + reporter$append_cards(list(card)) + shiny::showNotification(sprintf("The card added successfully."), type = "message") + shiny::removeModal() + } + }) + }) +} From c2388eb895dfff6e0d928b0758a4c9e39d6e5de1 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 4 Mar 2025 12:50:25 +0100 Subject: [PATCH 027/108] make it a named list --- R/teal_reporter.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 3b7621d3ce..7564e79744 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -277,13 +277,13 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { type = "error" ) } else { - card <- r_card_fun() - checkmate::assert_class(card, "ReportDocument") + card <- list(r_card_fun()) + checkmate::assert_class(card[[1]], "ReportDocument") names(card) <- input$label # todo card <- to_markdown(card) - reporter$append_cards(list(card)) + reporter$append_cards(card) shiny::showNotification(sprintf("The card added successfully."), type = "message") shiny::removeModal() } From 9990dcabb154b3f2a3c25990b49f26bb507cdd87 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 4 Mar 2025 17:29:34 +0100 Subject: [PATCH 028/108] attempt on S3 class directly in teal --- NAMESPACE | 4 ++++ R/teal_reporter.R | 49 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index ac53893f97..700f7c8953 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,6 +9,9 @@ S3method(print,teal_modules) S3method(srv_teal_module,default) S3method(srv_teal_module,teal_module) S3method(srv_teal_module,teal_modules) +S3method(teal::element_to_markdown,data.frame) +S3method(teal::element_to_markdown,default) +S3method(teal::element_to_markdown,ggplot) S3method(ui_teal_module,default) S3method(ui_teal_module,teal_module) S3method(ui_teal_module,teal_modules) @@ -18,6 +21,7 @@ export(add_landing_modal) export(as.teal_slices) export(as_tdata) export(build_app_title) +export(element_to_markdown) export(example_module) export(get_code_tdata) export(get_metadata) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 7564e79744..b460e92b8e 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -277,16 +277,57 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { type = "error" ) } else { - card <- list(r_card_fun()) - checkmate::assert_class(card[[1]], "ReportDocument") + + card <- r_card_fun() + checkmate::assert_class(card, "ReportDocument") + + card <- to_markdown(card) + card <- list(card) names(card) <- input$label + attributes(card) <- attributes(r_card_fun()) - # todo card <- to_markdown(card) + reporter$append_cards(list(card)) - reporter$append_cards(card) shiny::showNotification(sprintf("The card added successfully."), type = "message") shiny::removeModal() } }) }) } + +to_markdown <- function(card){ + lapply(card, element_to_markdown) +} + +#' @rdname element_to_markdown +#' @export element_to_markdown +element_to_markdown <- function(x) UseMethod("element_to_markdown") + +#' @rdname element_to_markdown +#' @method element_to_markdown default +#' @exportS3Method teal::element_to_markdown +element_to_markdown.default <- function(x) x + +#' @rdname element_to_markdown +#' @method element_to_markdown ggplot +#' @exportS3Method teal::element_to_markdown +element_to_markdown.ggplot <- function(x, width = 5, height = 4, dpi = 100) { + # Temporary file to save the plot + tmpfile <- tempfile(fileext = ".png") + + # Save the plot as a PNG file + ggsave(tmpfile, plot = x, width = width, height = height, dpi = dpi) + + # Read the binary data and encode as base64 + # base64enc::base64encode(tmpfile) + base64_string<- knitr::image_uri(tmpfile) + sprintf("![Plot](%s)", base64_string) +} + +#' @rdname element_to_markdown +#' @method element_to_markdown data.frame +#' @exportS3Method teal::element_to_markdown +element_to_markdown.data.frame <- function(x) { + knitr::kable(x) +} + From c5301ff0f967ce65d59f8b57b509865e5aab51e2 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 4 Mar 2025 22:59:05 +0100 Subject: [PATCH 029/108] compact to_markdown --- R/teal_reporter.R | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index b460e92b8e..c90c26a235 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -279,14 +279,10 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { } else { card <- r_card_fun() - checkmate::assert_class(card, "ReportDocument") - card <- to_markdown(card) - card <- list(card) names(card) <- input$label - attributes(card) <- attributes(r_card_fun()) - reporter$append_cards(list(card)) + reporter$append_cards(card) shiny::showNotification(sprintf("The card added successfully."), type = "message") shiny::removeModal() @@ -296,7 +292,10 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { } to_markdown <- function(card){ - lapply(card, element_to_markdown) + checkmate::assert_class(card, "ReportDocument") + card_markdown <- lapply(card, element_to_markdown) + class(card_markdown) <- "ReportDocument" + list(card_markdown) } #' @rdname element_to_markdown From ec60f501f33661d8951fe2ee9b72b450b6340d05 Mon Sep 17 00:00:00 2001 From: m7pr Date: Sun, 9 Mar 2025 19:28:54 +0100 Subject: [PATCH 030/108] bring back load button to reporter previewer --- R/modules.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/modules.R b/R/modules.R index 92cb31227c..9306a09764 100644 --- a/R/modules.R +++ b/R/modules.R @@ -652,7 +652,7 @@ append_reporter_module <- function(modules) { if (is_arg_used(modules, "reporter") && length(extract_module(modules, "teal_module_previewer")) == 0) { modules <- append_module( modules, - reporter_previewer_module(server_args = list(previewer_buttons = c("download", "reset"))) + reporter_previewer_module(server_args = list(previewer_buttons = c("download", "load", "reset"))) ) } modules From eb9abf3add25a54ce61a520097b94b9ab3a2147b Mon Sep 17 00:00:00 2001 From: m7pr Date: Sun, 9 Mar 2025 19:29:05 +0100 Subject: [PATCH 031/108] change knitr to lenght1 --- R/teal_reporter.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index c90c26a235..b9254c129f 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -327,6 +327,7 @@ element_to_markdown.ggplot <- function(x, width = 5, height = 4, dpi = 100) { #' @method element_to_markdown data.frame #' @exportS3Method teal::element_to_markdown element_to_markdown.data.frame <- function(x) { - knitr::kable(x) + paste(as.character(knitr::kable(x)), collapse = "\n") + # I am not sure it renders the table, but it's here to assure it has length 1. } From 366def0c6405f34d66c16cc8f12c8ee8494ebfa9 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 13 Mar 2025 09:26:59 +0100 Subject: [PATCH 032/108] Update DESCRIPTION Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index fe9b5e473a..23d6455974 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -38,7 +38,7 @@ Depends: R (>= 4.1), shiny (>= 1.8.1), teal.data (>= 0.7.0), - teal.slice (>= 0.6.0.9000) + teal.slice (>= 0.6.0.9001) Imports: bsicons, bslib (>= 0.8.0), From 35d410183fe7a7faf7d8717341c08530317b3577 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 13 Mar 2025 09:29:57 +0100 Subject: [PATCH 033/108] unify remotes --- DESCRIPTION | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 23d6455974..500299f273 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -73,17 +73,13 @@ Suggests: testthat (>= 3.2.0), withr (>= 2.1.0), yaml (>= 1.1.0) -Remotes: - insightsengineering/teal.slice@main, - insightsengineering/teal.widgets@main, - insightsengineering/teal.reporter@redesign@main VignetteBuilder: knitr, rmarkdown RdMacros: lifecycle Remotes: - insightsengineering/teal.reporter@main, + insightsengineering/teal.reporter@redesign@main, insightsengineering/teal.slice@main, insightsengineering/teal.widgets@main Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, From 2667426a8e1cdab0da30c7e1220df7d056cd76de Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 24 Mar 2025 14:36:04 +0100 Subject: [PATCH 034/108] element to markdown not needed in teal --- R/teal_reporter.R | 47 ++++------------------------------------------- 1 file changed, 4 insertions(+), 43 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index b9254c129f..4fe4b5b5db 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -279,10 +279,11 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { } else { card <- r_card_fun() - card <- to_markdown(card) - names(card) <- input$label + #card <- to_markdown(card) + lcard <- list(card) + names(lcard) <- input$label - reporter$append_cards(card) + reporter$append_cards(lcard) shiny::showNotification(sprintf("The card added successfully."), type = "message") shiny::removeModal() @@ -291,43 +292,3 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { }) } -to_markdown <- function(card){ - checkmate::assert_class(card, "ReportDocument") - card_markdown <- lapply(card, element_to_markdown) - class(card_markdown) <- "ReportDocument" - list(card_markdown) -} - -#' @rdname element_to_markdown -#' @export element_to_markdown -element_to_markdown <- function(x) UseMethod("element_to_markdown") - -#' @rdname element_to_markdown -#' @method element_to_markdown default -#' @exportS3Method teal::element_to_markdown -element_to_markdown.default <- function(x) x - -#' @rdname element_to_markdown -#' @method element_to_markdown ggplot -#' @exportS3Method teal::element_to_markdown -element_to_markdown.ggplot <- function(x, width = 5, height = 4, dpi = 100) { - # Temporary file to save the plot - tmpfile <- tempfile(fileext = ".png") - - # Save the plot as a PNG file - ggsave(tmpfile, plot = x, width = width, height = height, dpi = dpi) - - # Read the binary data and encode as base64 - # base64enc::base64encode(tmpfile) - base64_string<- knitr::image_uri(tmpfile) - sprintf("![Plot](%s)", base64_string) -} - -#' @rdname element_to_markdown -#' @method element_to_markdown data.frame -#' @exportS3Method teal::element_to_markdown -element_to_markdown.data.frame <- function(x) { - paste(as.character(knitr::kable(x)), collapse = "\n") - # I am not sure it renders the table, but it's here to assure it has length 1. -} - From 8729daa46b236ba0fe37c6423c89499ce9165225 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 25 Mar 2025 22:36:49 +0100 Subject: [PATCH 035/108] cleanup NAMESPACE --- NAMESPACE | 4 ---- 1 file changed, 4 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 700f7c8953..ac53893f97 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -9,9 +9,6 @@ S3method(print,teal_modules) S3method(srv_teal_module,default) S3method(srv_teal_module,teal_module) S3method(srv_teal_module,teal_modules) -S3method(teal::element_to_markdown,data.frame) -S3method(teal::element_to_markdown,default) -S3method(teal::element_to_markdown,ggplot) S3method(ui_teal_module,default) S3method(ui_teal_module,teal_module) S3method(ui_teal_module,teal_modules) @@ -21,7 +18,6 @@ export(add_landing_modal) export(as.teal_slices) export(as_tdata) export(build_app_title) -export(element_to_markdown) export(example_module) export(get_code_tdata) export(get_metadata) From 7a5e099698710a529b9a12c99b343cfa4de795be Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 25 Mar 2025 22:37:37 +0100 Subject: [PATCH 036/108] extend example_module to handle report_card --- R/dummy_functions.R | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index becb2706de..e27e135db4 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -36,7 +36,7 @@ example_module <- function(label = "example teal module", ans <- module( label, - server = function(id, data, decorators) { + server = function(id, data, reporter, decorators) { checkmate::assert_class(isolate(data()), "teal_data") moduleServer(id, function(input, output, session) { datanames_rv <- reactive(names(req(data()))) @@ -83,7 +83,19 @@ example_module <- function(label = "example teal module", title = "Example Code" ) - table_data_decorated + card_fun <- reactive({ + req(table_data_decorated()) + teal.reporter::report_document( + "## Table Data", + teal.reporter::code_chunk( + teal.code::get_code(table_data_decorated()) + ), + table_data_decorated()[["object"]] + ) + }) + list( + report_card = card_fun + ) }) }, ui = function(id, decorators) { From 0022f0d4c7c6d70a0d2a30188cf6df9e7e0ebc45 Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 25 Mar 2025 22:37:59 +0100 Subject: [PATCH 037/108] extend shinytest2 tests for new teal reporter previewer --- tests/testthat/helper-shinytest2.R | 30 ------------- tests/testthat/test-shinytest2-reporter.R | 52 +++++++++++++++-------- 2 files changed, 35 insertions(+), 47 deletions(-) diff --git a/tests/testthat/helper-shinytest2.R b/tests/testthat/helper-shinytest2.R index 0656e6db5f..705b87c853 100644 --- a/tests/testthat/helper-shinytest2.R +++ b/tests/testthat/helper-shinytest2.R @@ -5,33 +5,3 @@ simple_teal_data <- function() { }) data } - -report_module <- function(label = "example teal module") { - module( - label = label, - server = function(id, data, reporter) { - moduleServer(id, function(input, output, session) { - teal.reporter::simple_reporter_srv( - id = "reporter", - reporter = reporter, - card_fun = function(card) card - ) - updateSelectInput(session, "dataname", choices = isolate(names(data()))) - output$dataset <- renderPrint({ - req(input$dataname) - data()[[input$dataname]] - }) - }) - }, - ui = function(id) { - ns <- NS(id) - sidebarLayout( - sidebarPanel( - teal.reporter::simple_reporter_ui(ns("reporter")), - selectInput(ns("dataname"), "Choose a dataset", choices = NULL) - ), - mainPanel(verbatimTextOutput(ns("dataset"))) - ) - } - ) -} diff --git a/tests/testthat/test-shinytest2-reporter.R b/tests/testthat/test-shinytest2-reporter.R index 1489468dcd..8976d16476 100644 --- a/tests/testthat/test-shinytest2-reporter.R +++ b/tests/testthat/test-shinytest2-reporter.R @@ -1,12 +1,12 @@ testthat::skip_if_not_installed("shinytest2") testthat::skip_if_not_installed("rvest") -testthat::test_that("e2e: reporter tab is created when a module has reporter", { +testthat::test_that("e2e: reporter tab is created when a module has reporter + report_fun", { testthat::skip("chromium") skip_if_too_deep(5) app <- TealAppDriver$new( data = simple_teal_data(), - modules = report_module(label = "Module with Reporter") + modules = example_module(label = "Module with Reporter") ) teal_tabs <- rvest::html_elements(app$get_html_rvest(selector = "#teal-teal_modules-active_tab"), "a") @@ -22,8 +22,13 @@ testthat::test_that("e2e: reporter tab is created when a module has reporter", { app$stop() }) +testthat::test_that("e2e: reporter card can be customized", { + skip("TODO") +}) + testthat::test_that("e2e: reporter tab is not created when a module has no reporter", { testthat::skip("chromium") + skip("TODO - change to reporter button is not visible if module has no report_card") skip_if_too_deep(5) app <- TealAppDriver$new( data = simple_teal_data(), @@ -46,26 +51,22 @@ testthat::test_that("e2e: reporter tab is not created when a module has no repor app$stop() }) -testthat::test_that("e2e: adding a report card in a module adds it in the report previewer tab", { +testthat::test_that("e2e: adding a report card with global button adds it in the report previewer tab", { testthat::skip("chromium") skip_if_too_deep(5) app <- TealAppDriver$new( data = simple_teal_data(), - modules = report_module(label = "Module with Reporter") + modules = example_module(label = "Module with Reporter") ) - app$click(NS(app$active_module_ns(), "reporter-add_report_card_simple-add_report_card_button")) + app$click(NS(app$active_module_ns(), "reporter_add-add_report_card_button")) app$set_input( - NS(app$active_module_ns(), "reporter-add_report_card_simple-label"), + NS(app$active_module_ns(), "reporter_add-label"), "Card name" ) - app$set_input( - NS(app$active_module_ns(), "reporter-add_report_card_simple-comment"), - "Card comment" - ) - app$click(NS(app$active_module_ns(), "reporter-add_report_card_simple-add_card_ok")) + app$click(NS(app$active_module_ns(), "reporter_add-add_card_ok")) app$navigate_teal_tab("Report previewer") @@ -75,15 +76,32 @@ testthat::test_that("e2e: adding a report card in a module adds it in the report testthat::expect_match( app$get_text(selector = accordian_selector), - "Card 1: Card name" + "Card name" ) - testthat::expect_match( - app$get_text(selector = "#card1 pre"), - "Card comment" + app$stop() +}) + +testthat::test_that("e2e: reporter_previewer_module has download, load and reset buttons", { + testthat::skip("chromium") + skip_if_too_deep(5) + app <- teal:::TealAppDriver$new( + data = simple_teal_data(), + modules = example_module(label = "Example Module with Reporter") + ) + + app$navigate_teal_tab("Report previewer") + + testthat::expect_true( + app$is_visible(app$active_module_element("download_data_prev")) + ) + testthat::expect_true( + app$is_visible(app$active_module_element("load_reporter_previewer")) + ) + testthat::expect_true( + app$is_visible(app$active_module_element("resetButtonPreviewer-reset_reporter")) ) - app$stop() }) testthat::test_that("e2e: reporter_previewer_module do not show data_summary nor filter_panel", { @@ -91,7 +109,7 @@ testthat::test_that("e2e: reporter_previewer_module do not show data_summary nor skip_if_too_deep(5) app <- teal:::TealAppDriver$new( data = simple_teal_data(), - modules = report_module(label = "Module with Reporter") + modules = example_module(label = "Module with Reporter") ) app$navigate_teal_tab("Report previewer") From c0b9836c91852d8afac472b632dc1b44b24d403e Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 2 Apr 2025 16:17:07 +0200 Subject: [PATCH 038/108] prototype of modify_teal_module_report --- R/modify_teal_module_report.R | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 R/modify_teal_module_report.R diff --git a/R/modify_teal_module_report.R b/R/modify_teal_module_report.R new file mode 100644 index 0000000000..43ac4f2d50 --- /dev/null +++ b/R/modify_teal_module_report.R @@ -0,0 +1,87 @@ +modify_teal_module_report <- function(teal_module, modify_fun) { + stopifnot(inherits(teal_module, "teal_module")) + + original_server <- teal_module$server + + new_server <- function(id, data, reporter, ...) { + moduleServer(id, function(input, output, session) { + original_outputs <- original_server(id, data, ...) + + if (!"report_card" %in% names(original_outputs)) { + stop("The provided teal module does not return a 'report_card'.") + } + + modified_report_card <- reactive({ + modify_fun(original_outputs$report_card()) + }) + + return(modifyList(original_outputs, list(report_card = modified_report_card))) + }) + } + + new_module <- module( + label = paste0(teal_module$label, " (Modified)"), + server = new_server, + ui = teal_module$ui, + datanames = teal_module$datanames, + server_args = teal_module$server_args, + ui_args = teal_module$ui_args, + transformators = teal_module$transformators + ) + + return(new_module) +} +# +# +# take_3_elements <- function(x){ +# teal.reporter::edit_report_document(x, modify = 1:3) +# } +# +# +# +# devtools::load_all('../teal.reporter') +# devtools::load_all('../teal.widgets') +# devtools::load_all('../teal.code') +# devtools::load_all('../teal.modules.general') +# devtools::load_all('.') +# +# # general data example +# data <- teal_data() +# data <- within(data, { +# require(nestcolor) +# CO2 <- CO2 +# }) +# +# app <- init( +# data = data, +# modules = modules( +# tm_a_regression( +# label = "Regression", +# response = data_extract_spec( +# dataname = "CO2", +# select = select_spec( +# label = "Select variable:", +# choices = "uptake", +# selected = "uptake", +# multiple = FALSE, +# fixed = TRUE +# ) +# ), +# regressor = data_extract_spec( +# dataname = "CO2", +# select = select_spec( +# label = "Select variables:", +# choices = variable_choices(data[["CO2"]], c("conc", "Treatment")), +# selected = "conc", +# multiple = TRUE, +# fixed = FALSE +# ) +# ) +# ) |> +# modify_teal_module_report(modify_fun = take_3_elements) +# ) +# ) +# +# if (interactive()) { +# shinyApp(app$ui, app$server) +# } From 795a24666b82402d1fa7778137276fe8bb447154 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 9 Apr 2025 19:28:25 +0200 Subject: [PATCH 039/108] workout modify_teal_module_output --- R/modify_teal_module_report.R | 127 ++++++++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 20 deletions(-) diff --git a/R/modify_teal_module_report.R b/R/modify_teal_module_report.R index 43ac4f2d50..7165151d0d 100644 --- a/R/modify_teal_module_report.R +++ b/R/modify_teal_module_report.R @@ -1,51 +1,138 @@ -modify_teal_module_report <- function(teal_module, modify_fun) { + +# This function is just here, so it is easier to understand what's happening in +# modify_teal_module_output. +# We want to have a match in parameter names in new_server and original_server +# assuming we don't know names of parameters in original_server. +# modify_teal_module_output is the final implementation that shows how to do it without knowing parameters. +# modify_teal_module_output_named_params is just here to show how would that look like for a custom and stable +# set of parameter names. +modify_teal_module_output_named_params <- function(teal_module, modify_fun, output_name) { stopifnot(inherits(teal_module, "teal_module")) original_server <- teal_module$server + original_ui <- teal_module$ui + new_ui <- function(id, ...){ + ns <- NS(id) + original_ui(ns('modified'), ...) + } + + new_server <- function(id, + data, + filter_panel_api, + response, + regressor, + reporter, + plot_height, + plot_width, + ggplot2_args, + default_outlier_label, + decorators) - new_server <- function(id, data, reporter, ...) { moduleServer(id, function(input, output, session) { - original_outputs <- original_server(id, data, ...) - if (!"report_card" %in% names(original_outputs)) { - stop("The provided teal module does not return a 'report_card'.") + original_outputs <- original_server( + 'modified', + data, + filter_panel_api, + response = response, + regressor = regressor, + reporter, + plot_height, + plot_width, + ggplot2_args, + default_outlier_label, + decorators + ) + # work on original_outputs + }) + + module( + label = teal_module$label, + server = new_server, + ui = new_ui, + datanames = teal_module$datanames, + server_args = teal_module$server_args, + ui_args = teal_module$ui_args, + transformators = teal_module$transformators + ) +} + + +modify_teal_module_output <- function(teal_module, modify_fun, output_name) { + stopifnot(inherits(teal_module, "teal_module")) + + original_server <- teal_module$server + original_ui <- teal_module$ui + new_ui <- function(id, ...){ + ns <- NS(id) + original_ui(ns('modified'), ...) + } + + original_server <- teal_module$server + new_server <- function(...) { } + formals(new_server) <- formals(original_server) + + body(new_server) <- quote({ + + arg_names <- names(formals(original_server)) + + # Capture current call and evaluate each argument + call_expr <- match.call() + call_args <- as.list(call_expr)[-1] # remove function name + call_args <- call_args[names(call_args) %in% arg_names] + + # Evaluate all arguments in the current environment + eval_args <- lapply(call_args, eval, envir = parent.frame()) + + moduleServer(id, function(input, output, session) { + eval_args$id <- 'modified' + original_outputs <- do.call(original_server, eval_args) + if (!output_name %in% names(original_outputs)) { + stop(paste0("The provided teal_module does not return ", output_name)) } + stopifnot(is.reactive(original_outputs[[output_name]])) - modified_report_card <- reactive({ - modify_fun(original_outputs$report_card()) + modified_output <- reactive({ + modify_fun(original_outputs[[output_name]]()) }) - return(modifyList(original_outputs, list(report_card = modified_report_card))) + named_modified_output <- setNames(list(modified_output), output_name) + return(modifyList(original_outputs, named_modified_output)) + }) - } + }) - new_module <- module( - label = paste0(teal_module$label, " (Modified)"), + module( + label = teal_module$label, server = new_server, - ui = teal_module$ui, + ui = new_ui, datanames = teal_module$datanames, server_args = teal_module$server_args, ui_args = teal_module$ui_args, transformators = teal_module$transformators ) +} - return(new_module) +modify_teal_module_report_card <- function(teal_module, modify_fun) { + modify_teal_module_output(teal_module, modify_fun, output_name = 'report_card') } -# + +nullify_teal_module_report_card <- function(teal_module) { + modify_teal_module_output(teal_module, modify_fun = function() NULL, output_name = 'report_card') +} + +############################################################## EXAMPLE # # take_3_elements <- function(x){ # teal.reporter::edit_report_document(x, modify = 1:3) # } # -# -# # devtools::load_all('../teal.reporter') # devtools::load_all('../teal.widgets') # devtools::load_all('../teal.code') -# devtools::load_all('../teal.modules.general') # devtools::load_all('.') +# devtools::load_all('../teal.modules.general') # -# # general data example # data <- teal_data() # data <- within(data, { # require(nestcolor) @@ -77,11 +164,11 @@ modify_teal_module_report <- function(teal_module, modify_fun) { # fixed = FALSE # ) # ) -# ) |> -# modify_teal_module_report(modify_fun = take_3_elements) +# ) |> modify_teal_module_report_card(take_3_elements) # ) # ) # # if (interactive()) { # shinyApp(app$ui, app$server) # } + From 30118ec1673b8641dced1b48f744723e48816dec Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 10 Apr 2025 15:32:42 +0200 Subject: [PATCH 040/108] introduce reporter parameter, so that you can disable it globally, or locally and you can pre-define reporter --- NEWS.md | 7 +++++++ R/init.R | 9 ++++++--- R/modify_teal_module_report.R | 6 +++++- R/module_nested_tabs.R | 26 ++++++++++++++------------ R/module_teal.R | 12 +++++++----- R/modules.R | 4 ++-- 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/NEWS.md b/NEWS.md index 899f3c5f13..939068305f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,13 @@ # teal 0.16.0.9003 +### New features + +* `init` and `srv_teal` have new `reporter` parameter, that allows to predefine `teal.reporter::Reporter` object to be +used for storing the content of the report. You can also globally disable reporting by setting `reporter = NULL` +(and `disable = TRUE` in `ui_teal` for cases when `ui_teal` is used as shiny module). +* TODO: verify if we need to clone/deep_clone reporter in `srv_teal/init`. + # teal 0.16.0 ### New features diff --git a/R/init.R b/R/init.R index c8d9265e22..7d66b2e5d4 100644 --- a/R/init.R +++ b/R/init.R @@ -34,6 +34,7 @@ #' a string specifying the `shiny` module id in cases it is used as a `shiny` module #' rather than a standalone `shiny` app. #' This parameter is no longer supported. Use [ui_teal()] and [srv_teal()] instead. +#' @param reporter (`Reporter`) object used to store report contents. Set to `NULL` to globally disable reporting. #' #' @return Named list containing server and UI functions. #' @@ -99,7 +100,8 @@ init <- function(data, title = lifecycle::deprecated(), header = lifecycle::deprecated(), footer = lifecycle::deprecated(), - id = lifecycle::deprecated()) { + id = lifecycle::deprecated(), + reporter = teal.reporter::Reporter$new()) { logger::log_debug("init initializing teal app with: data ('{ class(data) }').") # argument checking (independent) @@ -221,7 +223,8 @@ init <- function(data, ), ui_teal( id = "teal", - modules = modules + modules = modules, + disable = is.null(reporter) ), tags$footer( id = "teal-footer", @@ -231,7 +234,7 @@ init <- function(data, ) }, server = function(input, output, session) { - srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter)) + srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter), reporter = reporter) srv_session_info("teal-footer-session_info") } ), diff --git a/R/modify_teal_module_report.R b/R/modify_teal_module_report.R index 7165151d0d..3be4cac255 100644 --- a/R/modify_teal_module_report.R +++ b/R/modify_teal_module_report.R @@ -118,9 +118,12 @@ modify_teal_module_report_card <- function(teal_module, modify_fun) { } nullify_teal_module_report_card <- function(teal_module) { - modify_teal_module_output(teal_module, modify_fun = function() NULL, output_name = 'report_card') + modify_teal_module_output(teal_module, modify_fun = function(report_card) NULL, output_name = 'report_card') } +disable_teal_module_report <- nullify_teal_module_report_card + + ############################################################## EXAMPLE # # take_3_elements <- function(x){ @@ -172,3 +175,4 @@ nullify_teal_module_report_card <- function(teal_module) { # shinyApp(app$ui, app$server) # } + diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index fa933bcd54..89b5d7f953 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -230,7 +230,7 @@ srv_teal_module <- function(id, checkmate::assert_multi_class(modules, c("teal_modules", "teal_module")) assert_reactive(datasets, null.ok = TRUE) checkmate::assert_class(slices_global, ".slicesGlobal") - checkmate::assert_class(reporter, "Reporter") + checkmate::assert_class(reporter, "Reporter", null.ok = TRUE) assert_reactive(data_load_status) UseMethod("srv_teal_module", modules) } @@ -414,18 +414,20 @@ srv_teal_module.teal_module <- function(id, } }) - reporter_card_out <- reactive({ - card <- if (is.list(module_out())) { - module_out()$report_card() - } - }) + if (!is.null(reporter)) { + reporter_card_out <- reactive({ + card <- if (is.list(module_out())) { + module_out()$report_card() + } + }) - output$reporter_add_container <- renderUI({ - req(reporter_card_out()) - teal.reporter::add_card_button_ui(session$ns("reporter_add")) - }) + output$reporter_add_container <- renderUI({ + req(reporter_card_out()) + teal.reporter::add_card_button_ui(session$ns("reporter_add")) + }) - add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) + add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) + } module_out }) @@ -437,7 +439,7 @@ srv_teal_module.teal_module <- function(id, # collect arguments to run teal_module args <- c(list(id = "module"), modules$server_args) - if (is_arg_used(modules$server, "reporter")) { + if (is_arg_used(modules$server, "reporter") && !is.null(reporter)) { args <- c(args, list(reporter = reporter)) } diff --git a/R/module_teal.R b/R/module_teal.R index 18d5c8fde2..7cfd4ebe61 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -40,18 +40,20 @@ #' `teal_modules` object. These are the specific output modules which #' will be displayed in the `teal` application. See [modules()] and [module()] for #' more details. +#' @param reporter (`Reporter`) object used to store report contents. Set to `NULL` to globally disable reporting. +#' @param disable Whether to disable `reporter` in `ui_teal`. Useful for using `ui_teal` and `srv_teal` as shiny module. #' #' @return `NULL` invisibly NULL #' @rdname module_teal #' @export -ui_teal <- function(id, modules) { +ui_teal <- function(id, modules, disable = FALSE) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) checkmate::assert_class(modules, "teal_modules") ns <- NS(id) - modules <- append_reporter_module(modules) + modules <- append_reporter_module(modules, disable = disable) # show busy icon when `shiny` session is busy computing stuff # based on https://stackoverflow.com/questions/17325521/r-shiny-display-loading-message-while-function-is-running/22475216#22475216 # nolint: line_length. @@ -103,13 +105,13 @@ ui_teal <- function(id, modules) { #' @rdname module_teal #' @export -srv_teal <- function(id, data, modules, filter = teal_slices()) { +srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal.reporter::Reporter$new()) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) checkmate::assert_multi_class(data, c("teal_data", "teal_data_module", "reactive")) checkmate::assert_class(modules, "teal_modules") checkmate::assert_class(filter, "teal_slices") - modules <- append_reporter_module(modules) + modules <- append_reporter_module(modules, disable = is.null(reporter)) moduleServer(id, function(input, output, session) { logger::log_debug("srv_teal initializing.") @@ -215,7 +217,7 @@ srv_teal <- function(id, data, modules, filter = teal_slices()) { ) } - reporter <- teal.reporter::Reporter$new()$set_id(attr(filter, "app_id")) + if (!is.null(reporter)) reporter$set_id(attr(filter, "app_id")) module_labels <- unlist(module_labels(modules), use.names = FALSE) slices_global <- methods::new(".slicesGlobal", filter, module_labels) modules_output <- srv_teal_module( diff --git a/R/modules.R b/R/modules.R index 9306a09764..7e953965ba 100644 --- a/R/modules.R +++ b/R/modules.R @@ -648,8 +648,8 @@ append_module <- function(modules, module) { #' @rdname module_teal #' @keywords internal #' @noRd -append_reporter_module <- function(modules) { - if (is_arg_used(modules, "reporter") && length(extract_module(modules, "teal_module_previewer")) == 0) { +append_reporter_module <- function(modules, disable) { + if (!disable && is_arg_used(modules, "reporter") && length(extract_module(modules, "teal_module_previewer")) == 0) { modules <- append_module( modules, reporter_previewer_module(server_args = list(previewer_buttons = c("download", "load", "reset"))) From 4daffe444cb0e56a374e34f788cedff25c43abab Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 30 Apr 2025 12:01:35 +0200 Subject: [PATCH 041/108] bring back comments to addcard modal --- R/teal_reporter.R | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 4fe4b5b5db..e42422ddb2 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -224,6 +224,13 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { placeholder = "Add the card title here", width = "100%" ), + shiny::textAreaInput( + ns("comment"), + "Comment", + value = "", + placeholder = "Add a comment here...", + width = "100%" + ), shiny::tags$script( shiny::HTML( sprintf( @@ -279,6 +286,7 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { } else { card <- r_card_fun() + card <- teal.reporter::edit_report_document(card, append = input$comment, after = 0) #card <- to_markdown(card) lcard <- list(card) names(lcard) <- input$label From ec5141a18b16b71954bad01c47be202ff33ba8ff Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Fri, 2 May 2025 14:15:50 +0200 Subject: [PATCH 042/108] clone reporter --- R/init.R | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/R/init.R b/R/init.R index 7d66b2e5d4..cc3d5e6ca5 100644 --- a/R/init.R +++ b/R/init.R @@ -223,8 +223,7 @@ init <- function(data, ), ui_teal( id = "teal", - modules = modules, - disable = is.null(reporter) + modules = modules ), tags$footer( id = "teal-footer", @@ -234,7 +233,13 @@ init <- function(data, ) }, server = function(input, output, session) { - srv_teal(id = "teal", data = data, modules = modules, filter = deep_copy_filter(filter), reporter = reporter) + srv_teal( + id = "teal", + data = data, + modules = modules, + filter = deep_copy_filter(filter), + reporter = reporter$clone(deep = TRUE) + ) srv_session_info("teal-footer-session_info") } ), From ea2c7e5ae4e26df3cd405b3e3cf6293a11aa4148 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Mon, 5 May 2025 15:07:12 +0200 Subject: [PATCH 043/108] fix error when report is NULL (no method NULL$clone) --- R/init.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/init.R b/R/init.R index cc3d5e6ca5..8dcbc4892d 100644 --- a/R/init.R +++ b/R/init.R @@ -185,7 +185,6 @@ init <- function(data, landing <- extract_module(modules, "teal_module_landing") modules <- drop_module(modules, "teal_module_landing") - if (lifecycle::is_present(id)) { lifecycle::deprecate_soft( when = "0.16.0", @@ -238,7 +237,7 @@ init <- function(data, data = data, modules = modules, filter = deep_copy_filter(filter), - reporter = reporter$clone(deep = TRUE) + reporter = if (!is.null(reporter)) reporter$clone(deep = TRUE) ) srv_session_info("teal-footer-session_info") } From 2fad14025beaafc9421f76b7fbdf0ab5f9c79848 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Mon, 5 May 2025 15:08:57 +0200 Subject: [PATCH 044/108] - always add report_previewer_module - display report previewer tab only when report added --- R/module_nested_tabs.R | 20 +++++++------------- R/module_teal.R | 41 +++++++++++++++++++++++++++++++---------- R/modules.R | 6 +++--- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 89b5d7f953..2a5ae6acee 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -399,19 +399,13 @@ srv_teal_module.teal_module <- function(id, }) # Call modules. - if (!inherits(modules, "teal_module_previewer")) { - obs_module <- .call_once_when( - !is.null(module_teal_data()), - ignoreNULL = TRUE, - handlerExpr = { - module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) - } - ) - } else { - # Report previewer must be initiated on app start for report cards to be included in bookmarks. - # When previewer is delayed, cards are bookmarked only if previewer has been initiated (visited). - module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) - } + obs_module <- .call_once_when( + !is.null(module_teal_data()), + ignoreNULL = TRUE, + handlerExpr = { + module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) + } + ) }) if (!is.null(reporter)) { diff --git a/R/module_teal.R b/R/module_teal.R index cc6859a613..4555436135 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -48,13 +48,11 @@ NULL #' @rdname module_teal #' @export -ui_teal <- function(id, modules, disable = FALSE) { +ui_teal <- function(id, modules) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) checkmate::assert_class(modules, "teal_modules") ns <- NS(id) - - modules <- append_reporter_module(modules, disable = disable) - + modules <- drop_module(modules, "teal_previewer_module") # show busy icon when `shiny` session is busy computing stuff # based on https://stackoverflow.com/questions/17325521/r-shiny-display-loading-message-while-function-is-running/22475216#22475216 # nolint: line_length. shiny_busy_message_panel <- conditionalPanel( @@ -110,9 +108,7 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. checkmate::assert_multi_class(data, c("teal_data", "teal_data_module", "reactive")) checkmate::assert_class(modules, "teal_modules") checkmate::assert_class(filter, "teal_slices") - - modules <- append_reporter_module(modules, disable = is.null(reporter)) - + modules <- append_reporter_module(modules) moduleServer(id, function(input, output, session) { logger::log_debug("srv_teal initializing.") @@ -183,8 +179,6 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. }) } - - if (inherits(data, "teal_data_module")) { setBookmarkExclude(c("teal_modules-active_tab")) bslib::nav_insert( @@ -217,7 +211,34 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. ) } - if (!is.null(reporter)) reporter$set_id(attr(filter, "app_id")) + if (!is.null(reporter)) { + reporter$set_id(attr(filter, "app_id")) + reporter_module <- extract_module(modules, "teal_module_previewer")[[1]] + modules <- drop_module(modules, "teal_module_previewer") + + # call once + do.call( + reporter_module$server, + args = c(list(id = "report_previewer", reporter = reporter), reporter_module$server_args) + ) + + previewer_ui <- do.call( + reporter_module$ui, + args = c(list(id = session$ns("report_previewer")), reporter_module$ui_args) + ) + + observeEvent(reporter$get_reactive_add_card(), { + if (reporter$get_reactive_add_card() > 0) { + bslib::nav_insert( + id = "teal_modules-active_tab", + nav = bslib::nav_panel(title = reporter_module$label, previewer_ui) + ) + } else { + bslib::nav_remove(id = "teal_modules-active_tab", target = reporter_module$label) + } + }) + } + module_labels <- unlist(module_labels(modules), use.names = FALSE) slices_global <- methods::new(".slicesGlobal", filter, module_labels) modules_output <- srv_teal_module( diff --git a/R/modules.R b/R/modules.R index 7e953965ba..7d6da35fae 100644 --- a/R/modules.R +++ b/R/modules.R @@ -648,11 +648,11 @@ append_module <- function(modules, module) { #' @rdname module_teal #' @keywords internal #' @noRd -append_reporter_module <- function(modules, disable) { - if (!disable && is_arg_used(modules, "reporter") && length(extract_module(modules, "teal_module_previewer")) == 0) { +append_reporter_module <- function(modules) { + if (length(extract_module(modules, "teal_module_previewer")) == 0) { modules <- append_module( modules, - reporter_previewer_module(server_args = list(previewer_buttons = c("download", "load", "reset"))) + reporter_previewer_module(server_args = list(previewer_buttons = c("download", "reset"))) ) } modules From a8201af5d35edf8c3dc5e761bf6235589bd1b65f Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Mon, 5 May 2025 15:09:27 +0200 Subject: [PATCH 045/108] disable_report instead of nullify_teal_module_report_card --- R/modify_teal_module_report.R | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/R/modify_teal_module_report.R b/R/modify_teal_module_report.R index 3be4cac255..5d3b40dd84 100644 --- a/R/modify_teal_module_report.R +++ b/R/modify_teal_module_report.R @@ -1,4 +1,3 @@ - # This function is just here, so it is easier to understand what's happening in # modify_teal_module_output. # We want to have a match in parameter names in new_server and original_server @@ -11,9 +10,9 @@ modify_teal_module_output_named_params <- function(teal_module, modify_fun, outp original_server <- teal_module$server original_ui <- teal_module$ui - new_ui <- function(id, ...){ + new_ui <- function(id, ...) { ns <- NS(id) - original_ui(ns('modified'), ...) + original_ui(ns("modified"), ...) } new_server <- function(id, @@ -26,12 +25,10 @@ modify_teal_module_output_named_params <- function(teal_module, modify_fun, outp plot_width, ggplot2_args, default_outlier_label, - decorators) - + decorators) { moduleServer(id, function(input, output, session) { - original_outputs <- original_server( - 'modified', + "modified", data, filter_panel_api, response = response, @@ -45,6 +42,7 @@ modify_teal_module_output_named_params <- function(teal_module, modify_fun, outp ) # work on original_outputs }) + } module( label = teal_module$label, @@ -63,9 +61,9 @@ modify_teal_module_output <- function(teal_module, modify_fun, output_name) { original_server <- teal_module$server original_ui <- teal_module$ui - new_ui <- function(id, ...){ + new_ui <- function(id, ...) { ns <- NS(id) - original_ui(ns('modified'), ...) + original_ui(ns("modified"), ...) } original_server <- teal_module$server @@ -73,19 +71,18 @@ modify_teal_module_output <- function(teal_module, modify_fun, output_name) { formals(new_server) <- formals(original_server) body(new_server) <- quote({ - arg_names <- names(formals(original_server)) # Capture current call and evaluate each argument call_expr <- match.call() - call_args <- as.list(call_expr)[-1] # remove function name + call_args <- as.list(call_expr)[-1] # remove function name call_args <- call_args[names(call_args) %in% arg_names] # Evaluate all arguments in the current environment eval_args <- lapply(call_args, eval, envir = parent.frame()) moduleServer(id, function(input, output, session) { - eval_args$id <- 'modified' + eval_args$id <- "modified" original_outputs <- do.call(original_server, eval_args) if (!output_name %in% names(original_outputs)) { stop(paste0("The provided teal_module does not return ", output_name)) @@ -98,7 +95,6 @@ modify_teal_module_output <- function(teal_module, modify_fun, output_name) { named_modified_output <- setNames(list(modified_output), output_name) return(modifyList(original_outputs, named_modified_output)) - }) }) @@ -114,14 +110,14 @@ modify_teal_module_output <- function(teal_module, modify_fun, output_name) { } modify_teal_module_report_card <- function(teal_module, modify_fun) { - modify_teal_module_output(teal_module, modify_fun, output_name = 'report_card') + modify_teal_module_output(teal_module, modify_fun, output_name = "report_card") } nullify_teal_module_report_card <- function(teal_module) { - modify_teal_module_output(teal_module, modify_fun = function(report_card) NULL, output_name = 'report_card') + modify_teal_module_output(teal_module, modify_fun = function(report_card) NULL, output_name = "report_card") } -disable_teal_module_report <- nullify_teal_module_report_card +disable_report <- nullify_teal_module_report_card ############################################################## EXAMPLE @@ -174,5 +170,3 @@ disable_teal_module_report <- nullify_teal_module_report_card # if (interactive()) { # shinyApp(app$ui, app$server) # } - - From 0201c7a678f91acec456d3ae3223bfb978e80809 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 6 May 2025 05:51:26 +0200 Subject: [PATCH 046/108] - rename modifiers - show previewer if reporter used --- R/dummy_functions.R | 2 +- ...dule_report.R => modify_reactive_output.R} | 98 +++++-------------- R/module_teal.R | 57 ++++++----- 3 files changed, 62 insertions(+), 95 deletions(-) rename R/{modify_teal_module_report.R => modify_reactive_output.R} (50%) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index e27e135db4..656df01b55 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -36,7 +36,7 @@ example_module <- function(label = "example teal module", ans <- module( label, - server = function(id, data, reporter, decorators) { + server = function(id, data, decorators) { checkmate::assert_class(isolate(data()), "teal_data") moduleServer(id, function(input, output, session) { datanames_rv <- reactive(names(req(data()))) diff --git a/R/modify_teal_module_report.R b/R/modify_reactive_output.R similarity index 50% rename from R/modify_teal_module_report.R rename to R/modify_reactive_output.R index 5d3b40dd84..a3c546fe97 100644 --- a/R/modify_teal_module_report.R +++ b/R/modify_reactive_output.R @@ -1,64 +1,11 @@ # This function is just here, so it is easier to understand what's happening in -# modify_teal_module_output. +# modify_reactive_output # We want to have a match in parameter names in new_server and original_server # assuming we don't know names of parameters in original_server. -# modify_teal_module_output is the final implementation that shows how to do it without knowing parameters. -# modify_teal_module_output_named_params is just here to show how would that look like for a custom and stable -# set of parameter names. -modify_teal_module_output_named_params <- function(teal_module, modify_fun, output_name) { - stopifnot(inherits(teal_module, "teal_module")) - - original_server <- teal_module$server - original_ui <- teal_module$ui - new_ui <- function(id, ...) { - ns <- NS(id) - original_ui(ns("modified"), ...) - } - - new_server <- function(id, - data, - filter_panel_api, - response, - regressor, - reporter, - plot_height, - plot_width, - ggplot2_args, - default_outlier_label, - decorators) { - moduleServer(id, function(input, output, session) { - original_outputs <- original_server( - "modified", - data, - filter_panel_api, - response = response, - regressor = regressor, - reporter, - plot_height, - plot_width, - ggplot2_args, - default_outlier_label, - decorators - ) - # work on original_outputs - }) - } - - module( - label = teal_module$label, - server = new_server, - ui = new_ui, - datanames = teal_module$datanames, - server_args = teal_module$server_args, - ui_args = teal_module$ui_args, - transformators = teal_module$transformators - ) -} - - -modify_teal_module_output <- function(teal_module, modify_fun, output_name) { - stopifnot(inherits(teal_module, "teal_module")) - +# modify_reactive_output is the final implementation that shows how to do it without knowing parameters. +modify_reactive_output <- function(teal_module, ...) { + checkmate::assert_class(teal_module, "teal_module") + output_funs <- list(...) original_server <- teal_module$server original_ui <- teal_module$ui new_ui <- function(id, ...) { @@ -84,17 +31,26 @@ modify_teal_module_output <- function(teal_module, modify_fun, output_name) { moduleServer(id, function(input, output, session) { eval_args$id <- "modified" original_outputs <- do.call(original_server, eval_args) - if (!output_name %in% names(original_outputs)) { - stop(paste0("The provided teal_module does not return ", output_name)) + + missing_output_names <- setdiff(names(output_funs), names(original_outputs)) + if (length(missing_output_names)) { + stop("The provided teal_module does not return: ", toString(missing_output_names)) } - stopifnot(is.reactive(original_outputs[[output_name]])) - modified_output <- reactive({ - modify_fun(original_outputs[[output_name]]()) - }) + modified_output <- sapply( + names(output_funs), + function(output_name) { + res <- if (is.reactive(original_outputs[[output_name]])) { + original_outputs[[output_name]]() + } else { + original_outputs[[output_name]] + } + output_funs[[output_name]](res) + }, + USE.NAMES = TRUE + ) - named_modified_output <- setNames(list(modified_output), output_name) - return(modifyList(original_outputs, named_modified_output)) + return(modifyList(original_outputs, modified_output)) }) }) @@ -109,16 +65,14 @@ modify_teal_module_output <- function(teal_module, modify_fun, output_name) { ) } -modify_teal_module_report_card <- function(teal_module, modify_fun) { - modify_teal_module_output(teal_module, modify_fun, output_name = "report_card") +modify_report_card <- function(teal_module, modify_fun) { + modify_reactive_output(teal_module, report_card = modify_fun) } -nullify_teal_module_report_card <- function(teal_module) { - modify_teal_module_output(teal_module, modify_fun = function(report_card) NULL, output_name = "report_card") +disable_report <- function(teal_module) { + modify_reactive_output(teal_module, report_card = function(report_card) NULL) } -disable_report <- nullify_teal_module_report_card - ############################################################## EXAMPLE # diff --git a/R/module_teal.R b/R/module_teal.R index 4555436135..a6dea26676 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -211,45 +211,58 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. ) } + module_labels <- unlist(module_labels(modules), use.names = FALSE) + slices_global <- methods::new(".slicesGlobal", filter, module_labels) + modules_output <- srv_teal_module( + id = "teal_modules", + data = data_signatured, + datasets = datasets_rv, + modules = modules, + slices_global = slices_global, + data_load_status = data_load_status, + reporter = reporter + ) + if (!is.null(reporter)) { reporter$set_id(attr(filter, "app_id")) reporter_module <- extract_module(modules, "teal_module_previewer")[[1]] modules <- drop_module(modules, "teal_module_previewer") - # call once - do.call( + previewer_out <- do.call( # out for testing reporter_module$server, args = c(list(id = "report_previewer", reporter = reporter), reporter_module$server_args) ) - previewer_ui <- do.call( reporter_module$ui, args = c(list(id = session$ns("report_previewer")), reporter_module$ui_args) ) - observeEvent(reporter$get_reactive_add_card(), { - if (reporter$get_reactive_add_card() > 0) { - bslib::nav_insert( - id = "teal_modules-active_tab", - nav = bslib::nav_panel(title = reporter_module$label, previewer_ui) - ) + # Report Previewer tab needs to be shown only if any module has a reporter functionality + any_use_reporter <- reactive({ + f <- function(x) { + if (is.function(x)) { + f(x()) + } else if ("report_card" %in% names(x)) { + TRUE + } else if (is.list(x)) { + sapply(x, f) + } + } + any(unlist(f(modules_output))) || is_arg_used(modules, "reporter") + }) + # always insert reporter + bslib::nav_insert( + id = "teal_modules-active_tab", + nav = bslib::nav_panel(title = reporter_module$label, previewer_ui) + ) + observeEvent(any_use_reporter(), { + if (any_use_reporter()) { + bslib::nav_show(id = "teal_modules-active_tab", target = reporter_module$label) } else { - bslib::nav_remove(id = "teal_modules-active_tab", target = reporter_module$label) + bslib::nav_hide(id = "teal_modules-active_tab", target = reporter_module$label) } }) } - - module_labels <- unlist(module_labels(modules), use.names = FALSE) - slices_global <- methods::new(".slicesGlobal", filter, module_labels) - modules_output <- srv_teal_module( - id = "teal_modules", - data = data_signatured, - datasets = datasets_rv, - modules = modules, - slices_global = slices_global, - data_load_status = data_load_status, - reporter = reporter - ) mapping_table <- srv_filter_manager_panel("filter_manager_panel", slices_global = slices_global) snapshots <- srv_snapshot_manager_panel("snapshot_manager_panel", slices_global = slices_global) srv_bookmark_panel("bookmark_manager", modules) From 4bf6e991117c5a79ad47d81bb45845be8444afb4 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 6 May 2025 05:52:36 +0200 Subject: [PATCH 047/108] reload docs --- DESCRIPTION | 1 + man/add_document_button_srv.Rd | 12 ++++++++++++ man/init.Rd | 5 ++++- man/module_teal.Rd | 12 +++++++++++- man/module_teal_module.Rd | 2 ++ 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 man/add_document_button_srv.Rd diff --git a/DESCRIPTION b/DESCRIPTION index d4c9cc1a8c..69390477b7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -107,6 +107,7 @@ Collate: 'modules.R' 'init.R' 'landing_popup_module.R' + 'modify_reactive_output.R' 'module_bookmark_manager.R' 'module_data_summary.R' 'module_filter_data.R' diff --git a/man/add_document_button_srv.Rd b/man/add_document_button_srv.Rd new file mode 100644 index 0000000000..6037b18159 --- /dev/null +++ b/man/add_document_button_srv.Rd @@ -0,0 +1,12 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/teal_reporter.R +\name{add_document_button_srv} +\alias{add_document_button_srv} +\title{Server function for \code{ReportDocument} card class.} +\usage{ +add_document_button_srv(id, reporter, r_card_fun) +} +\description{ +Server function for \code{ReportDocument} card class. +} +\keyword{internal} diff --git a/man/init.Rd b/man/init.Rd index fcf18d116d..e02d66dcc7 100644 --- a/man/init.Rd +++ b/man/init.Rd @@ -11,7 +11,8 @@ init( title = lifecycle::deprecated(), header = lifecycle::deprecated(), footer = lifecycle::deprecated(), - id = lifecycle::deprecated() + id = lifecycle::deprecated(), + reporter = teal.reporter::Reporter$new() ) } \arguments{ @@ -45,6 +46,8 @@ This parameter is no longer supported. Use \code{modify_footer()} on the teal ap a string specifying the \code{shiny} module id in cases it is used as a \code{shiny} module rather than a standalone \code{shiny} app. This parameter is no longer supported. Use \code{\link[=ui_teal]{ui_teal()}} and \code{\link[=srv_teal]{srv_teal()}} instead.} + +\item{reporter}{(\code{Reporter}) object used to store report contents. Set to \code{NULL} to globally disable reporting.} } \value{ Named list containing server and UI functions. diff --git a/man/module_teal.Rd b/man/module_teal.Rd index df3329ed76..4ca48331f5 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -8,7 +8,13 @@ \usage{ ui_teal(id, modules) -srv_teal(id, data, modules, filter = teal_slices()) +srv_teal( + id, + data, + modules, + filter = teal_slices(), + reporter = teal.reporter::Reporter$new() +) } \arguments{ \item{id}{(\code{character(1)}) \code{shiny} module instance id.} @@ -23,6 +29,10 @@ The data which application will depend on.} \item{filter}{(\code{teal_slices}) Optionally, specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} + +\item{reporter}{(\code{Reporter}) object used to store report contents. Set to \code{NULL} to globally disable reporting.} + +\item{disable}{Whether to disable \code{reporter} in \code{ui_teal}. Useful for using \code{ui_teal} and \code{srv_teal} as shiny module.} } \value{ \code{NULL} invisibly diff --git a/man/module_teal_module.Rd b/man/module_teal_module.Rd index c12912dbd4..87f5c91f6d 100644 --- a/man/module_teal_module.Rd +++ b/man/module_teal_module.Rd @@ -84,6 +84,8 @@ which implies in filter-panel to be "global". When \code{NULL} then filter-panel \item{slices_global}{(\code{reactiveVal} returning \code{modules_teal_slices}) see \code{\link{module_filter_manager}}} +\item{reporter}{(\code{Reporter}) object used to store report contents. Set to \code{NULL} to globally disable reporting.} + \item{data_load_status}{(\code{reactive} returning \code{character}) Determines action dependent on a data loading status: \itemize{ From 6cf260d745d109cc618ddfd874245a6cd1cc6616 Mon Sep 17 00:00:00 2001 From: vedhav Date: Tue, 6 May 2025 14:07:04 +0530 Subject: [PATCH 048/108] feat: move the reporter button --- R/module_nested_tabs.R | 13 +++++++------ inst/css/custom.css | 6 ++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 2a5ae6acee..de73fe671b 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -111,6 +111,8 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { class = "teal_validated", ui_check_module_datanames(ns("validate_datanames")) ), + uiOutput(ns("reporter_add_container")), + # uiOutput(ns("show_rcode_container")) # todo: same mechanism as for the reporter do.call(what = modules$ui, args = args, quote = TRUE) ) ) @@ -207,11 +209,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { ) } else { ui_teal - }, - div( # todo: fix the position to the top right of the tab content? - uiOutput(ns("reporter_add_container")) - # uiOutput(ns("show_rcode_container")) # todo: same mechanism as for the reporter - ) + } ) ) } @@ -417,7 +415,10 @@ srv_teal_module.teal_module <- function(id, output$reporter_add_container <- renderUI({ req(reporter_card_out()) - teal.reporter::add_card_button_ui(session$ns("reporter_add")) + tags$div( + class = "teal add-reporter-container", + teal.reporter::add_card_button_ui(session$ns("reporter_add")) + ) }) add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) diff --git a/inst/css/custom.css b/inst/css/custom.css index 18386de7f5..d28ff4044d 100644 --- a/inst/css/custom.css +++ b/inst/css/custom.css @@ -53,3 +53,9 @@ body > div:has(~ #shiny-modal-wrapper .blur_background) { .teal-body .bslib-mb-spacing { --bslib-mb-spacer: 0; } + +.teal.add-reporter-container { + display: flex; + justify-content: flex-end; + padding: 10px; +} From 8be8963d0f4e04eaa944d4e8a6d84da0e936df1a Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 7 May 2025 13:37:45 +0200 Subject: [PATCH 049/108] prevent adding a card with the existing name --- R/teal_reporter.R | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index e42422ddb2..908730533d 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -284,17 +284,37 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { type = "error" ) } else { + new_card_name <- trimws(input$label) - card <- r_card_fun() - card <- teal.reporter::edit_report_document(card, append = input$comment, after = 0) - #card <- to_markdown(card) - lcard <- list(card) - names(lcard) <- input$label + if (nchar(new_card_name) == 0) { + shiny::showNotification( + "Card name cannot be empty.", + type = "error", + duration = 5 + ) + return(NULL) + } + existing_card_names <- names(reporter$get_cards()) - reporter$append_cards(lcard) + if (new_card_name %in% existing_card_names) { + shiny::showNotification( + paste("A card with the name '", new_card_name, "' already exists. Please use a different name."), + type = "error", + duration = 5 + ) + } else { - shiny::showNotification(sprintf("The card added successfully."), type = "message") - shiny::removeModal() + card <- r_card_fun() + card <- teal.reporter::edit_report_document(card, append = input$comment, after = 0) + #card <- to_markdown(card) + lcard <- list(card) + names(lcard) <- input$label + + reporter$append_cards(lcard) + + shiny::showNotification(sprintf("The card added successfully."), type = "message") + shiny::removeModal() + } } }) }) From ef03a5b683c08d2151b3c8ffb73bebe1be87ed96 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Thu, 8 May 2025 13:16:08 +0200 Subject: [PATCH 050/108] fix reactivity --- R/modify_reactive_output.R | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/R/modify_reactive_output.R b/R/modify_reactive_output.R index a3c546fe97..abecb0fd6f 100644 --- a/R/modify_reactive_output.R +++ b/R/modify_reactive_output.R @@ -40,12 +40,15 @@ modify_reactive_output <- function(teal_module, ...) { modified_output <- sapply( names(output_funs), function(output_name) { - res <- if (is.reactive(original_outputs[[output_name]])) { - original_outputs[[output_name]]() + if (is.reactive(original_outputs[[output_name]])) { + reactive({ + res <- original_outputs[[output_name]]() + output_funs[[output_name]](res) + }) } else { - original_outputs[[output_name]] + res <- original_outputs[[output_name]] + output_funs[[output_name]](res) } - output_funs[[output_name]](res) }, USE.NAMES = TRUE ) From 35d3ae27064c10c29076b5d91c4f190e8da8f473 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 8 May 2025 13:18:52 +0200 Subject: [PATCH 051/108] restart add card modal becuase the save button gets disabled after a warning --- R/teal_reporter.R | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 908730533d..3b68c7cf14 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -287,6 +287,8 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { new_card_name <- trimws(input$label) if (nchar(new_card_name) == 0) { + shiny::removeModal() # currently the save button is disabled and shinyjs::enable is not working + shiny::showModal(add_modal()) # so we show the modal again shiny::showNotification( "Card name cannot be empty.", type = "error", @@ -297,6 +299,8 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { existing_card_names <- names(reporter$get_cards()) if (new_card_name %in% existing_card_names) { + shiny::removeModal() # currently the save button is disabled and shinyjs::enable is not working + shiny::showModal(add_modal()) # so we show the modal again shiny::showNotification( paste("A card with the name '", new_card_name, "' already exists. Please use a different name."), type = "error", From ab96d228f8e069d0ff31908bb6023f32e110c34d Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 8 May 2025 13:35:10 +0200 Subject: [PATCH 052/108] enable add_card button after unsuccessful card addition --- R/teal_reporter.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 3b68c7cf14..e9f3fc720e 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -287,25 +287,23 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { new_card_name <- trimws(input$label) if (nchar(new_card_name) == 0) { - shiny::removeModal() # currently the save button is disabled and shinyjs::enable is not working - shiny::showModal(add_modal()) # so we show the modal again shiny::showNotification( "Card name cannot be empty.", type = "error", duration = 5 ) + shinyjs::enable("add_card_ok") return(NULL) } existing_card_names <- names(reporter$get_cards()) if (new_card_name %in% existing_card_names) { - shiny::removeModal() # currently the save button is disabled and shinyjs::enable is not working - shiny::showModal(add_modal()) # so we show the modal again shiny::showNotification( paste("A card with the name '", new_card_name, "' already exists. Please use a different name."), type = "error", duration = 5 ) + shinyjs::enable("add_card_ok") } else { card <- r_card_fun() From 0372479e6dc0483575cb2476389f5004c3409dd4 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 8 May 2025 14:28:38 +0200 Subject: [PATCH 053/108] documentation and checks for modify_reactive_output --- NAMESPACE | 1 + R/modify_reactive_output.R | 45 +++++++++++++++++++++++++++++++---- man/modify_reactive_output.Rd | 35 +++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 man/modify_reactive_output.Rd diff --git a/NAMESPACE b/NAMESPACE index ac53893f97..c74c8c165b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -26,6 +26,7 @@ export(landing_popup_module) export(make_teal_transform_server) export(modify_footer) export(modify_header) +export(modify_reactive_output) export(modify_title) export(module) export(modules) diff --git a/R/modify_reactive_output.R b/R/modify_reactive_output.R index abecb0fd6f..3ee6edbf1c 100644 --- a/R/modify_reactive_output.R +++ b/R/modify_reactive_output.R @@ -1,11 +1,46 @@ -# This function is just here, so it is easier to understand what's happening in -# modify_reactive_output -# We want to have a match in parameter names in new_server and original_server -# assuming we don't know names of parameters in original_server. -# modify_reactive_output is the final implementation that shows how to do it without knowing parameters. +#' Modify Reactive Outputs of a `teal` Module +#' +#' @description +#' This function takes a `teal_module` object and allows modification of its +#' server's reactive outputs. It wraps the original module's server, +#' applying specified functions to one or more of the outputs returned by the +#' original server. +#' +#' The original server logic is called within a new server function. +#' The transformations are applied to the outputs of the original server +#' before they are returned by the new server. +#' +#' @param teal_module (`teal_module`) +#' @param ... Named arguments where each name corresponds to an output element +#' (e.g., a reactive expression or a static value) returned by the +#' `teal_module`'s server function. The value for each named argument must be a +#' function that takes the original output's value as its single argument and +#' returns the modified value. If the original output is a reactive, its +#' resolved value is passed to the modifying function, and the modifying +#' function's result is wrapped in a new reactive. +#' +#' @return A new `teal_module` object with the modified server logic and a UI +#' that internally namespaces the original UI. The new module will have the +#' same label, datanames, server_args, ui_args, and transformators as the +#' original `teal_module`. +#' +#' @export modify_reactive_output <- function(teal_module, ...) { checkmate::assert_class(teal_module, "teal_module") output_funs <- list(...) + + if (length(output_funs) == 0) { + warning("No output transformations specified for modify_reactive_output. Returning original module.") + return(teal_module) + } + if (is.null(names(output_funs)) || any(names(output_funs) == "" | duplicated(names(output_funs)))) { + stop("All transformations supplied to modify_reactive_output must be uniquely named.") + } + are_functions <- vapply(output_funs, is.function, logical(1)) + if (!all(are_functions)) { + stop("All transformations supplied to modify_reactive_output must be functions.") + } + original_server <- teal_module$server original_ui <- teal_module$ui new_ui <- function(id, ...) { diff --git a/man/modify_reactive_output.Rd b/man/modify_reactive_output.Rd new file mode 100644 index 0000000000..8b7313934c --- /dev/null +++ b/man/modify_reactive_output.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/modify_reactive_output.R +\name{modify_reactive_output} +\alias{modify_reactive_output} +\title{Modify Reactive Outputs of a \code{teal} Module} +\usage{ +modify_reactive_output(teal_module, ...) +} +\arguments{ +\item{teal_module}{(\code{teal_module})} + +\item{...}{Named arguments where each name corresponds to an output element +(e.g., a reactive expression or a static value) returned by the +\code{teal_module}'s server function. The value for each named argument must be a +function that takes the original output's value as its single argument and +returns the modified value. If the original output is a reactive, its +resolved value is passed to the modifying function, and the modifying +function's result is wrapped in a new reactive.} +} +\value{ +A new \code{teal_module} object with the modified server logic and a UI +that internally namespaces the original UI. The new module will have the +same label, datanames, server_args, ui_args, and transformators as the +original \code{teal_module}. +} +\description{ +This function takes a \code{teal_module} object and allows modification of its +server's reactive outputs. It wraps the original module's server, +applying specified functions to one or more of the outputs returned by the +original server. + +The original server logic is called within a new server function. +The transformations are applied to the outputs of the original server +before they are returned by the new server. +} From 18b524e1377f1e8d67828de91981e6d3ad1a3d26 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 20:15:55 +0000 Subject: [PATCH 054/108] [skip style] [skip vbump] Restyle files --- R/teal_reporter.R | 4 +--- tests/testthat/test-shinytest2-reporter.R | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index e9f3fc720e..323bb15de8 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -305,10 +305,9 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { ) shinyjs::enable("add_card_ok") } else { - card <- r_card_fun() card <- teal.reporter::edit_report_document(card, append = input$comment, after = 0) - #card <- to_markdown(card) + # card <- to_markdown(card) lcard <- list(card) names(lcard) <- input$label @@ -321,4 +320,3 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { }) }) } - diff --git a/tests/testthat/test-shinytest2-reporter.R b/tests/testthat/test-shinytest2-reporter.R index 8976d16476..9b64764450 100644 --- a/tests/testthat/test-shinytest2-reporter.R +++ b/tests/testthat/test-shinytest2-reporter.R @@ -101,7 +101,6 @@ testthat::test_that("e2e: reporter_previewer_module has download, load and reset testthat::expect_true( app$is_visible(app$active_module_element("resetButtonPreviewer-reset_reporter")) ) - }) testthat::test_that("e2e: reporter_previewer_module do not show data_summary nor filter_panel", { From 6c92309812eb203a986b1c07f45f83c05fb76d36 Mon Sep 17 00:00:00 2001 From: vedhav Date: Tue, 13 May 2025 14:53:58 +0530 Subject: [PATCH 055/108] feat: move the reporter button outside the teal module --- R/module_nested_tabs.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index de73fe671b..f18e6b6caa 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -111,8 +111,6 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { class = "teal_validated", ui_check_module_datanames(ns("validate_datanames")) ), - uiOutput(ns("reporter_add_container")), - # uiOutput(ns("show_rcode_container")) # todo: same mechanism as for the reporter do.call(what = modules$ui, args = args, quote = TRUE) ) ) @@ -120,6 +118,8 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { div( id = id, class = "teal_module", + uiOutput(ns("reporter_add_container")), + # uiOutput(ns("show_rcode_container")), # todo: same mechanism as for the reporter uiOutput(ns("data_reactive"), inline = TRUE), tagList( if (depth >= 2L) tags$div(), From d1360e59bfb079da6f9dabbf158466dcd6367800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 15 May 2025 15:30:51 +0200 Subject: [PATCH 056/108] Minor changes from PR review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- DESCRIPTION | 2 +- R/module_nested_tabs.R | 2 +- R/teal_reporter.R | 18 +++++++----------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index cd2e7b6932..77c73b8f7f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -79,7 +79,7 @@ VignetteBuilder: RdMacros: lifecycle Remotes: - insightsengineering/teal.reporter@redesign@main, + insightsengineering/teal.reporter@redesign%40main, insightsengineering/teal.slice@main, insightsengineering/teal.widgets@main Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index f18e6b6caa..67e4988ada 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -408,7 +408,7 @@ srv_teal_module.teal_module <- function(id, if (!is.null(reporter)) { reporter_card_out <- reactive({ - card <- if (is.list(module_out())) { + if (is.list(module_out())) { module_out()$report_card() } }) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 323bb15de8..3f86e228a6 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -273,10 +273,10 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { # please check the ui part for more information shiny::observeEvent(input$add_card_ok, { if (inherits(r_card_fun, "try-error")) { - msg <- paste0( - "The card could not be added to the report. ", - "Have the outputs for the report been created yet? If not please try again when they ", - "are ready. Otherwise contact your application developer" + msg <- paste( + "The card could not be added to the report.", + "Have the outputs for the report been created yet? If not please try again when they", + "are ready. Otherwise contact your application developer." ) warning(msg) shiny::showNotification( @@ -305,15 +305,11 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { ) shinyjs::enable("add_card_ok") } else { - card <- r_card_fun() - card <- teal.reporter::edit_report_document(card, append = input$comment, after = 0) + card <- c(report_document(input$comment), r_card_fun()) # card <- to_markdown(card) - lcard <- list(card) - names(lcard) <- input$label + reporter$append_cards(structure(list(card), names = input$label)) - reporter$append_cards(lcard) - - shiny::showNotification(sprintf("The card added successfully."), type = "message") + shiny::showNotification("The card added successfully.", type = "message") shiny::removeModal() } } From 93cd49c444d799a1bca1685c05e6d4f54543dfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 15 May 2025 14:54:19 +0100 Subject: [PATCH 057/108] feat: use common structure for js autofocus input and enter to submit --- R/module_snapshot_manager.R | 6 ++++++ R/teal_reporter.R | 10 ++-------- inst/js/init.js | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/R/module_snapshot_manager.R b/R/module_snapshot_manager.R index 321d7a8af0..b521450363 100644 --- a/R/module_snapshot_manager.R +++ b/R/module_snapshot_manager.R @@ -171,6 +171,12 @@ srv_snapshot_manager <- function(id, slices_global) { showModal( modalDialog( textInput(ns("snapshot_name"), "Name the snapshot", width = "100%", placeholder = "Meaningful, unique name"), + tags$script( + shiny::HTML( + sprintf("shinyjs.autoFocusModal('%s');", ns("snapshot_name")), + sprintf("shinyjs.enterToSubmit('%s', '%s');", ns("snapshot_name"), ns("snapshot_name_accept")) + ) + ), footer = tagList( actionButton(ns("snapshot_name_accept"), "Accept", icon = icon("far fa-thumbs-up")), modalButton(label = "Cancel", icon = icon("far fa-thumbs-down")) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 3f86e228a6..8e4c17271a 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -233,14 +233,8 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { ), shiny::tags$script( shiny::HTML( - sprintf( - " - $('#shiny-modal').on('shown.bs.modal', () => { - $('#%s').focus() - }) - ", - ns("label") - ) + sprintf("shinyjs.autoFocusModal('%s');", ns("label")), + sprintf("shinyjs.enterToSubmit('%s', '%s');", ns("label"), ns("add_card_ok")) ) ), footer = shiny::div( diff --git a/inst/js/init.js b/inst/js/init.js index 69e9b7a27b..49eb4b9e2a 100644 --- a/inst/js/init.js +++ b/inst/js/init.js @@ -3,3 +3,23 @@ // this code alows the show R code "copy to clipbaord" button to work var clipboard = new ClipboardJS(".btn[data-clipboard-target]"); + +shinyjs.autoFocusModal = function(id) { + document.getElementById('shiny-modal').addEventListener( + 'shown.bs.modal', + () => document.getElementById(id).focus(), + { once: true } + ); +} + +shinyjs.enterToSubmit = function(id, submit_id) { + document.getElementById('shiny-modal').addEventListener( + 'shown.bs.modal', + () => document.getElementById(id).addEventListener('keyup', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); // prevent form submission + document.getElementById(submit_id).click(); + } + }) + ); +} From a47a24aade9dcb1dd0563b01bcea888736bfef11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 15 May 2025 14:55:32 +0100 Subject: [PATCH 058/108] chore: simplification of code into moduleServers --- R/module_nested_tabs.R | 21 ++------------- R/module_teal.R | 41 +--------------------------- R/reporter_previewer_module.R | 51 +++++++++++++++++++++++++++++++++++ R/teal_reporter.R | 27 +++++++++++++++++++ 4 files changed, 81 insertions(+), 59 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 67e4988ada..06fe5336b0 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -118,7 +118,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { div( id = id, class = "teal_module", - uiOutput(ns("reporter_add_container")), + ui_add_reporter(ns("add_reporter_wrapper")), # uiOutput(ns("show_rcode_container")), # todo: same mechanism as for the reporter uiOutput(ns("data_reactive"), inline = TRUE), tagList( @@ -406,24 +406,7 @@ srv_teal_module.teal_module <- function(id, ) }) - if (!is.null(reporter)) { - reporter_card_out <- reactive({ - if (is.list(module_out())) { - module_out()$report_card() - } - }) - - output$reporter_add_container <- renderUI({ - req(reporter_card_out()) - tags$div( - class = "teal add-reporter-container", - teal.reporter::add_card_button_ui(session$ns("reporter_add")) - ) - }) - - add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) - } - + srv_add_reporter("add_reporter_wrapper", module_out, reporter) module_out }) } diff --git a/R/module_teal.R b/R/module_teal.R index a6dea26676..5d3c216fcb 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -223,46 +223,7 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. reporter = reporter ) - if (!is.null(reporter)) { - reporter$set_id(attr(filter, "app_id")) - reporter_module <- extract_module(modules, "teal_module_previewer")[[1]] - modules <- drop_module(modules, "teal_module_previewer") - - previewer_out <- do.call( # out for testing - reporter_module$server, - args = c(list(id = "report_previewer", reporter = reporter), reporter_module$server_args) - ) - previewer_ui <- do.call( - reporter_module$ui, - args = c(list(id = session$ns("report_previewer")), reporter_module$ui_args) - ) - - # Report Previewer tab needs to be shown only if any module has a reporter functionality - any_use_reporter <- reactive({ - f <- function(x) { - if (is.function(x)) { - f(x()) - } else if ("report_card" %in% names(x)) { - TRUE - } else if (is.list(x)) { - sapply(x, f) - } - } - any(unlist(f(modules_output))) || is_arg_used(modules, "reporter") - }) - # always insert reporter - bslib::nav_insert( - id = "teal_modules-active_tab", - nav = bslib::nav_panel(title = reporter_module$label, previewer_ui) - ) - observeEvent(any_use_reporter(), { - if (any_use_reporter()) { - bslib::nav_show(id = "teal_modules-active_tab", target = reporter_module$label) - } else { - bslib::nav_hide(id = "teal_modules-active_tab", target = reporter_module$label) - } - }) - } + srv_reporter_previewer_tab("reporter_manager", modules, modules_output, reporter, session) mapping_table <- srv_filter_manager_panel("filter_manager_panel", slices_global = slices_global) snapshots <- srv_snapshot_manager_panel("snapshot_manager_panel", slices_global = slices_global) srv_bookmark_panel("bookmark_manager", modules) diff --git a/R/reporter_previewer_module.R b/R/reporter_previewer_module.R index 19b707b34f..45e70c2068 100644 --- a/R/reporter_previewer_module.R +++ b/R/reporter_previewer_module.R @@ -46,3 +46,54 @@ reporter_previewer_module <- function(label = "Report previewer", server_args = attr(module, "teal_bookmarkable") <- TRUE module } + +#' Reporter previewer tab +#' +#' Creates navigation for reporter previewer in main tab of the teal UI +#' @noRd +srv_reporter_previewer_tab <- function(id, modules, modules_output, reporter, parent_session) { + if (is.null(reporter)) { + return(FALSE) + } + moduleServer(id, function(input, output, session) { + reporter$set_id(attr(filter, "app_id")) + reporter_module <- extract_module(modules, "teal_module_previewer")[[1]] + modules <- drop_module(modules, "teal_module_previewer") + + previewer_out <- do.call( # out for testing + reporter_module$server, + args = c(list(id = "report_previewer", reporter = reporter), reporter_module$server_args) + ) + previewer_ui <- do.call( + reporter_module$ui, + args = c(list(id = session$ns("report_previewer")), reporter_module$ui_args) + ) + + # Report Previewer tab needs to be shown only if any module has a reporter functionality + any_use_reporter <- reactive({ + f <- function(x) { + if (is.function(x)) { + f(x()) + } else if ("report_card" %in% names(x)) { + TRUE + } else if (is.list(x)) { + sapply(x, f) + } + } + any(unlist(f(modules_output))) || is_arg_used(modules, "reporter") + }) + # always insert reporter + bslib::nav_insert( + id = "teal_modules-active_tab", + nav = bslib::nav_panel(title = reporter_module$label, previewer_ui), + session = parent_session + ) + observeEvent(any_use_reporter(), { + if (any_use_reporter()) { + bslib::nav_show(id = "teal_modules-active_tab", target = reporter_module$label) + } else { + bslib::nav_hide(id = "teal_modules-active_tab", target = reporter_module$label) + } + }) + }) +} diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 8e4c17271a..4a27fbe5bb 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -310,3 +310,30 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { }) }) } + +#' @noRd +ui_add_reporter <- function(id) uiOutput(NS(id, "reporter_add_container")) + +#' @noRd +srv_add_reporter <- function(id, module_out, reporter) { + if (is.null(reporter)) { + return(FALSE) + } # early exit + moduleServer(id, function(input, output, session) { + reporter_card_out <- reactive({ + if (is.list(module_out())) { + module_out()$report_card() + } + }) + + output$reporter_add_container <- renderUI({ + req(reporter_card_out()) + tags$div( + class = "teal add-reporter-container", + teal.reporter::add_card_button_ui(session$ns("reporter_add")) + ) + }) + + add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) + }) +} From 0857eb2d218ec42fc58a9034de3eda6fbbef608e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 15 May 2025 15:03:09 +0100 Subject: [PATCH 059/108] chore: code trimming --- R/teal_reporter.R | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 4a27fbe5bb..d212b53658 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -273,19 +273,12 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { "are ready. Otherwise contact your application developer." ) warning(msg) - shiny::showNotification( - msg, - type = "error" - ) + shiny::showNotification(msg, type = "error") } else { new_card_name <- trimws(input$label) if (nchar(new_card_name) == 0) { - shiny::showNotification( - "Card name cannot be empty.", - type = "error", - duration = 5 - ) + shiny::showNotification("Card name cannot be empty.", type = "error", duration = 5) shinyjs::enable("add_card_ok") return(NULL) } @@ -293,7 +286,7 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { if (new_card_name %in% existing_card_names) { shiny::showNotification( - paste("A card with the name '", new_card_name, "' already exists. Please use a different name."), + sprintf("A card with the name '%s' already exists. Please use a different name.", new_card_name), type = "error", duration = 5 ) From 8330ddbd92553176c682e496e0a83605e5c56fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 15 May 2025 15:05:31 +0100 Subject: [PATCH 060/108] fix: use trimmed named to store card --- R/teal_reporter.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index d212b53658..3620074059 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -294,7 +294,7 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { } else { card <- c(report_document(input$comment), r_card_fun()) # card <- to_markdown(card) - reporter$append_cards(structure(list(card), names = input$label)) + reporter$append_cards(structure(list(card), names = new_card_name)) shiny::showNotification("The card added successfully.", type = "message") shiny::removeModal() From e90a5d5ca15c833f5e326ea05c91692cdea86109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 16 May 2025 08:12:37 +0100 Subject: [PATCH 061/108] revert: back to @ instead of %40 due to pak error --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 77c73b8f7f..cd2e7b6932 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -79,7 +79,7 @@ VignetteBuilder: RdMacros: lifecycle Remotes: - insightsengineering/teal.reporter@redesign%40main, + insightsengineering/teal.reporter@redesign@main, insightsengineering/teal.slice@main, insightsengineering/teal.widgets@main Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, From 835cf0f75b4eacc3ba3752ed9b334914c91e8ca0 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 20 May 2025 15:52:00 +0200 Subject: [PATCH 062/108] collapse subsequent chunks --- R/module_init_data.R | 2 +- R/teal_data_utils.R | 29 +++++++++++++++++++++++++++++ R/teal_reporter.R | 14 +++++++++----- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index 8ad8800ef9..c5a04fdb3b 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -103,7 +103,7 @@ srv_init_data <- function(id, data) { ) ) ) - + tdata@report <- data@report tdata@verified <- data@verified tdata } diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index 2b5b51c8b6..ace12554bb 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -20,6 +20,11 @@ NULL .append_evaluated_code <- function(data, code) { checkmate::assert_class(data, "teal_data") data@code <- c(data@code, code2list(code)) + teal.data::report(data) <- c( + teal.data::report(data), + "# Data filtering", + teal.reporter::code_chunk(code) + ) methods::validObject(data) data } @@ -33,3 +38,27 @@ NULL data@.xData <- new_env data } + +#' @rdname teal_data_utilities +.collapse_subsequent_chunks <- function(report) { + Reduce( + function(x, this) { + l <- length(x) + if (l && inherits(x[[l]], "code_chunk") && inherits(this, "code_chunk") && + identical(attr(x[[l]], "params"), attr(this, "params"))) { + x[[length(x)]] <- do.call( + code_chunk, + args = c( + list(code = paste(x[[l]], this, sep = "\n")), + attr(x[[l]], "params") + ) + ) + x + } else { + c(x, this) + } + }, + init = teal.reporter::report_document(), + x = report + ) +} diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 3620074059..81d9e8ee48 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -313,20 +313,24 @@ srv_add_reporter <- function(id, module_out, reporter) { return(FALSE) } # early exit moduleServer(id, function(input, output, session) { - reporter_card_out <- reactive({ - if (is.list(module_out())) { - module_out()$report_card() + report_document_out <- reactive({ + req(module_out()) + if (is.reactive(module_out())) { + req(module_out()()) + if (inherits(module_out()(), "teal_data")) { + .collapse_subsequent_chunks(teal.data::report(module_out()())) + } } }) output$reporter_add_container <- renderUI({ - req(reporter_card_out()) + req(report_document_out()) tags$div( class = "teal add-reporter-container", teal.reporter::add_card_button_ui(session$ns("reporter_add")) ) }) - add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) + add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = report_document_out) }) } From 05c2255a7e8d07e71ba119cebe8bca7611586415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Tue, 27 May 2025 11:30:55 +0200 Subject: [PATCH 063/108] Improves `ReportDocument` editor (#1536) # Pull Request Companion to https://github.com/insightsengineering/teal.reporter/pull/326 --- R/teal_reporter.R | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 3620074059..893237f241 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -209,7 +209,6 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { )) ns <- session$ns - add_modal <- function() { div( class = "teal-widgets reporter-modal", @@ -260,6 +259,7 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { } shiny::observeEvent(input$add_report_card_button, { + shiny::removeModal() shiny::showModal(add_modal()) }) @@ -276,29 +276,12 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { shiny::showNotification(msg, type = "error") } else { new_card_name <- trimws(input$label) + card <- c(report_document(input$comment), r_card_fun()) + metadata(card, "title") <- new_card_name - if (nchar(new_card_name) == 0) { - shiny::showNotification("Card name cannot be empty.", type = "error", duration = 5) - shinyjs::enable("add_card_ok") - return(NULL) - } - existing_card_names <- names(reporter$get_cards()) - - if (new_card_name %in% existing_card_names) { - shiny::showNotification( - sprintf("A card with the name '%s' already exists. Please use a different name.", new_card_name), - type = "error", - duration = 5 - ) - shinyjs::enable("add_card_ok") - } else { - card <- c(report_document(input$comment), r_card_fun()) - # card <- to_markdown(card) - reporter$append_cards(structure(list(card), names = new_card_name)) - - shiny::showNotification("The card added successfully.", type = "message") - shiny::removeModal() - } + reporter$append_cards(card) + shiny::showNotification("The card added successfully.", type = "message") + shiny::removeModal() } }) }) @@ -327,6 +310,10 @@ srv_add_reporter <- function(id, module_out, reporter) { ) }) - add_document_button_srv("reporter_add", reporter = reporter, r_card_fun = reporter_card_out) + add_document_button_srv( + "reporter_add", + reporter = reporter, + r_card_fun = reporter_card_out + ) }) } From a803de7abceaa512d6d47584aa420de462ec1786 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 09:37:58 +0000 Subject: [PATCH 064/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/TealReportCard.Rd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/TealReportCard.Rd b/man/TealReportCard.Rd index 061dbb73ba..85d4d2208b 100644 --- a/man/TealReportCard.Rd +++ b/man/TealReportCard.Rd @@ -57,6 +57,7 @@ card$get_content()[[1]]$get_content()
  • teal.reporter::ReportCard$get_name()
  • teal.reporter::ReportCard$initialize()
  • teal.reporter::ReportCard$reset()
  • +
  • teal.reporter::ReportCard$set_content_names()
  • teal.reporter::ReportCard$set_name()
  • teal.reporter::ReportCard$to_list()
  • From 21e400a57901eaab5b515e18e9129e0670316306 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 12:14:32 +0200 Subject: [PATCH 065/108] typo --- R/teal_reporter.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 326c5a2635..f4f2401cb0 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -317,7 +317,7 @@ srv_add_reporter <- function(id, module_out, reporter) { add_document_button_srv( "reporter_add", reporter = reporter, - r_card_fun = reporter_card_out + r_card_fun = report_document_out ) }) } From 6c6262690811257d924d27c3d73427b69cdb5f2e Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 14:26:01 +0200 Subject: [PATCH 066/108] convert teal_data to reportable --- R/module_init_data.R | 11 +++++++---- R/teal_data_utils.R | 4 ++-- R/teal_reporter.R | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index c5a04fdb3b..9b07b9d853 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -90,11 +90,15 @@ srv_init_data <- function(id, data) { #' @keywords internal .add_signature_to_data <- function(data) { hashes <- .get_hashes_code(data) + data_reportable <- as.reportable(data) tdata <- do.call( - teal.data::teal_data, + teal.reporter::teal_reportable, c( - list(code = trimws(c(teal.code::get_code(data), hashes), which = "right")), - list(join_keys = teal.data::join_keys(data)), + list( + code = trimws(c(teal.code::get_code(data), hashes), which = "right"), + join_keys = teal.data::join_keys(data), + report = teal.reporter::report(data) + ), sapply( names(data), teal.code::get_var, @@ -103,7 +107,6 @@ srv_init_data <- function(id, data) { ) ) ) - tdata@report <- data@report tdata@verified <- data@verified tdata } diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index ace12554bb..647ee93db0 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -20,8 +20,8 @@ NULL .append_evaluated_code <- function(data, code) { checkmate::assert_class(data, "teal_data") data@code <- c(data@code, code2list(code)) - teal.data::report(data) <- c( - teal.data::report(data), + teal.reporter::report(data) <- c( + teal.reporter::report(data), "# Data filtering", teal.reporter::code_chunk(code) ) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index f4f2401cb0..e738a7b737 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -300,8 +300,8 @@ srv_add_reporter <- function(id, module_out, reporter) { req(module_out()) if (is.reactive(module_out())) { req(module_out()()) - if (inherits(module_out()(), "teal_data")) { - .collapse_subsequent_chunks(teal.data::report(module_out()())) + if (inherits(module_out()(), "teal_reportable")) { + .collapse_subsequent_chunks(teal.reporter::report(module_out()())) } } }) From 40d514746fd27b2cb8b3062638ae13030b270c7b Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 14:59:34 +0200 Subject: [PATCH 067/108] fix modify_reactive_output --- R/modify_reactive_output.R | 103 +++---------------------------------- 1 file changed, 8 insertions(+), 95 deletions(-) diff --git a/R/modify_reactive_output.R b/R/modify_reactive_output.R index 3ee6edbf1c..ba487e2a60 100644 --- a/R/modify_reactive_output.R +++ b/R/modify_reactive_output.R @@ -25,21 +25,9 @@ #' original `teal_module`. #' #' @export -modify_reactive_output <- function(teal_module, ...) { +modify_reactive_output <- function(teal_module, fun = function(data) data) { checkmate::assert_class(teal_module, "teal_module") - output_funs <- list(...) - - if (length(output_funs) == 0) { - warning("No output transformations specified for modify_reactive_output. Returning original module.") - return(teal_module) - } - if (is.null(names(output_funs)) || any(names(output_funs) == "" | duplicated(names(output_funs)))) { - stop("All transformations supplied to modify_reactive_output must be uniquely named.") - } - are_functions <- vapply(output_funs, is.function, logical(1)) - if (!all(are_functions)) { - stop("All transformations supplied to modify_reactive_output must be functions.") - } + checkmate::assert_function(fun) original_server <- teal_module$server original_ui <- teal_module$ui @@ -65,30 +53,8 @@ modify_reactive_output <- function(teal_module, ...) { moduleServer(id, function(input, output, session) { eval_args$id <- "modified" - original_outputs <- do.call(original_server, eval_args) - - missing_output_names <- setdiff(names(output_funs), names(original_outputs)) - if (length(missing_output_names)) { - stop("The provided teal_module does not return: ", toString(missing_output_names)) - } - - modified_output <- sapply( - names(output_funs), - function(output_name) { - if (is.reactive(original_outputs[[output_name]])) { - reactive({ - res <- original_outputs[[output_name]]() - output_funs[[output_name]](res) - }) - } else { - res <- original_outputs[[output_name]] - output_funs[[output_name]](res) - } - }, - USE.NAMES = TRUE - ) - - return(modifyList(original_outputs, modified_output)) + out <- do.call(original_server, eval_args) + reactive(fun(out())) }) }) @@ -103,62 +69,9 @@ modify_reactive_output <- function(teal_module, ...) { ) } -modify_report_card <- function(teal_module, modify_fun) { - modify_reactive_output(teal_module, report_card = modify_fun) -} - disable_report <- function(teal_module) { - modify_reactive_output(teal_module, report_card = function(report_card) NULL) + modify_reactive_output(teal_module, fun = function(data) { + report(data) <- report_document() + data + }) } - - -############################################################## EXAMPLE -# -# take_3_elements <- function(x){ -# teal.reporter::edit_report_document(x, modify = 1:3) -# } -# -# devtools::load_all('../teal.reporter') -# devtools::load_all('../teal.widgets') -# devtools::load_all('../teal.code') -# devtools::load_all('.') -# devtools::load_all('../teal.modules.general') -# -# data <- teal_data() -# data <- within(data, { -# require(nestcolor) -# CO2 <- CO2 -# }) -# -# app <- init( -# data = data, -# modules = modules( -# tm_a_regression( -# label = "Regression", -# response = data_extract_spec( -# dataname = "CO2", -# select = select_spec( -# label = "Select variable:", -# choices = "uptake", -# selected = "uptake", -# multiple = FALSE, -# fixed = TRUE -# ) -# ), -# regressor = data_extract_spec( -# dataname = "CO2", -# select = select_spec( -# label = "Select variables:", -# choices = variable_choices(data[["CO2"]], c("conc", "Treatment")), -# selected = "conc", -# multiple = TRUE, -# fixed = FALSE -# ) -# ) -# ) |> modify_teal_module_report_card(take_3_elements) -# ) -# ) -# -# if (interactive()) { -# shinyApp(app$ui, app$server) -# } From daa23adb8a196eb14fe64e166cdd5002145b9721 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 15:41:29 +0200 Subject: [PATCH 068/108] rename classes --- R/module_init_data.R | 2 +- R/teal_reporter.R | 2 +- man/add_document_button_srv.Rd | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index 9b07b9d853..d05adaa4d2 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -92,7 +92,7 @@ srv_init_data <- function(id, data) { hashes <- .get_hashes_code(data) data_reportable <- as.reportable(data) tdata <- do.call( - teal.reporter::teal_reportable, + teal.reporter::teal_report, c( list( code = trimws(c(teal.code::get_code(data), hashes), which = "right"), diff --git a/R/teal_reporter.R b/R/teal_reporter.R index e738a7b737..930506a527 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -300,7 +300,7 @@ srv_add_reporter <- function(id, module_out, reporter) { req(module_out()) if (is.reactive(module_out())) { req(module_out()()) - if (inherits(module_out()(), "teal_reportable")) { + if (inherits(module_out()(), "teal_report")) { .collapse_subsequent_chunks(teal.reporter::report(module_out()())) } } diff --git a/man/add_document_button_srv.Rd b/man/add_document_button_srv.Rd index 6037b18159..ae1b785060 100644 --- a/man/add_document_button_srv.Rd +++ b/man/add_document_button_srv.Rd @@ -2,11 +2,11 @@ % Please edit documentation in R/teal_reporter.R \name{add_document_button_srv} \alias{add_document_button_srv} -\title{Server function for \code{ReportDocument} card class.} +\title{Server function for \code{doc} card class.} \usage{ add_document_button_srv(id, reporter, r_card_fun) } \description{ -Server function for \code{ReportDocument} card class. +Server function for \code{doc} card class. } \keyword{internal} From 693c8e5064e656f143c776c86c10646bb7056e40 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 15:44:59 +0200 Subject: [PATCH 069/108] rename missing --- R/teal_reporter.R | 2 +- man/modify_reactive_output.Rd | 2 +- man/reporter_previewer_module.Rd | 6 +++--- man/teal_data_module.Rd | 4 ---- man/teal_data_utilities.Rd | 3 +++ 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 930506a527..6da2d40786 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -195,7 +195,7 @@ TealSlicesBlock <- R6::R6Class( # nolint: object_name_linter. ) ) -#' Server function for `ReportDocument` card class. +#' Server function for `doc` class. #' @keywords internal add_document_button_srv <- function(id, reporter, r_card_fun) { checkmate::assert_class(r_card_fun, "reactive") diff --git a/man/modify_reactive_output.Rd b/man/modify_reactive_output.Rd index 8b7313934c..5573bc7816 100644 --- a/man/modify_reactive_output.Rd +++ b/man/modify_reactive_output.Rd @@ -4,7 +4,7 @@ \alias{modify_reactive_output} \title{Modify Reactive Outputs of a \code{teal} Module} \usage{ -modify_reactive_output(teal_module, ...) +modify_reactive_output(teal_module, fun = function(data) data) } \arguments{ \item{teal_module}{(\code{teal_module})} diff --git a/man/reporter_previewer_module.Rd b/man/reporter_previewer_module.Rd index e54418141d..1aa7bb0784 100644 --- a/man/reporter_previewer_module.Rd +++ b/man/reporter_previewer_module.Rd @@ -11,7 +11,7 @@ reporter_previewer_module(label = "Report previewer", server_args = list()) For \code{modules()} defaults to \code{"root"}. See \code{Details}.} \item{server_args}{(named \code{list}) -Arguments passed to \code{\link[teal.reporter:reporter_previewer]{teal.reporter::reporter_previewer_srv()}}.} +Arguments passed to \code{\link[teal.reporter:reporter_previewer_srv]{teal.reporter::reporter_previewer_srv()}}.} } \value{ \code{teal_module} (extended with \code{teal_module_previewer} class) containing the \code{teal.reporter} previewer functionality. @@ -19,8 +19,8 @@ Arguments passed to \code{\link[teal.reporter:reporter_previewer]{teal.reporter: \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} -This function wraps \code{\link[teal.reporter:reporter_previewer]{teal.reporter::reporter_previewer_ui()}} and -\code{\link[teal.reporter:reporter_previewer]{teal.reporter::reporter_previewer_srv()}} into a \code{teal_module} to be +This function wraps \code{\link[teal.reporter:reporter_previewer_ui]{teal.reporter::reporter_previewer_ui()}} and +\code{\link[teal.reporter:reporter_previewer_srv]{teal.reporter::reporter_previewer_srv()}} into a \code{teal_module} to be used in \code{teal} applications. If you are creating a \code{teal} application using \code{\link[=init]{init()}} then this diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 683c6d9ef9..fa1501c49d 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,10 +35,6 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} -\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. -It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an -\code{expression} being a result of \code{parse(keep.source = TRUE)}.} - \item{data}{(\code{teal_data_module}) object} \item{expr}{(\code{expression}) to evaluate. Must be inline code. See \code{\link[=within]{within()}}} diff --git a/man/teal_data_utilities.Rd b/man/teal_data_utilities.Rd index 0fdcaa6653..72bec71171 100644 --- a/man/teal_data_utilities.Rd +++ b/man/teal_data_utilities.Rd @@ -4,11 +4,14 @@ \alias{teal_data_utilities} \alias{.append_evaluated_code} \alias{.append_modified_data} +\alias{.collapse_subsequent_chunks} \title{\code{teal_data} utils} \usage{ .append_evaluated_code(data, code) .append_modified_data(data, objects) + +.collapse_subsequent_chunks(report) } \arguments{ \item{data}{(\code{teal_data})} From 9f6965a0869d862e1443cff21914ab8dc6443b14 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Wed, 28 May 2025 07:57:37 +0200 Subject: [PATCH 070/108] fix srv_reporter_previewer_tab to work with reactive output --- R/module_teal.R | 9 +++- R/reporter_previewer_module.R | 80 ++++++++++++++++++----------------- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index 5d3c216fcb..c96040bc44 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -217,13 +217,18 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. id = "teal_modules", data = data_signatured, datasets = datasets_rv, - modules = modules, + modules = drop_module(modules, "teal_previewer_module"), slices_global = slices_global, data_load_status = data_load_status, reporter = reporter ) - srv_reporter_previewer_tab("reporter_manager", modules, modules_output, reporter, session) + insert_reporter_previewer_tab( + session = session, + modules = modules, + modules_output = modules_output, + reporter = reporter + ) mapping_table <- srv_filter_manager_panel("filter_manager_panel", slices_global = slices_global) snapshots <- srv_snapshot_manager_panel("snapshot_manager_panel", slices_global = slices_global) srv_bookmark_panel("bookmark_manager", modules) diff --git a/R/reporter_previewer_module.R b/R/reporter_previewer_module.R index 45e70c2068..6d1ede9676 100644 --- a/R/reporter_previewer_module.R +++ b/R/reporter_previewer_module.R @@ -51,49 +51,51 @@ reporter_previewer_module <- function(label = "Report previewer", server_args = #' #' Creates navigation for reporter previewer in main tab of the teal UI #' @noRd -srv_reporter_previewer_tab <- function(id, modules, modules_output, reporter, parent_session) { +insert_reporter_previewer_tab <- function(session, modules, modules_output, reporter) { if (is.null(reporter)) { return(FALSE) } - moduleServer(id, function(input, output, session) { - reporter$set_id(attr(filter, "app_id")) - reporter_module <- extract_module(modules, "teal_module_previewer")[[1]] - modules <- drop_module(modules, "teal_module_previewer") + reporter$set_id(attr(filter, "app_id")) + reporter_module <- extract_module(modules, "teal_module_previewer")[[1]] + modules <- drop_module(modules, "teal_module_previewer") - previewer_out <- do.call( # out for testing - reporter_module$server, - args = c(list(id = "report_previewer", reporter = reporter), reporter_module$server_args) - ) - previewer_ui <- do.call( - reporter_module$ui, - args = c(list(id = session$ns("report_previewer")), reporter_module$ui_args) - ) + previewer_out <- do.call( + reporter_module$server, + args = c(list(id = "report_previewer", reporter = reporter), reporter_module$server_args) + ) + previewer_ui <- do.call( + reporter_module$ui, + args = c(list(id = session$ns("report_previewer")), reporter_module$ui_args) + ) + + # Report Previewer tab needs to be shown only if any module has a reporter functionality + returns_teal_report <- function(x) { + if (is.reactive(x)) { + returns_teal_report(tryCatch(x(), error = function(e) e)) + } else if (inherits(x, "teal_report")) { + TRUE + } else if (is.list(x)) { + any(unlist(sapply(x, returns_teal_report))) + } else { + FALSE + } + } + any_use_reporter <- reactiveVal(FALSE) + observeEvent(returns_teal_report(modules_output), { + if ((is_arg_used(modules, "reporter") || returns_teal_report(modules_output)) && !isTRUE(any_use_reporter())) { + any_use_reporter(TRUE) + } + }) - # Report Previewer tab needs to be shown only if any module has a reporter functionality - any_use_reporter <- reactive({ - f <- function(x) { - if (is.function(x)) { - f(x()) - } else if ("report_card" %in% names(x)) { - TRUE - } else if (is.list(x)) { - sapply(x, f) - } - } - any(unlist(f(modules_output))) || is_arg_used(modules, "reporter") - }) - # always insert reporter - bslib::nav_insert( - id = "teal_modules-active_tab", - nav = bslib::nav_panel(title = reporter_module$label, previewer_ui), - session = parent_session - ) - observeEvent(any_use_reporter(), { - if (any_use_reporter()) { - bslib::nav_show(id = "teal_modules-active_tab", target = reporter_module$label) - } else { - bslib::nav_hide(id = "teal_modules-active_tab", target = reporter_module$label) - } - }) + observeEvent(any_use_reporter(), { + if (any_use_reporter()) { + bslib::nav_insert( + id = "teal_modules-active_tab", + nav = bslib::nav_panel(title = reporter_module$label, previewer_ui), + session = session + ) + } }) + + previewer_out # returned for testing } From 3ca11ab59cf9c8eda8dc858293a0e86f94546475 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Wed, 28 May 2025 08:16:22 +0200 Subject: [PATCH 071/108] reload docs --- R/module_teal.R | 2 -- man/TealReportCard.Rd | 1 - man/add_document_button_srv.Rd | 4 ++-- man/module_teal.Rd | 2 -- man/reporter_previewer_module.Rd | 6 +++--- man/teal_data_module.Rd | 4 ++++ 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index c96040bc44..cb18e14caa 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -40,8 +40,6 @@ #' `teal_modules` object. These are the specific output modules which #' will be displayed in the `teal` application. See [modules()] and [module()] for #' more details. -#' @param reporter (`Reporter`) object used to store report contents. Set to `NULL` to globally disable reporting. -#' @param disable Whether to disable `reporter` in `ui_teal`. Useful for using `ui_teal` and `srv_teal` as shiny module. #' #' @return `NULL` invisibly NULL diff --git a/man/TealReportCard.Rd b/man/TealReportCard.Rd index 85d4d2208b..061dbb73ba 100644 --- a/man/TealReportCard.Rd +++ b/man/TealReportCard.Rd @@ -57,7 +57,6 @@ card$get_content()[[1]]$get_content()
  • teal.reporter::ReportCard$get_name()
  • teal.reporter::ReportCard$initialize()
  • teal.reporter::ReportCard$reset()
  • -
  • teal.reporter::ReportCard$set_content_names()
  • teal.reporter::ReportCard$set_name()
  • teal.reporter::ReportCard$to_list()
  • diff --git a/man/add_document_button_srv.Rd b/man/add_document_button_srv.Rd index ae1b785060..e14f88e37f 100644 --- a/man/add_document_button_srv.Rd +++ b/man/add_document_button_srv.Rd @@ -2,11 +2,11 @@ % Please edit documentation in R/teal_reporter.R \name{add_document_button_srv} \alias{add_document_button_srv} -\title{Server function for \code{doc} card class.} +\title{Server function for \code{doc} class.} \usage{ add_document_button_srv(id, reporter, r_card_fun) } \description{ -Server function for \code{doc} card class. +Server function for \code{doc} class. } \keyword{internal} diff --git a/man/module_teal.Rd b/man/module_teal.Rd index 4ca48331f5..ff086329cb 100644 --- a/man/module_teal.Rd +++ b/man/module_teal.Rd @@ -31,8 +31,6 @@ The data which application will depend on.} specifies the initial filter using \code{\link[=teal_slices]{teal_slices()}}.} \item{reporter}{(\code{Reporter}) object used to store report contents. Set to \code{NULL} to globally disable reporting.} - -\item{disable}{Whether to disable \code{reporter} in \code{ui_teal}. Useful for using \code{ui_teal} and \code{srv_teal} as shiny module.} } \value{ \code{NULL} invisibly diff --git a/man/reporter_previewer_module.Rd b/man/reporter_previewer_module.Rd index 1aa7bb0784..e54418141d 100644 --- a/man/reporter_previewer_module.Rd +++ b/man/reporter_previewer_module.Rd @@ -11,7 +11,7 @@ reporter_previewer_module(label = "Report previewer", server_args = list()) For \code{modules()} defaults to \code{"root"}. See \code{Details}.} \item{server_args}{(named \code{list}) -Arguments passed to \code{\link[teal.reporter:reporter_previewer_srv]{teal.reporter::reporter_previewer_srv()}}.} +Arguments passed to \code{\link[teal.reporter:reporter_previewer]{teal.reporter::reporter_previewer_srv()}}.} } \value{ \code{teal_module} (extended with \code{teal_module_previewer} class) containing the \code{teal.reporter} previewer functionality. @@ -19,8 +19,8 @@ Arguments passed to \code{\link[teal.reporter:reporter_previewer_srv]{teal.repor \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} -This function wraps \code{\link[teal.reporter:reporter_previewer_ui]{teal.reporter::reporter_previewer_ui()}} and -\code{\link[teal.reporter:reporter_previewer_srv]{teal.reporter::reporter_previewer_srv()}} into a \code{teal_module} to be +This function wraps \code{\link[teal.reporter:reporter_previewer]{teal.reporter::reporter_previewer_ui()}} and +\code{\link[teal.reporter:reporter_previewer]{teal.reporter::reporter_previewer_srv()}} into a \code{teal_module} to be used in \code{teal} applications. If you are creating a \code{teal} application using \code{\link[=init]{init()}} then this diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index fa1501c49d..683c6d9ef9 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -35,6 +35,10 @@ App user will be able to interact and change the data output from the module mul \item{object}{(\code{teal_data_module})} +\item{code}{(\code{character}, \code{language} or \code{expression}) code to evaluate. +It is possible to preserve original formatting of the \code{code} by providing a \code{character} or an +\code{expression} being a result of \code{parse(keep.source = TRUE)}.} + \item{data}{(\code{teal_data_module}) object} \item{expr}{(\code{expression}) to evaluate. Must be inline code. See \code{\link[=within]{within()}}} From 45da1b44ff51074ebe5d57d3b8d9d9af6967279a Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Wed, 28 May 2025 14:19:33 +0200 Subject: [PATCH 072/108] after function --- DESCRIPTION | 2 +- NAMESPACE | 3 +- R/after.R | 87 ++++++++++++++++++++++++++++++++++++++ R/dummy_functions.R | 14 +----- R/modify_reactive_output.R | 77 --------------------------------- R/teal_reporter.R | 11 ++++- 6 files changed, 101 insertions(+), 93 deletions(-) create mode 100644 R/after.R delete mode 100644 R/modify_reactive_output.R diff --git a/DESCRIPTION b/DESCRIPTION index cd2e7b6932..f09ca2c662 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -101,13 +101,13 @@ Roxygen: list(markdown = TRUE, packages = c("roxy.shinylive")) RoxygenNote: 7.3.2 Collate: 'TealAppDriver.R' + 'after.R' 'checkmate.R' 'dummy_functions.R' 'include_css_js.R' 'modules.R' 'init.R' 'landing_popup_module.R' - 'modify_reactive_output.R' 'module_bookmark_manager.R' 'module_data_summary.R' 'module_filter_data.R' diff --git a/NAMESPACE b/NAMESPACE index c74c8c165b..6a7d513ad6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,9 +15,11 @@ S3method(ui_teal_module,teal_modules) S3method(within,teal_data_module) export(TealReportCard) export(add_landing_modal) +export(after) export(as.teal_slices) export(as_tdata) export(build_app_title) +export(disable_report) export(example_module) export(get_code_tdata) export(get_metadata) @@ -26,7 +28,6 @@ export(landing_popup_module) export(make_teal_transform_server) export(modify_footer) export(modify_header) -export(modify_reactive_output) export(modify_title) export(module) export(modules) diff --git a/R/after.R b/R/after.R new file mode 100644 index 0000000000..fd17a3fd86 --- /dev/null +++ b/R/after.R @@ -0,0 +1,87 @@ +#' @param x (`teal_data`) +#' @param ui (`function(id, elem, ...)`) function to receive output (`shiny.tag`) from `x$ui` +#' @param server (`function(input, output, session, data, ...)`) function to receive output data from `x$server` +#' @param ... additional argument passed to `ui` and `server` by matching their formals names. +#' @export +after <- function(x, + ui = function(id, elem) elem, + server = function(input, output, session, data) data, + ...) { + # todo: make a method for teal_app and remove teal_extend_server? + checkmate::assert_multi_class(x, "teal_module") + if (!is.function(ui) || !all(names(formals(ui)) %in% c("id", "elem"))) { + stop("ui should be a function of id and elem") + } + if (!is.function(server) || !all(names(formals(server)) %in% c("input", "output", "session", "data"))) { + stop("server should be a function of `input` and `output`, `session`, `data`") + } + + additional_args <- list(...) + new_x <- x # because overwriting x$ui/server will cause infinite recursion + new_x$ui <- .after_ui(x$ui, ui, additional_args) + new_x$server <- .after_server(x$server, server, additional_args) + new_x +} + + +.after_ui <- function(x, y, additional_args) { + # add `_`-prefix to make sure objects are not masked in the wrapper functions + `_x` <- x + `_y` <- y + new_x <- function() { + original_args <- as.list(environment()) + if ("..." %in% names(formals(`_x`))) { + original_args <- c(original_args, list(...)) + } + ns <- NS(id) + original_args$id <- ns("wrapped") + original_out <- do.call(`_x`, original_args, quote = TRUE) + + wrapper_args <- c( + additional_args, + list(id = ns("wrapper"), elem = original_out) + ) + do.call(`_y`, args = wrapper_args[names(formals(`_y`))]) + } + formals(new_x) <- formals(x) + new_x +} + +.after_server <- function(x, y, additional_args) { + # add `_`-prefix to make sure objects are not masked in the wrapper functions + `_x` <- x + `_y` <- y + new_x <- function() { + original_args <- as.list(environment()) + original_args$id <- "wrapped" + if ("..." %in% names(formals(`_x`))) { + original_args <- c(original_args, list(...)) + } + moduleServer(id, function(input, output, session) { + original_out <- if (all(c("input", "output", "session") %in% names(formals(`_x`)))) { + original_args$module <- `_x` + do.call(call_module, args = original_args) + } else { + do.call(`_x`, original_args) + } + original_out_r <- reactive( + if (is.reactive(original_out)) { + original_out() + } else { + original_out + } + ) + wrapper_args <- modifyList( + additional_args, + list(id = "wrapper", input = input, output = output, session = session) + ) + reactive({ + req(original_out_r()) + wrapper_args$data <- original_out() + do.call(`_y`, wrapper_args[names(formals(`_y`))], quote = TRUE) + }) + }) + } + formals(new_x) <- formals(x) + new_x +} diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 656df01b55..becb2706de 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -83,19 +83,7 @@ example_module <- function(label = "example teal module", title = "Example Code" ) - card_fun <- reactive({ - req(table_data_decorated()) - teal.reporter::report_document( - "## Table Data", - teal.reporter::code_chunk( - teal.code::get_code(table_data_decorated()) - ), - table_data_decorated()[["object"]] - ) - }) - list( - report_card = card_fun - ) + table_data_decorated }) }, ui = function(id, decorators) { diff --git a/R/modify_reactive_output.R b/R/modify_reactive_output.R deleted file mode 100644 index ba487e2a60..0000000000 --- a/R/modify_reactive_output.R +++ /dev/null @@ -1,77 +0,0 @@ -#' Modify Reactive Outputs of a `teal` Module -#' -#' @description -#' This function takes a `teal_module` object and allows modification of its -#' server's reactive outputs. It wraps the original module's server, -#' applying specified functions to one or more of the outputs returned by the -#' original server. -#' -#' The original server logic is called within a new server function. -#' The transformations are applied to the outputs of the original server -#' before they are returned by the new server. -#' -#' @param teal_module (`teal_module`) -#' @param ... Named arguments where each name corresponds to an output element -#' (e.g., a reactive expression or a static value) returned by the -#' `teal_module`'s server function. The value for each named argument must be a -#' function that takes the original output's value as its single argument and -#' returns the modified value. If the original output is a reactive, its -#' resolved value is passed to the modifying function, and the modifying -#' function's result is wrapped in a new reactive. -#' -#' @return A new `teal_module` object with the modified server logic and a UI -#' that internally namespaces the original UI. The new module will have the -#' same label, datanames, server_args, ui_args, and transformators as the -#' original `teal_module`. -#' -#' @export -modify_reactive_output <- function(teal_module, fun = function(data) data) { - checkmate::assert_class(teal_module, "teal_module") - checkmate::assert_function(fun) - - original_server <- teal_module$server - original_ui <- teal_module$ui - new_ui <- function(id, ...) { - ns <- NS(id) - original_ui(ns("modified"), ...) - } - - original_server <- teal_module$server - new_server <- function(...) { } - formals(new_server) <- formals(original_server) - - body(new_server) <- quote({ - arg_names <- names(formals(original_server)) - - # Capture current call and evaluate each argument - call_expr <- match.call() - call_args <- as.list(call_expr)[-1] # remove function name - call_args <- call_args[names(call_args) %in% arg_names] - - # Evaluate all arguments in the current environment - eval_args <- lapply(call_args, eval, envir = parent.frame()) - - moduleServer(id, function(input, output, session) { - eval_args$id <- "modified" - out <- do.call(original_server, eval_args) - reactive(fun(out())) - }) - }) - - module( - label = teal_module$label, - server = new_server, - ui = new_ui, - datanames = teal_module$datanames, - server_args = teal_module$server_args, - ui_args = teal_module$ui_args, - transformators = teal_module$transformators - ) -} - -disable_report <- function(teal_module) { - modify_reactive_output(teal_module, fun = function(data) { - report(data) <- report_document() - data - }) -} diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 6da2d40786..2510013fbf 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -300,7 +300,7 @@ srv_add_reporter <- function(id, module_out, reporter) { req(module_out()) if (is.reactive(module_out())) { req(module_out()()) - if (inherits(module_out()(), "teal_report")) { + if (inherits(module_out()(), "teal_report") || length(teal.reporter::report(module_out()()))) { .collapse_subsequent_chunks(teal.reporter::report(module_out()())) } } @@ -321,3 +321,12 @@ srv_add_reporter <- function(id, module_out, reporter) { ) }) } + +#' @export +disable_report <- function(x) { + checkmate::assert_class(x, "teal_module") + after(x, server = function(data) { + teal.reporter::report(data) <- teal.reporter::report_document() + NULL + }) +} From bcab748de4f809b2b20409371d8acec0fd450834 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Wed, 28 May 2025 14:21:06 +0200 Subject: [PATCH 073/108] remove docs of modify_reactive_output --- man/modify_reactive_output.Rd | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 man/modify_reactive_output.Rd diff --git a/man/modify_reactive_output.Rd b/man/modify_reactive_output.Rd deleted file mode 100644 index 5573bc7816..0000000000 --- a/man/modify_reactive_output.Rd +++ /dev/null @@ -1,35 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/modify_reactive_output.R -\name{modify_reactive_output} -\alias{modify_reactive_output} -\title{Modify Reactive Outputs of a \code{teal} Module} -\usage{ -modify_reactive_output(teal_module, fun = function(data) data) -} -\arguments{ -\item{teal_module}{(\code{teal_module})} - -\item{...}{Named arguments where each name corresponds to an output element -(e.g., a reactive expression or a static value) returned by the -\code{teal_module}'s server function. The value for each named argument must be a -function that takes the original output's value as its single argument and -returns the modified value. If the original output is a reactive, its -resolved value is passed to the modifying function, and the modifying -function's result is wrapped in a new reactive.} -} -\value{ -A new \code{teal_module} object with the modified server logic and a UI -that internally namespaces the original UI. The new module will have the -same label, datanames, server_args, ui_args, and transformators as the -original \code{teal_module}. -} -\description{ -This function takes a \code{teal_module} object and allows modification of its -server's reactive outputs. It wraps the original module's server, -applying specified functions to one or more of the outputs returned by the -original server. - -The original server logic is called within a new server function. -The transformations are applied to the outputs of the original server -before they are returned by the new server. -} From 8a1e9d370e71a922c2faa7bfe5a4c0c7f9c15306 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Wed, 28 May 2025 14:49:24 +0200 Subject: [PATCH 074/108] Don't append filter panel code if no filters applied --- R/teal_data_utils.R | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index 647ee93db0..d7bf15cd7b 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -19,13 +19,15 @@ NULL #' @rdname teal_data_utilities .append_evaluated_code <- function(data, code) { checkmate::assert_class(data, "teal_data") - data@code <- c(data@code, code2list(code)) - teal.reporter::report(data) <- c( - teal.reporter::report(data), - "# Data filtering", - teal.reporter::code_chunk(code) - ) - methods::validObject(data) + if (length(code) && !identical(code, "")) { + data@code <- c(data@code, code2list(code)) + teal.reporter::report(data) <- c( + teal.reporter::report(data), + "# Data filtering", + teal.reporter::code_chunk(code) + ) + methods::validObject(data) + } data } From a19a367edfde12903a1daca72507d705cb129750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 28 May 2025 14:15:58 +0100 Subject: [PATCH 075/108] chore: last of consisent naming --- R/module_init_data.R | 4 ++-- R/teal_data_utils.R | 2 +- R/teal_reporter.R | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index d05adaa4d2..e839e5864d 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -96,8 +96,8 @@ srv_init_data <- function(id, data) { c( list( code = trimws(c(teal.code::get_code(data), hashes), which = "right"), - join_keys = teal.data::join_keys(data), - report = teal.reporter::report(data) + join_keys = teal.data::join_keys(data) + # report = teal.reporter::report(data) ), sapply( names(data), diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index d7bf15cd7b..6f0bf7f6ed 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -60,7 +60,7 @@ NULL c(x, this) } }, - init = teal.reporter::report_document(), + init = teal.reporter::doc(), x = report ) } diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 2510013fbf..2d220e4ee4 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -276,7 +276,7 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { shiny::showNotification(msg, type = "error") } else { new_card_name <- trimws(input$label) - card <- c(report_document(input$comment), r_card_fun()) + card <- c(doc(input$comment), r_card_fun()) metadata(card, "title") <- new_card_name reporter$append_cards(card) @@ -296,7 +296,7 @@ srv_add_reporter <- function(id, module_out, reporter) { return(FALSE) } # early exit moduleServer(id, function(input, output, session) { - report_document_out <- reactive({ + doc_out <- reactive({ req(module_out()) if (is.reactive(module_out())) { req(module_out()()) @@ -307,7 +307,7 @@ srv_add_reporter <- function(id, module_out, reporter) { }) output$reporter_add_container <- renderUI({ - req(report_document_out()) + req(doc_out()) tags$div( class = "teal add-reporter-container", teal.reporter::add_card_button_ui(session$ns("reporter_add")) @@ -317,7 +317,7 @@ srv_add_reporter <- function(id, module_out, reporter) { add_document_button_srv( "reporter_add", reporter = reporter, - r_card_fun = report_document_out + r_card_fun = doc_out ) }) } @@ -326,7 +326,7 @@ srv_add_reporter <- function(id, module_out, reporter) { disable_report <- function(x) { checkmate::assert_class(x, "teal_module") after(x, server = function(data) { - teal.reporter::report(data) <- teal.reporter::report_document() + teal.reporter::report(data) <- teal.reporter::doc() NULL }) } From 78ad8b2c97ec5cd29bdaa0b0879b46f3d31619fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 28 May 2025 14:19:51 +0100 Subject: [PATCH 076/108] fix: revert temporary fix that shouldn't have been pushed --- R/module_init_data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index e839e5864d..01556b8851 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -97,7 +97,7 @@ srv_init_data <- function(id, data) { list( code = trimws(c(teal.code::get_code(data), hashes), which = "right"), join_keys = teal.data::join_keys(data) - # report = teal.reporter::report(data) + report = teal.reporter::report(data) ), sapply( names(data), From b7cba1abbca9e9c5b4a6c7c883d7f1d636777549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 28 May 2025 14:20:03 +0100 Subject: [PATCH 077/108] fix: revert temporary fix that shouldn't have been pushed --- R/module_init_data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index 01556b8851..d05adaa4d2 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -96,7 +96,7 @@ srv_init_data <- function(id, data) { c( list( code = trimws(c(teal.code::get_code(data), hashes), which = "right"), - join_keys = teal.data::join_keys(data) + join_keys = teal.data::join_keys(data), report = teal.reporter::report(data) ), sapply( From 329f7288a54e130f777f07394f16a60166f7d8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 28 May 2025 14:46:20 +0100 Subject: [PATCH 078/108] fix: use teal_report object instead of teal_data in signature --- R/module_init_data.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index d05adaa4d2..27db7e8cac 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -90,24 +90,24 @@ srv_init_data <- function(id, data) { #' @keywords internal .add_signature_to_data <- function(data) { hashes <- .get_hashes_code(data) - data_reportable <- as.reportable(data) + data_teal_report <- as.teal_report(data) tdata <- do.call( teal.reporter::teal_report, c( list( - code = trimws(c(teal.code::get_code(data), hashes), which = "right"), - join_keys = teal.data::join_keys(data), - report = teal.reporter::report(data) + code = trimws(c(teal.code::get_code(data_teal_report), hashes), which = "right"), + join_keys = teal.data::join_keys(data_teal_report), + report = teal.reporter::report(data_teal_report) ), sapply( - names(data), + names(data_teal_report), teal.code::get_var, - object = data, + object = data_teal_report, simplify = FALSE ) ) ) - tdata@verified <- data@verified + tdata@verified <- data_teal_report@verified tdata } From a31fdaa063952a55fa08dac58796ef4328811eed Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 3 Jun 2025 05:38:05 +0200 Subject: [PATCH 079/108] Remove unnecessary methods --- R/teal_data_module-eval_code.R | 14 ++------------ man/TealReportCard.Rd | 1 + man/teal_data_module.Rd | 6 ++---- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/R/teal_data_module-eval_code.R b/R/teal_data_module-eval_code.R index fa95ec9f4b..791701c0a1 100644 --- a/R/teal_data_module-eval_code.R +++ b/R/teal_data_module-eval_code.R @@ -18,14 +18,12 @@ setOldClass("teal_data_module") #' @include teal_data_module.R #' @name eval_code #' @rdname teal_data_module -#' @aliases eval_code,teal_data_module,character-method -#' @aliases eval_code,teal_data_module,language-method -#' @aliases eval_code,teal_data_module,expression-method +#' @aliases eval_code,teal_data_module #' #' @importFrom methods setMethod #' @importMethodsFrom teal.code eval_code #' -setMethod("eval_code", signature = c("teal_data_module", "character"), function(object, code) { +setMethod("eval_code", signature = c(object = "teal_data_module"), function(object, code) { teal_data_module( ui = function(id) { ns <- NS(id) @@ -49,11 +47,3 @@ setMethod("eval_code", signature = c("teal_data_module", "character"), function( } ) }) - -setMethod("eval_code", signature = c("teal_data_module", "language"), function(object, code) { - eval_code(object, code = paste(lang2calls(code), collapse = "\n")) -}) - -setMethod("eval_code", signature = c("teal_data_module", "expression"), function(object, code) { - eval_code(object, code = paste(lang2calls(code), collapse = "\n")) -}) diff --git a/man/TealReportCard.Rd b/man/TealReportCard.Rd index 061dbb73ba..85d4d2208b 100644 --- a/man/TealReportCard.Rd +++ b/man/TealReportCard.Rd @@ -57,6 +57,7 @@ card$get_content()[[1]]$get_content()
  • teal.reporter::ReportCard$get_name()
  • teal.reporter::ReportCard$initialize()
  • teal.reporter::ReportCard$reset()
  • +
  • teal.reporter::ReportCard$set_content_names()
  • teal.reporter::ReportCard$set_name()
  • teal.reporter::ReportCard$to_list()
  • diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 683c6d9ef9..d59115b6f3 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -4,16 +4,14 @@ \name{teal_data_module} \alias{teal_data_module} \alias{eval_code} -\alias{eval_code,teal_data_module,character-method} -\alias{eval_code,teal_data_module,language-method} -\alias{eval_code,teal_data_module,expression-method} +\alias{eval_code,teal_data_module} \alias{within} \alias{within.teal_data_module} \title{Data module for \code{teal} applications} \usage{ teal_data_module(ui, server, label = "data module", once = TRUE) -\S4method{eval_code}{teal_data_module,character}(object, code) +\S4method{eval_code}{teal_data_module}(object, code) \method{within}{teal_data_module}(data, expr, ...) } From 860edf153e5ad5f333283b5420ba1dc30ea9c9e8 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 3 Jun 2025 05:38:51 +0200 Subject: [PATCH 080/108] Add "Code preparation" heading --- R/module_init_data.R | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/R/module_init_data.R b/R/module_init_data.R index 27db7e8cac..e57d2e179a 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -91,6 +91,13 @@ srv_init_data <- function(id, data) { .add_signature_to_data <- function(data) { hashes <- .get_hashes_code(data) data_teal_report <- as.teal_report(data) + if (!inherits(data, "teal_report")) { + teal.reporter::report(data_teal_report) <- c( + teal.reporter::doc(), + "# Code preparation", + teal.reporter::report(data_teal_report) + ) + } tdata <- do.call( teal.reporter::teal_report, c( From a7f76e9dd321a071c195c423b7ca96ba6c536565 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 13:20:18 +0000 Subject: [PATCH 081/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_data_module.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index d59115b6f3..0db7baf3da 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -11,7 +11,7 @@ \usage{ teal_data_module(ui, server, label = "data module", once = TRUE) -\S4method{eval_code}{teal_data_module}(object, code) +\S4method{eval_code}{teal_data_module,ANY}(object, code) \method{within}{teal_data_module}(data, expr, ...) } From 33c0ff359bb9d20fdf84b48e8d8ea50779b6db42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:51:06 +0100 Subject: [PATCH 082/108] chore: add remotes --- DESCRIPTION | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 253249318d..64934665fd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -79,7 +79,10 @@ VignetteBuilder: RdMacros: lifecycle Remotes: - insightsengineering/teal.reporter@redesign@main, + insightsengineering/teal.code@teal_reportable, + insightsengineering/teal.data@teal_reportable, + insightsengineering/teal.logger@main, + insightsengineering/teal.reporter@teal_reportable, insightsengineering/teal.slice@main, insightsengineering/teal.widgets@main Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, From f01ad0944982b69a86027a9c332b86b6e690cf37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 08:30:48 +0100 Subject: [PATCH 083/108] chore: bump deps versions --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 64934665fd..b52b9fea8b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -37,7 +37,7 @@ BugReports: https://github.com/insightsengineering/teal/issues Depends: R (>= 4.1), shiny (>= 1.8.1), - teal.data (>= 0.7.0), + teal.data (>= 0.7.0.9002), teal.slice (>= 0.6.0.9001) Imports: bsicons, @@ -52,7 +52,7 @@ Imports: rlang (>= 1.0.0), shinyjs, stats, - teal.code (>= 0.6.1), + teal.code (>= 0.6.1.9002), teal.logger (>= 0.3.2), teal.reporter (>= 0.4.0.9004), teal.widgets (>= 0.4.3.9001), From 8b48ac01cdf1db243b42fe544e43204ee80c94b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:23:08 +0100 Subject: [PATCH 084/108] tests: update test to new method of disabling reporter globaly --- R/module_teal.R | 5 +++-- tests/testthat/test-module_teal.R | 23 +++-------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index cb18e14caa..9c1e327f3d 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -106,7 +106,9 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. checkmate::assert_multi_class(data, c("teal_data", "teal_data_module", "reactive")) checkmate::assert_class(modules, "teal_modules") checkmate::assert_class(filter, "teal_slices") - modules <- append_reporter_module(modules) + if (!is.null(reporter)) { + modules <- append_reporter_module(modules) + } moduleServer(id, function(input, output, session) { logger::log_debug("srv_teal initializing.") @@ -144,7 +146,6 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. srv_check_module_datanames("datanames_warning", data_handled, modules) data_validated <- .trigger_on_success(data_handled) - data_signatured <- reactive({ req(inherits(data_validated(), "teal_data")) is_filter_ok <- check_filter_datanames(filter, names(data_validated())) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index b09b499c19..2d5ab1401d 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1046,7 +1046,7 @@ testthat::describe("srv_teal teal_modules", { ) }) - testthat::it("does not receive report_previewer when none of the modules contain reporter argument", { + testthat::it("does not receive report_previewer when reporter is NULL", { shiny::testServer( app = srv_teal, args = list( @@ -1055,7 +1055,8 @@ testthat::describe("srv_teal teal_modules", { modules = modules( module("module_1", server = function(id) {}), module("module_2", server = function(id) {}) - ) + ), + reporter = NULL ), expr = { session$setInputs(`teal_modules-active_tab` = "report_previewer") @@ -1063,24 +1064,6 @@ testthat::describe("srv_teal teal_modules", { } ) }) - - testthat::it("receives one report_previewer module when any module contains reporter argument", { - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = teal.data::teal_data(iris = iris, mtcars = mtcars), - modules = modules( - module("module_1", server = function(id, reporter) {}), - module("module_2", server = function(id) {}) - ) - ), - expr = { - session$setInputs(`teal_modules-active_tab` = "report_previewer") - testthat::expect_setequal(names(modules_output), c("module_1", "module_2", "report_previewer")) - } - ) - }) }) testthat::describe("srv_teal filters", { From 1516f8f729b511000de4eeb48b1885bf9892665e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:30:10 +0100 Subject: [PATCH 085/108] tests: fix previewer was inserted in teal_slices --- R/module_teal.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/module_teal.R b/R/module_teal.R index 9c1e327f3d..53b3547594 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -210,13 +210,14 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. ) } - module_labels <- unlist(module_labels(modules), use.names = FALSE) + module_labels <- unlist(module_labels(drop_module(modules, "teal_module_previewer")), use.names = FALSE) + slices_global <- methods::new(".slicesGlobal", filter, module_labels) modules_output <- srv_teal_module( id = "teal_modules", data = data_signatured, datasets = datasets_rv, - modules = drop_module(modules, "teal_previewer_module"), + modules = drop_module(modules, "teal_module_previewer"), slices_global = slices_global, data_load_status = data_load_status, reporter = reporter From 8b37b72cc8325846e640ad8b8bc17dcb85e2a606 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:34:09 +0000 Subject: [PATCH 086/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/teal_data_module.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index 0db7baf3da..d59115b6f3 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -11,7 +11,7 @@ \usage{ teal_data_module(ui, server, label = "data module", once = TRUE) -\S4method{eval_code}{teal_data_module,ANY}(object, code) +\S4method{eval_code}{teal_data_module}(object, code) \method{within}{teal_data_module}(data, expr, ...) } From d356591ddef729d2adfd3a803699f1c6d97a0017 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:26:38 +0200 Subject: [PATCH 087/108] Naming (#1542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to https://github.com/insightsengineering/teal.reporter/pull/334 Consequence of changing naming convention for `teal_report` object. --------- Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- R/module_init_data.R | 8 ++++---- R/teal_data_utils.R | 6 +++--- R/teal_reporter.R | 10 +++++----- vignettes/adding-support-for-reporting.Rmd | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index e57d2e179a..6ba83e78a0 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -92,10 +92,10 @@ srv_init_data <- function(id, data) { hashes <- .get_hashes_code(data) data_teal_report <- as.teal_report(data) if (!inherits(data, "teal_report")) { - teal.reporter::report(data_teal_report) <- c( - teal.reporter::doc(), + teal.reporter::teal_card(data_teal_report) <- c( + teal.reporter::teal_card(), "# Code preparation", - teal.reporter::report(data_teal_report) + teal.reporter::teal_card(data_teal_report) ) } tdata <- do.call( @@ -104,7 +104,7 @@ srv_init_data <- function(id, data) { list( code = trimws(c(teal.code::get_code(data_teal_report), hashes), which = "right"), join_keys = teal.data::join_keys(data_teal_report), - report = teal.reporter::report(data_teal_report) + teal_card = teal.reporter::teal_card(data_teal_report) ), sapply( names(data_teal_report), diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index 6f0bf7f6ed..9189ec3ad0 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -21,8 +21,8 @@ NULL checkmate::assert_class(data, "teal_data") if (length(code) && !identical(code, "")) { data@code <- c(data@code, code2list(code)) - teal.reporter::report(data) <- c( - teal.reporter::report(data), + teal.reporter::teal_card(data) <- c( + teal.reporter::teal_card(data), "# Data filtering", teal.reporter::code_chunk(code) ) @@ -60,7 +60,7 @@ NULL c(x, this) } }, - init = teal.reporter::doc(), + init = teal.reporter::teal_card(), x = report ) } diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 2d220e4ee4..8dac8f8915 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -195,7 +195,7 @@ TealSlicesBlock <- R6::R6Class( # nolint: object_name_linter. ) ) -#' Server function for `doc` class. +#' Server function for `teal_card` class. #' @keywords internal add_document_button_srv <- function(id, reporter, r_card_fun) { checkmate::assert_class(r_card_fun, "reactive") @@ -276,7 +276,7 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { shiny::showNotification(msg, type = "error") } else { new_card_name <- trimws(input$label) - card <- c(doc(input$comment), r_card_fun()) + card <- c(teal.reporter::teal_card(input$comment), r_card_fun()) metadata(card, "title") <- new_card_name reporter$append_cards(card) @@ -300,8 +300,8 @@ srv_add_reporter <- function(id, module_out, reporter) { req(module_out()) if (is.reactive(module_out())) { req(module_out()()) - if (inherits(module_out()(), "teal_report") || length(teal.reporter::report(module_out()()))) { - .collapse_subsequent_chunks(teal.reporter::report(module_out()())) + if (inherits(module_out()(), "teal_report") || length(teal.reporter::teal_card(module_out()()))) { + .collapse_subsequent_chunks(teal.reporter::teal_card(module_out()())) } } }) @@ -326,7 +326,7 @@ srv_add_reporter <- function(id, module_out, reporter) { disable_report <- function(x) { checkmate::assert_class(x, "teal_module") after(x, server = function(data) { - teal.reporter::report(data) <- teal.reporter::doc() + teal.reporter::teal_card(data) <- teal.reporter::teal_card() NULL }) } diff --git a/vignettes/adding-support-for-reporting.Rmd b/vignettes/adding-support-for-reporting.Rmd index 3ebb13138a..4e04e3fb8f 100644 --- a/vignettes/adding-support-for-reporting.Rmd +++ b/vignettes/adding-support-for-reporting.Rmd @@ -226,7 +226,7 @@ To explore the content, we can use the `$get_content` method. For further details, refer to the documentation of `TealReportCard` and `teal.reporter::ReportCard`. We will add simple text to the card by modifying the `card_fun` argument passed to `teal.reporter::simple_reporter_srv`. -The function must return the `card` object, otherwise errors may occur in `teal`. +The function must return the `teal_card` object, otherwise errors may occur in `teal`. ```{r module_4} custom_function <- function(card = teal.reporter::ReportCard$new()) { From 473b9876d88bca9a3b40ae67ddbe82e22b68ae71 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:30:39 +0000 Subject: [PATCH 088/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/add_document_button_srv.Rd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/add_document_button_srv.Rd b/man/add_document_button_srv.Rd index e14f88e37f..ccf6015cb8 100644 --- a/man/add_document_button_srv.Rd +++ b/man/add_document_button_srv.Rd @@ -2,11 +2,11 @@ % Please edit documentation in R/teal_reporter.R \name{add_document_button_srv} \alias{add_document_button_srv} -\title{Server function for \code{doc} class.} +\title{Server function for \code{teal_card} class.} \usage{ add_document_button_srv(id, reporter, r_card_fun) } \description{ -Server function for \code{doc} class. +Server function for \code{teal_card} class. } \keyword{internal} From 8d2477ed1f4bd482d1a5fa89b35398d87f299e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:33:51 +0100 Subject: [PATCH 089/108] chore: linter errors --- R/after.R | 8 ++++---- R/teal_data_utils.R | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/R/after.R b/R/after.R index fd17a3fd86..865df5c526 100644 --- a/R/after.R +++ b/R/after.R @@ -26,8 +26,8 @@ after <- function(x, .after_ui <- function(x, y, additional_args) { # add `_`-prefix to make sure objects are not masked in the wrapper functions - `_x` <- x - `_y` <- y + `_x` <- x # nolint: object_name. + `_y` <- y # nolint: object_name. new_x <- function() { original_args <- as.list(environment()) if ("..." %in% names(formals(`_x`))) { @@ -49,8 +49,8 @@ after <- function(x, .after_server <- function(x, y, additional_args) { # add `_`-prefix to make sure objects are not masked in the wrapper functions - `_x` <- x - `_y` <- y + `_x` <- x # nolint: object_name. + `_y` <- y # nolint: object_name. new_x <- function() { original_args <- as.list(environment()) original_args$id <- "wrapped" diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index 9189ec3ad0..24cbe19803 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -46,8 +46,12 @@ NULL Reduce( function(x, this) { l <- length(x) - if (l && inherits(x[[l]], "code_chunk") && inherits(this, "code_chunk") && - identical(attr(x[[l]], "params"), attr(this, "params"))) { + if ( + l && + inherits(x[[l]], "code_chunk") && + inherits(this, "code_chunk") && + identical(attr(x[[l]], "params"), attr(this, "params")) + ) { x[[length(x)]] <- do.call( code_chunk, args = c( From 60d57072278a0849d6d351ba875be763c418d596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:37:11 +0100 Subject: [PATCH 090/108] docs: fix spelling --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 17995302a6..1da0cf8212 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,7 @@ ### New features -* `init` and `srv_teal` have new `reporter` parameter, that allows to predefine `teal.reporter::Reporter` object to be +* `init` and `srv_teal` have new `reporter` parameter, that allows to pre-define `teal.reporter::Reporter` object to be used for storing the content of the report. You can also globally disable reporting by setting `reporter = NULL` (and `disable = TRUE` in `ui_teal` for cases when `ui_teal` is used as shiny module). * TODO: verify if we need to clone/deep_clone reporter in `srv_teal/init`. From 82fd60501a5d23cc12304fe55778e304a46cc03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:58:42 +0100 Subject: [PATCH 091/108] chore: cleanup for CI --- DESCRIPTION | 2 -- R/after.R | 12 ++++++++---- R/module_init_data.R | 2 +- R/module_teal.R | 3 ++- R/reporter_previewer_module.R | 4 ++-- R/teal_data_utils.R | 2 +- R/teal_reporter.R | 18 +++++++++++++++++- 7 files changed, 31 insertions(+), 12 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b52b9fea8b..8971260412 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -76,8 +76,6 @@ Suggests: VignetteBuilder: knitr, rmarkdown -RdMacros: - lifecycle Remotes: insightsengineering/teal.code@teal_reportable, insightsengineering/teal.data@teal_reportable, diff --git a/R/after.R b/R/after.R index 865df5c526..42536653d5 100644 --- a/R/after.R +++ b/R/after.R @@ -1,7 +1,12 @@ +#' Executes modifications to the result of a module +#' +#' Primarily used to modify the output object of module to change the containing +#' report. #' @param x (`teal_data`) #' @param ui (`function(id, elem, ...)`) function to receive output (`shiny.tag`) from `x$ui` #' @param server (`function(input, output, session, data, ...)`) function to receive output data from `x$server` #' @param ... additional argument passed to `ui` and `server` by matching their formals names. +#' @return A `teal_report` object with the result of the server function. #' @export after <- function(x, ui = function(id, elem) elem, @@ -23,12 +28,11 @@ after <- function(x, new_x } - .after_ui <- function(x, y, additional_args) { # add `_`-prefix to make sure objects are not masked in the wrapper functions `_x` <- x # nolint: object_name. `_y` <- y # nolint: object_name. - new_x <- function() { + new_x <- function(id, ...) { original_args <- as.list(environment()) if ("..." %in% names(formals(`_x`))) { original_args <- c(original_args, list(...)) @@ -51,7 +55,7 @@ after <- function(x, # add `_`-prefix to make sure objects are not masked in the wrapper functions `_x` <- x # nolint: object_name. `_y` <- y # nolint: object_name. - new_x <- function() { + new_x <- function(id) { original_args <- as.list(environment()) original_args$id <- "wrapped" if ("..." %in% names(formals(`_x`))) { @@ -71,7 +75,7 @@ after <- function(x, original_out } ) - wrapper_args <- modifyList( + wrapper_args <- utils::modifyList( additional_args, list(id = "wrapper", input = input, output = output, session = session) ) diff --git a/R/module_init_data.R b/R/module_init_data.R index 6ba83e78a0..3679883e6f 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -90,7 +90,7 @@ srv_init_data <- function(id, data) { #' @keywords internal .add_signature_to_data <- function(data) { hashes <- .get_hashes_code(data) - data_teal_report <- as.teal_report(data) + data_teal_report <- teal.reporter::as.teal_report(data) if (!inherits(data, "teal_report")) { teal.reporter::teal_card(data_teal_report) <- c( teal.reporter::teal_card(), diff --git a/R/module_teal.R b/R/module_teal.R index 53b3547594..ecf81dcb5f 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -227,7 +227,8 @@ srv_teal <- function(id, data, modules, filter = teal_slices(), reporter = teal. session = session, modules = modules, modules_output = modules_output, - reporter = reporter + reporter = reporter, + app_id = attr(filter, "app_id") ) mapping_table <- srv_filter_manager_panel("filter_manager_panel", slices_global = slices_global) snapshots <- srv_snapshot_manager_panel("snapshot_manager_panel", slices_global = slices_global) diff --git a/R/reporter_previewer_module.R b/R/reporter_previewer_module.R index 6d1ede9676..2d557c8dff 100644 --- a/R/reporter_previewer_module.R +++ b/R/reporter_previewer_module.R @@ -51,11 +51,11 @@ reporter_previewer_module <- function(label = "Report previewer", server_args = #' #' Creates navigation for reporter previewer in main tab of the teal UI #' @noRd -insert_reporter_previewer_tab <- function(session, modules, modules_output, reporter) { +insert_reporter_previewer_tab <- function(session, modules, modules_output, reporter, app_id) { if (is.null(reporter)) { return(FALSE) } - reporter$set_id(attr(filter, "app_id")) + reporter$set_id(app_id) reporter_module <- extract_module(modules, "teal_module_previewer")[[1]] modules <- drop_module(modules, "teal_module_previewer") diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index 24cbe19803..e12d1e7ccd 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -53,7 +53,7 @@ NULL identical(attr(x[[l]], "params"), attr(this, "params")) ) { x[[length(x)]] <- do.call( - code_chunk, + teal.reporter::code_chunk, args = c( list(code = paste(x[[l]], this, sep = "\n")), attr(x[[l]], "params") diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 8dac8f8915..e5cf4c0608 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -277,7 +277,7 @@ add_document_button_srv <- function(id, reporter, r_card_fun) { } else { new_card_name <- trimws(input$label) card <- c(teal.reporter::teal_card(input$comment), r_card_fun()) - metadata(card, "title") <- new_card_name + teal.reporter::metadata(card, "title") <- new_card_name reporter$append_cards(card) shiny::showNotification("The card added successfully.", type = "message") @@ -322,7 +322,23 @@ srv_add_reporter <- function(id, module_out, reporter) { }) } +#' Disable the report for a `teal_module` +#' +#' Convenience function that disables the user's ability to add the module +#' to the report previewer. +#' @param x (`teal_module`) a `teal_module` object. +#' @return `NULL` that indicates that it should disable the reporter functionality. #' @export +#' @examples +#' app <- init( +#' data = within(teal_data(), iris <- iris), +#' modules = modules( +#' example_module(label = "example teal module") |> disable_report(), +#' ) +#' ) +#' if (interactive()) { +#' shinyApp(app$ui, app$server) +#' } disable_report <- function(x) { checkmate::assert_class(x, "teal_module") after(x, server = function(data) { From f4ac12f06cf66bf4d0b575c1d44b7ed8e6bdf03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:42:28 +0100 Subject: [PATCH 092/108] chore: rcmdcheck with 0 erros and warnings --- R/after.R | 4 ++-- R/teal_data_module-eval_code.R | 1 + R/teal_reporter.R | 2 +- man/teal_data_module.Rd | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/R/after.R b/R/after.R index 42536653d5..57ee83c72a 100644 --- a/R/after.R +++ b/R/after.R @@ -55,7 +55,7 @@ after <- function(x, # add `_`-prefix to make sure objects are not masked in the wrapper functions `_x` <- x # nolint: object_name. `_y` <- y # nolint: object_name. - new_x <- function(id) { + new_x <- function(id, ...) { original_args <- as.list(environment()) original_args$id <- "wrapped" if ("..." %in% names(formals(`_x`))) { @@ -64,7 +64,7 @@ after <- function(x, moduleServer(id, function(input, output, session) { original_out <- if (all(c("input", "output", "session") %in% names(formals(`_x`)))) { original_args$module <- `_x` - do.call(call_module, args = original_args) + do.call(shiny::callModule, args = original_args) } else { do.call(`_x`, original_args) } diff --git a/R/teal_data_module-eval_code.R b/R/teal_data_module-eval_code.R index 791701c0a1..831f304302 100644 --- a/R/teal_data_module-eval_code.R +++ b/R/teal_data_module-eval_code.R @@ -19,6 +19,7 @@ setOldClass("teal_data_module") #' @name eval_code #' @rdname teal_data_module #' @aliases eval_code,teal_data_module +#' @aliases \S4method{eval_code}{teal_data_module} #' #' @importFrom methods setMethod #' @importMethodsFrom teal.code eval_code diff --git a/R/teal_reporter.R b/R/teal_reporter.R index e5cf4c0608..9801bd5b0a 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -333,7 +333,7 @@ srv_add_reporter <- function(id, module_out, reporter) { #' app <- init( #' data = within(teal_data(), iris <- iris), #' modules = modules( -#' example_module(label = "example teal module") |> disable_report(), +#' example_module(label = "example teal module") |> disable_report() #' ) #' ) #' if (interactive()) { diff --git a/man/teal_data_module.Rd b/man/teal_data_module.Rd index d59115b6f3..cd96531899 100644 --- a/man/teal_data_module.Rd +++ b/man/teal_data_module.Rd @@ -5,6 +5,7 @@ \alias{teal_data_module} \alias{eval_code} \alias{eval_code,teal_data_module} +\alias{\S4method{eval_code}{teal_data_module}} \alias{within} \alias{within.teal_data_module} \title{Data module for \code{teal} applications} From 4f2e06a86ab1194af620e1897c18b9a72282e805 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:48:46 +0000 Subject: [PATCH 093/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/after.Rd | 29 +++++++++++++++++++++++++++++ man/disable_report.Rd | 29 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 man/after.Rd create mode 100644 man/disable_report.Rd diff --git a/man/after.Rd b/man/after.Rd new file mode 100644 index 0000000000..4ef7498d9c --- /dev/null +++ b/man/after.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/after.R +\name{after} +\alias{after} +\title{Executes modifications to the result of a module} +\usage{ +after( + x, + ui = function(id, elem) elem, + server = function(input, output, session, data) data, + ... +) +} +\arguments{ +\item{x}{(\code{teal_data})} + +\item{ui}{(\verb{function(id, elem, ...)}) function to receive output (\code{shiny.tag}) from \code{x$ui}} + +\item{server}{(\verb{function(input, output, session, data, ...)}) function to receive output data from \code{x$server}} + +\item{...}{additional argument passed to \code{ui} and \code{server} by matching their formals names.} +} +\value{ +A \code{teal_report} object with the result of the server function. +} +\description{ +Primarily used to modify the output object of module to change the containing +report. +} diff --git a/man/disable_report.Rd b/man/disable_report.Rd new file mode 100644 index 0000000000..b735ae4ac7 --- /dev/null +++ b/man/disable_report.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/teal_reporter.R +\name{disable_report} +\alias{disable_report} +\title{Disable the report for a \code{teal_module}} +\usage{ +disable_report(x) +} +\arguments{ +\item{x}{(\code{teal_module}) a \code{teal_module} object.} +} +\value{ +\code{NULL} that indicates that it should disable the reporter functionality. +} +\description{ +Convenience function that disables the user's ability to add the module +to the report previewer. +} +\examples{ +app <- init( + data = within(teal_data(), iris <- iris), + modules = modules( + example_module(label = "example teal module") |> disable_report() + ) +) +if (interactive()) { + shinyApp(app$ui, app$server) +} +} From 65e6e04516e5aedf049d4c834498da6c2b514983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:51:57 +0100 Subject: [PATCH 094/108] empty: trigger ci From 93071a3a1012bf801304c9db4cf02794decf477a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:21:45 +0100 Subject: [PATCH 095/108] chore: fix docs ci --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 468399619f..f0bc2d1578 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -130,6 +130,7 @@ reference: - reporter_previewer_module - TealReportCard - report_card_template + - disable_report - title: Landing popup contents: - landing_popup_module From abb2307badccacd53886190599f7886ec457389b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ka=C5=82=C4=99dkowski?= Date: Thu, 12 Jun 2025 11:09:59 +0200 Subject: [PATCH 096/108] Disable report when error (#1547) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When teal_module's server returns an error (validation error, qenv error etc.) teal.reporter-add button has no protection against it. This PR fixes following: - To not display error received from a module - To disable add-to-report-button when error occurs --------- Signed-off-by: Dawid Kałędkowski Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- R/module_nested_tabs.R | 4 ++-- R/teal_reporter.R | 36 ++++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 06fe5336b0..5351751530 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -404,9 +404,9 @@ srv_teal_module.teal_module <- function(id, module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) } ) - }) - srv_add_reporter("add_reporter_wrapper", module_out, reporter) + srv_add_reporter("add_reporter_wrapper", module_out, reporter) + }) module_out }) } diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 9801bd5b0a..05028dba29 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -296,29 +296,33 @@ srv_add_reporter <- function(id, module_out, reporter) { return(FALSE) } # early exit moduleServer(id, function(input, output, session) { - doc_out <- reactive({ + mod_out_r <- reactive({ req(module_out()) if (is.reactive(module_out())) { - req(module_out()()) - if (inherits(module_out()(), "teal_report") || length(teal.reporter::teal_card(module_out()()))) { - .collapse_subsequent_chunks(teal.reporter::teal_card(module_out()())) - } + module_out()() } }) - output$reporter_add_container <- renderUI({ - req(doc_out()) - tags$div( - class = "teal add-reporter-container", - teal.reporter::add_card_button_ui(session$ns("reporter_add")) - ) + doc_out <- reactive({ + teal_data_handled <- tryCatch(mod_out_r(), error = function(e) e) + if (inherits(teal_data_handled, "teal_report") && length(teal.reporter::teal_card(teal_data_handled))) { + .collapse_subsequent_chunks(teal.reporter::teal_card(teal_data_handled)) + } }) - add_document_button_srv( - "reporter_add", - reporter = reporter, - r_card_fun = doc_out - ) + .call_once_when(!is.null(doc_out()), { + output$reporter_add_container <- renderUI({ + tags$div( + class = "teal add-reporter-container", + teal.reporter::add_card_button_ui(session$ns("reporter_add")) + ) + }) + teal.reporter::add_card_button_srv("reporter_add", reporter = reporter, card_fun = doc_out) + }) + + observeEvent(doc_out(), ignoreNULL = FALSE, { + shinyjs::toggleState("reporter_add_container", condition = inherits(doc_out(), "teal_card")) + }) }) } From 290cbb919ad84afbd2457aaddaf97ffecfdff5d7 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Thu, 19 Jun 2025 08:27:34 +0200 Subject: [PATCH 097/108] changing sections to h2 as h1 is dedicated to the title --- R/module_init_data.R | 2 +- R/teal_data_utils.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index 3679883e6f..0f065a523f 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -94,7 +94,7 @@ srv_init_data <- function(id, data) { if (!inherits(data, "teal_report")) { teal.reporter::teal_card(data_teal_report) <- c( teal.reporter::teal_card(), - "# Code preparation", + "## Code preparation", teal.reporter::teal_card(data_teal_report) ) } diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index e12d1e7ccd..eaaecfb071 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -23,7 +23,7 @@ NULL data@code <- c(data@code, code2list(code)) teal.reporter::teal_card(data) <- c( teal.reporter::teal_card(data), - "# Data filtering", + "## Data filtering", teal.reporter::code_chunk(code) ) methods::validObject(data) From cf9c3b4d7ef9ede4e85b29220b720f29db00773f Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Thu, 19 Jun 2025 08:30:37 +0200 Subject: [PATCH 098/108] adding an attribute to start a new chunk for following (no .collapse_following_chunks for next code_chunks) --- R/teal_data_utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index eaaecfb071..bb691c4f35 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -24,7 +24,7 @@ NULL teal.reporter::teal_card(data) <- c( teal.reporter::teal_card(data), "## Data filtering", - teal.reporter::code_chunk(code) + teal.reporter::code_chunk(code, "data-filtering") ) methods::validObject(data) } From b8549a6e2df27cc4ac8d2fac1945d6d54e60f22f Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 1 Jul 2025 07:53:53 +0200 Subject: [PATCH 099/108] don't add a label to teal_card's code_chunk - avoid duplicated label in a combined document --- R/teal_data_utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index bb691c4f35..eaaecfb071 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -24,7 +24,7 @@ NULL teal.reporter::teal_card(data) <- c( teal.reporter::teal_card(data), "## Data filtering", - teal.reporter::code_chunk(code, "data-filtering") + teal.reporter::code_chunk(code) ) methods::validObject(data) } From 47271339fc2a2a6478de4cbbf29b85f0975a4d26 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 1 Jul 2025 07:54:18 +0200 Subject: [PATCH 100/108] unwrap reactive sooner --- R/module_nested_tabs.R | 5 +++-- R/teal_reporter.R | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 7b06d28702..b791a817f1 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -401,8 +401,9 @@ srv_teal_module.teal_module <- function(id, !is.null(module_teal_data()), ignoreNULL = TRUE, handlerExpr = { - module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) - srv_add_reporter("add_reporter_wrapper", module_out, reporter) + out <- .call_teal_module(modules, datasets, module_teal_data, reporter) + srv_add_reporter("add_reporter_wrapper", out, reporter) + module_out(out) } ) }) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index bf0883265e..0d91f24a94 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -297,9 +297,9 @@ srv_add_reporter <- function(id, module_out, reporter) { } # early exit moduleServer(id, function(input, output, session) { mod_out_r <- reactive({ - req(module_out()) - if (is.reactive(module_out())) { - module_out()() + req(module_out) + if (is.reactive(module_out)) { + module_out() } }) From 16939fe4f0ef84ff2a23c7cf0bdbcaca5679eb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:15:27 +0100 Subject: [PATCH 101/108] fix: revert back to main --- DESCRIPTION | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 2d10d8037c..6e44122fa1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -77,10 +77,10 @@ VignetteBuilder: knitr, rmarkdown Remotes: - insightsengineering/teal.code@teal_reportable, - insightsengineering/teal.data@teal_reportable, + insightsengineering/teal.code@main, + insightsengineering/teal.data@main, insightsengineering/teal.logger@main, - insightsengineering/teal.reporter@teal_reportable, + insightsengineering/teal.reporter@main, insightsengineering/teal.slice@main, insightsengineering/teal.widgets@main Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, From 02839420952f1254cdc392c42d543cba28d7df31 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 10:38:49 +0000 Subject: [PATCH 102/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/TealReportCard.Rd | 1 - 1 file changed, 1 deletion(-) diff --git a/man/TealReportCard.Rd b/man/TealReportCard.Rd index 85d4d2208b..061dbb73ba 100644 --- a/man/TealReportCard.Rd +++ b/man/TealReportCard.Rd @@ -57,7 +57,6 @@ card$get_content()[[1]]$get_content()
  • teal.reporter::ReportCard$get_name()
  • teal.reporter::ReportCard$initialize()
  • teal.reporter::ReportCard$reset()
  • -
  • teal.reporter::ReportCard$set_content_names()
  • teal.reporter::ReportCard$set_name()
  • teal.reporter::ReportCard$to_list()
  • From 02d3ba6b920d077b3c51c064c22b6fe18a3d640d Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Wed, 2 Jul 2025 07:25:58 +0200 Subject: [PATCH 103/108] remotes --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6e44122fa1..8854f6ebea 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -80,7 +80,7 @@ Remotes: insightsengineering/teal.code@main, insightsengineering/teal.data@main, insightsengineering/teal.logger@main, - insightsengineering/teal.reporter@main, + insightsengineering/teal.reporter@teal_reportable, insightsengineering/teal.slice@main, insightsengineering/teal.widgets@main Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, From 29a4cecf21cfa94804fa61fb48be559d68612bf1 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 05:31:18 +0000 Subject: [PATCH 104/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/TealReportCard.Rd | 1 + 1 file changed, 1 insertion(+) diff --git a/man/TealReportCard.Rd b/man/TealReportCard.Rd index 061dbb73ba..85d4d2208b 100644 --- a/man/TealReportCard.Rd +++ b/man/TealReportCard.Rd @@ -57,6 +57,7 @@ card$get_content()[[1]]$get_content()
  • teal.reporter::ReportCard$get_name()
  • teal.reporter::ReportCard$initialize()
  • teal.reporter::ReportCard$reset()
  • +
  • teal.reporter::ReportCard$set_content_names()
  • teal.reporter::ReportCard$set_name()
  • teal.reporter::ReportCard$to_list()
  • From 9307ed79403f25a843e84e888d1502a71c7d97b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 2 Jul 2025 13:12:55 +0200 Subject: [PATCH 105/108] Update R/module_teal.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com> --- R/module_teal.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/module_teal.R b/R/module_teal.R index ecf81dcb5f..f76fc80c07 100644 --- a/R/module_teal.R +++ b/R/module_teal.R @@ -50,7 +50,6 @@ ui_teal <- function(id, modules) { checkmate::assert_character(id, max.len = 1, any.missing = FALSE) checkmate::assert_class(modules, "teal_modules") ns <- NS(id) - modules <- drop_module(modules, "teal_previewer_module") # show busy icon when `shiny` session is busy computing stuff # based on https://stackoverflow.com/questions/17325521/r-shiny-display-loading-message-while-function-is-running/22475216#22475216 # nolint: line_length. shiny_busy_message_panel <- conditionalPanel( From 23f3d0317b4a29a2d37c1fd2e6882276bd558534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 4 Jul 2025 08:30:14 +0100 Subject: [PATCH 106/108] cleanup: remove function --- R/teal_reporter.R | 92 ----------------------------------------------- 1 file changed, 92 deletions(-) diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 0d91f24a94..6ebe5531ff 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -195,98 +195,6 @@ TealSlicesBlock <- R6::R6Class( # nolint: object_name_linter. ) ) -#' Server function for `teal_card` class. -#' @keywords internal -add_document_button_srv <- function(id, reporter, r_card_fun) { - checkmate::assert_class(r_card_fun, "reactive") - checkmate::assert_class(reporter, "Reporter") - - shiny::moduleServer(id, function(input, output, session) { - shiny::setBookmarkExclude(c( - "add_report_card_button", "download_button", "reset_reporter", - "add_card_ok", "download_data", "reset_reporter_ok", - "label", "comment" - )) - - ns <- session$ns - add_modal <- function() { - div( - class = "teal-widgets reporter-modal", - shiny::modalDialog( - easyClose = TRUE, - shiny::tags$h3("Add a Card to the Report"), - shiny::tags$hr(), - shiny::textInput( - ns("label"), - "Card Name", - value = "", - placeholder = "Add the card title here", - width = "100%" - ), - shiny::textAreaInput( - ns("comment"), - "Comment", - value = "", - placeholder = "Add a comment here...", - width = "100%" - ), - shiny::tags$script( - shiny::HTML( - sprintf("shinyjs.autoFocusModal('%s');", ns("label")), - sprintf("shinyjs.enterToSubmit('%s', '%s');", ns("label"), ns("add_card_ok")) - ) - ), - footer = shiny::div( - shiny::tags$button( - type = "button", - class = "btn btn-secondary", - `data-dismiss` = "modal", - `data-bs-dismiss` = "modal", - NULL, - "Cancel" - ), - shiny::tags$button( - id = ns("add_card_ok"), - type = "button", - class = "btn btn-primary action-button", - `data-val` = shiny::restoreInput(id = ns("add_card_ok"), default = NULL), - NULL, - "Add Card" - ) - ) - ) - ) - } - - shiny::observeEvent(input$add_report_card_button, { - shiny::removeModal() - shiny::showModal(add_modal()) - }) - - # the add card button is disabled when clicked to prevent multi-clicks - # please check the ui part for more information - shiny::observeEvent(input$add_card_ok, { - if (inherits(r_card_fun, "try-error")) { - msg <- paste( - "The card could not be added to the report.", - "Have the outputs for the report been created yet? If not please try again when they", - "are ready. Otherwise contact your application developer." - ) - warning(msg) - shiny::showNotification(msg, type = "error") - } else { - new_card_name <- trimws(input$label) - card <- c(teal.reporter::teal_card(input$comment), r_card_fun()) - teal.reporter::metadata(card, "title") <- new_card_name - - reporter$append_cards(card) - shiny::showNotification("The card added successfully.", type = "message") - shiny::removeModal() - } - }) - }) -} - #' @noRd ui_add_reporter <- function(id) uiOutput(NS(id, "reporter_add_container")) From 93074e00937d1c9ca4ec072aa46fb31423a64798 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 13:20:34 +0000 Subject: [PATCH 107/108] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/add_document_button_srv.Rd | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 man/add_document_button_srv.Rd diff --git a/man/add_document_button_srv.Rd b/man/add_document_button_srv.Rd deleted file mode 100644 index ccf6015cb8..0000000000 --- a/man/add_document_button_srv.Rd +++ /dev/null @@ -1,12 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_reporter.R -\name{add_document_button_srv} -\alias{add_document_button_srv} -\title{Server function for \code{teal_card} class.} -\usage{ -add_document_button_srv(id, reporter, r_card_fun) -} -\description{ -Server function for \code{teal_card} class. -} -\keyword{internal} From dbb46cd9baa5deb40bb676422b9308d65a212b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Tue, 8 Jul 2025 14:31:55 +0200 Subject: [PATCH 108/108] Block removal@teal reportable (#1552) Companion of https://github.com/insightsengineering/teal.reporter/pull/351 --------- Co-authored-by: Dawid Kaledkowski --- R/teal_reporter.R | 152 ++++++++--------------------------- R/zzz.R | 2 - man/TealSlicesBlock.Rd | 177 ----------------------------------------- 3 files changed, 33 insertions(+), 298 deletions(-) delete mode 100644 man/TealSlicesBlock.Rd diff --git a/R/teal_reporter.R b/R/teal_reporter.R index 6ebe5531ff..09358a1887 100644 --- a/R/teal_reporter.R +++ b/R/teal_reporter.R @@ -22,14 +22,7 @@ TealReportCard <- R6::R6Class( # nolint: object_name. #' ) #' card$get_content()[[1]]$get_content() append_src = function(src, ...) { - checkmate::assert_character(src, min.len = 0, max.len = 1) - params <- list(...) - params$eval <- FALSE - rblock <- RcodeBlock$new(src) - rblock$set_params(params) - self$append_content(rblock) - self$append_metadata("SRC", src) - invisible(self) + super$append_rcode(text = src, ...) }, #' @description Appends the filter state list to the `content` and `metadata` of this `TealReportCard`. #' If the filter state list has an attribute named `formatted`, it appends it to the card otherwise it uses @@ -40,11 +33,8 @@ TealReportCard <- R6::R6Class( # nolint: object_name. #' @return `self`, invisibly. append_fs = function(fs) { checkmate::assert_class(fs, "teal_slices") - self$append_text("Filter State", "header3") - if (length(fs)) { - self$append_content(TealSlicesBlock$new(fs)) - } else { - self$append_text("No filters specified.") + if (length(fs) > 0) { + self$append_content(code_chunk(.teal_slice_to_yaml(fs), eval = FALSE, lang = "verbatim")) } invisible(self) }, @@ -85,115 +75,39 @@ TealReportCard <- R6::R6Class( # nolint: object_name. ) ) -#' @title `TealSlicesBlock` -#' @docType class -#' @description -#' Specialized `TealSlicesBlock` block for managing filter panel content in reports. -#' @keywords internal -TealSlicesBlock <- R6::R6Class( # nolint: object_name_linter. - classname = "TealSlicesBlock", - inherit = teal.reporter:::TextBlock, - public = list( - #' @description Returns a `TealSlicesBlock` object. - #' - #' @details Returns a `TealSlicesBlock` object with no content and no parameters. - #' - #' @param content (`teal_slices`) object returned from [teal_slices()] function. - #' @param style (`character(1)`) string specifying style to apply. - #' - #' @return Object of class `TealSlicesBlock`, invisibly. - #' - initialize = function(content = teal_slices(), style = "verbatim") { - self$set_content(content) - self$set_style(style) - invisible(self) - }, - - #' @description Sets content of this `TealSlicesBlock`. - #' Sets content as `YAML` text which represents a list generated from `teal_slices`. - #' The list displays limited number of fields from `teal_slice` objects, but this list is - #' sufficient to conclude which filters were applied. - #' When `selected` field in `teal_slice` object is a range, then it is displayed as a "min" - #' - #' - #' @param content (`teal_slices`) object returned from [teal_slices()] function. - #' @return `self`, invisibly. - set_content = function(content) { - checkmate::assert_class(content, "teal_slices") - if (length(content) != 0) { - states_list <- lapply(content, function(x) { - x_list <- shiny::isolate(as.list(x)) - if ( - inherits(x_list$choices, c("integer", "numeric", "Date", "POSIXct", "POSIXlt")) && - length(x_list$choices) == 2 && - length(x_list$selected) == 2 - ) { - x_list$range <- paste(x_list$selected, collapse = " - ") - x_list["selected"] <- NULL - } - if (!is.null(x_list$arg)) { - x_list$arg <- if (x_list$arg == "subset") "Genes" else "Samples" - } +.teal_slice_to_yaml <- function(fs) { + checkmate::assert_class(fs, "teal_slices") + states_list <- lapply(fs, function(x) { + x_list <- shiny::isolate(as.list(x)) + if ( + inherits(x_list$choices, c("integer", "numeric", "Date", "POSIXct", "POSIXlt")) && + length(x_list$choices) == 2 && + length(x_list$selected) == 2 + ) { + x_list$range <- paste(x_list$selected, collapse = " - ") + x_list["selected"] <- NULL + } + if (!is.null(x_list$arg)) { + x_list$arg <- if (x_list$arg == "subset") "Genes" else "Samples" + } - x_list <- x_list[ - c("dataname", "varname", "experiment", "arg", "expr", "selected", "range", "keep_na", "keep_inf") - ] - names(x_list) <- c( - "Dataset name", "Variable name", "Experiment", "Filtering by", "Applied expression", - "Selected Values", "Selected range", "Include NA values", "Include Inf values" - ) + x_list <- x_list[ + c("dataname", "varname", "experiment", "arg", "expr", "selected", "range", "keep_na", "keep_inf") + ] + names(x_list) <- c( + "Dataset name", "Variable name", "Experiment", "Filtering by", "Applied expression", + "Selected Values", "Selected range", "Include NA values", "Include Inf values" + ) - Filter(Negate(is.null), x_list) - }) + Filter(Negate(is.null), x_list) + }) - if (requireNamespace("yaml", quietly = TRUE)) { - super$set_content(yaml::as.yaml(states_list)) - } else { - stop("yaml package is required to format the filter state list") - } - } - private$teal_slices <- content - invisible(self) - }, - #' @description Create the `TealSlicesBlock` from a list. - #' - #' @param x (`named list`) with two fields `text` and `style`. - #' Use the `get_available_styles` method to get all possible styles. - #' - #' @return `self`, invisibly. - #' @examples - #' TealSlicesBlock <- getFromNamespace("TealSlicesBlock", "teal") - #' block <- TealSlicesBlock$new() - #' block$from_list(list(text = "sth", style = "default")) - #' - from_list = function(x) { - checkmate::assert_list(x) - checkmate::assert_names(names(x), must.include = c("text", "style")) - super$set_content(x$text) - super$set_style(x$style) - invisible(self) - }, - #' @description Convert the `TealSlicesBlock` to a list. - #' - #' @return `named list` with a text and style. - #' @examples - #' TealSlicesBlock <- getFromNamespace("TealSlicesBlock", "teal") - #' block <- TealSlicesBlock$new() - #' block$to_list() - #' - to_list = function() { - content <- self$get_content() - list( - text = if (length(content)) content else "", - style = self$get_style() - ) - } - ), - private = list( - style = "verbatim", - teal_slices = NULL # teal_slices - ) -) + if (requireNamespace("yaml", quietly = TRUE)) { + yaml::as.yaml(states_list) + } else { + stop("yaml package is required to format the filter state list") + } +} #' @noRd ui_add_reporter <- function(id) uiOutput(NS(id, "reporter_add_container")) diff --git a/R/zzz.R b/R/zzz.R index 62c8029561..a4c91329fd 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -31,8 +31,6 @@ setdiff_teal_slices <- getFromNamespace("setdiff_teal_slices", "teal.slice") # This one is here because it is needed by c.teal_slices but we don't want it exported from teal.slice. coalesce_r <- getFromNamespace("coalesce_r", "teal.slice") -# all *Block objects are private in teal.reporter -RcodeBlock <- getFromNamespace("RcodeBlock", "teal.reporter") # nolint: object_name. # Use non-exported function(s) from teal.code # This one is here because lang2calls should not be exported from teal.code diff --git a/man/TealSlicesBlock.Rd b/man/TealSlicesBlock.Rd deleted file mode 100644 index a5be9a0446..0000000000 --- a/man/TealSlicesBlock.Rd +++ /dev/null @@ -1,177 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_reporter.R -\docType{class} -\name{TealSlicesBlock} -\alias{TealSlicesBlock} -\title{\code{TealSlicesBlock}} -\description{ -Specialized \code{TealSlicesBlock} block for managing filter panel content in reports. -} -\examples{ - -## ------------------------------------------------ -## Method `TealSlicesBlock$from_list` -## ------------------------------------------------ - -TealSlicesBlock <- getFromNamespace("TealSlicesBlock", "teal") -block <- TealSlicesBlock$new() -block$from_list(list(text = "sth", style = "default")) - - -## ------------------------------------------------ -## Method `TealSlicesBlock$to_list` -## ------------------------------------------------ - -TealSlicesBlock <- getFromNamespace("TealSlicesBlock", "teal") -block <- TealSlicesBlock$new() -block$to_list() - -} -\keyword{internal} -\section{Super classes}{ -\code{\link[teal.reporter:ContentBlock]{teal.reporter::ContentBlock}} -> \code{\link[teal.reporter:TextBlock]{teal.reporter::TextBlock}} -> \code{TealSlicesBlock} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-TealSlicesBlock-new}{\code{TealSlicesBlock$new()}} -\item \href{#method-TealSlicesBlock-set_content}{\code{TealSlicesBlock$set_content()}} -\item \href{#method-TealSlicesBlock-from_list}{\code{TealSlicesBlock$from_list()}} -\item \href{#method-TealSlicesBlock-to_list}{\code{TealSlicesBlock$to_list()}} -\item \href{#method-TealSlicesBlock-clone}{\code{TealSlicesBlock$clone()}} -} -} -\if{html}{\out{ -
    Inherited methods - -
    -}} -\if{html}{\out{
    }} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-TealSlicesBlock-new}{}}} -\subsection{Method \code{new()}}{ -Returns a \code{TealSlicesBlock} object. -\subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{TealSlicesBlock$new(content = teal_slices(), style = "verbatim")}\if{html}{\out{
    }} -} - -\subsection{Arguments}{ -\if{html}{\out{
    }} -\describe{ -\item{\code{content}}{(\code{teal_slices}) object returned from \code{\link[=teal_slices]{teal_slices()}} function.} - -\item{\code{style}}{(\code{character(1)}) string specifying style to apply.} -} -\if{html}{\out{
    }} -} -\subsection{Details}{ -Returns a \code{TealSlicesBlock} object with no content and no parameters. -} - -\subsection{Returns}{ -Object of class \code{TealSlicesBlock}, invisibly. -} -} -\if{html}{\out{
    }} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-TealSlicesBlock-set_content}{}}} -\subsection{Method \code{set_content()}}{ -Sets content of this \code{TealSlicesBlock}. -Sets content as \code{YAML} text which represents a list generated from \code{teal_slices}. -The list displays limited number of fields from \code{teal_slice} objects, but this list is -sufficient to conclude which filters were applied. -When \code{selected} field in \code{teal_slice} object is a range, then it is displayed as a "min" -\subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{TealSlicesBlock$set_content(content)}\if{html}{\out{
    }} -} - -\subsection{Arguments}{ -\if{html}{\out{
    }} -\describe{ -\item{\code{content}}{(\code{teal_slices}) object returned from \code{\link[=teal_slices]{teal_slices()}} function.} -} -\if{html}{\out{
    }} -} -\subsection{Returns}{ -\code{self}, invisibly. -} -} -\if{html}{\out{
    }} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-TealSlicesBlock-from_list}{}}} -\subsection{Method \code{from_list()}}{ -Create the \code{TealSlicesBlock} from a list. -\subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{TealSlicesBlock$from_list(x)}\if{html}{\out{
    }} -} - -\subsection{Arguments}{ -\if{html}{\out{
    }} -\describe{ -\item{\code{x}}{(\verb{named list}) with two fields \code{text} and \code{style}. -Use the \code{get_available_styles} method to get all possible styles.} -} -\if{html}{\out{
    }} -} -\subsection{Returns}{ -\code{self}, invisibly. -} -\subsection{Examples}{ -\if{html}{\out{
    }} -\preformatted{TealSlicesBlock <- getFromNamespace("TealSlicesBlock", "teal") -block <- TealSlicesBlock$new() -block$from_list(list(text = "sth", style = "default")) - -} -\if{html}{\out{
    }} - -} - -} -\if{html}{\out{
    }} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-TealSlicesBlock-to_list}{}}} -\subsection{Method \code{to_list()}}{ -Convert the \code{TealSlicesBlock} to a list. -\subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{TealSlicesBlock$to_list()}\if{html}{\out{
    }} -} - -\subsection{Returns}{ -\verb{named list} with a text and style. -} -\subsection{Examples}{ -\if{html}{\out{
    }} -\preformatted{TealSlicesBlock <- getFromNamespace("TealSlicesBlock", "teal") -block <- TealSlicesBlock$new() -block$to_list() - -} -\if{html}{\out{
    }} - -} - -} -\if{html}{\out{
    }} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-TealSlicesBlock-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
    }}\preformatted{TealSlicesBlock$clone(deep = FALSE)}\if{html}{\out{
    }} -} - -\subsection{Arguments}{ -\if{html}{\out{
    }} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
    }} -} -} -}