Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TextField] - Implement helper text/error text as a separate component #4526

Closed
wants to merge 11 commits into from
3 changes: 1 addition & 2 deletions src/Table/TableBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ class TableBody extends Component {
componentWillReceiveProps(nextProps) {
if (this.props.allRowsSelected && !nextProps.allRowsSelected) {
this.setState({
selectedRows: this.state.selectedRows.length > 0 ?
[this.state.selectedRows[this.state.selectedRows.length - 1]] : [],
selectedRows: [],
});
// TODO: should else be conditional, not run any time props other than allRowsSelected change?
} else {
Expand Down
23 changes: 11 additions & 12 deletions src/TextField/TextField.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import EnhancedTextarea from './EnhancedTextarea';
import TextFieldHint from './TextFieldHint';
import TextFieldLabel from './TextFieldLabel';
import TextFieldUnderline from './TextFieldUnderline';
import TextFieldHelper from './TextFieldHelper';
import warning from 'warning';

const getStyles = (props, context, state) => {
Expand Down Expand Up @@ -38,12 +39,7 @@ const getStyles = (props, context, state) => {
transition: transitions.easeOut('200ms', 'height'),
},
error: {
position: 'relative',
bottom: 2,
fontSize: 12,
lineHeight: '12px',
color: errorColor,
transition: transitions.easeOut(),
},
floatingLabel: {
color: hintColor,
Expand Down Expand Up @@ -91,10 +87,6 @@ const getStyles = (props, context, state) => {
if (!props.multiLine) {
styles.input.marginTop = 14;
}

if (state.errorText) {
styles.error.bottom = !props.multiLine ? styles.error.fontSize + 3 : 3;
}
}

if (state.errorText) {
Expand Down Expand Up @@ -416,9 +408,16 @@ class TextField extends Component {
const styles = getStyles(this.props, this.context, this.state);
const inputId = id || this.uniqueId;

const errorTextElement = this.state.errorText && (
<div style={prepareStyles(styles.error)}>{this.state.errorText}</div>
);
const errorTextElement = this.state.errorText ? (
<TextFieldHelper
adjustForMultiLine={multiLine}
adjustForFloatingLabel={Boolean(floatingLabelText)}

style={styles.error}
muiTheme={this.context.muiTheme}
errorText={this.state.errorText}
/>
) : null;

const floatingLabelTextElement = floatingLabelText && (
<TextFieldLabel
Expand Down
82 changes: 82 additions & 0 deletions src/TextField/TextFieldHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, {PropTypes} from 'react';
import transitions from '../styles/transitions';

const getStyles = (props) => {
const {
muiTheme: {
textField: {
hintColor,
errorColor,
},
},

errorText,
adjustForMultiLine,
adjustForFloatingLabel,
} = props;

const styles = {
root: {
position: 'relative',
bottom: 2,
fontSize: 12,
lineHeight: '12px',
color: errorText ? errorColor : hintColor,
transition: transitions.easeOut(),
},
};

if (adjustForFloatingLabel) {
styles.root.bottom = adjustForMultiLine ? 3 : styles.root.fontSize + 3;
}

return styles;
};

const TextFieldHelperText = (props) => {
const {
muiTheme: {
prepareStyles,
},
style,
helperText,
errorText,
adjustForMultiLine, // eslint-disable-line no-unused-vars
adjustForFloatingLabel, // eslint-disable-line no-unused-vars
...restProps,
} = props;

const styles = getStyles(props);

return (
<div style={prepareStyles(Object.assign(styles.root, style))} {...restProps}>
{errorText || helperText}
</div>
);
};

TextFieldHelperText.propTypes = {
/** @ignore */
adjustForFloatingLabel: PropTypes.bool,
/** @ignore */
adjustForMultiLine: PropTypes.bool,
/**
* The error text that will override helper text.
*/
errorText: PropTypes.node,
/**
* The helper text displayed.
*/
helperText: PropTypes.node,
/**
* @ignore
* The material-ui theme applied to this component.
*/
muiTheme: PropTypes.object.isRequired,
/**
* Override the inline-styles of the root element.
*/
style: PropTypes.object,
};

export default TextFieldHelperText;
98 changes: 98 additions & 0 deletions src/TextField/TextFieldHelper.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* eslint-env mocha */
import React from 'react';
import {shallow} from 'enzyme';
import {expect} from 'chai';
import TextFieldHelper from './TextFieldHelper';
import getMuiTheme from '../styles/getMuiTheme';

describe('<TextFieldHelper>', () => {
it('should prefer errorText prop over helperText', () => {
const themeDefaults = {
textField: {
hintColor: 'grey',
errorColor: 'red',
},
};

const wrapper = shallow(
<TextFieldHelper
helperText="Foo"
muiTheme={getMuiTheme(themeDefaults)}
/>
);

expect(wrapper.type()).to.equal('div');

expect(wrapper.prop('style').color).to.equal('grey');
expect(wrapper.text()).to.equal('Foo');

wrapper.setProps({errorText: 'Bar'});

expect(wrapper.prop('style').color).to.equal('red');
expect(wrapper.text()).to.equal('Bar');

wrapper.setProps({errorText: false});

expect(wrapper.prop('style').color).to.equal('grey');
expect(wrapper.text()).to.equal('Foo');

wrapper.setProps({errorText: null});

expect(wrapper.prop('style').color).to.equal('grey');
expect(wrapper.text()).to.equal('Foo');
});

it('should override default styles', () => {
const wrapper = shallow(
<TextFieldHelper
style={{color: 'blue'}}
muiTheme={getMuiTheme()}
/>
);

expect(wrapper.type()).to.equal('div');

expect(wrapper.prop('style').color).to.equal('blue');
});

it('should adjust styles for floating label', () => {
const wrapper = shallow(
<TextFieldHelper
muiTheme={getMuiTheme()}
/>
);

expect(wrapper.type()).to.equal('div');

expect(wrapper.prop('style').bottom).to.equal(2);

wrapper.setProps({adjustForFloatingLabel: true});

expect(wrapper.prop('style').bottom).to.equal(15);

wrapper.setProps({adjustForFloatingLabel: false});

expect(wrapper.prop('style').bottom).to.equal(2);
});

it('should adjust styles for multi line with floating label', () => {
const wrapper = shallow(
<TextFieldHelper
muiTheme={getMuiTheme()}
adjustForFloatingLabel={true}
/>
);

expect(wrapper.type()).to.equal('div');

expect(wrapper.prop('style').bottom).to.equal(15);

wrapper.setProps({adjustForMultiLine: true});

expect(wrapper.prop('style').bottom).to.equal(3);

wrapper.setProps({adjustForMultiLine: false});

expect(wrapper.prop('style').bottom).to.equal(15);
});
});