diff --git a/src/traces/box/set_positions.js b/src/traces/box/set_positions.js index 038e6e5533e..d1bd9dfed3b 100644 --- a/src/traces/box/set_positions.js +++ b/src/traces/box/set_positions.js @@ -74,8 +74,9 @@ module.exports = function setPositions(gd, plotinfo) { Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true); // set the width of all boxes - for(i = 0; i < boxlist.length; ++i) { - gd.calcdata[i][0].t.dPos = dPos; + for(i = 0; i < boxlist.length; i++) { + var boxListIndex = boxlist[i]; + gd.calcdata[boxListIndex][0].t.dPos = dPos; } // autoscale the x axis - including space for points if they're off the side diff --git a/src/traces/ohlc/transform.js b/src/traces/ohlc/transform.js index fe86367558e..bb39320d76d 100644 --- a/src/traces/ohlc/transform.js +++ b/src/traces/ohlc/transform.js @@ -121,7 +121,7 @@ exports.calcTransform = function calcTransform(gd, trace, opts) { var xa = axisIds.getFromTrace(gd, trace, 'x'), ya = axisIds.getFromTrace(gd, trace, 'y'), - tickWidth = convertTickWidth(trace.x, xa, trace._fullInput.tickwidth); + tickWidth = convertTickWidth(gd, xa, trace); var open = trace.open, high = trace.high, @@ -191,16 +191,50 @@ exports.calcTransform = function calcTransform(gd, trace, opts) { trace.text = textOut; }; -function convertTickWidth(coords, ax, tickWidth) { - if(coords.length < 2) return tickWidth; +function convertTickWidth(gd, xa, trace) { + var fullInput = trace._fullInput, + tickWidth = fullInput.tickwidth, + minDiff = fullInput._minDiff; - var _coords = coords.map(ax.d2c), - minDTick = Math.abs(_coords[1] - _coords[0]); + if(!minDiff) { + var fullData = gd._fullData, + ohlcTracesOnThisXaxis = []; - for(var i = 1; i < _coords.length - 1; i++) { - var dist = Math.abs(_coords[i + 1] - _coords[i]); - minDTick = Math.min(dist, minDTick); + minDiff = Infinity; + + // find min x-coordinates difference of all traces + // attached to this x-axis and stash the result + + var i; + + for(i = 0; i < fullData.length; i++) { + var _trace = fullData[i]._fullInput; + + if(_trace.type === 'ohlc' && + _trace.visible === true && + _trace.xaxis === xa._id + ) { + ohlcTracesOnThisXaxis.push(_trace); + + // - _trace.x may be undefined here, + // it is filled later in calcTransform + // + // - handle trace of length 1 separately. + + if(_trace.x && _trace.x.length > 1) { + var _minDiff = Lib.distinctVals(_trace.x.map(xa.d2c)).minDiff; + minDiff = Math.min(minDiff, _minDiff); + } + } + } + + // if minDiff is still Infinity here, set it to 1 + if(minDiff === Infinity) minDiff = 1; + + for(i = 0; i < ohlcTracesOnThisXaxis.length; i++) { + ohlcTracesOnThisXaxis[i]._minDiff = minDiff; + } } - return minDTick * tickWidth; + return minDiff * tickWidth; } diff --git a/test/jasmine/tests/finance_test.js b/test/jasmine/tests/finance_test.js index 3cf908235f9..b09e31dc87d 100644 --- a/test/jasmine/tests/finance_test.js +++ b/test/jasmine/tests/finance_test.js @@ -512,7 +512,7 @@ describe('finance charts calc transforms:', function() { expect(out[2].name).toEqual('trace 0 - increasing'); expect(out[2].x.map(ms2DateTime)).toEqual([ - '2016-09-03 23:59:59.999', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', null + '2016-09-03 22:48', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04 01:12', null ]); expect(out[2].y).toEqual([ 32.06, 32.06, 34.25, 31.62, 33.18, 33.18, null @@ -564,6 +564,78 @@ describe('finance charts calc transforms:', function() { 32.87, 33.5, 33.37, 33.37, 33.37, 33.62 ]); }); + + it('should use the smallest trace minimum x difference to convert *tickwidth* to data coords for all traces attached to a given x-axis', function() { + var trace0 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + tickwidth: 0.5 + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + tickwidth: 0.5 + }); + + // shift time coordinates by 10 hours + trace1.x = trace1.x.map(function(d) { + return d + ' 10:00'; + }); + + var out = _calc([trace0, trace1]); + + expect(out[0].x.map(ms2DateTime)).toEqual([ + '2016-08-31 12', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01', '2016-09-01 12', null, + '2016-09-03 12', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04', '2016-09-04 12', null, + '2016-09-05 12', '2016-09-06', '2016-09-06', '2016-09-06', '2016-09-06', '2016-09-06 12', null, + '2016-09-09 12', '2016-09-10', '2016-09-10', '2016-09-10', '2016-09-10', '2016-09-10 12', null + ]); + + expect(out[1].x.map(ms2DateTime)).toEqual([ + '2016-09-01 12', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02', '2016-09-02 12', null, + '2016-09-02 12', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03', '2016-09-03 12', null, + '2016-09-04 12', '2016-09-05', '2016-09-05', '2016-09-05', '2016-09-05', '2016-09-05 12', null, + '2016-09-06 12', '2016-09-07', '2016-09-07', '2016-09-07', '2016-09-07', '2016-09-07 12', null + ]); + + expect(out[2].x.map(ms2DateTime)).toEqual([ + '2016-08-31 22', '2016-09-01 10', '2016-09-01 10', '2016-09-01 10', '2016-09-01 10', '2016-09-01 22', null, + '2016-09-03 22', '2016-09-04 10', '2016-09-04 10', '2016-09-04 10', '2016-09-04 10', '2016-09-04 22', null, + '2016-09-05 22', '2016-09-06 10', '2016-09-06 10', '2016-09-06 10', '2016-09-06 10', '2016-09-06 22', null, + '2016-09-09 22', '2016-09-10 10', '2016-09-10 10', '2016-09-10 10', '2016-09-10 10', '2016-09-10 22', null + ]); + + expect(out[3].x.map(ms2DateTime)).toEqual([ + '2016-09-01 22', '2016-09-02 10', '2016-09-02 10', '2016-09-02 10', '2016-09-02 10', '2016-09-02 22', null, + '2016-09-02 22', '2016-09-03 10', '2016-09-03 10', '2016-09-03 10', '2016-09-03 10', '2016-09-03 22', null, + '2016-09-04 22', '2016-09-05 10', '2016-09-05 10', '2016-09-05 10', '2016-09-05 10', '2016-09-05 22', null, + '2016-09-06 22', '2016-09-07 10', '2016-09-07 10', '2016-09-07 10', '2016-09-07 10', '2016-09-07 22', null + ]); + }); + + it('should fallback to a minimum x difference of 0.5 in one-item traces', function() { + var trace0 = Lib.extendDeep({}, mock1, { + type: 'ohlc', + tickwidth: 0.5 + }); + trace0.x = [ '2016-01-01' ]; + + var trace1 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + tickwidth: 0.5 + }); + trace1.x = [ 10 ]; + + var out = _calc([trace0, trace1]); + + var x0 = out[0].x; + expect(x0[x0.length - 2] - x0[0]).toEqual(1); + + var x2 = out[2].x; + expect(x2[x2.length - 2] - x2[0]).toEqual(1); + + expect(out[1].x).toEqual([]); + expect(out[3].x).toEqual([]); + }); }); describe('finance charts updates:', function() { @@ -780,4 +852,53 @@ describe('finance charts updates:', function() { done(); }); }); + + it('Plotly.addTraces + Plotly.relayout should update candlestick box position values', function(done) { + + function assertBoxPosFields(dPos) { + expect(gd.calcdata.length).toEqual(dPos.length); + + gd.calcdata.forEach(function(calcTrace, i) { + if(dPos[i] === undefined) { + expect(calcTrace[0].t.dPos).toBeUndefined(); + } + else { + expect(calcTrace[0].t.dPos).toEqual(dPos[i]); + } + }); + } + + var trace0 = { + type: 'candlestick', + x: ['2011-01-01'], + open: [0], + high: [3], + low: [1], + close: [3] + }; + + Plotly.plot(gd, [trace0]).then(function() { + assertBoxPosFields([0.5, undefined]); + + return Plotly.addTraces(gd, {}); + + }) + .then(function() { + var update = { + type: 'candlestick', + x: [['2011-02-02']], + open: [[0]], + high: [[3]], + low: [[1]], + close: [[3]] + }; + + return Plotly.restyle(gd, update); + }) + .then(function() { + assertBoxPosFields([0.5, undefined, 0.5, undefined]); + + done(); + }); + }); });