mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Internal data by axis instead of scale id (#6912)
* Internal data by axis instead of scale id * Test * Update test * Update docs
This commit is contained in:
parent
224fc11500
commit
b5d5ed987a
@ -25,7 +25,7 @@ data: [{x:'2016-12-25', y:20}, {x:'2016-12-26', y:10}]
|
||||
data: [{x:'Sales', y:20}, {x:'Revenue', y:10}]
|
||||
```
|
||||
|
||||
This is also the internal format used for parsed data. Property names are matched to scale-id. In this mode, parsing can be disabled by specifying `parsing: false` at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally.
|
||||
This is also the internal format used for parsed data. In this mode, parsing can be disabled by specifying `parsing: false` at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally.
|
||||
|
||||
## Object
|
||||
|
||||
|
||||
@ -144,7 +144,7 @@ function parseFloatBar(arr, item, vScale, i) {
|
||||
|
||||
// Store `barEnd` (furthest away from origin) as parsed value,
|
||||
// to make stacking straight forward
|
||||
item[vScale.id] = barEnd;
|
||||
item[vScale.axis] = barEnd;
|
||||
|
||||
item._custom = {
|
||||
barStart: barStart,
|
||||
@ -167,12 +167,12 @@ function parseArrayOrPrimitive(meta, data, start, count) {
|
||||
for (i = start, ilen = start + count; i < ilen; ++i) {
|
||||
entry = data[i];
|
||||
item = {};
|
||||
item[iScale.id] = singleScale || iScale._parse(labels[i], i);
|
||||
item[iScale.axis] = singleScale || iScale._parse(labels[i], i);
|
||||
|
||||
if (helpers.isArray(entry)) {
|
||||
parseFloatBar(entry, item, vScale, i);
|
||||
} else {
|
||||
item[vScale.id] = vScale._parse(entry, i);
|
||||
item[vScale.axis] = vScale._parse(entry, i);
|
||||
}
|
||||
|
||||
parsed.push(item);
|
||||
@ -230,12 +230,12 @@ module.exports = DatasetController.extend({
|
||||
for (i = start, ilen = start + count; i < ilen; ++i) {
|
||||
obj = data[i];
|
||||
item = {};
|
||||
item[iScale.id] = iScale._parseObject(obj, iScale.axis, i);
|
||||
item[iScale.axis] = iScale._parseObject(obj, iScale.axis, i);
|
||||
value = obj[vProp];
|
||||
if (helpers.isArray(value)) {
|
||||
parseFloatBar(value, item, vScale, i);
|
||||
} else {
|
||||
item[vScale.id] = vScale._parseObject(obj, vProp, i);
|
||||
item[vScale.axis] = vScale._parseObject(obj, vProp, i);
|
||||
}
|
||||
parsed.push(item);
|
||||
}
|
||||
@ -253,10 +253,10 @@ module.exports = DatasetController.extend({
|
||||
const custom = parsed._custom;
|
||||
const value = custom
|
||||
? '[' + custom.start + ', ' + custom.end + ']'
|
||||
: '' + vScale.getLabelForValue(parsed[vScale.id]);
|
||||
: '' + vScale.getLabelForValue(parsed[vScale.axis]);
|
||||
|
||||
return {
|
||||
label: '' + iScale.getLabelForValue(parsed[iScale.id]),
|
||||
label: '' + iScale.getLabelForValue(parsed[iScale.axis]),
|
||||
value: value
|
||||
};
|
||||
},
|
||||
@ -394,7 +394,7 @@ module.exports = DatasetController.extend({
|
||||
let i, ilen;
|
||||
|
||||
for (i = 0, ilen = meta.data.length; i < ilen; ++i) {
|
||||
pixels.push(iScale.getPixelForValue(me._getParsed(i)[iScale.id]));
|
||||
pixels.push(iScale.getPixelForValue(me._getParsed(i)[iScale.axis]));
|
||||
}
|
||||
|
||||
return {
|
||||
@ -417,9 +417,9 @@ module.exports = DatasetController.extend({
|
||||
const minBarLength = options.minBarLength;
|
||||
const parsed = me._getParsed(index);
|
||||
const custom = parsed._custom;
|
||||
let value = parsed[vScale.id];
|
||||
let value = parsed[vScale.axis];
|
||||
let start = 0;
|
||||
let length = meta._stacked ? me._applyStack(vScale, parsed) : parsed[vScale.id];
|
||||
let length = meta._stacked ? me._applyStack(vScale, parsed) : parsed[vScale.axis];
|
||||
let base, head, size;
|
||||
|
||||
if (length !== value) {
|
||||
@ -489,7 +489,7 @@ module.exports = DatasetController.extend({
|
||||
helpers.canvas.clipArea(chart.ctx, chart.chartArea);
|
||||
|
||||
for (; i < ilen; ++i) {
|
||||
if (!isNaN(me._getParsed(i)[vScale.id])) {
|
||||
if (!isNaN(me._getParsed(i)[vScale.axis])) {
|
||||
rects[i].draw(me._ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,15 +59,13 @@ module.exports = DatasetController.extend({
|
||||
*/
|
||||
_parseObjectData: function(meta, data, start, count) {
|
||||
const {xScale, yScale} = meta;
|
||||
const xId = xScale.id;
|
||||
const yId = yScale.id;
|
||||
const parsed = [];
|
||||
let i, ilen, item;
|
||||
for (i = start, ilen = start + count; i < ilen; ++i) {
|
||||
item = data[i];
|
||||
parsed.push({
|
||||
[xId]: xScale._parseObject(item, 'x', i),
|
||||
[yId]: yScale._parseObject(item, 'y', i),
|
||||
x: xScale._parseObject(item, 'x', i),
|
||||
y: yScale._parseObject(item, 'y', i),
|
||||
_custom: item && item.r && +item.r
|
||||
});
|
||||
}
|
||||
@ -96,8 +94,8 @@ module.exports = DatasetController.extend({
|
||||
const meta = me._cachedMeta;
|
||||
const {xScale, yScale} = meta;
|
||||
const parsed = me._getParsed(index);
|
||||
const x = xScale.getLabelForValue(parsed[xScale.id]);
|
||||
const y = yScale.getLabelForValue(parsed[yScale.id]);
|
||||
const x = xScale.getLabelForValue(parsed.x);
|
||||
const y = yScale.getLabelForValue(parsed.y);
|
||||
const r = parsed._custom;
|
||||
|
||||
return {
|
||||
@ -133,8 +131,8 @@ module.exports = DatasetController.extend({
|
||||
const point = points[i];
|
||||
const index = start + i;
|
||||
const parsed = !reset && me._getParsed(index);
|
||||
const x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(parsed[xScale.id]);
|
||||
const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(parsed[yScale.id]);
|
||||
const x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(parsed.x);
|
||||
const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(parsed.y);
|
||||
const properties = {
|
||||
x,
|
||||
y,
|
||||
|
||||
@ -104,8 +104,8 @@ module.exports = DatasetController.extend({
|
||||
const index = start + i;
|
||||
const point = points[i];
|
||||
const parsed = me._getParsed(index);
|
||||
const x = xScale.getPixelForValue(parsed[xScale.id]);
|
||||
const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(_stacked ? me._applyStack(yScale, parsed) : parsed[yScale.id]);
|
||||
const x = xScale.getPixelForValue(parsed.x);
|
||||
const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(_stacked ? me._applyStack(yScale, parsed) : parsed.y);
|
||||
const properties = {
|
||||
x,
|
||||
y,
|
||||
|
||||
@ -81,7 +81,7 @@ module.exports = DatasetController.extend({
|
||||
|
||||
return {
|
||||
label: vScale._getLabels()[index],
|
||||
value: '' + vScale.getLabelForValue(parsed[vScale.id])
|
||||
value: '' + vScale.getLabelForValue(parsed[vScale.axis])
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@ -193,17 +193,17 @@ function updateStacks(controller, parsed) {
|
||||
const {chart, _cachedMeta: meta} = controller;
|
||||
const stacks = chart._stacks || (chart._stacks = {}); // map structure is {stackKey: {datasetIndex: value}}
|
||||
const {iScale, vScale, index: datasetIndex} = meta;
|
||||
const iId = iScale.id;
|
||||
const vId = vScale.id;
|
||||
const iAxis = iScale.axis;
|
||||
const vAxis = vScale.axis;
|
||||
const key = getStackKey(iScale, vScale, meta);
|
||||
const ilen = parsed.length;
|
||||
let stack;
|
||||
|
||||
for (let i = 0; i < ilen; ++i) {
|
||||
const item = parsed[i];
|
||||
const {[iId]: index, [vId]: value} = item;
|
||||
const {[iAxis]: index, [vAxis]: value} = item;
|
||||
const itemStacks = item._stacks || (item._stacks = {});
|
||||
stack = itemStacks[vId] = getOrCreateStack(stacks, key, index);
|
||||
stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);
|
||||
stack[datasetIndex] = value;
|
||||
}
|
||||
}
|
||||
@ -478,7 +478,7 @@ helpers.extend(DatasetController.prototype, {
|
||||
const me = this;
|
||||
const {_cachedMeta: meta, _data: data} = me;
|
||||
const {iScale, vScale, _stacked} = meta;
|
||||
const iScaleId = iScale.id;
|
||||
const iAxis = iScale.axis;
|
||||
let sorted = true;
|
||||
let i, parsed, cur, prev;
|
||||
|
||||
@ -503,7 +503,7 @@ helpers.extend(DatasetController.prototype, {
|
||||
for (i = 0; i < count; ++i) {
|
||||
meta._parsed[i + start] = cur = parsed[i];
|
||||
if (sorted) {
|
||||
if (prev && cur[iScaleId] < prev[iScaleId]) {
|
||||
if (prev && cur[iAxis] < prev[iAxis]) {
|
||||
sorted = false;
|
||||
}
|
||||
prev = cur;
|
||||
@ -533,8 +533,8 @@ helpers.extend(DatasetController.prototype, {
|
||||
*/
|
||||
_parsePrimitiveData: function(meta, data, start, count) {
|
||||
const {iScale, vScale} = meta;
|
||||
const iId = iScale.id;
|
||||
const vId = vScale.id;
|
||||
const iAxis = iScale.axis;
|
||||
const vAxis = vScale.axis;
|
||||
const labels = iScale._getLabels();
|
||||
const singleScale = iScale === vScale;
|
||||
const parsed = new Array(count);
|
||||
@ -543,8 +543,8 @@ helpers.extend(DatasetController.prototype, {
|
||||
for (i = 0, ilen = count; i < ilen; ++i) {
|
||||
index = i + start;
|
||||
parsed[i] = {
|
||||
[iId]: singleScale || iScale._parse(labels[index], index),
|
||||
[vId]: vScale._parse(data[index], index)
|
||||
[iAxis]: singleScale || iScale._parse(labels[index], index),
|
||||
[vAxis]: vScale._parse(data[index], index)
|
||||
};
|
||||
}
|
||||
return parsed;
|
||||
@ -558,13 +558,11 @@ helpers.extend(DatasetController.prototype, {
|
||||
* @param {number} count - number of items to parse
|
||||
* @returns {object} parsed item - item containing index and a parsed value
|
||||
* for each scale id.
|
||||
* Example: {xScale0: 0, yScale0: 1}
|
||||
* Example: {x: 0, y: 1}
|
||||
* @private
|
||||
*/
|
||||
_parseArrayData: function(meta, data, start, count) {
|
||||
const {xScale, yScale} = meta;
|
||||
const xId = xScale.id;
|
||||
const yId = yScale.id;
|
||||
const parsed = new Array(count);
|
||||
let i, ilen, index, item;
|
||||
|
||||
@ -572,8 +570,8 @@ helpers.extend(DatasetController.prototype, {
|
||||
index = i + start;
|
||||
item = data[index];
|
||||
parsed[i] = {
|
||||
[xId]: xScale._parse(item[0], index),
|
||||
[yId]: yScale._parse(item[1], index)
|
||||
x: xScale._parse(item[0], index),
|
||||
y: yScale._parse(item[1], index)
|
||||
};
|
||||
}
|
||||
return parsed;
|
||||
@ -592,8 +590,6 @@ helpers.extend(DatasetController.prototype, {
|
||||
*/
|
||||
_parseObjectData: function(meta, data, start, count) {
|
||||
const {xScale, yScale} = meta;
|
||||
const xId = xScale.id;
|
||||
const yId = yScale.id;
|
||||
const parsed = new Array(count);
|
||||
let i, ilen, index, item;
|
||||
|
||||
@ -601,8 +597,8 @@ helpers.extend(DatasetController.prototype, {
|
||||
index = i + start;
|
||||
item = data[index];
|
||||
parsed[i] = {
|
||||
[xId]: xScale._parseObject(item, 'x', index),
|
||||
[yId]: yScale._parseObject(item, 'y', index)
|
||||
x: xScale._parseObject(item, 'x', index),
|
||||
y: yScale._parseObject(item, 'y', index)
|
||||
};
|
||||
}
|
||||
return parsed;
|
||||
@ -612,11 +608,7 @@ helpers.extend(DatasetController.prototype, {
|
||||
* @private
|
||||
*/
|
||||
_getParsed: function(index) {
|
||||
const data = this._cachedMeta._parsed;
|
||||
if (index < 0 || index >= data.length) {
|
||||
return;
|
||||
}
|
||||
return data[index];
|
||||
return this._cachedMeta._parsed[index];
|
||||
},
|
||||
|
||||
/**
|
||||
@ -625,10 +617,10 @@ helpers.extend(DatasetController.prototype, {
|
||||
_applyStack: function(scale, parsed) {
|
||||
const chart = this.chart;
|
||||
const meta = this._cachedMeta;
|
||||
const value = parsed[scale.id];
|
||||
const value = parsed[scale.axis];
|
||||
const stack = {
|
||||
keys: getSortedDatasetIndices(chart, true),
|
||||
values: parsed._stacks[scale.id]
|
||||
values: parsed._stacks[scale.axis]
|
||||
};
|
||||
return applyStack(stack, value, meta.index);
|
||||
},
|
||||
@ -651,7 +643,7 @@ helpers.extend(DatasetController.prototype, {
|
||||
|
||||
function _compute() {
|
||||
if (stack) {
|
||||
stack.values = parsed._stacks[scale.id];
|
||||
stack.values = parsed._stacks[scale.axis];
|
||||
// Need to consider individual stack values for data range,
|
||||
// in addition to the stacked value
|
||||
min = Math.min(min, value);
|
||||
@ -668,8 +660,8 @@ helpers.extend(DatasetController.prototype, {
|
||||
function _skip() {
|
||||
item = data[i];
|
||||
parsed = _parsed[i];
|
||||
value = parsed[scale.id];
|
||||
otherValue = parsed[otherScale.id];
|
||||
value = parsed[scale.axis];
|
||||
otherValue = parsed[otherScale.axis];
|
||||
return ((item && item.hidden) || isNaN(value) || otherMin > otherValue || otherMax < otherValue);
|
||||
}
|
||||
|
||||
@ -703,7 +695,7 @@ helpers.extend(DatasetController.prototype, {
|
||||
let i, ilen, value;
|
||||
|
||||
for (i = 0, ilen = parsed.length; i < ilen; ++i) {
|
||||
value = parsed[i][scale.id];
|
||||
value = parsed[i][scale.axis];
|
||||
if (!isNaN(value)) {
|
||||
values.push(value);
|
||||
}
|
||||
@ -759,8 +751,8 @@ helpers.extend(DatasetController.prototype, {
|
||||
const vScale = meta.vScale;
|
||||
const parsed = me._getParsed(index);
|
||||
return {
|
||||
label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.id]) : '',
|
||||
value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.id]) : ''
|
||||
label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',
|
||||
value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@ -118,6 +118,55 @@ describe('Chart.DatasetController', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse data using correct scales', function() {
|
||||
const data1 = [0, 1, 2, 3, 4, 5];
|
||||
const data2 = ['a', 'b', 'c', 'd', 'a'];
|
||||
const chart = acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [
|
||||
{data: data1},
|
||||
{data: data2, xAxisID: 'x2', yAxisID: 'y2'}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
x: {
|
||||
type: 'category',
|
||||
labels: ['one', 'two', 'three', 'four', 'five', 'six']
|
||||
},
|
||||
x2: {
|
||||
type: 'logarithmic',
|
||||
labels: ['1', '10', '100', '1000', '2000']
|
||||
},
|
||||
y: {
|
||||
type: 'linear'
|
||||
},
|
||||
y2: {
|
||||
type: 'category',
|
||||
labels: ['a', 'b', 'c', 'd', 'e']
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const meta1 = chart.getDatasetMeta(0);
|
||||
const parsedXValues1 = meta1._parsed.map(p => p.x);
|
||||
const parsedYValues1 = meta1._parsed.map(p => p.y);
|
||||
|
||||
expect(meta1.data.length).toBe(6);
|
||||
expect(parsedXValues1).toEqual([0, 1, 2, 3, 4, 5]); // label indices
|
||||
expect(parsedYValues1).toEqual(data1);
|
||||
|
||||
const meta2 = chart.getDatasetMeta(1);
|
||||
const parsedXValues2 = meta2._parsed.map(p => p.x);
|
||||
const parsedYValues2 = meta2._parsed.map(p => p.y);
|
||||
|
||||
expect(meta2.data.length).toBe(5);
|
||||
expect(parsedXValues2).toEqual([1, 10, 100, 1000, 2000]); // logarithmic scale labels
|
||||
expect(parsedYValues2).toEqual([0, 1, 2, 3, 0]); // label indices
|
||||
});
|
||||
|
||||
it('should synchronize metadata when data are inserted or removed and parsing is on', function() {
|
||||
const data = [0, 1, 2, 3, 4, 5];
|
||||
const chart = acquireChart({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user