Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filtering multiple sub-tables with a single filter widget #346

Open
ilevantis opened this issue Nov 3, 2023 · 2 comments
Open

Filtering multiple sub-tables with a single filter widget #346

ilevantis opened this issue Nov 3, 2023 · 2 comments
Labels
enhancement New feature or request

Comments

@ilevantis
Copy link

ilevantis commented Nov 3, 2023

Hello and thanks for the great work on this package! It works fantastically well.

I was trying to improve an exisitng Rmarkdown HTML report by including interactive filtering for a table which contains a sub-table nested in the detail section of each row.

Here is some example code:

library(dplyr)
library(reactable)
library(htmltools)

df <- iris

summary_df <- data.frame(Species = c('setosa', 'versicolor', 'virginica'),
                         fun.fact = c('dark purple', 'medium purple', 'stripy purple'))


subtableFilter <- function(table_ids, col, min, max, value = NULL, step = NULL) {
  
  value <- if (!is.null(value)) value else min
  
  jsfunc <- paste0(
    paste0("try{Reactable.setFilter('", table_ids, "', '",col,"', this.value);}catch{}"),
    collapse = ";"
  )
  
  htmltools::tags$input(
    id = "subtable-filter-input",
    type = "number",
    min = min,
    max = max,
    step = step,
    value = value,
    oninput = jsfunc
  )
}

filterMinValue <- JS("function(rows, columnId, filterValue) {
  return rows.filter(function(row) {
    return row.values[columnId] >= filterValue
  })
}")


render_subtable <- function(mini_df, table_id) {
  htmlwidgets::onRender(
    reactable(mini_df,
      columns = list(Sepal.Width = colDef(filterMethod=filterMinValue)),
      elementId = table_id
    ),
    sprintf("
    function(el, x) {
      Reactable.setFilter('%s', 'Sepal.Width', 3.0);
    }
    ", table_id)
  )
}

htmltools::tagList(
  subtableFilter(paste0('subtable-', seq_along(summary_df$Species)), 'Sepal.Width', 0.0, 100.0, value = 3.0, step = 0.1),
  reactable(summary_df,
    pagination = FALSE,
    details = function(idx){
      mini_df <- df %>%
        filter(Species == summary_df$Species[idx]) %>%
        arrange(desc(Sepal.Width))
      render_subtable(mini_df, sprintf("subtable-%d",idx))
    }
  )
)

With this code, the filter widget successfully filters any subtables that are currently expanded as the value in the filter is changed. However my attempt to filter a subtable as soon as it is expanded using htmlwidgets::onRender and a hardcoded default value does not seem to work. I'm not sure how to successfully achieve this though, does anyone have any ideas?

Secondly, assuming I can get the filtering to work on a subtable as soon as it expands, how would I go about referencing the current value of the filter widget rather than a hardcoded value? I don't know enough about JS and React to properly understand how it all fits together, but from some googling it seems I would maybe need to make use of the React useRef hook?

@glin
Copy link
Owner

glin commented Nov 6, 2023

Hi, I can't think of any good way to do either unfortunately. In general, nested subtables are really hard to do state-ful things with because they're only created on details expansion, and then destroyed on details collapse or page change. Getting filter state to persist with these changes will be really tough.

For the first issue, it's probably another good reason to add a default table filter state (related: #326), so you don't have to hack it in via JavaScript. Using htmlwidgets::onRender() would usually work, but I think it doesn't work here because it's not supported by how reactable renders custom HTML.

For now, I could only suggest figuring out a different way to achieve this that doesn't use nested subtables, e.g. a Shiny app with a selectable table, where row selections will render the details table in a separate panel. Of course this is completely different being a Shiny app rather than a static doc, but just one idea that I think would actually work.

@glin glin added the enhancement New feature or request label Nov 6, 2023
@ilevantis
Copy link
Author

Using htmlwidgets::onRender() would usually work, but I think it doesn't work here because it's not supported by how reactable renders custom HTML.

Fair enough. This was a nice-to -have for me not a crucial feature so no worries that I've hit up against a limitation.

I'm sure there would be a way to restructure things to not use nested tables if it was crucial.

Thanks for taking a look at my use case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants