-
-
Notifications
You must be signed in to change notification settings - Fork 5
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
Update FilteredData.R #57
Conversation
Code Coverage Summary
Results for commit: 0da0166 Minimum allowed coverage is ♻️ This comment has been updated with latest results |
The issue is a bit more complex because the root cause is I will take over this PR :) |
* Fixed a bug when the filter panel overview would not recalculate when changing a new module in cases when the panel was hidden during a transition between the modules. * Changed the behaviour of the burger button when there are no active datanames in the filter panel. Closes #55
This should be ready to review. I changed the behaviour of the burger button to show the filter panel even when there are no active datanames. The previous behaviour was messy - the toggle state was confused with the manual hiding of the panel in such cases, which caused a need for an additional click to actually get in sync with the rest of the tabs and on top of that when the burger was clicked it would resize the encoding and the main panel to fit the hidden filter panel. It effectively was worse than doing nothing. If it is unclear where the actual fix is it is in shiny::outputOption(output, "table", suspendWhenHidden = FALSE) that forces the table to recalculate even when the overview is hidden in the UI. As a bonus I refactored the long server function a little bit, should be more readable right now. EDIT: This one took an unreasonable amount of time to debug mainly due to what I consider an unintuitive behaviour of |
@@ -899,6 +857,7 @@ FilteredData <- R6::R6Class( # nolint | |||
table_html | |||
}) | |||
|
|||
shiny::outputOptions(output, "table", suspendWhenHidden = FALSE) |
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.
Here's the actual fix.
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.
I will reduce the PR to only this line, if it is enough.
R/FilteredData.R
Outdated
private$hide_inactive_datasets(active_datanames) | ||
if (length(active_datanames()) == 0 || is.null(active_datanames())) { | ||
# hide the filter panel UI when the active module does not use any datasets from the panel | ||
shinyjs::runjs("if (filter_open) toggle_sidebar();") | ||
} |
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.
Make note of this reversed order of business here compared to main
.
Here we:
- hide inactive dataset names from the panel
- Hide the panel
On main we do the reverse which leads to the lack of update of the names in the filter panel because shiny
does not see a need to update anything in the UI if it is hidden anyway. Sadly, when the UI is dynamically shown, it does not detect that these inactive dataset names should be removed from the UI. That led to the active datanames being a copy of the last module's that had active datanames.
try with a module which have a filters = NULL.
It not works correctly for such scenario now. If you add other modules too, then this example module will close a FilterPanel globally not only locally. |
I think the separation of the toggle js function from active_datanames was done on purpose. |
I pivoted on my initial idea and implemented it as it should be - using events :) Should be working now as intended. Here is the example I used in the development: devtools::load_all("teal.slice")
devtools::load_all("teal")
example_module <- function(label = "example teal module") {
checkmate::assert_string(label)
module(
label,
server = function(id, datasets) {
moduleServer(id, function(input, output, session) {
output$text <- renderPrint(datasets$get_data(input$dataname, filtered = TRUE))
})
},
ui = function(id, datasets) {
ns <- NS(id)
teal.widgets::standard_layout(
output = verbatimTextOutput(ns("text")),
encoding = selectInput(ns("dataname"), "Choose a dataset", choices = datasets$datanames())
)
},
filters = NULL
)
}
app <- teal::init(data = list("iris" = iris), modules = list(example_module(), teal::example_module()))
shiny::shinyApp(app$ui, app$server)
|
We can discuss this more. At this moment module with app options(teal.log_level = logger::TRACE)
devtools::load_all("teal")
#library(teal.modules.general)
library(scda)
#logger::log_threshold(logger::TRACE, namespace = "teal")
ADSL <- synthetic_cdisc_data("latest")$adsl
ADTTE <- synthetic_cdisc_data("latest")$adtte
ADRS <- synthetic_cdisc_data("latest")$adrs
reporter_module <- function(label = "example teal module") {
checkmate::assert_string(label)
module(
label,
server = function(id, datasets, reporter) {
moduleServer(id, function(input, output, session) {
teal.reporter::simple_reporter_srv(
id = "reporter",
reporter = reporter,
card_fun = function(card) card
)
output$text <- renderPrint(datasets$get_data(input$dataname, filtered = TRUE))
})
},
ui = function(id, datasets) {
ns <- NS(id)
teal.widgets::standard_layout(
output = tagList(
teal.reporter::simple_reporter_ui(ns("reporter")),
verbatimTextOutput(ns("text"))
),
encoding = selectInput(ns("dataname"), "Choose a dataset", choices = datasets$datanames())
)
},
filters = "all"
)
}
adsl_module <- module(
label = "ADSL module",
ui = function(id, ...) {
ns <- NS(id)
div(
h1("adsl module"),
verbatimTextOutput(ns("out")),
selectInput(ns("button"), "select val", choices = c("a", "b"), selected = "a")
)
},
server = function(id, datasets, ...) moduleServer(id, function(input, output, session) {
cat("\nmodule-init input$button is: ", input$button)
observeEvent(input$button, cat("\nobserver input$button is: ", input$button))
output$out <- renderPrint(datasets$get_data("ADSL", filtered = TRUE))
}),
filters = "ADSL"
)
adtte_module <- module(
label = "ADTTE module",
ui = function(id, ...) {
div(
h1("adtte module"),
verbatimTextOutput(NS(id)("out"))
)
},
server = function(id, datasets, ...) {
moduleServer(id, function(input, output, session) {
output$out <- renderPrint(datasets$get_data("ADTTE", filtered = TRUE))
})
},
filters = "ADTTE"
)
adrs_module <- module(
label = "ADRS module",
ui = function(id, ...) {
div("adrs module")
verbatimTextOutput(NS(id)("out"))
},
server = function(id, datasets, ...) {
moduleServer(id, function(input, output, session) {
output$out <- renderPrint(datasets$get_data("ADRS", filtered = TRUE))
})
},
filters = "ADRS"
)
null_module <- module(
label = "NULL module",
ui = function(id, ...) {
div("NULL module")
verbatimTextOutput(NS(id)("out"))
},
server = function(id, datasets, ...) {
moduleServer(id, function(input, output, session) {
output$out <- renderPrint(datasets$get_data("ADRS", filtered = TRUE))
})
},
filters = NULL
)
app <- init(
data = cdisc_data(
cdisc_dataset("ADSL", ADSL),
cdisc_dataset("ADTTE", ADTTE),
cdisc_dataset("ADRS", ADRS)
),
modules = modules(
modules(
label = "tabs 1",
modules(
label = "tab 11",
adsl_module,
adtte_module
),
adrs_module
),
modules(
label = "tabs 2",
null_module,
reporter_module()
)
),
filter = list(
ADSL = list(
SEX = list("M", keep_na = TRUE),
COUNTRY = c("USA", NA),
AGE = default_filter()
)
)
)
runApp(app) |
@@ -592,7 +592,7 @@ FilteredData <- R6::R6Class( # nolint | |||
ui_filter_panel = function(id) { | |||
ns <- NS(id) | |||
div( | |||
id = ns("filter_panel_whole"), # used for hiding / showing | |||
id = ns(NULL), # used for hiding / showing |
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.
good move as we do not need additional layer here
R/FilteredData.R
Outdated
}, | ||
ignoreNULL = FALSE | ||
) | ||
private$active_datanames_observer(active_datanames, session$ns(NULL)) |
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.
The functionality transferred to private
# shinyjs takes care of the namespace around the id | ||
shinyjs::show(id_add_filter) | ||
shinyjs::show(id_filter_dataname) | ||
} else { | ||
shinyjs::hide(id_add_filter) | ||
shinyjs::hide(id_filter_dataname) |
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.
it comes from shinyjs:::jsFuncHelper where session$ns() is used around id.
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.
I don't think there could be a good solution in the current design of the FilteredData. I think it should be like this:
- teal can hide filter panel if active_datanames is null. It means hiding whole panel and disabling burger should be in teal only.
- teal.slice can hide datasets in the filter-panel if they are not within active_datanames. The js functions to hide particulat datasets should be in teal.slice
}, | ||
ignoreNULL = FALSE | ||
priority = 1 |
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.
why is it needed? I think it's fine without priority
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.
it will work without the priority = 1, I assumed Konrad made some research that it boost performance
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.
👍
closes #55
linked to insightsengineering/teal#681
Refactor the FIlterPanel