From 1bbad173d6ffa811a43f906a33865181669d6593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:22:44 +0000 Subject: [PATCH 01/11] fix: warning on choices is determined after pretty range --- R/FilterStateRange.R | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/R/FilterStateRange.R b/R/FilterStateRange.R index 69eeaec07..6abdac787 100644 --- a/R/FilterStateRange.R +++ b/R/FilterStateRange.R @@ -282,43 +282,45 @@ RangeFilterState <- R6::R6Class( # nolint set_choices = function(choices) { x <- private$x[is.finite(private$x)] - if (is.null(choices)) { - choices <- range(x) + new_choices <- if (is.null(choices)) { + range(x) } else { choices_adjusted <- c(max(choices[1L], min(x)), min(choices[2L], max(x))) - if (any(choices != choices_adjusted)) { - warning(sprintf( - "Choices adjusted (some values outside of variable range). Varname: %s, dataname: %s.", - private$get_varname(), private$get_dataname() - )) - choices <- choices_adjusted - } - if (choices[1L] > choices[2L]) { + if (choices_adjusted[1L] > choices_adjusted[2L]) { warning(sprintf( "Invalid choices: lower is higher / equal to upper, or not in range of variable values. Setting defaults. Varname: %s, dataname: %s.", private$get_varname(), private$get_dataname() )) - choices <- range(x) + choices_adjusted <- range(x) } + choices_adjusted } - private$set_is_choice_limited(private$x, choices) + private$set_is_choice_limited(private$x, new_choices) private$x <- private$x[ - (private$x >= choices[1L] & private$x <= choices[2L]) | is.na(private$x) | !is.finite(private$x) + (private$x >= new_choices[1L] & private$x <= new_choices[2L]) | is.na(private$x) | !is.finite(private$x) ] x_range <- range(private$x, finite = TRUE) # Required for displaying ticks on the slider, can modify choices! if (identical(diff(x_range), 0)) { - choices <- x_range + new_choices <- x_range } else { x_pretty <- pretty(x_range, 100L) - choices <- range(x_pretty) + new_choices <- range(x_pretty) private$numeric_step <- signif(private$get_pretty_range_step(x_pretty), digits = 10) } - private$teal_slice$choices <- choices + private$teal_slice$choices <- new_choices + # Only throw warning if pretty choices are different + if (!is.null(choices) && !identical(choices, new_choices)) { + warning(sprintf( + "Choices adjusted (some values outside of variable range). Varname: %s, dataname: %s.", + private$get_varname(), private$get_dataname() + )) + } + invisible(NULL) }, From f8d1ddbdc821115a609091291ce692662cf7f1cf Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:34:35 +0000 Subject: [PATCH 02/11] [skip style] [skip vbump] Restyle files --- R/FilterStateRange.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/FilterStateRange.R b/R/FilterStateRange.R index 6abdac787..8e9970497 100644 --- a/R/FilterStateRange.R +++ b/R/FilterStateRange.R @@ -129,7 +129,6 @@ RangeFilterState <- R6::R6Class( # nolint # public methods ---- public = list( - #' @description #' Initialize a `FilterState` object for range selection. #' @param x (`numeric`) From b5d7fd2bae0480cfe7112b211cdab3aede9a54f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:35:48 +0000 Subject: [PATCH 03/11] docs: adds news item --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 52514d0a4..3c248b997 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,7 @@ ### Miscellaneous * Improve unit test coverage (#666). +* Re-setting choices for slice only shows warning of modified choices if the post-processed range is different (#676). # teal.slice 0.7.0 From ec1fa4dff571107fdd66b9e6c6c70e25004dfd6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:59:26 +0000 Subject: [PATCH 04/11] pr: @llrs-roche suggestion --- R/FilterStateRange.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/R/FilterStateRange.R b/R/FilterStateRange.R index 8e9970497..4d8f67f26 100644 --- a/R/FilterStateRange.R +++ b/R/FilterStateRange.R @@ -297,10 +297,8 @@ RangeFilterState <- R6::R6Class( # nolint } private$set_is_choice_limited(private$x, new_choices) - private$x <- private$x[ - (private$x >= new_choices[1L] & private$x <= new_choices[2L]) | is.na(private$x) | !is.finite(private$x) - ] - + valid_range_index <- (private$x >= new_choices[1L] & private$x <= new_choices[2L]) + private$x <- private$x[valid_range_index | is.na(private$x) | !is.finite(private$x)] x_range <- range(private$x, finite = TRUE) # Required for displaying ticks on the slider, can modify choices! From 4f24aeb624e05c7a1ab6f0d88127b530a15f7d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:03:03 +0000 Subject: [PATCH 05/11] tests: verify if choices are adjusted and does not trigger false positive --- tests/testthat/test-RangeFilterState.R | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/testthat/test-RangeFilterState.R b/tests/testthat/test-RangeFilterState.R index 9177663a2..a547d5fbf 100644 --- a/tests/testthat/test-RangeFilterState.R +++ b/tests/testthat/test-RangeFilterState.R @@ -49,6 +49,23 @@ testthat::test_that("constructor raises error when selection is not numeric or c ) }) +testthat::describe("constructor modifies choices", { + local_nums <- c(seq(1.2, 1.9, .1), 1.905) + it("to better fit the tick on the slider", { + range <- RangeFilterState$new(local_nums, slice = teal_slice(dataname = "data", varname = "var")) + testthat::expect_failure( + testthat::expect_identical(shiny::isolate(range$get_state()[["choices"]]), range(local_nums)) + ) + }) + + it("and does not throw error when re-used", { + range <- RangeFilterState$new(local_nums, slice = teal_slice(dataname = "data", varname = "var")) + testthat::expect_no_condition( + RangeFilterState$new(local_nums, slice = range$get_state()) + ) + }) +}) + testthat::test_that("constructor raises error when choices is out of range", { testthat::expect_warning( RangeFilterState$new( From 822e3482e55494b60915e5b1969b8624b05767a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:17:39 +0000 Subject: [PATCH 06/11] tests: one more that keeps warning when manually using range(x) --- tests/testthat/test-RangeFilterState.R | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-RangeFilterState.R b/tests/testthat/test-RangeFilterState.R index a547d5fbf..dfab7d416 100644 --- a/tests/testthat/test-RangeFilterState.R +++ b/tests/testthat/test-RangeFilterState.R @@ -51,14 +51,24 @@ testthat::test_that("constructor raises error when selection is not numeric or c testthat::describe("constructor modifies choices", { local_nums <- c(seq(1.2, 1.9, .1), 1.905) - it("to better fit the tick on the slider", { + it("that are automatically calculated to better fit the tick on the slider", { range <- RangeFilterState$new(local_nums, slice = teal_slice(dataname = "data", varname = "var")) testthat::expect_failure( testthat::expect_identical(shiny::isolate(range$get_state()[["choices"]]), range(local_nums)) ) }) - it("and does not throw error when re-used", { + it("and throws warning when manually calculated with range(x)", { + testthat::expect_warning( + RangeFilterState$new( + local_nums, + slice = teal_slice(dataname = "data", varname = "var", choices = range(local_nums)) + ), + "Choices adjusted" + ) + }) + + it("and does not throw error when re-used", { # use case in teal when slice is used across modules range <- RangeFilterState$new(local_nums, slice = teal_slice(dataname = "data", varname = "var")) testthat::expect_no_condition( RangeFilterState$new(local_nums, slice = range$get_state()) From e92104e396a7ebd43de5bcb85177da9ad54ab764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:54:58 +0000 Subject: [PATCH 07/11] fix: use any(x != y) to check for diferences allowing double/integers --- R/FilterStateRange.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/FilterStateRange.R b/R/FilterStateRange.R index 4d8f67f26..8f1061a87 100644 --- a/R/FilterStateRange.R +++ b/R/FilterStateRange.R @@ -311,7 +311,7 @@ RangeFilterState <- R6::R6Class( # nolint } private$teal_slice$choices <- new_choices # Only throw warning if pretty choices are different - if (!is.null(choices) && !identical(choices, new_choices)) { + if (!is.null(choices) && any(choices != new_choices)) { warning(sprintf( "Choices adjusted (some values outside of variable range). Varname: %s, dataname: %s.", private$get_varname(), private$get_dataname() From 7cb55e6c24908be5872892ec318d327c6d4dfc96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 17:38:28 +0000 Subject: [PATCH 08/11] tests: expand test cases with non-finite --- R/FilterStateRange.R | 2 +- tests/testthat/test-RangeFilterState.R | 75 +++++++++++++++++++++----- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/R/FilterStateRange.R b/R/FilterStateRange.R index 8f1061a87..006c461e6 100644 --- a/R/FilterStateRange.R +++ b/R/FilterStateRange.R @@ -298,7 +298,7 @@ RangeFilterState <- R6::R6Class( # nolint private$set_is_choice_limited(private$x, new_choices) valid_range_index <- (private$x >= new_choices[1L] & private$x <= new_choices[2L]) - private$x <- private$x[valid_range_index | is.na(private$x) | !is.finite(private$x)] + private$x <- private$x[valid_range_index | !is.finite(private$x)] x_range <- range(private$x, finite = TRUE) # Required for displaying ticks on the slider, can modify choices! diff --git a/tests/testthat/test-RangeFilterState.R b/tests/testthat/test-RangeFilterState.R index dfab7d416..d32bccf3b 100644 --- a/tests/testthat/test-RangeFilterState.R +++ b/tests/testthat/test-RangeFilterState.R @@ -9,18 +9,69 @@ testthat::test_that("constructor accepts numerical values", { ) }) -testthat::test_that("constructor accepts infinite values but not infinite only", { - testthat::expect_no_error( - RangeFilterState$new(c(nums, Inf, -Inf), slice = teal_slice(dataname = "data", varname = "var")) - ) - testthat::expect_error( - RangeFilterState$new(Inf, slice = teal_slice(dataname = "data", varname = "var")), - "\"x\" contains no finite values" - ) - testthat::expect_error( - RangeFilterState$new(c(Inf, NA), slice = teal_slice(dataname = "data", varname = "var")), - "\"x\" contains no finite values" - ) +testthat::describe("constructor", { + nums <- 1:10 + it("accepts infinite values as part of numeric", { + testthat::expect_no_error( + RangeFilterState$new(c(nums, Inf, -Inf), slice = teal_slice(dataname = "data", varname = "var")) + ) + }) + + it("accepts NA values as part of numeric", { + lapply( + list(NA, NA_integer_, NA_real_), + function(x) { + testthat::expect_no_error( + RangeFilterState$new(c(nums, x), slice = teal_slice(dataname = "data", varname = "var")) + ) + } + ) + }) + + it("throws error with non-numeric NA", { + lapply( + list(NA_character_, NA_complex_), + function(x) { + testthat::expect_error( + RangeFilterState$new(c(nums, x), slice = teal_slice(dataname = "data", varname = "var")), + "not '(character|complex)'" + ) + } + ) + }) + + it("throws error on any complex values", { + testthat::expect_error( + RangeFilterState$new(c(nums, 1+0i), slice = teal_slice(dataname = "data", varname = "var")), + "not 'complex'" + ) + }) + + it("throws error on only infinite values", { + testthat::expect_error( + RangeFilterState$new(Inf, slice = teal_slice(dataname = "data", varname = "var")), + "\"x\" contains no finite values" + ) + }) + + it("throws error on only NA values", { + lapply( + list(NA, NA_character_, NA_complex_, NA_integer_, NA_real_), + function(x) { + testthat::expect_error( + RangeFilterState$new(x, slice = teal_slice(dataname = "data", varname = "var")), + "Contains only missing values" + ) + } + ) + }) + + it("throws error when no finite values are present", { + testthat::expect_error( + RangeFilterState$new(c(Inf, NA), slice = teal_slice(dataname = "data", varname = "var")), + "\"x\" contains no finite values" + ) + }) }) testthat::test_that("constructor initializes keep_inf = TRUE by default if x contains Infs", { From b2fc4cdeccb5b549ae02fd53b8d1f9cabe7a2a93 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 17:40:40 +0000 Subject: [PATCH 09/11] [skip style] [skip vbump] Restyle files --- tests/testthat/test-RangeFilterState.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-RangeFilterState.R b/tests/testthat/test-RangeFilterState.R index d32bccf3b..dc375f047 100644 --- a/tests/testthat/test-RangeFilterState.R +++ b/tests/testthat/test-RangeFilterState.R @@ -42,7 +42,7 @@ testthat::describe("constructor", { it("throws error on any complex values", { testthat::expect_error( - RangeFilterState$new(c(nums, 1+0i), slice = teal_slice(dataname = "data", varname = "var")), + RangeFilterState$new(c(nums, 1 + 0i), slice = teal_slice(dataname = "data", varname = "var")), "not 'complex'" ) }) From f256d8f313aa1a0bdecefd63eaf677e39593fe6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Wed, 11 Feb 2026 17:42:54 +0000 Subject: [PATCH 10/11] tests: also looks for NaN --- tests/testthat/test-RangeFilterState.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-RangeFilterState.R b/tests/testthat/test-RangeFilterState.R index dc375f047..4200bf89e 100644 --- a/tests/testthat/test-RangeFilterState.R +++ b/tests/testthat/test-RangeFilterState.R @@ -17,9 +17,9 @@ testthat::describe("constructor", { ) }) - it("accepts NA values as part of numeric", { + it("accepts NA / NaN values as part of numeric", { lapply( - list(NA, NA_integer_, NA_real_), + list(NA, NA_integer_, NA_real_, NaN), function(x) { testthat::expect_no_error( RangeFilterState$new(c(nums, x), slice = teal_slice(dataname = "data", varname = "var")) @@ -56,7 +56,7 @@ testthat::describe("constructor", { it("throws error on only NA values", { lapply( - list(NA, NA_character_, NA_complex_, NA_integer_, NA_real_), + list(NaN, NA, NA_character_, NA_complex_, NA_integer_, NA_real_), function(x) { testthat::expect_error( RangeFilterState$new(x, slice = teal_slice(dataname = "data", varname = "var")), From b7cc2e2b6296cf7519cb030a23a147a5cf70b7d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:42:28 +0000 Subject: [PATCH 11/11] feat: accepts dates and positxt --- tests/testthat/test-RangeFilterState.R | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/testthat/test-RangeFilterState.R b/tests/testthat/test-RangeFilterState.R index 4200bf89e..e94227eea 100644 --- a/tests/testthat/test-RangeFilterState.R +++ b/tests/testthat/test-RangeFilterState.R @@ -17,6 +17,16 @@ testthat::describe("constructor", { ) }) + it("accepts dates", { + range <- RangeFilterState$new(Sys.Date() + seq(0, 5, 1), slice = teal_slice(dataname = "data", varname = "var")) + checkmate::expect_date(shiny::isolate(range$get_state()$choices)) + }) + + it("accepts POSIXt", { + range <- RangeFilterState$new(Sys.time() + seq(0, 5, 1), slice = teal_slice(dataname = "data", varname = "var")) + checkmate::expect_posixct(shiny::isolate(range$get_state()$choices)) + }) + it("accepts NA / NaN values as part of numeric", { lapply( list(NA, NA_integer_, NA_real_, NaN),