diff --git a/R/input-checkboxgroup.R b/R/input-checkboxgroup.R index 611c1ea1a..2122b039a 100644 --- a/R/input-checkboxgroup.R +++ b/R/input-checkboxgroup.R @@ -75,9 +75,7 @@ checkboxGroupInput <- function(inputId, label, choices = NULL, selected = NULL, selected <- restoreInput(id = inputId, default = selected) # default value if it's not specified - if (!is.null(selected)) - selected <- normalizeSelected(selected, inputId, - args$choiceNames, args$choiceValues) + if (!is.null(selected)) selected <- as.character(selected) options <- generateOptions(inputId, selected, inline, 'checkbox', args$choiceNames, args$choiceValues) diff --git a/R/input-radiobuttons.R b/R/input-radiobuttons.R index 02a03c0d1..a9279b704 100644 --- a/R/input-radiobuttons.R +++ b/R/input-radiobuttons.R @@ -89,14 +89,12 @@ radioButtons <- function(inputId, label, choices = NULL, selected = NULL, selected <- restoreInput(id = inputId, default = selected) # default value if it's not specified - selected <- if (is.null(selected)) args$choiceValues[[1]] else { - normalizeSelected(selected, inputId, args$choiceNames, args$choiceValues) - } + selected <- if (is.null(selected)) args$choiceValues[[1]] else as.character(selected) if (length(selected) > 1) stop("The 'selected' argument must be of length 1") - options <- generateOptions(inputId, selected, inline, 'radio', - args$choiceNames, args$choiceValues) + options <- generateOptions(inputId, selected, inline, + 'radio', args$choiceNames, args$choiceValues) divClass <- "form-group shiny-input-radiogroup shiny-input-container" if (inline) divClass <- paste(divClass, "shiny-input-container-inline") diff --git a/R/input-select.R b/R/input-select.R index d6554091d..e87ab40fc 100644 --- a/R/input-select.R +++ b/R/input-select.R @@ -85,7 +85,7 @@ selectInput <- function(inputId, label, choices, selected = NULL, # default value if it's not specified if (is.null(selected)) { if (!multiple) selected <- firstChoice(choices) - } else selected <- normalizeSelected(selected, inputId, names(choices), unname(choices)) + } else selected <- as.character(selected) if (!is.null(size) && selectize) { stop("'size' argument is incompatible with 'selectize=TRUE'.") diff --git a/R/input-utils.R b/R/input-utils.R index 03728994e..aecc23402 100644 --- a/R/input-utils.R +++ b/R/input-utils.R @@ -14,9 +14,10 @@ normalizeChoicesArgs <- function(choices, choiceNames, choiceValues) { # if-else to check that either choices OR (choiceNames + choiceValues) # were correctly provided if (is.null(choices)) { - if (length(choiceNames) == 0 || length(choiceValues) == 0) { - stop("Please specify a non-empty vector for `choices` (or, - alternatively, for both `choiceNames` and `choiceValues`).") + if (is.null(choiceNames) || is.null(choiceValues)) { + return(list(choiceNames = NULL, choiceValues = NULL)) + # stop("Please specify a non-empty vector for `choices` (or, + # alternatively, for both `choiceNames` and `choiceValues`).") } if (length(choiceNames) != length(choiceValues)) { stop("`choiceNames` and `choiceValues` must have the same length.") @@ -33,37 +34,8 @@ normalizeChoicesArgs <- function(choices, choiceNames, choiceValues) { choiceValues <- unname(choices) } - return(list(choiceNames = choiceNames, choiceValues = choiceValues)) -} - -# Before shiny 0.9, `selected` refers to names/labels of `choices`; now it -# refers to values. Below is a function for backward compatibility. It also -# coerces the value to `character`. -normalizeSelected <- function(selected, inputId, choiceNames, choiceValues) { - # this line accomplishes two tings: - # - coerces selected to character - # - drops name, otherwise toJSON() keeps it too - selected <- as.character(selected) - - # if you are using optgroups, you're using shiny > 0.10.0, and you should - # already know that `selected` must be a value instead of a label - if (needOptgroup(choiceValues)) return(selected) - - if (is.list(choiceNames)) choiceNames <- unlist(as.character(choiceNames)) - if (is.list(choiceValues)) choiceValues <- unlist(choiceValues) - - # when selected labels instead of values - i <- (selected %in% choiceNames) & !(selected %in% choiceValues) - if (any(i)) { - warnFun <- if (all(i)) { - # replace names with values - selected <- choiceValues[[which(choiceNames == selected)]] - warning - } else stop # stop when it is ambiguous (some labels == values) - warnFun("'selected' must be the values instead of names of 'choices' ", - "for the input '", inputId, "'") - } - selected + return(list(choiceNames = as.list(choiceNames), + choiceValues = as.list(choiceValues))) } # generate options for radio buttons and checkbox groups (type = 'checkbox' or diff --git a/R/update-input.R b/R/update-input.R index f5ab062a8..9db8c4ea3 100644 --- a/R/update-input.R +++ b/R/update-input.R @@ -454,11 +454,11 @@ updateSliderInput <- function(session, inputId, label = NULL, value = NULL, updateInputOptions <- function(session, inputId, label = NULL, choices = NULL, selected = NULL, inline = FALSE, type = NULL, choiceNames = NULL, choiceValues = NULL) { + if (is.null(type)) stop("Please specify the type ('checkbox' or 'radio')") + args <- normalizeChoicesArgs(choices, choiceNames, choiceValues) - if (!is.null(selected)) - selected <- normalizeSelected(selected, session$ns(inputId), - args$choiceNames, args$choiceValues) + if (!is.null(selected)) selected <- as.character(selected) options <- if (!is.null(args$choiceValues)) { format(tagList( @@ -558,7 +558,10 @@ updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL, selected = NULL, inline = FALSE, choiceNames = NULL, choiceValues = NULL) { # you must select at least one radio button - if (is.null(selected) && !is.null(choices)) selected <- choices[[1]] + if (is.null(selected)) { + if (!is.null(choices)) selected <- choices[[1]] + else if (!is.null(choiceValues)) selected <- choiceValues[[1]] + } updateInputOptions(session, inputId, label, choices, selected, inline, 'radio', choiceNames, choiceValues) } @@ -606,8 +609,7 @@ updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL, updateSelectInput <- function(session, inputId, label = NULL, choices = NULL, selected = NULL) { choices <- if (!is.null(choices)) choicesWithNames(choices) - if (!is.null(selected)) - selected <- normalizeSelected(selected, inputId, names(choices), unname(choices)) + if (!is.null(selected)) selected <- as.character(selected) options <- if (!is.null(choices)) selectOptions(choices, selected) message <- dropNulls(list(label = label, options = options, value = selected)) session$sendInputMessage(inputId, message) diff --git a/tests/testthat/test-bootstrap.r b/tests/testthat/test-bootstrap.r index d8b490078..db8a93239 100644 --- a/tests/testthat/test-bootstrap.r +++ b/tests/testthat/test-bootstrap.r @@ -192,21 +192,18 @@ test_that("selectInput selects items by default", { test_that("normalizeChoicesArgs does its job", { # Unnamed vectors and lists - expected <- list(choiceNames = c("a", "b"), choiceValues = list("a", "b")) + expected <- list(choiceNames = list("a", "b"), choiceValues = list("a", "b")) expect_equal(normalizeChoicesArgs(c("a", "b"), NULL, NULL), expected) expect_equal(normalizeChoicesArgs(list("a", "b"), NULL, NULL), expected) # Named list - expected <- list(choiceNames = c("one", "two"), choiceValues = list("a", "b")) + expected <- list(choiceNames = list("one", "two"), choiceValues = list("a", "b")) x <- list(one = "a", two = "b") expect_equal(normalizeChoicesArgs(x, NULL, NULL), expected) expect_equal(normalizeChoicesArgs(NULL, names(x), unname(x)), expected) # Using unnamed `choiceNames` and `choiceValues` vectors/lists directly - expected <- list(choiceNames = c("one", "two"), choiceValues = c("a", "b")) expect_equal(normalizeChoicesArgs(NULL, c("one", "two"), c("a", "b")), expected) - - expected <- list(choiceNames = list("one", "two"), choiceValues = list("a", "b")) expect_equal(normalizeChoicesArgs(NULL, list("one", "two"), list("a", "b")), expected) # Using choiceNames with HTML and choiceValues @@ -219,5 +216,21 @@ test_that("normalizeChoicesArgs does its job", { x <- list("a", "b") expect_warning(res <- normalizeChoicesArgs(x, nms, vals), "Using `choices` argument; ignoring `choiceNames` and `choiceValues`.") - expect_equal(res, list(choiceNames = c("a", "b"), choiceValues = list("a", "b"))) + expect_equal(res, list(choiceNames = list("a", "b"), choiceValues = list("a", "b"))) + + # Set possibilities to character(0) + expected <- list(choiceNames = list(), choiceValues = list()) + expect_equal(normalizeChoicesArgs(character(0), NULL, NULL), expected) + expect_equal(normalizeChoicesArgs(NULL, character(0), character(0)), expected) + expect_warning(res <- normalizeChoicesArgs(character(0), character(0), character(0)), + "Using `choices` argument; ignoring `choiceNames` and `choiceValues`.") + expect_equal(res, expected) + + # Set possibilities to character(0) in an inconsistent way + expected <- list(choiceNames = NULL, choiceValues = NULL) + expect_equal(normalizeChoicesArgs(NULL, character(0), NULL), expected) + expect_equal(normalizeChoicesArgs(NULL, NULL, character(0)), expected) + + # Set all possibilities to NULL + expect_equal(normalizeChoicesArgs(NULL, NULL, NULL), expected) })