Search code examples
rshinyshinydashboardshinyappsshinyjs

how to automatically scale uiOutput() when sidebar collapses in Shiny


I have a toggle switch which collapses the sidebar panel; however, when I do that, the datatable in uiOutput() doesn't stretch accordingly. I don't know what argument I am missing. I have changed the renderDatatable() arguments but nothing changed. Also, if possible, how can I change the render so that the datatable takes entire whitespace regardless of sidebard being collapsed?

library(shiny)
library(shinythemes)
library(shinyjs)
library(shinyWidgets)
#ui.r
ui <- fluidPage(
  theme=shinytheme("flatly") ,

  useShinyjs(),
  
  dropdownButton(
    
    tags$h3("Toggle"),
    
    materialSwitch(inputId = "toggleSidebar",label = "Hide Table? ",
                   value = TRUE, status = "success"),
    
    circle = TRUE, status = "info",
    icon = icon("gear"), width = "300px",
    
    tooltip = tooltipOptions(title = "Choose for more options!")
  ),


  
  # Sidebar layout with input and output definitions 
  sidebarLayout(
    div( id ="Sidebar",
    # Sidebar panel for inputs
    sidebarPanel(
      uiOutput("rad")
    )),
    
    # Main panel for displaying outputs
    mainPanel(
      uiOutput("tabers")
    )
  )
)
#server.r

server <- function(input, output) {
  
  data_sets <- list(NULL, iris, mtcars, ToothGrowth)
  
  
  observeEvent(input$toggleSidebar, {
    shinyjs::toggle(id = "Sidebar", condition = input$toggleSidebar)
  })
  
  output$rad<-renderUI({
    radioButtons("radio", label = "",
                 choices = list("Navigation" = 1, "Iris" = 2, "Mtcars" = 3,"ToothGrowth" = 4), 
                 selected = character(0))
  })
  
  output$tabers<- renderUI({
    if(is.null(input$radio)) {
      tabsetPanel(
        id="tabC",
        type = "tabs",
        tabPanel("Welcome!")
      )
    }
    else if(input$radio==1){
      tabsetPanel(
        id="tabA",
        type = "tabs",
        tabPanel("Navigation...")
      )
    }
    else if(input$radio==2){
      tabsetPanel(
        id="tabA",
        type = "tabs",
        tabPanel("Data", DT::renderDataTable({ data_sets[[as.integer(input$radio)]]}, filter = 'top', 
                                             options = list(scrollX = TRUE, lengthChange = TRUE, widthChange= TRUE))),
        tabPanel("Summary",renderPrint({ summary(data_sets[[as.integer(input$radio)]]) }) ),
        tabPanel("etc.")
      ) 
    }
    else if(input$radio==3){
      tabsetPanel(
        id="tabA",
        type = "tabs",
        tabPanel("Data", DT::renderDataTable({ data_sets[[as.integer(input$radio)]]}, filter = 'top', 
                                             options = list(scrollX = TRUE, lengthChange = TRUE, widthChange= TRUE))),
        #tabPanel("Plot" ),
        tabPanel("etc.")
      ) 
    }
    else if(input$radio==4){
      tabsetPanel(
        id="tabA",
        type = "tabs",
        tabPanel("Navigation", DT::renderDataTable({ data_sets[[as.integer(input$radio)]]}, filter = 'top', 
                                                   options = list(scrollX = TRUE, lengthChange = TRUE, widthChange= TRUE))),
        tabPanel("Summary",renderPrint({ summary(data_sets[[as.integer(input$radio)]]) }) ),
        tabPanel("etc.")
      )
    }
    # Left last else in here but should not get called as is
    else{
      tabsetPanel(
        id="tabC",
        type = "tabs",
        tabPanel("Global"),
        tabPanel("Performance" )
      ) 
    }
  })
}

shinyApp(ui, server)

enter image description here enter image description here

I was wondering if I can get some assistance with that, please!


