React with bokeh extension

Hi,
I work at a finance firm. My firm is looking to adopt panel as a dashboarding technology. We have some in house tools for interactive visualizations of table and charts(which are wrapper over highcharts) for jupyter environment which are react based.

Tasked with integrating panel with our in house react based tools, I set out to build some bokeh extensions. I came across panel_highcharts and react related issues such as this and this.

I set out to create my own react based renderers and I was successful as well to an extent. But I realized that the CSS of bokeh visualizations is extremely poor and almost everything is absolutely positioned.

Consider DevelopingCustomModels.ipynb(unable to add link as only two links are allowed) for example which was my starting point. Now in chart.ts, if I try to something like

        setTimeout(() => {
            element.innerHTML = '<div style="width: 500px; height: 500px; background-color: red"></div>'
            console.log(win, props);
        }, 5000)

it is not able to determine the positions correctly and messes up the layout. The output is something like this:

image

The workaround I am forced to use as of now is to determine the width and height of my element and render a div of that size inside the bokeh element.

How it effects me is, when I try render a react component in element, it is not able to figure out the width and height of the component as it takes time for the component to be mounted.

So I have couple of things to ask:

  1. Is there a way I can make render asynchronous so I can wait for my component to be mounted?
  2. Is there a documentation where I can find some more guidelines on what all things these extensions have to offer? For example, I was able to figure out what js_skip does only after diving into panel code.

Also, I would be really interested in working on creating examples of react based extensions for panel. I will clean my code for the open source world and share what I have got sometime next month.

2 Likes

Would love to see some more examples. However concretely I’d really have to see what specifically you have tried and found wasn’t working for you, e.g. I just tried your setTimeout example without issue:

from panel.reactive import ReactiveHTML

class Test(ReactiveHTML):
    
    _template = "<div id='element'></div>"

    _scripts = {
        'render': """
        setTimeout(() => {
            element.innerHTML = '<div style="width: 500px; height: 500px; background-color: red"></div>'
            console.log(win, props);
        }, 5000)        
        """
    }

t = Test()


pn.Row(slider, t)

Is there a way I can make render asynchronous so I can wait for my component to be mounted?

Good question, I’ll have to play around but I think you should be able to wait on a Promise easily enough. A concrete example would help me a lot here.

Is there a documentation where I can find some more guidelines on what all things these extensions have to offer? For example, I was able to figure out what js_skip does only after diving into panel code.

I think there’s tons of dev docs still missing. A list of things that haven’t been covered would be a helpful starting point. We can collaborate on that in an issue.

Thanks a lot for your response.

I am yet to experiment with it. Let me give it a shot and get back.

Just reverse slider and t in your example.

import panel.widgets as pnw
slider  = pnw.IntSlider(value=10, start=1, end=60)


from panel.reactive import ReactiveHTML

class Test(ReactiveHTML):
    
    _template = "<div id='element'></div>"

    _scripts = {
        'render': """
        setTimeout(() => {
            element.innerHTML = '<div style="width: 500px; height: 500px; background-color: red"></div>'
            console.log(win, props);
        }, 5000)        
        """
    }

t = Test()


pn.Row(t, slider)

image

The issue is, the slider here is absolutely positioned based on the previous component which is not a great way to write CSS I feel. Since this might require some major changes, I was looking for that async thing as I thought that might work good enough for me. Basically what I would like is slider should wait for a component before that, the Test to render completely and should determine the position accordingly.

I assumed that because the following code works perfectly fine:

import panel.widgets as pnw
slider  = pnw.IntSlider(value=10, start=1, end=60)


from panel.reactive import ReactiveHTML

class Test(ReactiveHTML):
    
    _template = "<div id='element'></div>"

    _scripts = {
        'render': """
            element.innerHTML = '<div style="width: 500px; height: 500px; background-color: red"></div>'      
        """
    }

t = Test()


pn.Row(t, slider)

Sure will log an issue. While building my extensions, I have encountered several things which I feel can be documented to easily build extensions.

Ah okay, in that case you can invalidate the layout after rendering, which should make sure the parent correctly allocates space:

    _scripts = {
        'render': """
        setTimeout(() => {
            element.innerHTML = '<div style="width: 500px; height: 500px; background-color: red"></div>'
            view.invalidate_layout()
        }, 5000)        
        """

Oh great! Pretty much what I needed. This should work for me. Will let you know if I find any issues.

Thank you so much!

2 Likes

Hi @govinda18

Welcome to the community.

Can see we share some common interests in supporting traders using react, panel and highcharts.

Regarding the bokeh css. Bokeh comes with a layout engine which makes nested layouts slow. To avoid that you should use either built in templates or a custom template. Bokeh 3.0 should get rid of the layout engine which should solve the problem.

Thanks Marc, glad to be a part of it!

Yes, hopefully we can collaborate some time in future.

Thank you, that was insightful.