ebailey78 / shinyBS

Twitter Bootstrap Components for Shiny
182 stars 47 forks source link

Bug: ConditionalPanel within a CollapsePanel confuses the collapsePanel #38

Open ghost opened 9 years ago

ghost commented 9 years ago

I have a nested conditionalPanel within a bsCollapse. When the conditionalPanel hits a FALSE condition and elements are hidden, the value of the bsCollapsePanel does not get returned properly. That is, when you try to ask which panel is open, you will get NULL (which is wrong). If you go back and forth between the conditions, you see that the values get added to a list for some reason.

So you get NULL and then ["thePanel", "thePanel"]

(behavior should be clear if you check with what I have below)

ui

shinyUI(fluidPage(bsCollapse(
      id = "theCollapse",
      bsCollapsePanel(title = "thePanel",
                      selectInput(inputId = "selectId", label = "label", choices = c(a= 'a', b='b')),
                      conditionalPanel(condition = "input.selectId == 'b'",
                                       selectInput(inputId = "condId", label = "label2", choices = c('d', 'e'))))))))

server

shinyServer(function(input, output) {
observe({
  x <- reactive({input$theCollapse})
  print(paste(x()))

Unless I'm mistaken, this is truly a bug. Let me know if I can be of any help here; this is a pretty big blocker for my project. Thanks!

mattflor commented 8 years ago

I just found out that I have the same issue. This is currently a big show stopper for me, too. Have you ever found a way around this?

Here's a reproducible example I made by adapting the updateCollapse example (https://ebailey78.github.io/shinyBS/docs/Collapses.html#updateCollapse):

library(shiny)
library(shinyBS)

shinyApp(
    ui = fluidPage(sidebarLayout(
        sidebarPanel(
            HTML("This button will open Panel 1 using updateCollapse."),
            actionButton("p1Button", "Push Me!"),
            selectInput("styleSelect", "Select style for Panel 1",
                        c("default", "primary", "danger", "warning", "info", "success"))
        ),
        mainPanel(
            bsCollapse(
                id = "collapseExample", open = "Panel 2",
                bsCollapsePanel(
                    title = "Panel 1", 
                    style = "info",
                    p("This is a panel which has the default style. You can change the style in the sidebar."),
                    p("However, something goes wrong with this panel's shiny communication when there is a visible conditionalPanel inside of it. First, set", code("options(shiny.trace = TRUE)"), " and start the app. Open and close panels 1 and 2 and you'll see messages like this in the console:"), 
                    p(code("RECV {'method':'update','data':{'collapseExample':['Panel 1'],'.clientdata_output_genericPlot_hidden':true}}")),
                    p(code("RECV {'method':'update','data':{'collapseExample':['Panel 2'],'.clientdata_output_genericPlot_hidden':false}}")), 
                    p("Now choose the panel style 'danger' in the sidebar which will trigger the display of a conditionalPanel within panel 1. Then open and close panels 1 and 2 again and watch shiny's messages in the console:"), 
                    p(code("RECV {'method':'update','data':{'collapseExample':['Panel 1','Panel 2'],'.clientdata_output_genericPlot_hidden':false}}")),
                    p(code("RECV {'method':'update','data':{'collapseExample':['Panel 1','Panel 1'],'.clientdata_output_genericPlot_hidden':true}}")),
                    conditionalPanel(
                        condition = "input.styleSelect == 'danger'",
                        wellPanel(
                            h1("DANGER ZONE"),
                            p("'Panel 1' seems to be added to shiny's messages. This can mess up everything pretty bad if one uses a lot of conditional panels.")
                        )
                    )
                ),
                bsCollapsePanel(
                    title = "Panel 2", 
                    style = "success", 
                    "This panel has a generic plot. ",
                    "and a 'success' style.", 
                    plotOutput("genericPlot")
                )
            )
        )
    )),
    server = function(input, output, session) {
        output$genericPlot <- renderPlot(plot(rnorm(100)))

        observeEvent(input$p1Button, ({
            updateCollapse(session, "collapseExample", open = c("Panel 1"))
        }))

        observeEvent(input$styleSelect, ({
            updateCollapse(session, "collapseExample", 
                           style = list("Panel 1" = input$styleSelect))
        }))
    }
)
ghost commented 8 years ago

I did not find a workaround. I ended up not using this feature in my projects

mattflor commented 8 years ago

Damn it. Thanks anyway for the quick response!

mattflor commented 8 years ago

@pan0ramic Don't know whether you got notified automatically, but I just made a pull request with a fix for this issue. Hopefully, that is. Seems to work for my shiny app.

trDC commented 7 years ago

+1 also having this issue.

ghost commented 7 years ago

@mattflor I didn't see this! Thanks for making the commit. Doesn't look like the author wants to merge though :(

akaever commented 7 years ago

We have encountered the same issue (nested collapsePanels or conditionPanels within a collapsePanel). It seems that the nested elements fire the same event (show.bs.collapse and hide.bs.collapse). The provided fix did not solve the issue in our case. However, adding an explicit check whether the parent().parent() of the event.target is the actual bsCollapsePanel (and thereby avoiding nested events) did it:

$panels.on("show.bs.collapse", function(event) {
  // make sure this is not an event from a nested element
  if(!($el.is($(event.target).parent().parent()))) {
    return;
  }
  // ...
});

$panels.on("hide.bs.collapse", function(event) {
  // make sure this is not an event from a nested element
  if(!($el.is($(event.target).parent().parent()))) {
    return;
  }
  // ...
});

Perhaps there is a more elegant solution...

natbprice commented 5 years ago

I also encountered this bug. I proposed a workaround on Stackoverflow (https://stackoverflow.com/questions/58015021/conflict-between-shinybsbscollapse-and-shinyconditionalpanel) where shiny::conditionalPanel is replaced by the following:

uiOutput("condPanelA")
output$condPanelA <- renderUI({
  if(input$showPanelA) {
     helpText("Panel A conditional content")
  } else {
    NULL
  }
})