From b19d960f5ed33f5026f2320b53734a18f17bde1f Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 10 May 2021 11:41:06 -0400 Subject: [PATCH 1/8] drop hover and spike modebars --- src/components/modebar/manage.js | 35 +------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index 042be085ebe..de6138c32fb 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -3,7 +3,6 @@ var axisIds = require('../../plots/cartesian/axis_ids'); var scatterSubTypes = require('../../traces/scatter/subtypes'); var Registry = require('../../registry'); -var isUnifiedHover = require('../fx/helpers').isUnifiedHover; var createModeBar = require('./modebar'); var modeBarButtons = require('./buttons'); @@ -86,7 +85,6 @@ function getButtonGroups(gd) { var hasPolar = fullLayout._has('polar'); var hasSankey = fullLayout._has('sankey'); var allAxesFixed = areAllAxesFixed(fullLayout); - var hasUnifiedHoverLabel = isUnifiedHover(fullLayout.hovermode); var groups = []; @@ -111,45 +109,23 @@ function getButtonGroups(gd) { addGroup(commonGroup); var zoomGroup = []; - var hoverGroup = []; var resetGroup = []; var dragModeGroup = []; if((hasCartesian || hasGL2D || hasPie || hasFunnelarea || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) { // graphs with more than one plot types get 'union buttons' - // which reset the view or toggle hover labels across all subplots. - hoverGroup = ['toggleHover']; + // which reset the view across all subplots. resetGroup = ['resetViews']; } else if(hasGeo) { zoomGroup = ['zoomInGeo', 'zoomOutGeo']; - hoverGroup = ['hoverClosestGeo']; resetGroup = ['resetGeo']; } else if(hasGL3D) { - hoverGroup = ['hoverClosest3d']; resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d']; } else if(hasMapbox) { zoomGroup = ['zoomInMapbox', 'zoomOutMapbox']; - hoverGroup = ['toggleHover']; resetGroup = ['resetViewMapbox']; - } else if(hasGL2D) { - hoverGroup = ['hoverClosestGl2d']; - } else if(hasPie) { - hoverGroup = ['hoverClosestPie']; } else if(hasSankey) { - hoverGroup = ['hoverClosestCartesian', 'hoverCompareCartesian']; resetGroup = ['resetViewSankey']; - } else { // hasPolar, hasTernary - // always show at least one hover icon. - hoverGroup = ['toggleHover']; - } - // if we have cartesian, allow switching between closest and compare - // regardless of what other types are on the plot, since they'll all - // just treat any truthy hovermode as 'closest' - if(hasCartesian) { - hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian']; - } - if(hasNoHover(fullData) || hasUnifiedHoverLabel) { - hoverGroup = []; } if((hasCartesian || hasGL2D) && !allAxesFixed) { @@ -191,7 +167,6 @@ function getButtonGroups(gd) { addGroup(dragModeGroup); addGroup(zoomGroup.concat(resetGroup)); - addGroup(hoverGroup); return appendButtonsToGroups(groups, buttonsToAdd); } @@ -240,14 +215,6 @@ function isSelectable(fullData) { return selectable; } -// check whether all trace are 'noHover' -function hasNoHover(fullData) { - for(var i = 0; i < fullData.length; i++) { - if(!Registry.traceIs(fullData[i], 'noHover')) return false; - } - return true; -} - function appendButtonsToGroups(groups, buttons) { if(buttons.length) { if(Array.isArray(buttons[0])) { From 992352b9f38ffec9c80b4e55aef63b59a873edb1 Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 10 May 2021 14:47:35 -0400 Subject: [PATCH 2/8] add hover and spike modes to extra modes that could be enabled --- src/components/modebar/manage.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index de6138c32fb..b490c5ef7df 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -66,6 +66,25 @@ var DRAW_MODES = [ 'eraseshape' ]; +var HOVER_MODES = [ + 'hoverCompareCartesian', + 'hoverClosestCartesian', + 'hoverClosestGl2d', + 'hoverClosest3d', + 'hoverClosestGeo', + 'hoverClosestPie', + 'toggleHover' +]; + +var SPIKE_MODES = [ + 'toggleSpikelines' +]; + +var EXTRA_MODES = [] + .concat(DRAW_MODES) + .concat(HOVER_MODES) + .concat(SPIKE_MODES); + // logic behind which buttons are displayed by default function getButtonGroups(gd) { var fullLayout = gd._fullLayout; @@ -152,8 +171,9 @@ function getButtonGroups(gd) { for(var i = 0; i < buttonsToAdd.length; i++) { var b = buttonsToAdd[i]; if(typeof b === 'string') { - if(DRAW_MODES.indexOf(b) !== -1) { + if(EXTRA_MODES.indexOf(b) !== -1) { if( + DRAW_MODES.indexOf(b) === -1 || fullLayout._has('mapbox') || // draw shapes in paper coordinate (could be improved in future to support data coordinate, when there is no pitch) fullLayout._has('cartesian') // draw shapes in data coordinate ) { From f7d5a3a269007779537a1ec47800c6b81e79516e Mon Sep 17 00:00:00 2001 From: archmoj Date: Mon, 10 May 2021 16:19:27 -0400 Subject: [PATCH 3/8] adjust tests --- test/jasmine/tests/gl3d_plot_interact_test.js | 3 + test/jasmine/tests/modebar_test.js | 144 +++++++----------- test/jasmine/tests/plot_api_test.js | 2 +- 3 files changed, 55 insertions(+), 94 deletions(-) diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index 92913a5c769..2d48ca4986d 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -455,6 +455,9 @@ describe('Test gl3d modebar handlers - perspective case', function() { }, aspectratio: { x: 3, y: 2, z: 1 } } + }, + config: { + modeBarButtonsToAdd: ['hoverClosest3d'] } }; diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index 674a5a8eb55..38b3a19f255 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -342,8 +342,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom2d', 'pan2d'], - ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'] ]); var gd = getMockGraphInfo(['x'], ['y']); @@ -361,8 +360,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom2d', 'pan2d', 'select2d', 'lasso2d'], - ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'] ]); var gd = getMockGraphInfo(['x'], ['y']); @@ -385,8 +383,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom2d', 'pan2d', 'select2d', 'lasso2d'], - ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'] ]); var gd = getMockGraphInfo(['x'], ['y']); @@ -407,8 +404,7 @@ describe('ModeBar', function() { it('creates mode bar (cartesian fixed-axes version)', function() { var buttons = getButtons([ - ['toImage'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['toImage'] ]); var gd = getMockGraphInfo(); @@ -425,8 +421,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'], - ['resetCameraDefault3d', 'resetCameraLastSave3d'], - ['hoverClosest3d'] + ['resetCameraDefault3d', 'resetCameraLastSave3d'] ]); var gd = getMockGraphInfo(); @@ -443,8 +438,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['pan2d'], - ['zoomInGeo', 'zoomOutGeo', 'resetGeo'], - ['hoverClosestGeo'] + ['zoomInGeo', 'zoomOutGeo', 'resetGeo'] ]); var gd = getMockGraphInfo(); @@ -461,8 +455,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['pan2d', 'select2d', 'lasso2d'], - ['zoomInGeo', 'zoomOutGeo', 'resetGeo'], - ['hoverClosestGeo'] + ['zoomInGeo', 'zoomOutGeo', 'resetGeo'] ]); var gd = getMockGraphInfo(); @@ -484,8 +477,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['pan2d'], - ['zoomInMapbox', 'zoomOutMapbox', 'resetViewMapbox'], - ['toggleHover'] + ['zoomInMapbox', 'zoomOutMapbox', 'resetViewMapbox'] ]); var gd = getMockGraphInfo(); @@ -502,8 +494,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['pan2d', 'select2d', 'lasso2d'], - ['zoomInMapbox', 'zoomOutMapbox', 'resetViewMapbox'], - ['toggleHover'] + ['zoomInMapbox', 'zoomOutMapbox', 'resetViewMapbox'] ]); var gd = getMockGraphInfo(); @@ -525,8 +516,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom2d', 'pan2d'], - ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], - ['hoverClosestGl2d'] + ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'] ]); var gd = getMockGraphInfo(['x'], ['y']); @@ -542,8 +532,7 @@ describe('ModeBar', function() { it('creates mode bar (pie version)', function() { var buttons = getButtons([ - ['toImage'], - ['hoverClosestPie'] + ['toImage'] ]); var gd = getMockGraphInfo(); @@ -560,8 +549,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'], - ['resetViews'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['resetViews'] ]); var gd = getMockGraphInfo(); @@ -578,8 +566,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom2d', 'pan2d'], - ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetViews'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetViews'] ]); var gd = getMockGraphInfo(['x'], ['y']); @@ -597,8 +584,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom2d', 'pan2d', 'select2d', 'lasso2d'], - ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetViews'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetViews'] ]); var gd = getMockGraphInfo(['x'], ['y']); @@ -621,8 +607,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom2d', 'pan2d', 'select2d', 'lasso2d'], - ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['zoomIn2d', 'zoomOut2d', 'autoScale2d', 'resetScale2d'] ]); var gd = getMockGraphInfo(['x'], ['y']); @@ -645,8 +630,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'], - ['resetViews'], - ['toggleHover'] + ['resetViews'] ]); var gd = getMockGraphInfo(); @@ -662,8 +646,7 @@ describe('ModeBar', function() { it('creates mode bar (un-selectable ternary version)', function() { var buttons = getButtons([ ['toImage'], - ['zoom2d', 'pan2d'], - ['toggleHover'] + ['zoom2d', 'pan2d'] ]); var gd = getMockGraphInfo(); @@ -679,8 +662,7 @@ describe('ModeBar', function() { it('creates mode bar (selectable ternary version)', function() { var buttons = getButtons([ ['toImage'], - ['zoom2d', 'pan2d', 'select2d', 'lasso2d'], - ['toggleHover'] + ['zoom2d', 'pan2d', 'select2d', 'lasso2d'] ]); var gd = getMockGraphInfo(); @@ -701,8 +683,7 @@ describe('ModeBar', function() { it('creates mode bar (ternary + cartesian version)', function() { var buttons = getButtons([ ['toImage'], - ['zoom2d', 'pan2d'], - ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian'] + ['zoom2d', 'pan2d'] ]); var gd = getMockGraphInfo(); @@ -719,8 +700,7 @@ describe('ModeBar', function() { var buttons = getButtons([ ['toImage'], ['zoom3d', 'pan3d', 'orbitRotation', 'tableRotation'], - ['resetViews'], - ['toggleHover'] + ['resetViews'] ]); var gd = getMockGraphInfo(); @@ -733,36 +713,6 @@ describe('ModeBar', function() { checkButtons(modeBar, buttons, 1); }); - it('creates mode bar without hover button when all traces are noHover', function() { - var buttons = getButtons([ - ['toImage'] - ]); - - var gd = getMockGraphInfo(); - gd._fullData = [{ type: 'indicator' }]; - - manageModeBar(gd); - var modeBar = gd._fullLayout._modeBar; - - checkButtons(modeBar, buttons, 1); - }); - - it('creates mode bar with hover button even in the presence of one noHover trace', function() { - var buttons = getButtons([ - ['toImage'], - ['hoverClosestPie'] - ]); - - var gd = getMockGraphInfo(); - gd._fullLayout._basePlotModules = [{ name: 'pie' }]; - gd._fullData = [{ type: 'indicator' }, {type: 'pie'}]; - - manageModeBar(gd); - var modeBar = gd._fullLayout._modeBar; - - checkButtons(modeBar, buttons, 1); - }); - it('throws an error if modeBarButtonsToRemove isn\'t an array', function() { var gd = getMockGraphInfo(); gd._context.modeBarButtonsToRemove = 'not gonna work'; @@ -811,32 +761,28 @@ describe('ModeBar', function() { gd._fullData = [{type: 'pie'}]; manageModeBar(gd); checkButtons(gd._fullLayout._modeBar, getButtons([ - ['toImage'], - ['hoverClosestPie'] + ['toImage'] ]), 1); gd._context.showSendToCloud = true; gd._context.showEditInChartStudio = false; manageModeBar(gd); checkButtons(gd._fullLayout._modeBar, getButtons([ - ['toImage', 'sendDataToCloud'], - ['hoverClosestPie'] + ['toImage', 'sendDataToCloud'] ]), 1); gd._context.showSendToCloud = false; gd._context.showEditInChartStudio = true; manageModeBar(gd); checkButtons(gd._fullLayout._modeBar, getButtons([ - ['toImage', 'editInChartStudio'], - ['hoverClosestPie'] + ['toImage', 'editInChartStudio'] ]), 1); gd._context.showSendToCloud = true; gd._context.showEditInChartStudio = true; manageModeBar(gd); checkButtons(gd._fullLayout._modeBar, getButtons([ - ['toImage', 'editInChartStudio'], - ['hoverClosestPie'] + ['toImage', 'editInChartStudio'] ]), 1); }); @@ -863,13 +809,13 @@ describe('ModeBar', function() { var gd = setupGraphInfo(); manageModeBar(gd); - expect(countButtons(gd._fullLayout._modeBar)).toEqual(11); + expect(countButtons(gd._fullLayout._modeBar)).toEqual(8); - gd._fullLayout._basePlotModules = [{ name: 'gl3d' }]; - gd._fullData = [{type: 'scatter3d'}]; + gd._fullLayout._basePlotModules = [{ name: 'geo' }]; + gd._fullData = [{type: 'scattergeo'}]; manageModeBar(gd); - expect(countButtons(gd._fullLayout._modeBar)).toEqual(9); + expect(countButtons(gd._fullLayout._modeBar)).toEqual(6); }); it('updates mode bar buttons if modeBarButtonsToRemove changes', function() { @@ -906,7 +852,7 @@ describe('ModeBar', function() { it('sets up buttons with modeBarButtonsToAdd and modeBarButtonToRemove', function() { var gd = setupGraphInfo(); gd._context.modeBarButtonsToRemove = [ - 'toImage', 'pan2d', 'hoverCompareCartesian' + 'toImage', 'pan2d' ]; gd._context.modeBarButtonsToAdd = [ { name: 'some button', click: noop }, @@ -916,14 +862,14 @@ describe('ModeBar', function() { manageModeBar(gd); var modeBar = gd._fullLayout._modeBar; - expect(countGroups(modeBar)).toEqual(6); - expect(countButtons(modeBar)).toEqual(10); + expect(countGroups(modeBar)).toEqual(5); + expect(countButtons(modeBar)).toEqual(8); }); it('sets up buttons with modeBarButtonsToAdd and modeBarButtonToRemove (2)', function() { var gd = setupGraphInfo(); gd._context.modeBarButtonsToRemove = [ - 'toImage', 'pan2d', 'hoverCompareCartesian' + 'toImage', 'pan2d' ]; gd._context.modeBarButtonsToAdd = [[ { name: 'some button', click: noop }, @@ -936,8 +882,8 @@ describe('ModeBar', function() { manageModeBar(gd); var modeBar = gd._fullLayout._modeBar; - expect(countGroups(modeBar)).toEqual(7); - expect(countButtons(modeBar)).toEqual(12); + expect(countGroups(modeBar)).toEqual(6); + expect(countButtons(modeBar)).toEqual(10); }); it('sets up buttons with fully custom modeBarButtons', function() { @@ -1119,7 +1065,13 @@ describe('ModeBar', function() { }; gd = createGraphDiv(); - Plotly.newPlot(gd, mockData, mockLayout).then(function() { + Plotly.newPlot(gd, mockData, mockLayout, { + modeBarButtonsToAdd: [ + 'toggleSpikelines', + 'hoverCompareCartesian', + 'hoverClosestCartesian' + ] + }).then(function() { modeBar = gd._fullLayout._modeBar; buttonToggle = selectButton(modeBar, 'toggleSpikelines'); buttonCompare = selectButton(modeBar, 'hoverCompareCartesian'); @@ -1197,7 +1149,7 @@ describe('ModeBar', function() { }); }); - describe('buttons hoverCompareCartesian and hoverClosestCartesian ', function() { + describe('buttons hoverCompareCartesian and hoverClosestCartesian', function() { it('should update layout hovermode', function() { expect(gd._fullLayout.hovermode).toBe('closest'); assertActive(hovermodeButtons, buttonClosest); @@ -1293,7 +1245,9 @@ describe('ModeBar', function() { }]; gd = createGraphDiv(); - Plotly.newPlot(gd, mockData).then(function() { + Plotly.newPlot(gd, mockData, {}, { + modeBarButtonsToAdd: ['hoverClosestPie'] + }).then(function() { modeBar = gd._fullLayout._modeBar; done(); }); @@ -1326,7 +1280,9 @@ describe('ModeBar', function() { }]; gd = createGraphDiv(); - Plotly.newPlot(gd, mockData).then(function() { + Plotly.newPlot(gd, mockData, {}, { + modeBarButtonsToAdd: ['hoverClosestGeo'] + }).then(function() { modeBar = gd._fullLayout._modeBar; done(); }); @@ -1410,7 +1366,9 @@ describe('ModeBar', function() { Plotly.newPlot(gd, [ {type: 'scatterternary', a: [1], b: [2], c: [3]} - ]) + ], {}, { + modeBarButtonsToAdd: ['toggleHover'] + }) .then(function() { _run('base'); diff --git a/test/jasmine/tests/plot_api_test.js b/test/jasmine/tests/plot_api_test.js index 78383e283f2..4b79a98133a 100644 --- a/test/jasmine/tests/plot_api_test.js +++ b/test/jasmine/tests/plot_api_test.js @@ -2687,7 +2687,7 @@ describe('Test plot api', function() { var modebars = document.getElementsByClassName('modebar-container'); expect(modebars.length).toBe(1, msg + ' # of modebar container'); var groups = document.getElementsByClassName('modebar-group'); - expect(groups.length).toBe(5, msg + ' # of modebar button groups'); + expect(groups.length).toBe(4, msg + ' # of modebar button groups'); }; } From 74649d047866b4c1fc267de31c372a094aea2c7e Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 12 May 2021 18:01:33 -0400 Subject: [PATCH 4/8] enable hover modebars using simplified strings --- src/components/modebar/manage.js | 80 +++++++++++++------ test/jasmine/tests/gl3d_plot_interact_test.js | 4 +- test/jasmine/tests/modebar_test.js | 8 +- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index b490c5ef7df..b457657a2f1 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -3,6 +3,7 @@ var axisIds = require('../../plots/cartesian/axis_ids'); var scatterSubTypes = require('../../traces/scatter/subtypes'); var Registry = require('../../registry'); +var isUnifiedHover = require('../fx/helpers').isUnifiedHover; var createModeBar = require('./modebar'); var modeBarButtons = require('./buttons'); @@ -66,25 +67,6 @@ var DRAW_MODES = [ 'eraseshape' ]; -var HOVER_MODES = [ - 'hoverCompareCartesian', - 'hoverClosestCartesian', - 'hoverClosestGl2d', - 'hoverClosest3d', - 'hoverClosestGeo', - 'hoverClosestPie', - 'toggleHover' -]; - -var SPIKE_MODES = [ - 'toggleSpikelines' -]; - -var EXTRA_MODES = [] - .concat(DRAW_MODES) - .concat(HOVER_MODES) - .concat(SPIKE_MODES); - // logic behind which buttons are displayed by default function getButtonGroups(gd) { var fullLayout = gd._fullLayout; @@ -104,6 +86,7 @@ function getButtonGroups(gd) { var hasPolar = fullLayout._has('polar'); var hasSankey = fullLayout._has('sankey'); var allAxesFixed = areAllAxesFixed(fullLayout); + var hasUnifiedHoverLabel = isUnifiedHover(fullLayout.hovermode); var groups = []; @@ -128,23 +111,45 @@ function getButtonGroups(gd) { addGroup(commonGroup); var zoomGroup = []; + var hoverGroup = []; var resetGroup = []; var dragModeGroup = []; if((hasCartesian || hasGL2D || hasPie || hasFunnelarea || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) { // graphs with more than one plot types get 'union buttons' - // which reset the view across all subplots. + // which reset the view or toggle hover labels across all subplots. + hoverGroup = ['toggleHover']; resetGroup = ['resetViews']; } else if(hasGeo) { zoomGroup = ['zoomInGeo', 'zoomOutGeo']; + hoverGroup = ['hoverClosestGeo']; resetGroup = ['resetGeo']; } else if(hasGL3D) { + hoverGroup = ['hoverClosest3d']; resetGroup = ['resetCameraDefault3d', 'resetCameraLastSave3d']; } else if(hasMapbox) { zoomGroup = ['zoomInMapbox', 'zoomOutMapbox']; + hoverGroup = ['toggleHover']; resetGroup = ['resetViewMapbox']; + } else if(hasGL2D) { + hoverGroup = ['hoverClosestGl2d']; + } else if(hasPie) { + hoverGroup = ['hoverClosestPie']; } else if(hasSankey) { + hoverGroup = ['hoverClosestCartesian', 'hoverCompareCartesian']; resetGroup = ['resetViewSankey']; + } else { // hasPolar, hasTernary + // always show at least one hover icon. + hoverGroup = ['toggleHover']; + } + // if we have cartesian, allow switching between closest and compare + // regardless of what other types are on the plot, since they'll all + // just treat any truthy hovermode as 'closest' + if(hasCartesian) { + hoverGroup = ['toggleSpikelines', 'hoverClosestCartesian', 'hoverCompareCartesian']; + } + if(hasNoHover(fullData) || hasUnifiedHoverLabel) { + hoverGroup = []; } if((hasCartesian || hasGL2D) && !allAxesFixed) { @@ -165,20 +170,40 @@ function getButtonGroups(gd) { dragModeGroup.push('select2d', 'lasso2d'); } - // accept pre-defined buttons as string + var enabledHoverGroup = []; + var enableHover = function(a) { + // return if already added + if(enabledHoverGroup.indexOf(a) !== -1) return; + // should be in hoverGroup + if(hoverGroup.indexOf(a) !== -1) { + enabledHoverGroup.push(a); + } + }; if(Array.isArray(buttonsToAdd)) { var newList = []; for(var i = 0; i < buttonsToAdd.length; i++) { var b = buttonsToAdd[i]; if(typeof b === 'string') { - if(EXTRA_MODES.indexOf(b) !== -1) { + if(DRAW_MODES.indexOf(b) !== -1) { + // accept pre-defined drag modes i.e. shape drawing features as string if( - DRAW_MODES.indexOf(b) === -1 || fullLayout._has('mapbox') || // draw shapes in paper coordinate (could be improved in future to support data coordinate, when there is no pitch) fullLayout._has('cartesian') // draw shapes in data coordinate ) { dragModeGroup.push(b); } + } else if(b === 'toggleSpikelines') { + enableHover('toggleSpikelines'); + } else if(b === 'toggleHover') { + enableHover('toggleHover'); + } else if(b === 'hoverCompare') { + enableHover('hoverCompareCartesian'); + } else if(b === 'hoverClosest') { + enableHover('hoverClosestCartesian'); + enableHover('hoverClosestGeo'); + enableHover('hoverClosest3d'); + enableHover('hoverClosestGl2d'); + enableHover('hoverClosestPie'); } } else newList.push(b); } @@ -187,6 +212,7 @@ function getButtonGroups(gd) { addGroup(dragModeGroup); addGroup(zoomGroup.concat(resetGroup)); + addGroup(enabledHoverGroup); return appendButtonsToGroups(groups, buttonsToAdd); } @@ -235,6 +261,14 @@ function isSelectable(fullData) { return selectable; } +// check whether all trace are 'noHover' +function hasNoHover(fullData) { + for(var i = 0; i < fullData.length; i++) { + if(!Registry.traceIs(fullData[i], 'noHover')) return false; + } + return true; +} + function appendButtonsToGroups(groups, buttons) { if(buttons.length) { if(Array.isArray(buttons[0])) { diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index 2d48ca4986d..1172411397f 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -457,7 +457,7 @@ describe('Test gl3d modebar handlers - perspective case', function() { } }, config: { - modeBarButtonsToAdd: ['hoverClosest3d'] + modeBarButtonsToAdd: ['hoverClosest'] } }; @@ -537,7 +537,7 @@ describe('Test gl3d modebar handlers - perspective case', function() { expect(buttonOrbit.isActive()).toBe(false); }); - it('@gl button hoverClosest3d should update the scene hovermode and spikes', function() { + it('@gl button hoverClosest should update the scene hovermode and spikes', function() { var buttonHover = selectButton(modeBar, 'hoverClosest3d'); assertScenes(gd._fullLayout, 'hovermode', 'closest'); diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index 38b3a19f255..8c560c5d6f1 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -1068,8 +1068,8 @@ describe('ModeBar', function() { Plotly.newPlot(gd, mockData, mockLayout, { modeBarButtonsToAdd: [ 'toggleSpikelines', - 'hoverCompareCartesian', - 'hoverClosestCartesian' + 'hoverCompare', + 'hoverClosest' ] }).then(function() { modeBar = gd._fullLayout._modeBar; @@ -1246,7 +1246,7 @@ describe('ModeBar', function() { gd = createGraphDiv(); Plotly.newPlot(gd, mockData, {}, { - modeBarButtonsToAdd: ['hoverClosestPie'] + modeBarButtonsToAdd: ['hoverClosest'] }).then(function() { modeBar = gd._fullLayout._modeBar; done(); @@ -1281,7 +1281,7 @@ describe('ModeBar', function() { gd = createGraphDiv(); Plotly.newPlot(gd, mockData, {}, { - modeBarButtonsToAdd: ['hoverClosestGeo'] + modeBarButtonsToAdd: ['hoverClosest'] }).then(function() { modeBar = gd._fullLayout._modeBar; done(); From 3602a51f3eca365181e2b345203f5f094bf2b7e0 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 12 May 2021 18:08:50 -0400 Subject: [PATCH 5/8] use lowercase for exposed hover button names --- src/components/modebar/manage.js | 8 ++++---- test/jasmine/tests/gl3d_plot_interact_test.js | 2 +- test/jasmine/tests/modebar_test.js | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index b457657a2f1..af0600b0bad 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -192,13 +192,13 @@ function getButtonGroups(gd) { ) { dragModeGroup.push(b); } - } else if(b === 'toggleSpikelines') { + } else if(b === 'togglespikelines') { enableHover('toggleSpikelines'); - } else if(b === 'toggleHover') { + } else if(b === 'togglehover') { enableHover('toggleHover'); - } else if(b === 'hoverCompare') { + } else if(b === 'hovercompare') { enableHover('hoverCompareCartesian'); - } else if(b === 'hoverClosest') { + } else if(b === 'hoverclosest') { enableHover('hoverClosestCartesian'); enableHover('hoverClosestGeo'); enableHover('hoverClosest3d'); diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index 1172411397f..d8bcef9bb21 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -457,7 +457,7 @@ describe('Test gl3d modebar handlers - perspective case', function() { } }, config: { - modeBarButtonsToAdd: ['hoverClosest'] + modeBarButtonsToAdd: ['hoverclosest'] } }; diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index 8c560c5d6f1..695e99a6dce 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -1067,9 +1067,9 @@ describe('ModeBar', function() { gd = createGraphDiv(); Plotly.newPlot(gd, mockData, mockLayout, { modeBarButtonsToAdd: [ - 'toggleSpikelines', - 'hoverCompare', - 'hoverClosest' + 'togglespikelines', + 'hovercompare', + 'hoverclosest' ] }).then(function() { modeBar = gd._fullLayout._modeBar; @@ -1246,7 +1246,7 @@ describe('ModeBar', function() { gd = createGraphDiv(); Plotly.newPlot(gd, mockData, {}, { - modeBarButtonsToAdd: ['hoverClosest'] + modeBarButtonsToAdd: ['hoverclosest'] }).then(function() { modeBar = gd._fullLayout._modeBar; done(); @@ -1281,7 +1281,7 @@ describe('ModeBar', function() { gd = createGraphDiv(); Plotly.newPlot(gd, mockData, {}, { - modeBarButtonsToAdd: ['hoverClosest'] + modeBarButtonsToAdd: ['hoverclosest'] }).then(function() { modeBar = gd._fullLayout._modeBar; done(); @@ -1367,7 +1367,7 @@ describe('ModeBar', function() { Plotly.newPlot(gd, [ {type: 'scatterternary', a: [1], b: [2], c: [3]} ], {}, { - modeBarButtonsToAdd: ['toggleHover'] + modeBarButtonsToAdd: ['togglehover'] }) .then(function() { _run('base'); From cd6c5a10f12fb1337d0f433d1d2beb0933557f35 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 12 May 2021 18:16:30 -0400 Subject: [PATCH 6/8] bring back and adjust two noHover tests --- test/jasmine/tests/modebar_test.js | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index 695e99a6dce..4277a9daa07 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -713,6 +713,38 @@ describe('ModeBar', function() { checkButtons(modeBar, buttons, 1); }); + it('creates mode bar without hover button when all traces are noHover', function() { + var buttons = getButtons([ + ['toImage'] + ]); + + var gd = getMockGraphInfo(); + gd._context.modeBarButtonsToAdd = ['hoverclosest']; + gd._fullData = [{ type: 'indicator' }]; + + manageModeBar(gd); + var modeBar = gd._fullLayout._modeBar; + + checkButtons(modeBar, buttons, 1); + }); + + it('creates mode bar with hover button even in the presence of one noHover trace', function() { + var buttons = getButtons([ + ['toImage'], + ['hoverClosestPie'] + ]); + + var gd = getMockGraphInfo(); + gd._context.modeBarButtonsToAdd = ['hoverclosest']; + gd._fullLayout._basePlotModules = [{ name: 'pie' }]; + gd._fullData = [{ type: 'indicator' }, {type: 'pie'}]; + + manageModeBar(gd); + var modeBar = gd._fullLayout._modeBar; + + checkButtons(modeBar, buttons, 1); + }); + it('throws an error if modeBarButtonsToRemove isn\'t an array', function() { var gd = getMockGraphInfo(); gd._context.modeBarButtonsToRemove = 'not gonna work'; From 2d4bc789ab29c198f3f3b38c797f5fff1abf3edb Mon Sep 17 00:00:00 2001 From: archmoj Date: Thu, 13 May 2021 09:03:12 -0400 Subject: [PATCH 7/8] accept uppercase and camelCase strings modebar button strings --- src/components/modebar/manage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index af0600b0bad..90a10fb7da6 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -184,6 +184,8 @@ function getButtonGroups(gd) { for(var i = 0; i < buttonsToAdd.length; i++) { var b = buttonsToAdd[i]; if(typeof b === 'string') { + b = b.toLowerCase(); + if(DRAW_MODES.indexOf(b) !== -1) { // accept pre-defined drag modes i.e. shape drawing features as string if( From f4e6120f8b4595ee44db9ce14971a1ad24a803c1 Mon Sep 17 00:00:00 2001 From: archmoj Date: Thu, 13 May 2021 15:51:37 -0400 Subject: [PATCH 8/8] add v1hovermode option --- src/components/modebar/manage.js | 8 ++++++++ test/jasmine/tests/modebar_test.js | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index 90a10fb7da6..1e4f9f8fc50 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -206,6 +206,14 @@ function getButtonGroups(gd) { enableHover('hoverClosest3d'); enableHover('hoverClosestGl2d'); enableHover('hoverClosestPie'); + } else if(b === 'v1hovermode') { + enableHover('toggleHover'); + enableHover('hoverClosestCartesian'); + enableHover('hoverCompareCartesian'); + enableHover('hoverClosestGeo'); + enableHover('hoverClosest3d'); + enableHover('hoverClosestGl2d'); + enableHover('hoverClosestPie'); } } else newList.push(b); } diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index 4277a9daa07..5c0457d602a 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -1100,8 +1100,7 @@ describe('ModeBar', function() { Plotly.newPlot(gd, mockData, mockLayout, { modeBarButtonsToAdd: [ 'togglespikelines', - 'hovercompare', - 'hoverclosest' + 'v1hovermode' ] }).then(function() { modeBar = gd._fullLayout._modeBar;