diff --git a/spec/bubble-chart-spec.js b/spec/bubble-chart-spec.js index 5030ea225..54a66a11e 100644 --- a/spec/bubble-chart-spec.js +++ b/spec/bubble-chart-spec.js @@ -232,10 +232,10 @@ describe('dc.bubbleChart', function () { it('creates correct label for each bubble', function () { chart.selectAll('g.node title').each(function (d, i) { if (i === 0) { - expect(d3.select(this).text()).toBe('F: {count:0,value:0}'); + expect(d3.select(this).text()).toBe('T: {count:2,value:77}'); } if (i === 1) { - expect(d3.select(this).text()).toBe('T: {count:2,value:77}'); + expect(d3.select(this).text()).toBe('F: {count:0,value:0}'); } }); }); @@ -243,10 +243,10 @@ describe('dc.bubbleChart', function () { it('fills bubbles with correct colors', function () { chart.selectAll('circle.bubble').each(function (d, i) { if (i === 0) { - expect(d3.select(this).attr('fill')).toBe('#a60000'); + expect(d3.select(this).attr('fill')).toBe('#ff4040'); } if (i === 1) { - expect(d3.select(this).attr('fill')).toBe('#ff4040'); + expect(d3.select(this).attr('fill')).toBe('#a60000'); } }); }); diff --git a/src/bubble-chart.js b/src/bubble-chart.js index 1a01c853b..ae2e3e83b 100644 --- a/src/bubble-chart.js +++ b/src/bubble-chart.js @@ -60,8 +60,14 @@ dc.bubbleChart = function (parent, chartGroup) { _chart.r().range([_chart.MIN_RADIUS, _chart.xAxisLength() * _chart.maxBubbleRelativeSize()]); + // sort descending so smaller bubbles are on top + var data = _chart.data(), + radiusAccessor = _chart.radiusValueAccessor(); + data.sort(function (a, b) { return d3.descending(radiusAccessor(a), radiusAccessor(b)); }); var bubbleG = _chart.chartBodyG().selectAll('g.' + _chart.BUBBLE_NODE_CLASS) - .data(_chart.data(), function (d) { return d.key; }); + .data(data, function (d) { return d.key; }); + // Call order here to update dom order based on sort + bubbleG.order(); renderNodes(bubbleG); diff --git a/src/bubble-mixin.js b/src/bubble-mixin.js index f18f280d4..861bc4d4a 100644 --- a/src/bubble-mixin.js +++ b/src/bubble-mixin.js @@ -95,8 +95,16 @@ dc.bubbleMixin = function (_chart) { return _chart.label()(d); }; + var shouldLabel = function (d) { + return (_chart.bubbleR(d) > _minRadiusWithLabel); + }; + var labelOpacity = function (d) { - return (_chart.bubbleR(d) > _minRadiusWithLabel) ? 1 : 0; + return shouldLabel(d) ? 1 : 0; + }; + + var labelDisplay = function (d) { + return shouldLabel(d) ? 'block' : 'none'; }; _chart._doRenderLabel = function (bubbleGEnter) { @@ -112,18 +120,29 @@ dc.bubbleMixin = function (_chart) { label .attr('opacity', 0) - .text(labelFunction); + .text(labelFunction) + .style('display', 'none'); dc.transition(label, _chart.transitionDuration()) - .attr('opacity', labelOpacity); + .attr('opacity', labelOpacity) + .call(dc.afterTransition, label.style.bind(label, 'display', labelDisplay)); } }; _chart.doUpdateLabels = function (bubbleGEnter) { if (_chart.renderLabel()) { var labels = bubbleGEnter.selectAll('text') - .text(labelFunction); + .text(labelFunction) + .style('display', function (d) { + // On update, we can't fade in if it's hidden... + var current = d3.select(this).style('display'); + if (current === 'none' && shouldLabel(d)) { + return 'block'; + } + return current; + }); dc.transition(labels, _chart.transitionDuration()) - .attr('opacity', labelOpacity); + .attr('opacity', labelOpacity) + .call(dc.afterTransition, labels.style.bind(labels, 'display', labelDisplay)); } }; diff --git a/src/core.js b/src/core.js index 867e5becc..fc9b0bb29 100644 --- a/src/core.js +++ b/src/core.js @@ -212,6 +212,22 @@ dc.optionalTransition = function (enable, duration, callback, name) { } }; +// See http://stackoverflow.com/a/20773846 +dc.afterTransition = function (transition, callback) { + if (transition.empty() || !transition.duration) { + callback.call(transition); + } else { + var n = 0; + transition + .each(function () { ++n; }) + .each('end', function () { + if (!--n) { + callback.call(transition); + } + }); + } +}; + /** * @name units * @memberof dc