Skip to content

Commit

Permalink
feat(react-radio): add radio and radio group components
Browse files Browse the repository at this point in the history
[Finishes #86529336]

Signed-off-by: Ryan Dy <rdy@pivotal.io>
  • Loading branch information
Geoff Pleiss committed Jan 22, 2015
1 parent 3605113 commit 17a0325
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 2 deletions.
63 changes: 61 additions & 2 deletions src/pivotal-ui/components/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1254,11 +1254,19 @@ Add the focus-input directive to an element that should be focused if the focus-

/*doc
---
title: React Search Inputs
name: form_search_input_react
title: React Forms
name: form_react
categories:
- Beta
---
*/

/*doc
---
title: Search Inputs
name: form_search_input_react
parent: form_react
---
```react_example
<UI.SearchInput placeholder="Search..."/>
Expand Down Expand Up @@ -1314,3 +1322,54 @@ var FilteringSearchExample = React.createClass({
```
*/

/*doc
---
title: Radio Inputs
name: form_radio_input_react
parent: form_react
---
```jsx_example
var MyComponent = React.createClass({
getInitialState: function() { return {selection: null}; },
change: function(value) {
this.setState({selection: value});
},
render: function() {
return (
<form role="form" className="form-horizontal">
<div className="form-group">
<UI.Col md={3}>
<label>Options</label>
</UI.Col>
<UI.Col md={21}>
<UI.RadioGroup name="stuff" onChange={this.change}>
<UI.Radio value="others">Others</UI.Radio>
<UI.Radio value="others1" defaultChecked>More others</UI.Radio>
<UI.Radio value="special">Click for more!</UI.Radio>
</UI.RadioGroup>
</UI.Col>
</div>
{this.state.selection === 'special' && <div className="form-group">
<UI.Col md={3}>
<label>Stuff that appears</label>
</UI.Col>
<UI.Col md={21}>
<label htmlFor="exampleInputEmail1">Email address</label>
<input type="email" className="form-control" id="exampleInputEmail1" placeholder="Enter email" />
</UI.Col>
</div>}
</form>
);
}
});
```
```react_example
<MyComponent />
```
*/
3 changes: 3 additions & 0 deletions src/pivotal-ui/javascripts/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ module.exports = {

PivnetHomepage: require('./pivnet_homepage.jsx').PivnetHomepage,

Radio: require('./radio').Radio,
RadioGroup: require('./radio-group').RadioGroup,

Panel: require('./panels.jsx').Panel,
SimplePanel: require('./panels.jsx').SimplePanel,
ClickablePanel: require('./panels.jsx').ClickablePanel,
Expand Down
32 changes: 32 additions & 0 deletions src/pivotal-ui/javascripts/radio-group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

var _ = require('lodash');
var React = require('react/addons');
var cloneWithProps = React.addons.cloneWithProps;

var RadioGroup = React.createClass({
propTypes: {
id: React.PropTypes.string,
name: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func
},

getDefaultProps: function() {
return {onChange: _.noop};
},

onChange: function(e) {
this.props.onChange(e.target.value);
},

render: function() {
var {name} = this.props;
var children = React.Children.map(this.props.children, (child) => cloneWithProps(child, {name, onChange: this.onChange}));

return (
<div className="radio-group" id={this.props.id}>{children}</div>
);
},
});

module.exports = {RadioGroup};
29 changes: 29 additions & 0 deletions src/pivotal-ui/javascripts/radio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

var React = require('react/addons');

var Radio = React.createClass({
propTypes: {
checked: React.PropTypes.bool,
defaultChecked: React.PropTypes.bool,
name: React.PropTypes.string,
value: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func,
id: React.PropTypes.string
},

render: function() {
var {id, checked, defaultChecked, name, value, onChange} = this.props;
return (
<div className='radio'>
<label>
<input type='radio' {...{id, checked, defaultChecked, name, value, onChange}}>
{this.props.children}
</input>
</label>
</div>
);
}
});

module.exports = {Radio};
52 changes: 52 additions & 0 deletions test/spec/javascripts/radio_group_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

require('./spec_helper');

var $ = require('jquery');
var React = require('react/addons');

var RadioGroup = require('../../../src/pivotal-ui/javascripts/radio-group').RadioGroup;
var Radio = require('../../../src/pivotal-ui/javascripts/radio').Radio;

describe('RadioGroup', function() {
var changeSpy;
beforeEach(function() {
this.node = $('<div id="container"></div>').appendTo('body').get(0);
changeSpy = jasmine.createSpy('change');
React.render(
<RadioGroup name='bananas' onChange={changeSpy} id="clear-channel">
<Radio value="1">One!!!</Radio>
<Radio value="2">The two value</Radio>
<Radio value="3">Three</Radio>
</RadioGroup>,
this.node
);
});

afterEach(function() {
React.unmountComponentAtNode(this.node);
document.body.removeChild(this.node);
});

it("renders the radio group", function() {
expect($('#container .radio-group#clear-channel')).toExist();
});


it('renders 3 radiobuttons', function() {
expect($('#container .radio-group .radio label :radio[name=bananas]')).toHaveLength(3);
expect($('#container .radio-group .radio label :radio[name=bananas]').eq(0)).toHaveValue('1');
expect($('#container .radio-group .radio label :radio[name=bananas]').eq(1)).toHaveValue('2');
expect($('#container .radio-group .radio label :radio[name=bananas]').eq(2)).toHaveValue('3');
});

describe("when the radio button is changed", function() {
beforeEach(function() {
$('#container .radio-group :radio').eq(0).simulate('change').simulate('click');
});

it("calls the change callback", function() {
expect(changeSpy).toHaveBeenCalledWith('1');
});
});
});
56 changes: 56 additions & 0 deletions test/spec/javascripts/radio_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

require('./spec_helper');

var $ = require('jquery');
var React = require('react/addons');

var Radio = require('../../../src/pivotal-ui/javascripts/radio').Radio;

describe('Radio', function() {
beforeEach(function() {
this.node = $('<div id="container"></div>').appendTo('body').get(0);
});

afterEach(function() {
React.unmountComponentAtNode(this.node);
document.body.removeChild(this.node);
});

it("renders a radio", function() {
React.render(<Radio value="1" name="bananas" id="npr">One!!!</Radio>, this.node);
expect($('#container .radio label :radio[name=bananas]')).toHaveValue('1');
expect($('#container .radio label')).toContainText('One!!!');
expect($('#container #npr:radio')).toExist();
});

describe('when the checked property is passed', function() {
beforeEach(function() {
React.render(<Radio value="1" name="bananas" checked onChange={jasmine.createSpy('change')}>One!!!</Radio>, this.node);
});

it('renders a checked radio', function() {
expect($('#container .radio label :radio[name=bananas]:checked')).toExist();
});
});

describe('when the defaultChecked property is passed', function() {
var changeSpy;

beforeEach(function() {
changeSpy = jasmine.createSpy('change');
React.render(<Radio value="1" name="bananas" onChange={changeSpy} defaultChecked>One!!!</Radio>, this.node);
});

it('renders a checked radio', function() {
expect($('#container .radio label :radio[name=bananas]:checked')).toExist();
});

describe('changing the value of the radio button', function() {
it('triggers the onChange callback', function() {
$('#container .radio label :radio').simulate('click');
expect(changeSpy).toHaveBeenCalled();
});
});
});
});

0 comments on commit 17a0325

Please sign in to comment.