Mforns has submitted this change and it was merged.
Change subject: Sort the legend by value and align it
......................................................................
Sort the legend by value and align it
mforns had this idea of sorting the legend in descending order and I
ignored it because it would've required a big hack. It turns out that
styling the legend labels requires the big hack anyway, so I did it and
I love it, it makes the graph make so much more sense.
Bug: T131935
Change-Id: I93a749f4d354d1e2ca1ba7c95ef1c21efb19cabd
---
M bower.json
M src/app/require.config.js
M src/components/visualizers/dygraphs-timeseries/bindings.js
M src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
A src/components/visualizers/dygraphs-timeseries/dygraphs.patch.js
5 files changed, 131 insertions(+), 15 deletions(-)
Approvals:
Mforns: Verified; Looks good to me, approved
diff --git a/bower.json b/bower.json
index df38e55..583feb0 100644
--- a/bower.json
+++ b/bower.json
@@ -20,7 +20,8 @@
"twix": "~0.9.0",
"dygraphs": "~1.1.0",
"lodash": "3.9.0-amd",
- "pageviews": null
+ "pageviews": null,
+ "numeral": "^1.5.3"
},
"resolutions": {
"d3": "3.5.2"
diff --git a/src/app/require.config.js b/src/app/require.config.js
index 29312a7..196ff05 100644
--- a/src/app/require.config.js
+++ b/src/app/require.config.js
@@ -9,6 +9,7 @@
// (Issue reported https://github.com/knockout/knockout/issues/1528)
'knockout' : 'bower_modules/knockout/dist/knockout.debug',
'text' : 'bower_modules/requirejs-text/text',
+ 'numeral' : 'bower_modules/numeral/numeral',
'd3' : 'bower_modules/d3/d3',
'vega' : 'bower_modules/vega/vega',
'datepicker' :
'bower_modules/semantic-datepicker/daterangepicker',
diff --git a/src/components/visualizers/dygraphs-timeseries/bindings.js
b/src/components/visualizers/dygraphs-timeseries/bindings.js
index 22b043a..18dff84 100644
--- a/src/components/visualizers/dygraphs-timeseries/bindings.js
+++ b/src/components/visualizers/dygraphs-timeseries/bindings.js
@@ -1,11 +1,13 @@
+'use strict';
define(function(require) {
- 'use strict';
var ko = require('knockout'),
_ = require('lodash'),
- moment = require('moment');
+ moment = require('moment'),
+ numeral = require('numeral');
require('dygraphs');
+ require('./dygraphs.patch');
ko.bindingHandlers.dygraphs = {
init: function (element, valueAccessor) {
@@ -36,27 +38,22 @@
},
axisLabelWidth: 77,
},
+ y: {
+ valueFormatter: function(d) {
+ return numeral(d).format(d < 1 ? '0.00' :
'0.0a');
+ },
+ },
},
labels: ['Date'],
labelsKMB: true,
- labelsDivWidth: 350,
- labelsDivStyles: {
- 'margin-left': '-120px',
- 'backgroundColor': 'rgba(255, 255, 255, 0.9)',
- 'padding': '4px',
- 'border': '1px solid #dadada',
- 'borderRadius': '5px',
- 'boxShadow': '2px 2px 2px #aaa',
- 'textAlign': 'left',
- 'fontFamily': 'sans-serif',
- },
- labelsSeparateLines: true,
+ // labelsDivStyles: defined as CSS in
dygraphs-timeseries.html
strokeWidth: 1.8,
gridLineColor: '#cacaca',
gridLinePattern: [10, 5],
series: {},
showRoller: true,
animatedZooms: true,
+ labelsSeparateLines: true,
};
var patterns = _(data.patternLabels)
diff --git
a/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
b/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
index 34819c4..27db935 100644
--- a/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
+++ b/src/components/visualizers/dygraphs-timeseries/dygraphs-timeseries.html
@@ -1,3 +1,31 @@
+<style>
+ div.dygraph-legend {
+ background-color: rgba(255, 255, 255, 0.9)!important;
+ padding: 4px;
+ border: 1px solid #dadada;
+ border-radius: 5px;
+ box-shadow: 2px 2px 2px #aaa;
+ margin-left: -120px;
+ pointer-events: none;
+ width: auto!important;
+ }
+
+ div.dygraph-legend > span {
+ font-weight: bold;
+ }
+ div.dygraph-legend > span > span {
+ padding: 1px 3px 1px 1px;
+ text-align: right;
+ font-family: 'courier new';
+ display: inline-block;
+ width: 70px;
+ }
+ div.dygraph-legend > span > label {
+ padding: 1px 1px 1px 2px;
+ text-align: left;
+ }
+</style>
+
<div class="parent-of-resizable" data-bind="
if: data && data.header,
dygraphs: {
diff --git a/src/components/visualizers/dygraphs-timeseries/dygraphs.patch.js
b/src/components/visualizers/dygraphs-timeseries/dygraphs.patch.js
new file mode 100644
index 0000000..9d6f8f4
--- /dev/null
+++ b/src/components/visualizers/dygraphs-timeseries/dygraphs.patch.js
@@ -0,0 +1,89 @@
+'use strict';
+define(function(require) {
+
+ require('dygraphs');
+
+ var L = window.Dygraph.Plugins.Legend;
+ var escapeHTML = function(str) {
+ return str.replace(/&/g, '&').replace(/'/g,
'"').replace(/</g, '<').replace(/>/g, '>');
+ };
+
+ /* patch the way the legend HTML is generated.
+ * This kind of override appears to be supported in the next version of
+ * Dygraphs, but that's not out yet
+ */
+ window.Dygraph.Plugins.Legend.generateLegendHTML = function(g, x,
sel_points, oneEmWidth, row) {
+ // todo(danvk): deprecate this option in place of {legend: 'never'}
+ if (g.getOption('showLabelsOnHighlight') !== true) { return ''; }
+
+ // If no points are selected, we display a default legend.
Traditionally,
+ // this has been blank. But a better default would be a conventional
legend,
+ // which provides essential information for a non-interactive chart.
+ var html, sepLines, i, dash, strokePattern;
+ var labels = g.getLabels();
+ sepLines = g.getOption('labelsSeparateLines');
+
+ if (typeof x === 'undefined') {
+ if (g.getOption('legend') !== 'always') {
+ return '';
+ }
+
+ html = '';
+ for (i = 1; i < labels.length; i++) {
+ var series = g.getPropertiesForSeries(labels[i]);
+ if (!series.visible) { continue; }
+
+ if (html !== '') { html += (sepLines ? '<br/>' : ' '); }
+ strokePattern = g.getOption('strokePattern', labels[i]);
+ dash = L.generateLegendDashHTML(strokePattern, series.color,
oneEmWidth);
+ html += '<span style="font-weight: bold; color: ' +
series.color + ';">' +
+ dash + ' ' + escapeHTML(labels[i]) + '</span>';
+ }
+ return html;
+ }
+
+ // todo(danvk): remove this use of a private API
+ var xOptView = g.optionsViewForAxis_('x');
+ var xvf = xOptView('valueFormatter');
+ html = xvf.call(g, x, xOptView, labels[0], g, row, 0);
+ if (html !== '') {
+ html += sepLines ? '<hr/>' : ':';
+ }
+
+ var yOptViews = [];
+ var num_axes = g.numAxes();
+ for (i = 0; i < num_axes; i++) {
+ // todo(danvk): remove this use of a private API
+ yOptViews[i] = g.optionsViewForAxis_('y' + (i ? 1 + i : ''));
+ }
+ var showZeros = g.getOption('labelsShowZeroValues');
+ var highlightSeries = g.getHighlightSeries();
+ sel_points = sel_points.sort(function (a, b) {
+ if(!isFinite(a.yval - b.yval)) {
+ return isFinite(a.yval) ? 1 : -1;
+ }
+ return b.yval - a.yval;
+ });
+ for (i = 0; i < sel_points.length; i++) {
+ var pt = sel_points[i];
+ if (pt.yval === 0 && !showZeros) { continue; }
+ if (!window.Dygraph.isOK(pt.canvasy)) { continue; }
+ var series = g.getPropertiesForSeries(pt.name);
+ var yOptView = yOptViews[series.axis - 1];
+ var fmtFunc = yOptView('valueFormatter');
+ var yval = fmtFunc.call(g, pt.yval, yOptView, pt.name, g, row,
labels.indexOf(pt.name));
+
+ var cls = (pt.name === highlightSeries) ? ' class="highlight"' :
'';
+
+ // todo(danvk): use a template string here and make it an
attribute.
+ html += '<span' + cls + '>' +
+ '<span>' + yval + '</span>' +
+ '<label style="color: ' + series.color + ';">' +
+ escapeHTML(pt.name) +
+ '</label>' +
+ '</span>';
+ if (sepLines) { html += '<br/>'; }
+ }
+ return html;
+ };
+});
--
To view, visit https://gerrit.wikimedia.org/r/281947
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I93a749f4d354d1e2ca1ba7c95ef1c21efb19cabd
Gerrit-PatchSet: 1
Gerrit-Project: analytics/dashiki
Gerrit-Branch: master
Gerrit-Owner: Milimetric <[email protected]>
Gerrit-Reviewer: Mforns <[email protected]>
Gerrit-Reviewer: Nuria <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits