Skip to content

Commit 76a5452

Browse files
committed
fix docs, add test
1 parent f549efa commit 76a5452

File tree

3 files changed

+136
-40
lines changed

3 files changed

+136
-40
lines changed

packages/joint-core/docs/src/joint/api/layout/DirectedGraph.html

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,15 @@ <h3 id="layout.DirectedGraph.api">API</h3>
145145
<table>
146146
<tr><th>toGraphLib(graph, opt)</th>
147147
<td>Convert the provided JointJS <code>joint.dia.Graph</code> object to a Graphlib graph object.
148-
<pre><code>import { DirectedGraph } from '@joint/layout-directed-graph';
148+
<pre><code>import { dia, shapes } from '@joint/core';
149+
import { DirectedGraph } from '@joint/layout-directed-graph';
149150
import * as graphlib from '@dagrejs/graphlib';
150151

151-
var graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
152+
const graph = new dia.Graph({}, { cellNamespace: shapes });
152153
// ... populate the graph with elements connected with links
153154

154155
// Get a Graphlib representation of the graph:
155-
var glGraph = DirectedGraph.toGraphLib(graph);
156+
const glGraph = DirectedGraph.toGraphLib(graph);
156157

157158
// Use Graphlib algorithms:
158159
graphlib.alg.isAcyclic(glGraph); // true if the graph is acyclic</code></pre>
@@ -161,31 +162,48 @@ <h3 id="layout.DirectedGraph.api">API</h3>
161162
<tr><th>fromGraphLib(glGraph, opt)</th>
162163
<td>Convert the provided Graphlib graph object to a JointJS <code>joint.dia.Graph</code> object.
163164
<br/><br/>
164-
The <code>opt.importNode</code> and <code>opt.importEdge</code> callbacks are provided with a Graphlib node / edge object, and are expected to return a corresponding JointJS element / link object.
165-
<pre><code>import { DirectedGraph } from '@joint/layout-directed-graph';
165+
Custom <code>opt.importNode</code> and <code>opt.importEdge</code> callbacks need to be provided in order for this method to work as expected - to return a JointJS Graph matching the structure of the provided Graphlib graph (<code>glGraph</code>). The callbacks are provided with the following attributes:
166+
<ul>
167+
<li><code>nodeId</code> / <code>edgeObj</code> - a <a href="https://github.com/dagrejs/graphlib/wiki/API-Reference#node-and-edge-representation">Graphlib item ID</a>,</li>
168+
<li><code>glGraph</code> - the original Graphlib graph object,</li>
169+
<li><code>graph</code> - the JointJS Graph object to be populated (a newly created <code>joint.dia.Graph</code> by default),</li>
170+
<li><code>opt</code> - the options object provided to <code>fromGraphLib()</code>.</li>
171+
</ul>
172+
You can implement your own logic inside the two callbacks to support your use case.
173+
<br/><br/>
174+
The example below illustrates how Graphlib node/edge labels can be used to store additional information for each item. These labels are accessed in the callbacks as <code>nodeData</code> / <code>edgeData</code>, and the information within is used to specify the position, size, and label text of the Element / Link being created. (Note: In Graphlib, edge source id / edge target id are specified via <code>nodeId</code> of the respective node; we need to set the <code>id</code> of generated Elements as <code>nodeId</code> so that the generated Links can connect the generated Elements.) The callbacks work by adding the generated Element / Link to the <code>graph</code> in the end (e.g. via the <code>graph.addCell()</code> function):
175+
<pre><code>import { shapes } from '@joint/core';
176+
import { DirectedGraph } from '@joint/layout-directed-graph';
166177
import * as graphlib from 'graphlib';
167178

168179
// Create a graph in Graphlib:
169-
var glGraph = new graphlib.Graph();
170-
glGraph.setNode(1);
171-
glGraph.setNode(2);
172-
glGraph.setNode(3);
173-
glGraph.setEdge(1, 2);
174-
glGraph.setEdge(2, 3);
180+
const glGraph = new graphlib.Graph();
181+
glGraph.setNode(1, { x: 50, y: 50, width: 100, height: 50, label: 'A' });
182+
glGraph.setNode(2, { x: 50, y: 150, width: 100, height: 50, label: 'B' });
183+
glGraph.setNode(3, { x: 50, y: 250, width: 100, height: 50, label: 'C' });
184+
glGraph.setEdge(1, 2, { label: 'Hello' });
185+
glGraph.setEdge(2, 3, { label: 'World!' });
175186

