Skip to content

Commit cd79f0d

Browse files
authored
Merge pull request #2360 from plotly/react-errorbars
fix errorbars for Plotly.react and for uneven data arrays
2 parents dc73a48 + 3b7faf7 commit cd79f0d

File tree

6 files changed

+77
-15
lines changed

6 files changed

+77
-15
lines changed

src/components/errorbars/compute_error.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,26 @@ module.exports = function makeComputeError(opts) {
3030
symmetric = opts.symmetric;
3131

3232
if(type === 'data') {
33-
var array = opts.array,
34-
arrayminus = opts.arrayminus;
33+
var array = opts.array || [];
3534

36-
if(symmetric || arrayminus === undefined) {
35+
if(symmetric) {
3736
return function computeError(dataPt, index) {
3837
var val = +(array[index]);
3938
return [val, val];
4039
};
4140
}
4241
else {
42+
var arrayminus = opts.arrayminus || [];
4343
return function computeError(dataPt, index) {
44-
return [+arrayminus[index], +array[index]];
44+
var val = +array[index];
45+
var valMinus = +arrayminus[index];
46+
// in case one is present and the other is missing, fill in 0
47+
// so we still see the present one. Mostly useful during manual
48+
// data entry.
49+
if(!isNaN(val) || !isNaN(valMinus)) {
50+
return [valMinus || 0, val || 0];
51+
}
52+
return [NaN, NaN];
4553
};
4654
}
4755
}

src/components/errorbars/defaults.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,10 @@ module.exports = function(traceIn, traceOut, defaultColor, opts) {
4444
}
4545

4646
if(type === 'data') {
47-
var array = coerce('array');
48-
if(!array) containerOut.array = [];
47+
coerce('array');
4948
coerce('traceref');
5049
if(!symmetric) {
51-
var arrayminus = coerce('arrayminus');
52-
if(!arrayminus) containerOut.arrayminus = [];
50+
coerce('arrayminus');
5351
coerce('tracerefminus');
5452
}
5553
}

src/components/errorbars/plot.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ module.exports = function plot(traces, plotinfo, transitionOpts) {
7676

7777
var path;
7878

79+
var yerror = errorbar.select('path.yerror');
7980
if(yObj.visible && isNumeric(coords.x) &&
8081
isNumeric(coords.yh) &&
8182
isNumeric(coords.ys)) {
@@ -88,8 +89,6 @@ module.exports = function plot(traces, plotinfo, transitionOpts) {
8889

8990
if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe
9091

91-
var yerror = errorbar.select('path.yerror');
92-
9392
isNew = !yerror.size();
9493

9594
if(isNew) {
@@ -105,7 +104,9 @@ module.exports = function plot(traces, plotinfo, transitionOpts) {
105104

106105
yerror.attr('d', path);
107106
}
107+
else yerror.remove();
108108

109+
var xerror = errorbar.select('path.xerror');
109110
if(xObj.visible && isNumeric(coords.y) &&
110111
isNumeric(coords.xh) &&
111112
isNumeric(coords.xs)) {
@@ -117,8 +118,6 @@ module.exports = function plot(traces, plotinfo, transitionOpts) {
117118

118119
if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe
119120

120-
var xerror = errorbar.select('path.xerror');
121-
122121
isNew = !xerror.size();
123122

124123
if(isNew) {
@@ -134,6 +133,7 @@ module.exports = function plot(traces, plotinfo, transitionOpts) {
134133

135134
xerror.attr('d', path);
136135
}
136+
else xerror.remove();
137137
});
138138
});
139139
};

test/image/mocks/basic_error_bar.json

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
],
2121
"visible": true
2222
},
23+
"error_x": {
24+
"type": "data",
25+
"symmetric": false,
26+
"visible": true
27+
},
2328
"type": "scatter"
2429
}
2530
]

test/jasmine/tests/errorbars_test.js

