Skip to content
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

implement hoverinfo 'none' and 'skip' in sankey #3096

Merged
merged 18 commits into from
Oct 23, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/plot_api/edit_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

var Lib = require('../lib');
var extendFlat = Lib.extendFlat;
var extendDeepAll = Lib.extendDeepAll;
var isPlainObject = Lib.isPlainObject;

var traceOpts = {
Expand Down Expand Up @@ -115,7 +116,7 @@ function falseObj(keys) {
* @return {object} a new attributes object with `editType` modified as directed
*/
function overrideAll(attrs, editTypeOverride, overrideContainers) {
var out = extendFlat({}, attrs);
var out = extendDeepAll({}, attrs);
antoinerg marked this conversation as resolved.
Show resolved Hide resolved
for(var key in out) {
var attr = out[key];
if(isPlainObject(attr)) {
Expand Down
13 changes: 7 additions & 6 deletions src/traces/sankey/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ var extendFlat = require('../../lib/extend').extendFlat;
var overrideAll = require('../../plot_api/edit_types').overrideAll;

module.exports = overrideAll({
hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {
flags: ['label', 'text', 'value', 'percent', 'name'],
}),
hoverlabel: fxAttrs.hoverlabel, // needs editType override

domain: domainAttrs({name: 'sankey', trace: true}),

orientation: {
Expand Down Expand Up @@ -127,6 +122,8 @@ module.exports = overrideAll({
role: 'style',
description: 'Sets the thickness (in px) of the `nodes`.'
},
hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {flags: []}),
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved
hoverlabel: fxAttrs.hoverlabel, // needs editType override,
description: 'The nodes of the Sankey plot.'
},

Expand Down Expand Up @@ -185,6 +182,10 @@ module.exports = overrideAll({
role: 'info',
description: 'A numeric value representing the flow volume value.'
},
hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {flags: []}),
hoverlabel: fxAttrs.hoverlabel, // needs editType override,
description: 'The links of the Sankey plot.'
}
},
antoinerg marked this conversation as resolved.
Show resolved Hide resolved
hoverinfo: undefined,
hoverlabel: undefined
}, 'calc', 'nested');
11 changes: 11 additions & 0 deletions src/traces/sankey/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var attributes = require('./attributes');
var Color = require('../../components/color');
var tinycolor = require('tinycolor2');
var handleDomainDefaults = require('../../plots/domain').defaults;
var Registry = require('../../registry');

module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
function coerce(attr, dflt) {
Expand All @@ -24,6 +25,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
coerce('node.thickness');
coerce('node.line.color');
coerce('node.line.width');
coerce('node.hoverinfo');
Registry.getComponentMethod(
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved
'fx',
'supplyDefaults'
)(traceIn.node, traceOut.node, defaultColor, layout);

var colors = layout.colorway;

Expand All @@ -39,6 +45,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
coerce('link.value');
coerce('link.line.color');
coerce('link.line.width');
coerce('link.hoverinfo');
Registry.getComponentMethod(
'fx',
'supplyDefaults'
)(traceIn.link, traceOut.link, defaultColor, layout);

coerce('link.color', traceOut.link.value.map(function() {
return tinycolor(layout.paper_bgcolor).getLuminance() < 0.333 ?
Expand Down
68 changes: 40 additions & 28 deletions src/traces/sankey/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,13 @@ module.exports = function plot(gd, calcData) {
var linkHover = function(element, d, sankey) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(linkHoveredStyle.bind(0, d, sankey, true));
gd.emit('plotly_hover', {
event: d3.event,
points: [d.link]
});
if(d.link.trace.link.hoverinfo !== 'skip') {
gd.emit('plotly_hover', {
event: d3.event,
points: [d.link]
});
}

};

var sourceLabel = _(gd, 'source:') + ' ';
Expand All @@ -145,7 +148,8 @@ module.exports = function plot(gd, calcData) {

var linkHoverFollow = function(element, d) {
if(gd._fullLayout.hovermode === false) return;
var trace = d.link.trace;
var obj = d.link.trace.link;
if(obj.hoverinfo === 'none' || obj.hoverinfo === 'skip') return;
var rootBBox = gd._fullLayout._paperdiv.node().getBoundingClientRect();
var boundingBox = element.getBoundingClientRect();
var hoverCenterX = boundingBox.left + boundingBox.width / 2;
Expand All @@ -160,11 +164,11 @@ module.exports = function plot(gd, calcData) {
sourceLabel + d.link.source.label,
targetLabel + d.link.target.label
].filter(renderableValuePresent).join('<br>'),
color: castHoverOption(trace, 'bgcolor') || Color.addOpacity(d.tinyColorHue, 1),
borderColor: castHoverOption(trace, 'bordercolor'),
fontFamily: castHoverOption(trace, 'font.family'),
fontSize: castHoverOption(trace, 'font.size'),
fontColor: castHoverOption(trace, 'font.color'),
color: castHoverOption(obj, 'bgcolor') || Color.addOpacity(d.tinyColorHue, 1),
borderColor: castHoverOption(obj, 'bordercolor'),
fontFamily: castHoverOption(obj, 'font.family'),
fontSize: castHoverOption(obj, 'font.size'),
fontColor: castHoverOption(obj, 'font.color'),
idealAlign: d3.event.x < hoverCenterX ? 'right' : 'left'
}, {
container: fullLayout._hoverlayer.node(),
Expand All @@ -179,10 +183,12 @@ module.exports = function plot(gd, calcData) {
var linkUnhover = function(element, d, sankey) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(linkNonHoveredStyle.bind(0, d, sankey, true));
gd.emit('plotly_unhover', {
event: d3.event,
points: [d.link]
});
if(d.link.trace.link.hoverinfo !== 'skip') {
gd.emit('plotly_unhover', {
event: d3.event,
points: [d.link]
});
}

Fx.loneUnhover(fullLayout._hoverlayer.node());
};
Expand All @@ -198,15 +204,19 @@ module.exports = function plot(gd, calcData) {
var nodeHover = function(element, d, sankey) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(nodeHoveredStyle, d, sankey);
gd.emit('plotly_hover', {
event: d3.event,
points: [d.node]
});
if(d.node.trace.node.hoverinfo !== 'skip') {
gd.emit('plotly_hover', {
event: d3.event,
points: [d.node]
});
}
};

var nodeHoverFollow = function(element, d) {
if(gd._fullLayout.hovermode === false) return;
var trace = d.node.trace;

var obj = d.node.trace.node;
if(obj.hoverinfo === 'none' || obj.hoverinfo === 'skip') return;
var nodeRect = d3.select(element).select('.' + cn.nodeRect);
var rootBBox = gd._fullLayout._paperdiv.node().getBoundingClientRect();
var boundingBox = nodeRect.node().getBoundingClientRect();
Expand All @@ -224,11 +234,11 @@ module.exports = function plot(gd, calcData) {
incomingLabel + d.node.targetLinks.length,
outgoingLabel + d.node.sourceLinks.length
].filter(renderableValuePresent).join('<br>'),
color: castHoverOption(trace, 'bgcolor') || d.tinyColorHue,
borderColor: castHoverOption(trace, 'bordercolor'),
fontFamily: castHoverOption(trace, 'font.family'),
fontSize: castHoverOption(trace, 'font.size'),
fontColor: castHoverOption(trace, 'font.color'),
color: castHoverOption(obj, 'bgcolor') || d.tinyColorHue,
borderColor: castHoverOption(obj, 'bordercolor'),
fontFamily: castHoverOption(obj, 'font.family'),
fontSize: castHoverOption(obj, 'font.size'),
fontColor: castHoverOption(obj, 'font.color'),
idealAlign: 'left'
}, {
container: fullLayout._hoverlayer.node(),
Expand All @@ -243,10 +253,12 @@ module.exports = function plot(gd, calcData) {
var nodeUnhover = function(element, d, sankey) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(nodeNonHoveredStyle, d, sankey);
gd.emit('plotly_unhover', {
event: d3.event,
points: [d.node]
});
if(d.node.trace.node.hoverinfo !== 'skip') {
gd.emit('plotly_unhover', {
event: d3.event,
points: [d.node]
});
}

Fx.loneUnhover(fullLayout._hoverlayer.node());
};
Expand Down
111 changes: 88 additions & 23 deletions test/jasmine/tests/sankey_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@ describe('sankey tests', function() {
Lib.clearThrottle();
}

var node = [404, 302],
link = [450, 300];

it('should show the correct hover labels', function(done) {
var gd = createGraphDiv();
var mockCopy = Lib.extendDeep({}, mock);
Expand Down Expand Up @@ -433,10 +436,14 @@ describe('sankey tests', function() {
);

return Plotly.restyle(gd, {
'hoverlabel.bgcolor': 'red',
'hoverlabel.bordercolor': 'blue',
'hoverlabel.font.size': 20,
'hoverlabel.font.color': 'black'
'node.hoverlabel.bgcolor': 'red',
'node.hoverlabel.bordercolor': 'blue',
'node.hoverlabel.font.size': 20,
'node.hoverlabel.font.color': 'black',
'link.hoverlabel.bgcolor': 'red',
'link.hoverlabel.bordercolor': 'blue',
'link.hoverlabel.font.size': 20,
'link.hoverlabel.font.color': 'black'
antoinerg marked this conversation as resolved.
Show resolved Hide resolved
});
})
.then(function() {
Expand All @@ -459,7 +466,7 @@ describe('sankey tests', function() {
.then(done);
});

it('should show correct hover labels even if there is no link.label supplied', function(done) {
it('should show the correct hover labels even if there is no link.label supplied', function(done) {
var gd = createGraphDiv();
var mockCopy = Lib.extendDeep({}, mock);
delete mockCopy.data[0].link.label;
Expand All @@ -477,16 +484,64 @@ describe('sankey tests', function() {
.then(done);
});

it('should not show labels if hovermode is false', function(done) {
it('should not show any labels if hovermode is false', function(done) {
var gd = createGraphDiv();
var mockCopy = Lib.extendDeep({}, mock);

Plotly.plot(gd, mockCopy).then(function() {
return Plotly.relayout(gd, 'hovermode', false);
})
.then(function() {
_hover(404, 302);
_hover(node[0], node[1]);
assertNoLabel();
})
.then(function() {
_hover(link[0], link[1]);
assertNoLabel();
})
.catch(failTest)
.then(done);
});

it('should not show node labels if node.hoverinfo is none', function(done) {
var gd = createGraphDiv();
var mockCopy = Lib.extendDeep({}, mock);

Plotly.plot(gd, mockCopy).then(function() {
return Plotly.restyle(gd, 'node.hoverinfo', 'none');
})
.then(function() {
_hover(node[0], node[1]);
assertNoLabel();
})
.catch(failTest)
.then(done);
});

it('should not show link labels if link.hoverinfo is none', function(done) {
var gd = createGraphDiv();
var mockCopy = Lib.extendDeep({}, mock);

Plotly.plot(gd, mockCopy).then(function() {
return Plotly.restyle(gd, 'link.hoverinfo', 'none');
})
.then(function() {
_hover(link[0], link[1]);
assertNoLabel();
})
.catch(failTest)
.then(done);
});

it('should not show node labels if node.hoverinfo is skip', function(done) {
var gd = createGraphDiv();
var mockCopy = Lib.extendDeep({}, mock);

Plotly.plot(gd, mockCopy).then(function() {
return Plotly.restyle(gd, 'node.hoverinfo', 'skip');
})
.then(function() {
_hover(node[0], node[1]);
assertNoLabel();
})
.catch(failTest)
Expand Down Expand Up @@ -574,6 +629,7 @@ describe('sankey tests', function() {
var fig = Lib.extendDeep({}, mock);

Plotly.plot(gd, fig)
.then(function() { return Plotly.restyle(gd, 'hoverinfo', 'none'); })
.then(function() { return _hover('node'); })
.then(function(d) {
_assert(d, {
Expand Down Expand Up @@ -610,27 +666,36 @@ describe('sankey tests', function() {
.then(done);
});

it('should not output hover/unhover event data when hovermoder is false', function(done) {
var fig = Lib.extendDeep({}, mock);

Plotly.plot(gd, fig)
.then(function() { return Plotly.relayout(gd, 'hovermode', false); })
.then(function() { return _hover('node'); })
.then(failTest).catch(function(err) {
expect(err).toBe('plotly_hover did not get called!');
})
.then(function() { return _unhover('node'); })
.then(failTest).catch(function(err) {
expect(err).toBe('plotly_unhover did not get called!');
})
.then(function() { return _hover('link'); })
function assertNoHoverEvents(type) {
return Promise.resolve()
.then(function() { return _hover(type); })
.then(failTest).catch(function(err) {
expect(err).toBe('plotly_hover did not get called!');
})
.then(function() { return _unhover('link'); })
.then(function() { return _unhover(type); })
.then(failTest).catch(function(err) {
expect(err).toBe('plotly_unhover did not get called!');
})
});
}

it('should not output hover/unhover event data when hovermoder is false', function(done) {
var fig = Lib.extendDeep({}, mock);

Plotly.plot(gd, fig)
.then(function() { return Plotly.relayout(gd, 'hovermode', false); })
.then(function() { return assertNoHoverEvents('node');})
.then(function() { return assertNoHoverEvents('link');})
antoinerg marked this conversation as resolved.
Show resolved Hide resolved
.catch(failTest)
.then(done);
});

it('should not output hover/unhover event data when hoverinfo is skip', function(done) {
var fig = Lib.extendDeep({}, mock);

Plotly.plot(gd, fig)
.then(function() { return Plotly.restyle(gd, 'link.hoverinfo', 'skip'); })
.then(function() { return assertNoHoverEvents('link');})
.catch(failTest)
.then(done);
});
});
Expand Down