glin / reactable

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

Crosstalk interface - how to keep selected elements visible/selected? #267

Open tbrittoborges opened 2 years ago

tbrittoborges commented 2 years ago

Using reactable with plotly::highlight and crosstalk::SharedData as input works like a charm:

library(reactable)
library(plotly)
library(crosstalk)

shared <- SharedData$new(iris, function(data) rownames(data))

bscols(
  shared %>% 
    plot_ly(x = ~Sepal.Length, y = ~Sepal.Width, color = ~Species) %>% 
    add_markers(alpha = 0.5) %>% 
    highlight(color = 'red'),
  reactable(shared, selection = 'multiple')
)

Selection from reactable -> highlights plotly:

image

Highlight from plotly -> filter reactable:

Table is filter but row is not selected: image

Highlight from plotly and then select from filtered table:

image

Once a row is selected, the table is unfiltered and the selected element is lost. The element is still selected. Is there a way to improve this integration? Ideally, keep the selected entries at the top of the table.

Session info

R version 4.0.5 (2021-03-31) Platform: x86_64-pc-linux-gnu (64-bit) Running under: Debian GNU/Linux 10 (buster) Matrix products: default BLAS: /beegfs/biosw/R/4.0.5_deb10/lib/R/lib/libRblas.so LAPACK: /beegfs/biosw/R/4.0.5_deb10/lib/R/lib/libRlapack.so locale: [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 [6] LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C attached base packages: [1] stats graphics grDevices utils datasets methods base other attached packages: [1] crosstalk_1.2.0 plotly_4.10.0 ggplot2_3.3.6 reactable_0.2.3.9000 loaded via a namespace (and not attached): [1] Rcpp_1.0.9 RColorBrewer_1.1-3 later_1.3.0 pillar_1.8.1 compiler_4.0.5 tools_4.0.5 digest_0.6.29 [8] jsonlite_1.8.0 lifecycle_1.0.1 tibble_3.1.8 gtable_0.3.0 viridisLite_0.4.1 pkgconfig_2.0.3 rlang_1.0.4 [15] shiny_1.7.1 cli_3.3.0 DBI_1.1.2 rstudioapi_0.13 yaml_2.3.5 fastmap_1.1.0 reactR_0.4.4 [22] withr_2.5.0 dplyr_1.0.9 httr_1.4.3 generics_0.1.3 vctrs_0.4.1 htmlwidgets_1.5.4 grid_4.0.5 [29] tidyselect_1.1.2 glue_1.6.2 data.table_1.14.2 R6_2.5.1 fansi_1.0.3 farver_2.1.1 purrr_0.3.4 [36] tidyr_1.2.0 magrittr_2.0.3 ellipsis_0.3.2 promises_1.2.0.1 scales_1.2.1 htmltools_0.5.2 assertthat_0.2.1 [43] xtable_1.8-4 mime_0.12 colorspace_2.0-3 httpuv_1.6.5 utf8_1.2.2 lazyeval_0.2.2 munsell_0.5.0

glin commented 2 years ago

I remember finding this weird behavior as well while adding the Crosstalk integration way back. There's a complicated reason why it works like this, and I unfortunately have no ideas for a good solution.

This is also kind of an issue with Crosstalk widgets in general, and happens with DT and Plotly, or DT and Leaflet on the Crosstalk home page: https://rstudio.github.io/crosstalk/index.html (filter the Leaflet map and then select a row in the DT table)

Since the issue is complicated, here's what happening behind the scenes in Crosstalk:

  1. Select a row in reactable: reactable performs a Crosstalk selection (linked brushing) on Plotly that highlights a point.
  2. Select a point in Plotly: Plotly clears the previous selection and performs a Crosstalk selection on reactable that filters rows.
  3. Select a row in reactable: reactable clears the previous Crosstalk selection and performs a Crosstalk selection on Plotly. Since the previous selection that filtered the table was cleared, the filtering is reset and the selected row potentially gets lost on a different page.

The main problem is that the Crosstalk selection behaviors aren't really congruent when coming in and out of a table. Crosstalk selections from the table are done by selecting table rows, but Crosstalk selections onto the table will actually filter rows rather than select rows.

Package authors can interpret Crosstalk selections however they want, but this was how DT worked, and I thought it made a lot of sense. Most users would probably expect selecting Plotly/Leaflet points to filter the table as well. But unfortunately, it can also cause weird behaviors like this where resetting an existing Crosstalk selection will appear to reset a completely different state. I.e., selecting a row won't reset a previous row selection, but will reset a previous row filter.

Thinking through a couple solutions:

tbrittoborges commented 2 years ago

Hi, @glin. Thank you for the detailed response. I understand this is a complicated issue, given multiple users expect different behaviors. What do you think about allowing users to configure the selection columns? We could then define the sortable and default sort value. This would require breaking the current behavior, by default.

I could help with the implementation, if necessary.