Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Package: uteals
Title: Shared Utilities to Extend the 'teal' Modules
Language: en-US
Version: 0.0.4.9000
Date: 2026-04-13
Date: 2026-06-30
URL: https://github.com/phuse-org/uteals
Authors@R:
c(
Expand Down
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ importFrom(gridify,gridifyCells)
importFrom(gridify,gridifyLayout)
importFrom(gridify,gridifyObject)
importFrom(gridify,set_cell)
importFrom(jsonlite,base64_enc)
importFrom(jsonlite,read_json)
importFrom(methods,new)
importFrom(openxlsx,read.xlsx)
importFrom(patchwork,plot_annotation)
Expand Down Expand Up @@ -102,6 +104,8 @@ importFrom(teal,teal_transform_module)
importFrom(teal.code,eval_code)
importFrom(teal.modules.clinical,add_expr)
importFrom(teal.modules.clinical,bracket_expr)
importFrom(teal.reporter,Reporter)
importFrom(tern,rtable2gg)
importFrom(utils,write.csv)
importFrom(yaml,as.yaml)
importFrom(zip,zip)
9 changes: 5 additions & 4 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
- Fixed ID conflict in `or_filtering_transformator` that caused errors when multiple instances were used in the same Shiny app. Removed a `shinyBS::bsModal()` block with fixed, non-module-scoped IDs (dead code — preview is handled by `shiny::showModal()`), resolving duplicate element IDs across instances.
- Added `updateOn = "blur"` to all `textInput` controls so that reactive updates are only triggered when the user leaves the field, reducing unnecessary re-renders while typing. Requires `shiny >= 1.11.0`.
- Refactored `title_footer_decorator` not to overwrite the first row of the TABLE.ID column in the imported file. This change allows for importing files that have meaningful data in the first row.
- Allowed to deselect the title in `title_footer_decorator`.
- Added a button for CSV download for `tm_report_manager` module. #46

# Version 0.0.3

- Refactored the `merge_levels_transformator` to handle the predefined inputs to the transformator ([#25](https://github.com/phuse-org/uteals/pull/25)).
- Added new `watermark_decorator` ([#23](https://github.com/phuse-org/uteals/pull/23)).
- Changed the example to a working one in `create_rel_risk_transformator` ([#27](https://github.com/phuse-org/uteals/pull/27))
- Changed the example to a working one in `ggplot_decorator` ([#28](https://github.com/phuse-org/uteals/pull/28))
- Refactored the `merge_levels_transformator` to handle the predefined inputs to the transformator (#25).
- Added new `watermark_decorator` (#23).
- Changed the example to a working one in `create_rel_risk_transformator` (#27)
- Changed the example to a working one in `ggplot_decorator` (#28)

# Version 0.0.2

Expand Down
16 changes: 14 additions & 2 deletions R/title_footer_decorator.R
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ title_footer_decorator <- function(output_name, titles_file, choices = NULL, sel
titles <- openxlsx::read.xlsx(titles_file, "Sheet1")
titles <- titles |> dplyr::filter(!grepl("delete", .data$TABLE.ID, ignore.case = TRUE))

selected <- `if`(is.null(selected), "", selected)
choices <- `if`(is.null(choices), unique(titles$TABLE.ID), intersect(choices, titles$TABLE.ID))
choices <- c(choices, "")
checkmate::assert(
checkmate::check_null(selected),
checkmate::check_choice(selected, choices)
Expand All @@ -122,7 +124,17 @@ title_footer_decorator <- function(output_name, titles_file, choices = NULL, sel
ns <- NS(id)
tagList(
div(
selectInput(ns("selectTitle"), label = "Select Title", choices = choices, selected = selected),
selectizeInput(
ns("selectTitle"),
label = "Select Title",
choices = choices,
selected = selected,
options = list(
allowEmptyOption = TRUE,
placeholder = "Select a value...",
plugins = list("clear_button")
)
),
checkboxInput(ns("customize"), label = "Customize Title and Footer", value = FALSE),
uiOutput(ns("customInputs"))
)
Expand Down Expand Up @@ -174,7 +186,7 @@ title_footer_decorator <- function(output_name, titles_file, choices = NULL, sel
customTitle = input$customTitle,
customFooter = input$customFooter
)
} else if (input$selectTitle != "blank") {
} else if (input$selectTitle != "blank" && !is.null(input$selectTitle) && input$selectTitle != "") {
res <- within(
res,
{
Expand Down
3 changes: 3 additions & 0 deletions R/uteals.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
#'
#' Provides decorator and transformator modules
#' for `teal` modules.
#' @importFrom jsonlite base64_enc read_json
#' @importFrom teal.reporter Reporter
#' @importFrom zip zip
"_PACKAGE"
40 changes: 40 additions & 0 deletions tests/testthat/test-title_footer_decorator.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,43 @@ testthat::test_that("title_footer_decorator errors when selected is not in choic
title_footer_decorator("plot", titles_file, choices = c("TSFLAB01", "TSFLAB01b"), selected = "TBL99")
)
})

testthat::test_that("server logic handles empty selection correctly for plots", {
decorator <- title_footer_decorator("plot", titles_file, choices = c("TSFLAB01", "TSFLAB01b"), selected = "")

mock_data <- shiny::reactive({
q <- teal.code::qenv()
teal.code::eval_code(q, "plot <- ggplot2::ggplot()")
})

shiny::testServer(decorator$server, args = list(data = mock_data), {
session$setInputs(customize = FALSE, selectTitle = "")
res_reactive <- session$getReturned()
res <- withr::with_options(list(device = function() pdf(file = NULL)), {
res_reactive()
})

testthat::expect_equal(res[["plot"]]$labels$title, "")
testthat::expect_equal(res[["plot"]]$labels$caption, "")
})
})

testthat::test_that("server logic handles empty selection correctly for tables", {
decorator <- title_footer_decorator("table", titles_file, choices = c("TSFLAB01", "TSFLAB01b"), selected = "")

mock_data <- shiny::reactive({
q <- teal.code::qenv()
teal.code::eval_code(q, "table <- rtables::rtable(header = 'a', rtables::rrow('1', 1))")
})

shiny::testServer(decorator$server, args = list(data = mock_data), {
session$setInputs(customize = FALSE, selectTitle = "")
res_reactive <- session$getReturned()
res <- withr::with_options(list(device = function() pdf(file = NULL)), {
res_reactive()
})

testthat::expect_equal(formatters::main_title(res[["table"]]), "")
testthat::expect_equal(formatters::main_footer(res[["table"]]), "")
})
})
Loading