diff --git a/src/components/fx/hovertemplate_attributes.js b/src/components/fx/hovertemplate_attributes.js
index 6c002d6404c..7d0359cb0e1 100644
--- a/src/components/fx/hovertemplate_attributes.js
+++ b/src/components/fx/hovertemplate_attributes.js
@@ -19,7 +19,7 @@ module.exports = function(opts, extra) {
for(var i = 0; i < keys.length; i++) {
quotedKeys[i] = '`' + keys[i] + '`';
}
- descPart = descPart + 'Finally, this trace also supports ';
+ descPart = descPart + 'Finally, the template string has access to ';
if(keys.length === 1) {
descPart = 'variable ' + quotedKeys[0];
} else {
diff --git a/src/traces/sankey/attributes.js b/src/traces/sankey/attributes.js
index ee4d1cd5530..df4af7ba137 100644
--- a/src/traces/sankey/attributes.js
+++ b/src/traces/sankey/attributes.js
@@ -13,6 +13,7 @@ var plotAttrs = require('../../plots/attributes');
var colorAttrs = require('../../components/color/attributes');
var fxAttrs = require('../../components/fx/attributes');
var domainAttrs = require('../../plots/domain').attributes;
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var extendFlat = require('../../lib/extend').extendFlat;
var overrideAll = require('../../plot_api/edit_types').overrideAll;
@@ -146,6 +147,10 @@ var attrs = module.exports = overrideAll({
].join(' ')
},
hoverlabel: fxAttrs.hoverlabel, // needs editType override,
+ hovertemplate: hovertemplateAttrs({}, {
+ description: 'Variables `sourceLinks` and `targetLinks` are arrays of link objects.',
+ keys: ['value', 'label']
+ }),
description: 'The nodes of the Sankey plot.'
},
@@ -216,6 +221,10 @@ var attrs = module.exports = overrideAll({
].join(' ')
},
hoverlabel: fxAttrs.hoverlabel, // needs editType override,
+ hovertemplate: hovertemplateAttrs({}, {
+ description: 'Variables `source` and `target` are node objects.',
+ keys: ['value', 'label']
+ }),
description: 'The links of the Sankey plot.'
}
}, 'calc', 'nested');
diff --git a/src/traces/sankey/defaults.js b/src/traces/sankey/defaults.js
index 51dc790f846..25a89c91dc2 100644
--- a/src/traces/sankey/defaults.js
+++ b/src/traces/sankey/defaults.js
@@ -35,6 +35,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
coerceNode('line.width');
coerceNode('hoverinfo', traceIn.hoverinfo);
handleHoverLabelDefaults(nodeIn, nodeOut, coerceNode, hoverlabelDefault);
+ coerceNode('hovertemplate');
var colors = layout.colorway;
@@ -57,6 +58,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
coerceLink('line.width');
coerceLink('hoverinfo', traceIn.hoverinfo);
handleHoverLabelDefaults(linkIn, linkOut, coerceLink, hoverlabelDefault);
+ coerceLink('hovertemplate');
var defaultLinkColor = tinycolor(layout.paper_bgcolor).getLuminance() < 0.333 ?
'rgba(255, 255, 255, 0.6)' :
diff --git a/src/traces/sankey/plot.js b/src/traces/sankey/plot.js
index cb6c7300c74..6a89c2306cd 100644
--- a/src/traces/sankey/plot.js
+++ b/src/traces/sankey/plot.js
@@ -133,6 +133,7 @@ module.exports = function plot(gd, calcData) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(linkHoveredStyle.bind(0, d, sankey, true));
if(d.link.trace.link.hoverinfo !== 'skip') {
+ d.link.fullData = d.link.trace;
gd.emit('plotly_hover', {
event: d3.event,
points: [d.link]
@@ -155,10 +156,13 @@ module.exports = function plot(gd, calcData) {
var hoverCenterX = boundingBox.left + boundingBox.width / 2;
var hoverCenterY = boundingBox.top + boundingBox.height / 2;
+ var hovertemplateLabels = {valueLabel: d3.format(d.valueFormat)(d.link.value) + d.valueSuffix};
+ d.link.fullData = d.link.trace;
+
var tooltip = Fx.loneHover({
x: hoverCenterX - rootBBox.left,
y: hoverCenterY - rootBBox.top,
- name: d3.format(d.valueFormat)(d.link.value) + d.valueSuffix,
+ name: hovertemplateLabels.valueLabel,
text: [
d.link.label || '',
sourceLabel + d.link.source.label,
@@ -169,7 +173,11 @@ module.exports = function plot(gd, calcData) {
fontFamily: castHoverOption(obj, 'font.family'),
fontSize: castHoverOption(obj, 'font.size'),
fontColor: castHoverOption(obj, 'font.color'),
- idealAlign: d3.event.x < hoverCenterX ? 'right' : 'left'
+ idealAlign: d3.event.x < hoverCenterX ? 'right' : 'left',
+
+ hovertemplate: obj.hovertemplate,
+ hovertemplateLabels: hovertemplateLabels,
+ eventData: [d.link]
}, {
container: fullLayout._hoverlayer.node(),
outerContainer: fullLayout._paper.node(),
@@ -184,6 +192,7 @@ module.exports = function plot(gd, calcData) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(linkNonHoveredStyle.bind(0, d, sankey, true));
if(d.link.trace.link.hoverinfo !== 'skip') {
+ d.link.fullData = d.link.trace;
gd.emit('plotly_unhover', {
event: d3.event,
points: [d.link]
@@ -205,6 +214,7 @@ module.exports = function plot(gd, calcData) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(nodeHoveredStyle, d, sankey);
if(d.node.trace.node.hoverinfo !== 'skip') {
+ d.node.fullData = d.node.trace;
gd.emit('plotly_hover', {
event: d3.event,
points: [d.node]
@@ -224,6 +234,9 @@ module.exports = function plot(gd, calcData) {
var hoverCenterX1 = boundingBox.right + 2 - rootBBox.left;
var hoverCenterY = boundingBox.top + boundingBox.height / 4 - rootBBox.top;
+ var hovertemplateLabels = {valueLabel: d3.format(d.valueFormat)(d.node.value) + d.valueSuffix};
+ d.node.fullData = d.node.trace;
+
var tooltip = Fx.loneHover({
x0: hoverCenterX0,
x1: hoverCenterX1,
@@ -239,7 +252,11 @@ module.exports = function plot(gd, calcData) {
fontFamily: castHoverOption(obj, 'font.family'),
fontSize: castHoverOption(obj, 'font.size'),
fontColor: castHoverOption(obj, 'font.color'),
- idealAlign: 'left'
+ idealAlign: 'left',
+
+ hovertemplate: obj.hovertemplate,
+ hovertemplateLabels: hovertemplateLabels,
+ eventData: [d.node]
}, {
container: fullLayout._hoverlayer.node(),
outerContainer: fullLayout._paper.node(),
@@ -254,6 +271,7 @@ module.exports = function plot(gd, calcData) {
if(gd._fullLayout.hovermode === false) return;
d3.select(element).call(nodeNonHoveredStyle, d, sankey);
if(d.node.trace.node.hoverinfo !== 'skip') {
+ d.node.fullData = d.node.trace;
gd.emit('plotly_unhover', {
event: d3.event,
points: [d.node]
diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js
index dfb1855f275..4f881126619 100644
--- a/test/jasmine/tests/sankey_test.js
+++ b/test/jasmine/tests/sankey_test.js
@@ -497,6 +497,53 @@ describe('sankey tests', function() {
.then(done);
});
+ it('should show the correct hover labels when hovertemplate is specified', function(done) {
+ var gd = createGraphDiv();
+ var mockCopy = Lib.extendDeep({}, mock);
+
+ Plotly.plot(gd, mockCopy).then(function() {
+ _hover(404, 302);
+
+ assertLabel(
+ ['Solid', 'incoming flow count: 4', 'outgoing flow count: 3', '447TWh'],
+ ['rgb(148, 103, 189)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)']
+ );
+ })
+ .then(function() {
+ _hover(450, 300);
+
+ assertLabel(
+ ['source: Solid', 'target: Industry', '46TWh'],
+ ['rgb(0, 0, 96)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)']
+ );
+ })
+ // Test (node|link).hovertemplate
+ .then(function() {
+ return Plotly.restyle(gd, {
+ 'node.hovertemplate': 'hovertemplate
%{value}
%{value:0.2f}%{fullData.name}',
+ 'link.hovertemplate': 'hovertemplate
source: %{source.label}
target: %{target.label}
size: %{value:0.0f}TWh%{fullData.name}'
+ });
+ })
+ .then(function() {
+ _hover(404, 302);
+
+ assertLabel(
+ [ 'hovertemplate', '447TWh', '447.48', 'trace 0'],
+ ['rgb(148, 103, 189)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)']
+ );
+ })
+ .then(function() {
+ _hover(450, 300);
+
+ assertLabel(
+ ['hovertemplate', 'source: Solid', 'target: Industry', 'size: 46TWh', 'trace 0'],
+ ['rgb(0, 0, 96)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)']
+ );
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
it('should show the correct hover labels with the style provided in template', function(done) {
var gd = createGraphDiv();
var mockCopy = Lib.extendDeep({}, mock);
@@ -744,8 +791,12 @@ describe('sankey tests', function() {
_assert(d, {
curveNumber: 0,
pointNumber: 4,
- label: 'Solid'
+ label: 'Solid',
+ value: 447.48
});
+ var pt = d.points[0];
+ expect(pt.sourceLinks.length).toBe(3);
+ expect(pt.targetLinks.length).toBe(4);
})
.then(function() { return _hover('link'); })
.then(function(d) {
@@ -754,6 +805,9 @@ describe('sankey tests', function() {
pointNumber: 61,
value: 46.477
});
+ var pt = d.points[0];
+ expect(pt.hasOwnProperty('source')).toBeTruthy();
+ expect(pt.hasOwnProperty('target')).toBeTruthy();
})
.then(function() { return _unhover('node'); })
.then(function(d) {