Skip to content

Commit

Permalink
fix: duplicated slices are lingering the filter panel after DDL reload (
Browse files Browse the repository at this point in the history
#602)

# Pull Request

Companion of insightsengineering/teal#1275

Note: There's a similar solution that is cleaner. I'll push it on Monday

#### Changes description

- Keeps track of `remove observers` on filter panel
- Destroys `remove observer` when clearing filter panel



Co-authored-by: go_gonzo <[email protected]>
  • Loading branch information
averissimo and gogonzo authored Aug 1, 2024
1 parent 58eb499 commit 0c90543
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 43 deletions.
27 changes: 19 additions & 8 deletions R/FilterState.R
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,12 @@ FilterState <- R6::R6Class( # nolint
#' @param id (`character(1)`)
#' `shiny` module instance id.
#'
#' @param remove_callback (`function`)
#' callback to handle removal of this `FilterState` object from `state_list`
#'
#' @return Reactive expression signaling that remove button has been clicked.
#'
server = function(id) {
server = function(id, remove_callback) {
moduleServer(
id = id,
function(input, output, session) {
Expand All @@ -214,7 +217,7 @@ FilterState <- R6::R6Class( # nolint
private$server_inputs("inputs")
}

private$observers$state <- observeEvent(
private$observers[[session$ns("state")]] <- observeEvent(
eventExpr = list(private$get_selected(), private$get_keep_na(), private$get_keep_inf()),
handlerExpr = {
current_state <- as.list(self$get_state())
Expand All @@ -224,7 +227,7 @@ FilterState <- R6::R6Class( # nolint
}
)

private$observers$back <- observeEvent(
private$observers[[session$ns("back")]] <- observeEvent(
eventExpr = input$back,
handlerExpr = {
history <- rev(private$state_history())
Expand All @@ -235,7 +238,7 @@ FilterState <- R6::R6Class( # nolint
}
)

private$observers$reset <- observeEvent(
private$observers[[session$ns("reset")]] <- observeEvent(
eventExpr = input$reset,
handlerExpr = {
slice <- private$state_history()[[1L]]
Expand All @@ -245,7 +248,7 @@ FilterState <- R6::R6Class( # nolint

# Buttons for rewind/reset are disabled upon change in history to prevent double-clicking.
# Re-enabling occurs after 100 ms, after they are potentially hidden when no history is present.
private$observers$state_history <- observeEvent(
private$observers[[session$ns("state_history")]] <- observeEvent(
eventExpr = private$state_history(),
handlerExpr = {
shinyjs::disable(id = "back")
Expand All @@ -267,6 +270,13 @@ FilterState <- R6::R6Class( # nolint
}
)

private$observers[[session$ns("remove")]] <- observeEvent(
once = TRUE, # remove button can be called once, should be destroyed afterwards
ignoreInit = TRUE, # ignoreInit: should not matter because we destroy the previous input set of the UI
eventExpr = input$remove, # when remove button is clicked in the FilterState ui
handlerExpr = remove_callback()
)

private$destroy_shiny <- function() {
logger::log_debug("Destroying FilterState inputs and observers; id: { private$get_id() }")
# remove values from the input list
Expand All @@ -276,7 +286,7 @@ FilterState <- R6::R6Class( # nolint
lapply(private$observers, function(x) x$destroy())
}

reactive(input$remove)
NULL
}
)
},
Expand Down Expand Up @@ -381,6 +391,7 @@ FilterState <- R6::R6Class( # nolint
destroy_observers = function() {
if (!is.null(private$destroy_shiny)) {
private$destroy_shiny()
private$destroy_shiny <- NULL
}
}
),
Expand Down Expand Up @@ -761,7 +772,7 @@ FilterState <- R6::R6Class( # nolint
# this observer is needed in the situation when private$keep_inf has been
# changed directly by the api - then it's needed to rerender UI element
# to show relevant values
private$observers$keep_na_api <- observeEvent(
private$observers[[session$ns("keep_na_api")]] <- observeEvent(
ignoreNULL = FALSE, # nothing selected is possible for NA
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = private$get_keep_na(),
Expand All @@ -776,7 +787,7 @@ FilterState <- R6::R6Class( # nolint
}
}
)
private$observers$keep_na <- observeEvent(
private$observers[[session$ns("keep_na")]] <- observeEvent(
ignoreNULL = FALSE, # ignoreNULL: we don't want to ignore NULL when nothing is selected in the `selectInput`
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = input$value,
Expand Down
8 changes: 4 additions & 4 deletions R/FilterStateChoices.R
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,8 @@ ChoicesFilterState <- R6::R6Class( # nolint
})
})

if (private$is_checkboxgroup()) {
private$observers$selection <- observeEvent(
private$observers[[session$ns("selection")]] <- if (private$is_checkboxgroup()) {
observeEvent(
ignoreNULL = FALSE,
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = input$selection,
Expand All @@ -472,7 +472,7 @@ ChoicesFilterState <- R6::R6Class( # nolint
}
)
} else {
private$observers$selection <- observeEvent(
observeEvent(
ignoreNULL = FALSE,
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = input$selection_open, # observe click on a dropdown
Expand Down Expand Up @@ -510,7 +510,7 @@ ChoicesFilterState <- R6::R6Class( # nolint
# this observer is needed in the situation when teal_slice$selected has been
# changed directly by the api - then it's needed to rerender UI element
# to show relevant values
private$observers$selection_api <- observeEvent(private$get_selected(), {
private$observers[[session$ns("selection_api")]] <- observeEvent(private$get_selected(), {
# it's important to not retrigger when the input$selection is the same as reactive values
# kept in the teal_slice$selected
if (!setequal(input$selection, private$get_selected())) {
Expand Down
8 changes: 4 additions & 4 deletions R/FilterStateDate.R
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ DateFilterState <- R6::R6Class( # nolint
# this observer is needed in the situation when teal_slice$selected has been
# changed directly by the api - then it's needed to rerender UI element
# to show relevant values
private$observers$seletion_api <- observeEvent(
private$observers[[session$ns("selection_api")]] <- observeEvent(
ignoreNULL = TRUE, # dates needs to be selected
ignoreInit = TRUE,
eventExpr = private$get_selected(),
Expand All @@ -344,7 +344,7 @@ DateFilterState <- R6::R6Class( # nolint
}
)

private$observers$selection <- observeEvent(
private$observers[[session$ns("selection")]] <- observeEvent(
ignoreNULL = TRUE, # dates needs to be selected
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = input$selection,
Expand Down Expand Up @@ -374,7 +374,7 @@ DateFilterState <- R6::R6Class( # nolint

private$keep_na_srv("keep_na")

private$observers$reset1 <- observeEvent(input$start_date_reset, {
private$observers[[session$ns("reset1")]] <- observeEvent(input$start_date_reset, {
logger::log_debug("DateFilterState$server@3 reset start date, id: { private$get_id() }")
updateDateRangeInput(
session = session,
Expand All @@ -383,7 +383,7 @@ DateFilterState <- R6::R6Class( # nolint
)
})

private$observers$reset2 <- observeEvent(input$end_date_reset, {
private$observers[[session$ns("reset2")]] <- observeEvent(input$end_date_reset, {
logger::log_debug("DateFilterState$server@4 reset end date, id: { private$get_id() }")
updateDateRangeInput(
session = session,
Expand Down
10 changes: 5 additions & 5 deletions R/FilterStateDatettime.R
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ DatetimeFilterState <- R6::R6Class( # nolint
# this observer is needed in the situation when teal_slice$selected has been
# changed directly by the api - then it's needed to rerender UI element
# to show relevant values
private$observers$selection_api <- observeEvent(
private$observers[[session$ns("selection_api")]] <- observeEvent(
ignoreNULL = TRUE, # dates needs to be selected
ignoreInit = TRUE, # on init selected == default, so no need to trigger
eventExpr = private$get_selected(),
Expand Down Expand Up @@ -417,7 +417,7 @@ DatetimeFilterState <- R6::R6Class( # nolint
)


private$observers$selection_start <- observeEvent(
private$observers[[session$ns("selection_start")]] <- observeEvent(
ignoreNULL = TRUE, # dates needs to be selected
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = input$selection_start,
Expand Down Expand Up @@ -445,7 +445,7 @@ DatetimeFilterState <- R6::R6Class( # nolint
}
)

private$observers$selection_end <- observeEvent(
private$observers[[session$ns("selection_end")]] <- observeEvent(
ignoreNULL = TRUE, # dates needs to be selected
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = input$selection_end,
Expand Down Expand Up @@ -475,7 +475,7 @@ DatetimeFilterState <- R6::R6Class( # nolint

private$keep_na_srv("keep_na")

private$observers$reset1 <- observeEvent(
private$observers[[session$ns("reset1")]] <- observeEvent(
ignoreInit = TRUE, # reset button shouldn't be trigger on init
ignoreNULL = TRUE, # it's impossible and wrong to set default to NULL
input$start_date_reset,
Expand All @@ -488,7 +488,7 @@ DatetimeFilterState <- R6::R6Class( # nolint
logger::log_debug("DatetimeFilterState$server@2 reset start date, id: { private$get_id() }")
}
)
private$observers$reset2 <- observeEvent(
private$observers[[session$ns("reset2")]] <- observeEvent(
ignoreInit = TRUE, # reset button shouldn't be trigger on init
ignoreNULL = TRUE, # it's impossible and wrong to set default to NULL
input$end_date_reset,
Expand Down
14 changes: 12 additions & 2 deletions R/FilterStateExpr.R
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,12 @@ FilterStateExpr <- R6::R6Class( # nolint
#' @param id (`character(1)`)
#' `shiny` module instance id.
#'
#' @param remove_callback (`function`)
#' callback to handle removal of this `FilterState` object from `state_list`
#'
#' @return Reactive expression signaling that the remove button has been clicked.
#'
server = function(id) {
server = function(id, remove_callback) {
moduleServer(
id = id,
function(input, output, session) {
Expand All @@ -176,7 +179,14 @@ FilterStateExpr <- R6::R6Class( # nolint
lapply(session$ns(names(input)), .subset2(input, "impl")$.values$remove)
}

reactive(input$remove) # back to parent to remove self
private$observers[[session$ns("remove")]] <- observeEvent(
once = TRUE, # remove button can be called once, should be destroyed afterwards
ignoreInit = TRUE, # ignoreInit: should not matter because we destroy the previous input set of the UI
eventExpr = input$remove, # when remove button is clicked in the FilterState ui
handlerExpr = remove_callback()
)

NULL
}
)
},
Expand Down
4 changes: 2 additions & 2 deletions R/FilterStateLogical.R
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ LogicalFilterState <- R6::R6Class( # nolint
NULL
})

private$observers$seleted_api <- observeEvent(
private$observers[[session$ns("selected_api")]] <- observeEvent(
ignoreNULL = !private$is_multiple(),
ignoreInit = TRUE,
eventExpr = private$get_selected(),
Expand All @@ -335,7 +335,7 @@ LogicalFilterState <- R6::R6Class( # nolint
}
)

private$observers$selection <- observeEvent(
private$observers[[session$ns("selection")]] <- observeEvent(
ignoreNULL = FALSE,
ignoreInit = TRUE,
eventExpr = input$selection,
Expand Down
10 changes: 5 additions & 5 deletions R/FilterStateRange.R
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ RangeFilterState <- R6::R6Class( # nolint
})

# Dragging shapes (lines) on plot updates selection.
private$observers$relayout <-
private$observers[[session$ns("relayout")]] <-
observeEvent(
ignoreNULL = FALSE,
ignoreInit = TRUE,
Expand Down Expand Up @@ -541,7 +541,7 @@ RangeFilterState <- R6::R6Class( # nolint
)

# Change in selection updates shapes (lines) on plot and numeric input.
private$observers$selection_api <-
private$observers[[session$ns("selection_api")]] <-
observeEvent(
ignoreNULL = FALSE,
ignoreInit = TRUE,
Expand All @@ -559,7 +559,7 @@ RangeFilterState <- R6::R6Class( # nolint
)

# Manual input updates selection.
private$observers$selection_manual <- observeEvent(
private$observers[[session$ns("selection_manual")]] <- observeEvent(
ignoreNULL = FALSE,
ignoreInit = TRUE,
eventExpr = selection_manual(),
Expand Down Expand Up @@ -714,7 +714,7 @@ RangeFilterState <- R6::R6Class( # nolint
# this observer is needed in the situation when private$teal_slice$keep_inf has been
# changed directly by the api - then it's needed to rerender UI element
# to show relevant values
private$observers$keep_inf_api <- observeEvent(
private$observers[[session$ns("keep_inf_api")]] <- observeEvent(
ignoreNULL = TRUE, # its not possible for range that NULL is selected
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = private$get_keep_inf(),
Expand All @@ -729,7 +729,7 @@ RangeFilterState <- R6::R6Class( # nolint
}
)

private$observers$keep_inf <- observeEvent(
private$observers[[session$ns("keep_inf")]] <- observeEvent(
ignoreNULL = TRUE, # it's not possible for range that NULL is selected
ignoreInit = TRUE, # ignoreInit: should not matter because we set the UI with the desired initial state
eventExpr = input$value,
Expand Down
13 changes: 7 additions & 6 deletions R/FilterStates.R
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,9 @@ FilterStates <- R6::R6Class( # nolint
added_state_names <- vapply(added_states(), function(x) x$get_state()$id, character(1L))
logger::log_debug("FilterStates$srv_active@2 triggered by added states: { toString(added_state_names) }")
lapply(added_states(), function(state) {
fs_callback <- state$server(id = fs_to_shiny_ns(state))
observeEvent(
once = TRUE, # remove button can be called once, should be destroyed afterwards
ignoreInit = TRUE, # ignoreInit: should not matter because we destroy the previous input set of the UI
eventExpr = fs_callback(), # when remove button is clicked in the FilterState ui
handlerExpr = private$state_list_remove(state$get_state()$id)
state$server(
id = fs_to_shiny_ns(state),
function() private$state_list_remove(state$get_state()$id)
)
})
added_states(NULL)
Expand Down Expand Up @@ -642,6 +639,10 @@ FilterStates <- R6::R6Class( # nolint
return(TRUE)
} else {
state$destroy_observers()
lapply(
Filter(function(x) grepl(state$get_state()$id, x, fixed = TRUE), names(private$observers)),
function(x) private$observers[[x]]$destroy()
)
FALSE
}
} else {
Expand Down
6 changes: 3 additions & 3 deletions R/FilteredData.R
Original file line number Diff line number Diff line change
Expand Up @@ -499,13 +499,13 @@ FilteredData <- R6::R6Class( # nolint
#' @param active_datanames (`reactive`)
#' defining subset of `self$datanames()` to be displayed.
#' @return `shiny.tag`
ui_filter_panel = function(id, active_datanames = self$datanames()) {
ui_filter_panel = function(id, active_datanames = self$datanames) {
ns <- NS(id)
tags$div(
id = ns(NULL), # used for hiding / showing
include_css_files(pattern = "filter-panel"),
self$ui_overview(ns("overview")),
self$ui_active(ns("active"), active_datanames, private$allow_add)
self$ui_active(ns("active"), active_datanames = active_datanames)
)
},

Expand Down Expand Up @@ -547,7 +547,7 @@ FilteredData <- R6::R6Class( # nolint
#' @param active_datanames (`reactive`)
#' defining subset of `self$datanames()` to be displayed.
#' @return `shiny.tag`
ui_active = function(id, active_datanames = self$datanames()) {
ui_active = function(id, active_datanames = self$datanames) {
ns <- NS(id)
tags$div(
id = id, # not used, can be used to customize CSS behavior
Expand Down
5 changes: 4 additions & 1 deletion man/FilterState.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion man/FilterStateExpr.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0c90543

Please sign in to comment.