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

Form input tags cannot have id="nodeName" or id="nodeType" inside sidebarPanel() #4192

Open
jfunction opened this issue Feb 25, 2025 · 10 comments

Comments

@jfunction
Copy link

I am on Shiny 1.10.0

If I create a downloadButton in an app it appears enabled. If I create a selectInput before the downloadButton it becomes disabled at startup.

See reprex.

System details

Browser Version:

Microsoft Edge
Version 133.0.3065.82 (Official build) (64-bit)

Output of sessionInfo():

R version 4.4.0 (2024-04-24 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)

Matrix products: default


locale:
[1] LC_COLLATE=English_South Africa.utf8  LC_CTYPE=English_South Africa.utf8    LC_MONETARY=English_South Africa.utf8 LC_NUMERIC=C                         
[5] LC_TIME=English_South Africa.utf8    

time zone: Africa/Johannesburg
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] shiny_1.10.0

loaded via a namespace (and not attached):
 [1] jsonlite_1.8.9    compiler_4.4.0    promises_1.3.2    reprex_2.1.0      Rcpp_1.0.14       clipr_0.8.0       callr_3.7.6       later_1.4.1      
 [9] jquerylib_0.1.4   yaml_2.3.8        fastmap_1.2.0     mime_0.12         R6_2.5.1          knitr_1.47        tibble_3.2.1      R.cache_0.16.0   
[17] pillar_1.9.0      bslib_0.9.0       R.utils_2.12.3    rlang_1.1.4       utf8_1.2.4        cachem_1.1.0      httpuv_1.6.15     xfun_0.45        
[25] fs_1.6.5          sass_0.4.9        memoise_2.0.1     cli_3.6.3         withr_3.0.2       magrittr_2.0.3    ps_1.7.6          processx_3.8.4   
[33] digest_0.6.36     rstudioapi_0.17.1 fontawesome_0.5.3 xtable_1.8-4      lifecycle_1.0.4   R.methodsS3_1.8.2 R.oo_1.26.0       vctrs_0.6.5      
[41] evaluate_0.24.0   glue_1.7.0        styler_1.10.3     fansi_1.0.6       rmarkdown_2.27    purrr_1.0.2       pkgconfig_2.0.3   tools_4.4.0      
[49] htmltools_0.5.8.1

Steps to reproduce the problem

library(shiny)

ui <- fluidPage(
  selectInput("x", "X",
              choices = c("A", "B")),
  downloadButton(outputId = "y", label = "Y"),
)

server <- function(input, output, session) {
}

shinyApp(ui, server)
Shiny applications not supported in static R Markdown documents

Created on 2025-02-25 with reprex v2.1.0

@gadenbuie
Copy link
Member

In a relatively recent version of Shiny, we started disabling the download button until the download handler is registered on the server side. This protects you against a bunch of weirdness that can happen with the download button if you don't have a handler in place.

What's very likely is that there's an ID conflict in your app. Check your input/output IDs, or run your app with shiny::devmode(TRUE) enabled.

Here's a working version of your app:

library(shiny)

ui <- fluidPage(
  selectInput("x", "X", choices = c("A", "B")),
  downloadButton(outputId = "y", label = "Y"),
)

server <- function(input, output, session) {
  output$y <- downloadHandler(
    filename = function() {
      "sample_data.csv"
    },
    content = function(file) {
      # Create some sample data
      data <- data.frame(
        ID = 1:5,
        Name = c("Alice", "Bob", "Charlie", "David", "Eve"),
        Value = rnorm(5)
      )
      # Write the data to a CSV file
      write.csv(data, file, row.names = FALSE)
    }
  )
}

shinyApp(ui, server)

@jfunction
Copy link
Author

Thank you for the assist - I was able to get to the bottom of this, kind of.

I did indeed leave out important context in my original post - I named the selectInput(inputId="nodeType") and put things in a sidebarLayout. After making those changes the downloadHandler is not registering for some reason so it remains disabled. There are references to "nodeType" in the codebase (in particular in selectize) but I found that renaming it to "nodeFlowType" did the trick for me. Not sure I need to pull this thread any more :)

Here is a minimal breaking example building on the last one:

library(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("nodeType", "X", choices = c("A", "B")),
      downloadButton(outputId = "y", label = "Y")
    ),
    mainPanel("empty")
  )
)

