-
-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
shinyvalidate
improvements
#786
Merged
Merged
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
bad88da
add gather_fails functions
851f9db
amend documentation
4320c68
update NEWS
55cc6e7
add dependency
444af9a
add unit tests
3b50b9e
Merge 444af9a19dda0084a8df544bedc0c7a209849f37 into e35e91a10c787bf99…
chlebowa 4e92db3
[skip actions] Restyle files
github-actions[bot] 30bdddb
update pkgdown
183fca6
fix links in docs
2d7d43a
lintr
5ea79ce
spellcheck
0d93eec
move shinyvalidate to Suggests
794777f
reduce and rename functions
8657c58
Merge 794777ff14c36ab91f1496a08c1c098b014a7d4b into e35e91a10c787bf99…
chlebowa 2ce23b9
[skip actions] Restyle files
github-actions[bot] b96e7cb
trigger
f5a87ff
trigger
7ffefe7
fix example
8d8c3ac
fix pkgdown
bf0b063
update documentation
1048640
update NEWS
b899785
fix links in docs
e469ba1
final review
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
|
||
#' send input validation messages to output | ||
#' | ||
#' Captures messages from `InputValidator` objects and collates them | ||
#' into one message passed to `validate`. | ||
#' | ||
#' `shiny::validate` is used to withhold rendering of an output element until | ||
#' certain conditions are met and a print a validation message in place | ||
#' of the output element. | ||
#' `shinyvalidate::InputValidator` allows to validate input elements | ||
#' and display specific messages in their respective input widgets. | ||
#' This function is a hybrid solution. Given an `InputValidator` object, | ||
#' it extracts messages from inputs that fail validation and places them all in one | ||
#' validation message that is passed to a `validate`/`need` call. | ||
#' This way the input validator messages are repeated in the output. | ||
#' | ||
#' \code{gather_fails} accepts one `InputValidator` | ||
#' and can add a header to its validation messages. | ||
#' \code{gather_fails_com} accepts an arbitrary number of `InputValidator`s | ||
#' and prints all messages together under one header. | ||
#' \code{gather_fails_grp} accepts a \strong{list} of `InputValidator`s | ||
#' and prints messages in groups. If elements of \code{validators} are named, | ||
#' the names are used as headers for their respective message groups. | ||
#' | ||
#' | ||
#' @name gather_fails | ||
#' | ||
#' @param iv object of class `InputValidator` | ||
#' @param header `character(1)` optional generic validation message | ||
#' @param ... for \code{gather_fails} and \code{gather_fails_grp} arguments passed to `shiny::validate`\cr | ||
#' for \code{gather_fails_com} any number of `InputValidator` objects | ||
#' @param validators optionally named `list` of `InputValidator` objects, see \code{Details} | ||
#' | ||
#' @return | ||
#' Returns NULL if the final validation call passes and a `shiny.silent.error` if it fails. | ||
#' | ||
#' @seealso \code{[shinyvalidate::InputValidator]} \code{[shiny::validate]} | ||
#' | ||
#' @examples | ||
#' library(shiny) | ||
#' library(shinyvalidate) | ||
#' | ||
#' ui <- fluidPage( | ||
#' selectInput("method", "validation method", c("hierarchical", "combined", "grouped")), | ||
#' sidebarLayout( | ||
#' sidebarPanel( | ||
#' selectInput("letter", "select a letter:", c(letters[1:3], LETTERS[4:6])), | ||
#' selectInput("number", "select a number:", 1:6), | ||
#' br(), | ||
#' selectInput("color", "select a color:", | ||
#' c("black", "indianred2", "springgreen2", "cornflowerblue"), | ||
#' multiple = TRUE | ||
#' ), | ||
#' sliderInput("size", "select point size:", | ||
#' min = 0.1, max = 4, value = 0.25 | ||
#' ) | ||
#' ), | ||
#' mainPanel(plotOutput("plot")) | ||
#' ) | ||
#' ) | ||
#' | ||
#' server <- function(input, output) { | ||
#' # set up input validation | ||
#' iv <- InputValidator$new() | ||
#' iv$add_rule("letter", sv_in_set(LETTERS, "choose a capital letter")) | ||
#' iv$add_rule("number", ~ if (as.integer(.) %% 2L == 1L) "choose an even number") | ||
#' iv$enable() | ||
#' # more input validation | ||
#' iv_par <- InputValidator$new() | ||
#' iv_par$add_rule("color", sv_required(message = "choose a color")) | ||
#' iv_par$add_rule("color", ~ if (length(.) > 1L) "choose only one color") | ||
#' iv_par$add_rule( | ||
#' "size", | ||
#' sv_between( | ||
#' left = 0.5, right = 3, | ||
#' message_fmt = "choose a value between {left} and {right}" | ||
#' ) | ||
#' ) | ||
#' iv_par$enable() | ||
#' | ||
#' output$plot <- renderPlot({ | ||
#' # validate output | ||
#' switch(input[["method"]], | ||
#' "hierarchical" = { | ||
#' gather_fails(iv) | ||
#' gather_fails(iv_par, "Set proper graphical parameters") | ||
#' }, | ||
#' "combined" = gather_fails_com(iv, iv_par), | ||
#' "grouped" = gather_fails_grp(list( | ||
#' "Some inputs require attention" = iv, | ||
#' "Set proper graphical parameters" = iv_par | ||
#' )) | ||
#' ) | ||
#' | ||
#' plot(eruptions ~ waiting, faithful, | ||
#' las = 1, pch = 16, | ||
#' col = input[["color"]], cex = input[["size"]] | ||
#' ) | ||
#' }) | ||
#' } | ||
#' | ||
#' if (interactive()) { | ||
#' shinyApp(ui, server) | ||
#' } | ||
|
||
#' @rdname gather_fails | ||
#' @export | ||
gather_fails <- function(iv, header = "Some inputs require attention", ...) { | ||
checkmate::assert_class(iv, "InputValidator") | ||
checkmate::assert_string(header, null.ok = TRUE) | ||
|
||
fail_messages <- gather_messages(iv) | ||
failings <- add_header(fail_messages, header) | ||
|
||
shiny::validate(shiny::need(is.null(failings), failings), ...) | ||
} | ||
|
||
|
||
#' @rdname gather_fails | ||
#' @export | ||
gather_fails_com <- function(..., header = "Some inputs require attention") { | ||
vals <- list(...) | ||
lapply(vals, checkmate::assert_class, "InputValidator") | ||
checkmate::assert_string(header, null.ok = TRUE) | ||
|
||
fail_messages <- unlist(lapply(vals, gather_messages)) | ||
failings <- add_header(fail_messages, header) | ||
|
||
shiny::validate(shiny::need(is.null(failings), failings)) | ||
} | ||
|
||
|
||
#' @rdname gather_fails | ||
#' @export | ||
gather_fails_grp <- function(validators, ...) { | ||
checkmate::assert_list(validators, types = "InputValidator") | ||
|
||
# Since some or all names may be NULL, mapply cannot be used here, a loop is required. | ||
fail_messages <- vector("list", length(validators)) | ||
for (v in seq_along(validators)) { | ||
fail_messages[[v]] <- gather_and_add(validators[[v]], names(validators)[v]) | ||
} | ||
|
||
failings <- unlist(fail_messages) | ||
|
||
shiny::validate(shiny::need(is.null(failings), failings), ...) | ||
} | ||
|
||
|
||
### internal functions | ||
|
||
#' @keywords internal | ||
# internal used by all methods | ||
# collate failing messages from validator | ||
gather_messages <- function(iv) { | ||
status <- iv$validate() | ||
failing_inputs <- Filter(Negate(is.null), status) | ||
unique(lapply(failing_inputs, function(x) x[["message"]])) | ||
} | ||
|
||
|
||
#' @keywords internal | ||
# internal used by all hierarchical and combined methods | ||
# format failing messages with optional header message | ||
add_header <- function(messages, header) { | ||
if (length(messages) > 0L) { | ||
c(paste0(header, "\n"), unlist(messages), "\n") | ||
} else { | ||
NULL | ||
} | ||
} | ||
|
||
#' @keywords internal | ||
# collate failing messages with optional header message | ||
# internal used by grouped method | ||
gather_and_add <- function(iv, header) { | ||
fail_messages <- gather_messages(iv) | ||
failings <- add_header(fail_messages, header) | ||
failings | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,3 +37,4 @@ ui | |
repo | ||
Forkers | ||
README | ||
validator |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As those functions use keywords internal they don't appear in pkgdown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only at the bottom of the file use
@keywords internal
, pkgdown breaks if the main ones are not added here (but I did forget to change the name here).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup though the line above starts_with("validate_") should cover them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I hear they are to deprecated ("are we still using those?") so maybe it makes sense to add an extra line for these two?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are used quite a bit downstream so I doubt they will be going.. but happy for you to add these extra two explicitly - as long as pkgdown doesn't then output them twice :)