Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cytoscape.js raises errors when used with Bokeh 3.x/ Panel 1.0 #3133

Closed
7 tasks
MarcSkovMadsen opened this issue Jul 1, 2023 · 7 comments
Closed
7 tasks
Labels

Comments

@MarcSkovMadsen
Copy link

MarcSkovMadsen commented Jul 1, 2023

Environment

  • Cytoscape.js version : 3.25.0
  • Browser/Node.js & version : Edge and Chrome
  • Panel: 1.1.1
  • Bokeh: 3.1.1

To get everything installed in your Python environment

Context

HoloViz Panel is a very popular open source, free and NumFocus sponsored Python data app framework. Its used by scientists and business users in Jupyter Notebooks and Editors like VS Code and PyCharm to explore and share their data and models.

In holoviz/panel#5195 a user of Panel reports that he was using cytoscape.js just fine with Panel 0.14.4/ Bokeh 2.4.3. But not with Panel 1.x./ Bokeh 3.x.

The big change is that in Panel 1.x/ Bokeh 3.x components are rendered inside shadow root.

I'm a simply a user of and contributor to Panel with minimal front end skills. I'm just trying to help the user. I believe that the issues below are related to cytoscape.js in some way. My hypothesis is that cytoscape.js does not work well inside the shadow root?

I hope that as a minimum you can help us understand the issue. If you can fix the issue on cytoscape.js side or give us enough understanding of the issue to solve the issue on the Panel side it would be much appreciated. Thanks.

Minimum Reproducible Example

In a terminal install Panel

pip install panel

Add the below code to a script.py file.

import panel as pn
import param
from panel.reactive import ReactiveHTML

layouts = {
    "cose": {"name": "cose"},
    "grid": {"name": "grid"},
    "circle": {"name": "circle"},
    "concentric": {"name": "concentric"},
    "breadthfirst": {"name": "breadthfirst"},
    "random": {"name": "random"},
    "preset": {"name": "preset"},
}


class Cytoscape(ReactiveHTML):
    layout_names = param.ObjectSelector(
        default="cose", doc="Layout Options to choose from.", objects=list(layouts.keys())
    )
    graph_layouts = param.Dict(default=layouts)
    style = param.List(doc="Use to set the styles of the nodes/edges")
    data = param.List(doc="Use to send node's data/attributes to Cytoscape")

    _template = """
        <div id="cy" style="position: relative; width: 200px; height: 200px; border: 1px solid"></div>
    """
    __javascript__ = ["https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.25.0/cytoscape.umd.js"]

    _scripts = {
        "after_layout": """
        self.create()
        """,
        "create": """
            state.cy = cytoscape({
              container: cy,
              layout: {name: 'cose'}, 
              elements: [{data:{id:'A', label:'A'}},{data:{id:'B', label:'B'}}, {data:{source:'A', target:'B'}}],
              zoom: 1,
              pan: {x: 0, y: 0},
            });
            state.cy.on('click', function (evt) {
                console.log(evt.originalEvent, evt)
            });
        """,
        "remove": """
        state.cy.destroy()
        delete state.cy
        """,
        "layout_names": """
            console.log("layout_names")
            const layout = state.cy.layout(data.graph_layouts[data.layout_names])
            layout.run()
        """,
    }

    _extension_name = "cytoscape"


pn.extension("cytoscape", template="fast")

my_graph = Cytoscape(width=800, height=400)

widget_container = pn.Column(pn.pane.Markdown("## Title"))
app = pn.Column(widget_container).servable(target="main")
widget_container.append(pn.Row(my_graph.param.layout_names, my_graph))

app.servable()

In a terminal run

panel serve script.py --autoreload

Open the app in your browser at http://localhost:5006/script.

As you can see in the video below

  • errors are raised both when the app is refreshed
  • errors are raised when the mouse is clicked
  • When the page is scrolled a tiny bit mouse clicks a not visualized where they are actually clicked
  • When the page is scrolled more mouse events are no longer triggered/ shown.
cytoscape-issues.mp4

Desired behaviour

