diff --git a/src/traces/sankey/attributes.js b/src/traces/sankey/attributes.js index dfbd8845822..85f35ac118b 100644 --- a/src/traces/sankey/attributes.js +++ b/src/traces/sankey/attributes.js @@ -9,13 +9,27 @@ 'use strict'; var fontAttrs = require('../../plots/font_attributes'); +var plotAttrs = require('../../plots/attributes'); var colorAttrs = require('../../components/color/attributes'); var fxAttrs = require('../../components/fx/attributes'); var domainAttrs = require('../../plots/domain').attributes; +var extendFlat = require('../../lib/extend').extendFlat; var overrideAll = require('../../plot_api/edit_types').overrideAll; -var attrs = module.exports = overrideAll({ +module.exports = overrideAll({ + hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + flags: [], + arrayOk: false, + description: [ + 'Determines which trace information appear on hover.', + 'If `none` or `skip` are set, no information is displayed upon hovering.', + 'But, if `none` is set, click and hover events are still fired.', + 'Note that this attribute is superseded by `node.hoverinfo` and `node.hoverinfo`', + 'for nodes and links respectively.' + ].join(' ') + }), + hoverlabel: fxAttrs.hoverlabel, domain: domainAttrs({name: 'sankey', trace: true}), orientation: { @@ -205,6 +219,3 @@ var attrs = module.exports = overrideAll({ description: 'The links of the Sankey plot.' } }, 'calc', 'nested'); -// hide unsupported top-level properties from plot-schema -attrs.hoverinfo = undefined; -attrs.hoverlabel = undefined; diff --git a/src/traces/sankey/defaults.js b/src/traces/sankey/defaults.js index a755fc34e75..51dc790f846 100644 --- a/src/traces/sankey/defaults.js +++ b/src/traces/sankey/defaults.js @@ -21,6 +21,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } + var hoverlabelDefault = Lib.extendDeep(layout.hoverlabel, traceIn.hoverlabel); + // node attributes var nodeIn = traceIn.node, nodeOut = Template.newContainer(traceOut, 'node'); function coerceNode(attr, dflt) { @@ -31,8 +33,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerceNode('thickness'); coerceNode('line.color'); coerceNode('line.width'); - coerceNode('hoverinfo'); - handleHoverLabelDefaults(nodeIn, nodeOut, coerceNode, layout.hoverlabel); + coerceNode('hoverinfo', traceIn.hoverinfo); + handleHoverLabelDefaults(nodeIn, nodeOut, coerceNode, hoverlabelDefault); var colors = layout.colorway; @@ -53,16 +55,14 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerceLink('value'); coerceLink('line.color'); coerceLink('line.width'); - coerceLink('hoverinfo'); - handleHoverLabelDefaults(linkIn, linkOut, coerceLink, layout.hoverlabel); + coerceLink('hoverinfo', traceIn.hoverinfo); + handleHoverLabelDefaults(linkIn, linkOut, coerceLink, hoverlabelDefault); var defaultLinkColor = tinycolor(layout.paper_bgcolor).getLuminance() < 0.333 ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.2)'; - coerceLink('color', linkOut.value.map(function() { - return defaultLinkColor; - })); + coerceLink('color', Lib.repeat(defaultLinkColor, linkOut.value.length)); handleDomainDefaults(traceOut, layout, coerce); diff --git a/test/jasmine/tests/sankey_test.js b/test/jasmine/tests/sankey_test.js index d5cd1f90021..dfb1855f275 100644 --- a/test/jasmine/tests/sankey_test.js +++ b/test/jasmine/tests/sankey_test.js @@ -416,7 +416,9 @@ describe('sankey tests', function() { ['source: Solid', 'target: Industry', '46TWh'], ['rgb(0, 0, 96)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)'] ); - + }) + // Test layout.hoverlabel + .then(function() { return Plotly.relayout(gd, 'hoverlabel.font.family', 'Roboto'); }) .then(function() { @@ -434,16 +436,45 @@ describe('sankey tests', function() { ['source: Solid', 'target: Industry', '46TWh'], ['rgb(0, 0, 96)', 'rgb(255, 255, 255)', 13, 'Roboto', 'rgb(255, 255, 255)'] ); + }) + // Test trace-level hoverlabel + .then(function() { + return Plotly.restyle(gd, { + 'hoverlabel.bgcolor': 'blue', + 'hoverlabel.bordercolor': 'red', + 'hoverlabel.font.size': 22, + 'hoverlabel.font.color': 'magenta' + }); + }) + .then(function() { + _hover(404, 302); + assertLabel( + ['Solid', 'incoming flow count: 4', 'outgoing flow count: 3', '447TWh'], + ['rgb(0, 0, 255)', 'rgb(255, 0, 0)', 22, 'Roboto', 'rgb(255, 0, 255)'] + ); + }) + .then(function() { + _hover(450, 300); + + assertLabel( + ['source: Solid', 'target: Industry', '46TWh'], + ['rgb(0, 0, 255)', 'rgb(255, 0, 0)', 22, 'Roboto', 'rgb(255, 0, 255)'] + ); + }) + // Test (node|link).hoverlabel + .then(function() { return Plotly.restyle(gd, { 'node.hoverlabel.bgcolor': 'red', 'node.hoverlabel.bordercolor': 'blue', 'node.hoverlabel.font.size': 20, 'node.hoverlabel.font.color': 'black', + 'node.hoverlabel.font.family': 'Roboto', 'link.hoverlabel.bgcolor': 'yellow', 'link.hoverlabel.bordercolor': 'magenta', 'link.hoverlabel.font.size': 18, - 'link.hoverlabel.font.color': 'green' + 'link.hoverlabel.font.color': 'green', + 'link.hoverlabel.font.family': 'Roboto' }); }) .then(function() { @@ -556,49 +587,59 @@ describe('sankey tests', function() { .then(done); }); - it('should not show node labels if node.hoverinfo is none', function(done) { - var gd = createGraphDiv(); - var mockCopy = Lib.extendDeep({}, mock); + ['skip', 'none'].forEach(function(hoverinfoFlag) { + it('should not show node labels if node.hoverinfo is ' + hoverinfoFlag, 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); + Plotly.plot(gd, mockCopy).then(function() { + return Plotly.restyle(gd, 'node.hoverinfo', hoverinfoFlag); + }) + .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); + ['skip', 'none'].forEach(function(hoverinfoFlag) { + it('should not show link labels if link.hoverinfo is ' + hoverinfoFlag, 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); + Plotly.plot(gd, mockCopy).then(function() { + return Plotly.restyle(gd, 'link.hoverinfo', hoverinfoFlag); + }) + .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); + ['skip', 'none'].forEach(function(hoverinfoFlag) { + it('should not show labels if trace hoverinfo is ' + hoverinfoFlag + ' and (node|link).hoverinfo is undefined', 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) - .then(done); + Plotly.plot(gd, mockCopy).then(function() { + return Plotly.restyle(gd, 'hoverinfo', hoverinfoFlag); + }) + .then(function() { + _hover(node[0], node[1]); + assertNoLabel(); + }) + .then(function() { + _hover(link[0], link[1]); + assertNoLabel(); + }) + .catch(failTest) + .then(done); + }); }); it('should not show link labels if link.hoverinfo is skip', function(done) { @@ -748,7 +789,7 @@ describe('sankey tests', function() { }; } - it('should not output hover/unhover event data when hovermoder is false', function(done) { + it('should not output hover/unhover event data when hovermode is false', function(done) { var fig = Lib.extendDeep({}, mock); Plotly.plot(gd, fig) @@ -759,17 +800,28 @@ describe('sankey tests', function() { .then(done); }); - it('should not output hover/unhover event data when hoverinfo is skip', function(done) { + it('should not output hover/unhover event data when trace 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 Plotly.restyle(gd, 'hoverinfo', 'skip'); }) .then(assertNoHoverEvents('link')) - .then(function() { return Plotly.restyle(gd, 'node.hoverinfo', 'skip'); }) .then(assertNoHoverEvents('node')) .catch(failTest) .then(done); }); + + ['node', 'link'].forEach(function(obj) { + it('should not output hover/unhover event data when ' + obj + '.hoverinfo is skip', function(done) { + var fig = Lib.extendDeep({}, mock); + + Plotly.plot(gd, fig) + .then(function() { return Plotly.restyle(gd, obj + '.hoverinfo', 'skip'); }) + .then(assertNoHoverEvents(obj)) + .catch(failTest) + .then(done); + }); + }); }); it('emits a warning if node.pad is too large', function(done) {