plotly / plotly.R

An interactive graphing library for R
https://plotly-r.com
Other
2.55k stars 625 forks source link

`selectedpoints` does not function properly when a vector is passed using `plotlyProxy` & `"restyle"`. #2237

Open JacobBumgarner opened 1 year ago

JacobBumgarner commented 1 year ago

Bug/Unexpected Behavior

When updating a scatter plot's selectedpoints in a Shiny server using "restyle" on a plotlyProxy, single value vectors don't update the graph. E.g., passing c(1) to selectedpoints won't update the graph.

However, if we duplicate this value, e.g., c(1, 1) and pass it to selectedpoints, the 1 point will be selected.

Reproducible example:

# selected points single value incorrect behavior
library(shiny)

ui <- fluidPage(fluidRow(plotly::plotlyOutput("graph"),
                         DT::DTOutput("table")))

server <- function(input, output, session) {
  output$table <- DT::renderDT({
    cars
  })

  output$graph <- plotly::renderPlotly({
    plotly::plot_ly(
      cars,
      x = ~ speed,
      y = ~ dist,
      selected = list(marker = list(color = "red")),
      type = "scatter",
      mode = "markers",
      source = "graph"
    ) %>%
      plotly::layout(dragmode = "select",
                     clickmode = "event+select") %>%
      plotly::event_register("plotly_selected")
  })

  plot_proxy <- plotly::plotlyProxy("graph")
  table_proxy <- DT::dataTableProxy("table")

  selected <-
    reactive({
      plotly::event_data("plotly_selected", source = "graph")
    })

  observeEvent(selected(), {
    selected <- selected()$pointNumber + 1
    table_proxy %>% DT::selectRows(selected)
  })

  observeEvent(input$table_cell_clicked, {
    selected_rows <- input$table_rows_selected - 1

   #### comment this conditional to see the bug ####
    if (length(selected_rows) == 1) {
      selected_rows <- rep(selected_rows, 2)
    }
   #### end comment this conditional to see the bug ####

   plotly::plotlyProxyInvoke(
      plotly::plotlyProxy("graph", session),
      "restyle",
      list(selectedpoints = list(selected_rows)),
      0
    )
  })

}

shinyApp(ui, server)
JacobBumgarner commented 1 year ago

After further experimentation, it appears that the required argument type is a matrix.

The following correction to the selected() observer event fixes this.

# update the plot based on table
  observeEvent(input$table_cell_clicked, {
    selected_rows <- input$table_rows_selected - 1
    selected_rows <- t(matrix(selected_rows))

    plotly::plotlyProxyInvoke(
      plotly::plotlyProxy("graph", session),
      "restyle",
      list(selectedpoints = selected_rows),  # arg wants a row-major matrix
      0  # this number represents the plot trace. No arg updates all traces
    )
  })

Regardless, this should probably be documented somewhere for future users.