Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
19 changes: 19 additions & 0 deletions assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,22 @@ html, body {
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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: remove extra space.

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;
}
31 changes: 26 additions & 5 deletions assets/js/barChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class BarChart {

// Create scaling for y that converts formattedData names to pixels
let yScale = d3.scale.ordinal()
.domain(formattedData.map(function(d) {return d.name; }))
.domain(formattedData.map(function(d) {return d.trimName; }))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we want to use the full name here. Consider the case where two names only differ on character 51 and we truncate on character 50.

   [
       "some...long...name1"
       "some...long...name2"
   ]

After truncation we would get:

  [
      "some...long...name"
      "some...long...name"
  ]

Each bar in the chart needs a distinct name in the y-axis "domain" in order to be drawn at a distinct height along the y-axis. In the case above, there would be no way for the y axis to distinguish the names, and you would probably get overlapping bars at that same position instead of two different bars at different heights.

It looks like one possible solution might use be to use the d3 axis's .tickFormat() to set the names of the labels different from the domain of the y-axis.

.rangeRoundBands([0, chartHeight], 0.1 /* white space percentage */);

// Creates a d3 axis given a scale (takes care of tick marks and labels)
Expand All @@ -69,11 +69,25 @@ class BarChart {
.enter().append('g');

this.rects = bars.append('rect')
.attr('y', function(d) { return yScale(d.name); })
.attr('height', yScale.rangeBand());
.attr('y', function(d) { return yScale(d.trimName); })
.attr('height', yScale.rangeBand())
.on("mouseover", function (d) { tooltip.style("display", "inline-block"); })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should also have a tooltip on the y-axis label. I've noticed some cases where the bar is so small that there's actually no way to trigger the tooltip.

.on("mousemove", function (d) {
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this needed since you set the display on mouseover?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You're right -- line 84 is redundant. Gone.

.html((d.name.replace('<', '&lt;') + ' (' + d.val + ')'));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can be changed to:

.text((d.name + ' (' + d.val + ')'));

.html is used to insert html. You simply want to display text.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks @jdaguil -- and I see the reference in the D3 3.x API.

})
.on("mouseout", function (d) { tooltip.style("display", "none"); });

// Clear tooltip div created when inadvertently triggered during dropdown selection.
$( ".toolTip" ).remove();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I couldn't reproduce the problem you described in your comment. Is this still needed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@jdaguil This is needed at least on Windows 10. Using the Elements view in the dev tools, with this code, we generate only a single toolTip div no matter how many times we select a different attribute for display:
image

When I comment out the code, a new div is created each time a different attribute is selected for display:
image

I take it you see different behavior on your Mac?


let tooltip = d3.select("body").append("div").attr("class", "toolTip");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a little confusing since it is defined after it's used above. Can you move this up?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I moved 89-92 together -- .remove has to be called before we define the tooltip variable.


this.texts = bars.append('text')
.attr('y', function(d) { return yScale(d.name); })
.attr('y', function(d) { return yScale(d.trimName); })
.attr('dy', '1.2em')
.text(function(d){ return '(' + d.val + ')'; })
.style('text-anchor', 'start');
Expand Down Expand Up @@ -121,7 +135,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.trimName; });

let maxStr = '';
$.each(names, function(i, name) {
Expand Down Expand Up @@ -149,8 +163,15 @@ class BarChart {

// Transform license count into array of objects with license name & count
let chartData = $.map(count, function(val, key) {
let trimName = "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Assigning to an empty string is unneeded. Instead do this:

let trimName = key.substring(0, 50) + (key.length > 50 ? " ..." : "");

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done -- using the modified variable name and the new static method:

let trimmedName = BarChart.trimName(key);

if (key.length > 50) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

style nit: you could use a ternary operator here:

trimName = key.substring(0, 50) + (key.length > 50 ? " ..." : "");

trimName = key.substring(0, 50) + ' . . .'
} else {
trimName = key;
}
return {
name: key,
trimName: trimName,
val: val
};
});
Expand Down