Search code examples
htmlcssrggplotlyggtext

str_wrap is ignored when using ggplotly (but not ggplot)


I want to wrap the text of the legend for the following plot, which works with ggplot:

library(tidyverse)
library(ggtext)
library(plotly)

df <- data.frame(xx = c(0, 3),
                 yy = c(0, 1))

df_text <- data.frame(start = c(0, 1, 2),
                      end = c(1, 2, 3),
                      heading_bold = c("title 1", "title 2", "title 3"),
                      normal_text = c("i want this to be normal text title 1 but it is also a very long title that i want to wrap",
                                      "i want this to be normal text title 2 but it is also a very long title that i want to wrap",
                                      "i want this to be normal text title 3 but it is also a very long title that i want to wrap")) %>% 
  mutate(merged_text = paste0("**",heading_bold,"**: ", normal_text)) 

pp <- ggplot(df, aes(x = xx, y = yy)) +
  geom_rect(data = df_text,
            aes(xmin = start,
                xmax = end,
                fill = merged_text,
                ymin = 0,
                ymax = 1),
            inherit.aes = FALSE) +
  scale_fill_viridis_d(option = "A", name = "Legend", 
                       limits = unique(df_text$merged_text), direction = -1, 
                       labels = function(x) str_replace_all(str_wrap(x, width = 30), "\\n", "<br>")) +
  theme(legend.text = element_markdown())
pp

enter image description here

but is lost with ggplotly:

ggplotly(pp)

enter image description here

I am sure there is clever way of applying css/html styling to achieve this (without writing out the full legend text again) - any ideas?

thanks


Solution

  • ggplotly() removes line breaks

    What is happening here is actually that the new lines are lost when you convert to a plotly object. You can see this here:

    ggpl <- ggplotly(pp)
    ggpl$x$data[[1]]$name
    # [1] "**title 1**: i want this to be normal text title 1 but it is also a very long title that i want to wrap"
    

    As you can see, the <br> blocks you faithfully added have disappeared. I thought this might be to do with plotly escaping html, but even if you use htmltools::HTML("<br>"), the same thing happens.

    Create an R function to modify the plotly object

    Fortunately, once you know what the problem is, it's easy to fix from R, without using any html, js or css. Simply:

    1. Line wrap.
    2. Replace all \n with <br>.
    3. Make all text in the form "**text**" into <b>text</b>.
    add_line_breaks <- function(ggplotly_object, width = 30) {
        ggplotly_object$x$data <- lapply(
            ggplotly_object$x$data,
            \(l) {
                l$name <- l$name |>
                    str_wrap(width = width) |>
                    str_replace_all("\\n", "<br>") |>
                    str_replace_all("\\*\\*(.*?)\\*\\*", "<b>\\1</b>")
    
                l
            }
        )
        ggplotly_object
    }
    

    Then you can use it as follows:

    pp |>
        ggplotly() |>
        add_line_breaks() 
    

    Output:

    Plot with line breaks and bold