From bfb3e6c02cd80488c4a41962d4fc61eb6ec5afdd Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Fri, 27 Mar 2015 11:36:08 -0600 Subject: [PATCH] [added] `standalone` prop to Input, which will not render the `form-group` class In some cases you may not want the `form-group` class applied to form input wrapper divs. To achieve this simply apply the `standalone` prop. Fixes #205 --- docs/src/ComponentsPage.js | 2 +- src/FormGroup.js | 33 +++++++++++++++ src/Input.js | 44 +++++++------------ test/FormGroupSpec.js | 86 ++++++++++++++++++++++++++++++++++++++ test/InputSpec.js | 24 +++++++++++ 5 files changed, 159 insertions(+), 30 deletions(-) create mode 100644 src/FormGroup.js create mode 100644 test/FormGroupSpec.js diff --git a/docs/src/ComponentsPage.js b/docs/src/ComponentsPage.js index ed47b8ad17..3eadb5e979 100644 --- a/docs/src/ComponentsPage.js +++ b/docs/src/ComponentsPage.js @@ -548,7 +548,7 @@ const ComponentsPage = React.createClass({

Input

Renders an input in bootstrap wrappers. Supports label, help, text input add-ons, validation and use as wrapper. Use getValue() or getChecked() to get the current state. - The helper method getInputDOMNode() returns the internal input element.

+ The helper method getInputDOMNode() returns the internal input element. If you don't want the form-group class applied apply the prop named standalone.

Types

Supports select, textarea, static as well as standard HTML input types. getValue() returns an array for multiple select.

diff --git a/src/FormGroup.js b/src/FormGroup.js new file mode 100644 index 0000000000..0bde453978 --- /dev/null +++ b/src/FormGroup.js @@ -0,0 +1,33 @@ +import React from 'react'; +import classSet from 'classnames'; + +class FormGroup extends React.Component { + render() { + let classes = { + 'form-group': !this.props.standalone, + 'has-feedback': this.props.hasFeedback, + 'has-success': this.props.bsStyle === 'success', + 'has-warning': this.props.bsStyle === 'warning', + 'has-error': this.props.bsStyle === 'error' + }; + + return ( +
+ {this.props.children} +
+ ); + } +} + +FormGroup.defaultProps = { + standalone: false +}; + +FormGroup.propTypes = { + standalone: React.PropTypes.bool, + hasFeedback: React.PropTypes.bool, + bsStyle: React.PropTypes.oneOf(['success', 'warning', 'error']), + groupClassName: React.PropTypes.string +}; + +export default FormGroup; diff --git a/src/Input.js b/src/Input.js index 412630a3d2..24d7b2e168 100644 --- a/src/Input.js +++ b/src/Input.js @@ -1,6 +1,7 @@ import React from 'react'; import classSet from 'classnames'; import Button from './Button'; +import FormGroup from './FormGroup'; const Input = React.createClass({ @@ -215,38 +216,21 @@ const Input = React.createClass({ ) : children; }, - renderFormGroup(children) { - let classes = { - 'form-group': true, - 'has-feedback': this.props.hasFeedback, - 'has-success': this.props.bsStyle === 'success', - 'has-warning': this.props.bsStyle === 'warning', - 'has-error': this.props.bsStyle === 'error' - }; - classes[this.props.groupClassName] = this.props.groupClassName; - - return ( -
- {children} -
- ); - }, - render() { + let children; + if (this.isCheckboxOrRadio()) { - return this.renderFormGroup( - this.renderWrapper([ - this.renderCheckboxandRadioWrapper( - this.renderLabel( - this.renderInput() - ) - ), - this.renderHelp() - ]) - ); + children = this.renderWrapper([ + this.renderCheckboxandRadioWrapper( + this.renderLabel( + this.renderInput() + ) + ), + this.renderHelp() + ]); } else { - return this.renderFormGroup([ + children = [ this.renderLabel(), this.renderWrapper([ this.renderInputGroup( @@ -255,8 +239,10 @@ const Input = React.createClass({ this.renderIcon(), this.renderHelp() ]) - ]); + ]; } + + return {children}; } }); diff --git a/test/FormGroupSpec.js b/test/FormGroupSpec.js new file mode 100644 index 0000000000..c174fd5a29 --- /dev/null +++ b/test/FormGroupSpec.js @@ -0,0 +1,86 @@ +import React from 'react'; +import ReactTestUtils from 'react/lib/ReactTestUtils'; +import FormGroup from '../src/FormGroup'; + +describe('FormGroup', function() { + beforeEach(function() { + sinon.spy(console, 'warn'); + }); + + afterEach(function() { + if (typeof console.warn.restore === 'function') { + console.warn.called.should.be.false; + console.warn.restore(); + } + }); + + it('renders children', function() { + let instance = ReactTestUtils.renderIntoDocument( + + + + + ); + + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'child1')); + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'child2')); + }); + + it('renders with form-group class', function() { + let instance = ReactTestUtils.renderIntoDocument( + + + + ); + + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'form-group')); + }); + + it('renders no form-group class when standalone', function() { + let instance = ReactTestUtils.renderIntoDocument( + + + + ); + + assert.equal(ReactTestUtils.scryRenderedDOMComponentsWithClass(instance, 'form-group').length, 0); + }); + + [{ + className: 'has-feedback', + props: { hasFeedback: true } + }, { + className: 'has-success', + props: { bsStyle: 'success' } + }, { + className: 'has-warning', + props: { bsStyle: 'warning' } + }, { + className: 'has-error', + props: { bsStyle: 'error' } + }, { + className: 'custom-group', + props: { groupClassName: 'custom-group' } + } + ].forEach(function(testCase) { + it(`does not render ${testCase.className} class`, function() { + let instance = ReactTestUtils.renderIntoDocument( + + + + ); + + assert.equal(ReactTestUtils.scryRenderedDOMComponentsWithClass(instance, testCase.className).length, 0); + }); + + it(`renders with ${testCase.className} class`, function() { + let instance = ReactTestUtils.renderIntoDocument( + + + + ); + + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, testCase.className)); + }); + }); +}); diff --git a/test/InputSpec.js b/test/InputSpec.js index 9b45b247ec..3c3c973f87 100644 --- a/test/InputSpec.js +++ b/test/InputSpec.js @@ -167,6 +167,30 @@ describe('Input', function () { assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'help-block')); }); + it('renders form-group class', function() { + let instance = ReactTestUtils.renderIntoDocument( + + ); + + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'form-group')); + }); + + it('renders no form-group class', function() { + let instance = ReactTestUtils.renderIntoDocument( + + ); + + assert.equal(ReactTestUtils.scryRenderedDOMComponentsWithClass(instance, 'form-group').length, 0); + }); + + it('renders custom-form-group class', function() { + let instance = ReactTestUtils.renderIntoDocument( + + ); + + assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'custom-form-class')); + }); + it('renders feedback icon', function () { let instance = ReactTestUtils.renderIntoDocument(