Search code examples
rshinyreactable

How to display a dynamic sum of filtered scores in a reactable footer?


I would like to display a data frame with scores for individuals using the reactable package in an R dashboard. The footer row should show the sum of the scores of the filtered options. In the current version, the sum of the entire data frame (sum = 246) is displayed statically. How can I make the sum always refer to the filtered subset?

library(shiny)
library(shinydashboard)
library(reactable)

d <- data.frame(
  name = c("Frank", "Emma", "Kurt", "Johanna", "Anna", "Ben", "Chris", "David", "Eva", "Felix", "Gina", "Hannah", "Iris", "Jack", "Karen", "Leo", "Mia", "Nina", "Omar", "Paul"),
  team = c("A", "A", "B", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B"),
  score = c(12, 15, 13, 13, 14, 11, 10, 16, 9, 8, 17, 14, 12, 13, 15, 16, 11, 10, 9, 8)
)

ui <- dashboardPage(
  dashboardHeader(title = "Test"),
  dashboardSidebar(disable = TRUE),
  dashboardBody(
    fluidRow(
      box(width = 12,
          title = "Test",
          reactableOutput("table"))
    )
  )
)

server <- function(input, output, session) {
  output$table <- renderReactable({
    reactable(
      d,
      filterable = TRUE,
      columns = list(
        score = colDef(
          footer = function(values) sum(values, na.rm = TRUE)
        ),
        name = colDef(footer = "Total")
      ),
      defaultSorted = "score",
      defaultSortOrder = "desc",
      defaultPageSize = 5
    )
  })
}

shinyApp(ui, server)

Solution

  • You can use a custom JavaScript render function as shown below.

    library(reactable)
    
    d <- data.frame(
      name = c("Frank", "Emma", "Kurt", "Johanna", "Anna", "Ben", "Chris", "David", "Eva", "Felix", "Gina", "Hannah", "Iris", "Jack", "Karen", "Leo", "Mia", "Nina", "Omar", "Paul"),
      team = c("A", "A", "B", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B", "A", "B"),
      score = c(12, 15, 13, 13, 14, 11, 10, 16, 9, 8, 17, 14, 12, 13, 15, 16, 11, 10, 9, 8)
    )
    
    reactable(
      d,
      filterable = TRUE,
      columns = list(
        score = colDef(footer = JS(
          c(
            "function(column, state) {",
            "  let total = 0",
            "  state.sortedData.forEach(function(row) {",
            "    total += row[column.id] ",
            "  })",
            "  return total",
            "}"
          )
        )),
        name = colDef(footer = "Total")
      ),
      defaultSorted = "score",
      defaultSortOrder = "desc",
      defaultPageSize = 5
    )
    
    

    enter image description here