Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9d88f72
init POC MDR
gogonzo Sep 11, 2025
bf04a84
fix for front_page or other modules with datanames = NULL
Sep 15, 2025
cd94b1e
Merge branch 'main' into redesign_extraction@main
gogonzo Sep 18, 2025
8cf261a
badge dropdown styling - same size, scrollable
gogonzo Sep 23, 2025
3339e53
dropdown active for shiny
gogonzo Sep 23, 2025
1feb3eb
POC 1
gogonzo Sep 24, 2025
117309f
cleanup badge_dropdown
gogonzo Sep 24, 2025
598bb09
fix badge-dropdown to have red border when validation error appears
gogonzo Sep 25, 2025
8596df6
- multiple variables concatenates values
gogonzo Oct 7, 2025
e0c08c2
Merge branch 'main' into redesign_extraction@main
gogonzo Oct 9, 2025
1fbc31e
Merge branch 'main' into redesign_extraction@main
gogonzo Oct 20, 2025
827e43b
remove badge_dropdown from teal and make private in teal.transform
gogonzo Oct 21, 2025
f5348a0
spec -> picks
gogonzo Oct 24, 2025
cfcca0c
Merge branch 'main' into redesign_extraction@main
gogonzo Oct 29, 2025
c162d7b
revert unnecessary
gogonzo Oct 29, 2025
1bed0b1
fix lintr
gogonzo Oct 30, 2025
123e457
placeholders for tests
gogonzo Nov 3, 2025
b9c29b0
Merge branch 'main' into redesign_extraction@main
gogonzo Nov 3, 2025
021535b
Merge branch 'main' into redesign_extraction@main
gogonzo Jan 20, 2026
4ee12ce
Merge branch 'main' into redesign_extraction@main
m7pr Apr 2, 2026
4d868cf
revert changes after merge
m7pr Apr 2, 2026
cefc62c
bring back removed js-code
gogonzo Apr 14, 2026
15101b5
feat: adds missing shinytest2 tests
averissimo May 4, 2026
192c070
[skip style] [skip vbump] Restyle files
github-actions[bot] May 4, 2026
1950009
fix: last test under input spec
averissimo May 4, 2026
a505fd4
chore: fix long line
averissimo May 4, 2026
a4ba5d0
chore: newline at the end of the file
averissimo May 4, 2026
b3a0af1
chore: uses withr to defer app stop
averissimo May 4, 2026
406f79c
chore: minor correction on existing test
averissimo May 4, 2026
35d3abe
fix: long line
averissimo May 4, 2026
cf4d5d8
[skip roxygen] [skip vbump] Roxygen Man Pages Auto Update
github-actions[bot] May 4, 2026
de9525e
empty: trigger ci
averissimo May 4, 2026
490c207
fix: solves initialization problem
averissimo May 11, 2026
e97422f
fix: remove test in favor of implementation on other branch
averissimo May 14, 2026
c6e6ee1
restore
averissimo May 14, 2026
7314f79
Improve on `validate_input` and exports it (#1713)
averissimo May 18, 2026
d54a051
[skip roxygen] [skip vbump] Roxygen Man Pages Auto Update
github-actions[bot] May 18, 2026
5d26523
lintr and roxy
gogonzo May 18, 2026
dc18315
Merge remote-tracking branch 'origin/redesign_extraction@main' into r…
gogonzo May 18, 2026
7f29d21
snapshot manager disabled
gogonzo May 19, 2026
830613b
fix tests
gogonzo May 19, 2026
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 @@ -97,7 +97,6 @@ Encoding: UTF-8
Language: en-US
LazyData: true
Roxygen: list(markdown = TRUE, packages = c("roxy.shinylive"))
RoxygenNote: 7.3.3
Collate:
'TealAppDriver.R'
'after.R'
Expand Down Expand Up @@ -134,3 +133,4 @@ Collate:
'validate_inputs.R'
'validations.R'
'zzz.R'
Config/roxygen2/version: 8.0.0
1 change: 1 addition & 0 deletions R/module_teal.R
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ ui_teal <- function(id, modules) {
include_teal_css_js(),
shinyjs::useShinyjs(),
shiny::includeScript(system.file("js/extendShinyJs.js", package = "teal")),
shiny::singleton(shiny::includeScript(system.file("js/input-validator.js", package = "teal"))),
shiny_busy_message_panel,
tags$div(id = ns("tabpanel_wrapper"), class = "teal-body", navbar),
tags$hr(style = "margin: 1rem 0 0.5rem 0;")
Expand Down
42 changes: 42 additions & 0 deletions R/validate_inputs.R
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,45 @@ any_names <- function(x) {
}
)
}

