Fix: Allow users to opt out of shiny's download autoenable#4371
Conversation
f348912 to
35f71c4
Compare
2e1c842 to
c16218b
Compare
- Prevent early clicks on enabled=TRUE buttons before renderValue sets
the URL (href="" would otherwise navigate to the current page)
- Add skip_if_not_installed("shinyjs") to skip_if_not_shinytest2() so
tests skip cleanly in envs where shinyjs is not available
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
afbab40 to
d3696b2
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@copilot resolve the merge conflicts in this pull request |
Co-authored-by: cpsievert <1365941+cpsievert@users.noreply.github.com>
|
|
Resolved and pushed in 4c0548e. I merged |
|
Thanks @elnelson575. Please also make a |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Move shinytest2 to Suggests; access shinyjs indirectly via getFromNamespace() to avoid the static pattern scanner.
Motivation
In v1.9.0 (#4041), Shiny began automatically enabling
downloadButton()/downloadLink()once the server'sdownloadHandleris ready. This is a good default — it prevents users from clicking a download button before the server can handle it.However, this auto-enable creates a problem for apps that want to keep a download button disabled until some condition is met (e.g., the user selects a dataset first). Before #4041, apps could render the button as disabled and use
shinyjs::enable()/shinyjs::disable()(or custom JS) to manage its state. Now, the auto-enable unconditionally overrides that disabled state as soon as thedownloadHandlerinitializes.Closes #4119. Also likely fixes daattali/shinyjs#277.
Solution
Add an
enabledparameter todownloadButton()anddownloadLink()with three possible values:"auto"(default): Current behavior — starts disabled, auto-enables when thedownloadHandleris ready. No breaking change.TRUE: Starts enabled immediately, without waiting for the server. Useful when you know the handler will be ready and don't want the brief disabled flash.FALSE: Starts disabled and Shiny will never auto-enable it. You manage the enabled/disabled state yourself (e.g., viashinyjs).Additionally, when
enabled = "auto", Shiny now detects ifshinyjshas independently disabled the element (via theshinyjs-disabledclass) and skips the auto-enable in that case. This meansshinyjs::disabled()in the UI andshinyjs::disable()from the server both work correctly without any explicit opt-out.Implementation details
data-shiny-disable-auto-enableattribute is added to the element whenenabledisTRUEorFALSE(i.e., any non-"auto"value). The client-siderenderValuechecks for this attribute (and forshinyjs-disabled) before enabling.pointer-events: none(previously, a disabled-looking anchor link could still be clicked).Also used by rstudio/bslib#1298.
App example
Screen.Recording.2026-04-23.at.6.33.35.PM.mov
Sample demo app