diff --git a/packages/joint-layout-directed-graph/DirectedGraph.mjs b/packages/joint-layout-directed-graph/DirectedGraph.mjs index 5761e0be9..50e9d3e56 100644 --- a/packages/joint-layout-directed-graph/DirectedGraph.mjs +++ b/packages/joint-layout-directed-graph/DirectedGraph.mjs @@ -94,17 +94,6 @@ 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; @@ -120,6 +109,22 @@ export const DirectedGraph = { // This is not needed anymore. graphOrCells = null; + // Check that we are not trying to connect a child to a container: + // - child to a container + // - container to a child + // - container to a container + graph.getLinks().forEach((link) => { + const source = link.getSourceElement(); + const target = link.getTargetElement(); + // is container = is element && has at least one embedded element + const isSourceContainer = source && (source.getEmbeddedCells().filter((cell) => cell.isElement()).length !== 0); + const isTargetContainer = target && (target.getEmbeddedCells().filter((cell) => cell.isElement()).length !== 0); + if ((isSourceContainer && target) || (source && isTargetContainer)) { + // see https://github.com/clientIO/joint/issues/455 + throw new Error('DirectedGraph: It is not possible to connect a child to a container.'); + } + }); + opt = util.defaults(opt || {}, { resizeClusters: true, clusterPadding: 10, @@ -127,8 +132,7 @@ export const DirectedGraph = { exportLink: this.exportLink }); - // create a graphlib.Graph that represents the joint.dia.Graph - // var glGraph = graph.toGraphLib({ + // Create a graphlib.Graph that represents the joint.dia.Graph var glGraph = DirectedGraph.toGraphLib(graph, { directed: true, // We are about to use edge naming feature. @@ -170,9 +174,8 @@ export const DirectedGraph = { // Set the option object for the graph label. glGraph.setGraph(glLabel); - // Executes the layout. - // - See https://stackoverflow.com/a/19728876/2263595 - this.tryLayout(glGraph, { debugTiming: !!opt.debugTiming }); + // Execute the layout. + dagreUtil.layout(glGraph, { debugTiming: !!opt.debugTiming }); // Wrap all graph changes into a batch. graph.startBatch('layout'); diff --git a/packages/joint-layout-directed-graph/test/index.js b/packages/joint-layout-directed-graph/test/index.js index 25d1d71c2..db1f3143e 100644 --- a/packages/joint-layout-directed-graph/test/index.js +++ b/packages/joint-layout-directed-graph/test/index.js @@ -322,19 +322,26 @@ QUnit.module('DirectedGraph', function(hooks) { 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 } }) + new joint.shapes.standard.Rectangle({ position: { x: 400, y: 50 }, size: { width: 300, height: 300 } }), + new joint.shapes.standard.Rectangle({ position: { x: 525, y: 175 }, size: { width: 50, height: 50 } }), ]; elements[0].embed(elements[1]); + elements[2].embed(elements[3]); const links = [ - new joint.shapes.standard.Link({ source: { id: elements[0].id }, target: { id: elements[1].id }}), // container -> its child + // this throws error: 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 + new joint.shapes.standard.Link({ source: { id: elements[1].id }, target: { id: elements[2].id }}), // child -> unrelated container + 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[0].id }, target: { id: elements[3].id }}), // container -> unrelated child + new joint.shapes.standard.Link({ source: { id: elements[0].id }, target: { id: elements[2].id }}), // container -> unrelated container + // this is ok: + new joint.shapes.standard.Link({ source: { id: elements[1].id }, target: { id: elements[3].id }}), // child -> unrelated child + new joint.shapes.standard.Link({ source: { id: elements[1].id }, target: { x: 0, y: 0 }}), // child -> point + new joint.shapes.standard.Link({ source: { x: 0, y: 0 }, target: { id: elements[1].id }}), // point -> child + new joint.shapes.standard.Link({ source: { id: elements[0].id }, target: { x: 0, y: 0 }}), // container -> point + new joint.shapes.standard.Link({ source: { x: 0, y: 0 }, target: { id: elements[0].id }}), // point -> container ]; let cells; @@ -357,6 +364,38 @@ QUnit.module('DirectedGraph', function(hooks) { assert.throws(() => { DirectedGraph.layout(graph); }, error); + + cells = elements.concat([links[3]]); + graph.resetCells(cells); + assert.throws(() => { + DirectedGraph.layout(graph); + }, error); + + cells = elements.concat([links[4]]); + graph.resetCells(cells); + assert.throws(() => { + DirectedGraph.layout(graph); + }, error); + + cells = elements.concat([links[5]]); + graph.resetCells(cells); + assert.ok(DirectedGraph.layout(graph) instanceof g.Rect); + + cells = elements.concat([links[6]]); + graph.resetCells(cells); + assert.ok(DirectedGraph.layout(graph) instanceof g.Rect); + + cells = elements.concat([links[7]]); + graph.resetCells(cells); + assert.ok(DirectedGraph.layout(graph) instanceof g.Rect); + + cells = elements.concat([links[8]]); + graph.resetCells(cells); + assert.ok(DirectedGraph.layout(graph) instanceof g.Rect); + + cells = elements.concat([links[9]]); + graph.resetCells(cells); + assert.ok(DirectedGraph.layout(graph) instanceof g.Rect); }) }); });