176187
// Get a JointJS representation of the Graphlib graph:
177-
var graph = DirectedGraph.fromGraphLib(glGraph, {
178-
importNode: function(node) {
179-
return new joint.shapes.standard.Rectangle({
180-
position: { x: node.x, y: node.y },
181-
size: { width: node.width, height: node.height }
188+
const graph = DirectedGraph.fromGraphLib(glGraph, {
189+
importNode: (nodeId, glGraph, graph, opt) => {
190+
const nodeData = glGraph.node(nodeId);
191+
const element = new shapes.standard.Rectangle({
192+
id: nodeId,
193+
position: { x: nodeData.x, y: nodeData.y },
194+
size: { width: nodeData.width, height: nodeData.height },
195+
attrs: { label: { text: nodeData.label }}
182196
});
197+
graph.addCell(element);
183198
},
184-
importEdge: function(edge) {
185-
return new joint.shapes.standard.Link({
186-
source: { id: edge.v },
187-
target: { id: edge.w }
199+
importEdge: (edgeObj, glGraph, graph, opt) => {
200+
const edgeData = glGraph.edge(edgeObj);
201+
const link = new shapes.standard.Link({
202+
source: { id: edgeObj.v },
203+
target: { id: edgeObj.w },
204+
labels: [{ attrs: { text: { text: edgeData.label }}}]
188205
});
206+
graph.addCell(link);
189207
}
190208
});</code></pre>
191209
</td>

packages/joint-layout-directed-graph/DirectedGraph.mjs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,31 @@ export const DirectedGraph = {
3737
/**
3838
* @private
3939
*/
40-
importElement: function(node, glGraph, graph, opt) {
40+
importElement: function(nodeId, glGraph, graph, opt) {
4141

42-
var element = graph.getCell(node);
43-
var glNode = glGraph.node(node);
42+
const element = graph.getCell(nodeId);
43+
const nodeData = glGraph.node(nodeId);
4444

4545
if (opt.setPosition) {
46-
opt.setPosition(element, glNode);
46+
opt.setPosition(element, nodeData);
4747
} else {
4848
element.set('position', {
49-
x: glNode.x - glNode.width / 2,
50-
y: glNode.y - glNode.height / 2
49+
x: nodeData.x - (nodeData.width / 2),
50+
y: nodeData.y - (nodeData.height / 2)
5151
});
5252
}
5353
},
5454

5555
/**
5656
* @private
5757
*/
58-
importLink: function(edge, glGraph, graph, opt) {
58+
importLink: function(edgeObj, glGraph, graph, opt) {
5959

6060
const SIMPLIFY_THRESHOLD = 0.001;
6161

62-
const link = graph.getCell(edge.name);
63-
const glEdge = glGraph.edge(edge);
64-
const points = glEdge.points || [];
62+
const link = graph.getCell(edgeObj.name);
63+
const edgeData = glGraph.edge(edgeObj);
64+
const points = edgeData.points || [];
6565
const polyline = new g.Polyline(points);
6666

6767
// check the `setLinkVertices` here for backwards compatibility
@@ -79,8 +79,8 @@ export const DirectedGraph = {
7979
}
8080
}
8181

82-
if (opt.setLabels && ('x' in glEdge) && ('y' in glEdge)) {
83-
const labelPosition = { x: glEdge.x, y: glEdge.y };
82+
if (opt.setLabels && ('x' in edgeData) && ('y' in edgeData)) {
83+
const labelPosition = { x: edgeData.x, y: edgeData.y };
8484
if (util.isFunction(opt.setLabels)) {
8585
opt.setLabels(link, labelPosition, points);
8686
} else {
@@ -134,7 +134,7 @@ export const DirectedGraph = {
134134
setNodeLabel: opt.exportElement,
135135
setEdgeLabel: opt.exportLink,
136136
setEdgeName: function(link) {
137-
// Graphlib edges have no ids. We use edge name property
137+
// Graphlib edges have no ids. We use `edgeObj.name` property
138138
// to store and retrieve ids instead.
139139
return link.id;
140140
}
@@ -221,14 +221,10 @@ export const DirectedGraph = {
221221
var graph = opt.graph || new dia.Graph();
222222

223223
// Import all nodes.
224-
glGraph.nodes().forEach(function(node) {
225-
importNode(node, glGraph, graph, opt);
226-
});
224+
glGraph.nodes().forEach((nodeId) => importNode(nodeId, glGraph, graph, opt));
227225

228226
// Import all edges.
229-
glGraph.edges().forEach(function(edge) {
230-
importEdge(edge, glGraph, graph, opt);
231-
});
227+
glGraph.edges().forEach((edgeObj) => importEdge(edgeObj, glGraph, graph, opt));
232228

233229
return graph;
234230
},

packages/joint-layout-directed-graph/test/index.js

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,89 @@ QUnit.module('DirectedGraph', function(hooks) {
1515

1616
assert.equal(typeof DirectedGraph.fromGraphLib, 'function');
1717
});
18+
19+
QUnit.test('should correctly convert a graphlib graph into JointJS graph', function(assert) {
20+
21+
const glGraph = new graphlib.Graph();
22+
glGraph.setNode(1, { x: 50, y: 50, width: 100, height: 50, label: 'A' });
23+
glGraph.setNode(2, { x: 50, y: 150, width: 100, height: 50, label: 'B' });
24+
glGraph.setNode(3, { x: 50, y: 250, width: 100, height: 50, label: 'C' });
25+
glGraph.setEdge(1, 2, { label: 'Hello' });
26+
glGraph.setEdge(2, 3, { label: 'World!' });
27+
28+
const graph = DirectedGraph.fromGraphLib(glGraph, {
29+
importNode: (nodeId, glGraph, graph, _opt) => {
30+
const nodeData = glGraph.node(nodeId);
31+
const element = new joint.shapes.standard.Rectangle({
32+
id: nodeId,
33+
position: { x: nodeData.x, y: nodeData.y },
34+
size: { width: nodeData.width, height: nodeData.height },
35+
attrs: { label: { text: nodeData.label }}
36+
});
37+
graph.addCell(element);
38+
},
39+
importEdge: (edgeObj, glGraph, graph, _opt) => {
40+
const edgeData = glGraph.edge(edgeObj);
41+
const link = new joint.shapes.standard.Link({
42+
source: { id: edgeObj.v },
43+
target: { id: edgeObj.w },
44+
labels: [{ attrs: { text: { text: edgeData.label }}}]
45+
});
46+
graph.addCell(link);
47+
}
48+
});
49+
50+
// elements
51+
const elements = graph.getElements();
52+
assert.equal(elements.length, 3);
53+
let id, x, y, width, height, elementLabel;
54+
55+
(id = elements[0].id);
56+
assert.equal(id, '1');
57+
({ x, y } = elements[0].position());
58+
assert.deepEqual({ x, y }, { x: 50, y: 50 });
59+
({ width, height} = elements[0].size());
60+
assert.deepEqual({ width, height }, {width: 100, height: 50 });
61+
(elementLabel = elements[0].attr('label/text'));
62+
assert.equal(elementLabel, 'A');
63+
64+
(id = elements[1].id);
65+
assert.equal(id, '2');
66+
({ x, y } = elements[1].position());
67+
assert.deepEqual({ x, y }, { x: 50, y: 150 });
68+
({ width, height} = elements[1].size());
69+
assert.deepEqual({ width, height }, {width: 100, height: 50 });
70+
(elementLabel = elements[1].attr('label/text'));
71+
assert.equal(elementLabel, 'B');
72+
73+
(id = elements[2].id);
74+
assert.equal(id, '3');
75+
({ x, y } = elements[2].position());
76+
assert.deepEqual({ x, y }, { x: 50, y: 250 });
77+
({ width, height} = elements[2].size());
78+
assert.deepEqual({ width, height }, {width: 100, height: 50 });
79+
(elementLabel = elements[2].attr('label/text'));
80+
assert.equal(elementLabel, 'C');
81+
82+
// links
83+
const links = graph.getLinks();
84+
assert.equal(links.length, 2);
85+
let source, target, linkLabel;
86+
87+
(source = links[0].source().id);
88+
assert.equal(source, '1');
89+
(target = links[0].target().id);
90+
assert.equal(target, '2');
91+
(linkLabel = links[0].label(0).attrs.text.text);
92+
assert.equal(linkLabel, 'Hello');
93+
94+
(source = links[1].source().id);
95+
assert.equal(source, '2');
96+
(target = links[1].target().id);
97+
assert.equal(target, '3');
98+
(linkLabel = links[1].label(0).attrs.text.text);
99+
assert.equal(linkLabel, 'World!');
100+
});
18101
});
19102

20103
QUnit.module('toGraphLib(jointGraph[, opt])', function(hooks) {
@@ -187,7 +270,6 @@ QUnit.module('DirectedGraph', function(hooks) {
187270
assert.deepEqual({ x, y }, { x: 5, y: 210 });
188271
});
189272

190-
191273
QUnit.test('should return a rectangle representing the graph bounding box', function(assert) {
192274

193275
var bbox;

0 commit comments

Comments
 (0)