diff --git a/src/components/updatemenus/attributes.js b/src/components/updatemenus/attributes.js index f37e02e9e46..2d2a8794e8b 100644 --- a/src/components/updatemenus/attributes.js +++ b/src/components/updatemenus/attributes.js @@ -48,6 +48,22 @@ var buttonsAttrs = templatedArray('button', { 'method set in `method` on click.' ].join(' ') }, + args2: { + valType: 'info_array', + role: 'info', + freeLength: true, + items: [ + {valType: 'any'}, + {valType: 'any'}, + {valType: 'any'} + ], + description: [ + 'Sets a 2nd set of `args`,', + 'these arguments values are passed to the Plotly', + 'method set in `method` when clicking this button while in the active state.', + 'Use this to create toggle buttons.' + ].join(' ') + }, label: { valType: 'string', role: 'info', diff --git a/src/components/updatemenus/defaults.js b/src/components/updatemenus/defaults.js index 492b9c911a9..3c646a5efac 100644 --- a/src/components/updatemenus/defaults.js +++ b/src/components/updatemenus/defaults.js @@ -74,6 +74,7 @@ function buttonDefaults(buttonIn, buttonOut) { if(visible) { coerce('method'); coerce('args'); + coerce('args2'); coerce('label'); coerce('execute'); } diff --git a/src/components/updatemenus/draw.js b/src/components/updatemenus/draw.js index 58c21b9122b..ac4ca74ef16 100644 --- a/src/components/updatemenus/draw.js +++ b/src/components/updatemenus/draw.js @@ -119,6 +119,7 @@ module.exports = function draw(gd) { var gHeader = d3.select(this); var _gButton = menuOpts.type === 'dropdown' ? gButton : null; + Plots.manageCommandObserver(gd, menuOpts, menuOpts.buttons, function(data) { setActive(gd, menuOpts, menuOpts.buttons[data.index], gHeader, _gButton, scrollBox, data.index, true); }); @@ -306,10 +307,14 @@ function drawButtons(gd, gHeader, gButton, scrollBox, menuOpts) { // skip `dragend` events if(d3.event.defaultPrevented) return; - setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex); - if(buttonOpts.execute) { - Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args); + if(buttonOpts.args2 && menuOpts.active === buttonIndex) { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, -1); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args2); + } else { + setActive(gd, menuOpts, buttonOpts, gHeader, gButton, scrollBox, buttonIndex); + Plots.executeAPICommand(gd, buttonOpts.method, buttonOpts.args); + } } gd.emit('plotly_buttonclicked', {menu: menuOpts, button: buttonOpts, active: menuOpts.active}); diff --git a/test/image/baselines/updatemenus_toggle.png b/test/image/baselines/updatemenus_toggle.png new file mode 100644 index 00000000000..8652d930e56 Binary files /dev/null and b/test/image/baselines/updatemenus_toggle.png differ diff --git a/test/image/mocks/updatemenus_toggle.json b/test/image/mocks/updatemenus_toggle.json new file mode 100644 index 00000000000..45b67ee019e --- /dev/null +++ b/test/image/mocks/updatemenus_toggle.json @@ -0,0 +1,33 @@ +{ + "data": [ + { + "mode": "lines", + "y": [1, 1], + "line": { + "width": 5, + "color": "blue" + } + } + ], + "layout": { + "updatemenus": [ + { + "type": "buttons", + "buttons": [ + { + "label": "toggle", + "method": "restyle", + "args": [ + "line.color", + "blue" + ], + "args2": [ + "line.color", + "red" + ] + } + ] + } + ] + } +} diff --git a/test/jasmine/tests/updatemenus_test.js b/test/jasmine/tests/updatemenus_test.js index e0f86a5bbcf..81449b9dc18 100644 --- a/test/jasmine/tests/updatemenus_test.js +++ b/test/jasmine/tests/updatemenus_test.js @@ -601,6 +601,50 @@ describe('update menus interactions', function() { .then(done); }); + it('should apply update on button click (toggle via args2 case)', function(done) { + var menuOpts = { + type: 'buttons', + buttons: [{ + label: 'toggle', + method: 'restyle', + args: ['line.color', 'blue'], + args2: ['line.color', 'red'] + }] + }; + + var btn; + + function assertLineColor(msg, lineColor) { + expect(gd.data[2].line.color).toBe(lineColor, 'gd.data line.color| ' + msg); + expect(gd._fullData[2].line.color).toBe(lineColor, 'gd._fullData line.color| ' + msg); + } + + Plotly.relayout(gd, 'updatemenus', null) + .then(function() { return Plotly.relayout(gd, 'updatemenus[0]', menuOpts); }) + .then(function() { + btn = selectButton(0, {type: 'buttons'}); + assertItemColor(btn, activeColor); + assertLineColor('base', 'blue'); + return click(btn); + }) + .then(function() { + assertItemColor(btn, bgColor); + assertLineColor('base', 'red'); + return click(btn); + }) + .then(function() { + assertItemColor(btn, activeColor); + assertLineColor('base', 'blue'); + return click(btn); + }) + .then(function() { + assertItemColor(btn, bgColor); + assertLineColor('base', 'red'); + }) + .catch(failTest) + .then(done); + }); + it('should update correctly on failed binding comparisons', function(done) { // See https://github.com/plotly/plotly.js/issues/1169 // for more info. @@ -871,8 +915,10 @@ describe('update menus interactions', function() { return header; } - function selectButton(buttonIndex) { - var buttons = d3.selectAll('.' + constants.dropdownButtonClassName); + function selectButton(buttonIndex, opts) { + opts = opts || {}; + var k = opts.type === 'buttons' ? 'buttonClassName' : 'dropdownButtonClassName'; + var buttons = d3.selectAll('.' + constants[k]); var button = d3.select(buttons[0][buttonIndex]); return button; }