Skip to content

Commit 49899c6

Browse files
authored
Merge pull request #4637 from plotly/fix-spikedistance
fix for spikedistance=-1
2 parents 09a6d8c + 118b97a commit 49899c6

File tree

3 files changed

+160
-3
lines changed

3 files changed

+160
-3
lines changed

Diff for: src/components/fx/hover.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ function _hover(gd, evt, subplot, noHoverEvent) {
543543
var thisSpikeDistance;
544544
for(var i = 0; i < pointsData.length; i++) {
545545
thisSpikeDistance = pointsData[i].spikeDistance;
546-
if(thisSpikeDistance < minDistance && thisSpikeDistance <= spikedistance) {
546+
if(thisSpikeDistance <= minDistance && thisSpikeDistance <= spikedistance) {
547547
resultPoint = pointsData[i];
548548
minDistance = thisSpikeDistance;
549549
}

Diff for: src/traces/bar/hover.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ function hoverOnBars(pointData, xval, yval, hovermode) {
3939
var isClosest = (hovermode === 'closest');
4040
var isWaterfall = (trace.type === 'waterfall');
4141
var maxHoverDistance = pointData.maxHoverDistance;
42-
var maxSpikeDistance = pointData.maxSpikeDistance;
4342

4443
var posVal, sizeVal, posLetter, sizeLetter, dx, dy, pRangeCalc;
4544

@@ -161,7 +160,7 @@ function hoverOnBars(pointData, xval, yval, hovermode) {
161160
pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal']);
162161

163162
// spikelines always want "closest" distance regardless of hovermode
164-
pointData.spikeDistance = (sizeFn(di) + thisBarPositionFn(di)) / 2 + maxSpikeDistance - maxHoverDistance;
163+
pointData.spikeDistance = (sizeFn(di) + thisBarPositionFn(di)) / 2 - maxHoverDistance;
165164
// they also want to point to the data value, regardless of where the label goes
166165
// in case of bars shifted within groups
167166
pointData[posLetter + 'Spike'] = pa.c2p(di.p, true);

Diff for: test/jasmine/tests/hover_spikeline_test.js

+158
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,29 @@ describe('spikeline hover', function() {
404404
.then(done);
405405
});
406406

407+
it('correctly select the closest bar even when setting spikedistance to -1', function(done) {
408+
var mock = require('@mocks/bar_stack-with-gaps');
409+
var mockCopy = Lib.extendDeep({}, mock);
410+
mockCopy.layout.xaxis.showspikes = true;
411+
mockCopy.layout.yaxis.showspikes = true;
412+
mockCopy.layout.spikedistance = -1;
413+
414+
Plotly.newPlot(gd, mockCopy)
415+
.then(function() {
416+
_hover({xpx: 600, ypx: 400});
417+
var lines = d3.selectAll('line.spikeline');
418+
expect(lines.size()).toBe(4);
419+
expect(lines[0][1].getAttribute('stroke')).toBe('#2ca02c');
420+
421+
_hover({xpx: 600, ypx: 200});
422+
lines = d3.selectAll('line.spikeline');
423+
expect(lines.size()).toBe(4);
424+
expect(lines[0][1].getAttribute('stroke')).toBe('#1f77b4');
425+
})
426+
.catch(failTest)
427+
.then(done);
428+
});
429+
407430
it('correctly responds to setting the spikedistance to 0 by disabling ' +
408431
'the search for points to draw the spikelines', function(done) {
409432
var _mock = makeMock('toaxis', 'closest');
@@ -593,4 +616,139 @@ describe('spikeline hover', function() {
593616
.catch(failTest)
594617
.then(done);
595618
});
619+
620+
it('correctly draws lines up to the last point', function(done) {
621+
Plotly.newPlot(gd, [
622+
{type: 'bar', y: [5, 7, 9, 6, 4, 3]},
623+
{y: [5, 7, 9, 6, 4, 3]},
624+
{y: [5, 7, 9, 6, 4, 3], marker: {color: 'red'}}
625+
], {
626+
xaxis: {showspikes: true},
627+
yaxis: {showspikes: true},
628+
spikedistance: -1,
629+
width: 400, height: 400,
630+
showlegend: false
631+
})
632+
.then(function() {
633+
_hover({xpx: 150, ypx: 250});
634+
635+
var lines = d3.selectAll('line.spikeline');
636+
expect(lines.size()).toBe(4);
637+
expect(lines[0][1].getAttribute('stroke')).toBe('red');
638+
expect(lines[0][3].getAttribute('stroke')).toBe('red');
639+
})
640+
.catch(failTest)
641+
.then(done);
642+
});
643+
644+
describe('works across all cartesian traces', function() {
645+
var schema = Plotly.PlotSchema.get();
646+
var traces = Object.keys(schema.traces);
647+
var tracesSchema = [];
648+
var i, j, k;
649+
for(i = 0; i < traces.length; i++) {
650+
tracesSchema.push(schema.traces[traces[i]]);
651+
}
652+
var excludedTraces = [ 'image' ];
653+
var cartesianTraces = tracesSchema.filter(function(t) {
654+
return t.categories.length &&
655+
t.categories.indexOf('cartesian') !== -1 &&
656+
t.categories.indexOf('noHover') === -1 &&
657+
excludedTraces.indexOf(t.type) === -1;
658+
});
659+
660+
function makeData(type, axName, a, b) {
661+
var input = [a, b];
662+
var cat = input[axName === 'yaxis' ? 1 : 0];
663+
var data = input[axName === 'yaxis' ? 0 : 1];
664+
665+
var measure = [];
666+
for(j = 0; j < data.length; j++) {
667+
measure.push('absolute');
668+
}
669+
670+
var z = Lib.init2dArray(cat.length, data.length);
671+
for(j = 0; j < z.length; j++) {
672+
for(k = 0; k < z[j].length; k++) {
673+
z[j][k] = 0;
674+
}
675+
}
676+
if(axName === 'xaxis') {
677+
for(j = 0; j < b.length; j++) {
678+
z[0][j] = b[j];
679+
}
680+
}
681+
if(axName === 'yaxis') {
682+
for(j = 0; j < b.length; j++) {
683+
z[j][0] = b[j];
684+
}
685+
}
686+
687+
return Lib.extendDeep({}, {
688+
orientation: axName === 'yaxis' ? 'h' : 'v',
689+
type: type,
690+
x: cat,
691+
a: cat,
692+
693+
b: data,
694+
y: data,
695+
z: z,
696+
697+
// For OHLC
698+
open: data,
699+
close: data,
700+
high: data,
701+
low: data,
702+
703+
// For histogram
704+
nbinsx: cat.length,
705+
nbinsy: data.length,
706+
707+
// For waterfall
708+
measure: measure,
709+
710+
// For splom
711+
dimensions: [
712+
{
713+
label: 'DimensionA',
714+
values: a
715+
},
716+
{
717+
label: 'DimensionB',
718+
values: b
719+
}
720+
]
721+
});
722+
}
723+
724+
cartesianTraces.forEach(function(trace) {
725+
it('correctly responds to setting the spikedistance to -1 for ' + trace.type, function(done) {
726+
var type = trace.type;
727+
var x = [4, 5, 6];
728+
var data = [7, 2, 3];
729+
730+
var mock = {
731+
data: [makeData(type, 'xaxis', x, data)],
732+
layout: {
733+
spikedistance: -1,
734+
xaxis: {showspikes: true},
735+
yaxis: {showspikes: true},
736+
zaxis: {showspikes: true},
737+
title: {text: trace.type},
738+
width: 400, height: 400
739+
}
740+
};
741+
742+
Plotly.newPlot(gd, mock)
743+
.then(function() {
744+
_hover({xpx: 200, ypx: 100});
745+
746+
var lines = d3.selectAll('line.spikeline');
747+
expect(lines.size()).toBe(4);
748+
})
749+
.catch(failTest)
750+
.then(done);
751+
});
752+
});
753+
});
596754
});

0 commit comments

Comments
 (0)