plotly / react-cytoscapejs Goto Github PK
View Code? Open in Web Editor NEWReact component for Cytoscape.js network visualisations
License: MIT License
React component for Cytoscape.js network visualisations
License: MIT License
I am trying to make arrows on the edges and crtoscape uses styles.. and an array of styles.. but this React wrapper seems to leverage the React style.. how to pass the array so I can get the arrow?
{
"selector": "node",
"style": {
"text-valign": "center",
"text-halign": "left",
"width": 16,
"height": 16
}
}, {
"selector": "node[type]",
"style": {
"label": "data(type)"
}
}, {
"selector": "edge",
"style": {
"width": 1,
"curve-style": "straight"
}
}, {
"selector": "edge[arrow]",
"style": {
"target-arrow-shape": "data(arrow)"
}
}, {
"selector": "edge.hollow",
"style": {
"target-arrow-fill": "hollow"
}
}]
Hi I am using cytoscape with compound nodes with the usage of cytoscape-extend-collapse. On before expand I would like to know what is the bounding box of children, I checked with autoWidth and height but they seem to be undefined. Is there a way to tell? My piece of code:
cy={cy => {
setCy(cy);
cy.nodes().on('expandcollapse.beforeexpand', e => {
const targetPosition = e.target.position();
const targetBoundingBox = ???;
});
}}
None of the console logs are getting triggered.
I have also tried updating the elements props.
class RendererWindow extends React.Component {
constructor(props) {
super(props);
this.cy = null;
}
componentDidMount() {
if (this.cy) {
console.log('here');
this.cy.nodes().on('click', (e) => {
console.log(e);
});
}
}
render() {
const { elements, width, height } = this.props;
return (
<CytoscapeComponent
elements={elements}
style={{ width, height }}
cy={(cy) => { console.log('heer'); this.cy = cy; }}
/>
);
}
}
Whenever I include the stylesheet
prop, node labels do not show. Is this expected behaviour?
Here's a quick example using some code from the examples in the repo's README:
<CytoscapeComponent
style={{
width: "600px",
height: "600px"
}}
stylesheet={[
{
selector: "node",
style: {
width: 20,
height: 20,
shape: "rectangle"
}
},
{
selector: "edge",
style: {
width: 15
}
}
]}
elements={CytoscapeComponent.normalizeElements({
nodes: [
{
data: { id: "one", label: "Node 1" },
position: { x: 0, y: 0 }
},
{
data: { id: "two", label: "Node 2" },
position: { x: 100, y: 0 }
}
],
edges: [
{
data: {
source: "one",
target: "two",
label: "Edge from Node1 to Node2"
}
}
]
})}
/>
The node labels do not appear. Removing the stylesheet
prop results in the node labels reappearing. This issue is visible in a number of screenshots/gifs from various issues on this repo, although no one has mentioned it.
Using the immutable diff described below does not render anything for react cytoscape. Had to change it to objectA !== objectB, however loading a complete different set of nodes with similar id's causes a problem
diff = (objectA, objectB) =>{
return objectA === objectB;
}
Hey, I'm having trouble upgrading to an underlying cytoscape 3.5 version with a Dagre layout. Have the modules changed their API's for registering layouts or is there a reason you are specifically on 3.2.19? Even upgrading to 3.2.20 breaks compatability.
When I store the current elements in the graph as a state (this.state.elements
), and at render time elements
is passed to Cytoscape elements prop, it is not updated correctly inside the graph. Here's a snippet:
render() {
const {
selectedLayout,
style,
elements,
layout
} = this.state;
return (
<div>
<CytoscapeComponent
elements={elements}
style={style}
cy={this.handleCy}
layout={{name: layout}}
/>
...
</div>
);
}
In my example, whenever the button is clicked, a new node is created and added to the elements
array (stored as a state). I log the size of the node inside the console and as seen, the state is correctly changing. Here's a picture:
Please refer to the previous issues.
Save this code as App.js
:
import React, { Component } from 'react';
import CytoscapeComponent from './react-cytoscapejs/src/component.js';
import Select from 'react-select';
import _ from 'lodash';
const layoutOptions = [
{ value: 'preset', label: 'preset' },
{ value: 'random', label: 'random' },
{ value: 'grid', label: 'grid' },
{ value: 'circle', label: 'circle' }
];
const original_elements = [
{
data: {id: 'one', label: 'Node 1'},
position: {x: 50, y: 50}
},
{
data: {id: 'two', label: 'Node 2'},
position: {x: 200, y: 200}
},
{
data: {id: 'three', label: 'Node 3'},
position: {x: 100, y: 150}
},
{
data: {id: 'four', label: 'Node 4'},
position: {x: 400, y: 50}
},
{
data: {id: 'five', label: 'Node 5'},
position: {x: 250, y: 100}
},
{
data: {id: 'six', label: 'Node 6', parent: 'three'},
position: {x: 150, y: 150}
},
{data: {
source: 'one',
target: 'two',
label: 'Edge from Node1 to Node2'
}},
{data: {
source: 'one',
target: 'five',
label: 'Edge from Node 1 to Node 5'
}},
{data: {
source: 'two',
target: 'four',
label: 'Edge from Node 2 to Node 4'
}},
{data: {
source: 'three',
target: 'five',
label: 'Edge from Node 3 to Node 5'
}},
{data: {
source: 'three',
target: 'two',
label: 'Edge from Node 3 to Node 2'
}},
{data: {
source: 'four',
target: 'four',
label: 'Edge from Node 4 to Node 4'
}},
{data: {
source: 'four',
target: 'six',
label: 'Edge from Node 4 to Node 6'
}},
{data: {
source: 'five',
target: 'one',
label: 'Edge from Node 5 to Node 1'
}},
];
class App extends Component {
state = {
selectedLayout: 'random',
sourceDistance: 0,
targetDistance: 0,
elements: original_elements,
layout: 'preset',
boxNodeData: [],
style: {width:'100%', height: '65vh'}
}
_handleCyCalled = false;
updateLayout = newOption => {
this.setState({
selectedLayout: newOption,
layout: newOption.value
});
console.log(`Layout selected:`, newOption);
}
addNode = () => {
const {
elements
} = this.state;
const newId = elements.length.toString();
const newLabel = "Node " + newId;
// console.log(newLabel, newId);
console.log(elements);
elements.push({data: {id: newId, label: newLabel}});
elements.push({data: {
source: 'one',
target: newId,
}});
this.setState({
elements: elements
})
}
generateNode = event => {
const ele = event.target;
const isParent = ele.isParent();
const isChildless = ele.isChildless();
const isChild = ele.isChild();
const isOrphan = ele.isOrphan();
const renderedPosition = ele.renderedPosition();
const relativePosition = ele.relativePosition();
const parent = ele.parent();
const style = ele.style();
// Trim down the element objects to only the data contained
const edgesData = ele.connectedEdges().map(ele => {return ele.data()});
const childrenData = ele.children().map(ele => {return ele.data()});
const ancestorsData = ele.ancestors().map(ele => {return ele.data()});
const descendantsData = ele.descendants().map(ele => {return ele.data()});
const siblingsData = ele.siblings().map(ele => {return ele.data()});
const {timeStamp} = event;
const {
classes,
data,
grabbable,
group,
locked,
position,
selected,
selectable
} = ele.json();
let parentData;
if (parent) {
parentData = parent.data();
} else {
parentData = null;
}
const nodeObject = {
// Nodes attributes
edgesData,
renderedPosition,
timeStamp,
// From ele.json()
classes,
data,
grabbable,
group,
locked,
position,
selectable,
selected,
// Compound Nodes additional attributes
ancestorsData,
childrenData,
descendantsData,
parentData,
siblingsData,
isParent,
isChildless,
isChild,
isOrphan,
relativePosition,
// Styling
style
};
return nodeObject;
}
generateEdge = event => {
const ele = event.target;
const midpoint = ele.midpoint();
const isLoop = ele.isLoop();
const isSimple = ele.isSimple();
const sourceData = ele.source().data();
const sourceEndpoint = ele.sourceEndpoint();
const style = ele.style();
const targetData = ele.target().data();
const targetEndpoint = ele.targetEndpoint();
const {timeStamp} = event;
const {
classes,
data,
grabbable,
group,
locked,
selectable,
selected,
} = ele.json();
const edgeObject = {
// Edges attributes
isLoop,
isSimple,
midpoint,
sourceData,
sourceEndpoint,
targetData,
targetEndpoint,
timeStamp,
// From ele.json()
classes,
data,
grabbable,
group,
locked,
selectable,
selected,
// Styling
style
};
return edgeObject;
}
handleCy = cy => {
if (cy === this._cy && this._handleCyCalled) {
return;
}
this._cy = cy;
window.cy = cy;
this._handleCyCalled = true;
cy.on('tap', 'node', event => {
const nodeObject = this.generateNode(event);
console.log('nodeObject:', nodeObject);
this.setState({
nodeObject
})
})
cy.on('tap', 'edge', event => {
const edgeObject = this.generateEdge(event);
console.log('edgeObject:', edgeObject);
this.setState({
edgesData: edgeObject
})
})
cy.on('mouseover', 'node', event => {
const nodeData = event.target._private.data;
// console.log(event.target.data());
this.setState({
mouseoverNodeData: nodeData
})
})
cy.on('mouseover', 'edge', event => {
const edgeData = event.target._private.data;
// console.log(edgeData);
this.setState({
mouseoverEdgeData: edgeData
})
})
// SELECTED DATA
// time delta that separates one select gesture from another
const SELECT_THRESHOLD = 100;
const selectedNodes = cy.collection();
const selectedEdges = cy.collection();
// We send the selected nodes only if sendSelectedNodesData wasn't called
// in the previous 100 ms (threshold), which prevents repetitive updates
const sendSelectedNodesData = _.debounce(() => {
const elsData = selectedNodes.map(el => el.data());
// send the array of data json objs somewhere; just log for now...
console.log('Selected Nodes:', elsData);
}, SELECT_THRESHOLD);
const sendSelectedEdgesData = _.debounce(() => {
const elsData = selectedEdges.map(el => el.data());
console.log('Selected Edges:', elsData);
}, SELECT_THRESHOLD);
cy.on('select', 'node', event => {
const ele = event.target;
selectedNodes.merge(ele);
sendSelectedNodesData();
});
cy.on('unselect', 'node', event => {
const ele = event.target;
selectedNodes.unmerge(ele);
sendSelectedNodesData();
});
cy.on('select', 'edge', event => {
const ele = event.target;
selectedEdges.merge(ele);
sendSelectedEdgesData();
});
cy.on('unselect', 'edge', event => {
const ele = event.target;
selectedEdges.unmerge(ele);
sendSelectedEdgesData();
});
}
render() {
const {
selectedLayout,
style,
elements,
layout
} = this.state;
return (
<div>
<CytoscapeComponent
elements={elements}
style={style}
cy={this.handleCy}
layout={{name: layout}}
/>
<p> Layout: </p>
<Select
value={selectedLayout}
onChange={this.updateLayout}
options={layoutOptions}
/>
<p><button onClick={this.addNode}>Add a Node</button></p>
</div>
);
}
}
export default App;
I tried to work through a few examples, and one problem that came up was that the element prop of the react version only accepts an array of objects, whereas the original implementation accepts both arrays and objects. Particularly, objects contain specifications for nodes
and edges
, for example:
{
"nodes": [{...}, {...}, ..., {...}],
"edges": [{...}, {...}, ..., {...}]
}
Whereas the only format accepted (for the element prop) is the following:
[
{"data": {...}, "position": {...}, ...},
{"data": {...}, "position": {...}, ...},
...
{"data": {...}, "position": {...}, ...}
]
Here's an example of elements
in the object (1st) format
Here's an example of elements
in the list (2nd) format.
data.json
is directly downloaded from the tokyo railway example
App.js
is written as such:
import React, { Component } from 'react';
import CytoscapeComponent from './react-cytoscapejs/src/component.js';
import data from './data.json'
class App extends Component {
constructor(props) {
super(props);
}
render() {
return <CytoscapeComponent
elements={data.elements}
/>
}
}
export default App;
The console output error is:
Uncaught TypeError: arr.forEach is not a function
at forEach (json.js:5)
at patchElements (patch.js:86)
at patch (patch.js:12)
at component.js:43
at Core.batch (cytoscape.cjs.js:13675)
at CytoscapeComponent.updateCytoscape (component.js:42)
at CytoscapeComponent.componentDidMount (component.js:34)
at commitLifeCycles (react-dom.development.js:14684)
at commitAllLifeCycles (react-dom.development.js:15904)
at HTMLUnknownElement.callCallback (react-dom.development.js:145)
at Object.invokeGuardedCallbackDev (react-dom.development.js:195)
at invokeGuardedCallback (react-dom.development.js:248)
at commitRoot (react-dom.development.js:16074)
at completeRoot (react-dom.development.js:17462)
at performWorkOnRoot (react-dom.development.js:17390)
at performWork (react-dom.development.js:17294)
at performSyncWork (react-dom.development.js:17266)
at requestWork (react-dom.development.js:17154)
at scheduleWork (react-dom.development.js:16948)
at scheduleRootUpdate (react-dom.development.js:17636)
at updateContainerAtExpirationTime (react-dom.development.js:17663)
at updateContainer (react-dom.development.js:17690)
at ReactRoot../node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render (react-dom.development.js:17956)
at react-dom.development.js:18096
at unbatchedUpdates (react-dom.development.js:17517)
at legacyRenderSubtreeIntoContainer (react-dom.development.js:18092)
at Object.render (react-dom.development.js:18151)
at Object../src/index.js (index.js:7)
at __webpack_require__ (bootstrap fe0e27dab2a44bac186c:678)
at fn (bootstrap fe0e27dab2a44bac186c:88)
at Object.0 (registerServiceWorker.js:117)
at __webpack_require__ (bootstrap fe0e27dab2a44bac186c:678)
at ./node_modules/ansi-regex/index.js.module.exports (bootstrap fe0e27dab2a44bac186c:724)
at bootstrap fe0e27dab2a44bac186c:724
index.js:2178 The above error occurred in the <CytoscapeComponent> component:
in CytoscapeComponent (at App.js:11)
in App (at src/index.js:7)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
Here is the screenshot of react app output:
Steps toward reproducing the error in command line:
# Create the app
npx create-react-app cytoscape
cd cytoscape
# Add dependencies
yarn add cytoscape
cd src
git clone https://github.com/plotly/react-cytoscapejs.git
# Here, replace App.js with the file described in the issue.
# Also add data.json if required
cd ..
yarn build
yarn start
When the graph is edited (i.e. a node is moved) the passed props never changes in the parent.
Perhaps there can be a onElementChange
callback so that the parent can update the elements state
I cannot access the underlying Cytoscape instance via Reacts ref API. I get the following error in the console -> Uncaught ReferenceError: cy is not defined
class Container extends React.Component {
constructor(props) {
super(props);
this.cy;
}
componentDidUpdate() {
console.log(this.cy);
}
render() {
return <CytoscapeComponent cy={ (cy) = this.cy = cy } elements={mockData} style={ { width: '600px', height: '600px' } } />;
}
}
ReactDOM.render(<Container />, document.querySelector('#container'));
Upon inspecting the component.js
file, I don't see any of the suggested syntax found in the React Docs. Am I doing something wrong?
I'm trying to create a functional component with this lib, and useState
hooks, but having problems dealing with the cy
instance.
I need to add events to the cy instance, eg tap. but this seems to create a memory leak - now each time the graph is rendered again, there's another 'graph' created. tapping will now send 3, 4, ... etc events.
what's the best way to manage this?
do i need to put some code in to try and remove the cy.on(...)
event before the next render?
Thanks!
code is like this below
import React, { useState, useEffect } from 'react';
import Cytoscape from 'cytoscape';
import CytoscapeComponent from 'react-cytoscapejs'
import cola from 'cytoscape-cola';
// import cydagre from 'cytoscape-dagre';
import { graphStyle, nodeStyle } from './graphStyle'
const layout = {
name: 'cola'
}
Cytoscape.use(cola);
// import { DcLib } from '../utils/DcLib'
const KGraph = (props: any) => {
const graph = props?.graph
const [cy, setCy] = useState({})
useEffect(() => {
console.log('effect')
// cy.layout(layout).run()
})
console.log('graph', graph)
const initCy = (cy: any) => {
// @ts-ignore
console.log('initCy')
if (!cy) {
setCy(cy)
}
cy.on('tap', (event: any) => {
console.log('tap cy', event)
console.log('target', event.target)
});
}
if (!props.graph.ready) {
return (<div> graph here </div>)
}
const layoutGraph = () => {
// @ts-ignore
cy.layout(layout).run()
}
return (
<div>
<button onClick={() => layoutGraph()}>redo graph</button>
<CytoscapeComponent
cy={initCy}
elements={graph.elements}
style={graphStyle}
layout={layout}
stylesheet={nodeStyle}
/>
</div>
)
}
export default KGraph
Using plain JavaScript cytoscape can render composite structures:
cytoscape({
container: document.getElementById('cy'),
elements:[
{ data: { id: 'a', parent: 'b' } },
{ data: { id: 'b' } },
{ data: { id: 'c', parent: 'b' } },
{ data: { id: 'd' } },
{ data: { id: 'e' } },
{ data: { id: 'f', parent: 'e' } },
{ data: { id: 'ad', source: 'a', target: 'd' } },
{ data: { id: 'eb', source: 'e', target: 'b' } }
],
layout: { name: "grid" }
});
Working example in https://codepen.io/anon/pen/Oqdqjj
But when using react-cytoscapejs:
import * as React from 'react';
import CytoscapeComponent from 'react-cytoscapejs';
class App extends React.Component {
public render() {
return (
<CytoscapeComponent
elements={
[
{ data: { id: 'a', parent: 'b' } },
{ data: { id: 'b' } },
{ data: { id: 'c', parent: 'b' } },
{ data: { id: 'd' } },
{ data: { id: 'e' } },
{ data: { id: 'f', parent: 'e' } },
{ data: { id: 'ad', source: 'a', target: 'd' } },
{ data: { id: 'eb', source: 'e', target: 'b' } }
]
}
layout={ {name: "grid"} }
/>
);
}
}
export default App;
Where would you put the Cytoscape.use(), when using this library with Gatsby? The registration of extensions work fine for the standard cytoscape, but using this library I get the following error:
Uncaught TypeError: _cy.cxtmenu is not a function
import React from "react"
import CytoscapeComponent from "react-cytoscapejs"
import Cytoscape from "cytoscape"
import cxtmenu from "cytoscape-cxtmenu"
Cytoscape.use(cxtmenu)
const Cyto = () => {
return (
<CytoscapeComponent
cy={cy => {
cy.cxtmenu({
selector: "core",
commands: [
{
content: "Add",
select: () => {
console.log("add")
},
fillColor: "#0F0",
},
],
})
}}
/>
)
}
export default Cyto
Hi!
I am attempting to write tests for my code that uses react-cytoscapejs using the Jest testing framework but can't get it working. I have replicated the setup and teardown procedure of the mocha tests you provide within my own test (see below) and this is the error I am getting:
TypeError: Cannot read property 'h' of undefined
62 | document.body.appendChild(root);
63 |
> 64 | ReactDOM.render(
| ^
65 | React.createElement(TestComponent, {
66 | setStateRef: ref => (setState = ref),
67 | defaults: json
at Layout.Object.<anonymous>.GridLayout.run (../../node_modules/cytoscape/dist/cytoscape.cjs.js:20308:10)
at setElesAndLayout (../../node_modules/cytoscape/dist/cytoscape.cjs.js:17905:27)
at ../../node_modules/cytoscape/dist/cytoscape.cjs.js:17917:5
at loadExtData (../../node_modules/cytoscape/dist/cytoscape.cjs.js:17864:7)
at new Core (../../node_modules/cytoscape/dist/cytoscape.cjs.js:17908:3)
at new cytoscape (../../node_modules/cytoscape/dist/cytoscape.cjs.js:30747:12)
at t.value (../../node_modules/react-cytoscapejs/dist/react-cytoscape.js:1:3894)
at commitLifeCycles (../../node_modules/react-dom/cjs/react-dom.development.js:17334:22)
at commitAllLifeCycles (../../node_modules/react-dom/cjs/react-dom.development.js:18736:7)
at HTMLUnknownElement.callCallback (../../node_modules/react-dom/cjs/react-dom.development.js:149:14)
at Object.invokeGuardedCallbackDev (../../node_modules/react-dom/cjs/react-dom.development.js:199:16)
at invokeGuardedCallback (../../node_modules/react-dom/cjs/react-dom.development.js:256:31)
at commitRoot (../../node_modules/react-dom/cjs/react-dom.development.js:18948:7)
at ../../node_modules/react-dom/cjs/react-dom.development.js:20418:5
at Object.unstable_runWithPriority (../../node_modules/scheduler/cjs/scheduler.development.js:255:12)
at completeRoot (../../node_modules/react-dom/cjs/react-dom.development.js:20417:13)
at performWorkOnRoot (../../node_modules/react-dom/cjs/react-dom.development.js:20346:9)
at performWork (../../node_modules/react-dom/cjs/react-dom.development.js:20254:7)
at performSyncWork (../../node_modules/react-dom/cjs/react-dom.development.js:20228:3)
at requestWork (../../node_modules/react-dom/cjs/react-dom.development.js:20097:5)
at scheduleWork (../../node_modules/react-dom/cjs/react-dom.development.js:19911:5)
at scheduleRootUpdate (../../node_modules/react-dom/cjs/react-dom.development.js:20572:3)
at updateContainerAtExpirationTime (../../node_modules/react-dom/cjs/react-dom.development.js:20600:10)
at updateContainer (../../node_modules/react-dom/cjs/react-dom.development.js:20657:10)
at ReactRoot.Object.<anonymous>.ReactRoot.render (../../node_modules/react-dom/cjs/react-dom.development.js:20953:3)
at ../../node_modules/react-dom/cjs/react-dom.development.js:21090:14
at unbatchedUpdates (../../node_modules/react-dom/cjs/react-dom.development.js:20459:10)
at legacyRenderSubtreeIntoContainer (../../node_modules/react-dom/cjs/react-dom.development.js:21086:5)
at Object.render (../../node_modules/react-dom/cjs/react-dom.development.js:21155:12)
at Object.render (src/tests/learningTests/cytoscape.test.js:64:14)
console.error ../../node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Uncaught [TypeError: Cannot read property 'h' of undefined]
at reportException (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
at commitRoot (/node_modules/react-dom/cjs/react-dom.development.js:18948:7)
at /node_modules/react-dom/cjs/react-dom.development.js:20418:5 TypeError: Cannot read property 'h' of undefined
at Layout.Object.<anonymous>.GridLayout.run (/node_modules/cytoscape/dist/cytoscape.cjs.js:20308:10)
at setElesAndLayout (/node_modules/cytoscape/dist/cytoscape.cjs.js:17905:27)
at /node_modules/cytoscape/dist/cytoscape.cjs.js:17917:5
at loadExtData (/node_modules/cytoscape/dist/cytoscape.cjs.js:17864:7)
at new Core (/node_modules/cytoscape/dist/cytoscape.cjs.js:17908:3)
at new cytoscape (/node_modules/cytoscape/dist/cytoscape.cjs.js:30747:12)
at t.value (/node_modules/react-cytoscapejs/dist/react-cytoscape.js:1:3894)
at commitLifeCycles (/node_modules/react-dom/cjs/react-dom.development.js:17334:22)
at commitAllLifeCycles (/node_modules/react-dom/cjs/react-dom.development.js:18736:7)
at HTMLUnknownElement.callCallback (/node_modules/react-dom/cjs/react-dom.development.js:149:14)
at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
at commitRoot (/node_modules/react-dom/cjs/react-dom.development.js:18948:7)
at /node_modules/react-dom/cjs/react-dom.development.js:20418:5
at Object.unstable_runWithPriority (/node_modules/scheduler/cjs/scheduler.development.js:255:12)
at completeRoot (/node_modules/react-dom/cjs/react-dom.development.js:20417:13)
at performWorkOnRoot (/node_modules/react-dom/cjs/react-dom.development.js:20346:9)
at performWork (/node_modules/react-dom/cjs/react-dom.development.js:20254:7)
at performSyncWork (/node_modules/react-dom/cjs/react-dom.development.js:20228:3)
at requestWork (/node_modules/react-dom/cjs/react-dom.development.js:20097:5)
at scheduleWork (/node_modules/react-dom/cjs/react-dom.development.js:19911:5)
at scheduleRootUpdate (/node_modules/react-dom/cjs/react-dom.development.js:20572:3)
at updateContainerAtExpirationTime (/node_modules/react-dom/cjs/react-dom.development.js:20600:10)
at updateContainer (/node_modules/react-dom/cjs/react-dom.development.js:20657:10)
at ReactRoot.Object.<anonymous>.ReactRoot.render (/node_modules/react-dom/cjs/react-dom.development.js:20953:3)
at /node_modules/react-dom/cjs/react-dom.development.js:21090:14
at unbatchedUpdates (/node_modules/react-dom/cjs/react-dom.development.js:20459:10)
at legacyRenderSubtreeIntoContainer (/node_modules/react-dom/cjs/react-dom.development.js:21086:5)
at Object.render (/node_modules/react-dom/cjs/react-dom.development.js:21155:12)
at Object.render (/apps/linepulse/src/tests/learningTests/cytoscape.test.js:64:14)
at Object.asyncJestLifecycle (/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
at resolve (/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
at mapper (/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
at promise.then (/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
at process.internalTickCallback (internal/process/next_tick.js:77:7)
console.error ../../node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Uncaught [TypeError: Cannot read property 'destroy' of undefined]
at reportException (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:209:9)
at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
at safelyCallComponentWillUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17176:5https://github.com/Remimstr/broken-cytoscape-test)
at commitUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17553:11) TypeError: Cannot read property 'destroy' of undefined
at t.value (/node_modules/react-cytoscapejs/dist/react-cytoscape.js:1:4283)
at callComponentWillUnmountWithTimer (/node_modules/react-dom/cjs/react-dom.development.js:17169:12)
at HTMLUnknownElement.callCallback (/node_modules/react-dom/cjs/react-dom.development.js:149:14)
at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
at safelyCallComponentWillUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17176:5)
at commitUnmount (/node_modules/react-dom/cjs/react-dom.development.js:17553:11)
at unmountHostComponents (/node_modules/react-dom/cjs/react-dom.development.js:17873:7)
at commitDeletion (/node_modules/react-dom/cjs/react-dom.development.js:17904:5)
at commitAllHostEffects (/node_modules/react-dom/cjs/react-dom.development.js:18685:11)
at HTMLUnknownElement.callCallback (/node_modules/react-dom/cjs/react-dom.development.js:149:14)
at invokeEventListeners (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
at HTMLUnknownElementImpl._dispatch (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
at HTMLUnknownElementImpl.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
at HTMLUnknownElement.dispatchEvent (/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
at Object.invokeGuardedCallbackDev (/node_modules/react-dom/cjs/react-dom.development.js:199:16)
at invokeGuardedCallback (/node_modules/react-dom/cjs/react-dom.development.js:256:31)
at commitRoot (/node_modules/react-dom/cjs/react-dom.development.js:18913:7)
at /node_modules/react-dom/cjs/react-dom.development.js:20418:5
at Object.unstable_runWithPriority (/node_modules/scheduler/cjs/scheduler.development.js:255:12)
at completeRoot (/node_modules/react-dom/cjs/react-dom.development.js:20417:13)
at performWorkOnRoot (/node_modules/react-dom/cjs/react-dom.development.js:20346:9)
at performWork (/node_modules/react-dom/cjs/react-dom.development.js:20254:7)
at performSyncWork (/node_modules/react-dom/cjs/react-dom.development.js:20228:3)
at requestWork (/node_modules/react-dom/cjs/react-dom.development.js:20097:5)
at scheduleWork (/node_modules/react-dom/cjs/react-dom.development.js:19911:5)
at scheduleRootUpdate (/node_modules/react-dom/cjs/react-dom.development.js:20572:3)
at updateContainerAtExpirationTime (/node_modules/react-dom/cjs/react-dom.development.js:20600:10)
at updateContainer (/node_modules/react-dom/cjs/react-dom.development.js:20657:10)
at ReactRoot.Object.<anonymous>.ReactRoot.render (/node_modules/react-dom/cjs/react-dom.development.js:20953:3)
at /node_modules/react-dom/cjs/react-dom.development.js:21090:14
at unbatchedUpdates (/node_modules/react-dom/cjs/react-dom.development.js:20459:10)
at legacyRenderSubtreeIntoContainer (/node_modules/react-dom/cjs/react-dom.development.js:21086:5)
at Object.render (/node_modules/react-dom/cjs/react-dom.development.js:21155:12)
at Object.render (/apps/linepulse/src/tests/learningTests/cytoscape.test.js:64:14)
at Object.asyncJestLifecycle (/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
at resolve (/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
at new Promise (<anonymous>)
at mapper (/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
at promise.then (/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
at process.internalTickCallback (internal/process/next_tick.js:77:7)
console.error ../../node_modules/react-dom/cjs/react-dom.development.js:17117
The above error occurred in the <t> component:
in t (created by TestComponent)
in TestComponent
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
console.error ../../node_modules/react-dom/cjs/react-dom.development.js:17117
The above error occurred in the <t> component:
in t (created by TestComponent)
in TestComponent
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
The test itself is this:
import React from "react";
import ReactDOM from "react-dom";
import CytoscapeComponent from "react-cytoscapejs";
const defaults = {
global: "cy",
id: "cy",
style: { width: "500px", height: "500px" },
zoom: 1,
pan: {
x: 0,
y: 0
},
elements: [
{
data: { id: "a", label: "apple" },
position: { x: 0, y: 0 },
scratch: { _test: 1 },
classes: "foo bar"
},
{
data: { id: "b", label: "banana" },
position: { x: 0, y: 0 },
scratch: { _test: 2 },
classes: "foo bar"
},
{
data: { id: "c", label: "cherry" },
position: { x: 0, y: 0 },
scratch: { _test: 3 },
classes: "foo bar"
}
]
};
const cloneDefaults = () => JSON.parse(JSON.stringify(defaults));
class TestComponent extends React.Component {
constructor(props) {
super(props);
props.setStateRef(this.setState.bind(this));
this.state = props.default;
}
render() {
return React.createElement(CytoscapeComponent, this.state);
}
}
describe.only("Component", () => {
let root, setState, json;
https://github.com/Remimstr/broken-cytoscape-test
let updateCyProps = props =>
new Promise(resolve => setState(Object.assign({}, json, props), resolve));
beforeEach(() => {
json = cloneDefaults();
root = document.createElement("div");
document.body.appendChild(root);
ReactDOM.render(
React.createElement(TestComponent, {
setStateRef: ref => (setState = ref),
defaults: json
}),
root
);
});
afterEach(() => {
ReactDOM.unmountComponentAtNode(root);
document.body.removeChild(root);
});
it("creates Cytoscape Instance", () => {});
});
I get generally the same errors whether I run this test or try to mount the component using enzyme.
I had difficulty creating a sandbox example of this issue so I make a new repository that you can checkout and play with: https://github.com/Remimstr/broken-cytoscape-test
I would really appreciate any insights you may have on this issue!
Currently trying to run the basic example on react-cytoscapejs version 1.0.0 using create-react-app. Here's what i have done:
npx create-react-app cytoscape-basic-app
cd cytoscape-basic-app
yarn add [email protected]
At this point, we change the src/App.js
file with the following code modified from the basic example:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import CytoscapeComponent from 'react-cytoscapejs';
class App extends Component {
constructor(props){
super(props);
}
render(){
const elements = [
{ data: { id: 'one', label: 'Node 1' }, position: { x: 0, y: 0 } },
{ data: { id: 'two', label: 'Node 2' }, position: { x: 100, y: 0 } },
{ data: { source: 'one', target: 'two', label: 'Edge from Node1 to Node2' } }
];
return <CytoscapeComponent elements={elements} style="width: 600px; height: 600px;" />;
}
}
export default App;
Running yarn start
returns the following error:
./src/App.js
Module not found: Can't resolve 'react-cytoscapejs' in 'C:\Users\xingh\temp\test_cytoscape\cytoscape-app\src'
I solved this by changing the import statement to be:
import CytoscapeComponent from 'react-cytoscapejs/src';
Then the error became:
The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.
in div (created by CytoscapeComponent)
in CytoscapeComponent (at App.js:17)
in App (at src/index.js:7)
which was solved by changing the return statement for the render()
method to:
return <CytoscapeComponent elements={elements} style={{width: "600px", height: "600px"}} />;
Which compiled successfully.
I suggest updating the readme to take those problems into account. Let me know if I should start a PR. Thank you!
hi, i want to add tooltip on each node , does this pkg provide this feature ?
In the cytoscape.js documentation, a list of user input device events is given. Particularly, those are the events relating to box selection:
Is there an event that is fired after box selection has ended and all the elements are selected?
boxend
in this case is fired after the box selection has ended, but before the elements are selected. Please refer to example #1 and example #2.
boxselect
is fired only after boxend
is fired, as shown in example #2. In fact, it is fired once for every element that is selected, whereas we are interested in an event that is fired once after all the elements have been selected by the box.
In the following example we run a simple cytoscape app, and set a breakpoint at cy.on('boxend', ...)
. It stops right after boxend
event is fired. Notice in the gif that the nodes 4 and 5 are not selected, or else their color would have been changed.
In the following example we set the event handlers to be:
let boxNodeData;
cy.on('boxstart', event => {
console.log('boxstart');
boxNodeData = [];
})
cy.on('box', 'node', event => {
console.log('pushed');
boxNodeData.push(event.target._private.data);
})
cy.on('boxend', event => {
console.log('boxend');
console.log(boxNodeData);
})
When we run that example, the following is shown in the console:
"pushed" is printed after "boxend", which means that the box
events are fired after the box selection ends. Therefore, boxend
can't be used for this particular purpose.
Could you please add TypeScript typings to this repo or to a new @types/react-cytoscapejs
package?
Looks like someone already wrote them https://github.com/manuc66/react-cytoscapejs-typescript-sample/blob/master/typings/react-cytoscapejs.d.ts
Hi @maxkfranz, @alexcjohnson, @chriddyp, @jackparmer,
In context of #55, a group of open source contributors in the Layer5 community are considering using react-cytoscape
. As we consider this, I would like to connect with you to understand how we can best collaborate and support the project; whether the project is still active and so on. Could you speak to the state of this effort?
Thanks!
- @leecalcote, @anirudhjain75, @kushthedude, @vineethvanga18, @josegonzalez @josegonzalez @dhruv0000
Are the rendering options such as
// rendering options:
headless: false,
styleEnabled: true,
hideEdgesOnViewport: false,
hideLabelsOnViewport: false,
textureOnViewport: false,
motionBlur: false,
motionBlurOpacity: 0.2,
wheelSensitivity: 1,
pixelRatio: 'auto'
available as props in the react component? I.e., the ones decribed here: http://js.cytoscape.org/#core/initialisation
Could someone help me figure out how to set the target-arrow-shape style?
The below snippet doesn't seem to work:
const elements = [
{ data: { id: 'one', label: 'Node 1' }, position: { x: 0, y: 0 } },
{ data: { id: 'two', label: 'Node 2' }, position: { x: 100, y: 0 } },
{
data: { source: 'one', target: 'two', label: 'Edge from Node1 to Node2' },
style: {'target-arrow-shape': 'triangle'}
}
];
<CytoscapeComponent elements={elements} layout={layout} cy={cy => this._cy(cy)}
style={{position: 'absolute', width: '100%', height: '100%'}}/>
Above just shows:
I've found out that if any of the source or target nodes are compound nodes themselves, then the triangle arrow head is displayed properly. Sample:
const elements = [
{ data: { id: 'one', label: 'Node 1' }, position: { x: 0, y: 0 } },
{ data: { id: 'two', label: 'Node 2' }, position: { x: 100, y: 0 } },
{ data: { id: 'three', label: 'Node 3', parent: 'two' }, position: { x: 200, y: 0 } },
{
data: { source: 'one', target: 'two', label: 'Edge from Node1 to Node2' },
style: {'target-arrow-shape': 'triangle'}
}
];
Now the arrow head is displayed:
Any help you can give me on why it works like this is appreciated!
Due to javascript rejecting hyphenated names, and the stylesheet prop not reflecting anything supplied with a key in quotations, I'm unable to use complex keys, such as target-arrow-shape.
Is there any indication as to how this can be done?
Thank you in advance!
When we use this library in a server-side rendering environment, we get the error ReferenceError: window is not defined
if you look at the source, it looks like the error is at line 1:456
which maps to where window
is referenced in this formatted version.
This means window
is referenced on module import, so even if we wait to render the component until it is mounted (i.e., in the browser) we get this error from module import alone. We have a work around to lazy load the module when a parent component is mounted, but it's a lot of setup just to work.
This SO question suggests that it may be your webpack output configuration.
Documentation about distance from node: http://js.cytoscape.org/#style/edge-endpoints
source-distance-from-node
and target-distance-from-node
don't seem to be updating correctly in React. When we update their state with setState, the changes aren't reflected immediately unless we change another property (e.g. layout) or if we move the nodes.
Here's a screenshot of the problem:
App.js is written as such:
import React, { Component } from 'react';
import CytoscapeComponent from './react-cytoscapejs/src/component.js';
import Select from 'react-select';
const layoutOptions = [
{ value: 'preset', label: 'preset' },
{ value: 'random', label: 'random' },
{ value: 'grid', label: 'grid' },
{ value: 'circle', label: 'circle' }
];
const elements = [
{
data: {id: 'one', label: 'Node 1'},
position: {x: 50, y: 50}
},
{
data: {id: 'two', label: 'Node 2'},
position: {x: 200, y: 200}
},
{
data: {id: 'three', label: 'Node 3'},
position: {x: 100, y: 150}
},
{
data: {id: 'four', label: 'Node 4'},
position: {x: 400, y: 50}
},
{
data: {id: 'five', label: 'Node 5'},
position: {x: 250, y: 100}
},
{
data: {id: 'six', label: 'Node 6', parent: 'three'},
position: {x: 150, y: 150}
},
{data: {
source: 'one',
target: 'two',
label: 'Edge from Node1 to Node2'
}},
{data: {
source: 'one',
target: 'five',
label: 'Edge from Node 1 to Node 5'
}},
{data: {
source: 'two',
target: 'four',
label: 'Edge from Node 2 to Node 4'
}},
{data: {
source: 'three',
target: 'five',
label: 'Edge from Node 3 to Node 5'
}},
{data: {
source: 'three',
target: 'two',
label: 'Edge from Node 3 to Node 2'
}},
{data: {
source: 'four',
target: 'four',
label: 'Edge from Node 4 to Node 4'
}},
{data: {
source: 'four',
target: 'six',
label: 'Edge from Node 4 to Node 6'
}},
{data: {
source: 'five',
target: 'one',
label: 'Edge from Node 5 to Node 1'
}},
];
class App extends Component {
state = {
selectedLayout: 'preset',
sourceDistance: 0,
targetDistance: 0,
layout: 'preset',
style: {width:'100%', height: '65vh'}
}
updateLayout = (newOption) => {
this.setState({
selectedLayout: newOption,
layout: newOption.value
});
console.log(`Option selected:`, newOption);
}
updateSourceDistance = event => {
if (event.target.value){
this.setState({
sourceDistance: event.target.value
})
}
console.log(`New Source Distance:`, event.target.value);
}
updateTargetDistance = event => {
if (event.target.value){
this.setState({
targetDistance: event.target.value
})
}
console.log(`New Target Distance:`, event.target.value);
}
render() {
const {
selectedLayout,
style,
layout,
sourceDistance,
targetDistance
} = this.state;
return (
<div>
<CytoscapeComponent
elements={elements}
style={style}
layout={{name: layout}}
stylesheet={
[
{
"selector": "edge",
"style": {
"width": null,
"curve-style": "haystack",
"line-color": "#999999",
"line-style": "solid",
"loop-direction": "-45deg",
"loop-sweep": "-90deg",
"source-endpoint": "outside-to-node",
"target-endpoint": "outside-to-node",
"source-distance-from-node": sourceDistance,
"target-distance-from-node": targetDistance
}
}
]
}
/>
<p> Layout: </p>
<Select
value={selectedLayout}
onChange={this.updateLayout}
options={layoutOptions}
/>
<p>
Source Distance from Node:
<input type="number" onChange={this.updateSourceDistance} />
</p>
<p>
Target Distance from Node:
<input type="number" onChange={this.updateTargetDistance} />
</p>
</div>
);
}
}
export default App;
Steps toward reproducing the error in command line:
# Create the app
npx create-react-app cytoscape-issue
cd preset
# Add dependencies
yarn add cytoscape
yarn add react-select
cd src
git clone https://github.com/plotly/react-cytoscapejs.git
# Here, replace App.js with the file described in the issue.
cd ..
yarn build
yarn start
Hello everyone,
There are several states on my page. When I update a state (onClick, onChange etc.) my nodes positions also change immediately. I looked at Cytoscape.js docs to avoid this via cy
reference, I didn't found any useful function. Do you have any suggestions with this?
Hi,
We are using this component in a context where users can add/remove nodes dynamically, and it seems useful to re-run the layout after patching regardless of whether the layout options change. Just wondering what you thought about having this as an option and if there are any downsides to this that I am not considering. It could potentially be an opt-in configuration in case performance is a concern.
Happy to do a PR if this is something you think is useful. Thanks for providing the component.
When I try to run the example given in readme, the following is returned:
ERROR in ./src/demo/App.js
Module not found: Error: Can't resolve 'react-cytoscapejs' in 'C:\Users\xingh\git\dash-cytoscape\src\demo'
@ ./src/demo/App.js 17:24-52
@ ./src/demo/index.js
@ multi ./src/demo/index.js
The following code is written in App.js:
/* eslint no-magic-numbers: 0 */
import React from 'react';
import ReactDOM from 'react-dom';
import CytoscapeComponent from 'react-cytoscapejs';
class App extends React.Component {
constructor(props){
super(props);
}
render(){
const elements = [
{ data: { id: 'one', label: 'Node 1' }, position: { x: 0, y: 0 } },
{ data: { id: 'two', label: 'Node 2' }, position: { x: 100, y: 0 } },
{ data: { source: 'one', target: 'two', label: 'Edge from Node1 to Node2' } }
];
return <CytoscapeComponent elements={elements} style="width: 600px; height: 600px;" />;
}
};
export default App;
and ran in index.js (by calling NPM run start):
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Is there event handler for this react wrapper?
In cytoscape.js events are handled in a jquery manner: http://js.cytoscape.org/#events
However, event handling in react is quite different. For example, we would use props such as onTap
, onClick
, etc. which are fired when the corresponding event happens. Should those props be added, or should we use the cy.on()
to handle those events?
Trying out the README example which renders the nodes but not able to move the nodes individually no matter what. Entire layout (two nodes and the edge) move in unison whenever I click on either nodes. What am I missing in order to grab individual nodes and move?
import React from 'react';
import CytoscapeComponent from 'react-cytoscapejs';
export default class Cyto extends React.Component {
constructor(props){
super(props);
}
render(){
const elements = [
{ data: { id: 'one', label: 'Node 1' }, position: { x: 0, y: 0 } },
{ data: { id: 'two', label: 'Node 2' }, position: { x: 100, y: 0 } },
{ data: { source: 'one', target: 'two', label: 'Edge from Node1 to Node2' } }
];
const layout = {
name: 'grid'
}
return <CytoscapeComponent elements={elements} style={{ height: '600px' }} layout={layout} />;
}
}
When creating a path using dagre, the whole nodes accumulate in one position. How can we set default positions for nodes ( Cytoscape js without react works fine)
instead of setting position separately using position attribute for nodes.
const layout = {
name: "dagre",
rankDir: "LR"
}
pageData = < CytoscapeComponent
elements = {
CytoscapeComponent.normalizeElements({
nodes: nodess,
edges: edgess,
layout: layout,
})
}
pan = {
{
x: 200,
y: 200
}
}
autounselectify = {
true
}
userZoomingEnabled = {
false
}
boxSelectionEnabled = {
false
}
style = {
{
width: "1200px",
height: "1000px"
}
}
/>
return (
< div
{
pageData
}
< /div>
);
I cannot install this libraray.
If I run : npm install react-cytoscapejs
I get :
"npm ERR! EINVAL: invalid argument, read"
I already have
"cytoscape": "3.10.2",
in my project
I have a simple react app that makes a REST GET call and uses the result to display a few nodes. However, all the nodes end up stacked on top of each other at the top left corner. Apologies in advance as I'm a web newbie.
import React, {Component} from 'react';
import Cytoscape from 'cytoscape';
import './App.css';
import CytoscapeComponent from 'react-cytoscapejs';
import cola from 'cytoscape-cola';
import Pace from 'react-pace-progress';
Cytoscape.use(cola);
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
graph: [],
elements: [],
};
}
render() {
const layout = {
name: 'cola',
padding: 50
}
return (
<div>
{this.state.isLoading ? <Pace color="#27ae60"/> :
<CytoscapeComponent elements={this.state.elements} layout={layout}
style={{position: 'absolute', width: '100%', height: '100%'}}/>}
</div>
);
}
componentDidMount() {
fetch(`http://127.0.0.1:5000/jil/all`)
.then(response => response.json())
.then(result => this.setState({elements: this.buildJobGraph(result)}))
this.setState({isLoading: false});
}
buildJobGraph(items) {
let jobGraph = [];
items.forEach(e => {
if (e['type'] === 'box') {
jobGraph.push(
{
data: {'id': e['name'], 'label': e['name']},
style: {'shape': 'rectangle', width: '280px', 'text-valign': 'center', 'text-halign': 'center'}
}
);
}
});
console.log(jobGraph);
return jobGraph;
}
/*sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}*/
}
export default App;
And this is what I end up with:
I then have to drag the items to the middle and spread them out:
I believe the cola layout is not taking effect. Any help you can give me would be immensely appreciated.
Can we have a sample code for tooltip implementation on graph elements?
I ran into the same error as cytoscape/cytoscape.js#2453 when doing ReactDOM.render and snapshot testing. It works fine in the browser.
Workaround further down
// CustomCytoscape.jsx
import React, { useRef } from 'react'
import Cytoscape from 'react-cytoscapejs'
function CustomCytoscape({ headless, elements }) {
const layout = { name: 'preset' }
const cyRef = useRef()
return (
<Cytoscape
className={classes.cytoscape}
layout={layout}
cy={cy => {
cyRef.current = cy
}}
elements={elements}
stylesheet={stylesheet}
/>
)
}
// CustomCytoscape.test.js
import React from 'react'
import ReactDOM from 'react-dom'
import renderer from 'react-test-renderer'
import CustomCytoscape from '../CustomCytoscape'
describe('CustomCytoscape', () => {
const elements = []
it('renders without crashing', () => {
const div = document.createElement('div')
ReactDOM.render(
<CustomCytoscape elements={elements} />,
div
)
})
it('matches snapshot', () => {
const component = renderer.create(
<CustomCytoscape elements={elements} />
)
let tree = component.toJSON()
expect(tree).toMatchSnapshot()
})
})
returns TypeError: Cannot read property 'h' of undefined
it('renders without crashing', () => {
8 | const div = document.createElement('div')
> 9 | ReactDOM.render(<App />, div)
| ^
10 | })
11 |
12 | const component = renderer.create(<App />)
And TypeError: Cannot read property 'indexOf' of undefined
const component = renderer.create(<App />)
| ^
13 | let tree = component.toJSON()
14 | expect(tree).toMatchSnapshot()
15 | })
At the moment I cannot spend more time looking into it, but I will try to look into it when I can.
// App.jsx
const App = () => {
const elements = []
const headless = process.env.NODE_ENV === 'test'
return (
<div className={classes.container}>
<CustomCytoscape elements={elements} headless={headless} />
</div>
)
}
// CustomCytoscape.jsx
import React, { useEffect, useRef } from 'react'
import cytoscape from 'cytoscape'
import Cytoscape from 'react-cytoscapejs'
function CustomCytoscape({ headless, elements }) {
const layout = { name: 'preset' }
const cyRef = useRef()
// Use cytoscape (non-react) if headless === true
useEffect(() => {
if (headless) {
cyRef.current = cytoscape({
container: document.getElementById('cyHeadless'),
layout,
elements,
style: stylesheet
})
}
}, [headless, elements, layout])
return headless ? (
<div id="cyHeadless" className={classes.cytoscape} />
) : (
<Cytoscape
className={classes.cytoscape}
layout={layout}
cy={cy => {
cyRef.current = cy
}}
elements={elements}
stylesheet={stylesheet}
/>
)
}
The following tests now works
CustomCytoscape.test.js
import React from 'react'
import ReactDOM from 'react-dom'
import renderer from 'react-test-renderer'
import CustomCytoscape from '../CustomCytoscape'
describe('CustomCytoscape', () => {
const elements = []
it('renders without crashing', () => {
const div = document.createElement('div')
ReactDOM.render(
<CustomCytoscape elements={elements} headless={true} />,
div
)
})
it('matches snapshot', () => {
const component = renderer.create(
<CustomCytoscape elements={elements} headless={true} />
)
let tree = component.toJSON()
expect(tree).toMatchSnapshot()
})
})
The docs indicate this technique for straight jsx:
class MyApp extends React.Component {
render() {
return <CytoscapeComponent cy={(cy) => { this.cy = cy }}>;
}
}
When I change my component to *.tsx, I get errors like TS2339: Property 'cy' does not exist on type 'MyApp'.
How can this be adapted so typescript won't complain for the class component?
Hi, here is and example and my question why data is mutated when I change position of a node.
Link for demo: https://codesandbox.io/s/cytoscapemutatingdata-uq9kh?file=/src/Component.js
Recreation path:
1).Click red button
2). Drag 'n0' node down or anywhere
3). Click red button
4)Click red button to rerender component
5) See that the position of 'n0' node is mutated
When my component re-renders or i mutate the state with a setter, the array containing the nodes is not cleared so we end up with a progressively larger array containing the copies of the same data.
What I also do not understand is if the array is getting larger with additional nodes on each rerender why is cytoscape not renderering more and more nodes ?
Stumped on this. Is it a bug or am I and idiot ?
I have tried cose-bilkent
and circle
layouts. Both render all of the nodes and edges, but all of the connected nodes are laid out on top of each other. I don't remember having to do anything special with the elements when using a layout in the original cytoscape.js library.
The layout will be performed if the react component is rerendered.
How to avoid overlapping paths to the nodes ?
Are built-in Cytoscape events (e.g., cy.on('layoutend', ... ) supported by this React Component? How can I instruct the program to respond to such events in a React-Cytoscape web app? I'm new to React and am looking for advice! Thanks.
When I make changes to elements variable, nodes are newly being created well but not edges.
I logged out the result but edges exist with exact source and target.
What is the issue occurring this?
I copied example with "cytoscape-cose-bilkent" from your readme and have a error No such layout 'cose-bilkent' found. Did you forget to import it and 'cytoscape.use()' it?
With "cytoscape.js-popper" i have a error .popper is not a function
.
With few other extensions a have the same problems :( Could someone help to understand where problem is?
I use:
"cytoscape": "3.16.2",
"react-cytoscapejs": "1.2.1",
"react": "16.13.1",
"mobx": "5.15.5"
Whenever the layout is changed to something else (random, grid, etc.), the component is rendered correctly. however, whenever it is changed back to preset
, nothing happens (it stays stuck in the previous layout).
App.js
is written as such:
import React, { Component } from 'react';
import CytoscapeComponent from './react-cytoscapejs/src/component.js';
import Select from 'react-select';
const options = [
{ value: 'preset', label: 'preset' },
{ value: 'random', label: 'random' },
{ value: 'grid', label: 'grid' },
{ value: 'circle', label: 'circle' }
];
const elements = [
{
data: {id: 'one', label: 'Node 1'},
position: {x: 50, y: 50}
},
{
data: {id: 'two', label: 'Node 2'},
position: {x: 200, y: 200}
},
{
data: {id: 'three', label: 'Node 3'},
position: {x: 100, y: 150}
},
{
data: {id: 'four', label: 'Node 4'},
position: {x: 400, y: 50}
},
{
data: {id: 'five', label: 'Node 5'},
position: {x: 250, y: 100}
},
{
data: {id: 'six', label: 'Node 6', parent: 'three'},
position: {x: 150, y: 150}
},
{data: {
source: 'one',
target: 'two',
label: 'Edge from Node1 to Node2'
}},
{data: {
source: 'one',
target: 'five',
label: 'Edge from Node 1 to Node 5'
}},
{data: {
source: 'two',
target: 'four',
label: 'Edge from Node 2 to Node 4'
}},
{data: {
source: 'three',
target: 'five',
label: 'Edge from Node 3 to Node 5'
}},
{data: {
source: 'three',
target: 'two',
label: 'Edge from Node 3 to Node 2'
}},
{data: {
source: 'four',
target: 'four',
label: 'Edge from Node 4 to Node 4'
}},
{data: {
source: 'four',
target: 'six',
label: 'Edge from Node 4 to Node 6'
}},
{data: {
source: 'five',
target: 'one',
label: 'Edge from Node 5 to Node 1'
}},
];
class App extends Component {
state = {
selectedOption: 'preset',
layout: 'preset',
style: {width:'800px', height: '500px'}
}
handleChange = (selectedOption) => {
this.setState({
selectedOption: selectedOption,
layout: selectedOption.value
});
console.log(`Option selected:`, selectedOption);
}
render() {
const { selectedOption, style, layout } = this.state;
return (
<div>
<CytoscapeComponent
elements={elements}
style={style}
layout={{name: layout}}
/>
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
/>
</div>
);
}
}
export default App;
Steps toward reproducing the error in command line:
# Create the app
npx create-react-app preset
cd preset
# Add dependencies
yarn add cytoscape
yarn add react-select
cd src
git clone https://github.com/plotly/react-cytoscapejs.git
# Here, replace App.js with the file described in the issue.
cd ..
yarn build
yarn start
Hi.
I started using this react-component.
The question is how can I use cy.on() for setting my own action for 'click' on nodes/edges?
I have this code and try using "cy.on()" from http://js.cytoscape.org/#core/events
Any advice?
<CytoscapeComponent elements={elements} style={style} stylesheet={stylesheet} cy={cy => this.cy = cy} />
Rather than bundling cytoscape, it would be nice to express it as a peer dependency pegged to the major version. I'm happy to provide a PR for this.
Maybe I misunderstand it, but it seems to me that there is no way to rerun layout algorithms after when new elements get added/removed.
Is this something you'd welcome a PR for?
Hi,
I would like to ask if it is possible, using this library, for the user to drag lines from a node to another in order to create the edges.
Thank you in advance!
When using react-cytoscape with gatsby app. It works while testing locally but has issues when used with gatsby cloud due to server side rendering.
I'm using this package in a newly created Reactjs project with create-react-app, which creates the project using React.StrictMode. Because of this I have the following warning:
Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of t which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://fb.me/react-strict-mode-find-node
It should be updated as the Reactjs docs recommend, I don't think it would be better to remove the StrictMode just for a single component.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.