CustomInput.jsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import React, { Component } from "react";
  2. import PropTypes from "prop-types";
  3. import reactTriggerChange from "react-trigger-change";
  4. const isLength = (string, min, max) => {
  5. return !(typeof string !== "string" || string.length < min || string.length > max);
  6. };
  7. const regex = {
  8. azAZ09_: /^[A-Za-z0-9_]+$/,
  9. azAZ09: /^[A-Za-z0-9]+$/,
  10. az09_: /^[a-z0-9_]+$/,
  11. az09: /^[a-z0-9]+$/,
  12. emailSimple: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-z0-9]+\.[a-z0-9]+(\.[a-z0-9]+)?$/,
  13. password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]/,
  14. ascii: /^[\x00-\x7F]+$/,
  15. };
  16. const dictionary = {
  17. username: {
  18. inputType: "text",
  19. minLength: 2,
  20. maxLength: 32,
  21. regex: regex.azAZ09_,
  22. errors: {
  23. format: "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _."
  24. },
  25. },
  26. email: {
  27. inputType: "email",
  28. minLength: 3,
  29. maxLength: 254,
  30. regex: regex.emailSimple,
  31. errors: {
  32. format: "Invalid email format. Email must contain one @ symbol.",
  33. },
  34. },
  35. password: {
  36. inputType: "password",
  37. minLength: 6,
  38. maxLength: 200,
  39. regex: regex.password,
  40. errors: {
  41. format: "Invalid password format. Password must have at least 1 lowercase letter, 1 uppercase letter, 1 number and one special character ($@$!%*?&).",
  42. },
  43. },
  44. uniqueCode: {
  45. inputType: "text",
  46. minLength: 8,
  47. maxLength: 8,
  48. regex: regex.azAZ09,
  49. errors: {
  50. length: "Code must be 8 characters long.",
  51. format: "Invalid code format.",
  52. },
  53. },
  54. };
  55. export default class CustomInput extends Component {
  56. static propTypes = {
  57. type: PropTypes.string,
  58. name: PropTypes.string,
  59. label: PropTypes.string,
  60. placeholder: PropTypes.string,
  61. onRef: PropTypes.func,
  62. };
  63. static defaultProps = {
  64. type: "",
  65. name: "",
  66. label: "",
  67. placeholder: "",
  68. valid: false,
  69. onRef: () => {},
  70. };
  71. static initialize = (context) => {
  72. context.input = {}; // eslint-disable-line no-param-reassign
  73. };
  74. static hasInvalidInput = (input, properties) => {
  75. let invalid = false;
  76. if (properties) {
  77. properties.forEach((property) => {
  78. if (!input[property].isValid()) invalid = true;
  79. });
  80. } else {
  81. Object.keys(input).forEach((key) => {
  82. if (!input[key].isValid()) invalid = true;
  83. });
  84. }
  85. return invalid;
  86. };
  87. static isTheSame = (input, properties) => {
  88. let invalid = false;
  89. const value = input[properties[0]].getValue();
  90. properties.forEach((key) => {
  91. if (input[key].getValue() !== value) invalid = true;
  92. });
  93. return invalid;
  94. };
  95. constructor(props) {
  96. super(props);
  97. this.state = {
  98. inputType: dictionary[props.type].inputType,
  99. value: "",
  100. original: "",
  101. errors: [],
  102. pristine: true,
  103. disabled: false,
  104. valid: false,
  105. };
  106. // More values/functions needs like isEmpty, isRequired
  107. }
  108. componentDidMount() {
  109. this.props.onRef(this);
  110. }
  111. componentWillUnmount() {
  112. this.props.onRef(null);
  113. }
  114. onBlur = () => {
  115. this.validate();
  116. };
  117. onFocus = () => {
  118. this.setState({
  119. pristine: false,
  120. });
  121. };
  122. onChange = (event) => {
  123. this.setState({
  124. value: event.target.value,
  125. });
  126. };
  127. setValue = (value, original = false) => {
  128. const state = {
  129. value,
  130. };
  131. if (original) state.original = value;
  132. this.setState(state);
  133. };
  134. getValue = () => {
  135. return this.state.value;
  136. };
  137. listErrors = () => {
  138. let errors = this.state.errors;
  139. let key = 0;
  140. if (errors.length > 0) {
  141. errors = errors.map((error) => {
  142. key++;
  143. return (<li key={ key }>{ error }</li>);
  144. });
  145. return (
  146. <ul className="validation-errors">
  147. { errors }
  148. </ul>
  149. );
  150. } return "";
  151. };
  152. isValid = () => {
  153. return this.state.valid;
  154. };
  155. isOriginal = () => {
  156. return this.state.original === this.state.value;
  157. };
  158. isPristine = () => {
  159. return this.state.pristine;
  160. };
  161. validate = (cb = () => {}) => {
  162. const errors = [];
  163. const info = dictionary[this.props.type];
  164. const value = this.state.value;
  165. if (!isLength(value, info.minLength, info.maxLength)) errors.push((info.errors.length) ? info.errors.length : `Value must be between ${ info.minLength } and ${ info.maxLength } characters long.`);
  166. if (!info.regex.test(value)) errors.push(info.errors.format);
  167. this.setState({
  168. errors,
  169. valid: errors.length === 0,
  170. }, cb);
  171. };
  172. render() {
  173. return (
  174. <label htmlFor={ this.props.name }>
  175. <span>{ this.props.label }</span>
  176. <input
  177. placeholder={ this.props.placeholder }
  178. type={ this.state.inputType }
  179. name={ this.props.name }
  180. value={ this.state.value }
  181. className={ (this.state.errors.length > 0) ? "has-validation-errors" : "" }
  182. onBlur={ this.onBlur }
  183. onFocus={ this.onFocus }
  184. onChange={ this.onChange }
  185. ref={ (input) => this.inputElement = input }
  186. />
  187. { this.listErrors() }
  188. </label>
  189. );
  190. }
  191. }