From 430e20e69d79d7585db135c7f958de736d40ec4d Mon Sep 17 00:00:00 2001 From: Skitch Date: Thu, 16 Apr 2015 00:17:19 -0400 Subject: [PATCH] Using d3.svg.axis instead of custom axis implementation on heatmap --- spec/bubble-chart-spec.js | 4 +- spec/heatmap-spec.js | 24 +++++------ src/heatmap.js | 87 +++++++++++++++++++-------------------- style/dc.scss | 12 ++++-- 4 files changed, 63 insertions(+), 64 deletions(-) diff --git a/spec/bubble-chart-spec.js b/spec/bubble-chart-spec.js index 26a83103b..6d9b67a42 100644 --- a/spec/bubble-chart-spec.js +++ b/spec/bubble-chart-spec.js @@ -695,7 +695,7 @@ describe('dc.bubbleChart', function () { } describe('column filtering with straight crossfilter', function () { beforeEach(function () { - var axisLabel = d3.select(heatMap.selectAll('.cols.axis text')[0][3]); + var axisLabel = d3.select(heatMap.selectAll('.cols.axis g')[0][3]); axisLabel.on('click')(axisLabel.datum()); d3.timer.flush(); }); @@ -713,7 +713,7 @@ describe('dc.bubbleChart', function () { beforeEach(function () { chart.group(clone_group(sepalGroup)); chart.render(); - var axisLabel = d3.select(heatMap.selectAll('.cols.axis text')[0][3]); + var axisLabel = d3.select(heatMap.selectAll('.cols.axis g')[0][3]); axisLabel.on('click')(axisLabel.datum()); d3.timer.flush(); }); diff --git a/spec/heatmap-spec.js b/spec/heatmap-spec.js index aa4a626b9..1a437b06a 100644 --- a/spec/heatmap-spec.js +++ b/spec/heatmap-spec.js @@ -76,11 +76,9 @@ describe('dc.heatmap', function () { }); it('should position the y-axis labels with their associated rows', function () { - var yaxisTexts = chart.selectAll('.rows.axis text'); - expect(+yaxisTexts[0][0].getAttribute('y')).toEqual(150); - expect(+yaxisTexts[0][0].getAttribute('x')).toEqual(0); - expect(+yaxisTexts[0][1].getAttribute('y')).toEqual(50); - expect(+yaxisTexts[0][1].getAttribute('x')).toEqual(0); + var yaxisTicks = chart.selectAll('.rows.axis .tick'); + expect(yaxisTicks[0][0].getAttribute('transform')).toMatchTranslate(0, 150); + expect(yaxisTicks[0][1].getAttribute('transform')).toMatchTranslate(0, 50); }); it('should have labels on the y-axis corresponding to the row values', function () { @@ -90,11 +88,9 @@ describe('dc.heatmap', function () { }); it('should position the x-axis labels with their associated columns', function () { - var xaxisTexts = chart.selectAll('.cols.axis text'); - expect(+xaxisTexts[0][0].getAttribute('y')).toEqual(200); - expect(+xaxisTexts[0][0].getAttribute('x')).toEqual(50); - expect(+xaxisTexts[0][1].getAttribute('y')).toEqual(200); - expect(+xaxisTexts[0][1].getAttribute('x')).toEqual(150); + var xaxisTexts = chart.selectAll('.cols.axis .tick'); + expect(xaxisTexts[0][0].getAttribute('transform')).toMatchTranslate(50, 0); + expect(xaxisTexts[0][1].getAttribute('transform')).toMatchTranslate(150, 0); }); it('should have labels on the x-axis corresponding to the row values', function () { @@ -355,13 +351,13 @@ describe('dc.heatmap', function () { describe('with nothing previously filtered', function () { it('should filter all cells on that axis', function () { - chart.selectAll('.cols.axis text').each(function (d) { + chart.selectAll('.cols.axis g').each(function (d) { var axisLabel = d3.select(this); axisLabel.on('click')(d); assertOnlyThisAxisIsFiltered(chart, 0, d); axisLabel.on('click')(d); }); - chart.selectAll('.rows.axis text').each(function (d) { + chart.selectAll('.rows.axis g').each(function (d) { var axisLabel = d3.select(this); axisLabel.on('click')(d); assertOnlyThisAxisIsFiltered(chart, 1, d); @@ -380,7 +376,7 @@ describe('dc.heatmap', function () { var xVal = box.datum().key[0]; - var columns = chart.selectAll('.cols.axis text'); + var columns = chart.selectAll('.cols.axis g'); var column = columns.filter(function (columnData) { return columnData === xVal; }); @@ -404,7 +400,7 @@ describe('dc.heatmap', function () { assertOnlyThisAxisIsFiltered(chart, 0, xVal); - var columns = chart.selectAll('.cols.axis text'); + var columns = chart.selectAll('.cols.axis g'); var column = columns.filter(function (columnData) { return columnData === xVal; }); diff --git a/src/heatmap.js b/src/heatmap.js index 6a802cca2..f4da44f5c 100644 --- a/src/heatmap.js +++ b/src/heatmap.js @@ -29,6 +29,8 @@ dc.heatMap = function (parent, chartGroup) { var _rowOrdering = d3.ascending; var _colScale = d3.scale.ordinal(); var _rowScale = d3.scale.ordinal(); + var _colAxis = d3.svg.axis().orient('bottom'); + var _rowAxis = d3.svg.axis().orient('left'); var _xBorderRadius = DEFAULT_BORDER_RADIUS; var _yBorderRadius = DEFAULT_BORDER_RADIUS; @@ -185,6 +187,13 @@ dc.heatMap = function (parent, chartGroup) { .append('g') .attr('class', 'heatmap') .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')'); + _chartBody.append('g') + .attr('class', 'cols axis') + .attr('transform', 'translate(0,' + _chart.effectiveHeight() + ')'); + _chartBody.append('g') + .attr('class', 'rows axis'); + _colScale.rangeRoundBands([0, _chart.effectiveWidth()]); + _rowScale.rangeRoundBands([_chart.effectiveHeight(), 0]); return _chart._doRedraw(); }; @@ -202,17 +211,39 @@ dc.heatMap = function (parent, chartGroup) { rows = _rowScale.domain(rows); cols = _colScale.domain(cols); - var rowCount = rows.domain().length, - colCount = cols.domain().length, - boxWidth = Math.floor(_chart.effectiveWidth() / colCount), - boxHeight = Math.floor(_chart.effectiveHeight() / rowCount); + // Update axis + _colAxis.scale(cols) + .tickFormat(_chart.colsLabel()); + var _colAxisTransition = dc.transition(_chartBody.select('g.cols.axis'), _chart.transitionDuration(), _chart.transitionDelay()) + .call(_colAxis); + _rowAxis.scale(rows) + .tickFormat(_chart.rowsLabel()); + var _rowAxisTransition = dc.transition(_chartBody.select('g.rows.axis'), _chart.transitionDuration(), _chart.transitionDelay()) + .call(_rowAxis); + // We need the clicks added after the transition. Unfortunately, this is handled differently + // for selection/transition + if (_colAxisTransition.duration || _rowAxisTransition.duration) { + _colAxisTransition.each('end', function () { + d3.select(this) + .selectAll('g') + .on('click', _chart.xAxisOnClick()); + }); + _rowAxisTransition.each('end', function () { + d3.select(this) + .selectAll('g') + .on('click', _chart.yAxisOnClick()); + }); + } else { + _colAxisTransition.selectAll('g').on('click', _chart.xAxisOnClick()); + _rowAxisTransition.selectAll('g').on('click', _chart.yAxisOnClick()); + } - cols.rangeRoundBands([0, _chart.effectiveWidth()]); - rows.rangeRoundBands([_chart.effectiveHeight(), 0]); + // Update boxes + var boxes = _chartBody.selectAll('g.box-group') + .data(_chart.data(), function (d, i) { + return _chart.keyAccessor()(d, i) + '\0' + _chart.valueAccessor()(d, i); + }); - var boxes = _chartBody.selectAll('g.box-group').data(_chart.data(), function (d, i) { - return _chart.keyAccessor()(d, i) + '\0' + _chart.valueAccessor()(d, i); - }); var gEnter = boxes.enter().append('g') .attr('class', 'box-group'); @@ -232,45 +263,11 @@ dc.heatMap = function (parent, chartGroup) { .attr('rx', _xBorderRadius) .attr('ry', _yBorderRadius) .attr('fill', _chart.getColor) - .attr('width', boxWidth) - .attr('height', boxHeight); + .attr('width', _colScale.rangeBand()) + .attr('height', _rowScale.rangeBand()); boxes.exit().remove(); - var gCols = _chartBody.select('g.cols'); - if (gCols.empty()) { - gCols = _chartBody.append('g').attr('class', 'cols axis'); - } - var gColsText = gCols.selectAll('text').data(cols.domain()); - gColsText.enter().append('text') - .attr('x', function (d) { return cols(d) + boxWidth / 2; }) - .style('text-anchor', 'middle') - .attr('y', _chart.effectiveHeight()) - .attr('dy', 12) - .on('click', _chart.xAxisOnClick()) - .text(_chart.colsLabel()); - dc.transition(gColsText, _chart.transitionDuration(), _chart.transitionDelay()) - .text(_chart.colsLabel()) - .attr('x', function (d) { return cols(d) + boxWidth / 2; }) - .attr('y', _chart.effectiveHeight()); - gColsText.exit().remove(); - var gRows = _chartBody.select('g.rows'); - if (gRows.empty()) { - gRows = _chartBody.append('g').attr('class', 'rows axis'); - } - var gRowsText = gRows.selectAll('text').data(rows.domain()); - gRowsText.enter().append('text') - .attr('dy', 6) - .style('text-anchor', 'end') - .attr('x', 0) - .attr('dx', -2) - .on('click', _chart.yAxisOnClick()) - .text(_chart.rowsLabel()); - dc.transition(gRowsText, _chart.transitionDuration(), _chart.transitionDelay()) - .text(_chart.rowsLabel()) - .attr('y', function (d) { return rows(d) + boxHeight / 2; }); - gRowsText.exit().remove(); - if (_chart.hasFilter()) { _chart.selectAll('g.box-group').each(function (d) { if (_chart.isSelectedNode(d)) { diff --git a/style/dc.scss b/style/dc.scss index 8844ac771..ca7e1d923 100644 --- a/style/dc.scss +++ b/style/dc.scss @@ -232,9 +232,15 @@ div.dc-chart { fill-opacity: 0.5; fill: $color_celeste; } - g.axis text { - pointer-events: all; - cursor: pointer; + g.axis { + text { + pointer-events: all; + cursor: pointer; + } + .domain, + .tick line { + opacity: 0; + } } } .empty-chart .pie-slice {