Fix Issue 202: Second Y-Axis Label
authorDan Vanderkam <danvk@google.com>
Tue, 3 Jan 2012 23:36:28 +0000 (18:36 -0500)
committerDan Vanderkam <danvk@google.com>
Tue, 3 Jan 2012 23:36:28 +0000 (18:36 -0500)
Option name is "y2label". This results in a label on the right-hand side
of the chart which is rotated in the opposite way of the label on the
left-hand side. It gets CSS classes "dygraph-label dygraph-y2label" (the
primary y-axis label gets "dygraph-label dygraph-ylabel").

auto_tests/tests/multiple_axes.js
dygraph-canvas.js
dygraph-layout.js
dygraph-options-reference.js
tests/two-axes.html

index ed9b362..2c0f18f 100644 (file)
@@ -167,3 +167,54 @@ MultipleAxesTestCase.prototype.testTwoAxisVisibility = function() {
   assertTrue(document.getElementsByClassName("dygraph-axis-label-y").length > 0);
   assertTrue(document.getElementsByClassName("dygraph-axis-label-y2").length > 0);
 };
+
+// verifies that all four chart labels (title, x-, y-, y2-axis label) can be
+// used simultaneously.
+MultipleAxesTestCase.prototype.testMultiChartLabels = function() {
+  var data = MultipleAxesTestCase.getData();
+
+  var el = document.getElementById("graph");
+  el.style.border = '1px solid black';
+  el.style.marginLeft = '200px';
+  el.style.marginTop = '200px';
+
+  g = new Dygraph(
+    el,
+    data,
+    {
+      labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
+      width: 640,
+      height: 350,
+      'Y3': {
+        axis: { }
+      },
+      'Y4': {
+        axis: 'Y3'  // use the same y-axis as series Y3
+      },
+      xlabel: 'x-axis',
+      ylabel: 'y-axis',
+      y2label: 'y2-axis',
+      title: 'Chart title'
+    }
+  );
+
+  // returns all text in tags w/ a given css class, sorted.
+  function getTexts(css_class) {
+    var texts = [];
+    var els = document.getElementsByClassName(css_class);
+    for (var i = 0; i < els.length; i++) {
+      texts[i] = els[i].textContent;
+    }
+    texts.sort();
+    return texts;
+  }
+
+  assertEquals(["Chart title", "x-axis", "y-axis", "y2-axis"],
+               getTexts("dygraph-label"));
+  assertEquals(["Chart title"], getTexts("dygraph-title"));
+  assertEquals(["x-axis"], getTexts("dygraph-xlabel"));
+  assertEquals(["y-axis"], getTexts("dygraph-ylabel"));
+  assertEquals(["y2-axis"], getTexts("dygraph-y2label"));
+
+  // TODO(danvk): check relative positioning here: title on top, y left of y2.
+};
index e2d304c..acf011f 100644 (file)
@@ -485,21 +485,26 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() {
     this.chartLabels.xlabel = div;
   }
 
