diff --git a/src/traces/waterfall/attributes.js b/src/traces/waterfall/attributes.js index a4b8efacc93..08ddfb6bdd2 100644 --- a/src/traces/waterfall/attributes.js +++ b/src/traces/waterfall/attributes.js @@ -10,6 +10,9 @@ var barAttrs = require('../bar/attributes'); var lineAttrs = require('../scatter/attributes').line; +var plotAttrs = require('../../plots/attributes'); +var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes'); +var constants = require('./constants'); var extendFlat = require('../../lib/extend').extendFlat; var Color = require('../../components/color'); @@ -74,7 +77,13 @@ module.exports = { dy: barAttrs.dy, hovertext: barAttrs.hovertext, - hovertemplate: barAttrs.hovertemplate, + hovertemplate: hovertemplateAttrs({}, { + keys: constants.eventDataKeys + }), + + hoverinfo: extendFlat({}, plotAttrs.hoverinfo, { + flags: ['name', 'x', 'y', 'text', 'initial', 'delta', 'final'] + }), textinfo: { valType: 'flaglist', diff --git a/src/traces/waterfall/constants.js b/src/traces/waterfall/constants.js new file mode 100644 index 00000000000..21def0c5a92 --- /dev/null +++ b/src/traces/waterfall/constants.js @@ -0,0 +1,17 @@ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = { + eventDataKeys: [ + 'initial', + 'delta', + 'final' + ] +}; diff --git a/src/traces/waterfall/event_data.js b/src/traces/waterfall/event_data.js new file mode 100644 index 00000000000..a3a8e308e75 --- /dev/null +++ b/src/traces/waterfall/event_data.js @@ -0,0 +1,25 @@ +/** +* Copyright 2012-2019, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +module.exports = function eventData(out, pt /* , trace, cd, pointNumber */) { + // standard cartesian event data + out.x = 'xVal' in pt ? pt.xVal : pt.x; + out.y = 'yVal' in pt ? pt.yVal : pt.y; + + // for funnel + if('initial' in pt) out.initial = pt.initial; + if('delta' in pt) out.delta = pt.delta; + if('final' in pt) out.final = pt.final; + + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + return out; +}; diff --git a/src/traces/waterfall/hover.js b/src/traces/waterfall/hover.js index f542765885e..477c59b8bbb 100644 --- a/src/traces/waterfall/hover.js +++ b/src/traces/waterfall/hover.js @@ -38,18 +38,45 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var size = (di.isSum) ? di.b + di.s : di.rawS; if(!di.isSum) { - // format delta numbers: - if(size > 0) { - point.extraText = formatNumber(size) + ' ' + DIRSYMBOL.increasing; - } else if(size < 0) { - point.extraText = '(' + (formatNumber(-size)) + ') ' + DIRSYMBOL.decreasing; - } else { - return; + point.initial = di.b + di.s - size; + point.delta = size; + point.final = point.initial + point.delta; + + var v = formatNumber(Math.abs(point.delta)); + point.deltaLabel = size < 0 ? '(' + v + ')' : v; + point.finalLabel = formatNumber(point.final); + point.initialLabel = formatNumber(point.initial); + } + + var hoverinfo = di.hi || trace.hoverinfo; + var text = []; + if(hoverinfo && hoverinfo !== 'none' && hoverinfo !== 'skip') { + var isAll = (hoverinfo === 'all'); + var parts = hoverinfo.split('+'); + + var hasFlag = function(flag) { return isAll || parts.indexOf(flag) !== -1; }; + + if(!di.isSum) { + if(hasFlag('final') && + (isHorizontal ? !hasFlag('x') : !hasFlag('y')) // don't display redundant info. + ) { + text.push(point.finalLabel); + } + if(hasFlag('delta')) { + if(size < 0) { + text.push(point.deltaLabel + ' ' + DIRSYMBOL.decreasing); + } else { + text.push(point.deltaLabel + ' ' + DIRSYMBOL.increasing); + } + } + if(hasFlag('initial')) { + text.push('Initial: ' + point.initialLabel); + } } - // display initial value - point.extraText += '
Initial: ' + formatNumber(di.b + di.s - size); } + if(text.length) point.extraText = text.join('
'); + point.color = getTraceColor(trace, di); return [point]; diff --git a/src/traces/waterfall/index.js b/src/traces/waterfall/index.js index 23da97872e2..73d0a7f8cfd 100644 --- a/src/traces/waterfall/index.js +++ b/src/traces/waterfall/index.js @@ -19,6 +19,8 @@ module.exports = { plot: require('./plot'), style: require('./style').style, hoverPoints: require('./hover'), + eventData: require('./event_data'), + selectPoints: require('../bar/select'), moduleType: 'trace', diff --git a/test/jasmine/tests/waterfall_test.js b/test/jasmine/tests/waterfall_test.js index c48451f2e4e..20845b600f2 100644 --- a/test/jasmine/tests/waterfall_test.js +++ b/test/jasmine/tests/waterfall_test.js @@ -1379,13 +1379,72 @@ describe('waterfall hover', function() { .then(done); }); + it('should turn off hoverinfo flags with hoveinfo none or skip', function(done) { + gd = createGraphDiv(); + + var mock = Lib.extendDeep({}, require('@mocks/text_chart_arrays')); + mock.data.forEach(function(t, i) { + t.type = 'waterfall'; + if(i === 0) { + t.hoverinfo = 'none'; + } else { + t.hoverinfo = 'skip'; + } + }); + + function _hover() { + var evt = { xpx: 125, ypx: 150 }; + Fx.hover('graph', evt, 'xy'); + } + + Plotly.plot(gd, mock) + .then(_hover) + .then(function() { + expect(d3.selectAll('g.hovertext').size()).toBe(0); + }) + .catch(failTest) + .then(done); + }); + + it('should turn on hoverinfo flags with hoveinfo all', function(done) { + gd = createGraphDiv(); + + var mock = Lib.extendDeep({}, require('@mocks/text_chart_arrays')); + mock.data.forEach(function(t) { + t.type = 'waterfall'; + t.base = 1000; + t.hoverinfo = 'all'; + }); + + function _hover() { + var evt = { xpx: 125, ypx: 150 }; + Fx.hover('graph', evt, 'xy'); + } + + Plotly.plot(gd, mock) + .then(_hover) + .then(function() { + assertHoverLabelContent({ + nums: [ + '1001\nHover text A\n1 ▲\nInitial: 1000', + '1002\nHover text G\n2 ▲\nInitial: 1000', + '1,001.5\na (hover)\n1.5 ▲\nInitial: 1000' + ], + name: ['Lines, Marke...', 'Lines and Text', 'missing text'], + axis: '0' + }); + }) + .catch(failTest) + .then(done); + }); + it('should use hovertemplate if specified', function(done) { gd = createGraphDiv(); var mock = Lib.extendDeep({}, require('@mocks/text_chart_arrays')); mock.data.forEach(function(t) { t.type = 'waterfall'; - t.hovertemplate = '%{y}'; + t.hovertemplate = 'Value: %{y}
SUM: %{final}
START: %{initial}
DIFF: %{delta}'; }); function _hover() { @@ -1397,11 +1456,14 @@ describe('waterfall hover', function() { .then(_hover) .then(function() { assertHoverLabelContent({ - nums: ['1', '2', '1.5'], + nums: [ + 'Value: 1\nSUM: 1\nSTART: 0\nDIFF: 1', + 'Value: 2\nSUM: 2\nSTART: 0\nDIFF: 2', + 'Value: 1.5\nSUM: 1.5\nSTART: 0\nDIFF: 1.5' + ], name: ['', '', ''], axis: '0' }); - // return Plotly.restyle(gd, 'text', ['APPLE', 'BANANA', 'ORANGE']); }) .catch(failTest) .then(done);