Bug: `shinyjs::disabled()` doesn't fully prevent downloads from `shiny::downloadButton()`
Description:
When using shinyjs::disabled() on a download button (shiny::downloadButton()), the button appears visually disabled but users can still click it. This results in:
- Internal Shiny errors being triggered if the downloadable file is not present yet
- Target file being downloaded regardless, if present
- Possible confusion for users when they can interact with buttons that appear disabled
Reproducible Example:
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
titlePanel("Download Button Disable Bug"),
mainPanel(
actionButton("disable_btn", "Disable Download Button"),
actionButton("enable_btn", "Enable Download Button"),
hr(),
shinyjs::disabled(downloadButton("download_data", "Download Data"))
)
)
server <- function(input, output, session) {
# Track whether downloads should be allowed
download_allowed <- reactiveVal(FALSE)
# Disable button handler
observeEvent(input$disable_btn, {
download_allowed(FALSE)
shinyjs::disable("download_data")
})
# Enable button handler
observeEvent(input$enable_btn, {
download_allowed(TRUE)
shinyjs::enable("download_data")
})
# Download handler
output$download_data <- downloadHandler(
filename = function() {
"data_mtcars.csv"
},
content = function(file) {
# Generate a file regardless of button state
write.csv(mtcars, file, row.names = FALSE)
}
)
}
shinyApp(ui, server)
Issue description:
When we use shinyjs::disabled() on a download button at the start of the app, the button looks disabled (grayed out), but users can still click it and download files.
After interacting with enable/disable buttons from the UI, the server side implementation of shinyjs::enable() / shinyjs::diable() works as expected. The download button is visually greyed out and is not clickable.
Expected Behavior:
When a download button is disabled with shinyjs::disabled(), it should not be clickable at all, similar to how other disabled inputs behave.
Fix Request:
It would be helpful if shinyjs::disabled() could completely prevent download buttons from triggering downloads, not just change their appearance, akin to how shinyjs::enable()/shinyjs::disable() behave.
Thank you very much. :)
I believe this got broken in shiny 1.9.0, specifically from this commit in shiny. This is the reason for that commit according to the NEWS file:
downloadButton() and downloadLink() are now disabled up until they are fully initialized. This prevents the user from clicking the button/link before the download is ready.
Here is a reprex that worked in shiny 1.8.1 but doesn't work since then:
library(shiny)
ui <- fluidPage(
useShinyjs(),
downloadButton("foo", "bar") |> disabled()
)
server <- function(input, output, session) {
output$aa <- downloadHandler(
filename = "test.csv",
content = function(file) {
write.csv(cars, file)
}
)
}
shinyApp(ui, server)
I need to think about how it's possible to fix this given shiny's new behaviour. Shiny now initiates all download buttons as "disabled" and then enables them when their download handler is ready, so I need to somehow circumvent shiny from enabling the button.
Looks like this is a known bug in shiny rstudio/shiny#4119
I doubt this will be fixed from within {shinyjs}