+53-3
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,29 @@ describe('errorbar plotting', function() {
1515

1616
afterEach(destroyGraphDiv);
1717

18+
function countBars(xCount, yCount) {
19+
expect(d3.select(gd).selectAll('.xerror').size()).toBe(xCount);
20+
expect(d3.select(gd).selectAll('.yerror').size()).toBe(yCount);
21+
}
22+
23+
function checkCalcdata(cdTrace, errorBarData) {
24+
cdTrace.forEach(function(di, i) {
25+
var ebi = errorBarData[i] || {};
26+
expect(di.xh).toBe(ebi.xh);
27+
expect(di.xs).toBe(ebi.xs);
28+
expect(di.yh).toBe(ebi.yh);
29+
expect(di.ys).toBe(ebi.ys);
30+
});
31+
}
32+
1833
it('should autorange to the visible bars and remove invisible bars', function(done) {
19-
function check(xrange, yrange, xcount, ycount) {
34+
function check(xrange, yrange, xCount, yCount) {
2035
var xa = gd._fullLayout.xaxis;
2136
var ya = gd._fullLayout.yaxis;
2237
expect(xa.range).toBeCloseToArray(xrange, 3);
2338
expect(ya.range).toBeCloseToArray(yrange, 3);
2439

25-
expect(d3.selectAll('.xerror').size()).toBe(xcount);
26-
expect(d3.selectAll('.yerror').size()).toBe(ycount);
40+
countBars(xCount, yCount);
2741
}
2842
Plotly.newPlot(gd, [{
2943
y: [1, 2, 3],
@@ -50,4 +64,40 @@ describe('errorbar plotting', function() {
5064
.catch(fail)
5165
.then(done);
5266
});
67+
68+
it('shows half errorbars and removes individual bars that disappear', function(done) {
69+
Plotly.newPlot(gd, [{
70+
x: [0, 10, 20],
71+
y: [30, 40, 50],
72+
error_x: {type: 'data', array: [2, 3], visible: true, symmetric: false},
73+
error_y: {type: 'data', arrayminus: [4], visible: true, symmetric: false}
74+
}])
75+
.then(function() {
76+
countBars(2, 1);
77+
checkCalcdata(gd.calcdata[0], [
78+
{xs: 0, xh: 2, ys: 26, yh: 30},
79+
{xs: 10, xh: 13}
80+
]);
81+
82+
Plotly.restyle(gd, {'error_x.array': [[1]], 'error_y.arrayminus': [[5, 6]]});
83+
})
84+
.then(function() {
85+
countBars(1, 2);
86+
checkCalcdata(gd.calcdata[0], [
87+
{xs: 0, xh: 1, ys: 25, yh: 30},
88+
{ys: 34, yh: 40}
89+
]);
90+
91+
Plotly.restyle(gd, {'error_x.array': [[7, 8]], 'error_y.arrayminus': [[9]]});
92+
})
93+
.then(function() {
94+
countBars(2, 1);
95+
checkCalcdata(gd.calcdata[0], [
96+
{xs: 0, xh: 7, ys: 21, yh: 30},
97+
{xs: 10, xh: 18}
98+
]);
99+
})
100+
.catch(fail)
101+
.then(done);
102+
});
53103
});

test/jasmine/tests/plot_api_test.js

+1
Original file line numberDiff line numberDiff line change
@@ -2577,6 +2577,7 @@ describe('Test plot api', function() {
25772577
['axes_enumerated_ticks', require('@mocks/axes_enumerated_ticks.json')],
25782578
['axes_visible-false', require('@mocks/axes_visible-false.json')],
25792579
['bar_and_histogram', require('@mocks/bar_and_histogram.json')],
2580+
['basic_error_bar', require('@mocks/basic_error_bar.json')],
25802581
['binding', require('@mocks/binding.json')],
25812582
['cheater_smooth', require('@mocks/cheater_smooth.json')],
25822583
['finance_style', require('@mocks/finance_style.json')],

0 commit comments

Comments
 (0)