Plotly-js not firing plotly_events in Streamlit iframe

Hello,

I am trying to create a custom Streamlit component using TypeScript only (I cannot use React as it has a bit different plotly chart style than plotlyjs).

I have created the custom component:

import { Streamlit, RenderData } from "streamlit-component-lib"
import Plotly from 'plotly.js-dist-min'
import * as d3 from 'd3'

const m_plot_div = document.createElement("div")
const m_plot = document.body.appendChild(m_plot_div)

m_plot.setAttribute("id","m_plot");

function onRender(event: Event): void {
  const data = (event as CustomEvent<RenderData>).detail
  var j = JSON.parse(data.args.props)
  
  Plotly.newPlot(
    m_plot.id,
    j.data,
    j.layout
  )


  document.getElementById(m_plot.id)?.addEventListener(
    "click",
    (e:Event) => console.log("1a")
  );
  
    document.getElementById(m_plot.id)?.addEventListener(
    "plotly_click",
    (e:Event) => console.log("1b")
  );


  d3.select(".plotly").on('click', function(this, event) {
    console.log("2a");
  })

  d3.select("#m_plot").on('plotly_click', function(this, event) {
    console.log("2b");
  })

  Streamlit.setFrameHeight()
}

Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender)

Streamlit.setComponentReady()

Streamlit.setFrameHeight()

And then displayed it in Python as Streamlit expects.

The chart looks fine. However the issue is I am unable to catch plotly_click events. They are simply not firing. Streamlit created the plotly chart in an iframe (I do not know whether the is an issue or not)

I should see “1b” or “2b” displayed in the console, but I only see “1a” and “2a”.

In React we can create the plot and assign the onclick callback as react-plotly plot already has onClick argument. plotly-js does not allow anything like that. The React-based approach works fine. Why do I see no events using plotly-js only?

Could anyone please point me in the right direction?

Thanks a lot!

RJ

Can someone move this thread to plotly-js, please? I created it here by mistake.

1 Like

Well actually the React-based approach does not fire the plotly_click at all.

import {
  Streamlit,
  StreamlitComponentBase,
  withStreamlitConnection,
} from "streamlit-component-lib"
import React, { ReactNode } from "react"
import Plot from 'react-plotly.js';
import Plotly from 'plotly.js';

class StreamlitPlotlyMyReact extends StreamlitComponentBase {
  public render = (): ReactNode => {
    const plot_obj = JSON.parse(this.props.args["plot_obj"]);
   
    return (
      <Plot
        data={plot_obj.data}
        layout={plot_obj.layout}
        onClick={this.plotlyEventHandler}
      />
    )

  }

  private plotlyEventHandler = (data: any) => {
    console.log('>>> data', data);
  }
}

export default withStreamlitConnection(StreamlitPlotlyMyReact)

Ok, the problem was in the fact I misunderstood TypeScript errors and did not attach the event listener to the correct element.

The trick is to first declare m_plot as

const m_plot: any = document.body.appendChild(m_plot_div)

and then you can simply

m_plot.on('plotly_click', function(e: any) { ... } )

Easy as that.

Rookie mistake. Case closed.