glin / reactable

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

`onClick` function not triggering when selecting row with radio button #311

Open Teebusch opened 1 year ago

Teebusch commented 1 year ago

Hi,

The custom onClick function is not called when I click the radio button that is added by selection = "single", or anywhere else in the column with the radio button. When I click any other column, the onClick function is called as expected.

I assume the selection column either stops the click event from propagating, or the event listener that calls the onClick function is not registered for that particular column.

Is there a workaround for this?

Here's a reproducible example where I'm trying to use the onClick function to differentiate between the user clicking a row to select it and programmatic updates of the selected row. Clicking the radio buttons updates the selected row but does not update the "last clicked" text (input$clicked_id).

library(shiny)
library(reactable)

ui <- fluidPage(
    shinyjs::useShinyjs(),
    actionButton("btn", "update selection programmatically"),
    div("last selected:", textOutput("txt_sel", inline = TRUE)),
    div("last clicked:", textOutput("txt_clk", inline = TRUE)),
    reactableOutput("tbl")
)

server <- function(input, output) {

    observeEvent(input$btn, {
      i <- if (is.null(selected_id())) 1 else 1 + (selected_id() %% 10)
      reactable::updateReactable("tbl", selected = i)
    })

    selected_id <- reactive({
      reactable::getReactableState("tbl", "selected")
    })

    output$txt_sel <- renderText({ selected_id() })
    output$txt_clk <- renderText({ input$clicked_id })

    output$tbl <- renderReactable({
      reactable::reactable(
        data = head(mtcars, 10),
        selection = "single",
        onClick = JS("
             function(row, column){
               row.toggleRowSelected()
               Shiny.setInputValue('clicked_id', row.index + 1)
             }
          ")
      )
    })
}

shinyApp(ui = ui, server = server)

This is somewhat related to #298

glin commented 1 year ago

Hi, you're correct, there's no custom onClick for the row selection column. That's intentional, as the click action for the row selection cells is reserved for row selection, and the row selection column isn't a typical data column anyway. I can see the use case for allowing a click to both select the row and do something else, but it's already possible to use the row selection event for that, and it seems very niche to try to differentiate between user inputs vs. programmatic Shiny updates. There are also really bad accessibility issues with the custom onClick action already, so I'm hesitant to expand that functionality more.

So I'd recommend looking for an alternative way to achieve what you'd like to do. Maybe differentiate between user input vs. programmatic updates on the Shiny side? Here's one possibility I thought of, but it's not totally equivalent. I'll come back if I can think of a better way to do this:

library(shiny)
library(reactable)

ui <- fluidPage(
  shinyjs::useShinyjs(),
  actionButton("btn", "update selection programmatically"),
  div("last selected (programmatic update):", textOutput("txt_sel", inline = TRUE)),
  div("last selected:", textOutput("txt_clk", inline = TRUE)),
  reactableOutput("tbl")
)

server <- function(input, output) {
  selected_id <- reactiveVal()
  shiny_selected_id <- reactiveVal()

  observeEvent(input$btn, {
    i <- if (is.null(selected_id())) 1 else 1 + (selected_id() %% 10)
    reactable::updateReactable("tbl", selected = i)
    shiny_selected_id(i)
  })

  selected_id <- reactive({
    reactable::getReactableState("tbl", "selected")
  })

  output$txt_sel <- renderText({ shiny_selected_id() })
  output$txt_clk <- renderText({ selected_id() })

  output$tbl <- renderReactable({
    reactable::reactable(
      data = head(mtcars, 10),
      selection = "single",
      onClick = "select"
    )
  })
}

shinyApp(ui = ui, server = server)