UNPKG

dc

Version:

A multi-dimensional charting library built to work natively with crossfilter and rendered using d3.js

1,211 lines (1,029 loc) 53 kB
/* global appendChartID, loadDateFixture, makeDate, cleanDateRange */ describe('dc.barChart', function () { var id, chart, data; var dimension, group; beforeEach(function () { data = crossfilter(loadDateFixture()); dimension = data.dimension(function (d) { return d3.time.day.utc(d.dd); }); group = dimension.group(); id = 'bar-chart'; appendChartID(id); chart = dc.barChart('#' + id); chart.dimension(dimension).group(group) .width(1100).height(200) .x(d3.time.scale.utc().domain([makeDate(2012, 0, 1), makeDate(2012, 11, 31)])) .transitionDuration(0) .controlsUseVisibility(true); }); describe('rendering', function () { beforeEach(function () { chart.render(); }); it('should set bar height using y-values from data', function () { forEachBar(function (bar, datum) { expect(+bar.attr('y')).toBe(chart.y()(datum.data.value)); }); }); it('should set bar width to the minimum for a relatively small chart', function () { forEachBar(function (bar) { expect(+bar.attr('width')).toBe(1); }); }); it('should preserve method chaining', function () { expect(chart.render()).toEqual(chart); }); it('should not display bar labels without setting renderLabel(true)', function () { expect(chart.selectAll('text.barLabel').size()).toBe(0); }); describe('with centered bars', function () { beforeEach(function () { chart.centerBar(true).render(); }); it('should position bars centered around their data points', function () { var halfBarWidth = 0.5; forEachBar(function (bar, datum) { var barPosition = chart.x()(datum.data.key); expect(+bar.attr('x')).toBeCloseTo(barPosition - halfBarWidth, 3); }); }); }); describe('without centered bars', function () { it('should position bars starting at their data points', function () { forEachBar(function (bar, datum) { var barPosition = chart.x()(datum.data.key); expect(+bar.attr('x')).toBeCloseTo(barPosition, 3); }); }); }); describe('with bar labels', function () { beforeEach(function () { chart.renderLabel(true).render(); }); it('should generate a label for each datum', function () { expect(chart.selectAll('text.barLabel').size()).toBe(6); }); it('should generate labels with positions corresponding to their data', function () { expect(nthStack(0).nthLabel(0).attr('x')).toBeWithinDelta(405, 1); expect(nthStack(0).nthLabel(0).attr('y')).toBeWithinDelta(104, 1); expect(nthStack(0).nthLabel(0).text()).toBe('1'); expect(nthStack(0).nthLabel(3).attr('x')).toBeWithinDelta(509, 1); expect(nthStack(0).nthLabel(3).attr('y')).toBeWithinDelta(104, 1); expect(nthStack(0).nthLabel(3).text()).toBe('1'); expect(nthStack(0).nthLabel(5).attr('x')).toBeWithinDelta(620, 1); expect(nthStack(0).nthLabel(5).attr('y')).toBeWithinDelta(50, 1); expect(nthStack(0).nthLabel(5).text()).toBe('2'); }); }); describe('with custom bar labels', function () { beforeEach(function () { chart.label(function () { return 'custom label'; }).render(); }); it('should render a label for each datum', function () { expect(chart.selectAll('text.barLabel').size()).toBe(6); }); it('should use the custom function for each label', function () { chart.selectAll('text.barLabel').each(function () { expect(d3.select(this).text()).toBe('custom label'); }); }); describe('with labels disabled', function () { beforeEach(function () { chart.renderLabel(false).render(); }); it('should not display labels', function () { expect(chart.selectAll('text.barLabel').size()).toBe(0); }); }); }); describe('and then switching the group at runtime', function () { beforeEach(function () { chart.rescale(); // BUG: barWidth cannot change after initial rendering var domain = [makeDate(2012, 4, 20), makeDate(2012, 7, 15)]; chart.x(d3.time.scale.utc().domain(domain)) .group(dimension.group().reduceSum(function (d) { return +d.nvalue; })) .elasticY(true) .centerBar(false) .xUnits(d3.time.days.utc) .yAxis().ticks(5); chart.render(); }); it('should generate a bar for each datum', function () { expect(chart.selectAll('rect.bar').size()).toBe(6); }); it('should automatically resize the bar widths', function () { forEachBar(function (bar) { expect(bar.attr('width')).toBe('9'); }); }); function nthYAxisText (n) { return d3.select(chart.selectAll('g.y text')[0][n]); } it('should generate bars with positions corresponding to their data', function () { expect(nthStack(0).nthBar(0).attr('x')).toBeWithinDelta(58, 1); expect(nthStack(0).nthBar(0).attr('y')).toBeWithinDelta(84, 1); expect(nthStack(0).nthBar(0).attr('height')).toBeWithinDelta(30, 1); expect(nthStack(0).nthBar(3).attr('x')).toBeWithinDelta(492, 1); expect(nthStack(0).nthBar(3).attr('y')).toBeWithinDelta(84, 1); expect(nthStack(0).nthBar(3).attr('height')).toBeWithinDelta(23, 1); expect(nthStack(0).nthBar(5).attr('x')).toBeWithinDelta(961, 1); expect(nthStack(0).nthBar(5).attr('y')).toBeWithinDelta(61, 1); expect(nthStack(0).nthBar(5).attr('height')).toBeWithinDelta(23, 1); }); it('should generate the y-axis domain dynamically', function () { expect(nthYAxisText(0).text()).toMatch(/-10/); expect(nthYAxisText(1).text()).toMatch(/-5/); expect(nthYAxisText(2).text()).toBe('0'); }); }); describe('with an ordinal x domain', function () { var stateDimension; beforeEach(function () { stateDimension = data.dimension(function (d) { return d.state; }); var stateGroup = stateDimension.group(); var ordinalDomainValues = ['California', 'Colorado', 'Delaware', 'Ontario', 'Mississippi', 'Oklahoma']; chart.rescale(); // BUG: barWidth cannot change after initial rendering chart.dimension(stateDimension) .group(stateGroup) .xUnits(dc.units.ordinal) .x(d3.scale.ordinal().domain(ordinalDomainValues)) .barPadding(0) .outerPadding(0.1) .render(); }); it('should automatically disable the brush', function () { expect(chart.brushOn()).toBeFalsy(); }); it('should generate a bar for each ordinal domain value', function () { expect(chart.selectAll('rect.bar').size()).toBe(6); }); it('should size the bars proportionally to the graph', function () { expect(+chart.select('rect.bar').attr('width')).toBe(164); }); it('should position the bar based on the ordinal range', function () { expect(nthStack(0).nthBar(0).attr('x')).toBeWithinDelta(16, 1); expect(nthStack(0).nthBar(3).attr('x')).toBeWithinDelta(674, 1); expect(nthStack(0).nthBar(5).attr('x')).toBeWithinDelta(509, 1); }); it('should fade deselected bars', function () { chart.filter('Ontario').filter('Colorado').redraw(); expect(nthStack(0).nthBar(0).classed('deselected')).toBeTruthy(); expect(nthStack(0).nthBar(1).classed('deselected')).toBeFalsy(); expect(nthStack(0).nthBar(5).classed('deselected')).toBeFalsy(); expect(stateDimension.top(Infinity).length).toBe(3); }); it('should respect the ordering of the specified domain', function () { // Note that bar chart works differently from pie chart. The bar objects (the // actual DOM nodes) don't get reordered by the custom ordering, but they are // placed so that they are drawn in the order specified. var ontarioXPos = nthStack(0).nthBar(5).attr('x'); var mississippiXPos = nthStack(0).nthBar(3).attr('x'); var oklahomaXPos = nthStack(0).nthBar(4).attr('x'); expect(ontarioXPos).toBeLessThan(mississippiXPos); expect(mississippiXPos).toBeLessThan(oklahomaXPos); }); describe('with elasticY enabled', function () { beforeEach(function () { chart.elasticY(true).render(); }); it('should use all ordinal keys to determine the maximum y', function () { expect(chart.y().domain()).toEqual([0, 3]); }); }); describe('with an unspecified domain', function () { beforeEach(function () { chart.x(d3.scale.ordinal()).render(); }); it('should use alphabetical ordering', function () { var data = chart.selectAll('rect.bar').data(); var expectedData = ['California', 'Colorado', 'Delaware', 'Mississippi', 'Oklahoma', 'Ontario']; expect(data.map(function (datum) { return datum.x; })).toEqual(expectedData); var oldX = -Infinity; forEachBar(function (bar) { expect(bar.attr('x')).toBeGreaterThan(oldX); oldX = bar.attr('x'); }); }); }); describe('redrawing after changing the value accessor', function () { beforeEach(function () { chart.valueAccessor(function () { return 30; }); chart.redraw(); }); it('should position bars based on ordinal range', function () { expect(nthStack(0).nthBar(0).attr('height')).toBe('1600'); expect(nthStack(0).nthBar(1).attr('height')).toBe('1600'); expect(nthStack(0).nthBar(2).attr('height')).toBe('1600'); }); }); describe('clicking', function () { it('causes other dimension to be filtered', function () { expect(dimension.top(Infinity).length).toEqual(10); // fake a click var abar = chart.selectAll('rect.bar:nth-child(3)'); abar.on('click')(abar.datum()); expect(dimension.top(Infinity).length).toEqual(1); }); }); describe('clicking bar labels', function () { beforeEach(function () { chart.renderLabel(true).render(); }); it('causes other dimension to be filtered', function () { expect(dimension.top(Infinity).length).toEqual(10); // fake a click var alabel = chart.select('text.barLabel'); alabel.on('click')(alabel.datum()); expect(dimension.top(Infinity).length).toEqual(3); }); }); }); describe('with a linear x domain', function () { beforeEach(function () { var linearDimension = data.dimension(function (d) { return +d.value; }); var linearGroup = linearDimension.group(); chart.rescale(); // BUG: barWidth cannot change after initial rendering chart.dimension(linearDimension) .group(linearGroup) .xUnits(dc.units.integers) .x(d3.scale.linear().domain([20, 70])) .render(); }); it('should generate the correct number of bars', function () { expect(chart.selectAll('rect.bar').size()).toBe(5); }); it('should auto size bar width', function () { forEachBar(function (bar) { expect(bar.attr('width')).toBe('18'); }); }); it('should position bars based on linear range', function () { expect(nthStack(0).nthBar(0).attr('x')).toBeWithinDelta(40, 1); expect(nthStack(0).nthBar(2).attr('x')).toBeWithinDelta(489, 1); expect(nthStack(0).nthBar(4).attr('x')).toBeWithinDelta(938, 1); }); describe('with a custom click handler', function () { beforeEach(function () { chart.brushOn(false) .on('renderlet', function (_chart) { _chart.selectAll('rect.bar').on('click', _chart.onClick); }) .render(); }); it('clicking causes another dimension to be filtered', function () { expect(dimension.top(Infinity).length).toEqual(10); var abar = chart.selectAll('rect.bar:nth-child(3)'); abar.on('click')(abar.datum()); expect(dimension.top(Infinity).length).toEqual(3); }); }); }); describe('with stacked data', function () { describe('with positive data', function () { beforeEach(function () { var idGroup = dimension.group().reduceSum(function (d) { return d.id; }); var sumGroup = dimension.group().reduceSum(function (d) { return d.value; }); chart .brushOn(false) .x(d3.time.scale.utc().domain([makeDate(2012, 4, 20), makeDate(2012, 7, 15)])) .group(idGroup, 'stack 0') .title('stack 0', function (d) { return 'stack 0: ' + d.value; }) .stack(sumGroup, 'stack 1') .title('stack 1', function (d) { return 'stack 1: ' + d.value; }) .stack(sumGroup, 'stack 2', function (d) { return 3; }) .elasticY(true) .renderLabel(true) .render(); }); it('should set the y domain to encompass all stacks', function () { expect(chart.y().domain()).toEqual([0, 152]); }); it('should generate each stack using its associated group', function () { expect(nthStack(0).selectAll('rect.bar').size()).toBe(6); expect(nthStack(1).selectAll('rect.bar').size()).toBe(6); expect(nthStack(2).selectAll('rect.bar').size()).toBe(6); }); it('should render the correct number of stacks', function () { expect(chart.selectAll('.stack').size()).toBe(3); }); it('should display one label for each stack', function () { expect(chart.selectAll('text.barLabel').size()).toBe(6); }); it('should generate labels with total value of stack', function () { expect(nthStack(2).nthLabel(0).text()).toBe('48'); expect(nthStack(2).nthLabel(3).text()).toBe('51'); expect(nthStack(2).nthLabel(5).text()).toBe('92'); }); it('should stack the bars', function () { expect(+nthStack(0).nthBar(2).attr('y')).toBe(142); expect(+nthStack(0).nthBar(4).attr('y')).toBe(144); expect(+nthStack(1).nthBar(2).attr('y')).toBe(3); expect(+nthStack(1).nthBar(4).attr('y')).toBe(86); expect(+nthStack(2).nthBar(2).attr('y')).toBe(0); expect(+nthStack(2).nthBar(4).attr('y')).toBe(83); }); it('should have its own title accessor', function () { expect(chart.title()({value: 1})).toBe('stack 0: 1'); expect(chart.title('stack 0')({value: 2})).toBe('stack 0: 2'); expect(chart.title('stack 1')({value: 3})).toBe('stack 1: 3'); }); it('should have titles rendered for extra stacks', function () { nthStack(1).forEachBar(function (bar, datum) { expect(bar.selectAll('title')[0].length).toBe(1); expect(bar.select('title').text()).toBe('stack 1: ' + datum.data.value); }); }); it('should default to first stack title for untitled stacks', function () { nthStack(2).forEachBar(function (bar, datum) { expect(bar.select('title').text()).toBe('stack 0: ' + datum.data.value); }); }); describe('extra redraws', function () { beforeEach(function () { chart.redraw(); chart.redraw(); }); it('should not create extra title elements', function () { nthStack(1).forEachBar(function (bar, datum) { expect(bar.selectAll('title')[0].length).toBe(1); }); }); }); describe('with title rendering disabled', function () { beforeEach(function () { chart.renderTitle(false).render(); }); it('should not generate title elements', function () { expect(chart.selectAll('rect.bar title').empty()).toBeTruthy(); }); }); describe('stack hiding', function () { describe('first stack', function () { beforeEach(function () { chart.hideStack('stack 0').render(); }); it('should hide the stack', function () { expect(nthStack(0).nthBar(0).attr('height')).toBe('52'); expect(nthStack(0).nthBar(1).attr('height')).toBe('78'); }); it('should show the stack', function () { chart.showStack('stack 0').render(); expect(nthStack(0).nthBar(0).attr('height')).toBe('1'); expect(nthStack(0).nthBar(1).attr('height')).toBe('6'); }); }); describe('any other stack', function () { beforeEach(function () { chart.title('stack 2', function (d) { return 'stack 2: ' + d.value; }); chart.hideStack('stack 1').render(); }); it('should hide the stack', function () { expect(nthStack(1).nthBar(0).attr('height')).toBe('24'); expect(nthStack(1).nthBar(1).attr('height')).toBe('24'); }); it('should show the stack', function () { chart.showStack('stack 1').render(); expect(nthStack(1).nthBar(0).attr('height')).toBe('46'); expect(nthStack(1).nthBar(1).attr('height')).toBe('70'); }); it('should still show the title for a visible stack', function () { nthStack(1).forEachBar(function (bar, datum) { expect(bar.select('title').text()).toBe('stack 2: ' + datum.data.value); }); }); }); describe('hiding all the stacks', function () { beforeEach(function () { chart.hideStack('stack 0') .hideStack('stack 1') .hideStack('stack 2') .render(); }); it('should show a blank graph', function () { expect(chart.selectAll('rect.bar').size()).toBe(0); }); }); }); }); describe('with mixed positive and negative data', function () { beforeEach(function () { var mixedGroup = dimension.group().reduceSum(function (d) { return d.nvalue; }); chart.group(mixedGroup).stack(mixedGroup).stack(mixedGroup); chart.x(d3.time.scale.utc().domain([makeDate(2012, 4, 20), makeDate(2012, 7, 15)])); chart.margins({top: 30, right: 50, bottom: 30, left: 30}) .yAxisPadding(5) .elasticY(true) .xUnits(d3.time.days.utc) .yAxis().ticks(5); chart.rescale(); // BUG: barWidth cannot change after initial rendering chart.render(); }); it('should generate a bar for each datum across all stacks', function () { expect(chart.selectAll('rect.bar').size()).toBe(18); }); it('should automatically size the bar widths', function () { forEachBar(function (bar) { expect(bar.attr('width')).toBe('9'); }); }); it('should generate negative bars for stack 0', function () { expect(nthStack(0).nthBar(0).attr('x')).toBeWithinDelta(58, 1); expect(nthStack(0).nthBar(0).attr('y')).toBeWithinDelta(73, 1); expect(nthStack(0).nthBar(0).attr('height')).toBeWithinDelta(8, 1); expect(nthStack(0).nthBar(3).attr('x')).toBeWithinDelta(492, 1); expect(nthStack(0).nthBar(3).attr('y')).toBeWithinDelta(73, 1); expect(nthStack(0).nthBar(3).attr('height')).toBeWithinDelta(6, 1); expect(nthStack(0).nthBar(5).attr('x')).toBeWithinDelta(961, 1); expect(nthStack(0).nthBar(5).attr('y')).toBeWithinDelta(67, 1); expect(nthStack(0).nthBar(5).attr('height')).toBeWithinDelta(6, 1); }); it('should generate negative bar for stack 1', function () { expect(nthStack(1).nthBar(0).attr('x')).toBeWithinDelta(58, 1); expect(nthStack(1).nthBar(0).attr('y')).toBeWithinDelta(81, 1); expect(nthStack(1).nthBar(0).attr('height')).toBeWithinDelta(7, 1); expect(nthStack(1).nthBar(3).attr('x')).toBeWithinDelta(492, 1); expect(nthStack(1).nthBar(3).attr('y')).toBeWithinDelta(79, 1); expect(nthStack(1).nthBar(3).attr('height')).toBeWithinDelta(5, 1); expect(nthStack(1).nthBar(5).attr('x')).toBeWithinDelta(961, 1); expect(nthStack(1).nthBar(5).attr('y')).toBeWithinDelta(61, 1); expect(nthStack(1).nthBar(5).attr('height')).toBeWithinDelta(6, 1); }); it('should generate y axis domain dynamically', function () { var nthText = function (n) { return d3.select(chart.selectAll('g.axis.y .tick text')[0][n]); }; expect(nthText(0).text()).toBe('-20'); expect(nthText(1).text()).toBe('0'); expect(nthText(2).text()).toBe('20'); }); }); describe('with negative data', function () { beforeEach(function () { var negativeGroup = dimension.group().reduceSum(function (d) { return -Math.abs(d.nvalue); }); chart.group(negativeGroup).stack(negativeGroup).stack(negativeGroup); chart.x(d3.time.scale.utc().domain([makeDate(2012, 4, 20), makeDate(2012, 7, 15)])); chart.margins({top: 30, right: 50, bottom: 30, left: 30}) .elasticY(true) .xUnits(d3.time.days.utc) .yAxis().ticks(3); chart.render(); }); it('should generate y axis domain dynamically', function () { var nthText = function (n) { return d3.select(chart.selectAll('g.axis.y .tick text')[0][n]); }; expect(nthText(0).text()).toBe('-30'); expect(nthText(1).text()).toBe('-20'); expect(nthText(2).text()).toBe('-10'); expect(nthText(3).text()).toBe('0'); }); }); }); it('should not be focused by default', function () { expect(chart.refocused()).toBeFalsy(); }); describe('when focused', function () { beforeEach(function () { chart.elasticY(true).gap(1).xUnits(d3.time.days.utc); chart.focus([makeDate(2012, 5, 11), makeDate(2012, 6, 9)]); }); it('should render the one (focused) bar', function () { expect(chart.selectAll('rect.bar').size()).toBe(1); }); it('should resize the bar width according to the focused width', function () { expect(chart.select('rect.bar').attr('width')).toBe('35'); }); it('should reset the y-axis domain based on the focus range', function () { expect(chart.y().domain()).toEqual([0, 1]); }); it('should redraw the x-axis scale and ticks', function () { expect(xAxisText().slice(0, 4)).toEqual(['Mon 11', 'Wed 13', 'Fri 15', 'Jun 17']); }); it('should set its focus flag', function () { expect(chart.refocused()).toBeTruthy(); }); it('should reset the focus when focused to null', function () { chart.focus(null); itBehavesLikeItWasReset(); }); it('should reset the focus when focused to []', function () { chart.focus([]); itBehavesLikeItWasReset(); }); function itBehavesLikeItWasReset () { expect(chart.refocused()).toBeFalsy(); expect(chart.x().domain()).toEqual([makeDate(2012, 0, 1), makeDate(2012, 11, 31)]); expect(xAxisText().slice(0, 4)).toEqual(['2012', 'February', 'March', 'April']); } function xAxisText () { return chart.selectAll('g.x text')[0].map(function (x) { return d3.select(x).text(); }); } }); describe('legend hovering', function () { var firstItem; beforeEach(function () { chart.stack(group) .legend(dc.legend().x(400).y(10).itemHeight(13).gap(5)) .render(); firstItem = chart.select('g.dc-legend g.dc-legend-item'); firstItem.on('mouseover')(firstItem.datum()); }); describe('when a legend item is hovered over', function () { it('should highlight corresponding lines and areas', function () { nthStack(0).forEachBar(function (bar) { expect(bar.classed('highlight')).toBeTruthy(); }); }); it('should fade out non-corresponding lines and areas', function () { nthStack(1).forEachBar(function (bar) { expect(bar.classed('fadeout')).toBeTruthy(); }); }); }); describe('when a legend item is hovered out', function () { it('should remove highlighting from corresponding lines and areas', function () { firstItem.on('mouseout')(firstItem.datum()); nthStack(0).forEachBar(function (bar) { expect(bar.classed('highlight')).toBeFalsy(); }); }); it('should fade in non-corresponding lines and areas', function () { firstItem.on('mouseout')(firstItem.datum()); nthStack(1).forEachBar(function (bar) { expect(bar.classed('fadeout')).toBeFalsy(); }); }); }); }); describe('filtering', function () { beforeEach(function () { d3.select('#' + id).append('span').attr('class', 'filter').style('visibility', 'hidden'); d3.select('#' + id).append('a').attr('class', 'reset').style('visibility', 'hidden'); chart.filter([makeDate(2012, 5, 1), makeDate(2012, 5, 30)]).redraw(); dc.dateFormat = d3.time.format.utc('%m/%d/%Y'); chart.redraw(); }); it('should set the chart filter', function () { expect(chart.filter()).toEqual([makeDate(2012, 5, 1), makeDate(2012, 5, 30)]); }); it('should enable the reset link after rendering', function () { expect(chart.select('a.reset').style('visibility')).not.toBe('none'); }); it('should set the filter printer', function () { expect(chart.filterPrinter()).not.toBeNull(); }); it('should show the filter info', function () { expect(chart.select('span.filter').style('visibility')).toBe('visible'); }); it('should set filter text after slice selection', function () { expect(chart.select('span.filter').text()).toBe('[06/01/2012 -> 06/30/2012]'); }); describe('when a brush is defined', function () { it('should position the brush with an offset', function () { expect(chart.select('g.brush').attr('transform')).toMatchTranslate(chart.margins().left, 10); }); it('should create a fancy brush resize handle', function () { chart.select('g.brush').selectAll('.resize path').each(function (d, i) { if (i === 0) { expect(d3.select(this).attr('d')) .toMatchPath('M0.5,53 A6,6 0 0 1 6.5,59 V100 A6,6 0 0 1 0.5,106 ZM2.5,61 V98 M4.5,61 V98'); } else { expect(d3.select(this).attr('d')) .toMatchPath('M-0.5,53 A6,6 0 0 0 -6.5,59 V100 A6,6 0 0 0 -0.5,106 ZM-2.5,61 V98 M-4.5,61 V98'); } }); }); it('should stretch the background', function () { expect(+chart.select('g.brush rect.background').attr('width')).toBe(1020); }); it('should set the background height to the chart height', function () { expect(+chart.select('g.brush rect.background').attr('height')).toBe(160); }); it('should set extent height to the chart height', function () { expect(+chart.select('g.brush rect.extent').attr('height')).toBe(160); }); it('should set extent width based on filter set', function () { expect(chart.select('g.brush rect.extent').attr('width')).toBeWithinDelta(81, 1); }); it('should push unselected bars to the background', function () { expect(nthStack(0).nthBar(0).classed('deselected')).toBeTruthy(); expect(nthStack(0).nthBar(1).classed('deselected')).toBeFalsy(); expect(nthStack(0).nthBar(3).classed('deselected')).toBeTruthy(); }); it('should push the selected bars to the foreground', function () { expect(nthStack(0).nthBar(1).classed('deselected')).toBeFalsy(); }); describe('after reset', function () { beforeEach(function () { chart.filterAll(); chart.redraw(); }); it('should push all bars to the foreground', function () { chart.selectAll('rect.bar').each(function () { var bar = d3.select(this); expect(bar.classed('deselected')).toBeFalsy(); }); }); }); }); }); describe('a chart with a large domain', function () { beforeEach(function () { chart.x(d3.time.scale.utc().domain([makeDate(2000, 0, 1), makeDate(2012, 11, 31)])); }); describe('when filters are applied', function () { beforeEach(function () { data.dimension(function (d) { return d.value; }).filter(66); chart.redraw(); }); it('should not deselect any bars', function () { forEachBar(function (bar) { expect(bar.classed('deselected')).toBeFalsy(); }); }); it('should set the bars to the minimum bar width', function () { forEachBar(function (bar) { expect(+bar.attr('width')).toBe(1); }); }); }); }); describe('a chart with a linear numerical domain', function () { beforeEach(function () { var numericalDimension = data.dimension(function (d) { return +d.value; }); chart.dimension(numericalDimension).group(numericalDimension.group()); chart.x(d3.scale.linear().domain([10, 80])).elasticY(true); chart.render(); }); it('should base the y-axis height on the maximum value in the data', function () { var yAxisMax = 3.0; var ticks = chart.selectAll('g.y g.tick'); var tickValues = ticks[0].map(function (tick) { return +d3.select(tick).text(); }); var maxTickValue = Math.max.apply(this, tickValues); expect(maxTickValue).toBe(yAxisMax); }); describe('when filters are applied', function () { beforeEach(function () { data.dimension(function (d) { return d.countrycode; }).filter('CA'); chart.redraw(); }); it('should rescale the y-axis after applying a filter', function () { var yAxisMax = 1.0; var ticks = chart.selectAll('g.y g.tick'); var tickValues = ticks[0].map(function (tick) { return +d3.select(tick).text(); }); var maxTickValue = Math.max.apply(this, tickValues); expect(maxTickValue).toBe(yAxisMax); }); }); }); }); describe('with another ordinal domain', function () { beforeEach(function () { var rows = []; rows.push({State: 'CA', 'Population': 2704659}); rows.push({State: 'TX', 'Population': 1827307}); data = crossfilter(rows); dimension = data.dimension(dc.pluck('State')); group = dimension.group().reduceSum(dc.pluck('Population')); chart = dc.barChart('#' + id); chart.xUnits(dc.units.ordinal) .x(d3.scale.ordinal()) .transitionDuration(0) .dimension(dimension) .group(group, 'Population'); chart.render(); }); it('should not overlap bars', function () { var x = numAttr('x'), wid = numAttr('width'); expect(x(nthStack(0).nthBar(0)) + wid(nthStack(0).nthBar(0))) .toBeLessThan(x(nthStack(0).nthBar(1))); }); }); describe('with yetnother ordinal domain', function () { beforeEach(function () { var rows = [{ name: 'Venezuela', sale: 300 }, { name: 'Saudi', sale: 253 }, { name: 'Canada', sale: 150 }, { name: 'Iran', sale: 125 }, { name: 'Russia', sale: 110 }, { name: 'UAE', sale: 90 }, { name: 'US', sale: 40 }, { name: 'China', sale: 37 }]; data = crossfilter(rows); dimension = data.dimension(function (d) { return d.name; }); group = dimension.group().reduceSum(function (d) { return d.sale; }); chart = dc.barChart('#' + id); chart.transitionDuration(0) .outerPadding(0) .dimension(dimension) .group(group) .x(d3.scale.ordinal()) .xUnits(dc.units.ordinal); chart.render(); }); it('should not overlap bars', function () { for (var i = 0; i < 7; ++i) { checkBarOverlap(i); } }); }); describe('with changing number of bars', function () { beforeEach(function () { var rows1 = [ {x: 1, y: 3}, {x: 2, y: 9}, {x: 5, y: 10}, {x: 6, y: 7} ]; data = crossfilter(rows1); dimension = data.dimension(function (d) { return d.x; }); group = dimension.group().reduceSum(function (d) { return d.y; }); chart = dc.barChart('#' + id); chart.width(500).transitionDuration(0) .x(d3.scale.linear().domain([0,7])) .elasticY(true) .dimension(dimension) .group(group); chart.render(); }); it('should not overlap bars', function () { for (var i = 0; i < 3; ++i) { checkBarOverlap(i); } }); describe('with bars added', function () { beforeEach(function () { var rows2 = [ {x: 7, y: 4}, {x: 12, y: 9} ]; data.add(rows2); chart.x().domain([0,13]); chart.render(); }); it('should not overlap bars', function () { for (var i = 0; i < 5; ++i) { checkBarOverlap(i); } }); }); }); describe('with elasticX and x-axis padding', function () { var date = makeDate(2012, 5, 1); beforeEach(function () { var rows = [ {x: date, y: 4}, ]; data = crossfilter(rows); dimension = data.dimension(function (d) { return d.x; }); group = dimension.group().reduceSum(function (d) { return d.y; }); chart = dc.barChart('#' + id); chart.width(500) .transitionDuration(0) .x(d3.time.scale()) .elasticY(true).elasticX(true) .dimension(dimension) .group(group); chart.render(); }); // note: these tests assume that the bar width is not included in the // chart width, so they should be broken when #792 is fixed it('should render the right xAxisMax/Min when no padding', function () { expect(chart.xAxisMin()).toEqual(date); expect(chart.xAxisMax()).toEqual(date); }); it('should render the right xAxisMax/Min when 10 day padding', function () { chart.xAxisPadding(10) .render(); var expectedStartDate = d3.time.day.offset(date, -10); var expectedEndDate = d3.time.day.offset(date, 10); expect(chart.xAxisMin()).toEqual(expectedStartDate); expect(chart.xAxisMax()).toEqual(expectedEndDate); }); it('should render the right xAxisMax/Min when 2 month padding', function () { chart.xAxisPaddingUnit('month') .xAxisPadding(2) .render(); var expectedStartDate = d3.time.month.offset(date, -2); var expectedEndDate = d3.time.month.offset(date, 2); expect(chart.xAxisMin()).toEqual(expectedStartDate); expect(chart.xAxisMax()).toEqual(expectedEndDate); }); }); describe('with changing number of bars and elasticX', function () { beforeEach(function () { var rows1 = [ {x: 1, y: 3}, {x: 2, y: 9}, {x: 5, y: 10}, {x: 6, y: 7} ]; data = crossfilter(rows1); dimension = data.dimension(function (d) { return d.x; }); group = dimension.group().reduceSum(function (d) { return d.y; }); chart = dc.barChart('#' + id); chart.width(500).transitionDuration(0) .x(d3.scale.linear()) .elasticY(true).elasticX(true) .dimension(dimension) .group(group); chart.render(); }); it('should not overlap bars', function () { for (var i = 0; i < 3; ++i) { checkBarOverlap(i); } }); describe('with bars added', function () { beforeEach(function () { var rows2 = [ {x: 7, y: 4}, {x: 12, y: 9} ]; data.add(rows2); chart.render(); }); it('should not overlap bars', function () { for (var i = 0; i < 5; ++i) { checkBarOverlap(i); } }); }); }); describe('with changing number of ordinal bars and elasticX', function () { beforeEach(function () { var rows1 = [ {x: 'a', y: 3}, {x: 'b', y: 9}, {x: 'e', y: 10}, {x: 'f', y: 7} ]; data = crossfilter(rows1); dimension = data.dimension(function (d) { return d.x; }); group = dimension.group().reduceSum(function (d) { return d.y; }); chart = dc.barChart('#' + id); chart.width(500).transitionDuration(0) .x(d3.scale.ordinal()) .xUnits(dc.units.ordinal) .elasticY(true).elasticX(true) .dimension(dimension) .group(group); chart.render(); }); it('should not overlap bars', function () { for (var i = 0; i < 3; ++i) { checkBarOverlap(i); } }); describe('with bars added', function () { beforeEach(function () { var rows2 = [ {x: 'g', y: 4}, {x: 'l', y: 9} ]; data.add(rows2); chart.render(); }); it('should not overlap bars', function () { for (var i = 0; i < 5; ++i) { checkBarOverlap(i); } }); }); }); describe('brushing with bars centered and rounding enabled', function () { beforeEach(function () { chart .brushOn(true) .round(d3.time.month.utc.round) .centerBar(true); }); describe('with alwaysUseRounding disabled', function () { var consoleWarnSpy; beforeEach(function () { chart.alwaysUseRounding(false); consoleWarnSpy = spyOn(console, 'warn'); chart.render(); chart.brush().extent([makeDate(2012, 6, 1), makeDate(2012, 7, 15)]); chart.brush().event(chart.root()); }); it('should log a warning indicating that brush rounding was disabled', function () { expect(consoleWarnSpy.calls.mostRecent().args[0]).toMatch(/brush rounding is disabled/); }); it('should not round the brush', function () { jasmine.clock().tick(100); var filter = cleanDateRange(chart.filter()); expect(filter).toEqual([makeDate(2012, 6, 1), makeDate(2012, 7, 15)]); }); }); describe('with alwaysUseRounding enabled', function () { beforeEach(function () { chart.alwaysUseRounding(true); chart.render(); chart.brush().extent([makeDate(2012, 6, 1), makeDate(2012, 7, 15)]); chart.brush().event(chart.root()); }); it('should round the brush', function () { jasmine.clock().tick(100); expect(chart.brush().extent()).toEqual([makeDate(2012, 6, 1), makeDate(2012, 7, 1)]); }); }); }); describe('check ordering option of the x axis', function () { beforeEach(function () { var rows = [ {x: 'a', y: 1}, {x: 'b', y: 3}, {x: 'd', y: 4}, {x: 'c', y: 2} ]; id = 'bar-chart'; appendChartID(id); data = crossfilter(rows); dimension = data.dimension(function (d) { return d.x; }); group = dimension.group().reduceSum(function (d) { return d.y; }); chart = dc.barChart('#' + id); chart.width(500).transitionDuration(0) .x(d3.scale.ordinal()) .xUnits(dc.units.ordinal) .elasticY(true).elasticX(true) .dimension(dimension) .group(group); chart.render(); }); it('should be ordered by default alphabetical order', function () { var data = chart.data()['0'].values; var expectedData = ['a', 'b', 'c', 'd']; expect(data.map(function (d) { return d.x; })).toEqual(expectedData); }); it('should be ordered by value increasing', function () { chart.ordering(function (d) { return d.value; }); chart.redraw(); expect(xAxisText()).toEqual(['a', 'c', 'b', 'd']); }); it('should be ordered by value decreasing', function () { chart.ordering(function (d) { return -d.value; }); chart.redraw(); expect(xAxisText()).toEqual(['d', 'b', 'c', 'a']); }); it('should be ordered by alphabetical order', function () { chart.ordering(function (d) { return d.key; }); chart.redraw(); expect(xAxisText()).toEqual(['a', 'b', 'c', 'd']); }); function xAxisText () { return chart.selectAll('g.x text')[0].map(function (x) { return d3.select(x).text(); }); } }); describe('ordering with stacks', function () { beforeEach(function () { var rows = [ {x: 'a', y: 1, z: 10}, {x: 'b', y: 3, z: 20}, {x: 'd', y: 4, z: 30}, {x: 'c', y: 2, z: 40} ]; id = 'bar-chart'; appendChartID(id); data = crossfilter(rows); dimension = data.dimension(function (d) {