From e34618bfa24b293faba43d7c9986c3a67bf81293 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 8 Feb 2025 11:03:42 -0500 Subject: [PATCH 1/5] new cartesian axis modebarbuttons attribute --- src/components/modebar/buttons.js | 7 ++- src/components/modebar/manage.js | 2 +- src/plots/cartesian/layout_attributes.js | 12 +++++ src/plots/cartesian/layout_defaults.js | 3 ++ test/jasmine/tests/modebar_test.js | 56 ++++++++++++++++++++++++ test/plot-schema.json | 28 ++++++++++++ 6 files changed, 105 insertions(+), 3 deletions(-) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 82c5dff9a48..6ed0e0178f4 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -256,12 +256,15 @@ function handleCartesian(gd, ev) { var mag = (val === 'in') ? 0.5 : 2; var r0 = (1 + mag) / 2; var r1 = (1 - mag) / 2; - var axName; + var axName, allowed; for(i = 0; i < axList.length; i++) { ax = axList[i]; + allowed = ax.modebarbuttons === 'all' || ax.modebarbuttons.indexOf( + (val === 'auto' || val === 'reset') ? 'auto' : 'inout' + ) !== -1; - if(!ax.fixedrange) { + if(allowed && !ax.fixedrange) { axName = ax._name; if(val === 'auto') { aobj[axName + '.autorange'] = true; diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index 1683b082617..6607175762e 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -269,7 +269,7 @@ function areAllAxesFixed(fullLayout) { var axList = axisIds.list({_fullLayout: fullLayout}, null, true); for(var i = 0; i < axList.length; i++) { - if(!axList[i].fixedrange) { + if(!axList[i].fixedrange && axList[i].modebarbuttons !== 'none') { return false; } } diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index 32afc99457a..c3e4d7fe976 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -389,6 +389,18 @@ module.exports = { 'If true, then zoom is disabled.' ].join(' ') }, + modebarbuttons: { + valType: 'flaglist', + flags: ['auto', 'inout'], + extras: ['all', 'none'], + dflt: 'all', + editType: 'modebar', + description: [ + 'Determines which modebar buttons impact this axis.', + '*auto* allows the autoscale buttons, *inout* allows the', + 'zoom-in and zoom-out buttons.' + ].join(' ') + }, insiderange: { valType: 'info_array', items: [ diff --git a/src/plots/cartesian/layout_defaults.js b/src/plots/cartesian/layout_defaults.js index 90351ecfa1b..dc929142646 100644 --- a/src/plots/cartesian/layout_defaults.js +++ b/src/plots/cartesian/layout_defaults.js @@ -338,6 +338,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { }); coerce('fixedrange'); + coerce('modebarbuttons'); addMissingMatchedAxis(); @@ -366,6 +367,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { } coerce('fixedrange'); + coerce('modebarbuttons'); } for(i = 0; i < yNames.length; i++) { @@ -378,6 +380,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { var fixedRangeDflt = getComponentMethod('rangeslider', 'isVisible')(anchoredAxis); coerce('fixedrange', fixedRangeDflt); + coerce('modebarbuttons'); } // Finally, handle scale constraints and matching axes. diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index 0a9ea06c52a..e3c32c7f5db 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -1193,6 +1193,62 @@ describe('ModeBar', function() { assertRange('xaxis2', [-1, 4]); assertRange('yaxis2', [0, 4]); }); + + it('should respect modebarbuttons attribute', function(done) { + Plotly.relayout(gd, { + 'xaxis.modebarbuttons': 'none', + 'xaxis2.modebarbuttons': 'auto', + 'yaxis.modebarbuttons': 'inout', + }).then(function() { + var buttonZoomIn = selectButton(modeBar, 'zoomIn2d'); + var buttonZoomOut = selectButton(modeBar, 'zoomOut2d'); + + assertRange('xaxis', ['2016-01-01', '2016-04-01']); + assertRange('yaxis', [1, 3]); + assertRange('xaxis2', [-1, 4]); + assertRange('yaxis2', [0, 4]); + + // xaxis and xaxis2 should not be affected by zoom in/out + // yaxis and yaxis2 should be affected as in previous test + buttonZoomIn.click(); + assertRange('xaxis', ['2016-01-01', '2016-04-01']); + assertRange('yaxis', [1.5, 2.5]); + assertRange('xaxis2', [-1, 4]); + assertRange('yaxis2', [1, 3]); + + buttonZoomOut.click(); + assertRange('xaxis', ['2016-01-01', '2016-04-01']); + assertRange('yaxis', [1, 3]); + assertRange('xaxis2', [-1, 4]); + assertRange('yaxis2', [0, 4]); + + return Plotly.relayout(gd, { + 'xaxis.range': ['2016-01-23 17:45', '2016-03-09 05:15'], + 'yaxis.range': [1.5, 2.5], + 'xaxis2.range': [0.25, 2.75], + 'yaxis2.range': [1, 3], + }); + }) + .then(function() { + var buttonAutoScale = selectButton(modeBar, 'autoScale2d'); + var buttonResetScale = selectButton(modeBar, 'resetScale2d'); + + // xaxis and yaxis should not be affected by autorange + // xaxis2 and yaxis2 should be affected as in previous test + buttonAutoScale.click(); + assertRange('xaxis', ['2016-01-23 17:45', '2016-03-09 05:15']); + assertRange('yaxis', [1.5, 2.5]); + assertRange('xaxis2', [-0.5, 2.5]); + assertRange('yaxis2', [0, 2.105263]); + + buttonResetScale.click(); + assertRange('xaxis', ['2016-01-23 17:45', '2016-03-09 05:15']); + assertRange('yaxis', [1.5, 2.5]); + assertRange('xaxis2', [-1, 4]); + assertRange('yaxis2', [0, 4]); + }) + .then(done, done.fail) + }); }); describe('buttons zoom2d, pan2d, select2d and lasso2d', function() { diff --git a/test/plot-schema.json b/test/plot-schema.json index 6aa77cf3338..2172bc4431d 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -13912,6 +13912,20 @@ "allticks" ] }, + "modebarbuttons": { + "description": "Determines which modebar buttons impact this axis. *auto* allows the autoscale buttons, *inout* allows the zoom-in and zoom-out buttons.", + "dflt": "all", + "editType": "modebar", + "extras": [ + "all", + "none" + ], + "flags": [ + "auto", + "inout" + ], + "valType": "flaglist" + }, "nticks": { "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", "dflt": 0, @@ -15463,6 +15477,20 @@ "allticks" ] }, + "modebarbuttons": { + "description": "Determines which modebar buttons impact this axis. *auto* allows the autoscale buttons, *inout* allows the zoom-in and zoom-out buttons.", + "dflt": "all", + "editType": "modebar", + "extras": [ + "all", + "none" + ], + "flags": [ + "auto", + "inout" + ], + "valType": "flaglist" + }, "nticks": { "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", "dflt": 0, From 99a0f1231c4579d0d7f801c9717895b39c31fb2f Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 8 Feb 2025 11:14:59 -0500 Subject: [PATCH 2/5] draftlog for 7358 --- draftlogs/7358_add.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7358_add.md diff --git a/draftlogs/7358_add.md b/draftlogs/7358_add.md new file mode 100644 index 00000000000..a85b49dd665 --- /dev/null +++ b/draftlogs/7358_add.md @@ -0,0 +1 @@ +- Add modebarbuttons attribute to cartesian axes, to allow fine control over which buttons affect which axes [[#7358](https://github.com/plotly/plotly.js/pull/7358)] From b252c883855d7c89496bcdfe294cea41ffaf767a Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 18 Jul 2025 12:07:45 -0400 Subject: [PATCH 3/5] reverse axis modebar disabling logic and update names --- src/components/modebar/buttons.js | 6 ++--- src/components/modebar/manage.js | 3 ++- src/plots/cartesian/layout_attributes.js | 14 ++++++------ src/plots/cartesian/layout_defaults.js | 6 ++--- test/jasmine/tests/modebar_test.js | 8 +++---- test/plot-schema.json | 28 ------------------------ 6 files changed, 19 insertions(+), 46 deletions(-) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 6ed0e0178f4..660a85caf58 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -260,9 +260,9 @@ function handleCartesian(gd, ev) { for(i = 0; i < axList.length; i++) { ax = axList[i]; - allowed = ax.modebarbuttons === 'all' || ax.modebarbuttons.indexOf( - (val === 'auto' || val === 'reset') ? 'auto' : 'inout' - ) !== -1; + allowed = ax.modebardisable === 'none' || ax.modebardisable.indexOf( + (val === 'auto' || val === 'reset') ? 'autoscale' : 'zoominout' + ) === -1; if(allowed && !ax.fixedrange) { axName = ax._name; diff --git a/src/components/modebar/manage.js b/src/components/modebar/manage.js index 6607175762e..3c9a4626192 100644 --- a/src/components/modebar/manage.js +++ b/src/components/modebar/manage.js @@ -269,7 +269,8 @@ function areAllAxesFixed(fullLayout) { var axList = axisIds.list({_fullLayout: fullLayout}, null, true); for(var i = 0; i < axList.length; i++) { - if(!axList[i].fixedrange && axList[i].modebarbuttons !== 'none') { + var disabled = axList[i].modebardisable; + if(!axList[i].fixedrange && disabled !== 'autoscale+zoominout' && disabled !== 'zoominout+autoscale') { return false; } } diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index c3e4d7fe976..3e9ae6a1c4e 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -389,16 +389,16 @@ module.exports = { 'If true, then zoom is disabled.' ].join(' ') }, - modebarbuttons: { + modebardisable: { valType: 'flaglist', - flags: ['auto', 'inout'], - extras: ['all', 'none'], - dflt: 'all', + flags: ['autoscale', 'zoominout'], + extras: ['none'], + dflt: 'none', editType: 'modebar', description: [ - 'Determines which modebar buttons impact this axis.', - '*auto* allows the autoscale buttons, *inout* allows the', - 'zoom-in and zoom-out buttons.' + 'Disables certain modebar buttons for this axis.', + '*autoscale* disables the autoscale buttons, *zoominout*', + 'disables the zoom-in and zoom-out buttons.' ].join(' ') }, insiderange: { diff --git a/src/plots/cartesian/layout_defaults.js b/src/plots/cartesian/layout_defaults.js index dc929142646..965b971e4f7 100644 --- a/src/plots/cartesian/layout_defaults.js +++ b/src/plots/cartesian/layout_defaults.js @@ -338,7 +338,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { }); coerce('fixedrange'); - coerce('modebarbuttons'); + coerce('modebardisable'); addMissingMatchedAxis(); @@ -367,7 +367,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { } coerce('fixedrange'); - coerce('modebarbuttons'); + coerce('modebardisable'); } for(i = 0; i < yNames.length; i++) { @@ -380,7 +380,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { var fixedRangeDflt = getComponentMethod('rangeslider', 'isVisible')(anchoredAxis); coerce('fixedrange', fixedRangeDflt); - coerce('modebarbuttons'); + coerce('modebardisable'); } // Finally, handle scale constraints and matching axes. diff --git a/test/jasmine/tests/modebar_test.js b/test/jasmine/tests/modebar_test.js index e3c32c7f5db..fac02c52fad 100644 --- a/test/jasmine/tests/modebar_test.js +++ b/test/jasmine/tests/modebar_test.js @@ -1194,11 +1194,11 @@ describe('ModeBar', function() { assertRange('yaxis2', [0, 4]); }); - it('should respect modebarbuttons attribute', function(done) { + it('should respect modebardisable attribute', function(done) { Plotly.relayout(gd, { - 'xaxis.modebarbuttons': 'none', - 'xaxis2.modebarbuttons': 'auto', - 'yaxis.modebarbuttons': 'inout', + 'xaxis.modebardisable': 'zoominout+autoscale', + 'xaxis2.modebardisable': 'zoominout', + 'yaxis.modebardisable': 'autoscale', }).then(function() { var buttonZoomIn = selectButton(modeBar, 'zoomIn2d'); var buttonZoomOut = selectButton(modeBar, 'zoomOut2d'); diff --git a/test/plot-schema.json b/test/plot-schema.json index 2172bc4431d..6aa77cf3338 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -13912,20 +13912,6 @@ "allticks" ] }, - "modebarbuttons": { - "description": "Determines which modebar buttons impact this axis. *auto* allows the autoscale buttons, *inout* allows the zoom-in and zoom-out buttons.", - "dflt": "all", - "editType": "modebar", - "extras": [ - "all", - "none" - ], - "flags": [ - "auto", - "inout" - ], - "valType": "flaglist" - }, "nticks": { "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", "dflt": 0, @@ -15477,20 +15463,6 @@ "allticks" ] }, - "modebarbuttons": { - "description": "Determines which modebar buttons impact this axis. *auto* allows the autoscale buttons, *inout* allows the zoom-in and zoom-out buttons.", - "dflt": "all", - "editType": "modebar", - "extras": [ - "all", - "none" - ], - "flags": [ - "auto", - "inout" - ], - "valType": "flaglist" - }, "nticks": { "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", "dflt": 0, From 77ab9abb2efff49e771e3ae5fbec630b2c3768f0 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 18 Jul 2025 12:09:49 -0400 Subject: [PATCH 4/5] fix draftlog for modebardisable --- draftlogs/7358_add.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/draftlogs/7358_add.md b/draftlogs/7358_add.md index a85b49dd665..4904806795e 100644 --- a/draftlogs/7358_add.md +++ b/draftlogs/7358_add.md @@ -1 +1 @@ -- Add modebarbuttons attribute to cartesian axes, to allow fine control over which buttons affect which axes [[#7358](https://github.com/plotly/plotly.js/pull/7358)] +- Add modebardisable attribute to cartesian axes, to allow fine control over which buttons affect which axes [[#7358](https://github.com/plotly/plotly.js/pull/7358)] From 6878acd00788023d5bfa222d324131eb7a83355d Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 18 Jul 2025 15:40:33 -0400 Subject: [PATCH 5/5] update schema --- test/plot-schema.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/plot-schema.json b/test/plot-schema.json index 887cd0d65cb..5e1c74f3793 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -13953,6 +13953,19 @@ "allticks" ] }, + "modebardisable": { + "description": "Disables certain modebar buttons for this axis. *autoscale* disables the autoscale buttons, *zoominout* disables the zoom-in and zoom-out buttons.", + "dflt": "none", + "editType": "modebar", + "extras": [ + "none" + ], + "flags": [ + "autoscale", + "zoominout" + ], + "valType": "flaglist" + }, "nticks": { "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", "dflt": 0, @@ -15525,6 +15538,19 @@ "allticks" ] }, + "modebardisable": { + "description": "Disables certain modebar buttons for this axis. *autoscale* disables the autoscale buttons, *zoominout* disables the zoom-in and zoom-out buttons.", + "dflt": "none", + "editType": "modebar", + "extras": [ + "none" + ], + "flags": [ + "autoscale", + "zoominout" + ], + "valType": "flaglist" + }, "nticks": { "description": "Specifies the maximum number of ticks for the particular axis. The actual number of ticks will be chosen automatically to be less than or equal to `nticks`. Has an effect only if `tickmode` is set to *auto*.", "dflt": 0,