From 89f7aca4b3a768bf5f106e8b71b84422a99628e6 Mon Sep 17 00:00:00 2001 From: ruhley Date: Fri, 5 Jun 2015 09:22:18 +1000 Subject: [PATCH 01/13] row chart useRightYAxis and examples --- src/row-chart.js | 24 ++++++++++++++++++++++-- web/examples/row.html | 20 ++++++++++++++++---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/row-chart.js b/src/row-chart.js index e575cc5f1..105ae2824 100644 --- a/src/row-chart.js +++ b/src/row-chart.js @@ -48,6 +48,8 @@ dc.rowChart = function (parent, chartGroup) { var _rowData; + var _useRightYAxis = false; + _chart.rowsCap = _chart.cap; function calculateAxisScale () { @@ -56,8 +58,13 @@ dc.rowChart = function (parent, chartGroup) { if (extent[0] > 0) { extent[0] = 0; } - _x = d3.scale.linear().domain(extent) - .range([0, _chart.effectiveWidth()]); + + var domain = d3.scale.linear().domain(extent); + if (_useRightYAxis) { + _x = domain.range([_chart.effectiveWidth(), 0]); + } else { + _x = domain.range([0, _chart.effectiveWidth()]); + } } _xAxis.scale(_x); } @@ -416,6 +423,19 @@ dc.rowChart = function (parent, chartGroup) { return _chart; }; + /** + #### .useRightYAxis() + Gets or sets whether the chart should be drawn with a right axis instead of a left axis. + **/ + + _chart.useRightYAxis = function (_) { + if (!arguments.length) { + return _useRightYAxis; + } + _useRightYAxis = _; + return _chart; + }; + function isSelectedRow (d) { return _chart.hasFilter(_chart.cappedKeyAccessor(d)); } diff --git a/web/examples/row.html b/web/examples/row.html index 86abc73f3..a5de945b4 100644 --- a/web/examples/row.html +++ b/web/examples/row.html @@ -1,13 +1,14 @@ - dc.js - Row Chart Example + dc.js - Bar Chart Example
+
@@ -15,6 +16,8 @@ From c7b9d5d2bcde87f527b71efbdf29daefa6e150c8 Mon Sep 17 00:00:00 2001 From: ruhley Date: Thu, 17 Dec 2015 13:01:31 -0500 Subject: [PATCH 02/13] Fixing up the width of paired row charts --- Gruntfile.js | 1 + dc.css | 4 + spec/helpers/fixtures.js | 34 ++++ spec/paired-row-chart-spec.js | 161 ++++++++++++++++++ spec/row-chart-spec.js | 59 +++++++ src/paired-row-chart.js | 273 +++++++++++++++++++++++++++++++ src/row-chart.js | 41 ++++- web/examples/paired-row.html | 128 +++++++++++++++ web/examples/paried-row-data.csv | 31 ++++ 9 files changed, 724 insertions(+), 8 deletions(-) create mode 100644 spec/paired-row-chart-spec.js create mode 100644 src/paired-row-chart.js create mode 100644 web/examples/paired-row.html create mode 100644 web/examples/paried-row-data.csv diff --git a/Gruntfile.js b/Gruntfile.js index 81d36ef98..b8e5969dc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -403,6 +403,7 @@ module.exports.jsFiles = [ 'src/geo-choropleth-chart.js', 'src/bubble-overlay.js', 'src/row-chart.js', + 'src/paired-row-chart.js', 'src/legend.js', 'src/scatter-plot.js', 'src/number-display.js', diff --git a/dc.css b/dc.css index c046cd72b..75b7a8916 100644 --- a/dc.css +++ b/dc.css @@ -324,3 +324,7 @@ g.dc-legend-item.fadeout { pointer-events: all; cursor: pointer; } + +.dc-chart g.row text.titlerow { + fill: #000000; +} diff --git a/spec/helpers/fixtures.js b/spec/helpers/fixtures.js index 7f37a0250..302f085bf 100644 --- a/spec/helpers/fixtures.js +++ b/spec/helpers/fixtures.js @@ -77,5 +77,39 @@ function loadColorFixture2 () { "{\"colData\":\"6\", \"rowData\": \"6\", \"colorData\": \"8\"}" + "]"); } + +function loadGenderFixture() { + return JSON.parse("[" + + "{\"gender\":\"Male\", \"age\": \"10\"}," + + "{\"gender\":\"Male\", \"age\": \"18\"}," + + "{\"gender\":\"Male\", \"age\": \"20\"}," + + "{\"gender\":\"Male\", \"age\": \"22\"}," + + "{\"gender\":\"Male\", \"age\": \"30\"}," + + "{\"gender\":\"Male\", \"age\": \"40\"}," + + "{\"gender\":\"Male\", \"age\": \"45\"}," + + "{\"gender\":\"Male\", \"age\": \"50\"}," + + "{\"gender\":\"Male\", \"age\": \"55\"}," + + "{\"gender\":\"Male\", \"age\": \"70\"}," + + "{\"gender\":\"Male\", \"age\": \"80\"}," + + "{\"gender\":\"Male\", \"age\": \"90\"}," + + "{\"gender\":\"Male\", \"age\": \"100\"}," + + "{\"gender\":\"Male\", \"age\": \"101\"}," + + "{\"gender\":\"Female\", \"age\": \"11\"}," + + "{\"gender\":\"Female\", \"age\": \"15\"}," + + "{\"gender\":\"Female\", \"age\": \"20\"}," + + "{\"gender\":\"Female\", \"age\": \"21\"}," + + "{\"gender\":\"Female\", \"age\": \"22\"}," + + "{\"gender\":\"Female\", \"age\": \"30\"}," + + "{\"gender\":\"Female\", \"age\": \"40\"}," + + "{\"gender\":\"Female\", \"age\": \"50\"}," + + "{\"gender\":\"Female\", \"age\": \"60\"}," + + "{\"gender\":\"Female\", \"age\": \"65\"}," + + "{\"gender\":\"Female\", \"age\": \"70\"}," + + "{\"gender\":\"Female\", \"age\": \"80\"}," + + "{\"gender\":\"Female\", \"age\": \"90\"}," + + "{\"gender\":\"Female\", \"age\": \"100\"}" + + "]"); +} + /* jscs:enable validateQuoteMarks, maximumLineLength */ /* jshint +W109, +W101, +W098 */ diff --git a/spec/paired-row-chart-spec.js b/spec/paired-row-chart-spec.js new file mode 100644 index 000000000..e7f997452 --- /dev/null +++ b/spec/paired-row-chart-spec.js @@ -0,0 +1,161 @@ +describe('dc.pairedRowChart', function() { + var id, chart; + var data, dimension, group, dummyGroup; + + beforeEach(function () { + genderFixture = loadGenderFixture(); + data = crossfilter(genderFixture); + dimension = data.dimension(function(d) { + var age_range = 'Unknown'; + + if (d.age <= 9) { + age_range = '0 - 9'; + } else if (d.age <= 19) { + age_range = '10 - 19'; + } else if (d.age <= 29) { + age_range = '20 - 29'; + } else if (d.age <= 39) { + age_range = '30 - 39'; + } else if (d.age <= 49) { + age_range = '40 - 49'; + } else if (d.age <= 59) { + age_range = '50 - 59'; + } else if (d.age <= 69) { + age_range = '60 - 69'; + } else if (d.age <= 79) { + age_range = '70 - 79'; + } else if (d.age <= 89) { + age_range = '80 - 89'; + } else if (d.age <= 99) { + age_range = '90 - 99'; + } else if (d.age >= 100) { + age_range = '100+'; + } + + return [d.gender, age_range]; + }), + group = dimension.group().reduceCount(); + + dummyGroup = { + all: function() { + var ageRanges = ['0 - 9', '10 - 19', '20 - 29', '30 - 39', '40 - 49', '50 - 59', '60 - 69', '70 - 79', '80 - 89', '90 - 99', '100+']; + + // convert to object so we can easily tell if a key exists + var values = {}; + group.all().forEach(function(d) { + values[d.key[0] + '.' + d.key[1]] = d.value; + }); + + // convert back into an array for the chart, making sure that all ageRanges exist + var g = []; + ageRanges.forEach(function(age_range) { + g.push({ + key: ['Male', age_range], + value: values['Male.' + age_range] || 0 + }); + g.push({ + key: ['Female', age_range], + value: values['Female.' + age_range] || 0 + }); + }); + + return g; + } + }; + + id = 'paired-row-chart'; + appendChartID(id); + + chart = dc.pairedRowChart("#" + id); + chart.dimension(dimension) + .group(dummyGroup) + .width(600).height(200).gap(10) + .leftKeyFilter(function(d) { + return d.key[0] === 'Male'; + }) + .rightKeyFilter(function(d) { + return d.key[0] === 'Female'; + }) + .transitionDuration(0); + }); + + describe('leftChart', function () { + beforeEach(function () { + chart.render(); + }); + + describe('useRightYAxis', function () { + it('should use right y axis', function () { + expect(chart.leftChart().useRightYAxis()).toBe(true); + }); + }); + + describe('key filter', function () { + it('can get key filter', function () { + expect(typeof chart.leftKeyFilter()).toBe('function'); + }); + + it('should filter data', function () { + expect(chart.leftChart().data().length < dummyGroup.all().length).toBe(true); + }); + }); + + describe('margins', function () { + it('should manually set margins', function () { + var margins = chart.margins(), + leftMargins = chart.leftChart().margins(); + + expect(leftMargins.top).toBe(margins.top); + expect(leftMargins.right).toBe(0); + expect(leftMargins.bottom).toBe(margins.bottom); + expect(leftMargins.left).toBe(margins.left); + }); + }); + + describe('calculateAxisScaleData', function() { + it('should equal the group data', function() { + expect(chart.leftChart().calculateAxisScaleData().length).toBe(dummyGroup.all().length); + }) + }); + }); + + describe('rightChart', function () { + beforeEach(function () { + chart.render(); + }); + + describe('useRightYAxis', function () { + it('should not use right y axis', function () { + expect(chart.rightChart().useRightYAxis()).toBe(false); + }); + }); + + describe('key filter', function () { + it('can get key filter', function () { + expect(typeof chart.rightKeyFilter()).toBe('function'); + }); + + it('should filter data', function () { + expect(chart.rightChart().data().length < dummyGroup.all().length).toBe(true); + }); + }); + + describe('margins', function () { + it('should manually set margins', function () { + var margins = chart.margins(), + rightMargins = chart.rightChart().margins(); + + expect(rightMargins.top).toBe(margins.top); + expect(rightMargins.right).toBe(margins.right); + expect(rightMargins.bottom).toBe(margins.bottom); + expect(rightMargins.left).toBe(0); + }); + }); + + describe('calculateAxisScaleData', function() { + it('should equal the group data', function() { + expect(chart.rightChart().calculateAxisScaleData().length).toBe(dummyGroup.all().length); + }) + }); + }); +}); diff --git a/spec/row-chart-spec.js b/spec/row-chart-spec.js index 687e99082..39b1daa12 100644 --- a/spec/row-chart-spec.js +++ b/spec/row-chart-spec.js @@ -75,6 +75,65 @@ describe('dc.rowChart', function () { }); }); + describe('_useRightYAxis', function () { + beforeEach(function () { + chart.group(positiveGroupHolder.group); + }); + + describe('is false', function () { + beforeEach(function () { + chart.useRightYAxis(false); + chart.render(); + }); + + describe('rows', function () { + it('should not translate', function () { + expect(chart.selectAll('.row').attr('transform')).toBe('translate(0,10)'); + }); + }); + + describe('bars', function () { + it('should not translate', function () { + expect(chart.selectAll('.row rect').attr('transform')).toBe('translate(0,0)'); + }); + }); + + describe('labels', function () { + it('should position the label by the end', function () { + expect(chart.selectAll('.row text').attr('text-anchor')).toBe('start'); + }); + }); + }); + + describe('is true', function () { + beforeEach(function () { + chart.useRightYAxis(true); + chart.render(); + }); + + describe('rows', function () { + it('should translate to the width of the chart', function () { + expect(chart.selectAll('.row').attr('transform')).toBe('translate(' + chart.effectiveWidth() + ',10)'); + }); + }); + + describe('bars', function () { + it('should translate to its own width', function () { + var rect = chart.selectAll('.row rect'); + + expect(rect.attr('transform')).toBe('translate(-' + rect[0][0].getBBox().width + ',0)'); + }); + }); + + describe('labels', function () { + it('should position the label by the end', function () { + expect(chart.selectAll('.row text').attr('text-anchor')).toBe('end'); + }); + }); + }); + + }); + function itShouldBehaveLikeARowChartWithGroup (groupHolder, N) { describe('for ' + groupHolder.groupType + ' data', function () { beforeEach(function () { diff --git a/src/paired-row-chart.js b/src/paired-row-chart.js new file mode 100644 index 000000000..69207f1ea --- /dev/null +++ b/src/paired-row-chart.js @@ -0,0 +1,273 @@ +/** +## Paired Row Chart +Includes: [Cap Mixin](#cap-mixin), [Margin Mixin](#margin-mixin), [Color Mixin](#color-mixin), [Base Mixin](#base-mixin) + +Concrete paired row chart implementation. +#### dc.pairedRowChart(parent[, chartGroup]) +Create a paired row chart instance and attach it to the given parent element. + +Parameters: + +* parent : string | node | selection - any valid + [d3 single selector](https://github.com/mbostock/d3/wiki/Selections#selecting-elements) specifying + a dom block element such as a div; or a dom element or d3 selection. + +* chartGroup : string (optional) - name of the chart group this chart instance should be placed in. + Interaction with a chart will only trigger events and redraws within the chart's group. + +Returns: +A newly created paired row chart instance + +```js +// create a paired row chart under #chart-container1 element using the default global chart group +var chart1 = dc.pairedRowChart('#chart-container1'); +// create a paired row chart under #chart-container2 element using chart group A +var chart2 = dc.pairedRowChart('#chart-container2', 'chartGroupA'); +``` +**/ +dc.pairedRowChart = function (parent, chartGroup) { + var _chart = dc.capMixin(dc.marginMixin(dc.colorMixin(dc.baseMixin({})))); + + var _leftChartWrapper = d3.select(parent).append('div'); + var _rightChartWrapper = d3.select(parent).append('div'); + + var _leftChart = dc.rowChart(_leftChartWrapper[0][0], chartGroup); + var _rightChart = dc.rowChart(_rightChartWrapper[0][0], chartGroup); + + _leftChart.useRightYAxis(true); + + // data filtering + + // we need a way to know which data belongs on the left chart and which data belongs on the right + var _leftKeyFilter = function (d) { + return d.key[0]; + }; + + var _rightKeyFilter = function (d) { + return d.key[0]; + }; + + /** + #### .leftKeyFilter([value]) - **mandatory** + Set or get the left key filter attribute of a chart. + + For example + function(d) { + return d.key[0] === 'Male'; + } + + If a value is given, then it will be used as the new left key filter. If no value is specified then + the current left key filter will be returned. + + **/ + _chart.leftKeyFilter = function (_) { + if (!arguments.length) { + return _leftKeyFilter; + } + + _leftKeyFilter = _; + return _chart; + }; + + /** + #### .rightKeyFilter([value]) - **mandatory** + Set or get the right key filter attribute of a chart. + + For example + function(d) { + return d.key[0] === 'Female'; + } + + If a value is given, then it will be used as the new right key filter. If no value is specified then + the current right key filter will be returned. + + **/ + _chart.rightKeyFilter = function (_) { + if (!arguments.length) { + return _rightKeyFilter; + } + + _rightKeyFilter = _; + return _chart; + }; + + // when trying to get the data for the left chart then filter all data using the leftKeyFilter function + _leftChart.data(function (data) { + var cap = _leftChart.cap(), + d = data.all().filter(function (d) { + return _chart.leftKeyFilter()(d); + }); + + if (cap === Infinity) { + return d; + } + + return d.slice(0, cap); + }); + + // when trying to get the data for the right chart then filter all data using the rightKeyFilter function + _rightChart.data(function (data) { + var cap = _rightChart.cap(), + d = data.all().filter(function (d) { + return _chart.rightKeyFilter()(d); + }); + + if (cap === Infinity) { + return d; + } + + return d.slice(0, cap); + }); + + // chart filtering + // on clicking either chart then filter both + + _leftChart.onClick = _rightChart.onClick = function (d) { + var filter = _leftChart.keyAccessor()(d); + dc.events.trigger(function () { + _leftChart.filter(filter); + _rightChart.filter(filter); + _leftChart.redrawGroup(); + }); + }; + + // width and margins + + // the margins between the charts need to be set to 0 so that they sit together + var _margins = _chart.margins(); // get the default margins + + _chart.margins = function (_) { + if (!arguments.length) { + return _margins; + } + _margins = _; + + // set left chart margins + _leftChart.margins({ + top: _.top, + right: 0, + bottom: _.bottom, + left: _.left, + }); + + // set right chart margins + _rightChart.margins({ + top: _.top, + right: _.right, + bottom: _.bottom, + left: 0, + }); + + return _chart; + }; + + _chart.margins(_margins); // set the new margins + + // the width needs to be halved + var _width = 0; // get the default width + + _chart.width = function (_) { + if (!arguments.length) { + return _width; + } + _width = _; + + // set left chart width + _leftChart.width(dc.utils.isNumber(_) ? _ / 2 : _); + + // set right chart width + _rightChart.width(dc.utils.isNumber(_) ? _ / 2 : _); + + return _chart; + }; + + // the minWidth needs to be halved + var _minWidth = _chart.minWidth(); // get the default minWidth + + _chart.minWidth = function (_) { + if (!arguments.length) { + return _minWidth; + } + _minWidth = _; + + // set left chart minWidth + _leftChart.minWidth(dc.utils.isNumber(_) ? _ / 2 : _); + + // set right chart minWidth + _rightChart.minWidth(dc.utils.isNumber(_) ? _ / 2 : _); + + return _chart; + }; + + _chart.minWidth(_minWidth); // set the new minWidth + + // svg + // return an array of both the sub chart svgs + + _chart.svg = function () { + return d3.selectAll([_leftChart.svg()[0][0], _rightChart.svg()[0][0]]); + }; + + // we need to make sure that the extent is the same for both charts + _leftChart.calculateAxisScaleData = _rightChart.calculateAxisScaleData = function () { + return _leftChart.data().concat(_rightChart.data()); + }; + + // get the charts - mainly used for testing + _chart.leftChart = function() { + return _leftChart; + }; + + _chart.rightChart = function() { + return _rightChart; + }; + + // functions that we just want to pass on to both sub charts + + var _getterSetterPassOn = [ + // display + 'height', 'minHeight', 'renderTitleLabel', 'fixedBarHeight', 'gap', 'othersLabel', + 'transitionDuration', 'label', 'renderLabel', 'title', 'renderTitle', 'chartGroup', + //colors + 'colors', 'ordinalColors', 'linearColors', 'colorAccessor', 'colorDomain', 'getColor', 'colorCalculator', + // x axis + 'x', 'elasticX', 'valueAccessor', 'labelOffsetX', 'titleLabelOffsetx', + // y axis + 'keyAccessor', 'labelOffsetY', + // data + 'cap', 'ordering' , 'dimension', 'group', 'othersGrouper', 'data' + ]; + + function addGetterSetterFunction(functionName) { + _chart[functionName] = function (_) { + if (!arguments.length) { + return _leftChart[functionName](); + } + _leftChart[functionName](_); + _rightChart[functionName](_); + return _chart; + }; + } + + for (var i = 0; i < _getterSetterPassOn.length; i++) { + addGetterSetterFunction(_getterSetterPassOn[i]); + } + + var _passOnFunctions = [ + '_doRedraw', 'redraw', '_doRender', 'render', 'calculateColorDomain', 'filterAll', 'resetSvg', 'expireCache' + ]; + + function addPassOnFunctions(functionName) { + _chart[functionName] = function () { + _leftChart[functionName](); + _rightChart[functionName](); + return _chart; + }; + } + + for (i = 0; i < _passOnFunctions.length; i++) { + addPassOnFunctions(_passOnFunctions[i]); + } + + return _chart.anchor(parent, chartGroup); +}; diff --git a/src/row-chart.js b/src/row-chart.js index 105ae2824..9f222d268 100644 --- a/src/row-chart.js +++ b/src/row-chart.js @@ -52,14 +52,18 @@ dc.rowChart = function (parent, chartGroup) { _chart.rowsCap = _chart.cap; + _chart.calculateAxisScaleData = function () { + return _rowData; + }; + function calculateAxisScale () { if (!_x || _elasticX) { - var extent = d3.extent(_rowData, _chart.cappedValueAccessor); + var extent = d3.extent(_chart.calculateAxisScaleData(), _chart.cappedValueAccessor); if (extent[0] > 0) { extent[0] = 0; } - var domain = d3.scale.linear().domain(extent); + if (_useRightYAxis) { _x = domain.range([_chart.effectiveWidth(), 0]); } else { @@ -188,7 +192,9 @@ dc.rowChart = function (parent, chartGroup) { } var rect = rows.attr('transform', function (d, i) { - return 'translate(0,' + ((i + 1) * _gap + i * height) + ')'; + var h = ((i + 1) * _gap + i * height), + w = _useRightYAxis ? _chart.effectiveWidth() : 0; + return 'translate(' + w + ',' + h + ')'; }).select('rect') .attr('height', height) .attr('fill', _chart.getColor) @@ -232,9 +238,10 @@ dc.rowChart = function (parent, chartGroup) { function updateLabels (rows) { if (_chart.renderLabel()) { var lab = rows.select('text') - .attr('x', _labelOffsetX) + .attr('x', _useRightYAxis ? -_labelOffsetX : _labelOffsetX) .attr('y', _labelOffsetY) .attr('dy', _dyOffset) + .attr('text-anchor', _useRightYAxis ? 'end' : 'start') .on('click', onClick) .attr('class', function (d, i) { return _rowCssClass + ' _' + i; @@ -243,13 +250,21 @@ dc.rowChart = function (parent, chartGroup) { return _chart.label()(d); }); dc.transition(lab, _chart.transitionDuration()) - .attr('transform', translateX); + .attr('transform', function (d) { + if (_useRightYAxis) { + return 'translate(0,0)'; + } + return translateX(d); + }); } if (_chart.renderTitleLabel()) { var titlelab = rows.select('.' + _titleRowCssClass) - .attr('x', _chart.effectiveWidth() - _titleLabelOffsetX) + .attr('x', _useRightYAxis ? + _titleLabelOffsetX - _chart.effectiveWidth() : + _chart.effectiveWidth() - _titleLabelOffsetX + ) .attr('y', _labelOffsetY) - .attr('text-anchor', 'end') + .attr('text-anchor', _useRightYAxis ? 'start' : 'end') .on('click', onClick) .attr('class', function (d, i) { return _titleRowCssClass + ' _' + i ; @@ -258,7 +273,12 @@ dc.rowChart = function (parent, chartGroup) { return _chart.title()(d); }); dc.transition(titlelab, _chart.transitionDuration()) - .attr('transform', translateX); + .attr('transform', function (d) { + if (_useRightYAxis) { + return 'translate(0,0)'; + } + return translateX(d); + }); } } @@ -287,6 +307,11 @@ dc.rowChart = function (parent, chartGroup) { var x = _x(_chart.cappedValueAccessor(d)), x0 = rootValue(), s = x > x0 ? x0 : x; + + if (_useRightYAxis) { + s -= _chart.effectiveWidth(); + } + return 'translate(' + s + ',0)'; } diff --git a/web/examples/paired-row.html b/web/examples/paired-row.html new file mode 100644 index 000000000..3cc1ad820 --- /dev/null +++ b/web/examples/paired-row.html @@ -0,0 +1,128 @@ + + + + dc.js - Paired Row Chart Example + + + + + +
+
+ + + + + + + + diff --git a/web/examples/paried-row-data.csv b/web/examples/paried-row-data.csv new file mode 100644 index 000000000..6fd22cca0 --- /dev/null +++ b/web/examples/paried-row-data.csv @@ -0,0 +1,31 @@ +gender,age +Male,10 +Male,18 +Male,20 +Male,22 +Male,30 +Male,40 +Male,45 +Male,50 +Male,55 +Male,70 +Male,80 +Male,90 +Male,100 +Male,101 +Female,11 +Female,15 +Female,20 +Female,21 +Female,22 +Female,30 +Female,40 +Female,50 +Female,60 +Female,65 +Female,70 +Female,80 +Female,90 +Female,99 +Female,100 +Unknown,100 From 487178d27aee11d0920eaf3bb4cfe68c239aacc8 Mon Sep 17 00:00:00 2001 From: Gordon Woodhull Date: Thu, 17 Dec 2015 13:12:45 -0500 Subject: [PATCH 03/13] de-lint --- spec/helpers/fixtures.js | 2 +- spec/paired-row-chart-spec.js | 80 +++++++++++++---------------------- src/paired-row-chart.js | 12 +++--- 3 files changed, 37 insertions(+), 57 deletions(-) diff --git a/spec/helpers/fixtures.js b/spec/helpers/fixtures.js index 302f085bf..7c2c173cc 100644 --- a/spec/helpers/fixtures.js +++ b/spec/helpers/fixtures.js @@ -78,7 +78,7 @@ function loadColorFixture2 () { "]"); } -function loadGenderFixture() { +function loadGenderFixture () { return JSON.parse("[" + "{\"gender\":\"Male\", \"age\": \"10\"}," + "{\"gender\":\"Male\", \"age\": \"18\"}," + diff --git a/spec/paired-row-chart-spec.js b/spec/paired-row-chart-spec.js index e7f997452..a02b59a2c 100644 --- a/spec/paired-row-chart-spec.js +++ b/spec/paired-row-chart-spec.js @@ -1,61 +1,41 @@ -describe('dc.pairedRowChart', function() { +/* global appendChartID, loadGenderFixture */ +describe('dc.pairedRowChart', function () { var id, chart; var data, dimension, group, dummyGroup; beforeEach(function () { - genderFixture = loadGenderFixture(); + var genderFixture = loadGenderFixture(); data = crossfilter(genderFixture); - dimension = data.dimension(function(d) { - var age_range = 'Unknown'; - - if (d.age <= 9) { - age_range = '0 - 9'; - } else if (d.age <= 19) { - age_range = '10 - 19'; - } else if (d.age <= 29) { - age_range = '20 - 29'; - } else if (d.age <= 39) { - age_range = '30 - 39'; - } else if (d.age <= 49) { - age_range = '40 - 49'; - } else if (d.age <= 59) { - age_range = '50 - 59'; - } else if (d.age <= 69) { - age_range = '60 - 69'; - } else if (d.age <= 79) { - age_range = '70 - 79'; - } else if (d.age <= 89) { - age_range = '80 - 89'; - } else if (d.age <= 99) { - age_range = '90 - 99'; - } else if (d.age >= 100) { - age_range = '100+'; - } + var ageRanges = ['0 - 9', '10 - 19', '20 - 29', '30 - 39', '40 - 49', '50 - 59', + '60 - 69', '70 - 79', '80 - 89', '90 - 99', '100+']; + + dimension = data.dimension(function (d) { + var ageRange = (d.age <= 99) ? + ageRanges[Math.floor(d.age / 10)] : + ageRanges[10]; - return [d.gender, age_range]; - }), + return [d.gender, ageRange]; + }); group = dimension.group().reduceCount(); dummyGroup = { - all: function() { - var ageRanges = ['0 - 9', '10 - 19', '20 - 29', '30 - 39', '40 - 49', '50 - 59', '60 - 69', '70 - 79', '80 - 89', '90 - 99', '100+']; - + all: function () { // convert to object so we can easily tell if a key exists var values = {}; - group.all().forEach(function(d) { + group.all().forEach(function (d) { values[d.key[0] + '.' + d.key[1]] = d.value; }); // convert back into an array for the chart, making sure that all ageRanges exist var g = []; - ageRanges.forEach(function(age_range) { + ageRanges.forEach(function (ageRange) { g.push({ - key: ['Male', age_range], - value: values['Male.' + age_range] || 0 + key: ['Male', ageRange], + value: values['Male.' + ageRange] || 0 }); g.push({ - key: ['Female', age_range], - value: values['Female.' + age_range] || 0 + key: ['Female', ageRange], + value: values['Female.' + ageRange] || 0 }); }); @@ -66,15 +46,15 @@ describe('dc.pairedRowChart', function() { id = 'paired-row-chart'; appendChartID(id); - chart = dc.pairedRowChart("#" + id); + chart = dc.pairedRowChart('#' + id); chart.dimension(dimension) .group(dummyGroup) .width(600).height(200).gap(10) - .leftKeyFilter(function(d) { - return d.key[0] === 'Male'; + .leftKeyFilter(function (d) { + return d.key[0] === 'Male'; }) - .rightKeyFilter(function(d) { - return d.key[0] === 'Female'; + .rightKeyFilter(function (d) { + return d.key[0] === 'Female'; }) .transitionDuration(0); }); @@ -112,10 +92,10 @@ describe('dc.pairedRowChart', function() { }); }); - describe('calculateAxisScaleData', function() { - it('should equal the group data', function() { + describe('calculateAxisScaleData', function () { + it('should equal the group data', function () { expect(chart.leftChart().calculateAxisScaleData().length).toBe(dummyGroup.all().length); - }) + }); }); }); @@ -152,10 +132,10 @@ describe('dc.pairedRowChart', function() { }); }); - describe('calculateAxisScaleData', function() { - it('should equal the group data', function() { + describe('calculateAxisScaleData', function () { + it('should equal the group data', function () { expect(chart.rightChart().calculateAxisScaleData().length).toBe(dummyGroup.all().length); - }) + }); }); }); }); diff --git a/src/paired-row-chart.js b/src/paired-row-chart.js index 69207f1ea..9501142b4 100644 --- a/src/paired-row-chart.js +++ b/src/paired-row-chart.js @@ -214,12 +214,12 @@ dc.pairedRowChart = function (parent, chartGroup) { }; // get the charts - mainly used for testing - _chart.leftChart = function() { - return _leftChart; + _chart.leftChart = function () { + return _leftChart; }; - _chart.rightChart = function() { - return _rightChart; + _chart.rightChart = function () { + return _rightChart; }; // functions that we just want to pass on to both sub charts @@ -238,7 +238,7 @@ dc.pairedRowChart = function (parent, chartGroup) { 'cap', 'ordering' , 'dimension', 'group', 'othersGrouper', 'data' ]; - function addGetterSetterFunction(functionName) { + function addGetterSetterFunction (functionName) { _chart[functionName] = function (_) { if (!arguments.length) { return _leftChart[functionName](); @@ -257,7 +257,7 @@ dc.pairedRowChart = function (parent, chartGroup) { '_doRedraw', 'redraw', '_doRender', 'render', 'calculateColorDomain', 'filterAll', 'resetSvg', 'expireCache' ]; - function addPassOnFunctions(functionName) { + function addPassOnFunctions (functionName) { _chart[functionName] = function () { _leftChart[functionName](); _rightChart[functionName](); From 953b8a5e51d7fd3bfb7eff08f61346bed9942f9d Mon Sep 17 00:00:00 2001 From: ruhley Date: Wed, 23 Sep 2015 15:46:32 +1000 Subject: [PATCH 04/13] Allowing access to the paired row chart xAxis and yAxis --- src/paired-row-chart.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/paired-row-chart.js b/src/paired-row-chart.js index 9501142b4..113e890d1 100644 --- a/src/paired-row-chart.js +++ b/src/paired-row-chart.js @@ -135,6 +135,7 @@ dc.pairedRowChart = function (parent, chartGroup) { // the margins between the charts need to be set to 0 so that they sit together var _margins = _chart.margins(); // get the default margins + _margins.right = _margins.left; _chart.margins = function (_) { if (!arguments.length) { @@ -231,9 +232,9 @@ dc.pairedRowChart = function (parent, chartGroup) { //colors 'colors', 'ordinalColors', 'linearColors', 'colorAccessor', 'colorDomain', 'getColor', 'colorCalculator', // x axis - 'x', 'elasticX', 'valueAccessor', 'labelOffsetX', 'titleLabelOffsetx', + 'x', 'elasticX', 'valueAccessor', 'labelOffsetX', 'titleLabelOffsetx', 'xAxis', // y axis - 'keyAccessor', 'labelOffsetY', + 'keyAccessor', 'labelOffsetY', 'yAxis', // data 'cap', 'ordering' , 'dimension', 'group', 'othersGrouper', 'data' ]; @@ -241,7 +242,7 @@ dc.pairedRowChart = function (parent, chartGroup) { function addGetterSetterFunction (functionName) { _chart[functionName] = function (_) { if (!arguments.length) { - return _leftChart[functionName](); + return [_leftChart[functionName](), _rightChart[functionName]()]; } _leftChart[functionName](_); _rightChart[functionName](_); From 7718f7e99b32250e1fcd31746098a32718d5b0ac Mon Sep 17 00:00:00 2001 From: ruhley Date: Thu, 17 Dec 2015 13:17:27 -0500 Subject: [PATCH 05/13] Paired row chart x axis labels --- src/paired-row-chart.js | 8 ++++++++ web/examples/paired-row.html | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/paired-row-chart.js b/src/paired-row-chart.js index 113e890d1..60fa31129 100644 --- a/src/paired-row-chart.js +++ b/src/paired-row-chart.js @@ -223,6 +223,14 @@ dc.pairedRowChart = function (parent, chartGroup) { return _rightChart; }; + _chart.leftXAxisLabel = function (labelText, padding) { + return _leftChart.xAxisLabel(labelText, padding); + }; + + _chart.rightXAxisLabel = function (labelText, padding) { + return _rightChart.xAxisLabel(labelText, padding); + }; + // functions that we just want to pass on to both sub charts var _getterSetterPassOn = [ diff --git a/web/examples/paired-row.html b/web/examples/paired-row.html index 3cc1ad820..7a38bb910 100644 --- a/web/examples/paired-row.html +++ b/web/examples/paired-row.html @@ -90,20 +90,22 @@ return '#C95AC7'; }, - - // data - dimension: ageGenderDimension, - group: group, - - // misc renderTitleLabel: true, title: function(d) { - return d.key[0] + ': ' + d.key[1]; + return d.key[1]; }, label: function(d) { - return d.key[0] + ': ' + d.key[1]; + return d.key[1]; }, + + // data + dimension: ageGenderDimension, + group: group, + + // x axis elasticX: true, + leftXAxisLabel: 'Male', + rightXAxisLabel: 'Female', // new fields for paired row chart leftKeyFilter: function(d) { From 6aea3a56c0165fec9db730186b3f1f42b4738312 Mon Sep 17 00:00:00 2001 From: ruhley Date: Thu, 19 Nov 2015 14:56:38 +1000 Subject: [PATCH 06/13] Fixing up margins on a paired row chart --- src/paired-row-chart.js | 79 +++++++---------------------------------- 1 file changed, 12 insertions(+), 67 deletions(-) diff --git a/src/paired-row-chart.js b/src/paired-row-chart.js index 60fa31129..f6c850f22 100644 --- a/src/paired-row-chart.js +++ b/src/paired-row-chart.js @@ -131,77 +131,22 @@ dc.pairedRowChart = function (parent, chartGroup) { }); }; - // width and margins - - // the margins between the charts need to be set to 0 so that they sit together - var _margins = _chart.margins(); // get the default margins - _margins.right = _margins.left; - - _chart.margins = function (_) { - if (!arguments.length) { - return _margins; - } - _margins = _; - - // set left chart margins - _leftChart.margins({ - top: _.top, - right: 0, - bottom: _.bottom, - left: _.left, - }); - - // set right chart margins - _rightChart.margins({ - top: _.top, - right: _.right, - bottom: _.bottom, - left: 0, - }); - - return _chart; + // margins + var left_margins = _leftChart.margins; + var right_margins = _rightChart.margins; + + _leftChart.margins = function() { + var margins = left_margins(); + margins.right = 0; + return margins; }; - _chart.margins(_margins); // set the new margins - - // the width needs to be halved - var _width = 0; // get the default width - - _chart.width = function (_) { - if (!arguments.length) { - return _width; - } - _width = _; - - // set left chart width - _leftChart.width(dc.utils.isNumber(_) ? _ / 2 : _); - - // set right chart width - _rightChart.width(dc.utils.isNumber(_) ? _ / 2 : _); - - return _chart; + _rightChart.margins = function() { + var margins = right_margins(); + margins.left = 0; + return margins; }; - // the minWidth needs to be halved - var _minWidth = _chart.minWidth(); // get the default minWidth - - _chart.minWidth = function (_) { - if (!arguments.length) { - return _minWidth; - } - _minWidth = _; - - // set left chart minWidth - _leftChart.minWidth(dc.utils.isNumber(_) ? _ / 2 : _); - - // set right chart minWidth - _rightChart.minWidth(dc.utils.isNumber(_) ? _ / 2 : _); - - return _chart; - }; - - _chart.minWidth(_minWidth); // set the new minWidth - // svg // return an array of both the sub chart svgs From 35000d68de8ffd390dedba8041e318a2bf37b465 Mon Sep 17 00:00:00 2001 From: Gordon Woodhull Date: Thu, 17 Dec 2015 13:37:40 -0500 Subject: [PATCH 07/13] de-lint --- src/paired-row-chart.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/paired-row-chart.js b/src/paired-row-chart.js index f6c850f22..fe2d82d8a 100644 --- a/src/paired-row-chart.js +++ b/src/paired-row-chart.js @@ -132,17 +132,17 @@ dc.pairedRowChart = function (parent, chartGroup) { }; // margins - var left_margins = _leftChart.margins; - var right_margins = _rightChart.margins; + var leftMargins = _leftChart.margins; + var rightMargins = _rightChart.margins; - _leftChart.margins = function() { - var margins = left_margins(); + _leftChart.margins = function () { + var margins = leftMargins(); margins.right = 0; return margins; }; - _rightChart.margins = function() { - var margins = right_margins(); + _rightChart.margins = function () { + var margins = rightMargins(); margins.left = 0; return margins; }; From e609b4d7c28ae85858d608de4baf997ec53b7a46 Mon Sep 17 00:00:00 2001 From: ruhley Date: Thu, 17 Dec 2015 13:40:00 -0500 Subject: [PATCH 08/13] Fixing up paired row chart width --- src/paired-row-chart.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/paired-row-chart.js b/src/paired-row-chart.js index fe2d82d8a..2e2b6c7f4 100644 --- a/src/paired-row-chart.js +++ b/src/paired-row-chart.js @@ -28,8 +28,8 @@ var chart2 = dc.pairedRowChart('#chart-container2', 'chartGroupA'); dc.pairedRowChart = function (parent, chartGroup) { var _chart = dc.capMixin(dc.marginMixin(dc.colorMixin(dc.baseMixin({})))); - var _leftChartWrapper = d3.select(parent).append('div'); - var _rightChartWrapper = d3.select(parent).append('div'); + var _leftChartWrapper = d3.select(parent).append('div').style('width', '50%').style('display', 'inline-block'); + var _rightChartWrapper = d3.select(parent).append('div').style('width', '50%').style('display', 'inline-block'); var _leftChart = dc.rowChart(_leftChartWrapper[0][0], chartGroup); var _rightChart = dc.rowChart(_rightChartWrapper[0][0], chartGroup); From 3cb4838dcc419a02c01dc651cddab292fa5d611a Mon Sep 17 00:00:00 2001 From: ruhley Date: Tue, 27 Oct 2015 10:56:30 +1000 Subject: [PATCH 09/13] Row chart x axis label --- src/row-chart.js | 51 +++++++++++++++++++++++++++++++++++++++++++ web/examples/row.html | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/row-chart.js b/src/row-chart.js index 9f222d268..2b7b1b135 100644 --- a/src/row-chart.js +++ b/src/row-chart.js @@ -23,6 +23,9 @@ */ dc.rowChart = function (parent, chartGroup) { + var X_AXIS_LABEL_CLASS = 'x-axis-label'; + var DEFAULT_AXIS_LABEL_PADDING = 30; + var _g; var _labelOffsetX = 10; @@ -31,6 +34,9 @@ dc.rowChart = function (parent, chartGroup) { var _dyOffset = '0.35em'; // this helps center labels https://github.com/mbostock/d3/wiki/SVG-Shapes#svg_text var _titleLabelOffsetX = 2; + var _xAxisLabel; + var _xAxisLabelPadding = 0; + var _gap = 5; var _fixedBarHeight = false; @@ -85,6 +91,26 @@ dc.rowChart = function (parent, chartGroup) { dc.transition(axisG, _chart.transitionDuration()) .call(_xAxis); + + renderXAxisLabel(axisG); + } + + function renderXAxisLabel (g) { + var axisXLab = g.selectAll('text.' + X_AXIS_LABEL_CLASS); + + if (axisXLab.empty() && _chart.xAxisLabel()) { + axisXLab = g.append('text') + .attr('class', X_AXIS_LABEL_CLASS) + .attr('transform', + 'translate(' + (_chart.margins().left + _chart.xAxisLength() / 2) + + ',' + _xAxisLabelPadding + ')' + ) + .attr('text-anchor', 'middle'); + } + + if (_chart.xAxisLabel() && axisXLab.text() !== _chart.xAxisLabel()) { + axisXLab.text(_chart.xAxisLabel()); + } } _chart._doRender = function () { @@ -396,6 +422,31 @@ dc.rowChart = function (parent, chartGroup) { return _chart; }; + _chart.xAxisLength = function () { + return _chart.effectiveWidth(); + }; + + /** + * Set or get the x axis label. If setting the label, you may optionally include additional padding to + * the margin to make room for the label. By default the padded is set to 12 to accomodate the text height. + * @name xAxisLabel + * @memberof dc.coordinateGridMixin + * @instance + * @param {String} [labelText] + * @param {Number} [padding=12] + * @return {String} + */ + _chart.xAxisLabel = function (labelText, padding) { + if (!arguments.length) { + return _xAxisLabel; + } + _xAxisLabel = labelText; + _chart.margins().bottom -= _xAxisLabelPadding; + _xAxisLabelPadding = (padding === undefined) ? DEFAULT_AXIS_LABEL_PADDING : padding; + _chart.margins().bottom += _xAxisLabelPadding; + return _chart; + }; + /** * Get or set the x offset (horizontal space to the top left corner of a row) for labels on a particular row chart. * @name labelOffsetX diff --git a/web/examples/row.html b/web/examples/row.html index a5de945b4..8e515c304 100644 --- a/web/examples/row.html +++ b/web/examples/row.html @@ -1,7 +1,7 @@ - dc.js - Bar Chart Example + dc.js - Row Chart Example From 3d3622cd794c94c2b2d0af5de95b08de6d66dbe0 Mon Sep 17 00:00:00 2001 From: ruhley Date: Tue, 27 Oct 2015 13:47:12 +1000 Subject: [PATCH 10/13] Fixing up row chart x axis label --- src/row-chart.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/row-chart.js b/src/row-chart.js index 2b7b1b135..2eef8ebed 100644 --- a/src/row-chart.js +++ b/src/row-chart.js @@ -24,7 +24,7 @@ dc.rowChart = function (parent, chartGroup) { var X_AXIS_LABEL_CLASS = 'x-axis-label'; - var DEFAULT_AXIS_LABEL_PADDING = 30; + var DEFAULT_AXIS_LABEL_PADDING = 12; var _g; @@ -92,18 +92,18 @@ dc.rowChart = function (parent, chartGroup) { dc.transition(axisG, _chart.transitionDuration()) .call(_xAxis); - renderXAxisLabel(axisG); + renderXAxisLabel(); } - function renderXAxisLabel (g) { - var axisXLab = g.selectAll('text.' + X_AXIS_LABEL_CLASS); + function renderXAxisLabel () { + var axisXLab = _g.selectAll('text.' + X_AXIS_LABEL_CLASS); if (axisXLab.empty() && _chart.xAxisLabel()) { - axisXLab = g.append('text') + axisXLab = _g.append('text') .attr('class', X_AXIS_LABEL_CLASS) .attr('transform', 'translate(' + (_chart.margins().left + _chart.xAxisLength() / 2) + - ',' + _xAxisLabelPadding + ')' + ',' + (_chart.height() - _xAxisLabelPadding) + ')' ) .attr('text-anchor', 'middle'); } From eaf59916d622012fbadf6c86659c1efc0079e38c Mon Sep 17 00:00:00 2001 From: ruhley Date: Mon, 2 Nov 2015 11:41:09 +1000 Subject: [PATCH 11/13] Fixing up x axis label positioning (mainly during resizing) --- src/row-chart.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/row-chart.js b/src/row-chart.js index 2eef8ebed..144a72fb5 100644 --- a/src/row-chart.js +++ b/src/row-chart.js @@ -111,6 +111,10 @@ dc.rowChart = function (parent, chartGroup) { if (_chart.xAxisLabel() && axisXLab.text() !== _chart.xAxisLabel()) { axisXLab.text(_chart.xAxisLabel()); } + + dc.transition(axisXLab, _chart.transitionDuration()) + .attr('transform', 'translate(' + (_chart.margins().left + _chart.xAxisLength() / 2) + ',' + + (_chart.height() - _xAxisLabelPadding) + ')'); } _chart._doRender = function () { From 711d62456e5532b41ddeaca34113771d75dfda21 Mon Sep 17 00:00:00 2001 From: ruhley Date: Mon, 2 Nov 2015 12:05:43 +1000 Subject: [PATCH 12/13] Fixing up row chart x axis label positioning --- src/row-chart.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/row-chart.js b/src/row-chart.js index 144a72fb5..5910c9ac0 100644 --- a/src/row-chart.js +++ b/src/row-chart.js @@ -102,7 +102,7 @@ dc.rowChart = function (parent, chartGroup) { axisXLab = _g.append('text') .attr('class', X_AXIS_LABEL_CLASS) .attr('transform', - 'translate(' + (_chart.margins().left + _chart.xAxisLength() / 2) + + 'translate(' + (_chart.xAxisLength() / 2) + ',' + (_chart.height() - _xAxisLabelPadding) + ')' ) .attr('text-anchor', 'middle'); @@ -113,7 +113,7 @@ dc.rowChart = function (parent, chartGroup) { } dc.transition(axisXLab, _chart.transitionDuration()) - .attr('transform', 'translate(' + (_chart.margins().left + _chart.xAxisLength() / 2) + ',' + + .attr('transform', 'translate(' + (_chart.xAxisLength() / 2) + ',' + (_chart.height() - _xAxisLabelPadding) + ')'); } From e9db6fee9dac345e1593fc0ba777fbfb5e1bec0a Mon Sep 17 00:00:00 2001 From: Gordon Woodhull Date: Thu, 17 Dec 2015 14:35:34 -0500 Subject: [PATCH 13/13] update doc; changelog --- Changelog.md | 2 ++ src/row-chart.js | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 70baafd9f..31d1dbeb6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,8 @@ * Heatmap allows customizing the ordering separately from the values, by Matt Traynham ([#869](https://github.com/dc-js/dc.js/pull/869) - thanks also to Quinn Lee for [#837](https://github.com/dc-js/dc.js/pull/837)) * Front page stable version automatically read from GitHub, by Enrico Spinielli ([#865](https://github.com/dc-js/dc.js/pull/865)) * Functional-style filter handlers: instead of modifying the array of filters in-place, filter handlers must return the new filter array. This is consistent with the old documention, but a different implementation: any changes to the `filters` argument will be ignored unless they are returned. This should make filter handlers easier to reason about. + * Row chart improvements `useRightYAxis`, `xAxisLabel`, by Timothy Ruhle + * Paired row chart, by Timothy Ruhle ([#510](https://github.com/dc-js/dc.js/issues/510) / [#943](https://github.com/dc-js/dc.js/pull/943)) # 2.0 Series ## 2.0.0 beta 23 diff --git a/src/row-chart.js b/src/row-chart.js index 5910c9ac0..2930e7fdd 100644 --- a/src/row-chart.js +++ b/src/row-chart.js @@ -504,15 +504,20 @@ dc.rowChart = function (parent, chartGroup) { }; /** - #### .useRightYAxis() - Gets or sets whether the chart should be drawn with a right axis instead of a left axis. + * Gets or sets whether the chart should be drawn with a right axis instead of a left axis. + * @name useRightYAxis + * @memberof dc.rowChart + * @instance + * @param {Number} [useRightYAxis=false] + * @return {Number} + * @return {dc.rowChart} **/ - _chart.useRightYAxis = function (_) { + _chart.useRightYAxis = function (useRightYAxis) { if (!arguments.length) { return _useRightYAxis; } - _useRightYAxis = _; + _useRightYAxis = useRightYAxis; return _chart; };