roots folder selection broken if shinyFilesButton in ModalDialog
Hi, I see that a lot of new features are being built into shinyFiles lately, and that's great, thanks for your hard work!
I have come across a problem that occurs when a shinyFilesButton/ shinyDirButton/ shinySaveButton is placed inside a shiny::modalDialog: The shinyFiles Dialog pops up and works, except that you cannot browse/change the root folder selection. The dropdown menu shows up for a half second sometimes if you keep clicking on the rootFolder selection input, but no chance to change the folder. Here is an example based on shinyFilesExample(). Note how the folder selection works for the buttons in the main page, but not the modal dialog:
library(shiny)
library(shinyFiles)
library(fs)
server <- shinyServer(function(input, output, session) {
volumes <- c(Home = fs::path_home(), "R Installation" = R.home())
shinyFileChoose(input, "file", roots = volumes, session = session)
shinyDirChoose(input, "directory", roots = volumes, session = session, restrictions = system.file(package = "base"))
shinyFileSave(input, "save", roots = volumes, session = session, restrictions = system.file(package = "base"))
shinyFileChoose(input, "fileInModal", roots = volumes, session = session)
shinyDirChoose(input, "directoryInModal", roots = volumes, session = session, restrictions = system.file(package = "base"))
shinyFileSave(input, "saveInModal", roots = volumes, session = session, restrictions = system.file(package = "base"))
observeEvent(input$openModal,{
showModal(modalDialog(
shinyFilesButton("fileInModal", "File select", "Please select a file", multiple = TRUE),
shinyDirButton("directoryInModal", "Folder select", "Please select a folder"),
shinySaveButton("saveInModal", "Save file", "Save file as...", filetype = list(text = "txt", picture = c("jpeg", "jpg"))),
title = "Folder selection does not work in these:",
easyClose = F
))
})
})
ui <- shinyUI(pageWithSidebar(
headerPanel(
"Selections with shinyFiles",
"shinyFiles example"
),
sidebarPanel(
tags$hr(),
shinyFilesButton("file", "File select", "Please select a file", multiple = TRUE),
tags$hr(),
shinyDirButton("directory", "Folder select", "Please select a folder"),
tags$hr(),
shinySaveButton("save", "Save file", "Save file as...", filetype = list(text = "txt", picture = c("jpeg", "jpg"))),
tags$hr(),
actionButton("openModal","Here is the Problem")
),
mainPanel(
)
))
shinyApp(ui,server)
The weird thing is that this worked for me until I updated shinyFiles (and possibly some other packages), but I cannot make it work again by installing older versions of shinyFiles from github, so maybe it's a problem with other packages. Have that problem on both Windows and Linux.
R version 3.5.1 (2018-07-02)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.1 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8
[6] LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] fs_1.2.6 shinyFiles_0.7.1.0001 shiny_1.1.0
loaded via a namespace (and not attached):
[1] Rcpp_0.12.17 crayon_1.3.4 digest_0.6.15 later_0.7.3 mime_0.5 R6_2.2.2 xtable_1.8-2 jsonlite_1.5 magrittr_1.5
[10] pillar_1.3.0 rlang_0.2.1 rstudioapi_0.7 promises_1.0.1 tools_3.5.1 httpuv_1.4.5 compiler_3.5.1 htmltools_0.3.6 tibble_1.4.2
It seems like I can fix the issue by removing the modalDialog like this in the server code (example for the fileInModal shinyFileButton):
observeEvent(input$fileInModal,{
removeModal()
})
This seems like a workaround for now but of course it closes the original modalDialog when the shinyFiles modalDialog comes up, so that may not always be desirable.
Hmmm. I tried the code you posted on Windows, Mac, and Linux and the buttons in the modal work (i.e., I can change the root directory just fine). Perhaps this is a browser issue? What browser do you use? My testing was with Chrome and Safari.
Interesting! As it turns out, it does work in Chrome for me too! I encountered the issue using Firefox on both Linux and Windows..
I can replicate with FireFox on Windows. I don't see any error messages in the debugger. At this point I can only suggest to either (1) hope/wait for an update in FireFox or (2) use the work-around you found.
@AFriendlyRobot Any ideas perhaps?
@vnijs @mjhelf Sorry, I've been moving this week and haven't had much chance to look at anything. I can take a look this weekend.
@vnijs @mjhelf I've played around a bit, and I can reproduce the error on Firefox on Mac. Removing classes/event listeners from the breadcrumbs (folder selection) dropdown does not seem to improve things. Additionally, the sorting dropdown still seems to work.
Unfortunately, I don't have much else to offer on this offhand.
Thanks @AFriendlyRobot. I'm not sure what options there are at this stage @mjhelf other than not using shinyFiles buttons embed in shinyModals with FF until there is an update from FF
Thanks to both of you, @vnijs, @AFriendlyRobot for looking into this! I don't know if the FF developers are aware of this issue - should I post an issue on Bugzilla? Do you think they will be able to deal with a shiny app?
I checked with a terribly old version of FF (v. 38) that I had on a forgotten VM, and the issue exists there, too. That is to say, the problem predates their "Quantum" update and hasn't been fixed in years. I don't know enough about browser internals to know if that could even play a role though.
I doubt you'd have much success posting an issue with FF to be honest but I do think that is where the issue is somehow. I tried the code you posted on Windows, Mac, and Linux and the buttons in the modal work with Chrome and Safari. Without any error messages I'm not sure how we'd even address this.
Hi,
I run into a similar issue, using shinySaveButton within a Modal.
In practice, although the "file selector" widget seems to work ok, it does not allow me to change the output basename (it is stuck on "filename" and does not change. Below a reprex:
library(shiny)
library(shinyFiles)
ui <- shinyUI(fluidPage(
titlePanel("Example"),
shiny::modalDialog(
shinySaveButton("save", "Save file", "Save file as ...", filetype=list(xlsx="xlsx"))
)
))
server <- shinyServer(function(input, output, session) {
observe({
volumes <- c("UserFolder"="D:/Data")
shinyFileSave(input, "save", roots=getVolumes(), session=session)
fileinfo <- parseSavePath(volumes, input$save)
data <- data.frame(a=c(1,2))
})
})
shinyApp(ui = ui, server = server)
If I remove the modalDialog, the button works just fine . Note that this does not work even on chrome (on Linux Ubuntu 18.10).
Any hints/suggestions to solve this?
Thanks in advance!
I can reproduce the problem you are seeing @lbusett. Thanks for reporting. The same issue occurs in previous versions of shinyFiles as well so it doesn't seem to be linked to any of the more recent changes to the package. I'm not seeing any errors in the JS console so it is not obvious to me what the problem could be.
@thomasp85, @AFriendlyRobot: Any ideas?
@vnijs @lbusett From this stackoverflow (https://stackoverflow.com/questions/23232081/bootstrap-modal-disabling-text-fields-from-child-div) it seems like the problem is pretty old and related specifically to bootstrap modals. Bootstrap modals have an attribute "tabindex" set to -1. I've tried deleting this attribute through the chrome developer tools (on a mac) and the shinyFiles text field seems to work fine after that. Does this work for anyone else?
If this is the real root of the problem, I'm not really sure what the best solution is. One temporary solution at the moment is to just remove the original modal (as discussed above) or to delete the "tabindex" attribute on that modal, I guess.
@AFriendlyRobot
Just tried deleting the tabindex and it works. How could one proceed to "remove" the tabindex programmatically? Is it possible ?
I've put up a minimal example project at https://github.com/afriendlyrobot/shiny_remove_tabindex_example with some plain javascript. The script.js file finds the '#shiny-modal' element and removes the 'tabindex' attribute, then sets an event listener to remove the 'tabindex' attribute whenever the modal is shown. This could break something somewhere down the line, especially if some external package relies on the tabindex attribute. However, it seems to work for me as a workaround.
However, GitHub seems to be having issues at the moment, so here's the javascript contents:
window.onload = function(event) {
// Remove modal 'tabindex' listener if the modal is already open on page load
document.getElementById('shiny-modal').removeAttribute('tabindex');
// Add event listener for whenever the modal is opened
document.getElementById('shiny-modal').addEventListener('shown', function(event) {
document.getElementById('shiny-modal').removeAttribute('tabindex');
});
};
Thanks @AFriendlyRobot! I agree there is a risk of breaking applications by removing tabindex by default (see also https://stackoverflow.com/questions/32911355/whats-the-tabindex-1-in-bootstrap-for).
That said, adding an option to source or activate the code in script.js when using shinyFiles from another modal may be a good approach. Perhaps this could be added as an argument for each of the "button" functions?
@vnijs It would certainly be possible to include an option (maybe for a slightly more general script, including more than just shiny modals), and I should be able to add something along those lines tomorrow. However, I believe the issue is a bit higher level, and this script solution seems to violate best practices for accessible design (after reading your link or the mdn docs for tabindex). Or at least, I believe this script solution is breaking explicit functionality of shiny modals (trapping focus for accessibility purposes).
On the other hand, I haven't been able to come up with a more satisfying solution yet. Really I think two modals just aren't designed to be placed on top of each other. I'm currently experimenting, but I doubt I'll find a clean solution in the next day or so. The most "straightforward" thing would probably be to either document/warn about opening a filesave/filechoose/dirchoose modal from within a modal, or force other modals to close (which of course could break applications). Along those lines, this post may be interesting.
The js script you created could be used at-your-own risk in other applications but, I agree, it is not ideal. The last link you posted has some interesting examples. The "stackable" option at http://jschr.github.io/bootstrap-modal/ seems to have a modal-from-modal approach that has inputs that work (from https://stackoverflow.com/a/22470230/1974918).