Ian
Ian

Reputation: 1707

Scaling plotOutput height to fill the row in a sidebarLayout

I have a Shiny app with produces the following output. I would like the height of the graph to scale to fill the row which contains the sidebar, (down to some minimum dimension). This sidebar height changes depending on the data being examined.

enter image description here

The ui code I'm currently using is:

  sidebarLayout(
    sidebarPanel(
      uiOutput("ridgeDates")
    ),
    mainPanel(
      plotOutput("ridgesPlot")
    )
  )

with the plot being rendered by renderPlot(...) This seems to adjust the /width/ automatically as I change the browser window width.

I've spent a while searching but can't find anything that does this. Is this possible?

Upvotes: 0

Views: 228

Answers (1)

Mr.Rlover
Mr.Rlover

Reputation: 2613

We can use jQuery to track the height of the sidebar and set the height of the plot in css before creating the plotOutput. To do that, we need to use uiOutput in the UI, then render the plot dynamically.

So in the UI, the mainPanel will now have:

uiOutput("ridgePlot")

Then the plot is rendered in the server like so:

output$ridgePlot <- renderUI({
          
         # plot data
         output$ridges <- renderPlot({
         
         # plot()
           
         })

         plotOutput("ridges")

})

Now we use shinyjs() to write a simple javascript function that sets the height value of the plot to the height of the sidebar. The sidebar is of class well, so we first get the height of the well, save it to a variable then set the ridges plot to the height of the variable, in javascript like this:

var newHeight = $('.well').outerHeight(); $('#ridges').height(newHeight)

I have used .outerHeight() because the well has extra padding that effectively gives it extra height than the height specified in the css rules for the well.

We can use this function in shiny using runjs() from shinyjs package. Since we need to get the height from the well after it has been rendered, we use observe and use it before the plotOutput inside the renderPlot, which is also inside the renderUI.

    observe({
      session$onFlushed(function() {
        shinyjs::runjs("var newHeight = $('.well').outerHeight(); $('#ridges').height(newHeight)")
      }, once=TRUE)
    })

Putting it together in one Shiny app:

library(shiny)
library(shinyjs)
library(ggplot2)

ui = fluidPage(
  useShinyjs(),
  titlePanel("This is just a test!"),
  sidebarLayout(
    
      sidebarPanel(
        uiOutput("ridgeDates")
      ),
      
    mainPanel(
      uiOutput("ridgePlot")
    ))
)


server = function(input, output, session) {
  
  output$ridgeDates <- renderUI({
    
    rng <- round(runif(1, 15, 21))
    
    radioButtons("choose", "A changing list", choices = 1:rng)
    
  })
  
  
  output$ridgePlot <- renderUI({
    datax <- matrix(c(1,2,3,4,5,6),6,1)
    datay <- matrix(c(1,7,6,4,5,3),6,1)
    titleplot<-"title"
    summary <- "testing text"
    
    
    output$ridges <- renderPlot({
 #     pl <- plot(datax, datay, main = titleplot, xlab = "input$axis1", ylab = "input$axis2", pch=18, col="blue")
      
      ggplot(NULL, aes(datax, datay))+
        geom_point(colour = "#1e90ff")
      
    })
    
    observe({
      session$onFlushed(function() {
        shinyjs::runjs("var newHeight = $('.well').outerHeight(); $('#ridges').height(newHeight)")
      }, once=TRUE)
    })
    
    
    plotOutput("ridges")
  
      
  })
  
}

# Run the application 
shinyApp(ui = ui, server = server)

My example:

enter image description here

Upvotes: 2

Related Questions