#' Validate input
#'
#' @param inputId (`character`) Character of input ID(s) to validate
#' @param condition (`logical(1)`, `function(x)`) Logical value or function returning logical value.
#' Condition should determine expected state, `FALSE` throws.
#' @param message (`character(1)`) Character string of validation message to display
#' @param session Shiny session object
#'
#' @return `NULL` or `shiny.silent.error` when condition is not met
#'
#' @keywords internal
validate_input <- function(inputId, # nolint
condition = function(x) TRUE,
message = "",
session = shiny::getDefaultReactiveDomain()) {
checkmate::assert_character(inputId, min.len = 1)
checkmate::assert(
checkmate::check_flag(condition),
checkmate::check_function(condition, nargs = length(inputId))
)
checkmate::assert_string(message)

# Evaluate condition if it's a function
condition_result <- if (is.function(condition)) {
input_value <- lapply(inputId, function(id) session$input[[id]])
checkmate::assert_flag(do.call(condition, input_value))
} else {
condition
}

# Send custom message to JavaScript handler
lapply(inputId, function(id) {
session$sendCustomMessage("validateInput", list(
inputId = session$ns(id),
isValid = condition_result,
message = message
))
})

validate(need(condition_result, message))
}
4 changes: 2 additions & 2 deletions inst/css/validation.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
padding: 0.5em 0 0.5em 0.5em;
}

.teal_primary_col > .teal_validated:has(.teal-output-warning),
.teal_primary_col > .teal_validated:has(.shiny-output-error) {
.teal_primary_col>.teal_validated:has(.teal-output-warning),
.teal_primary_col>.teal_validated:has(.shiny-output-error) {
width: 100%;
background-color: rgba(223, 70, 97, 0.1);
border: 1px solid red;
Expand Down
9 changes: 4 additions & 5 deletions inst/js/extendShinyJs.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// This file contains functions that should be executed at the start of each session,
// not included in the original HTML

shinyjs.autoFocusModal = function(id) {
shinyjs.autoFocusModal = function (id) {
document.getElementById('shiny-modal').addEventListener(
'shown.bs.modal',
() => document.getElementById(id).focus(),
{ once: true }
);
}

shinyjs.enterToSubmit = function(id, submit_id) {
shinyjs.enterToSubmit = function (id, submit_id) {
document.getElementById('shiny-modal').addEventListener(
'shown.bs.modal',
() => document.getElementById(id).addEventListener('keyup', (e) => {
Expand All @@ -22,10 +22,9 @@ shinyjs.enterToSubmit = function(id, submit_id) {
}

// Custom message handler to get document title and send it to the server
Shiny.addCustomMessageHandler('teal-get-document-title', function(message) {
Shiny.addCustomMessageHandler('teal-get-document-title', function (message) {
const title = document.title;
const inputId = message.inputId;
console.log('Teal: Sending document title:', title, 'to input:', inputId);
Shiny.setInputValue(inputId, title, {priority: 'event'});
Shiny.setInputValue(inputId, title, { priority: 'event' });
});

29 changes: 29 additions & 0 deletions inst/js/input-validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
$(document).on('shiny:connected', function () {
Shiny.addCustomMessageHandler('validateInput', function (data) {
var inputId = data.inputId;
var isValid = data.isValid;
var message = data.message;

// Try both CSS selector patterns
var selector1 = '.shiny-input-container#' + inputId;
var selector2 = '.shiny-input-container:has(#' + inputId + ')';

var container = $(selector1);
if (container.length === 0) {
container = $(selector2);
}

if (container.length > 0) {
// Remove existing validation message
container.find('.shiny-output-error').remove();

// Add validation message if rule failed
if (!isValid && message && message.trim() !== '') {
var validationSpan = $('<span>').addClass('shiny-output-error').text(message);
container.append(validationSpan);
}
} else {
console.warn('Container not found for input: ' + inputId);
}
});
});
Loading
Loading