From a1efc5bbed1a2d372781ba08f09c7437d16d0d42 Mon Sep 17 00:00:00 2001 From: Magnus Jacobsson Date: Sun, 28 Apr 2024 15:18:10 +0200 Subject: [PATCH 1/2] fix cluster's box shadows nodes inside it after animated transition Ensure that the elements are inserted into the document in the order given by the data in order to avoid clusters being inserted after nodes and therefore covering them. From https://d3js.org/d3-selection/joining#selection_data: "If a key function is specified, the order of elements in the selection may not match their order in the document; use selection.order or selection.sort as needed" Fixes https://github.com/magjac/d3-graphviz/issues/308. --- CHANGELOG.md | 3 +++ src/render.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e46cb671..15c26493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +* Cluster's box shadows nodes inside it after animated transition + ## [5.3.0] – 2024-02-11 ### Changed diff --git a/src/render.js b/src/render.js index 8574f252..9c3007b6 100644 --- a/src/render.js +++ b/src/render.js @@ -98,7 +98,8 @@ function _render(callback) { childrenExit = childrenExit .remove() children = childrenEnter - .merge(children); + .merge(children) + .order(); children.each(attributeElement); } From 6994e98fa8028b84d7d817c688d0b455ce391916 Mon Sep 17 00:00:00 2001 From: Magnus Jacobsson Date: Mon, 29 Apr 2024 20:31:27 +0200 Subject: [PATCH 2/2] add test/cluster-transition-test.js Tests that https://github.com/magjac/d3-graphviz/issues/308 is fixed. --- test/cluster-transition-test.js | 103 ++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 test/cluster-transition-test.js diff --git a/test/cluster-transition-test.js b/test/cluster-transition-test.js new file mode 100644 index 00000000..0e480462 --- /dev/null +++ b/test/cluster-transition-test.js @@ -0,0 +1,103 @@ +import assert from "assert"; +import it from "./it.js"; +import jsdom from "./jsdom.js"; +import * as d3 from "d3-selection"; +import * as d3_transition from "d3-transition"; +import * as d3_graphviz from "../index.js"; + +it("graphviz().render() transitions a cluster graph into another and renders all cluster before nodes.", async () => { + + const window = global.window = jsdom('
Hello World
'); + global.document = window.document + + let graphviz; + + await new Promise(resolve => { + graphviz = d3_graphviz.graphviz("#graph") + .on('initEnd', resolve); + }); + + const dots = [ +` +digraph { + graph[style=filled] + subgraph "cluster_a" { + fillcolor=purple + a + } +} +`, +` +digraph { + graph[style=filled] + subgraph "cluster_b" { + fillcolor=orange + b + } + subgraph "cluster_c" { + fillcolor=limegreen + c + } + subgraph "cluster_d" { + fillcolor=cyan + d + } + subgraph "cluster_a" { + fillcolor=purple + a + } +} +`, + ]; + + await new Promise(resolve => { + graphviz + .zoom(false) + .dot(dots[0]) + .on("end", resolve) + .render(); + + assert.ok(graphviz._active, 'Rendering is active after the 1st rendering has been initiated'); + + assert.equal(d3.selectAll('.node').size(), 1, 'Number of initial nodes'); + assert.equal(d3.selectAll('.edge').size(), 0, 'Number of initial edges'); + assert.equal(d3.selectAll('.cluster').size(), 1, 'Number of initial clusters'); + assert.equal(d3.selectAll('polygon').size(), 2, 'Number of initial polygons'); + assert.equal(d3.selectAll('ellipse').size(), 1, 'Number of initial ellipses'); + assert.equal(d3.selectAll('path').size(), 0, 'Number of initial paths'); + }); + + await new Promise(resolve => { + graphviz + .dot(dots[1]) + .transition(d3_transition.transition().duration(100)) + .on("end", resolve) + .render(); + }); + + assert.equal(d3.selectAll('.node').size(), 4, 'Number of nodes after 1st transition'); + assert.equal(d3.selectAll('.edge').size(), 0, 'Number of edges after 1st transition'); + assert.equal(d3.selectAll('.cluster').size(), 4, 'Number of clusters after 1st transition'); + assert.equal(d3.selectAll('polygon').size(), 5, 'Number of polygons after 1st transition'); + assert.equal(d3.selectAll('ellipse').size(), 4, 'Number of ellipses after 1st transition'); + assert.equal(d3.selectAll('path').size(), 0, 'Number of paths after 1st transition'); + assert.equal(d3.selectAll('g').size(), 9, 'Number of paths after 1st transition'); + + let seenClusters = {}; + let seenNodes = {}; + d3.selectAll('g>title').each(function () { + const titleText = this.textContent; + if (titleText.startsWith("cluster_")) { + const clusterName = titleText; + seenClusters[clusterName] = true; + const expectedNode = titleText.replace("cluster_", ""); + assert.equal(seenNodes[expectedNode], null, "Node shall not be seen before cluster"); + } + else { + const nodeName = titleText; + seenNodes[nodeName] = true; + const expectedCluster = "cluster_" + titleText; + assert.equal(seenClusters[expectedCluster], true, "Cluster shall be seen before node"); + } + }); +});