Search code examples
rlayoutbslib

bslib layout organization using conditionalPanel in shiny app


I am trying to get a simple column-based layout for a card in bslib, but I am not getting the expected results. The example app (see below) has two columns for ui elements in a card: the first column is always the same, and the ui elements in the second depend on the selection in the selectInput on the first column (using two conditionalPanels). When the first element is selected in ‘planningType’ the second column starts in the top, but if the second element is chosen, then the second column ui element has a top margin (an empty space), that I can’t get rid of. I tried using fill = FALSE and fillable = FALSE in the card_body and layout_column_wrap, but it didn’t make a difference. Is there something that can be done? Thanks a lot.

library(shiny)
library(bslib)

ui <- page_fluid(
  title = "Habit planner",
  
  card(
    card_header("General planning", class = "bg-primary"),
    
    card_body(

      layout_column_wrap(
        width = 1/2,
        
        # First column
        selectInput("planningType",
                    label = "Type of planning",
                    choices = c("Menu" = "menu", 
                                "Physical activity" = "physical"),
                    selected = "Menu"),
        
        # Second column
        layout_column_wrap(
          width = 1,
          conditionalPanel("input.planningType == 'menu'",
                           selectInput("diet",
                                       label = "Type of diet",
                                       choices = c("Mediterranean",
                                                   "Low calory",
                                                   "Mixed"),
                                       selected = "Mediterranean"),
                           selectInput("groceries",
                                       label = "Where do you want to buy food?",
                                       choices = c("Town market",
                                                   "Supermarket"),
                                       selected = "Mediterranean")
                           
          ),
          conditionalPanel("input.planningType == 'physical'",
                           selectInput("activity",
                                       label = "Type of activity",
                                       choices = c("Running",
                                                   "Strength training",
                                                   "Swimming"),
                                       selected = "Running")
          )
        )
      )
    )
  )
)


server <- function(input, output) {  }

shinyApp(ui = ui, server = server)

Solution

  • That's an odd error, but perhaps it's because it's experimental? Not sure. It's pretty easy to fix though—all you need to do is tag the two conditions together, e.g., the div() tag.

    The next problem you'll run into is the dropdowns disappearing behind the card. You can address that with using selectizeInput and setting the option dropdownParent = 'body'. (versus selectInput -- both of which use selectize.js in the background)

    Here's a simple example of how you can make this obey your commands.

    library(shiny)
    library(bslib)
    
    ui <- page_fluid(
      title = "Habit planner",
      
      card(
        card_header("General planning", class = "bg-primary"),
        
        card_body(
          
          layout_column_wrap(
            width = 1/2,
            
            # First column
            selectizeInput("planningType",
                        label = "Type of planning",
                        choices = c("Menu" = "menu", 
                                    "Physical activity" = "physical"),
                        selected = "Menu",
                        options = list(dropdownParent = 'body')),   # I'm new
            
            # Second column
            layout_column_wrap(
              width = 1,
              div(                          # <----------- I'm new
                conditionalPanel("input.planningType == 'menu'",
                                 selectizeInput("diet",
                                             label = "Type of diet",
                                             choices = c("Mediterranean",
                                                         "Low calory",
                                                         "Mixed"),
                                             selected = "Mediterranean",   # I'm new
                                             options = list(dropdownParent = 'body')),
                                 selectizeInput("groceries",
                                             label = "Where do you want to buy food?",
                                             choices = c("Town market",
                                                         "Supermarket"),
                                             selected = "Mediterranean",   # I'm new
                                             options = list(dropdownParent = 'body'))
                                 
                ),
                conditionalPanel("input.planningType == 'physical'",
                                 selectizeInput("activity",
                                             label = "Type of activity",
                                             choices = c("Running",
                                                         "Strength training",
                                                         "Swimming"),
                                             selected = "Running",       # I'm new
                                             options = list(dropdownParent = 'body'))
                )
              )
            )
          )
        )
      )
    )
      
      
    server <- function(input, output) { }
    shinyApp(ui = ui, server = server)
    

    updated app