diff --git a/src/components/FieldTextInput/FieldTextInput.css b/src/components/FieldTextInput/FieldTextInput.css new file mode 100644 index 00000000..2294a88d --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.css @@ -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 { +} diff --git a/src/components/FieldTextInput/FieldTextInput.example.css b/src/components/FieldTextInput/FieldTextInput.example.css new file mode 100644 index 00000000..9fe2d13d --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.example.css @@ -0,0 +1,7 @@ +.field { + margin-top: 24px; +} + +.submit { + margin-top: 24px; +} diff --git a/src/components/FieldTextInput/FieldTextInput.example.js b/src/components/FieldTextInput/FieldTextInput.example.js new file mode 100644 index 00000000..68cd4e2e --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.example.js @@ -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 => ( + { + const { handleSubmit, onChange, invalid, pristine, submitting, formName } = fieldRenderProps; + const required = validators.required('This field is required'); + const submitDisabled = invalid || pristine || submitting; + return ( +
{ + e.preventDefault(); + handleSubmit(e); + }} + > + + + + + + + + + + ); + }} + /> +); + +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', +}; diff --git a/src/components/FieldTextInput/FieldTextInput.js b/src/components/FieldTextInput/FieldTextInput.js new file mode 100644 index 00000000..bef47687 --- /dev/null +++ b/src/components/FieldTextInput/FieldTextInput.js @@ -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 ( +
+ {label ? : null} + {isTextarea ? : } + +
+ ); + } +} + +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 ; +}; + +export default FieldTextInput; diff --git a/src/components/index.js b/src/components/index.js index aa2b2ad4..ab485876 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -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'; diff --git a/src/examples.js b/src/examples.js index 6b057aa6..776a2f20 100644 --- a/src/examples.js +++ b/src/examples.js @@ -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,