diff --git a/spec/line-chart-spec.js b/spec/line-chart-spec.js index 745df366a..2b142f5a6 100644 --- a/spec/line-chart-spec.js +++ b/spec/line-chart-spec.js @@ -123,6 +123,16 @@ describe('dc.lineChart', function () { }); }); + describe('label rendering', function () { + beforeEach(function () { + chart.renderLabel(true); + chart.render(); + }); + + it('should generate a label for each datum', lineLabelCount); + it('should generate labels with positions corresponding to their data', lineLabelPositions); + }); + describe('data point highlights and refs off', function () { beforeEach(function () { chart.title(function (d) { return d.value; }); @@ -464,6 +474,16 @@ describe('dc.lineChart', function () { }); }); }); + + describe('label rendering', function () { + beforeEach(function () { + chart.renderLabel(true); + chart.render(); + }); + + it('should generate a label for each datum', lineLabelCount); + it('should generate labels with positions corresponding to their data', lineLabelPositions); + }); }); describe('with negative data', function () { @@ -506,6 +526,16 @@ describe('dc.lineChart', function () { expect(nthText(1).text()).toBe('0'); expect(nthText(2).text()).toBe('20'); }); + + describe('label rendering', function () { + beforeEach(function () { + chart.renderLabel(true); + chart.render(); + }); + + it('should generate a label for each datum', lineLabelCount); + it('should generate labels with positions corresponding to their data', lineLabelPositions); + }); }); }); @@ -650,4 +680,19 @@ describe('dc.lineChart', function () { expect(chart.select('circle.dot')[0][0].attributes.fill.value).toMatch(/#FF0000/i); }); }); + + function lineLabelCount () { + expect(chart.selectAll('text.lineLabel').size()).toBe(chart.stack().length * chart.group().all().length); + } + + function lineLabelPositions () { + var LABEL_PADDING = 3; + chart.selectAll('.stack')[0].forEach(function (stack, i) { + d3.select(stack).selectAll('text.lineLabel')[0].forEach(function (lineLabel, j) { + expect(+d3.select(lineLabel).attr('x')).toBeCloseTo(chart.x()(chart.data()[i].values[j].x)); + expect(+d3.select(lineLabel).attr('y') + LABEL_PADDING).toBeCloseTo(chart.y()(chart.data()[i].values[j].y + + chart.data()[i].values[j].y0)); + }); + }); + } }); diff --git a/src/line-chart.js b/src/line-chart.js index 759e1732c..7687c34f0 100644 --- a/src/line-chart.js +++ b/src/line-chart.js @@ -31,6 +31,7 @@ dc.lineChart = function (parent, chartGroup) { var Y_AXIS_REF_LINE_CLASS = 'yRef'; var X_AXIS_REF_LINE_CLASS = 'xRef'; var DEFAULT_DOT_OPACITY = 1e-6; + var LABEL_PADDING = 3; var _chart = dc.stackMixin(dc.coordinateGridMixin({})); var _renderArea = false; @@ -69,6 +70,10 @@ dc.lineChart = function (parent, chartGroup) { drawArea(layersEnter, layers); drawDots(chartBody, layers); + + if (_chart.renderLabel()) { + drawLabels(layers); + } }; /** @@ -309,6 +314,39 @@ dc.lineChart = function (parent, chartGroup) { } } + _chart.label(function (d) { + return dc.utils.printSingleValue(d.y0 + d.y); + }, false); + + function drawLabels (layers) { + layers.each(function (d, layerIndex) { + var layer = d3.select(this); + var labels = layer.selectAll('text.lineLabel') + .data(d.values, dc.pluck('x')); + + labels.enter() + .append('text') + .attr('class', 'lineLabel') + .attr('text-anchor', 'middle'); + + dc.transition(labels, _chart.transitionDuration()) + .attr('x', function (d) { + return dc.utils.safeNumber(_chart.x()(d.x)); + }) + .attr('y', function (d) { + var y = _chart.y()(d.y + d.y0) - LABEL_PADDING; + return dc.utils.safeNumber(y); + }) + .text(function (d) { + return _chart.label()(d); + }); + + dc.transition(labels.exit(), _chart.transitionDuration()) + .attr('height', 0) + .remove(); + }); + } + function createRefLines (g) { var yRefLine = g.select('path.' + Y_AXIS_REF_LINE_CLASS).empty() ? g.append('path').attr('class', Y_AXIS_REF_LINE_CLASS) : g.select('path.' + Y_AXIS_REF_LINE_CLASS);