I would expect cytospace.js to be to not raise errors and the mouse clicks to work as they should.

For reviewers

Reviewers should ensure that the following tasks are carried out for incorporated issues:

  • Ensure that the reporter has included a reproducible demo. They can easily fork this JSBin demo: http://jsbin.com/fiqugiq
  • The issue has been associated with a corresponding milestone.
  • The commits have been incorporated into the corresponding branches. Bug-fix patches go on
    • master,
    • unstable, and
    • the previous feature release branch (e.g. 1.1.x if the current release is 1.2).
  • The issue has been labelled as a bug, if necessary.
@MarcSkovMadsen
Copy link
Author

There is a running version of the app here that you can use for exploration

HF Space: https://huggingface.co/spaces/MarcSkovMadsen/panel-cytoscape-issue
App: https://marcskovmadsen-panel-cytoscape-issue.hf.space/app

@maxkfranz maxkfranz removed the bug A bug in the code of Cytoscape.js label Jul 5, 2023
@maxkfranz
Copy link
Member

Cytoscape works very well in data science systems like Jupyter and Plotly/Dash. It's a shame that, prima facie, Holoviz has compatibility issues given their architectural decisions.

You could be very well correct that Holoviz's use of shadow DOM is a part of the problem. If that's the case, then effectively Holoviz has placed a potential integration burden for every external library that might be used in that system.

If the Holoviz community is interested in support for new features in Cytoscape -- like shadow DOM support -- we do accept pull requests.

If you find any minimally reproducible issues (i.e. without Holoviz, forked from the JSbin example), you're welcome to post them to the issue tracker. For instance, you could test your shadow DOM hypothesis in a minimal example.

@MarcSkovMadsen
Copy link
Author

Thanks for the feedback 👍

@MarcSkovMadsen
Copy link
Author

I might not have explained very it very well. Its actually Bokeh that took the decission to use ShadowRoot. Simply because styling of Bokeh, Jupyter, VS Code, Colab and other Python frameworks often dont work well together.

Panel and HoloViz has taken the decission to build on top of Bokeh.

Panel is supporting many, many JS libraries. Cytoscape is the first JS library that to my knowledge show signs of not working inside shadowRoot.

  • I've tried to find examples of users using Cytoscape in web components for inspiration. But without luck.

@derrickmcs
Copy link

I was able to remove the "isHeadless" error by adding data and applying layout after initialization. "after_layout": """ state.cy = cytoscape({container: cy}); state.cy.add([{data:{id:'A', label:'A'}},{data:{id:'B', label:'B'}}, {data:{source:'A', target:'B'}}]) const layout = state.cy.layout({name: 'cose'}) layout.run() state.cy.on('click', function (evt) { console.log(evt.originalEvent, evt) }); """,

@maxkfranz
Copy link
Member

I've tried to find examples of users using Cytoscape in web components for inspiration. But without luck.

By and large, web components are a technology that hasn’t really taken off in comparison with other methods (e.g. React). It’s analogous to the old ideas around the semantic web: Great in theory, but nearly nobody used it and it wasn’t without its downsides.

I was able to remove the "isHeadless" error by adding data and applying layout after initialization. "after_layout": """ state.cy = cytoscape({container: cy}); state.cy.add([{data:{id:'A', label:'A'}},{data:{id:'B', label:'B'}}, {data:{source:'A', target:'B'}}]) const layout = state.cy.layout({name: 'cose'}) layout.run() state.cy.on('click', function (evt) { console.log(evt.originalEvent, evt) }); """,

That’s great that you have a workaround. It’s a bit suspect, though, since it seems as though your component might not be mounting at the proper time. Anyway, best of luck with your project.

@stale
Copy link

stale bot commented Jul 25, 2023

This issue has been automatically marked as stale, because it has not had activity within the past 14 days. It will be closed if no further activity occurs within the next 7 days. If a feature request is important to you, please consider making a pull request. Thank you for your contributions.

@stale stale bot added the stale label Jul 25, 2023
@stale stale bot closed this as completed Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants