reactable icon indicating copy to clipboard operation
reactable copied to clipboard

Raw HTML not rendering inside 'details' argument of reactable

Open gfsarmanho opened this issue 5 years ago • 2 comments

At first, congrats for the awesome package! I just started to use and I am getting familiar with possible configurations.

Inspired in the cran-packages demo I tried to insert some HTML code inside my reactable. The html = TRUE argument of colDef() works well Inside the rows, but the raw HTML in row_details is not rendering correctly:

```{r setup, include=FALSE}
# Packages
library(reactable)
library(htmltools)

# Data
df <- data.frame(
  letter = c("Alpha", "Beta", "Gamma", "Delta"),
  symbol = c("&alpha;", "&beta;", "&gamma;", "&delta;"),
  value = c(0, 5, 10, 20)
)
```

```{r}
# Expanding each row
row_details <- function(index){
  item <- df[index, ]
  detail <- div(
    p("Letter name: ", tags$b(item$letter, .noWS="outside")),      
    p("Letter symbol: ", HTML(item$symbol)),                            # not working
    tagList(HTML("<h3>header</h3><p>Simple text</p>")), # not working
   br(),
    tagList(h3("my header"), p("Simple text")),                           # not working
    tagList("<h3>header</h3><p>Simple text</p>"),             # not working
  )
  detail
}

# My reactable
reactable(
  data = df,
  onClick = "expand",
  resizable = TRUE,
  columns = list(
    symbol = colDef(name = "Symbol", maxWidth = 70, html = TRUE), # works
    letter = colDef(name = "Name", maxWidth = 70, style = list(fontWeight = "bold")),
    value = colDef(name = "Number", maxWidth = 70)
  ),
  details = row_details,
  wrap = FALSE,
  rowStyle = list(cursor = "pointer")
)
```

I tried to combine htmltools::HTML() with some tags, but nothing worked... Is there anything I am missing? Best regards!

gfsarmanho avatar Nov 12 '20 14:11 gfsarmanho

Hi, you're not missing anything - raw HTML via htmltools::HTML() just isn't supported at the moment. It's more because of technical limitations, so some day it may be possible. For now, I would recommend using htmltools tag objects only if possible.

If you want to render symbols, use escaped unicode strings instead of HTML entities, like this:

df <- data.frame(
  letter = c("Alpha", "Beta", "Gamma", "Delta"),
  symbol = c("\u03b1", "\u03b2", "\u03b3", "\u03b4"),
  value = c(0, 5, 10, 20)
)

Or convert the HTML entities into regular strings:

# Adapted from https://stackoverflow.com/questions/5060076/convert-html-character-entity-encoding-in-r/51828654#51828654
unescape_html <- function(str) {
  html <- paste0("<x>", paste0(str, collapse = "#_|"), "</x>")
  parsed <- xml2::xml_text(xml2::read_html(html))
  strsplit(parsed, "#_|", fixed = TRUE)[[1]]
}

df <- data.frame(
  letter = c("Alpha", "Beta", "Gamma", "Delta"),
  symbol = unescape_html(c("&alpha;", "&beta;", "&gamma;", "&delta;")),
  value = c(0, 5, 10, 20)
)

If you have to use raw HTML, you can build an HTML string in the row details function, and enable raw HTML rendering for the row details column: https://glin.github.io/reactable/articles/examples.html#expandable-row-details

reactable(
  MASS::Cars93,
  details = colDef(
    details = function(index) {
      sprintf("Details for <b>row %s</b>", index)
    },
    html = TRUE
  )
)

If you want to mix raw HTML with htmltools tags, you may also be able to convert the htmltools tags into a raw HTML string, and then render raw HTML:

library(htmltools)

html <- div(
  p("Letter symbol: ", HTML("&alpha;"))
)

raw_html <- as.character(html)

cat(raw_html)
## <div>
##  <p>
##     Letter symbol: 
##     &alpha;
##   </p>
## </div>
reactable(
  MASS::Cars93,
  details = colDef(
    details = function(index) {
      html <- div(
        p("Letter symbol: ", HTML("&alpha;"))
      )
      as.character(html)
    },
    html = TRUE
  )
)

I'm hoping to tackle this issue soon, since it breaks rendering for htmltools elements in some packages, like a few Shiny inputs. If it can't be fixed soon, I'll add a note to the docs.

glin avatar Nov 14 '20 20:11 glin

My issue may be related to this, i.e., rendering raw html.

The modified dataset: library(tidyverse) library(reactable)

df <- MASS::Cars93 %>% mutate(Model = paste0("", Model, ""), Type = paste0("", Type, ""))

Here is code that produces the effect I want:

reactable( df, columns = list(Model = colDef(html = TRUE), Type = colDef(html = TRUE)), details = function(index) { make <- df$Manufacturer[[index]] detail_df <- df %>% filter(Manufacturer == make) reactable( detail_df, columns = list(Model = colDef(html = TRUE), Type = colDef(html = TRUE))) } )

However, in my use case, the user can select the columns they want to see, so the list required by colDef() cannot be created ahead of time. I've tried creating the list dynamically, but without success so far. So I tried using defaultColDef = colDef(html - TRUE) which works for the parent, but produces only "[object Object] for each expanded detail row. (Note: including defaultColDef = colDef(html - TRUE) in the inner table doesn't seem to matter.) Here is the code:

reactable( df, defaultColDef = colDef(html = TRUE), details = function(index) { make <- df$Manufacturer[[index]] detail_df <- df %>% filter(Manufacturer == make) reactable(detail_df) } )

Edit: I meant to mention that I did try other values, namely defaultColDef = colDef(align = "center") which worked, confirming (perhaps) that to the issue is related specifically to html rendering.

reactable( df, defaultColDef = colDef(align = "center"), details = function(index) { make <- df$Manufacturer[[index]] detail_df <- df %>% filter(Manufacturer == make) reactable( detail_df, defaultColDef = colDef(align = "center") ) } )

Any advice or workarounds would be greatly appreciated! Great package!

mbjohnsonmn avatar Apr 27 '22 15:04 mbjohnsonmn