return this.chart.data.datasets[this.index];
},
- getScaleForId: function(scaleID) {
- return this.chart.scales[scaleID];
- },
-
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
_index: index,
});
- this.getDataset().backgroundColor.splice(index, 0, colorForNewElement);
+ if (colorForNewElement && helpers.isArray(this.getDataset().backgroundColor)) {
+ this.getDataset().backgroundColor.splice(index, 0, colorForNewElement);
+ }
// Reset the point
this.updateElement(arc, index, true);
this.chart.innerRadius = this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1;
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length;
-
this.getDataset().total = 0;
helpers.each(this.getDataset().data, function(value) {
this.getDataset().total += Math.abs(value);
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index);
this.innerRadius = this.outerRadius - this.chart.radiusLength;
+ // Make sure we have metaData for each data point
+ var numData = this.getDataset().data.length;
+ var numArcs = this.getDataset().metaData.length;
+
+ // Make sure that we handle number of datapoints changing
+ if (numData < numArcs) {
+ // Remove excess bars for data points that have been removed
+ this.getDataset().metaData.splice(numData, numArcs - numData)
+ } else if (numData > numArcs) {
+ // Add new elements
+ for (var index = numArcs; index < numData; ++index) {
+ this.addElementAndReset(index);
+ }
+ }
+
helpers.each(this.getDataset().metaData, function(arc, index) {
this.updateElement(arc, index, reset);
}, this);
}, this);
},
-
-
setHoverStyle: function(arc) {
var dataset = this.chart.data.datasets[arc._datasetIndex];
var index = arc._index;
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
- arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, arc._model.borderWidth);
+ arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, arc._model.borderWidth);
},
removeHoverStyle: function(arc) {
--- /dev/null
+// Test the bar controller
+describe('Doughnut controller tests', function() {
+ it('Should be constructed', function() {
+ var chart = {
+ data: {
+ datasets: [{
+ data: []
+ }]
+ }
+ };
+
+ var controller = new Chart.controllers.doughnut(chart, 0);
+ expect(controller).not.toBe(undefined);
+ expect(controller.index).toBe(0);
+ expect(chart.data.datasets[0].metaData).toEqual([]);
+
+ controller.updateIndex(1);
+ expect(controller.index).toBe(1);
+ });
+
+ it('Should create arc elements for each data item during initialization', function() {
+ var chart = {
+ data: {
+ datasets: [{
+ data: [10, 15, 0, 4]
+ }]
+ },
+ config: {
+ type: 'doughnut'
+ },
+ options: {
+ }
+ };
+
+ var controller = new Chart.controllers.doughnut(chart, 0);
+
+ expect(chart.data.datasets[0].metaData.length).toBe(4); // 4 rectangles created
+ expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Arc).toBe(true);
+ expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Arc).toBe(true);
+ expect(chart.data.datasets[0].metaData[2] instanceof Chart.elements.Arc).toBe(true);
+ expect(chart.data.datasets[0].metaData[3] instanceof Chart.elements.Arc).toBe(true);
+ });
+
+ it ('Should remove elements', function() {
+ var chart = {
+ data: {
+ datasets: [{
+ data: [10, 15, 0, 4]
+ }]
+ },
+ config: {
+ type: 'doughnut'
+ },
+ options: {
+ }
+ };
+
+ var controller = new Chart.controllers.doughnut(chart, 0);
+ controller.removeElement(1);
+ expect(chart.data.datasets[0].metaData.length).toBe(3);
+ });
+
+ it ('Should reset and update elements', function() {
+ var chart = {
+ chart: {
+ width: 100,
+ height: 200,
+ },
+ data: {
+ datasets: [{
+ data: [10, 15, 0, 4]
+ }, {
+ data: [1]
+ }],
+ labels: ['label0', 'label1', 'label2', 'label3']
+ },
+ config: {
+ type: 'doughnut'
+ },
+ options: {
+ animation: {
+ animateRotate: false,
+ animateScale: false
+ },
+ cutoutPercentage: 50,
+ elements: {
+ arc: {
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ hoverBackgroundColor: 'rgb(255, 255, 255)'
+ }
+ }
+ }
+ };
+
+ var controller = new Chart.controllers.doughnut(chart, 0);
+ controller.reset(); // reset first
+
+ expect(chart.data.datasets[0].metaData[0]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: Math.PI * -0.5,
+ circumference: 2.166614539857563,
+ outerRadius: 49,
+ innerRadius: 36.75,
+ });
+
+ expect(chart.data.datasets[0].metaData[1]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: Math.PI * -0.5,
+ circumference: 3.2499218097863447,
+ outerRadius: 49,
+ innerRadius: 36.75,
+ });
+
+ expect(chart.data.datasets[0].metaData[2]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: Math.PI * -0.5,
+ circumference: 0,
+ outerRadius: 49,
+ innerRadius: 36.75,
+ });
+
+ expect(chart.data.datasets[0].metaData[3]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: Math.PI * -0.5,
+ circumference: 0.8666458159430251,
+ outerRadius: 49,
+ innerRadius: 36.75,
+ });
+
+ controller.update();
+
+ expect(chart.data.datasets[0].metaData[0]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: Math.PI * -0.5,
+ endAngle: 0.5958182130626666,
+ circumference: 2.166614539857563,
+ outerRadius: 49,
+ innerRadius: 36.75,
+
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ hoverBackgroundColor: 'rgb(255, 255, 255)',
+
+ label: 'label0',
+ });
+
+ expect(chart.data.datasets[0].metaData[1]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: 0.5958182130626666,
+ endAngle: 3.8457400228490113,
+ circumference: 3.2499218097863447,
+ outerRadius: 49,
+ innerRadius: 36.75,
+
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ hoverBackgroundColor: 'rgb(255, 255, 255)',
+
+ label: 'label1'
+ });
+
+ expect(chart.data.datasets[0].metaData[2]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: 3.8457400228490113,
+ endAngle: 3.8457400228490113,
+ circumference: 0,
+ outerRadius: 49,
+ innerRadius: 36.75,
+
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ hoverBackgroundColor: 'rgb(255, 255, 255)',
+
+ label: 'label2'
+ });
+
+ expect(chart.data.datasets[0].metaData[3]._model).toEqual({
+ x: 50,
+ y: 100,
+ startAngle: 3.8457400228490113,
+ endAngle: 4.712385838792036,
+ circumference: 0.8666458159430251,
+ outerRadius: 49,
+ innerRadius: 36.75,
+
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ hoverBackgroundColor: 'rgb(255, 255, 255)',
+
+ label: 'label3'
+ });
+
+ // Change the amount of data and ensure that arcs are updated accordingly
+ chart.data.datasets[0].data = [1, 2]; // remove 2 elements from dataset 0
+ controller.update();
+
+ expect(chart.data.datasets[0].metaData.length).toBe(2);
+ expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Arc).toBe(true);
+ expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Arc).toBe(true);
+
+ // Add data
+ chart.data.datasets[0].data = [1, 2, 3, 4];
+ controller.update();
+
+ expect(chart.data.datasets[0].metaData.length).toBe(4);
+ expect(chart.data.datasets[0].metaData[0] instanceof Chart.elements.Arc).toBe(true);
+ expect(chart.data.datasets[0].metaData[1] instanceof Chart.elements.Arc).toBe(true);
+ expect(chart.data.datasets[0].metaData[2] instanceof Chart.elements.Arc).toBe(true);
+ expect(chart.data.datasets[0].metaData[3] instanceof Chart.elements.Arc).toBe(true);
+ });
+
+ it ('should draw all arcs', function() {
+ var chart = {
+ chart: {
+ width: 100,
+ height: 200,
+ },
+ data: {
+ datasets: [{
+ data: [10, 15, 0, 4]
+ }],
+ labels: ['label0', 'label1', 'label2', 'label3']
+ },
+ config: {
+ type: 'doughnut'
+ },
+ options: {
+ animation: {
+ animateRotate: false,
+ animateScale: false
+ },
+ cutoutPercentage: 50,
+ elements: {
+ arc: {
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ hoverBackgroundColor: 'rgb(255, 255, 255)'
+ }
+ }
+ }
+ };
+
+ var controller = new Chart.controllers.doughnut(chart, 0);
+
+ spyOn(chart.data.datasets[0].metaData[0], 'draw');
+ spyOn(chart.data.datasets[0].metaData[1], 'draw');
+ spyOn(chart.data.datasets[0].metaData[2], 'draw');
+ spyOn(chart.data.datasets[0].metaData[3], 'draw');
+
+ controller.draw();
+
+ expect(chart.data.datasets[0].metaData[0].draw.calls.count()).toBe(1);
+ expect(chart.data.datasets[0].metaData[1].draw.calls.count()).toBe(1);
+ expect(chart.data.datasets[0].metaData[2].draw.calls.count()).toBe(1);
+ expect(chart.data.datasets[0].metaData[3].draw.calls.count()).toBe(1);
+ });
+
+ it ('should set the hover style of an arc', function() {
+ var chart = {
+ chart: {
+ width: 100,
+ height: 200,
+ },
+ data: {
+ datasets: [{
+ data: [10, 15, 0, 4]
+ }],
+ labels: ['label0', 'label1', 'label2', 'label3']
+ },
+ config: {
+ type: 'doughnut'
+ },
+ options: {
+ animation: {
+ animateRotate: false,
+ animateScale: false
+ },
+ cutoutPercentage: 50,
+ elements: {
+ arc: {
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ }
+ }
+ }
+ };
+
+ var controller = new Chart.controllers.doughnut(chart, 0);
+ controller.reset(); // reset first
+ controller.update();
+
+ var arc = chart.data.datasets[0].metaData[0];
+
+ controller.setHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(230, 0, 0)');
+ expect(arc._model.borderColor).toBe('rgb(0, 0, 230)');
+ expect(arc._model.borderWidth).toBe(2);
+
+ // Set a dataset style to take precedence
+ chart.data.datasets[0].hoverBackgroundColor = 'rgb(9, 9, 9)';
+ chart.data.datasets[0].hoverBorderColor = 'rgb(18, 18, 18)';
+ chart.data.datasets[0].hoverBorderWidth = 1.56;
+
+ controller.setHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(9, 9, 9)');
+ expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
+ expect(arc._model.borderWidth).toBe(1.56);
+
+ // Dataset styles can be an array
+ chart.data.datasets[0].hoverBackgroundColor = ['rgb(255, 255, 255)', 'rgb(9, 9, 9)'];
+ chart.data.datasets[0].hoverBorderColor = ['rgb(18, 18, 18)'];
+ chart.data.datasets[0].hoverBorderWidth = [0.1, 1.56];
+
+ controller.setHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(255, 255, 255)');
+ expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
+ expect(arc._model.borderWidth).toBe(0.1);
+
+ // Element custom styles also work
+ arc.custom = {
+ hoverBackgroundColor: 'rgb(7, 7, 7)',
+ hoverBorderColor: 'rgb(17, 17, 17)',
+ hoverBorderWidth: 3.14159,
+ };
+
+ controller.setHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(7, 7, 7)');
+ expect(arc._model.borderColor).toBe('rgb(17, 17, 17)');
+ expect(arc._model.borderWidth).toBe(3.14159);
+ });
+
+ it ('should unset the hover style of an arc', function() {
+ var chart = {
+ chart: {
+ width: 100,
+ height: 200,
+ },
+ data: {
+ datasets: [{
+ data: [10, 15, 0, 4]
+ }],
+ labels: ['label0', 'label1', 'label2', 'label3']
+ },
+ config: {
+ type: 'doughnut'
+ },
+ options: {
+ animation: {
+ animateRotate: false,
+ animateScale: false
+ },
+ cutoutPercentage: 50,
+ elements: {
+ arc: {
+ backgroundColor: 'rgb(255, 0, 0)',
+ borderColor: 'rgb(0, 0, 255)',
+ borderWidth: 2,
+ }
+ }
+ }
+ };
+
+ var controller = new Chart.controllers.doughnut(chart, 0);
+ controller.reset(); // reset first
+ controller.update();
+
+ var arc = chart.data.datasets[0].metaData[0];
+
+ controller.removeHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(255, 0, 0)');
+ expect(arc._model.borderColor).toBe('rgb(0, 0, 255)');
+ expect(arc._model.borderWidth).toBe(2);
+
+ // Set a dataset style to take precedence
+ chart.data.datasets[0].backgroundColor = 'rgb(9, 9, 9)';
+ chart.data.datasets[0].borderColor = 'rgb(18, 18, 18)';
+ chart.data.datasets[0].borderWidth = 1.56;
+
+ controller.removeHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(9, 9, 9)');
+ expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
+ expect(arc._model.borderWidth).toBe(1.56);
+
+ // Dataset styles can be an array
+ chart.data.datasets[0].backgroundColor = ['rgb(255, 255, 255)', 'rgb(9, 9, 9)'];
+ chart.data.datasets[0].borderColor = ['rgb(18, 18, 18)'];
+ chart.data.datasets[0].borderWidth = [0.1, 1.56];
+
+ controller.removeHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(255, 255, 255)');
+ expect(arc._model.borderColor).toBe('rgb(18, 18, 18)');
+ expect(arc._model.borderWidth).toBe(0.1);
+
+ // Element custom styles also work
+ arc.custom = {
+ backgroundColor: 'rgb(7, 7, 7)',
+ borderColor: 'rgb(17, 17, 17)',
+ borderWidth: 3.14159,
+ };
+
+ controller.removeHoverStyle(arc);
+
+ expect(arc._model.backgroundColor).toBe('rgb(7, 7, 7)');
+ expect(arc._model.borderColor).toBe('rgb(17, 17, 17)');
+ expect(arc._model.borderWidth).toBe(3.14159);
+ });
+});
\ No newline at end of file