rggplot2shinyplotlyggplotly

Issues with ggplotly in a Shiny app: legend placement below plot, and plotly toolbar covering plot title


In my Shiny app, I want to have multiple interactive plots. I create the plot object with ggplot and then pass it to ggplotly to add the interactive element. To maximize the plot space, I want the legend to be below the plot. When I move the legend to the bottom, it obscures part of the x axis. How do I place the legend just below the x axis label? I wish I knew how to wrap the title or have the subtitle persist when it becomes a plotly object. Also, is there a way to move the plot title so that it is not hidden behind the plotly tools when a user interacts with the plot? Here's a small example:

library(shiny)
library(ggplot2)
library(plotly)

# adding a categorical variable to the faithful dataset
myfaithful <- faithful %>% 
  mutate(duration = if_else(eruptions <= 2,
                            "short",
                            "long")) %>% 
  mutate(duration = factor(duration, levels = c("short", "long"),
                           ordered = TRUE))

# Define UI for application that draws a histogram
ui <- fluidPage(
  
  # Application title
  titlePanel(
    "Stackoverflow example app"
  ),
  
  # Sidebar  
  sidebarLayout(
    sidebarPanel(
      width = 2,
      radioButtons("box",
                   label = "Some sidebar content",
                   choices = LETTERS[1:10])
    ),
    
    # 
    mainPanel(
      width = 10,
      uiOutput("plots"))
  )
)


# Define server logic required to draw a histogram
server <- function(input, output) {
  
  output$plots <- renderUI({
    fluidRow(
      column(6, 
             wellPanel(
               uiOutput("distPlot")
               
             )
      ),
      column(6, 
             wellPanel(
               "Panel 2"
             )
      )
    )
  })
  
  
  output$distPlot <- renderUI({
    # 
     p <- ggplot(myfaithful, aes(x = waiting, fill = duration)) + 
      geom_bar() +
      labs(title = "Frequency of time elapsed between eruptions, by length of eruption, long title",
           subtitle = "Does a subtitle work?",
           x = "Time (min)",
           y = "Frequency",
           fill = "Duration") +
       theme(legend.position = "bottom")
     
     
    ggplotly(p) %>% 
      plotly::layout(
        xaxis = list(title = "Time(min)",
                     tickangle = 45),
        legend=list(
          orientation='h'),
        title = list(
          font = list(
            family = "Arial",
            color = "black",
            size = 16),
          text = "Frequency of time elapsed between eruptions, by length of eruption, long title"
        )
      )
    
    
  })
}

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

And here's a screenshot that shows the 4 issues (legend placement, long title not wrapping, subtitle disappearing, and plotly tools hiding title when user hovers over plot): enter image description here


Solution

  • One way of solving this issue is by increasing the margin around the "paper" or grid space, then adjusting the position of the title and legend. The long title will remain an issue in some small devices because as far as I know, Plotly does not have a wrapping option for long strings in any label. To break the string you can add an html break <br>.

    I would remove the label definitions in ggplot and only define them in plotly::layout() to avoid double instructions.

    You can find the details of how to position the title and the legend in Plotly's documentation https://plotly.com/r/reference/layout/

    output$distPlot <- renderUI({
    
        p <- ggplot(myfaithful, aes(x = waiting, fill = duration)) +
          geom_bar()
    
        ggplotly(p) %>%
          plotly::layout(
            xaxis = list(
              title = list(text = "Time(min)", font = list(size = 12)),
              tickangle = 45
            ),
            yaxis = list(
              title = list(text ="Frequency", font = list(size = 12))
            ),
            legend = list(
              orientation = "h",
              y = -.25
            ),
            title = list(
              font = list(family = "Arial", color = "black", size = 14),
              text = "Frequency of time elapsed between eruptions, <br> by length of eruption, long title",
              yref = "paper",
              yanchor = "bottom",
              pad = list(b = 30),
              y = 1
            ),
            autosize = TRUE,
            margin = list(t = 90, r = 10, b = 90, l = 50)
          )
    
      })
    

    enter image description here