-  if (this.attr_('ylabel')) {
+  var that = this;
+  function createRotatedDiv(axis, classes, html) {
     var box = {
       left: 0,
-      top: this.area.y,
-      width: this.attr_('yLabelWidth'),
-      height: this.area.h
+      top: that.area.y,
+      width: that.attr_('yLabelWidth'),
+      height: that.area.h
     };
     // TODO(danvk): is this outer div actually necessary?
     div = document.createElement("div");
     div.style.position = 'absolute';
-    div.style.left = box.left;
+    if (axis == 1) {
+      div.style.left = box.left;
+    } else {
+      div.style.right = box.left;
+    }
     div.style.top = box.top + 'px';
     div.style.width = box.width + 'px';
     div.style.height = box.height + 'px';
-    div.style.fontSize = (this.attr_('yLabelWidth') - 2) + 'px';
+    div.style.fontSize = (that.attr_('yLabelWidth') - 2) + 'px';
 
     var inner_div = document.createElement("div");
     inner_div.style.position = 'absolute';
@@ -511,11 +516,12 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() {
 
     // CSS rotation is an HTML5 feature which is not standardized. Hence every
     // browser has its own name for the CSS style.
-    inner_div.style.transform = 'rotate(-90deg)';        // HTML5
-    inner_div.style.WebkitTransform = 'rotate(-90deg)';  // Safari/Chrome
-    inner_div.style.MozTransform = 'rotate(-90deg)';     // Firefox
-    inner_div.style.OTransform = 'rotate(-90deg)';       // Opera
-    inner_div.style.msTransform = 'rotate(-90deg)';      // IE9
+    var val = 'rotate(' + (axis == 1 ? '-' : '') + '90deg)';
+    inner_div.style.transform = val;        // HTML5
+    inner_div.style.WebkitTransform = val;  // Safari/Chrome
+    inner_div.style.MozTransform = val;     // Firefox
+    inner_div.style.OTransform = val;       // Opera
+    inner_div.style.msTransform = val;      // IE9
 
     if (typeof(document.documentMode) !== 'undefined' &&
         document.documentMode < 9) {
@@ -523,20 +529,34 @@ DygraphCanvasRenderer.prototype._renderChartLabels = function() {
       // using a BasicImage transform. This uses a different origin of rotation
       // than HTML5 rotation (top left of div vs. its center).
       inner_div.style.filter =
-       'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)';
+          'progid:DXImageTransform.Microsoft.BasicImage(rotation=' +
+          (axis == 1 ? '3' : '1') + ')';
       inner_div.style.left = '0px';
       inner_div.style.top = '0px';
     }
 
     class_div = document.createElement("div");
-    class_div.className = 'dygraph-label dygraph-ylabel';
-    class_div.innerHTML = this.attr_('ylabel');
+    class_div.className = classes;
+    class_div.innerHTML = html;
 
     inner_div.appendChild(class_div);
     div.appendChild(inner_div);
+    return div;
+  }
+
+  var div;
+  if (this.attr_('ylabel')) {
+    div = createRotatedDiv(1, 'dygraph-label dygraph-ylabel',
+                           this.attr_('ylabel'));
     this.container.appendChild(div);
     this.chartLabels.ylabel = div;
   }
+  if (this.attr_('y2label')) {
+    div = createRotatedDiv(2, 'dygraph-label dygraph-y2label',
+                           this.attr_('y2label'));
+    this.container.appendChild(div);
+    this.chartLabels.y2label = div;
+  }
 };
 
 
index 6f8f2fd..328da81 100644 (file)
@@ -99,6 +99,11 @@ DygraphLayout.prototype.computePlotArea_ = function() {
     // doesn't, the yAxisLabelWidth option can be increased.
   }
 
+  if (this.attr_('y2label')) {
+    // same logic applies here as for ylabel.
+    // TODO(danvk): make yAxisLabelWidth a per-axis property
+  }
+
   // Add space for range selector, if needed.
   if (this.attr_('showRangeSelector')) {
     area.h -= this.attr_('rangeSelectorHeight') + 4;
index b5825e6..92846ee 100644 (file)
@@ -476,6 +476,12 @@ Dygraph.OPTIONS_REFERENCE =  // <JSON>
     "default": "null",
     "description": "Text to display to the left of the chart's y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it's rotated, so width is controlled by the 'height' property) or set the yLabelWidth option."
   },
+  "y2label": {
+    "labels": ["Chart labels"],
+    "type": "string",
+    "default": "null",
+    "description": "Text to display to the right of the chart's secondary y-axis. This label is only displayed if a secondary y-axis is present. See <a href='http://dygraphs.com/tests/two-axes.html'>this test</a> for an example of how to do this. The comments for the 'ylabel' option generally apply here as well. This label gets a 'dygraph-y2label' instead of a 'dygraph-ylabel' class."
+  },
   "yLabelWidth": {
     "labels": ["Chart labels"],
     "type": "integer",
index 834fd2f..bb4a34a 100644 (file)
                 // set axis-related properties here
                 labelsKMB: true
               }
-            }
+            },
+            ylabel: 'Primary y-axis',
+            y2label: 'Secondary y-axis',
+            yAxisLabelWidth: 60
           }
       );
 
@@ -62,7 +65,8 @@
           data,
           {
             labels: [ 'Date', 'Y1', 'Y2', 'Y3', 'Y4' ],
-            labelsKMB: true
+            labelsKMB: true,
+            ylabel: 'Primary y-axis'
           }
       );