-
Notifications
You must be signed in to change notification settings - Fork 377
/
FieldTextInput.js
151 lines (130 loc) · 4.16 KB
/
FieldTextInput.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import React, { Component } from 'react';
import { bool, func, object, shape, string } from 'prop-types';
import { Field } from 'react-final-form';
import classNames from 'classnames';
import { ValidationError, ExpandingTextarea } from '../../components';
import css from './FieldTextInput.module.css';
const CONTENT_MAX_LENGTH = 5000;
class FieldTextInputComponent extends Component {
render() {
/* eslint-disable no-unused-vars */
const {
rootClassName,
className,
inputRootClass,
customErrorText,
id,
label,
input,
meta,
onUnmount,
isUncontrolled,
inputRef,
...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 = input.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 = !!customErrorText || !!(touched && invalid && error);
const fieldMeta = { touched: hasError, error: errorText };
// Textarea doesn't need type.
const { type, ...inputWithoutType } = input;
// Uncontrolled input uses defaultValue instead of value.
const { value: defaultValue, ...inputWithoutValue } = input;
// Use inputRef if it is passed as prop.
const refMaybe = inputRef ? { ref: inputRef } : {};
const inputClasses =
inputRootClass ||
classNames(css.input, {
[css.inputSuccess]: valid,
[css.inputError]: hasError,
[css.textarea]: isTextarea,
});
const maxLength = CONTENT_MAX_LENGTH;
const inputProps = isTextarea
? {
className: inputClasses,
id,
rows: 1,
maxLength,
...refMaybe,
...inputWithoutType,
...rest,
}
: isUncontrolled
? {
className: inputClasses,
id,
type,
defaultValue,
...refMaybe,
...inputWithoutValue,
...rest,
}
: { className: inputClasses, id, type, ...refMaybe, ...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,
onUnmount: null,
customErrorText: null,
id: null,
label: null,
isUncontrolled: false,
inputRef: null,
};
FieldTextInputComponent.propTypes = {
rootClassName: string,
className: string,
inputRootClass: string,
onUnmount: func,
// 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,
// Uncontrolled input uses defaultValue prop, but doesn't pass value from form to the field.
// https://reactjs.org/docs/uncontrolled-components.html#default-values
isUncontrolled: bool,
// a ref object passed for input element.
inputRef: object,
// Generated by final-form's Field component
input: shape({
onChange: func.isRequired,
// Either 'textarea' or something that is passed to the input element
type: string.isRequired,
}).isRequired,
meta: object.isRequired,
};
class FieldTextInput extends Component {
componentWillUnmount() {
// Unmounting happens too late if it is done inside Field component
// (Then Form has already registered its (new) fields and
// changing the value without corresponding field is prohibited in Final Form
if (this.props.onUnmount) {
this.props.onUnmount();
}
}
render() {
return <Field component={FieldTextInputComponent} {...this.props} />;
}
}
export default FieldTextInput;