glin / reactable

Interactive data tables for R
https://glin.github.io/reactable
Other
627 stars 80 forks source link

color code of cell and filter in reactable do not work as what it should be #277

Closed xiying2014 closed 1 year ago

xiying2014 commented 1 year ago

In below code, we created a reactable table. The cell of table is color coded using JS and we enable filter in reactable table.

library(magrittr)

df <- data.frame(iris)

df %>%
  reactable(
    filterable = TRUE,
    bordered = TRUE,
    wrap = FALSE,
    # defaultPageSize = 1000,
    compact = TRUE,
    style = list(fontSize = "12px"),
    fullWidth = FALSE,
    defaultColDef = colDef(
      minWidth = 150,
      sortNALast = TRUE,
      resizable = TRUE,
      style = JS(
        "function(rowInfo, colInfo) {
        var value = rowInfo.row[colInfo.id]
        if (value == 5.1) {var background = '#2ca25f'}
        else if (value == 3.5) {var background = '#e34a33'}
        else if (value == 1.4) {var background = '#99d8c9'}
        else {var background = '#fffff00'}

        return {background: background, color: background}
        }"
      )
    )
    )

However, there are two issues:

  1. Below screenshot shows what reactable table looks like in page 1 (it shows at the bottom right hand of table). image After we click to page 2 and click back to page 1. Color code of page 1 uses what page 2 shows. Only when we refresh the table, page 1 works as what it should be. image image
  2. color code and filter do not work as what it should be. Specifically, I filter Sepal.Length is 5.1. Cell is not color code correctly. Only 3.5 should be color coded as red and 1.4 be color coded as mint. However, we have a few cells color coded wrong and they are highlighted in yellow. image

Please could you help with above issues. Many thanks!

glin commented 1 year ago

Wow, that's a very odd and interesting issue. So there are two things going on here. First is that the last background color, #fffff00 is an invalid hex color with 7 digits. If you change it to a valid hex color with 6 digits like #fffff0, the table works as expected with both issues:

library(magrittr)

df <- data.frame(iris)

df %>%
  reactable(
    defaultColDef = colDef(
      minWidth = 150,
      sortNALast = TRUE,
      resizable = TRUE,
      filterable = TRUE,
      style = JS(
        "function(rowInfo, colInfo) {
        var value = rowInfo.row[colInfo.id]
        if (value == 5.1) {var background = '#2ca25f'}
        else if (value == 3.5) {var background = '#e34a33'}
        else if (value == 1.4) {var background = '#99d8c9'}
        else {var background = '#fffff0'}
        console.log({ value, background })

        return {background: background, color: background}
        }"
      )
    )
  )

Second is that invalid CSS property values like the 7-digit hex color are just ignored, and that's kind of a default browser behavior mixed with how reactable does rendering. If you try to add a style with an invalid CSS property value, browsers will just silently ignore the invalid value and continue using the existing style. There's no way to easily find out that your invalid CSS was ignored unless you go digging in browser devtools. This wouldn't be an issue if reactable rendered a completely new element for every cell that changed, and the styles got reset, but reactable does reuse cell elements when possible for faster table rendering. So with these two behaviors combined, whenever a table cell changes to a cell with the invalid color, the previous color is left intact until the next time it changes to a valid color.

This behavior definitely feels buggy and confusing, but there's unfortunately not any great solution I can think of to address it. reactable could potentially validate CSS colors and try to handle it in some way, like throwing an error or resetting the color, but it's way too hard (and maybe impossible?) to validate all CSS properties, especially since some invalid CSS properties are intentionally used as fallbacks for older browsers.

xiying2014 commented 1 year ago

Really appreciate your help, Greg! It works now!

Wow, that's a very odd and interesting issue. So there are two things going on here. First is that the last background color, #fffff00 is an invalid hex color with 7 digits. If you change it to a valid hex color with 6 digits like #fffff0, the table works as expected with both issues:

library(magrittr)

df <- data.frame(iris)

df %>%
  reactable(
    defaultColDef = colDef(
      minWidth = 150,
      sortNALast = TRUE,
      resizable = TRUE,
      filterable = TRUE,
      style = JS(
        "function(rowInfo, colInfo) {
        var value = rowInfo.row[colInfo.id]
        if (value == 5.1) {var background = '#2ca25f'}
        else if (value == 3.5) {var background = '#e34a33'}
        else if (value == 1.4) {var background = '#99d8c9'}
        else {var background = '#fffff0'}
        console.log({ value, background })

        return {background: background, color: background}
        }"
      )
    )
  )

Second is that invalid CSS property values like the 7-digit hex color are just ignored, and that's kind of a default browser behavior mixed with how reactable does rendering. If you try to add a style with an invalid CSS property value, browsers will just silently ignore the invalid value and continue using the existing style. There's no way to easily find out that your invalid CSS was ignored unless you go digging in browser devtools. This wouldn't be an issue if reactable rendered a completely new element for every cell that changed, and the styles got reset, but reactable does reuse cell elements when possible for faster table rendering. So with these two behaviors combined, whenever a table cell changes to a cell with the invalid color, the previous color is left intact until the next time it changes to a valid color.

This behavior definitely feels buggy and confusing, but there's unfortunately not any great solution I can think of to address it. reactable could potentially validate CSS colors and try to handle it in some way, like throwing an error or resetting the color, but it's way too hard (and maybe impossible?) to validate all CSS properties, especially since some invalid CSS properties are intentionally used as fallbacks for older browsers.