-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Generalize hover picking routine #575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
858a338
27683c2
39b8acf
92da0e3
76ba24b
7ff5ca9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -310,27 +310,45 @@ function hover(gd, evt, subplot) { | |
|
||
if(!subplot) subplot = 'xy'; | ||
|
||
// if the user passed in an array of subplots, | ||
// use those instead of finding overlayed plots | ||
var subplots = Array.isArray(subplot) ? subplot : [subplot]; | ||
|
||
var fullLayout = gd._fullLayout, | ||
plotinfo = fullLayout._plots[subplot], | ||
|
||
//If the user passed in an array of subplots, use those instead of finding overlayed plots | ||
subplots = Array.isArray(subplot) ? | ||
subplot : | ||
// list of all overlaid subplots to look at | ||
[subplot].concat(plotinfo.overlays | ||
.map(function(pi) { return pi.id; })), | ||
|
||
xaArray = subplots.map(function(spId) { | ||
var ternary = (gd._fullLayout[spId] || {})._ternary; | ||
if(ternary) return ternary.xaxis; | ||
return Axes.getFromId(gd, spId, 'x'); | ||
}), | ||
yaArray = subplots.map(function(spId) { | ||
var ternary = (gd._fullLayout[spId] || {})._ternary; | ||
if(ternary) return ternary.yaxis; | ||
return Axes.getFromId(gd, spId, 'y'); | ||
}), | ||
hovermode = evt.hovermode || fullLayout.hovermode; | ||
plots = fullLayout._plots || [], | ||
plotinfo = plots[subplot]; | ||
|
||
// list of all overlaid subplots to look at | ||
if(plotinfo) { | ||
var overlayedSubplots = plotinfo.overlays.map(function(pi) { | ||
return pi.id; | ||
}); | ||
|
||
subplots = subplots.concat(overlayedSubplots); | ||
} | ||
|
||
var len = subplots.length, | ||
xaArray = new Array(len), | ||
yaArray = new Array(len); | ||
|
||
for(var i = 0; i < len; i++) { | ||
var spId = subplots[i]; | ||
|
||
// 'cartesian' case | ||
var plotObj = plots[spId]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. look in:
|
||
if(plotObj) { | ||
xaArray[i] = plotObj.xaxis; | ||
yaArray[i] = plotObj.yaxis; | ||
continue; | ||
} | ||
|
||
// other subplot types | ||
var _subplot = fullLayout[spId]._subplot; | ||
xaArray[i] = _subplot.xaxis; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mocking xaxis and yaxis for hover is pretty easy and also pretty convenient for hover, as the x/y axes are parallel to the pixel coordinate axes. |
||
yaArray[i] = _subplot.yaxis; | ||
} | ||
|
||
var hovermode = evt.hovermode || fullLayout.hovermode; | ||
|
||
if(['x', 'y', 'closest'].indexOf(hovermode) === -1 || !gd.calcdata || | ||
gd.querySelector('.zoombox') || gd._dragging) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,7 +38,7 @@ exports.plot = function plotTernary(gd) { | |
for(var i = 0; i < ternaryIds.length; i++) { | ||
var ternaryId = ternaryIds[i], | ||
fullTernaryData = Plots.getSubplotData(fullData, 'ternary', ternaryId), | ||
ternary = fullLayout[ternaryId]._ternary; | ||
ternary = fullLayout[ternaryId]._subplot; | ||
|
||
// If ternary is not instantiated, create one! | ||
if(ternary === undefined) { | ||
|
@@ -50,7 +50,7 @@ exports.plot = function plotTernary(gd) { | |
fullLayout | ||
); | ||
|
||
fullLayout[ternaryId]._ternary = ternary; | ||
fullLayout[ternaryId]._subplot = ternary; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One step to generalize how we store instances to subplot instance in To note, I haven't changed all the |
||
} | ||
|
||
ternary.plot(fullTernaryData, fullLayout, gd._promises); | ||
|
@@ -62,7 +62,7 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) | |
|
||
for(var i = 0; i < oldTernaryKeys.length; i++) { | ||
var oldTernaryKey = oldTernaryKeys[i]; | ||
var oldTernary = oldFullLayout[oldTernaryKey]._ternary; | ||
var oldTernary = oldFullLayout[oldTernaryKey]._subplot; | ||
|
||
if(!newFullLayout[oldTernaryKey] && !!oldTernary) { | ||
oldTernary.plotContainer.remove(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,11 +13,16 @@ var scatterPlot = require('../scatter/plot'); | |
|
||
|
||
module.exports = function plot(ternary, data) { | ||
var plotContainer = ternary.plotContainer; | ||
|
||
// remove all nodes inside the scatter layer | ||
plotContainer.select('.scatterlayer').selectAll('*').remove(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This new line may look bad, but it's way better than previously. Commit 39b8acf had a nasty side effect that took me quite some time to debug. Previously, the ternary code stored a ref to a mock cartesian subplot object in We could definitely do better than removing all the inner nodes, but keeping the ordering of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure how d3 manages selections internally, but I'd guess There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember using |
||
|
||
// mimic cartesian plotinfo | ||
var plotinfo = { | ||
x: function() { return ternary.xaxis; }, | ||
y: function() { return ternary.yaxis; }, | ||
plot: ternary.plotContainer | ||
plot: plotContainer | ||
}; | ||
|
||
var calcdata = new Array(data.length), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -574,3 +574,34 @@ describe('hover info on stacked subplots', function() { | |
}); | ||
}); | ||
}); | ||
|
||
|
||
describe('hover info on overlaid subplots', function() { | ||
'use strict'; | ||
|
||
afterEach(destroyGraphDiv); | ||
|
||
it('should respond to hover', function(done) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might as well test this non-trivial logic in |
||
var mock = require('@mocks/autorange-tozero-rangemode.json'); | ||
|
||
Plotly.plot(createGraphDiv(), mock.data, mock.layout).then(function() { | ||
mouseEvent('mousemove', 775, 352); | ||
|
||
var axisText = d3.selectAll('g.axistext'), | ||
hoverText = d3.selectAll('g.hovertext'); | ||
|
||
expect(axisText.size()).toEqual(1, 'with 1 label on axis'); | ||
expect(hoverText.size()).toEqual(2, 'with 2 labels on the overlaid pts'); | ||
|
||
expect(axisText.select('text').html()).toEqual('1', 'with correct axis label'); | ||
|
||
var textNodes = hoverText.selectAll('text'); | ||
|
||
expect(textNodes[0][0].innerHTML).toEqual('Take Rate', 'with correct hover labels'); | ||
expect(textNodes[0][1].innerHTML).toEqual('0.35', 'with correct hover labels'); | ||
expect(textNodes[1][0].innerHTML).toEqual('Revenue', 'with correct hover labels'); | ||
expect(textNodes[1][1].innerHTML).toEqual('2,352.5', 'with correct hover labels'); | ||
|
||
}).then(done); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT keep
fullLayout._plots
forcartesian
subplots (andgl2d
although I'm having second doubt about this) only.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to keep them separate? In my mind, this seems like it will just introduce more surface area for inconsistent state bugs.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly for convenience.
Most of our per-subplot code post-supply-defaults is designed as:
fullLayout
fullLayout[/* name of subplot */]._subplot
So, it becomes convenient to store a ref to the subplot object in its corresponding
fullLayout
attribute container.Moreover, as finding all xy subplots in a given figure is somewhat expensive (see
Axes.getSubplots
), it becomes advantages to look for them infullLayout._plots
directly instead of callingAxes.getSubplots
every time we need to loop over all the x/y subplots.