Skip to content

Instantly share code, notes, and snippets.

@derek
Created January 11, 2012 22:30
Show Gist options
  • Save derek/1597168 to your computer and use it in GitHub Desktop.
Save derek/1597168 to your computer and use it in GitHub Desktop.
Y.ButtonGroup

Y.ButtonGroup

Problem

I have multiple Y.Button instances that are logically associated and need to be managed as a group.

For example, you may need a radio group, so when one button becomes selected, the others are unselected. Or a checkbox group, which allows you to select multiple buttons and at some point you need to obtain the values of all selected buttons.

Note: A push group is really just a checkbox group, but with Y.Button instances whose types are push as opposed to toggle.

Methods

  • addButton - Adds a button to the group
  • getButtons - Returns an array of Y.Button instances assigned to the group
  • getSelectedButtons - Returns an array of Y.Button instances that are currently selected
  • getSelectedValues - Returns an array of the values of select buttons (sugar)

Attributes

  • type ('checkbox' [default], or 'radio')

Events

  • selectionChange - Fires after the selected state on a grouped button changes.

Dependencies

  • button-base (requires: base, classnamemanager, node)

Example

// Instantiation
group = new Y.ButtonGroup({type: 'radio'});
buttonA = new Y.Button({srcNode: "#foo"});
buttonB = new Y.Button({srcNode: "#bar"});

// Interaction
group.addButton(ButtonA);
group.addButton(ButtonB);

buttonA.select();
selected = group.getSelectedButtons(); // Returns buttonA

buttonB.select();
selected = group.getSelectedButtons(); // Returns buttonB

Considerations

  • Add support for a buttons attribute in the Y.ButtonGroup config object to add all buttons on instantiation. Update: see comment #4
  • Add support for a srcNodes selector attribute in the Y.ButtonGroup config object to create Y.Button instances for each node and add to the group. Update: see comment #4
  • Add support for a click event (exact name TBD) which fires when any button in the group is clicked. This would be most applicable to push button groups.
@rgrove
Copy link

rgrove commented Jan 11, 2012

Can you clarify the use cases for ButtonGroup?

Is it only for groups of radio/checkbox-style buttons, or can it also be used to group non-toggleable buttons in a toolbar, panel, or other UI element? Will it do anything other than maintain a list of buttons and tell you which buttons are selected? Will it provide any sugar for creating buttons and managing their relationships to one another, or is button creation and event management left entirely up to Y.Button and the implementor?

If ButtonGroup will only target the narrow use case of toggleable radio/checkbox buttons, then I feel like maybe there should be a lower-level "group" abstraction under it, and it should be called ToggleGroup or something, to leave room for other kinds of groups in the future.

It'd be helpful if you could provide some real-world usage examples in addition to the sample code that exercises the API. That'd make it easier to decide whether ButtonGroup should be an ArrayList, a WidgetParent, a View, or something else.

@derek
Copy link
Author

derek commented Jan 11, 2012

In the original module coded up a few months ago, it was only for toggleable groups (all buttons have a selected/unselected state). However, I'm not sold that forcing that as you may want push button groups. If only push buttons, I'm not sure what you get by making them part of a group, but perhaps there's additional functionality that could be added that isn't in this Gist. No point in ruling that out unless necessary.

And yes, the 80% use-case is radio/checkbox groups, but I'll try and think of any additional use-cases. If anyone else can, please chime in.

@hatched
Copy link

hatched commented Jan 11, 2012

If ButtonGroup is simply a group of sugar methods to keep track of a group of buttons I would like to have the ability to create those buttons on the ButtonGroup's instantiation from markup or an object - essentially to add in the options in your 'Moar Sugar?' section.

@derek
Copy link
Author

derek commented Jan 12, 2012

To investigate @hatched's request, here's some implementation examples for supporting srcNodes and buttons config attributes. All 3 are pretty much identical in terms of output/result, just different approaches.


A: Instantiate with srcNodes instead of using addButton();

<div id='container'>
    <button class='myButtons' value='foo'>Foo</button>
    <button class='myButtons' value='bar'>Bar</button>
</div>
var group = new Y.ButtonGroup({
    srcNodes: '.myButtons' // or Y.all('.myButtons')
});

var myButtons = group.getButtons();

B: Instantiate with buttons instead of using addButton();

<div id='container'>
    <button id='foo' value='foo'>Foo</button>
    <button id='bar' value='bar'>Bar</button>
</div>
var buttonA = new Y.Button({srcNode: '#foo'});
var buttonB = new Y.Button({srcNode: '#bar'});
var myButtons = [buttonA, buttonB];

var group = new Y.ButtonGroup({
    buttons: myButtons
});

C: Instantiate with buttons instead of using addButton();, but with items not yet in the DOM, then later add them

<div id='container'></div>
var buttonA = new Y.Button({label: 'foo'});
var buttonB = new Y.Button({label: 'bar'});

var group = new Y.ButtonGroup({
    buttons: [buttonA, buttonB]
});

var myButtons = group.getButtons();

Y.Array.each(myButtons, function(button){
    var node = button.getNode();
    // Because Y.Button instantiation doesn't support a 'value' attribute. Will consider.
    node.set('value', button.get('label'));
    Y.one('#container').append(node);
});

@derek
Copy link
Author

derek commented Jan 12, 2012

@rgrove: to answer your question, Tripp & I couldn't think of any other 80% use-case for a group of buttons outside of a menu bar or tool bar. For those, it will be either push, radio, or toggle.

"or is button creation and event management left entirely up to Y.Button and the implementor?"

For the most part, yes. However, ButtonGroup will fire selectionChange (which I forgot to add to the Gist before, added) and an implementer can subscribe to that instead of listening on all buttons.

@hatched
Copy link

hatched commented Jan 12, 2012

Methods A & B look great, my 80% usecase method would be A.

Was there a reason as to why there is no value attribute? If Y.Button doesn't have the ability to create full markup with the instantiation I woudln't include it at all. There will be a lot of essentially useless code weight there as the dev has to edit the markup after the fact anyways and would be better off creating the nodes outside of Y.Button with something like Y.Node.create() and using methods A or B.

@derek
Copy link
Author

derek commented Jan 12, 2012

Actually, Y.Button creating nodes on the fly was a late addition. It would be preferable for the developer to use Y.Node.create() so then they have full control over the button. But feedback was that it seemed logical for Y.Button to also create buttons, which does make sense, so I added that in. If srcNode is empty at instantiation, it assigns that attritbute as Y.Node.create('<button></button>');.

Instead of Y.Button supporting value directly, it abstracts it out into a label attribute (which I think is what YUI 2 did). But now that I review the code that renders that attribute, I see an issue:

_renderLabel: function (value) {
    var node = this.getNode();
    node.set(node.test('input') ? 'value' : 'text', value);
},

It works fine for <div>, <a>, etc..., and since <input> only has a value attribute (no innerHTML) it will work fine for that as well. But what about a <button> node, which has both value and innerHTML/text? A single label attribute won't work, so I'll have to fix that.

It's not quite as simple as just throwing in a value attribute, because what happens here?

var button = new Y.Button({
    srcNode: Y.Node.create('<div></div>'),
    label: 'I am a Button!',
    value: 'No you are not, you just look like one'
})

<button> and <input> both have a value attribute, but a <div>, <a>, etc... do not. So does Y.Button just ignore the value property? Does it assign it as a custom attribute? It's probably one of those "You should know better that to do this" things, but you never know, and I haven't had a chance to fully think through it yet.

So, I'll work to resolve this issue before 3.5.0pr2.

Good question. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment