rshinyaccordionbslib

nested accordion does not seem adhere to `multiple=FALSE` parameter


When using bslib::accordion() and bslib::accordion_panel(), as below, I have an accordion with s (say s=3) panels. I want each of the accordion_panels to contain an accordion, which itself contains t (say t=2) panels. No problem constructing this, as below:

library(shiny)
library(bslib)

accordion(
  accordion_panel(
    title = "panel_1",
    accordion(
      accordion_panel(title = "panel_1_sub_1", HTML("panel 1, subpanel 1 content")),
      accordion_panel(title = "panel_1_sub_2", HTML("panel 1, subpanel 2 content")),
      open=FALSE,
      multiple=FALSE # this is not working

    )
  ),
  accordion_panel(
    title = "panel_2",
    accordion(
      accordion_panel(title = "panel_2_sub_1", HTML("panel 2, subpanel 1 content")),
      accordion_panel(title = "panel_2_sub_2", HTML("panel 2, subpanel 2 content")),
      open=FALSE, 
      multiple=FALSE # this is not working
    )
  ),
  accordion_panel(
    title = "panel_3",
    accordion(
      accordion_panel(title = "panel_3_sub_1", HTML("panel 3, subpanel 1 content")),
      accordion_panel(title = "panel_3_sub_2", HTML("panel 3, subpanel 2 content")),
      open=FALSE, 
      multiple=FALSE # this is not working
    )
  ),
  open=FALSE, 
  multiple=FALSE # this is working fine
)

Upon initiation, all three panels and each of the nested accordions are closed. As requested on the outer/parent accordion, only one accordion panel is allowed to be open multiple=FALSE, and this works well.

However, although each of the inner accordions also have multiple=FALSE, multiple panels within these inner accordions do open (i.e. the panels in the inner accordion do not automatically close when another panel within that inner accordion is selected).

Anyone know a work-around for this, or is there something I am doing incorrectly in the above design of a nested accordion?


Solution

  • The reason why the inner accordion_panel stay open is that they get multiple parents (attribute data-bs-parent) assigned when they are created. E.g. for your first nested accordion, the parents look like this:

    [1] "#bslib-accordion-7599" # "panel_1"
    [1] "#bslib-accordion-5703 #bslib-accordion-7599" # "panel_1_sub_1"
    [1] "#bslib-accordion-5703 #bslib-accordion-7599" # "panel_1_sub_2"
    

    However, only the first one is the "real" parent. This has to be fixed in bslib. As long as #819 is open, you can use the following workaround.

    The idea is that we have to drop all parts of the data-bs-parent attribute except the first one. Your accordion from above is, say, nestedAcc, then we manipulate it using htmltools::tagQuery:

    nestedAcc <- accordion( # defined as in the question
      accordion_panel(
      ...
      )
    )
    
    tagQ <- htmltools::tagQuery(nestedAcc)
    tagQ$
      find(".accordion-collapse.collapse")$
      each(function(el, i) {
        # save all parents
        parents <- el$attribs[names(el$attribs) == "data-bs-parent"] 
        # remove all parents
        el$attribs <- el$attribs[names(el$attribs) != "data-bs-parent"]
        # append the "true" parent, which is the first one
        tagAppendAttributes(el, `data-bs-parent` = unlist(parents)[1])
        # return edited tag
        el
      })$
      allTags()
    

    This yields an accordion where the inner panels have multiple = FALSE.

    enter image description here