Skip to content

Commit 6f73149

Browse files
committed
Merge pull request react-bootstrap#626 from aabenoja/submit-split
Splitting Button, Reset, and Submit from Input
2 parents cb1cc99 + 1e4cd18 commit 6f73149

File tree

13 files changed

+425
-243
lines changed

13 files changed

+425
-243
lines changed

docs/examples/.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"Badge",
1313
"Button",
1414
"ButtonGroup",
15+
"ButtonInput",
1516
"ButtonToolbar",
1617
"CollapsibleNav",
1718
"CollapsibleMixin",

docs/examples/ButtonInput.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const ButtonInputExample = React.createClass({
2+
getInitialState() {
3+
return {
4+
disabled: true,
5+
style: ''
6+
};
7+
},
8+
9+
resetValidation() {
10+
this.setState({
11+
disabled: true,
12+
style: ''
13+
});
14+
},
15+
16+
validationState() {
17+
let length = this.refs.input.getValue().length;
18+
let style = 'danger';
19+
20+
if (length > 10) { style = 'success'; }
21+
else if (length > 5) { style = 'warning'; }
22+
23+
let disabled = style !== 'success';
24+
25+
return { style, disabled };
26+
},
27+
28+
handleChange() {
29+
this.setState( this.validationState() );
30+
},
31+
32+
render() {
33+
return (
34+
<form>
35+
<Input type="text" ref="input" onChange={this.handleChange} />
36+
<ButtonInput bsSize="small">Child Text</ButtonInput>
37+
<ButtonInput type="reset" bsStyle="primary" onClick={this.resetValidation} />
38+
<ButtonInput type="submit" value="Submit Your Input" bsStyle={this.state.style} bsSize="large" disabled={this.state.disabled} />
39+
</form>
40+
);
41+
}
42+
});
43+
44+
React.render(<ButtonInputExample />, mountNode);

docs/examples/InputTypes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ const inputTypeInstance = (
1616
</Input>
1717
<Input type='textarea' label='Text Area' placeholder='textarea' />
1818
<Input type='static' value='Static Text' />
19-
<Input type='submit' value='Submit button' />
19+
<ButtonInput value='Button Input' />
20+
<ButtonInput type='reset' value='Reset Button' />
21+
<ButtonInput type='submit' value='Submit Button' />
2022
</form>
2123
);
2224

