diff --git a/DESCRIPTION b/DESCRIPTION index 6027976..4a21b46 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,10 +2,21 @@ Type: Package Package: teal.picks Title: Dataset and Variable Picker and Merge Module for 'teal' Applications -Version: 0.1.0.9244 -Date: 2026-05-08 -Authors@R: - person("insightsengineering", , , "insightsengineering@example.com", role = c("aut", "cre")) +Version: 0.1.0 +Date: 2026-05-07 +Authors@R: c( + person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), + comment = c(ORCID = "0000-0001-9533-457X")), + person("Andre", "Verissimo", , "andre.verissimo@roche.com", role = "aut", + comment = c(ORCID = "0000-0002-2212-339X")), + person("Marcin", "Kosinski", , "marcin.kosinski.mk1@roche.com", role = "aut"), + person("LluĂ­s", "Revilla Sancho", , "lluis.revilla_sancho@roche.com", role = "aut", + comment = c(ORCID = "0000-0001-9747-2570")), + person("Oriol", "Senan", , "oriol.senan@external.roche.com", role = "aut", + comment = c(ORCID = "0000-0002-9621-3371")), + person("Dony", "Unardi", , "unardid@gene.com", role = "rev"), + person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")) + ) Description: Allows users to interactively select datasets, variables, and values within 'teal' applications using a 'tidyselect'-style interface. Selected picks can be merged and transformed into diff --git a/NEWS.md b/NEWS.md index 998b774..cc19376 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal.picks 0.1.0.9244 +# teal.picks 0.1.0 ### New Features diff --git a/R/as_picks.R b/R/as_picks.R index e6386f9..001a0af 100644 --- a/R/as_picks.R +++ b/R/as_picks.R @@ -14,6 +14,7 @@ #' conversion from [`data_extract_spec`] to [`picks`]. Unfortunately, when [`data_extract_spec`] #' contains [`filter_spec`] then `as.picks` is unable to provide reliable [`picks`] equivalent. #' +#' @return A `picks` object when conversion is possible, otherwise `NULL` with a warning (if `quiet = FALSE`). #' @examples #' # convert des with eager select_spec #' as.picks( diff --git a/R/call_utils.R b/R/call_utils.R index ad0bae1..8f92400 100644 --- a/R/call_utils.R +++ b/R/call_utils.R @@ -233,6 +233,7 @@ calls_combine_by <- function(operator, calls) { #' @param variables (`list` of `character`) variables to select. If list is named then #' variables will be renamed if their name is different than its value #' (this produces a call `select(..., = )`). +#' @return `call`. #' @keywords internal .call_dplyr_select <- function(dataname, variables) { as.call( @@ -250,6 +251,7 @@ calls_combine_by <- function(operator, calls) { #' #' Create a `dplyr::filter` call #' @param x (`list`) containing `variables` and `values` +#' @return `call`. #' @keywords internal .call_dplyr_filter <- function(x) { if (any(!names(x) %in% c("variables", "values"))) { diff --git a/R/interaction.R b/R/interaction.R index 07de071..42a6dce 100644 --- a/R/interaction.R +++ b/R/interaction.R @@ -14,6 +14,16 @@ #' `var2` in `vars`, or `NA` where a variable is not found. #' #' @export +#' @examples +#' picks( +#' datasets("ADAE"), +#' variables( +#' c(AGE, RACE, interaction_vars("COUNTRY", "RACE")), +#' selected = "COUNTRY:RACE", +#' multiple = TRUE +#' ), +#' values() +#' ) interaction_vars <- function(var1, var2, vars = tidyselect::peek_vars(fn = "interaction_vars")) { new_var <- c(as.character(substitute(var1)), as.character(substitute(var2))) result <- match(new_var, vars) diff --git a/R/module_merge.R b/R/module_merge.R index fd0a152..84c67dc 100644 --- a/R/module_merge.R +++ b/R/module_merge.R @@ -482,6 +482,8 @@ merge_srv <- function(id, #' #' @inheritParams merge_srv #' @param join_keys (`join_keys`) The join keys object +#' @return `TRUE` if validation passes, otherwise raises a validation +#' error with details on which datasets cannot be merged and why. #' #' @keywords internal .validate_join_keys <- function(selectors, join_keys) { diff --git a/R/module_picks.R b/R/module_picks.R index 862ad66..17e30a6 100644 --- a/R/module_picks.R +++ b/R/module_picks.R @@ -32,6 +32,35 @@ #' @seealso [picks()] for creating `picks`` objects #' #' @name picks_module +#' @examples +#' library(shiny) +#' +#' example_pick <- picks( +#' datasets("ADSL"), +#' variables(selected = c("SEX", "COUNTRY", "ARMCD")) +#' ) +#' ui <- fluidPage( +#' picks_ui("my_picks", picks = example_pick), +#' h4("Resolved picks:"), +#' verbatimTextOutput("result"), +#' h4("Table:"), +#' tableOutput("table") +#' ) +#' server <- function(input, output, session) { +#' data <- teal.data::teal_data("ADSL" = teal.data::rADSL) +#' teal.data::join_keys(data) <- teal.data::default_cdisc_join_keys["ADSL"] +#' selectors <- picks_srv( +#' picks = list(my_picks = example_pick), +#' data = reactive(data) +#' ) +#' anl <- merge_srv("merge", data = reactive(data), selectors = selectors) +#' output$result <- renderPrint(cat(gsub("\033\\[[0-9;]*m", "", format(selectors$my_picks())))) +#' output$table <- renderTable(anl$data()$anl) +#' } +#' +#' if (interactive()) { +#' shinyApp(ui, server) +#' } NULL #' @rdname picks_module @@ -327,6 +356,7 @@ picks_srv.picks <- function(id, picks, data) { .pick_ui_fixed <- function(id, selected) { htmltools::tags$div( class = "form-group shiny-input-container", + style = "visibility: hidden; position: absolute;", htmltools::tags$input( id = id, disabled = "disabled", @@ -396,6 +426,7 @@ picks_srv.picks <- function(id, picks, data) { #' @param rv (`reactiveVal`) #' @param value (`vector`) #' @param log (`character(1)`) message to `log_debug` +#' @return the result of `reactiveVal` update if new value is different, `NULL` otherwise. #' @keywords internal .update_rv <- function(rv, value, log) { if (!isTRUE(all.equal(rv(), value, tolerance = 1e-15))) { # tolerance 1e-15 is a max precision in widgets. @@ -420,6 +451,8 @@ picks_srv.picks <- function(id, picks, data) { #' @param picks_resolved (`reactiveVal`) #' @param old_picks (`picks`) #' @param data (`any` asserted further in `resolver`) +#' @return The result of changing the `picks_resolved` `reactiveVal` given as argument. +#' It returns `NULL` if it does nothing. #' @keywords internal .resolve <- function(selected, slot_name, picks_resolved, old_picks, data) { checkmate::assert_vector(selected, null.ok = TRUE) @@ -479,7 +512,6 @@ picks_srv.picks <- function(id, picks, data) { #' Otherwise `default`. #' #' @keywords internal -#' restoreValue <- function(value, default) { # nolint: object_name. checkmate::assert_character("value") session_default <- shiny::getDefaultReactiveDomain() diff --git a/R/picks.R b/R/picks.R index c46574b..8e89416 100644 --- a/R/picks.R +++ b/R/picks.R @@ -20,6 +20,10 @@ #' @param check_dataset (`logical(1)`) whether to check that the first element of `picks` is `datasets()`. #' This is useful to set to `FALSE` when creating picks objects that have a required dataset that is not #' selected by the user and defined in the module itself. +#' @return For `picks()` it returns an object of `picks` class, which is a list of `pick` objects with additional +#' attributes for Shiny interactivity. +#' For `datasets()`, `variables()`, and `values()` it returns a `pick` object with +#' class corresponding to the type of selection including the choices and selected values. #' @details #' # `tidyselect` support #' @@ -377,6 +381,8 @@ values <- function(choices = function(x) !is.na(x), #' #' Create a `pick` object #' @inheritParams picks +#' @return `pick` generic object that is used by [datasets()], [variables()] and [values()] +#' to create objects of corresponding classes. #' @keywords internal .pick <- function(choices, selected, @@ -390,7 +396,7 @@ values <- function(choices = function(x) !is.na(x), warning( warningCondition( paste0( - deparse(sys.call(-1)), + deparse1(sys.call(-1)), "\n - Setting explicit `selected` while `choices` are delayed (set using `tidyselect`) doesn't ", "guarantee that `selected` is a subset of `choices`." ), @@ -470,6 +476,9 @@ values <- function(choices = function(x) !is.na(x), #' - `function` when predicate function provided (delayed) #' - `atomic` when vector of choices/selected provided (eager) #' @param x (`list`, `list of picks`, `picks`, `pick`, `$choices`, `$selected`) +#' @return A `logical(1)` indicating if any of the elements in picks is delayed., +#' For a single `pick`, such as [datasets()], [variables()] or [values()], +#' it checks if either `choices` or `selected` are delayed. #' @keywords internal .is_delayed <- function(x) { UseMethod(".is_delayed") diff --git a/R/tidyselect-helpers.R b/R/tidyselect-helpers.R index c199f06..d96b307 100644 --- a/R/tidyselect-helpers.R +++ b/R/tidyselect-helpers.R @@ -11,8 +11,12 @@ #' @rdname tidyselectors #' @param min.len (`integer(1)`) minimal number of unique values #' @param max.len (`integer(1)`) maximal number of unique values +#' @return A `tidyselector` that can be used directly in `choices` or `selected` of `variables()` in `picks()`. #' @export #' @examples +#' # Supports tidyselect helpers, e.g. to select all categorical variables with 2 to 10 unique values +#' dplyr::select(iris, dplyr::where(is_categorical(2, 10))) +#' #' p <- picks( #' datasets(is.data.frame, 2L), #' variables(is_categorical(2, 10)) @@ -51,6 +55,8 @@ is_categorical <- function(min.len, max.len) { #' columns. An informative error is raised if the resolved column type is unsupported. #' @param min (`numeric(1)`) Minimal value. #' @param max (`numeric(1)`) Maximal value. +#' @return A function that allows the use of a range in `choices` or `selected` of `values()` in +#' `numeric`, `Date`, or `POSIXct` variables. #' @export #' @examples #' p <- picks( diff --git a/R/tm_merge.R b/R/tm_merge.R index d8a54a8..83e2161 100644 --- a/R/tm_merge.R +++ b/R/tm_merge.R @@ -4,6 +4,7 @@ #' #' @inheritParams teal::module #' @param picks (`list` of `picks`) +#' @return A `teal::module` object that merges datasets based on user selections and displays the results. #' @examples #' library(teal) #' diff --git a/R/ui_containers.R b/R/ui_containers.R index cb17992..1fd42de 100644 --- a/R/ui_containers.R +++ b/R/ui_containers.R @@ -8,6 +8,7 @@ #' @param id (`character(1)`) shiny module's id #' @param label (`shiny.tag`) Label displayed on a badge. #' @param content (`shiny.tag`) Content of a drop-down. +#' @return A `shiny.tag` object representing a drop-down badge component to display the options. #' @keywords internal badge_dropdown <- function(id, label, content) { ns <- shiny::NS(id) @@ -48,11 +49,10 @@ badge_dropdown <- function(id, label, content) { ) } -#' Create ui component for fixed ui picks without user selection -#' @param id (`character(1)`) shiny module's id -#' @param label (`shiny.tag`) Label displayed on the component +#' Fixed badge +#' @inheritParams badge_dropdown +#' @return A `shiny.tag` object representing a fixed badge component to display the selected option. #' @keywords internal -#' @noRd badge_fixed <- function(id, label, content) { ns <- shiny::NS(id) diff --git a/README.md b/README.md index f323498..181e2ad 100644 --- a/README.md +++ b/README.md @@ -68,17 +68,3 @@ Wire `my_picks` into `picks_ui` / `picks_srv` with a reactive `teal_data` object ## Getting help If you encounter a bug or have a feature request, please file an issue. For questions, discussions, and staying up to date, please use the `teal` channel in the [`pharmaverse` slack workspace](https://pharmaverse.slack.com). - -## Stargazers and Forkers - -### Stargazers over time - -[![Stargazers over time](https://starchart.cc/insightsengineering/teal.picks.svg)](https://starchart.cc/insightsengineering/teal.picks) - -### Stargazers - -[![Stargazers repo roster for @insightsengineering/teal.picks](http://reporoster.com/stars/insightsengineering/teal.picks)](https://github.com/insightsengineering/teal.picks/stargazers) - -### Forkers - -[![Forkers repo roster for @insightsengineering/teal.picks](http://reporoster.com/forks/insightsengineering/teal.picks)](https://github.com/insightsengineering/teal.picks/network/members) diff --git a/inst/badge-dropdown/style.css b/inst/badge-dropdown/style.css index 703ae19..2c848b7 100644 --- a/inst/badge-dropdown/style.css +++ b/inst/badge-dropdown/style.css @@ -18,11 +18,12 @@ .badge-dropdown { overflow-x: auto; padding-right: 0.5rem; + border: 2px solid transparent; } .badge-dropdown:has(~ * .shiny-validation-message), .badge-dropdown:has(~ * .shiny-output-error) { - border: 2px solid red; + border: 2px solid var(--bs-danger); } .badge-dropdown-label, diff --git a/man/as.picks.Rd b/man/as.picks.Rd index 29b69b1..7bdb7bb 100644 --- a/man/as.picks.Rd +++ b/man/as.picks.Rd @@ -16,6 +16,9 @@ teal_transform_filter(x, label = "Filter") \item{label}{(\code{character(1)}) Label of the module.} } +\value{ +A \code{picks} object when conversion is possible, otherwise \code{NULL} with a warning (if \code{quiet = FALSE}). +} \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} Helper functions to ease transition between \code{\link[teal.transform:data_extract_spec]{teal.transform::data_extract_spec()}} and \code{\link[=picks]{picks()}}. diff --git a/man/badge_dropdown.Rd b/man/badge_dropdown.Rd index b920af8..d4287c2 100644 --- a/man/badge_dropdown.Rd +++ b/man/badge_dropdown.Rd @@ -13,6 +13,9 @@ badge_dropdown(id, label, content) \item{content}{(\code{shiny.tag}) Content of a drop-down.} } +\value{ +A \code{shiny.tag} object representing a drop-down badge component to display the options. +} \description{ Drop-down button in a form of a badge with \code{bg-primary} as default style Clicking badge shows a drop-down containing any \code{HTML} element. Folded drop-down diff --git a/man/badge_fixed.Rd b/man/badge_fixed.Rd new file mode 100644 index 0000000..66b575d --- /dev/null +++ b/man/badge_fixed.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ui_containers.R +\name{badge_fixed} +\alias{badge_fixed} +\title{Fixed badge} +\usage{ +badge_fixed(id, label, content) +} +\arguments{ +\item{id}{(\code{character(1)}) shiny module's id} + +\item{label}{(\code{shiny.tag}) Label displayed on a badge.} + +\item{content}{(\code{shiny.tag}) Content of a drop-down.} +} +\value{ +A \code{shiny.tag} object representing a fixed badge component to display the selected option. +} +\description{ +Fixed badge +} +\keyword{internal} diff --git a/man/dot-call_dplyr_filter.Rd b/man/dot-call_dplyr_filter.Rd index 8433461..2c2c908 100644 --- a/man/dot-call_dplyr_filter.Rd +++ b/man/dot-call_dplyr_filter.Rd @@ -9,6 +9,9 @@ \arguments{ \item{x}{(\code{list}) containing \code{variables} and \code{values}} } +\value{ +\code{call}. +} \description{ Create a \code{dplyr::filter} call } diff --git a/man/dot-call_dplyr_select.Rd b/man/dot-call_dplyr_select.Rd index fc94bb5..ec6627c 100644 --- a/man/dot-call_dplyr_select.Rd +++ b/man/dot-call_dplyr_select.Rd @@ -13,6 +13,9 @@ variables will be renamed if their name is different than its value (this produces a call \verb{select(..., = )}).} } +\value{ +\code{call}. +} \description{ Create \code{dplyr::select} call from \code{dataname} and \code{variables} } diff --git a/man/dot-is_delayed.Rd b/man/dot-is_delayed.Rd index 045f5d2..104f712 100644 --- a/man/dot-is_delayed.Rd +++ b/man/dot-is_delayed.Rd @@ -9,6 +9,11 @@ \arguments{ \item{x}{(\code{list}, \verb{list of picks}, \code{picks}, \code{pick}, \verb{$choices}, \verb{$selected})} } +\value{ +A \code{logical(1)} indicating if any of the elements in picks is delayed., +For a single \code{pick}, such as \code{\link[=datasets]{datasets()}}, \code{\link[=variables]{variables()}} or \code{\link[=values]{values()}}, +it checks if either \code{choices} or \code{selected} are delayed. +} \description{ Determine whether list of picks/picks or pick are delayed. When \code{"pick"} is created it could be either: diff --git a/man/dot-pick.Rd b/man/dot-pick.Rd index fcfc581..1471b88 100644 --- a/man/dot-pick.Rd +++ b/man/dot-pick.Rd @@ -32,6 +32,10 @@ and optionally \code{variables()} and \code{values()} for \code{variables(...)} and \code{values(...)}: additional arguments delivered to \code{pickerInput}} } +\value{ +\code{pick} generic object that is used by \code{\link[=datasets]{datasets()}}, \code{\link[=variables]{variables()}} and \code{\link[=values]{values()}} +to create objects of corresponding classes. +} \description{ Create a \code{pick} object } diff --git a/man/dot-resolve.Rd b/man/dot-resolve.Rd index 516cd37..7c8efc2 100644 --- a/man/dot-resolve.Rd +++ b/man/dot-resolve.Rd @@ -17,6 +17,10 @@ \item{data}{(\code{any} asserted further in \code{resolver})} } +\value{ +The result of changing the \code{picks_resolved} \code{reactiveVal} given as argument. +It returns \code{NULL} if it does nothing. +} \description{ @description When select input at position \code{i} changes: diff --git a/man/dot-update_rv.Rd b/man/dot-update_rv.Rd index 79c2b82..e0f6415 100644 --- a/man/dot-update_rv.Rd +++ b/man/dot-update_rv.Rd @@ -13,6 +13,9 @@ \item{log}{(\code{character(1)}) message to \code{log_debug}} } +\value{ +the result of \code{reactiveVal} update if new value is different, \code{NULL} otherwise. +} \description{ Update reactive values only if values differ to avoid unnecessary reactive trigger } diff --git a/man/dot-validate_join_keys.Rd b/man/dot-validate_join_keys.Rd index ccaec5f..9cb24d2 100644 --- a/man/dot-validate_join_keys.Rd +++ b/man/dot-validate_join_keys.Rd @@ -16,6 +16,10 @@ The names of this list are used as identifiers for tracking which variables come \item{join_keys}{(\code{join_keys}) The join keys object} } +\value{ +\code{TRUE} if validation passes, otherwise raises a validation +error with details on which datasets cannot be merged and why. +} \description{ Determines the topological order from join_keys, then checks that each dataset can be joined with at least one of the previously accumulated datasets. diff --git a/man/interaction_vars.Rd b/man/interaction_vars.Rd index 5d48aed..b67bec5 100644 --- a/man/interaction_vars.Rd +++ b/man/interaction_vars.Rd @@ -28,3 +28,14 @@ interact with each other. The pair is recorded in the selection environment and the positions of both variables within the available variables are returned. } +\examples{ +picks( + datasets("ADAE"), + variables( + c(AGE, RACE, interaction_vars("COUNTRY", "RACE")), + selected = "COUNTRY:RACE", + multiple = TRUE + ), + values() +) +} diff --git a/man/picks.Rd b/man/picks.Rd index 23d03af..98b84ee 100644 --- a/man/picks.Rd +++ b/man/picks.Rd @@ -51,6 +51,12 @@ Choices to be selected.} \item{ordered}{(\code{logical(1)}) if the selected should follow the selection order. If \code{FALSE} \code{selected} returned from \code{srv_module_input()} would be ordered according to order in \code{choices}.} } +\value{ +For \code{picks()} it returns an object of \code{picks} class, which is a list of \code{pick} objects with additional +attributes for Shiny interactivity. +For \code{datasets()}, \code{variables()}, and \code{values()} it returns a \code{pick} object with +class corresponding to the type of selection including the choices and selected values. +} \description{ Define choices and default selection for variables. \code{picks} allows app-developer to specify \code{datasets}, \code{variables} and \code{values} to be selected by app-user during Shiny session. diff --git a/man/picks_module.Rd b/man/picks_module.Rd index bde6ecd..f298065 100644 --- a/man/picks_module.Rd +++ b/man/picks_module.Rd @@ -59,6 +59,36 @@ The module uses S3 method dispatch to handle different ways to provide \code{pic The UI component (\code{picks_ui}) creates the visual elements, while the server component (\code{picks_srv}) manages the reactive logic, } +\examples{ +library(shiny) + +example_pick <- picks( + datasets("ADSL"), + variables(selected = c("SEX", "COUNTRY", "ARMCD")) +) +ui <- fluidPage( + picks_ui("my_picks", picks = example_pick), + h4("Resolved picks:"), + verbatimTextOutput("result"), + h4("Table:"), + tableOutput("table") +) +server <- function(input, output, session) { + data <- teal.data::teal_data("ADSL" = teal.data::rADSL) + teal.data::join_keys(data) <- teal.data::default_cdisc_join_keys["ADSL"] + selectors <- picks_srv( + picks = list(my_picks = example_pick), + data = reactive(data) + ) + anl <- merge_srv("merge", data = reactive(data), selectors = selectors) + output$result <- renderPrint(cat(gsub("\033\\\\[[0-9;]*m", "", format(selectors$my_picks())))) + output$table <- renderTable(anl$data()$anl) +} + +if (interactive()) { + shinyApp(ui, server) +} +} \seealso{ \code{\link[=picks]{picks()}} for creating `picks`` objects } diff --git a/man/ranged.Rd b/man/ranged.Rd index eae1d0c..1d74eee 100644 --- a/man/ranged.Rd +++ b/man/ranged.Rd @@ -11,6 +11,10 @@ ranged(min = -Inf, max = Inf) \item{max}{(\code{numeric(1)}) Maximal value.} } +\value{ +A function that allows the use of a range in \code{choices} or \code{selected} of \code{values()} in +\code{numeric}, \code{Date}, or \code{POSIXct} variables. +} \description{ Helper to work with ranges. Setting \code{choices} or \code{selected} to range using \code{ranged()} in any of them will automatically create a \code{numeric}, \code{Date} or \code{POSIXct} diff --git a/man/tidyselectors.Rd b/man/tidyselectors.Rd index 8495b60..41f9c22 100644 --- a/man/tidyselectors.Rd +++ b/man/tidyselectors.Rd @@ -12,6 +12,9 @@ is_categorical(min.len, max.len) \item{max.len}{(\code{integer(1)}) maximal number of unique values} } +\value{ +A \code{tidyselector} that can be used directly in \code{choices} or \code{selected} of \code{variables()} in \code{picks()}. +} \description{ #' \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} Predicate functions simplifying \code{picks} specification. @@ -20,6 +23,9 @@ Predicate functions simplifying \code{picks} specification. # select factor column but exclude foreign keys variables(choices = is_categorical(min.len = 2, max.len = 10)) +# Supports tidyselect helpers, e.g. to select all categorical variables with 2 to 10 unique values +dplyr::select(iris, dplyr::where(is_categorical(2, 10))) + p <- picks( datasets(is.data.frame, 2L), variables(is_categorical(2, 10)) diff --git a/man/tm_merge.Rd b/man/tm_merge.Rd index 3dcbf92..1603a9a 100644 --- a/man/tm_merge.Rd +++ b/man/tm_merge.Rd @@ -15,6 +15,9 @@ For \code{modules()} defaults to \code{"root"}. See \code{Details}.} \item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. To learn more check \code{vignette("transform-input-data", package = "teal")}.} } +\value{ +A \code{teal::module} object that merges datasets based on user selections and displays the results. +} \description{ Example \code{\link[teal:module]{teal::module}} containing interactive inputs and displaying results of merge. }