Skip to content

Commit

Permalink
Merge pull request #246 from meetup/WC-70_textinput_pure_component
Browse files Browse the repository at this point in the history
Wc 70 textinput pure component
  • Loading branch information
unjust authored Jul 10, 2017
2 parents 9bddd8e + bfd70c1 commit d87941e
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 120 deletions.
122 changes: 54 additions & 68 deletions src/forms/TextInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,80 +5,66 @@ import cx from 'classnames';
/**
* @module TextInput
*/
class TextInput extends React.Component {
const TextInput = (props) => {

constructor(props) {
super(props);
this.state = {
value: props.value || '',
};
this.onChange = this.onChange.bind(this);
}
const {
name,
value,
label,
labelClassName,
className,
children,
error,
placeholder,
required,
id,
onChange,
isSearch,
maxLength,
pattern,
disabled,
...other
} = props;

onChange(e) {
this.setState({ value: e.target.value });
if (this.props.onChange) {
this.props.onChange(e);
}
}
const classNames = cx(
{ 'field--error': error },
className
);

render() {
const {
name,
value, // eslint-disable-line no-unused-vars
label,
labelClassName,
className,
children,
error,
placeholder,
required,
id,
onChange, // eslint-disable-line no-unused-vars
isSearch,
maxLength,
pattern,
disabled
} = this.props;
const labelClassNames = cx(
'label--field',
{ required, disabled },
labelClassName
);

const classNames = cx(
{ 'field--error': error },
className
);
return (
<div>
{label &&
<label className={labelClassNames} htmlFor={id}>
{label}
</label>
}
<input type={isSearch ? 'search' : 'text'}
name={name}
value={value}
required={required}
placeholder={placeholder}
className={classNames}
onChange={onChange}
maxLength={maxLength}
pattern={pattern}
disabled={disabled}
id={id}
{...other}
/>

const labelClassNames = cx(
'label--field',
{ required, disabled },
labelClassName
);
{ maxLength && <p className='text--caption align--right'>{value.length} / {maxLength}</p> }

return (
<div>
{label &&
<label className={labelClassNames} htmlFor={id}>
{label}
</label>
}
<input type={isSearch ? 'search' : 'text'}
name={name}
value={this.state.value}
required={required}
placeholder={placeholder}
className={classNames}
onChange={this.onChange}
maxLength={maxLength}
pattern={pattern}
disabled={disabled}
id={id} />

{ this.props.maxLength && <p className='text--caption align--right'>{this.state.value.length} / {this.props.maxLength}</p> }

{ error && <p className='text--error'>{error}</p> }
{children}
</div>
);
}
}
{ error && <p className='text--error'>{error}</p> }
{children}
</div>
);
};

TextInput.propTypes = {
name: PropTypes.string.isRequired,
Expand Down
85 changes: 33 additions & 52 deletions src/forms/textInput.test.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import { TestWrapper } from '../utils/testUtils';
import TextInput from './TextInput';

describe('TextInput', function() {
Expand All @@ -21,12 +22,14 @@ describe('TextInput', function() {
required: true,
};
textInputComponent = TestUtils.renderIntoDocument(
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
{...formAttrs}
/>
<TestWrapper>
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
{...formAttrs}
/>
</TestWrapper>
);

inputEl = TestUtils.findRenderedDOMComponentWithTag(textInputComponent, 'input');
Expand Down Expand Up @@ -57,12 +60,14 @@ describe('TextInput', function() {

it('should have input type search if `isSearch` is set to true', () => {
textInputComponent = TestUtils.renderIntoDocument(
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
isSearch
/>
<TestWrapper>
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
isSearch
/>
</TestWrapper>
);

inputEl = TestUtils.findRenderedDOMComponentWithTag(textInputComponent, 'input');
Expand All @@ -72,12 +77,14 @@ describe('TextInput', function() {

it('should have a disabled attribute when specified', () => {
textInputComponent = TestUtils.renderIntoDocument(
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
disabled
/>
<TestWrapper>
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
disabled
/>
</TestWrapper>
);

inputEl = TestUtils.findRenderedDOMComponentWithTag(textInputComponent, 'input');
Expand All @@ -102,43 +109,17 @@ describe('TextInput', function() {
expect(inputEl.getAttribute('maxLength')).toEqual(MAX_LEN);
});

it('should set its value on input change', function() {
const newValue = `${VALUE}r`;
expect(inputEl.value).toEqual(VALUE);
TestUtils.Simulate.change(inputEl, { target: { value: newValue } });
expect(inputEl.value).toEqual(newValue);
});

it('should call onChange and setState with input change', function() {
const newValue = `${VALUE}r`;
const changeSpy = spyOn(TextInput.prototype, 'onChange').and.callThrough();
const stateSpy = spyOn(TextInput.prototype, 'setState').and.callThrough();

const boundComponent = TestUtils.renderIntoDocument(
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
onChange={onChange}
/>
);

inputEl = TestUtils.findRenderedDOMComponentWithTag(boundComponent, 'input');
TestUtils.Simulate.change(inputEl, { target: { value: newValue } });

expect(changeSpy).toHaveBeenCalled();
expect(stateSpy).toHaveBeenCalledWith({ value: newValue });
});

it('should call onChange `props` function when input is changed', () => {
const newValue = `${VALUE}r`;
const boundComponent = TestUtils.renderIntoDocument(
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
onChange={onChange}
/>
<TestWrapper>
<TextInput
name={NAME_ATTR}
label={LABEL_TEXT}
value={VALUE}
onChange={onChange}
/>
</TestWrapper>
);
inputEl = TestUtils.findRenderedDOMComponentWithTag(boundComponent, 'input');
TestUtils.Simulate.change(inputEl, { target: { value: newValue } });
Expand Down
11 changes: 11 additions & 0 deletions src/utils/testUtils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import createReactClass from 'create-react-class';
import TestUtils from 'react-addons-test-utils';

export const variantTest = (FoundationComponent, className, variants) => {
Expand Down Expand Up @@ -32,3 +33,13 @@ export const hasRoleAttribute = (el, roleName) => {
export const componentHasProperty = (component, prop, value) => {
expect(component && component.props && component.props[prop] === value).toBe(true);
};

// Note: functional components need to be
// wrapped it in a stateful component to use TestUtils effectively
export const TestWrapper = createReactClass({
render: function() {
return (
<div>{this.props.children}</div>
);
}
});

0 comments on commit d87941e

Please sign in to comment.