diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 9a997837a8c..d181c0d2ad7 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -628,7 +628,7 @@ fx.getClosest = function(cd, distfn, pointData) { }; function cleanPoint(d, hovermode) { - d.posref = hovermode==='y' ? (d.x0+d.x1)/2 : (d.y0+d.y1)/2; + d.posref = hovermode === 'y' ? (d.x0 + d.x1) / 2 : (d.y0 + d.y1) / 2; // then constrain all the positions to be on the plot d.x0 = Lib.constrain(d.x0, 0, d.xa._length); @@ -639,36 +639,36 @@ function cleanPoint(d, hovermode) { // and convert the x and y label values into objects // formatted as text, with font info var logOffScale; - if(d.xLabelVal!==undefined) { - logOffScale = (d.xa.type==='log' && d.xLabelVal<=0); + if(d.xLabelVal !== undefined) { + logOffScale = (d.xa.type === 'log' && d.xLabelVal <= 0); var xLabelObj = Axes.tickText(d.xa, d.xa.c2l(logOffScale ? -d.xLabelVal : d.xLabelVal), 'hover'); if(logOffScale) { - if(d.xLabelVal===0) d.xLabel = '0'; + if(d.xLabelVal === 0) d.xLabel = '0'; else d.xLabel = '-' + xLabelObj.text; } else d.xLabel = xLabelObj.text; d.xVal = d.xa.c2d(d.xLabelVal); } - if(d.yLabelVal!==undefined) { - logOffScale = (d.ya.type==='log' && d.yLabelVal<=0); + if(d.yLabelVal !== undefined) { + logOffScale = (d.ya.type === 'log' && d.yLabelVal <= 0); var yLabelObj = Axes.tickText(d.ya, d.ya.c2l(logOffScale ? -d.yLabelVal : d.yLabelVal), 'hover'); if(logOffScale) { - if(d.yLabelVal===0) d.yLabel = '0'; + if(d.yLabelVal === 0) d.yLabel = '0'; else d.yLabel = '-' + yLabelObj.text; } else d.yLabel = yLabelObj.text; d.yVal = d.ya.c2d(d.yLabelVal); } - if(d.zLabelVal!==undefined) d.zLabel = String(d.zLabelVal); + if(d.zLabelVal !== undefined) d.zLabel = String(d.zLabelVal); // for box means and error bars, add the range to the label - if(d.xerr!==undefined) { + if(!isNaN(d.xerr) && !(d.xa.type === 'log' && d.xerr <= 0)) { var xeText = Axes.tickText(d.xa, d.xa.c2l(d.xerr), 'hover').text; - if(d.xerrneg!==undefined) { + if(d.xerrneg !== undefined) { d.xLabel += ' +' + xeText + ' / -' + Axes.tickText(d.xa, d.xa.c2l(d.xerrneg), 'hover').text; } @@ -677,27 +677,27 @@ function cleanPoint(d, hovermode) { // small distance penalty for error bars, so that if there are // traces with errors and some without, the error bar label will // hoist up to the point - if(hovermode==='x') d.distance += 1; + if(hovermode === 'x') d.distance += 1; } - if(d.yerr!==undefined) { + if(!isNaN(d.yerr) && !(d.ya.type === 'log' && d.yerr <= 0)) { var yeText = Axes.tickText(d.ya, d.ya.c2l(d.yerr), 'hover').text; - if(d.yerrneg!==undefined) { + if(d.yerrneg !== undefined) { d.yLabel += ' +' + yeText + ' / -' + Axes.tickText(d.ya, d.ya.c2l(d.yerrneg), 'hover').text; } else d.yLabel += ' ± ' + yeText; - if(hovermode==='y') d.distance += 1; + if(hovermode === 'y') d.distance += 1; } var infomode = d.trace.hoverinfo; - if(infomode!=='all') { + if(infomode !== 'all') { infomode = infomode.split('+'); - if(infomode.indexOf('x')===-1) d.xLabel = undefined; - if(infomode.indexOf('y')===-1) d.yLabel = undefined; - if(infomode.indexOf('z')===-1) d.zLabel = undefined; - if(infomode.indexOf('text')===-1) d.text = undefined; - if(infomode.indexOf('name')===-1) d.name = undefined; + if(infomode.indexOf('x') === -1) d.xLabel = undefined; + if(infomode.indexOf('y') === -1) d.yLabel = undefined; + if(infomode.indexOf('z') === -1) d.zLabel = undefined; + if(infomode.indexOf('text') === -1) d.text = undefined; + if(infomode.indexOf('name') === -1) d.name = undefined; } return d; } diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index d9b42db1d33..616fe1e72e6 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -214,6 +214,60 @@ describe('hover info', function() { }); }); + describe('hover error x text (log axis positive)', function() { + var mockCopy = Lib.extendDeep({}, mock); + + mockCopy.data[0].error_x = { array: [] }; + mockCopy.data[0].error_x.array[17] = 1; + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); + }); + + it('responds to hover x+text', function() { + Fx.hover('graph', evt, 'xy'); + + expect(d3.selectAll('g.axistext').size()).toEqual(1); + expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388 ± 1'); + }); + }); + + describe('hover error text (log axis 0)', function() { + var mockCopy = Lib.extendDeep({}, mock); + + mockCopy.data[0].error_x = { array: [] }; + mockCopy.data[0].error_x.array[17] = 0; + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); + }); + + it('responds to hover x+text', function() { + Fx.hover('graph', evt, 'xy'); + + expect(d3.selectAll('g.axistext').size()).toEqual(1); + expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + }); + }); + + describe('hover error text (log axis negative)', function() { + var mockCopy = Lib.extendDeep({}, mock); + + mockCopy.data[0].error_x = { array: [] }; + mockCopy.data[0].error_x.array[17] = -1; + + beforeEach(function(done) { + Plotly.plot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); + }); + + it('responds to hover x+text', function() { + Fx.hover('graph', evt, 'xy'); + + expect(d3.selectAll('g.axistext').size()).toEqual(1); + expect(d3.selectAll('g.axistext').select('text').html()).toEqual('0.388'); + }); + }); + describe('hover info text with html', function() { var mockCopy = Lib.extendDeep({}, mock);