diff --git a/assets/css/main.css b/assets/css/main.css index 811e3987..7547a9d1 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -348,3 +348,22 @@ div.dataTables_scrollHead th:first-child { stroke: #000; shape-rendering: crispEdges; } + +/* Remove focus from Bar Chart dropdown after selection is made. */ +.select2-selection:focus { + outline: none; +} + +/* Tooltip for Bar Chart */ +.toolTip { + position: absolute; + display: none; + min-width: 80px; + height: auto; + background: #333333; + border: 1px solid #000000; + border-radius: 4px; + padding: 14px; + text-align: center; + color: #ffffff; +} \ No newline at end of file diff --git a/assets/js/barChart.js b/assets/js/barChart.js index 3af9ba7b..170ab406 100644 --- a/assets/js/barChart.js +++ b/assets/js/barChart.js @@ -61,6 +61,8 @@ class BarChart { // Creates a d3 axis given a scale (takes care of tick marks and labels) let yAxis = d3.svg.axis() .scale(yScale) + // Limit label length to 50 characters plus ellipses. + .tickFormat(BarChart.trimName) .orient('left'); // Creates a graphic tag () for each bar in the chart @@ -68,14 +70,28 @@ class BarChart { .data(formattedData) .enter().append('g'); + // Clear tooltip div created when inadvertently triggered during dropdown selection. + $( ".toolTip" ).remove(); + + let tooltip = d3.select("body").append("div").attr("class", "toolTip"); + this.rects = bars.append('rect') .attr('y', function(d) { return yScale(d.name); }) - .attr('height', yScale.rangeBand()); + .attr('height', yScale.rangeBand()) + // Add a tooltip to the bar. + .on("mouseover", function (d) { tooltip.style("display", "inline-block"); }) + .on("mousemove", function (d) { + tooltip + .style("left", d3.event.pageX - 50 + "px") + .style("top", d3.event.pageY - 70 + "px") + .text((d.name + ' (' + d.val + ')')); + }) + .on("mouseout", function (d) { tooltip.style("display", "none"); }); this.texts = bars.append('text') - .attr('y', function(d) { return yScale(d.name); }) + .attr('y', function (d) { return yScale(d.name); }) .attr('dy', '1.2em') - .text(function(d){ return '(' + d.val + ')'; }) + .text(function (d) { return '(' + d.val + ')'; }) .style('text-anchor', 'start'); // Adds the y-axis to the chart if data exists @@ -85,9 +101,26 @@ class BarChart { .call(yAxis); } + // Add a tooltip to the y-axis labels. + chart.selectAll(".y.axis .tick") + .on("mouseover", function (d) { tooltip.style("display", "inline-block"); }) + .on("mousemove", function (d) { + let result = $.grep(formattedData, function (e) { return e.name === d; }); + let displayValue = ' (' + result[0].val + ')'; + tooltip + .style("left", d3.event.pageX - 50 + "px") + .style("top", d3.event.pageY - 70 + "px") + .text((d + displayValue)); + }) + .on("mouseout", function (d) { tooltip.style("display", "none"); }); + this.draw(); } + static trimName(name) { + return name.substring(0, 50) + (name.length > 50 ? " ..." : ""); + } + // Redraws chart and sets width based on available chart width. // User needs to call this function whenever the width of the chart changes. draw() { @@ -121,7 +154,7 @@ class BarChart { // Returns the pixel width of the string with the longest length maxNameWidth(data) { - let names = data.map(function(d) { return d.name; }); + let names = data.map(function(d) { return d.trimmedName; }); let maxStr = ''; $.each(names, function(i, name) { @@ -151,6 +184,7 @@ class BarChart { let chartData = $.map(count, function(val, key) { return { name: key, + trimmedName: BarChart.trimName(key), val: val }; });