Skip to content

fix(layout.DirectedGraph): better error message when dagre fails to connect to container #2838

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Prev Previous commit
Next Next commit
separate try-catch block for performance in old V8
  • Loading branch information
zbynekstara committed May 20, 2025
commit 0e1d3abb6e7b4fb1e58d53f2f8f4482cb0987b4a
31 changes: 17 additions & 14 deletions packages/joint-layout-directed-graph/DirectedGraph.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ export const DirectedGraph = {
}
},

tryLayout: function(glGraph, opt) {
try {
dagreUtil.layout(glGraph, opt);
} catch (err) {
// ASSUMPTION: Only one error is relevant here:
// - `Uncaught TypeError: Cannot set property 'rank' of undefined`
// - See https://github.com/clientIO/joint/issues/455
throw new Error('DirectedGraph: It is not possible to connect a child to a container.');
}
},

layout: function(graphOrCells, opt) {

var graph;
Expand Down Expand Up @@ -180,20 +191,12 @@ export const DirectedGraph = {
}

// Executes the layout.
try {
dagreUtil.layout(glGraph, {
debugTiming: !!opt.debugTiming,
disableOptimalOrderHeuristic: !!opt.disableOptimalOrderHeuristic,
customOrder,
});
} catch (err) {
if (err instanceof TypeError) {
// see https://github.com/clientIO/joint/issues/455
throw new Error('DirectedGraph: It is not possible to connect a child to a container.');
} else {
throw err;
}
}
// - See https://stackoverflow.com/a/19728876/2263595
this.tryLayout(glGraph, {
debugTiming: !!opt.debugTiming,
disableOptimalOrderHeuristic: !!opt.disableOptimalOrderHeuristic,
customOrder,
});

// Wrap all graph changes into a batch.
graph.startBatch('layout');
Expand Down
41 changes: 41 additions & 0 deletions packages/joint-layout-directed-graph/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -583,5 +583,46 @@ QUnit.module('DirectedGraph', function(hooks) {
}
});
});

QUnit.test('should throw an understandable error when trying to connect a child to a container', function(assert) {
const elements = [
new joint.shapes.standard.Rectangle({ position: { x: 50, y: 50 }, size: { width: 300, height: 300 } }),
new joint.shapes.standard.Rectangle({ position: { x: 175, y: 175 }, size: {width: 50, height: 50 } }),
new joint.shapes.standard.Rectangle({ position: { x: 400, y: 150 }, size: { width: 100, height: 100 } }),
new joint.shapes.standard.Rectangle({ position: { x: 150, y: 400 }, size: { width: 100, height: 100 } })
];

elements[0].embed(elements[1]);

const links = [
new joint.shapes.standard.Link({ source: { id: elements[0].id }, target: { id: elements[1].id }}), // container -> its child
new joint.shapes.standard.Link({ source: { id: elements[1].id }, target: { id: elements[0].id }}), // child -> its container
new joint.shapes.standard.Link({ source: { id: elements[0].id }, target: { id: elements[2].id }}) // container -> unrelated element
// these are ok:
//new joint.shapes.standard.Link({ source: { id: elements[1].id }, target: { id: elements[2].id }}), // child -> unrelated element
//new joint.shapes.standard.Link({ source: { id: elements[2].id }, target: { id: elements[3].id }}) // unrelated element -> unrelated element
];

let cells;
const error = new Error('DirectedGraph: It is not possible to connect a child to a container.');

cells = elements.concat([links[0]]);
graph.resetCells(cells);
assert.throws(() => {
DirectedGraph.layout(graph);
}, error);

cells = elements.concat([links[1]]);
graph.resetCells(cells);
assert.throws(() => {
DirectedGraph.layout(graph);
}, error);

cells = elements.concat([links[2]]);
graph.resetCells(cells);
assert.throws(() => {
DirectedGraph.layout(graph);
}, error);
})
});
});