panel icon indicating copy to clipboard operation
panel copied to clipboard

Click event only generated when clicking in specific areas of Bar graph

Open francisco-pjbank opened this issue 10 months ago • 4 comments

This issue came out from a discussion on panel discourse, link here. I will reproduce below the main parts of it.

There is an integration problem between panel and plotly when trying to receive click events upon clicking on the bar graph using plotly. The event is only triggered when clicking in the space between the bars and not inside the Bar. if sizing_mode is not used (check the MRE below), situation gets even worse. Same reasoning holds true if orientation of the Bar changes from horizontal to vertical.

Relevant software version info

pandas 1.5.3 panel 1.2.0 plotly 5.15.0 plotly-express 0.4.1 python 3.9.17 ubuntu 20.04

Description of expected behavior and the observed behavior

Click events should only occur when clicking inside the Bar independent of the size mode or orientation used. Currently events are generated when clicking close to the Bar in the space between bars. If no size mode is used or orientation is changed, than click events are not triggered or occur randomly.

Complete, minimal, self-contained example code that reproduces the issue

import plotly.express as px
import pandas as pd
import panel as pn
from panel.template.react import ReactTemplate

df = pd.DataFrame(data = {'month':['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],'value':[10,15,20,5,12,6,21,8,9,3,11,17]})

fig=px.bar(x=df['month'], y=df['value'])

def on_click(event):
    if event.name == 'click_data':
        if event.obj is not None:
            print("on_click", event.obj.click_data)
            print("on_click old", event.old)
            print("on_click new", event.new)


plot_panel = pn.pane.Plotly(fig, 
                            config={"responsive": True},
                            sizing_mode="scale_both"
                            )

plot_panel.param.watch(on_click, ["click_data"],onlychanged=False)

pn.Column(plot_panel).servable()

Stack traceback and/or browser JavaScript console output

N/A

Screenshots or screencasts of the bug in action

It is pretty easy to reproduce so I think a screenshot is not necessary.

francisco-pjbank avatar Aug 10 '23 13:08 francisco-pjbank

I've been able to reproduce the issue. See the mentioned discourse discussion.

MarcSkovMadsen avatar Aug 10 '23 14:08 MarcSkovMadsen

Here is another issue which also has issues with mouse clicks. It might be related.

https://github.com/holoviz/panel/issues/5195

MarcSkovMadsen avatar Aug 11 '23 02:08 MarcSkovMadsen

Hi I've been hitting the exact same problem and been bashing my head all day trying to get it to work. I think the root cause lies within Plotly library not registering click events correctly within the ShadowDOM -> https://github.com/plotly/plotly.js/issues/6108

I have a workaround for now which was to download the plotly.js file from https://github.com/plotly/plotly.js/blob/v2.25.2/dist/plotly.js and then comment out the lines 7914-7918:

module.exports = function click(gd, evt, subplot) {
  var annotationsDone = Registry.getComponentMethod('annotations', 'onClick')(gd, gd._hoverdata);

  // fallback to fail-safe in case the plot type's hover method doesn't pass the subplot.
  // Ternary, for example, didn't, but it was caught because tested.
// !!! COMMENT THIS OUT to prevent retrigger of hover() on click !!!
//   if (subplot !== undefined) {
//     // The true flag at the end causes it to re-run the hover computation to figure out *which*
//     // point is being clicked. Without this, clicking is somewhat unreliable.
//     hover(gd, evt, subplot, true);
//   }

  function emitClick() {
    gd.emit('plotly_click', {
      points: gd._hoverdata,
      event: evt
    });
  }
  if (gd._hoverdata && evt && evt.target) {
    if (annotationsDone && annotationsDone.then) {
      annotationsDone.then(emitClick);
    } else emitClick();

    // why do we get a double event without this???
    if (evt.stopImmediatePropagation) evt.stopImmediatePropagation();
  }
};

Then I added to my app with pn.extension("plotly", js_files={'plotly_patched': 'assets/plotly-2.25.2_patched.js'}) (and serving that assets directory with panel serve app.py --show --static-dirs assets=./assets) and the clicks on plots seem work as expected again. I have no idea if this will cause unintended consequences with more complex plots and other functionality but it allowed me to proceed with what I was working on.

sblowers avatar Jan 22 '24 14:01 sblowers

Thanks for tracking this down @sblowers, I'll try to see if there is a way to this from within the panel.js bundle.

philippjfr avatar Jan 23 '24 13:01 philippjfr