docs/src/ComponentsPage.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,9 @@ const ComponentsPage = React.createClass({
551551
<h2 id='input-types'>Types</h2>
552552
<p>Supports <code>select</code>, <code>textarea</code>, <code>static</code> as well as standard HTML input types. <code>getValue()</code> returns an array for multiple select.</p>
553553
<ReactPlayground codeText={Samples.InputTypes} />
554+
<h2 id='button-input-types'>Button Input Types</h2>
555+
<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>
556+
<ReactPlayground codeText={Samples.ButtonInput} />
554557
<h2 id='input-addons'>Add-ons</h2>
555558
<p>Use <code>addonBefore</code> and <code>addonAfter</code> for normal addons, <code>buttonBefore</code> and <code>buttonAfter</code> for button addons.
556559
Exotic configurations may require some css on your side.</p>

docs/src/ReactPlayground.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as modAlert from '../../src/Alert';
55
import * as modBadge from '../../src/Badge';
66
import * as modmodButton from '../../src/Button';
77
import * as modButtonGroup from '../../src/ButtonGroup';
8+
import * as modButtonInput from '../../src/ButtonInput';
89
import * as modmodButtonToolbar from '../../src/ButtonToolbar';
910
import * as modCollapsibleNav from '../../src/CollapsibleNav';
1011
import * as modCollapsibleMixin from '../../src/CollapsibleMixin';
@@ -52,6 +53,7 @@ const Alert = modAlert.default;
5253
const Badge = modBadge.default;
5354
const Button = modmodButton.default;
5455
const ButtonGroup = modButtonGroup.default;
56+
const ButtonInput = modButtonInput.default;
5557
const ButtonToolbar = modmodButtonToolbar.default;
5658
const CollapsibleNav = modCollapsibleNav.default;
5759
const CollapsibleMixin = modCollapsibleMixin.default;

docs/src/Samples.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export default {
8383
TableResponsive: require('fs').readFileSync(__dirname + '/../examples/TableResponsive.js', 'utf8'),
8484
Input: require('fs').readFileSync(__dirname + '/../examples/Input.js', 'utf8'),
8585
InputTypes: require('fs').readFileSync(__dirname + '/../examples/InputTypes.js', 'utf8'),
86+
ButtonInput: require('fs').readFileSync(__dirname + '/../examples/ButtonInput.js', 'utf8'),
8687
InputAddons: require('fs').readFileSync(__dirname + '/../examples/InputAddons.js', 'utf8'),
8788
InputSizes: require('fs').readFileSync(__dirname + '/../examples/InputSizes.js', 'utf8'),
8889
InputValidation: require('fs').readFileSync(__dirname + '/../examples/InputValidation.js', 'utf8'),

src/ButtonInput.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import Button from './Button';
3+
import FormGroup from './FormGroup';
4+
import InputBase from './InputBase';
5+
6+
function valueValidation({children, value}, propName, componentName) {
7+
if (children && value) {
8+
return new Error('Both value and children cannot be passed to ButtonInput');
9+
}
10+
return React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]).call(null, {children, value}, propName, componentName);
11+
}
12+
13+
class ButtonInput extends InputBase {
14+
renderFormGroup(children) {
15+
let {bsStyle, value, ...other} = this.props; /* eslint no-unused-vars: 0 object-shorthand: 0 */
16+
return <FormGroup {...other}>{children}</FormGroup>;
17+
}
18+
19+
renderInput() {
20+
let {children, value, ...other} = this.props;
21+
let val = children ? children : value;
22+
return <Button {...other} componentClass="input" ref="input" key="input" value={val} />;
23+
}
24+
}
25+
26+
ButtonInput.defaultProps = {
27+
type: 'button'
28+
};
29+
30+
ButtonInput.propTypes = {
31+
type: React.PropTypes.oneOf(['button', 'reset', 'submit']),
32+
bsStyle(props) {
33+
//defer to Button propTypes of bsStyle
34+
return null;
35+
},
36+
children: valueValidation,
37+
value: valueValidation
38+
};
39+
40+
export default ButtonInput;

src/Input.js

Lines changed: 10 additions & 230 deletions
Original file line numberDiff line numberDiff line change
@@ -1,239 +1,19 @@
11
import React from 'react';
2-
import classNames from 'classnames';
3-
import Button from './Button';
4-
import FormGroup from './FormGroup';
2+
import InputBase from './InputBase';
3+
import ButtonInput from './ButtonInput';
4+
import deprecationWarning from './utils/deprecationWarning';
55

6-
class Input extends React.Component {
7-
getInputDOMNode() {
8-
return React.findDOMNode(this.refs.input);
9-
}
10-
11-
getValue() {
12-
if (this.props.type === 'static') {
13-
return this.props.value;
14-
} else if (this.props.type) {
15-
if (this.props.type === 'select' && this.props.multiple) {
16-
return this.getSelectedOptions();
17-
} else {
18-
return this.getInputDOMNode().value;
19-
}
20-
} else {
21-
throw 'Cannot use getValue without specifying input type.';
22-
}
23-
}
24-
25-
getChecked() {
26-
return this.getInputDOMNode().checked;
27-
}
28-
29-
getSelectedOptions() {
30-
let values = [];
31-
32-
Array.prototype.forEach.call(
33-
this.getInputDOMNode().getElementsByTagName('option'),
34-
(option) => {
35-
if (option.selected) {
36-
let value = option.getAttribute('value') || option.innerHtml;
37-
values.push(value);
38-
}
39-
});
40-
41-
return values;
42-
}
43-
44-
isCheckboxOrRadio() {
45-
return this.props.type === 'checkbox' || this.props.type === 'radio';
46-
}
47-
48-
isFile() {
49-
return this.props.type === 'file';
50-
}
51-
52-
renderInputGroup(children) {
53-
let addonBefore = this.props.addonBefore ? (
54-
<span className="input-group-addon" key="addonBefore">
55-
{this.props.addonBefore}
56-
</span>
57-
) : null;
58-
59-
let addonAfter = this.props.addonAfter ? (
60-
<span className="input-group-addon" key="addonAfter">
61-
{this.props.addonAfter}
62-
</span>
63-
) : null;
64-
65-
let buttonBefore = this.props.buttonBefore ? (
66-
<span className="input-group-btn">
67-
{this.props.buttonBefore}
68-
</span>
69-
) : null;
70-
71-
let buttonAfter = this.props.buttonAfter ? (
72-
<span className="input-group-btn">
73-
{this.props.buttonAfter}
74-
</span>
75-
) : null;
76-
77-
let inputGroupClassName;
78-
switch (this.props.bsSize) {
79-
case 'small': inputGroupClassName = 'input-group-sm'; break;
80-
case 'large': inputGroupClassName = 'input-group-lg'; break;
81-
}
82-
83-
return addonBefore || addonAfter || buttonBefore || buttonAfter ? (
84-
<div className={classNames(inputGroupClassName, 'input-group')} key="input-group">
85-
{addonBefore}
86-
{buttonBefore}
87-
{children}
88-
{addonAfter}
89-
{buttonAfter}
90-
</div>
91-
) : children;
92-
}
93-
94-
renderIcon() {
95-
let classes = {
96-
'glyphicon': true,
97-
'form-control-feedback': true,
98-
'glyphicon-ok': this.props.bsStyle === 'success',
99-
'glyphicon-warning-sign': this.props.bsStyle === 'warning',
100-
'glyphicon-remove': this.props.bsStyle === 'error'
101-
};
102-
103-
return this.props.hasFeedback ? (
104-
<span className={classNames(classes)} key="icon" />
105-
) : null;
106-
}
6+
const buttonTypes = ['button', 'reset', 'submit'];
1077

108-
renderHelp() {
109-
return this.props.help ? (
110-
<span className="help-block" key="help">
111-
{this.props.help}
112-
</span>
113-
) : null;
114-
}
115-
116-
renderCheckboxAndRadioWrapper(children) {
117-
let classes = {
118-
'checkbox': this.props.type === 'checkbox',
119-
'radio': this.props.type === 'radio'
120-
};
121-
122-
return (
123-
<div className={classNames(classes)} key="checkboxRadioWrapper">
124-
{children}
125-
</div>
126-
);
127-
}
128-
129-
renderWrapper(children) {
130-
return this.props.wrapperClassName ? (
131-
<div className={this.props.wrapperClassName} key="wrapper">
132-
{children}
133-
</div>
134-
) : children;
135-
}
136-
137-
renderLabel(children) {
138-
let classes = {
139-
'control-label': !this.isCheckboxOrRadio()
140-
};
141-
classes[this.props.labelClassName] = this.props.labelClassName;
142-
143-
return this.props.label ? (
144-
<label htmlFor={this.props.id} className={classNames(classes)} key="label">
145-
{children}
146-
{this.props.label}
147-
</label>
148-
) : children;
149-
}
150-
151-
renderInput() {
152-
if (!this.props.type) {
153-
return this.props.children;
154-
}
155-
156-
switch (this.props.type) {
157-
case 'select':
158-
return (
159-
<select {...this.props} className={classNames(this.props.className, 'form-control')} ref="input" key="input">
160-
{this.props.children}
161-
</select>
162-
);
163-
case 'textarea':
164-
return <textarea {...this.props} className={classNames(this.props.className, 'form-control')} ref="input" key="input" />;
165-
case 'static':
166-
return (
167-
<p {...this.props} className={classNames(this.props.className, 'form-control-static')} ref="input" key="input">
168-
{this.props.value}
169-
</p>
170-
);
171-
case 'submit':
172-
return <Button {...this.props} componentClass="input" ref="input" key="input" />;
173-
}
174-
175-
let className = this.isCheckboxOrRadio() || this.isFile() ? '' : 'form-control';
176-
return <input {...this.props} className={classNames(this.props.className, className)} ref="input" key="input" />;
177-
}
178-
179-
renderFormGroup(children) {
180-
if (this.props.type === 'submit') {
181-
let {bsStyle, ...other} = this.props; /* eslint no-unused-vars: 0 object-shorthand: 0 */
182-
return <FormGroup {...other}>{children}</FormGroup>;
8+
class Input extends InputBase {
9+
render() {
10+
if (buttonTypes.indexOf(this.props.type) > -1) {
11+
deprecationWarning(`Input type=${this.props.type}`, 'ButtonInput');
12+
return <ButtonInput {...this.props} />;
18313
}
18414

185-
return <FormGroup {...this.props}>{children}</FormGroup>;
186-
}
187-
188-
renderChildren() {
189-
return !this.isCheckboxOrRadio() ? [
190-
this.renderLabel(),
191-
this.renderWrapper([
192-
this.renderInputGroup(
193-
this.renderInput()
194-
),
195-
this.renderIcon(),
196-
this.renderHelp()
197-
])
198-
] : this.renderWrapper([
199-
this.renderCheckboxAndRadioWrapper(
200-
this.renderLabel(
201-
this.renderInput()
202-
)
203-
),
204-
this.renderHelp()
205-
]);
206-
}
207-
208-
render() {
209-
let children = this.renderChildren();
210-
return this.renderFormGroup(children);
15+
return super.render();
21116
}
21217
}
21318

214-
Input.propTypes = {
215-
type: React.PropTypes.string,
216-
label: React.PropTypes.node,
217-
help: React.PropTypes.node,
218-
addonBefore: React.PropTypes.node,
219-
addonAfter: React.PropTypes.node,
220-
buttonBefore: React.PropTypes.node,
221-
buttonAfter: React.PropTypes.node,
222-
bsSize: React.PropTypes.oneOf(['small', 'medium', 'large']),
223-
bsStyle(props) {
224-
if (props.type === 'submit') {
225-
return null;
226-
}
227-
return React.PropTypes.oneOf(['success', 'warning', 'error']).apply(null, arguments);
228-
},
229-
hasFeedback: React.PropTypes.bool,
230-
id: React.PropTypes.string,
231-
groupClassName: React.PropTypes.string,
232-
wrapperClassName: React.PropTypes.string,
233-
labelClassName: React.PropTypes.string,
234-
multiple: React.PropTypes.bool,
235-
disabled: React.PropTypes.bool,
236-
value: React.PropTypes.any
237-
};
238-
23919
export default Input;

0 commit comments

Comments
 (0)