server <- function(input, output, session) {
  output$y <- downloadHandler(
    filename = function() {
      "sample_data.csv"
    },
    content = function(file) {
      # Create some sample data
      data <- data.frame(
        ID = 1:5,
        Name = c("Alice", "Bob", "Charlie", "David", "Eve"),
        Value = rnorm(5)
      )
      # Write the data to a CSV file
      write.csv(data, file, row.names = FALSE)
    }
  )
}

shinyApp(ui, server)

@jfunction
Copy link
Author

Actually, I wonder if this is related to downloadButton being in sidebarPanel, which should contain input controls only?

@gadenbuie
Copy link
Member

Actually, I wonder if this is related to downloadButton being in sidebarPanel, which should contain input controls only?

It's much much more likely that there's a conflict in your IDs. Can you go back to the state when the app wasn't working? If so, with the app open in the browser, if you open the browser's developer tools (by right-clicking anywhere on the page and selecting "Inspect" or "Inspect element"), the Console tab should have warnings or error messages pointing to duplicate ID problems.

@jfunction
Copy link
Author

jfunction commented Feb 25, 2025

Thanks - there are no warnings about duplicates (I tried refresh also).

Image

The above was a pretty compact breaking example. If you remove the sidebarLayout/sidebarPanel/mainPanel the button won't be disabled and if you replace "nodeType" with something else it also won't be disabled. So I think internally "nodeType" is being used somewhere. Also just to note the js console gives the same output when I "fix" the minimal example in the above ways.

@gadenbuie
Copy link
Member

gadenbuie commented Feb 25, 2025

The above was a pretty compact breaking example. If you remove the sidebarLayout/sidebarPanel/mainPanel the button won't be disabled and if you replace "nodeType" with something else it also won't be disabled. So I think internally "nodeType" is being used somewhere.

Wow. Sorry, I hadn't run the example yet, but you're right! That's... bizarre. Here's the smallest reprex I can make, which comes down to having a <select id="nodeType"> tag inside a <form> tag with the download button:

library(shiny)

ui <- fluidPage(
  tags$form(
    tags$select(id="nodeType"),
    downloadButton(outputId = "y", label = "Y"),
  )
)

server <- function(input, output, session) {
  output$y <- downloadHandler(
    filename = function() "sample.txt",
    content = function(file) {
      writeLines(Sys.time(), file)
    }
  )
}

shinyApp(ui, server)

Example on shinylive

The <form> tag comes into play because that's what sidebarPanel() uses, but if you use bslib::page_sidebar() you avoid this problem entirely.

ui <- bslib::page_sidebar(
  sidebar = sidebar(
    tags$select(id="nodeType"),
    downloadButton(outputId = "y", label = "Y"),
  )
)

I'm amazed that the reprex doesn't throw any console errors, but I can get a console error by changing id = "nodeType" to id = "nodeName". Both are element properties and it seems that something is causing jQuery to try to read the nodeName or nodeType property when the select input with that name appears in a <form>...

...actually even this causes an console error:

ui <- fluidPage(
  tags$form(
    tags$select(id="nodeName")
  )
)
> e.nodeName.toLowerCase is not a function

I'm going to re-open the issue because this is clearly not expected!

@gadenbuie gadenbuie reopened this Feb 25, 2025
@gadenbuie
Copy link
Member

Seems like this is also an old and still open issue in React: https://github.com/search?q=repo%3Afacebook%2Freact+%22nodeName.toLowerCase%22&ref=opensearch&type=issues

@gadenbuie
Copy link
Member

Also any form input elements with the id="nodeType" is enough:

  • tags$select(id = "nodeType")
  • tags$input(id = "nodeType")
  • tags$button(id = "nodeType"), etc...

@gadenbuie gadenbuie changed the title Automatically disabled downloadButton when selectInput is used. Form input tags cannot have id="nodeName" or id="nodeType" inside sidebarPanel() Feb 25, 2025
@jcheng5
Copy link
Member

jcheng5 commented Feb 25, 2025

Yuck (see method 3 in the question)

@jcheng5
Copy link
Member

jcheng5 commented Feb 25, 2025

WOW, this is the safe way to read e.nodeName/e.nodeType:

Object.getOwnPropertyDescriptor(Node.prototype, "nodeName").get.call(e)

and even then it's breaking encapsulation pretty hard!

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

3 participants