Solution

  • Since you are using shinyjs, it can easily be:

    library(shiny)
    library(shinyjs)
    library(shinyWidgets)
    #ui.r
    ui <- fluidPage(
        
        useShinyjs(),
        
        dropdownButton(
            
            tags$h3("Toggle"),
            
            materialSwitch(inputId = "toggleSidebar",label = "Hide Table? ",
                           value = TRUE, status = "success"),
            
            circle = TRUE, status = "info",
            icon = icon("gear"), width = "300px",
            
            tooltip = tooltipOptions(title = "Choose for more options!")
        ),
        
        
        
        # Sidebar layout with input and output definitions 
        sidebarLayout(
            div( id ="Sidebar",
                 # Sidebar panel for inputs
                 sidebarPanel(
                     uiOutput("rad")
                 )),
            
            # Main panel for displaying outputs
            mainPanel(
                id = "main_panel",
                uiOutput("tabers")
            )
        )
    )
    #server.r
    
    server <- function(input, output) {
        
        data_sets <- list(NULL, iris, mtcars, ToothGrowth)
        
        
        observeEvent(input$toggleSidebar, {
            shinyjs::toggle(id = "Sidebar", condition = input$toggleSidebar)
            if(!isTRUE(input$toggleSidebar)) {
                shinyjs::runjs("$('#main_panel').removeClass('col-sm-8').addClass('col-sm-12')")
            } else {
                shinyjs::runjs("$('#main_panel').removeClass('col-sm-12').addClass('col-sm-8')")
            }
            
        })
        
        output$rad<-renderUI({
            radioButtons("radio", label = "",
                         choices = list("Navigation" = 1, "Iris" = 2, "Mtcars" = 3,"ToothGrowth" = 4), 
                         selected = character(0))
        })
        
        output$tabers<- renderUI({
            if(is.null(input$radio)) {
                tabsetPanel(
                    id="tabC",
                    type = "tabs",
                    tabPanel("Welcome!")
                )
            }
            else if(input$radio==1){
                tabsetPanel(
                    id="tabA",
                    type = "tabs",
                    tabPanel("Navigation...")
                )
            }
            else if(input$radio==2){
                tabsetPanel(
                    id="tabA",
                    type = "tabs",
                    tabPanel("Data", DT::renderDataTable({ data_sets[[as.integer(input$radio)]]}, filter = 'top', 
                                                         options = list(scrollX = TRUE, lengthChange = TRUE, widthChange= TRUE))),
                    tabPanel("Summary",renderPrint({ summary(data_sets[[as.integer(input$radio)]]) }) ),
                    tabPanel("etc.")
                ) 
            }
            else if(input$radio==3){
                tabsetPanel(
                    id="tabA",
                    type = "tabs",
                    tabPanel("Data", DT::renderDataTable({ data_sets[[as.integer(input$radio)]]}, filter = 'top', 
                                                         options = list(scrollX = TRUE, lengthChange = TRUE, widthChange= TRUE))),
                    #tabPanel("Plot" ),
                    tabPanel("etc.")
                ) 
            }
            else if(input$radio==4){
                tabsetPanel(
                    id="tabA",
                    type = "tabs",
                    tabPanel("Navigation", DT::renderDataTable({ data_sets[[as.integer(input$radio)]]}, filter = 'top', 
                                                               options = list(scrollX = TRUE, lengthChange = TRUE, widthChange= TRUE))),
                    tabPanel("Summary",renderPrint({ summary(data_sets[[as.integer(input$radio)]]) }) ),
                    tabPanel("etc.")
                )
            }
            # Left last else in here but should not get called as is
            else{
                tabsetPanel(
                    id="tabC",
                    type = "tabs",
                    tabPanel("Global"),
                    tabPanel("Performance" )
                ) 
            }
        })
    }
    
    shinyApp(ui, server)
    
    

    I added an ID for the main panel so I can easily select it

    mainPanel(
        id = "main_panel",
        uiOutput("tabers")
    )
    

    On server, add some javascript to toggle the same time you hide the sidebar:

        observeEvent(input$toggleSidebar, {
            shinyjs::toggle(id = "Sidebar", condition = input$toggleSidebar)
            if(!isTRUE(input$toggleSidebar)) {
                shinyjs::runjs("$('#main_panel').removeClass('col-sm-8').addClass('col-sm-12')")
            } else {
                shinyjs::runjs("$('#main_panel').removeClass('col-sm-12').addClass('col-sm-8')")
            }
            
        })