Create FieldTextInput: Final Form version of TextInputField

This commit is contained in:
Vesa Luusua 2018-04-16 11:58:30 +03:00
parent fe7dedd43d
commit 63bd9aed65
6 changed files with 236 additions and 0 deletions

View file

@ -0,0 +1,19 @@
@import '../../marketplace.css';
.root {
}
.input {
border-bottom-color: var(--attentionColor);
}
.inputSuccess {
border-bottom-color: var(--successColor);
}
.inputError {
border-bottom-color: var(--failColor);
}
.textarea {
}

View file

@ -0,0 +1,7 @@
.field {
margin-top: 24px;
}
.submit {
margin-top: 24px;
}

View file

@ -0,0 +1,90 @@
/* eslint-disable no-console */
import React from 'react';
import { Form as FinalForm, FormSpy } from 'react-final-form';
import * as validators from '../../util/validators';
import { Button } from '../../components';
import FieldTextInput from './FieldTextInput';
import css from './FieldTextInput.example.css';
const FormComponent = props => (
<FinalForm
{...props}
render={fieldRenderProps => {
const { handleSubmit, onChange, invalid, pristine, submitting, formName } = fieldRenderProps;
const required = validators.required('This field is required');
const submitDisabled = invalid || pristine || submitting;
return (
<form
onSubmit={e => {
e.preventDefault();
handleSubmit(e);
}}
>
<FormSpy onChange={onChange} />
<FieldTextInput
className={css.field}
type="text"
id={`${formName}.input1`}
name="input1"
label="Input that requires a value:"
validate={required}
/>
<FieldTextInput
className={css.field}
type="text"
id={`${formName}.input2`}
name="input2"
label="Input that does not require a value:"
/>
<FieldTextInput
className={css.field}
type="text"
name="input3"
placeholder="Input without label..."
/>
<FieldTextInput
className={css.field}
type="textarea"
id={`${formName}.textarea1`}
name="textarea1"
label="Textarea that requires a value:"
validate={required}
/>
<FieldTextInput
className={css.field}
type="textarea"
id={`${formName}.textarea2`}
name="textarea2"
label="Textarea that does not require a value:"
/>
<FieldTextInput
className={css.field}
type="textarea"
name="textarea3"
placeholder="Textarea without label..."
/>
<Button className={css.submit} type="submit" disabled={submitDisabled}>
Submit
</Button>
</form>
);
}}
/>
);
export const Inputs = {
component: FormComponent,
props: {
formName: 'Inputs',
onChange: formState => {
if (Object.keys(formState.values).length > 0) {
console.log('form values changed to:', formState.values);
}
},
onSubmit: values => {
console.log('submit values:', values);
},
},
group: 'inputs',
};

View file

@ -0,0 +1,117 @@
/**
* NOTE this component is part of react-final-form instead of Redux Form.
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Field } from 'react-final-form';
import classNames from 'classnames';
import { ValidationError, ExpandingTextarea } from '../../components';
import css from './FieldTextInput.css';
const CONTENT_MAX_LENGTH = 5000;
class FieldTextInputComponent extends Component {
componentWillUnmount() {
if (this.props.clearOnUnmount) {
this.props.input.onChange('');
}
}
render() {
/* eslint-disable no-unused-vars */
const {
rootClassName,
className,
inputRootClass,
clearOnUnmount,
customErrorText,
id,
label,
type,
input,
meta,
...rest
} = this.props;
/* eslint-enable no-unused-vars */
if (label && !id) {
throw new Error('id required when a label is given');
}
const { valid, invalid, touched, error } = meta;
const isTextarea = type === 'textarea';
const errorText = customErrorText || error;
// Error message and input error styles are only shown if the
// field has been touched and the validation has failed.
const hasError = touched && invalid && errorText;
const fieldMeta = { touched, error: errorText };
const inputClasses =
inputRootClass ||
classNames(css.input, {
[css.inputSuccess]: valid,
[css.inputError]: hasError,
[css.textarea]: isTextarea,
});
const inputProps = isTextarea
? { className: inputClasses, id, rows: 1, maxLength: CONTENT_MAX_LENGTH, ...input, ...rest }
: { className: inputClasses, id, type, ...input, ...rest };
const classes = classNames(rootClassName || css.root, className);
return (
<div className={classes}>
{label ? <label htmlFor={id}>{label}</label> : null}
{isTextarea ? <ExpandingTextarea {...inputProps} /> : <input {...inputProps} />}
<ValidationError fieldMeta={fieldMeta} />
</div>
);
}
}
FieldTextInputComponent.defaultProps = {
rootClassName: null,
className: null,
inputRootClass: null,
clearOnUnmount: false,
customErrorText: null,
id: null,
label: null,
};
const { string, bool, shape, func, object } = PropTypes;
FieldTextInputComponent.propTypes = {
rootClassName: string,
className: string,
inputRootClass: string,
clearOnUnmount: bool,
// Error message that can be manually passed to input field,
// overrides default validation message
customErrorText: string,
// Label is optional, but if it is given, an id is also required so
// the label can reference the input in the `for` attribute
id: string,
label: string,
// Either 'textarea' or something that is passed to the input element
type: string.isRequired,
// Generated by redux-form's Field component
input: shape({
onChange: func.isRequired,
}).isRequired,
meta: object.isRequired,
};
const FieldTextInput = props => {
return <Field component={FieldTextInputComponent} {...props} />;
};
export default FieldTextInput;

View file

@ -38,6 +38,7 @@ export { default as FieldDateRangeInput } from './FieldDateRangeInput/FieldDateR
export { default as FieldGroupCheckbox } from './FieldGroupCheckbox/FieldGroupCheckbox';
export { default as FieldPhoneNumberInput } from './FieldPhoneNumberInput/FieldPhoneNumberInput';
export { default as FieldReviewRating } from './FieldReviewRating/FieldReviewRating';
export { default as FieldTextInput } from './FieldTextInput/FieldTextInput';
export { default as Footer } from './Footer/Footer';
export { default as Form } from './Form/Form';
export { default as IconBannedUser } from './IconBannedUser/IconBannedUser';

View file

@ -14,6 +14,7 @@ import * as FieldDateRangeInput from './components/FieldDateRangeInput/FieldDate
import * as FieldGroupCheckbox from './components/FieldGroupCheckbox/FieldGroupCheckbox.example';
import * as FieldPhoneNumberInput from './components/FieldPhoneNumberInput/FieldPhoneNumberInput.example';
import * as FieldReviewRating from './components/FieldReviewRating/FieldReviewRating.example';
import * as FieldTextInput from './components/FieldTextInput/FieldTextInput.example';
import * as Footer from './components/Footer/Footer.example';
import * as IconBannedUser from './components/IconBannedUser/IconBannedUser.example';
import * as IconCheckmark from './components/IconCheckmark/IconCheckmark.example';
@ -104,6 +105,7 @@ export {
FieldGroupCheckbox,
FieldPhoneNumberInput,
FieldReviewRating,
FieldTextInput,
Footer,
IconBannedUser,
IconCheckmark,