Skip to content

Commit

Permalink
[changed] Deprecating Input type=radio in favor of new RadioButton an…
Browse files Browse the repository at this point in the history
…d RadioGroup components
  • Loading branch information
aabenoja committed Jul 8, 2015
1 parent b8a23d4 commit a66f4b8
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 2 deletions.
33 changes: 33 additions & 0 deletions docs/examples/RadioButtonGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const SimpleRadioGroup = React.createClass({
getInitialState() {
return {message: undefined};
},

handleChange(newValue) {
this.setState({message: newValue});
},

render() {
const message = this.state.message ?
<FormControls.Static>{this.state.message}</FormControls.Static> :
null;

const handleChange = this.handleChange;

return (
<form>
{message}
<FormControls.RadioGroup name="greeting">
<FormControls.RadioButton label="Hi!" value="Hi!" />
<FormControls.RadioButton label="Hello!" value="Hello!" />
</FormControls.RadioGroup>
<FormControls.RadioGroup name="farewell" onChange={handleChange}>
<FormControls.RadioButton label="Bye!" value="Bye!" />
<FormControls.RadioButton label="Goodbye!" value="Goodbye!" />
</FormControls.RadioGroup>
</form>
);
}
});

React.render(<SimpleRadioGroup />, mountNode);
3 changes: 3 additions & 0 deletions docs/src/ComponentsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,9 @@ const ComponentsPage = React.createClass({
<h2 id='button-input-types'>Button Input Types</h2>
<p>Form buttons are encapsulated by <code>ButtonInput</code>. Pass in <code>type="reset"</code> or <code>type="submit"</code> to suit your needs. Styling is the same as <code>Button</code>.</p>
<ReactPlayground codeText={Samples.ButtonInput} />
<h2 id='radio-group'>Radio Group</h2>
<p>Radio buttons can be created with <code>FormControls.RadioButton</code>. <code>FormControls.RadioGroup</code> will wrap your group of radio buttons in a fieldset.</p>
<ReactPlayground codeText={Samples.RadioButtonGroup} />
<h2 id='input-addons'>Add-ons</h2>
<p>Use <code>addonBefore</code> and <code>addonAfter</code> for normal addons, <code>buttonBefore</code> and <code>buttonAfter</code> for button addons.
Exotic configurations may require some css on your side.</p>
Expand Down
1 change: 1 addition & 0 deletions docs/src/Samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export default {
InputTypes: require('fs').readFileSync(__dirname + '/../examples/InputTypes.js', 'utf8'),
StaticText: require('fs').readFileSync(__dirname + '/../examples/StaticText.js', 'utf8'),
ButtonInput: require('fs').readFileSync(__dirname + '/../examples/ButtonInput.js', 'utf8'),
RadioButtonGroup: require('fs').readFileSync(__dirname + '/../examples/RadioButtonGroup.js', 'utf8'),
InputAddons: require('fs').readFileSync(__dirname + '/../examples/InputAddons.js', 'utf8'),
InputSizes: require('fs').readFileSync(__dirname + '/../examples/InputSizes.js', 'utf8'),
InputValidation: require('fs').readFileSync(__dirname + '/../examples/InputValidation.js', 'utf8'),
Expand Down
54 changes: 54 additions & 0 deletions src/FormControls/RadioButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import classNames from 'classnames';

export default class RadioButton extends React.Component {
constructor(props) {
super(props);
}

getDOMNode() {
return React.findDOMNode(this.refs.input);
}

getValue() {
return this.props.value;
}

getChecked() {
return this.getDOMNode().checked;
}

renderLabel(input) {
if (!this.props.label) {
return input;
}

const classes = {
'radio': !this.props.inline,
'radio-inline': this.props.inline
};

return (
<label className={classNames(classes)}>
{input}
{this.props.label}
</label>
);
}

render() {
const {inline, label, ...other} = this.props;
return this.renderLabel(
<input type="radio" {...other} ref="input" />
);
}
}

RadioButton.propTypes = {
disabled: React.PropTypes.bool,
inline: React.PropTypes.bool,
label: React.PropTypes.node,
name: React.PropTypes.string,
onChange: React.PropTypes.func,
value: React.PropTypes.any
};
59 changes: 59 additions & 0 deletions src/FormControls/RadioGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';

export default class RadioGroup extends React.Component {
constructor(props) {
super(props);
this.state = {value: undefined};
this.handleChange = this.handleChange.bind(this);
}

getDOMNode() {
return React.findDOMNode(this.refs.radioGroup);
}

getValue() {
return this.state.value;
}

renderLegend() {
return this.props.legend ?
<legend>{this.props.legend}</legend> :
null;
}

handleChange(evt) {
if (!evt.target) {
return;
}

const {value} = evt.target;
this.setState({value});
this.props.onChange(value);
}

renderChildren() {
const {name, inline} = this.props;
return React.Children.map(this.props.children, child => React.cloneElement(child, {name, inline, onChange: this.handleChange}));
}

render() {
const children = this.renderChildren();
return (
<fieldset ref="radioGroup" className="radio">
{this.renderLegend()}
{children}
</fieldset>
);
}
}

RadioGroup.propTypes = {
inline: React.PropTypes.bool,
legend: React.PropTypes.node,
name: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func
};

RadioGroup.defaultProps = {
onChange: () => {}
};
6 changes: 5 additions & 1 deletion src/FormControls/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import RadioButton from './RadioButton';
import RadioGroup from './RadioGroup';
import Static from './Static';

export default {
Static
Static,
RadioGroup,
RadioButton
};
5 changes: 4 additions & 1 deletion src/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ class Input extends InputBase {
deprecationWarning(`Input type=${this.props.type}`, 'ButtonInput');
return <ButtonInput {...this.props} />;
} else if (this.props.type === 'static') {
deprecationWarning('Input type=static', 'StaticText');
deprecationWarning('Input type=static', 'Static');
return <FormControls.Static {...this.props} />;
} else if (this.props.types === 'radio') {
deprecationWarning('Input type=radio', 'FormControls.RadioButton and FormControls.RadioGroup');
return <FormControls.RadioButton {...this.props} />;
}

return super.render();
Expand Down
70 changes: 70 additions & 0 deletions test/FormControlsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,76 @@ import ReactTestUtils from 'react/lib/ReactTestUtils';
import FormControls from '../src/FormControls';

describe('Form Controls', function () {
describe('RadioGroup', function () {
it('applies an on change to children', function () {
const instance = ReactTestUtils.renderIntoDocument(
<FormControls.RadioGroup name="test">
<FormControls.RadioButton label="thing" value="1" />
<FormControls.RadioButton label="pants" value="1" />
<FormControls.RadioButton label="jeans" value="1" />
</FormControls.RadioGroup>
);

const buttons = ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, 'INPUT');
buttons.every(button => button.props.onChange === instance.handleChange).should.be.true;
});

it('invokes a callback when a selection is changed', function() {
const callback = sinon.spy();

const instance = ReactTestUtils.renderIntoDocument(
<FormControls.RadioGroup name="test" onChange={callback}>
<FormControls.RadioButton label="thing" value="thing" id="thing" className="thing" />
<FormControls.RadioButton label="jeans" value="jeans" id="jeans" />
</FormControls.RadioGroup>
);

const thing = ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'thing');
ReactTestUtils.Simulate.change(thing, {target: {value: 5}});
callback.should.have.been.calledWith(5);
});

it('applies the name property to all children', function () {
const testName = 'test';
const instance = ReactTestUtils.renderIntoDocument(
<FormControls.RadioGroup name={testName}>
<FormControls.RadioButton label="thing" value="1" />
<FormControls.RadioButton label="pants" value="1" />
<FormControls.RadioButton label="jeans" value="1" />
</FormControls.RadioGroup>
);

const buttons = ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, 'INPUT');
buttons.every(button => button.props.name === testName).should.be.true;
});

it('does not inline if the inline property is false', function () {
const instance = ReactTestUtils.renderIntoDocument(
<FormControls.RadioGroup name="test" inline={false}>
<FormControls.RadioButton label="thing" value="1" />
<FormControls.RadioButton label="pants" value="1" />
<FormControls.RadioButton label="jeans" value="1" />
</FormControls.RadioGroup>
);

const labels = ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, 'LABEL');
labels.every(label => label.props.className === 'radio').should.be.true;
});

it('applies the radio-inline class when the inline prop is used', function () {
const instance = ReactTestUtils.renderIntoDocument(
<FormControls.RadioGroup name="test" inline>
<FormControls.RadioButton label="thing" value="1" />
<FormControls.RadioButton label="pants" value="1" />
<FormControls.RadioButton label="jeans" value="1" />
</FormControls.RadioGroup>
);

const labels = ReactTestUtils.scryRenderedDOMComponentsWithTag(instance, 'LABEL');
labels.every(label => label.props.className === 'radio-inline').should.be.true;
});
});

describe('Static', function () {
it('renders a p element wrapped around the given value', function () {
const instance = ReactTestUtils.renderIntoDocument(
Expand Down

0 comments on commit a66f4b8

Please sign in to comment.