Skip to content
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

roots folder selection broken if shinyFilesButton in ModalDialog #101

Open
mjhelf opened this issue Oct 3, 2018 · 17 comments
Open

roots folder selection broken if shinyFilesButton in ModalDialog #101

mjhelf opened this issue Oct 3, 2018 · 17 comments

Comments

@mjhelf
Copy link

mjhelf commented Oct 3, 2018

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   
@mjhelf
Copy link
Author

mjhelf commented Oct 3, 2018

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.

@vnijs
Copy link
Collaborator

vnijs commented Oct 3, 2018

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.

@mjhelf
Copy link
Author

mjhelf commented Oct 3, 2018

Interesting! As it turns out, it does work in Chrome for me too!
I encountered the issue using Firefox on both Linux and Windows..

@vnijs
Copy link
Collaborator

vnijs commented Oct 3, 2018

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?

@AFriendlyRobot
Copy link
Contributor

@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.

@AFriendlyRobot
Copy link
Contributor

@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.

@vnijs
Copy link
Collaborator

vnijs commented Oct 11, 2018

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

@mjhelf
Copy link
Author

mjhelf commented Oct 11, 2018

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.

@vnijs
Copy link
Collaborator

vnijs commented Oct 11, 2018

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.

@lbusett
Copy link

lbusett commented Oct 19, 2018

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!

@vnijs
Copy link
Collaborator

vnijs commented Oct 19, 2018

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?

@AFriendlyRobot
Copy link
Contributor

@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.

@lbusett
Copy link

lbusett commented Oct 22, 2018

@AFriendlyRobot

Just tried deleting the tabindex and it works. How could one proceed to "remove" the tabindex programmatically? Is it possible ?

@AFriendlyRobot
Copy link
Contributor

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');
	});
};

@vnijs
Copy link
Collaborator

vnijs commented Oct 23, 2018

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?

@AFriendlyRobot
Copy link
Contributor

@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.

@vnijs
Copy link
Collaborator

vnijs commented Oct 23, 2018

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants