Unverified Commit 1d25dfca by Farhanah Sheets Committed by GitHub

fix(input): Use prop.id/prop.type when available and allow for ref on inputs

Leverage the ID that is passed through as prop when it's available, allow for InputText to accept a type prop, and allow for input components to be assigned a ref
parent 57c13c46
......@@ -49,6 +49,7 @@ class Select extends React.Component {
aria-describedby={props.describedBy}
onChange={props.onChange}
onBlur={props.onBlur}
ref={props.inputRef}
>
{options}
</select>
......
......@@ -3,6 +3,41 @@ import React from 'react';
import { storiesOf } from '@storybook/react';
import InputText from './index';
import StatusAlert from '../StatusAlert';
class FocusInputWrapper extends React.Component {
constructor(props) {
super(props);
this.state = { open: true };
this.resetStatusAlertWrapperState = this.resetStatusAlertWrapperState.bind(this);
}
resetStatusAlertWrapperState() {
this.setState({ open: false });
this.inputForm.focus();
}
render() {
return (
<div>
<StatusAlert
alertType="info"
open={this.state.open}
dialog="Close me to input data!"
onClose={this.resetStatusAlertWrapperState}
/>
<InputText
id="data"
name="data"
label="Data Input"
inputRef={(input) => { this.inputForm = input; }}
/>
</div>
);
}
}
storiesOf('InputText', module)
.add('minimal usage', () => (
......@@ -14,6 +49,7 @@ storiesOf('InputText', module)
))
.add('validation', () => (
<InputText
id="username"
name="username"
label="Username"
description="The unique name that identifies you throughout the site."
......@@ -28,4 +64,7 @@ storiesOf('InputText', module)
return feedback;
}}
/>
))
.add('focus test', () => (
<FocusInputWrapper />
));
......@@ -8,7 +8,7 @@ function Text(props) {
<input
id={props.id}
className={classNames(props.className)}
type="text"
type={props.type || 'text'}
name={props.name}
value={props.value}
placeholder={props.placeholder}
......@@ -18,6 +18,7 @@ function Text(props) {
aria-invalid={!props.isValid}
disabled={props.disabled}
required={props.required}
ref={props.inputRef}
/>
);
}
......
......@@ -8,7 +8,6 @@ function Text(props) {
<textarea
id={props.id}
className={classNames(props.className)}
type="text"
name={props.name}
value={props.value}
placeholder={props.placeholder}
......@@ -18,6 +17,7 @@ function Text(props) {
aria-invalid={!props.isValid}
disabled={props.disabled}
required={props.required}
ref={props.inputRef}
/>
);
}
......
......@@ -8,6 +8,7 @@ import asInput, { getDisplayName } from './index';
function testComponent(props) {
return (
<input
id={props.id}
defaultValue={props.value}
onBlur={props.onBlur}
onChange={props.onChange}
......@@ -43,6 +44,43 @@ describe('asInput()', () => {
expect(wrapper.state('value')).toEqual(props.value);
});
it('creates generic prop id', () => {
const props = {
...baseProps,
};
const wrapper = mount(<InputTestComponent {...props} />);
expect(wrapper.state('id')).toContain('asInput');
expect(wrapper.find('label').prop('id')).toContain('asInput');
expect(wrapper.find('input').prop('id')).toContain('asInput');
expect(wrapper.find('small').prop('id')).toContain('asInput');
});
it('creates generic prop id when passed null id value', () => {
const testId = null;
const props = {
...baseProps,
id: testId,
};
const wrapper = mount(<InputTestComponent {...props} />);
expect(wrapper.state('id')).toContain('asInput');
expect(wrapper.find('label').prop('id')).toContain('asInput');
expect(wrapper.find('input').prop('id')).toContain('asInput');
expect(wrapper.find('small').prop('id')).toContain('asInput');
});
it('uses passed in prop id', () => {
const testId = 'testId';
const props = {
...baseProps,
id: testId,
};
const wrapper = mount(<InputTestComponent {...props} />);
expect(wrapper.state('id')).toEqual(testId);
expect(wrapper.find('label').prop('id')).toEqual(`label-${testId}`);
expect(wrapper.find('input').prop('id')).toEqual(testId);
expect(wrapper.find('small').prop('id')).toEqual(`description-${testId}`);
});
describe('fires', () => {
it('blur handler', () => {
const spy = jest.fn();
......
......@@ -11,6 +11,7 @@ export const getDisplayName = WrappedComponent =>
export const inputProps = {
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
id: PropTypes.string,
value: PropTypes.string,
description: PropTypes.oneOfType([
PropTypes.string,
......@@ -31,7 +32,7 @@ const asInput = (WrappedComponent, labelFirst = true) => {
this.handleChange = this.handleChange.bind(this);
this.handleBlur = this.handleBlur.bind(this);
const id = newId('asInput');
const id = this.props.id ? this.props.id : newId('asInput');
this.state = {
id,
value: this.props.value,
......@@ -90,7 +91,7 @@ const asInput = (WrappedComponent, labelFirst = true) => {
const { description, error, describedBy } = this.getDescriptions();
return (
<div className={styles['form-group']}>
{labelFirst && <label htmlFor={this.state.id}>{this.props.label}</label>}
{labelFirst && <label id={`label-${this.state.id}`} htmlFor={this.state.id}>{this.props.label}</label>}
<WrappedComponent
{...this.props}
{...this.state}
......@@ -117,6 +118,7 @@ const asInput = (WrappedComponent, labelFirst = true) => {
NewComponent.defaultProps = {
onChange: () => {},
onBlur: () => {},
id: newId('asInput'),
value: '',
description: undefined,
disabled: false,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment