src/views/form/field.view.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon as FA } from '@fortawesome/react-fontawesome';
import { faCheckCircle, faTimesCircle } from '@fortawesome/free-solid-svg-icons';
/**
* Base class for all form fields. It's supposed to be used as a super class
* but in case it's used as a component it renders an input type text
*
* #### SCSS Import
* ```
* @import "~@orloxx/ui-core/scss/form/field";
* ```
*
* @example
* <Field id='username' name='username'
* label='Username' placeholder='Enter your username'
* suggestion='Only accepts letters and numbers'
* patternError='Input format is wrong, only accepts letters and numbers'
* required pattern='^[a-zA-Z0-9]+$' />
*/
class Field extends Component {
/**
* @type {Object}
* @property {String} id - The input element `id` attribute
* @property {String} name - The input element `name` attribute
* @property {String} label - The label attached to the input field
* @property {String} [type='text'] - The input element `type` attribute
* @property {Boolean} [required] - The input element `required` attribute
* @property {String} [pattern] - The pattern to validate the input's value
* @property {String} [placeholder] - Placeholder text
* @property {String} [requiredLabel='(required)'] - The required label added to the input's label
* @property {String} [requiredError='This field is required'] - The error message when the required input is empty
* @property {String} [patternError='Please enter correct format'] - The error message when the input's pattern is not matched
* @property {String} [suggestion] - Suggestion text for the user
*/
static propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
type: PropTypes.string,
required: PropTypes.bool,
pattern: PropTypes.string,
placeholder: PropTypes.string,
requiredLabel: PropTypes.string,
requiredError: PropTypes.string,
patternError: PropTypes.string,
suggestion: PropTypes.string,
};
/**
* @ignore
*/
static defaultProps = {
type: 'text',
requiredLabel: '(required)',
requiredError: 'This field is required',
patternError: 'Please enter correct format',
};
/**
* @ignore
*/
constructor(props) {
super(props);
/**
* @ignore
*/
this.input = React.createRef();
/**
* @ignore
*/
this.state = { isValid: true, isPattern: true, isDirty: false };
}
/**
* @ignore
*/
get isValid() {
const { required } = this.props;
if (required) {
const { current } = this.input;
return !!current.value;
}
return true;
}
/**
* @ignore
*/
get isPattern() {
const { pattern } = this.props;
const { current } = this.input;
if (pattern && current.value) {
const reg = new RegExp(pattern, 'g');
return reg.test(current.value);
}
return true;
}
/**
* @ignore
*/
get fieldClasses() {
const { isValid, isPattern } = this.state;
return !isValid || !isPattern ? 'field--error' : '';
}
/**
* @ignore
*/
get requiredLabel() {
if (this.props.required) {
return this.props.requiredLabel;
}
}
/**
* @ignore
*/
validate() {
this.setState({
isDirty: true,
isValid: this.isValid,
isPattern: this.isPattern,
});
}
/**
* @ignore
*/
renderValidationIcon() {
const { isValid, isPattern, isDirty } = this.state;
const { required, pattern } = this.props;
if ((required || pattern) && isDirty) {
return isValid && isPattern
? (<div className='field__icon field__icon--valid'>
<FA icon={faCheckCircle} /></div>)
: (<div className='field__icon field__icon--invalid'>
<FA icon={faTimesCircle} /></div>);
}
}
/**
* @ignore
*/
renderValidationMessages() {
const { isValid, isPattern, isDirty } = this.state;
if (isDirty) {
if (!isValid) {
return (<em className='field__msg field__msg--error'>
{this.props.requiredError}
</em>);
} else if (!isPattern) {
return (<em className='field__msg field__msg--error'>
{this.props.patternError}
</em>);
}
}
if (this.props.suggestion) {
return (<em className='field__msg'>{this.props.suggestion}</em>);
}
}
/**
* @ignore
*/
render() {
const { id, name, label, type, required, pattern, placeholder } = this.props;
return (
<div className={`field ${this.fieldClasses}`}>
<label className='field__label' htmlFor={id}>{label} {this.requiredLabel}</label>
<div className='field__inputWrapper'>
<input
className='field__input' ref={this.input}
type={type} id={id} name={name}
placeholder={placeholder} title={placeholder}
required={required} pattern={pattern}
onBlur={() => this.validate()} />
{this.renderValidationIcon()}
</div>
{this.renderValidationMessages()}
</div>
);
}
}
export default Field;