Search code examples
rreactable

How to conditionally format multiple columns and specific rows in reactable table?


I have some example data below where I'm trying to conditionally format all numeric columns in my reactable table but only for row 1. For example, I'd like to use a traffic light system and assign the cell backgrounds in row 1 as green, red or orange if the value is more than that in row 2, within 10% of row 2 or more than 10% below the values in row 2, respectively.

library(reactable)

set.seed(1)
name <- c("Player A", "Player B", "Team")
var1 <- rnorm(3, 1000, 200)
var2 <- rnorm(3, 1000, 200)
var3 <- rnorm(3, 1000, 200)

d <- data.frame(name, var1, var2, var3)

reactable(d,
          defaultColDef = colDef(
            align = "center",
            format = colFormat(digits = 1)
          )
          )

I'm familiar with something like the below code, but I'm not sure how I can apply something like this to only specific rows and for multiple columns without repeating the function several times in colDef().

reactable(sleep[1:6, ], columns = list(
  extra = colDef(
    style = function(value) {
      if (value > 0) {
        color <- "#008000"
      } else if (value < 0) {
        color <- "#e00000"
      } else {
        color <- "#777"
      }
      list(color = color, fontWeight = "bold")
    }
  )
))

Solution

  • Here is one option to achieve your desired result which uses the row index passed to the style function as a second argument to access the value of the successor row. Additionally I use lapply to create the colDefs for a list of column names.

    library(reactable)
    
    style_fun <- function(.data, colname) {
      function(value, index) {
        if (index < 2) {
          successor_value <- .data[[colname]][index + 1]
          if (value > successor_value) {
            color <- "#008000"
          } else if (value >= .9 * successor_value) {
            color <- "orange"
          } else if (value < .9 * successor_value) {
            color <- "#e00000"
          } else {
            color <- "#777"
          }
          return(
            list(color = color, fontWeight = "bold")
          )
        } else {
          return(NULL)
        }
      }
    }
    
    cols <- names(d)[-1]
    col_defs <- lapply(
      cols, function(x)
      colDef(
        style = style_fun(d, x)
      )
    ) 
    names(col_defs) <- cols
    
    reactable(d,
      defaultColDef = colDef(
        align = "center",
        format = colFormat(digits = 1)
      ),
      columns = col_defs
    )
    

    enter image description here