rstudio / shiny

Easy interactive web applications with R
http://shiny.rstudio.com
Other
5.33k stars 1.87k forks source link

R-computed plot height causes layout problems #3798

Closed aronatkins closed 1 year ago

aronatkins commented 1 year ago

System details

Browser Version:

Output of sessionInfo():

R version 3.6.3 (2020-02-29)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: OS X Snow Leopard 12.6.3

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.9             compiler_3.6.3         pillar_1.8.1          
 [4] later_1.3.0            tools_3.6.3            digest_0.6.31         
 [7] lifecycle_1.0.3        tibble_3.1.8           gtable_0.3.0          
[10] pkgconfig_2.0.3        rlang_1.1.0            shiny_1.7.4           
[13] cli_3.6.0              DBI_1.1.3              rstudioapi_0.14.0-9000
[16] fastmap_1.1.0          dplyr_1.0.10           generics_0.1.3        
[19] vctrs_0.5.1            grid_3.6.3             tidyselect_1.1.1      
[22] glue_1.6.2             R6_2.5.1               fansi_1.0.3           
[25] ggplot2_3.4.1          purrr_1.0.0            magrittr_2.0.3        
[28] scales_1.2.1           promises_1.2.0.1       ellipsis_0.3.2        
[31] htmltools_0.5.4        rsconnect_0.8.29.9000  assertthat_0.2.1      
[34] mime_0.12              xtable_1.8-4           colorspace_2.0-2      
[37] httpuv_1.6.6           utf8_1.2.2             munsell_0.5.0         

Example application or steps to reproduce the problem

library(shiny)
library(ggplot2)

ui <- fluidPage(

    titlePanel("height-restricted"),

    sidebarLayout(
        sidebarPanel(
          h4("above"),
          plotOutput("side"),
          h4("below")
        ),

        mainPanel(
          h4("above"),
           plotOutput("main"),
           h4("below")
        )
    )
)

server <- function(input, output, session) {

    output$side <- renderPlot({
      ggplot(mtcars, aes(mpg, cyl)) + geom_point()
    }, height = function() {
      session$clientData$output_side_width / 2
    })

    output$main <- renderPlot({
      ggplot(mtcars, aes(mpg, cyl)) + geom_point()
    }, height = function() {
      session$clientData$output_main_width / 2
    })
}

shinyApp(ui = ui, server = server)

Describe the problem in detail

This example is using the height-modifying approach recommended in https://github.com/rstudio/shiny/issues/650#issuecomment-62443654

I expected a plot that is half as tall as it is wide, with no vertical space beneath, and the trailing text correctly placed.

In the RStudio IDE, the two plots show space beneath.

image

In Firefox, space is only below the plot in the sidebar.

image

In Safari, space is only below the sidebar plot, but the h4 beneath the main plot has no vertical spacing.

image

In Chrome, space is below the sidebar plot, and the h4 beneath the main plot overlaps with the plot.

image
gadenbuie commented 1 year ago

The height argument of renderPlot() controls the height of the generated image, so you are indeed creating an image that is twice as wide as it is tall.

But the image itself appears inside the container created by plotOutput(), which has a height controlled by height in plotOutput(). The default height of that container is 400px. Even though the image is smaller than 400px tall, the plot output container still reserves 400px of vertical space. This accounts for both the empty space and the overlapping "below" text.

In the image below, I added a red border around the plot output container (using tags$style(".shiny-plot-output { border: 1px solid red }")).

image

Try setting height = "auto" in plotOutput() to have the plot output container be driven by the contents within it. Your UI code would look like this:

ui <- fluidPage(

    titlePanel("height-restricted"),

    sidebarLayout(
        sidebarPanel(
          h4("above"),
          plotOutput("side", height = "auto"),
          h4("below")
        ),

        mainPanel(
          h4("above"),
           plotOutput("main", height = "auto"),
           h4("below")
        )
    )
)

image

aronatkins commented 1 year ago

Ah. I see. I need BOTH the height computation in R and height = "auto" in the UI definition.

Here's what I ended up with, which is closer to the real app:

# https://github.com/rstudio/shiny/issues/3798
library(shiny)
library(ggplot2)

ui <- fluidPage(
    titlePanel("height-restricted"),
    sidebarLayout(
        sidebarPanel(
          h4("above"),
          plotOutput("side", height = "auto"),
          plotOutput("second", height = "auto"),
          h4("below")
        ),
        mainPanel(
           "the contents of this panel do not matter."
        )
    )
)

server <- function(input, output, session) {
    output$side <- renderPlot({
      ggplot(mtcars, aes(mpg, cyl)) + geom_point()
    }, height = function() {
      # 1:1
      session$clientData$output_side_width
    })
    output$second <- renderPlot({
      ggplot(mtcars, aes(mpg, cyl)) + geom_point()
    }, height = function() {
      # 2:1
      session$clientData$output_second_width / 2
    })
}

shinyApp(ui = ui, server = server)
image

If I remove the height adjustment in R, the following error occurs:

Warning: Error in graphics::plot.new: figure margins too large
  130: graphics::plot.new
  127: startPNG
  126: drawPlot
  112: <reactive:plotObj>
   96: drawReactive
   83: renderFunc
   82: output$second
    1: shiny::runApp
aronatkins commented 1 year ago

Closing; I incompletely followed the advice in https://github.com/rstudio/shiny/issues/650#issuecomment-62443654