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);