Skip to content
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

Show all values in a series on mouse hover #2245

Merged
merged 4 commits into from
Sep 21, 2017
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 196 additions & 2 deletions src/mmw/js/src/core/chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,202 @@ function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax,
}

d3.select(svg)
.datum(data)
.call(chart);
.datum(data)
.call(chart);

// filter definition for drop shadows
var filter =
d3.select(svg)
.selectAll("defs")
.append("filter")
.attr("id","drop-shadow")
.attr("height", "120%");

filter.append("feGaussianBlur")
.attr("in", "SourceAlpha")
.attr("stdDeviation", 2)
.attr("result", "blur");

filter.append("feOffset")
.attr("in", "blur")
.attr("dx", 2)
.attr("dy", 2)
.attr("result", "offsetBlur");

var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode")
.attr("in", "offsetBlur");
feMerge.append("feMergeNode")
.attr("in", "SourceGraphic");

var chartContainerId = chartEl.id + "-chart-container";

// iterate over the series names to add the svg elements to the chart
_.forEach(["nv-series-0", "nv-series-1", "nv-series-2"],
function(el) {
d3.select("#" + chartEl.id).select("svg").selectAll("g." + el).each(function() {
var g = d3.select(this);
var parentG = d3.select(this.parentNode);
parentG.attr("id", chartContainerId);

g.selectAll(".nv-bar").each(function(bar) {
var b = d3.select(this);
var barWidth = b.attr("width");
var bgWidth = 40.0;
var bgHeight = 20.0;
var bgVertOffset = -20.0;

var dialogTickSize = 5.0;

// add drop-shadow
parentG.append("rect")
.each(function () {
d3.select(this).attr({
"transform": b.attr("transform"),
"y": (parseFloat(b.attr("y")) + bgVertOffset - 1),
"x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2) - (bgWidth / 2) - 1),
"width": bgWidth + 2,
"height": bgHeight + 2,
"stroke": 1,
"class": ("bar-value-text-bg-shadow_" + el)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of appending with an underscore, prefer appending with a space so these elements can be targeted via CSS like .bar-value-test-bg-shadow

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff --git a/src/mmw/js/src/core/chart.js b/src/mmw/js/src/core/chart.js
index a462dbe..508e071 100644
--- a/src/mmw/js/src/core/chart.js
+++ b/src/mmw/js/src/core/chart.js
@@ -437,7 +437,7 @@ function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax,
                                     "width": bgWidth + 2,
                                     "height": bgHeight + 2,
                                     "stroke": 1,
-                                    "class": ("bar-value-text-bg-shadow_" + el)
+                                    "class": ("bar-value-text-bg-shadow " + el)
                                 });
 
                                 d3.select(this).style({
@@ -459,7 +459,7 @@ function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax,
                                     "height": bgHeight,
                                     "stroke": "#aaaaaa",
                                     "stroke-width": 0.5,
-                                    "class": ("bar-value-text-bg_" + el)
+                                    "class": ("bar-value-text-bg " + el)
                                 });
 
                                 d3.select(this).style({
@@ -481,7 +481,7 @@ function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax,
                                     "x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2)),
                                     "width": dialogTickSize,
                                     "height": dialogTickSize,
-                                    "class": ("bar-value-text-bg-tick_" + el)
+                                    "class": ("bar-value-text-bg-tick " + el)
                                 });
 
                                 d3.select(this).style({
@@ -510,7 +510,7 @@ function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax,
                                     "font-size": "0.8rem",
                                     "font-weight": "normal",
                                     "font-family": "helvetica",
-                                    "class": ("bar-value-text_" + el)
+                                    "class": ("bar-value-text " + el)
                                 });
 
                                 d3.select(this).style({
@@ -531,21 +531,21 @@ function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax,
                     .selectAll("rect.nv-bar")
                     .on("mouseover", function () {
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text_" + el)
+                            .selectAll(".bar-value-text." + el)
                             .style("opacity", "1");
 
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text-bg-shadow_" + el)
+                            .selectAll(".bar-value-text-bg-shadow." + el)
                             .style("opacity", "0.5")
                             .style("fill-opacity", "0.5");
 
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text-bg_" + el)
+                            .selectAll(".bar-value-text-bg." + el)
                             .style("opacity", "1")
                             .style("fill-opacity", "1");
 
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text-bg-tick_" + el)
+                            .selectAll(".bar-value-text-bg-tick." + el)
                             .style("opacity", "1")
                             .style("fill-opacity", "1");
                     });
@@ -556,21 +556,21 @@ function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax,
                     .selectAll("rect.nv-bar")
                     .on("mouseout", function () {
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text_" + el)
+                            .selectAll(".bar-value-text." + el)
                             .style("opacity", "0");
 
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text-bg-shadow_" + el)
+                            .selectAll(".bar-value-text-bg-shadow." + el)
                             .style("opacity", "0")
                             .style("fill-opacity", "0");
 
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text-bg_" + el)
+                            .selectAll(".bar-value-text-bg." + el)
                             .style("opacity", "0")
                             .style("fill-opacity", "0");
 
                         d3.select("#" + chartContainerId)
-                            .selectAll(".bar-value-text-bg-tick_" + el)
+                            .selectAll(".bar-value-text-bg-tick." + el)
                             .style("opacity", "0")
                             .style("fill-opacity", "0");
                     });

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can then do something like this:

diff --git a/src/mmw/sass/pages/_compare.scss b/src/mmw/sass/pages/_compare.scss
index 3ebf563..d73574b 100644
--- a/src/mmw/sass/pages/_compare.scss
+++ b/src/mmw/sass/pages/_compare.scss
@@ -463,6 +463,13 @@ $compare-chart-table-height: calc(100vh - #{$height-lg} - #{$compare-footer-heig
     rect {
       transition: opacity 0.3s ease-in-out;
     }
+
+    .bar-value-text-bg-shadow,
+    .bar-value-text-bg,
+    .bar-value-text-bg-tick,
+    .bar-value-text {
+      pointer-events: none;
+    }
   }
 
   .compare-scenario-gradient {

Which allows hovering on the stacked chart in areas obscured by hidden tooltips to function correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason it's so specific is that those elements have to be manipulated via D3 later on. Could D3 selection be performed (easily) with a lower degree of specificity in the elements?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did that CSS get tested on the D3 charts? I observed that CSS and D3/SVG elements didn't seem to play very nice together.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As shown in the diff, a CSS class of abcd_wxyz can be accessed only via .abcd_wxyz, but one of abcd wxyz can be accessed specifically with .abcd.wxyz, but also in other groupings with .abcd and .wxyz. So the existing selection behavior is preserved by switching to spaces and dots instead of the underscore, and enables this CSS targeting after the fact.

});

d3.select(this).style({
"fill": "#aaaaaa",
"opacity": 0,
"fill-opacity": 0,
"filter": "url(#drop-shadow)"
});
});

// add background shape
parentG.append("rect")
.each(function() {
d3.select(this).attr({
"transform": b.attr("transform"),
"y": (parseFloat(b.attr("y")) + bgVertOffset),
"x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2) - (bgWidth / 2)),
"width": bgWidth,
"height": bgHeight,
"stroke": "#aaaaaa",
"stroke-width": 0.5,
"class": ("bar-value-text-bg_" + el)
});

d3.select(this).style({
"fill": "white",
"opacity": "0",
"fill-opacity": "0",
"stroke-opacity": 0.75
});
});

// add tick mark under shape
parentG.append("rect")
.each(function() {
var rotatedX = parseFloat(b.attr("x")) + parseFloat(barWidth) / 2;
var rotatedY = parseFloat(b.attr("y")) + bgHeight + bgVertOffset - (dialogTickSize / 2);
d3.select(this).attr({
"transform": (b.attr("transform") + " rotate(45," + rotatedX +"," + rotatedY + ")"),
"y": (parseFloat(b.attr("y")) + bgHeight + bgVertOffset - dialogTickSize),
"x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2)),
"width": dialogTickSize,
"height": dialogTickSize,
"class": ("bar-value-text-bg-tick_" + el)
});

d3.select(this).style({
"fill": "white",
"fill-opacity": "0",
"opacity": "0"
});
});

// add the text value
parentG.append("text")
.each(function() {
d3.select(this).text(function() {
if (bar.y === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, here's where the zero values seem to get filtered out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these tooltips can't be hidden, I'd rather they say "0.00" than be empty

image

return;
}
return parseFloat(bar.y).toFixed(2);
});

var width = this.getBBox().width;

d3.select(this).attr({
"transform": b.attr("transform"),
"y": (parseFloat(b.attr("y")) + 15 + bgVertOffset),
"x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2) - (width / 2)),
"font-size": "0.8rem",
"font-weight": "normal",
"font-family": "helvetica",
"class": ("bar-value-text_" + el)
});

d3.select(this).style({
"stroke": "black",
"stroke-width": 0.2,
"opacity": 0,
"font-weight": "normal",
"font-family": "arial"
});

});
});
});

d3.select(svg)
.select("#" + chartContainerId)
.selectAll("g." + el)
.selectAll("rect.nv-bar")
.on("mouseover", function () {
d3.select("#" + chartContainerId)
.selectAll(".bar-value-text_" + el)
.style("opacity", "1");

d3.select("#" + chartContainerId)
.selectAll(".bar-value-text-bg-shadow_" + el)
.style("opacity", "0.5")
.style("fill-opacity", "0.5");

d3.select("#" + chartContainerId)
.selectAll(".bar-value-text-bg_" + el)
.style("opacity", "1")
.style("fill-opacity", "1");

d3.select("#" + chartContainerId)
.selectAll(".bar-value-text-bg-tick_" + el)
.style("opacity", "1")
.style("fill-opacity", "1");
});

d3.select(svg)
.select("#" + chartContainerId)
.selectAll("g." + el)
.selectAll("rect.nv-bar")
.on("mouseout", function () {
d3.select("#" + chartContainerId)
.selectAll(".bar-value-text_" + el)
.style("opacity", "0");

d3.select("#" + chartContainerId)
.selectAll(".bar-value-text-bg-shadow_" + el)
.style("opacity", "0")
.style("fill-opacity", "0");

d3.select("#" + chartContainerId)
.selectAll(".bar-value-text-bg_" + el)
.style("opacity", "0")
.style("fill-opacity", "0");

d3.select("#" + chartContainerId)
.selectAll(".bar-value-text-bg-tick_" + el)
.style("opacity", "0")
.style("fill-opacity", "0");
});
});


return chart;
}, onRenderComplete);
Expand Down