diff --git a/src/traces/funnel/attributes.js b/src/traces/funnel/attributes.js index 045ea7bca02..5e5f5f2ed27 100644 --- a/src/traces/funnel/attributes.js +++ b/src/traces/funnel/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'); @@ -22,7 +25,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', 'percent initial', 'percent previous', 'percent total'] + }), textinfo: { valType: 'flaglist', diff --git a/src/traces/funnel/constants.js b/src/traces/funnel/constants.js new file mode 100644 index 00000000000..590f59a2b2c --- /dev/null +++ b/src/traces/funnel/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: [ + 'percentInitial', + 'percentPrevious', + 'percentTotal' + ] +}; diff --git a/src/traces/funnel/event_data.js b/src/traces/funnel/event_data.js new file mode 100644 index 00000000000..ae94e3d28a0 --- /dev/null +++ b/src/traces/funnel/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('percentInitial' in pt) out.percentInitial = pt.percentInitial; + if('percentPrevious' in pt) out.percentPrevious = pt.percentPrevious; + if('percentTotal' in pt) out.percentTotal = pt.percentTotal; + + if(pt.xa) out.xaxis = pt.xa; + if(pt.ya) out.yaxis = pt.ya; + + return out; +}; diff --git a/src/traces/funnel/hover.js b/src/traces/funnel/hover.js index 42935a87412..cbc0f0e7015 100644 --- a/src/traces/funnel/hover.js +++ b/src/traces/funnel/hover.js @@ -25,16 +25,36 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var di = cd[index]; var sizeLetter = isHorizontal ? 'x' : 'y'; - point[sizeLetter + 'LabelVal'] = di.s; - // display ratio to initial value - point.extraText = [ - formatPercent(di.begR, 1) + ' of initial', - formatPercent(di.difR, 1) + ' of previous', - formatPercent(di.sumR, 1) + ' of total' - ].join('
'); - // TODO: Should we use pieHelpers.formatPieValue instead ? + point.percentInitial = di.begR; + point.percentInitialLabel = formatPercent(di.begR, 1); + + point.percentPrevious = di.difR; + point.percentPreviousLabel = formatPercent(di.difR, 1); + + point.percentTotal = di.sumR; + point.percentTotalLabel = formatPercent(di.sumR, 1); + + 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(hasFlag('percent initial')) { + text.push(point.percentInitialLabel + ' of initial'); + } + if(hasFlag('percent previous')) { + text.push(point.percentPreviousLabel + ' of previous'); + } + if(hasFlag('percent total')) { + text.push(point.percentTotalLabel + ' of total'); + } + } + point.extraText = text.join('
'); point.color = getTraceColor(trace, di); diff --git a/src/traces/funnel/index.js b/src/traces/funnel/index.js index 844f4097471..d63618e5ec4 100644 --- a/src/traces/funnel/index.js +++ b/src/traces/funnel/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/funnel_test.js b/test/jasmine/tests/funnel_test.js index 1368f46efb6..cb3861833ae 100644 --- a/test/jasmine/tests/funnel_test.js +++ b/test/jasmine/tests/funnel_test.js @@ -1326,6 +1326,66 @@ describe('funnel hover', function() { .then(done); }); + it('should turn off percentages 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 = 'funnel'; + t.orientation = 'v'; + 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 percentages with hoveinfo all', function(done) { + gd = createGraphDiv(); + + var mock = Lib.extendDeep({}, require('@mocks/text_chart_arrays')); + mock.data.forEach(function(t) { + t.type = 'funnel'; + t.orientation = 'v'; + 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: [ + '1\nHover text A\n100% of initial\n100% of previous\n33.3% of total', + '2\nHover text G\n100% of initial\n100% of previous\n33.3% of total', + '1.5\na (hover)\n100% of initial\n100% of previous\n33.3% of total' + ], + name: ['Lines, Marke...', 'Lines and Text', 'missing text'], + axis: '0' + }); + }) + .catch(failTest) + .then(done); + }); + it('should use hovertemplate if specified', function(done) { gd = createGraphDiv(); @@ -1333,7 +1393,7 @@ describe('funnel hover', function() { mock.data.forEach(function(t) { t.type = 'funnel'; t.orientation = 'v'; - t.hovertemplate = '%{y}'; + t.hovertemplate = 'Value: %{y}
Total percentage: %{percentTotal}
Initial percentage: %{percentInitial}
Previous percentage: %{percentPrevious}'; }); function _hover() { @@ -1345,11 +1405,14 @@ describe('funnel hover', function() { .then(_hover) .then(function() { assertHoverLabelContent({ - nums: ['1', '2', '1.5'], + nums: [ + 'Value: 1\nTotal percentage: 33.3%\nInitial percentage: 100%\nPrevious percentage: 100%', + 'Value: 2\nTotal percentage: 33.3%\nInitial percentage: 100%\nPrevious percentage: 100%', + 'Value: 1.5\nTotal percentage: 33.3%\nInitial percentage: 100%\nPrevious percentage: 100%' + ], name: ['', '', ''], axis: '0' }); - // return Plotly.restyle(gd, 'text', ['APPLE', 'BANANA', 'ORANGE']); }) .catch(failTest) .then(done);