Input.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import React, { PureComponent } from 'react';
  2. import { ValidationEvents, ValidationRule } from 'app/types';
  3. import { validate, hasValidationEvent } from 'app/core/utils/validate';
  4. export enum InputStatus {
  5. Invalid = 'invalid',
  6. Valid = 'valid',
  7. }
  8. export enum InputTypes {
  9. Text = 'text',
  10. Number = 'number',
  11. Password = 'password',
  12. Email = 'email',
  13. }
  14. export enum EventsWithValidation {
  15. onBlur = 'onBlur',
  16. onFocus = 'onFocus',
  17. onChange = 'onChange',
  18. }
  19. interface Props extends React.HTMLProps<HTMLInputElement> {
  20. validationEvents?: ValidationEvents;
  21. hideErrorMessage?: boolean;
  22. // Override event props and append status as argument
  23. onBlur?: (event: React.FocusEvent<HTMLInputElement>, status?: InputStatus) => void;
  24. onFocus?: (event: React.FocusEvent<HTMLInputElement>, status?: InputStatus) => void;
  25. onChange?: (event: React.FormEvent<HTMLInputElement>, status?: InputStatus) => void;
  26. }
  27. export class Input extends PureComponent<Props> {
  28. static defaultProps = {
  29. className: '',
  30. };
  31. state = {
  32. error: null,
  33. };
  34. get status() {
  35. return this.state.error ? InputStatus.Invalid : InputStatus.Valid;
  36. }
  37. get isInvalid() {
  38. return this.status === InputStatus.Invalid;
  39. }
  40. validatorAsync = (validationRules: ValidationRule[]) => {
  41. return evt => {
  42. const errors = validate(evt.target.value, validationRules);
  43. this.setState(prevState => {
  44. return {
  45. ...prevState,
  46. error: errors ? errors[0] : null,
  47. };
  48. });
  49. };
  50. };
  51. populateEventPropsWithStatus = (restProps, validationEvents: ValidationEvents) => {
  52. const inputElementProps = { ...restProps };
  53. Object.keys(EventsWithValidation).forEach((eventName: EventsWithValidation) => {
  54. if (hasValidationEvent(eventName, validationEvents) || restProps[eventName]) {
  55. inputElementProps[eventName] = async evt => {
  56. evt.persist(); // Needed for async. https://reactjs.org/docs/events.html#event-pooling
  57. if (hasValidationEvent(eventName, validationEvents)) {
  58. await this.validatorAsync(validationEvents[eventName]).apply(this, [evt]);
  59. }
  60. if (restProps[eventName]) {
  61. restProps[eventName].apply(null, [evt, this.status]);
  62. }
  63. };
  64. }
  65. });
  66. return inputElementProps;
  67. };
  68. render() {
  69. const { validationEvents, className, hideErrorMessage, ...restProps } = this.props;
  70. const { error } = this.state;
  71. const inputClassName = 'gf-form-input' + (this.isInvalid ? ' invalid' : '') + ' ' + className;
  72. const inputElementProps = this.populateEventPropsWithStatus(restProps, validationEvents);
  73. return (
  74. <div className="our-custom-wrapper-class">
  75. <input {...inputElementProps} className={inputClassName} />
  76. {error && !hideErrorMessage && <span>{error}</span>}
  77. </div>
  78. );
  79. }
  80. }