Peter Holmberg vor 6 Jahren
Ursprung
Commit
c0eb140297

+ 93 - 0
packages/grafana-ui/src/components/Input/Input.tsx

@@ -0,0 +1,93 @@
+import React, { PureComponent } from 'react';
+import classNames from 'classnames';
+import { ValidationEvents, ValidationRule } from '../../types/forms';
+
+export enum InputStatus {
+  Invalid = 'invalid',
+  Valid = 'valid',
+}
+
+export enum InputTypes {
+  Text = 'text',
+  Number = 'number',
+  Password = 'password',
+  Email = 'email',
+}
+
+export enum EventsWithValidation {
+  onBlur = 'onBlur',
+  onFocus = 'onFocus',
+  onChange = 'onChange',
+}
+
+interface Props extends React.HTMLProps<HTMLInputElement> {
+  validationEvents?: ValidationEvents;
+  hideErrorMessage?: boolean;
+
+  // Override event props and append status as argument
+  onBlur?: (event: React.FocusEvent<HTMLInputElement>, status?: InputStatus) => void;
+  onFocus?: (event: React.FocusEvent<HTMLInputElement>, status?: InputStatus) => void;
+  onChange?: (event: React.FormEvent<HTMLInputElement>, status?: InputStatus) => void;
+}
+
+export class Input extends PureComponent<Props> {
+  static defaultProps = {
+    className: '',
+  };
+
+  state = {
+    error: null,
+  };
+
+  get status() {
+    return this.state.error ? InputStatus.Invalid : InputStatus.Valid;
+  }
+
+  get isInvalid() {
+    return this.status === InputStatus.Invalid;
+  }
+
+  validatorAsync = (validationRules: ValidationRule[]) => {
+    return evt => {
+      const errors = validate(evt.target.value, validationRules);
+      this.setState(prevState => {
+        return {
+          ...prevState,
+          error: errors ? errors[0] : null,
+        };
+      });
+    };
+  };
+
+  populateEventPropsWithStatus = (restProps, validationEvents: ValidationEvents) => {
+    const inputElementProps = { ...restProps };
+    Object.keys(EventsWithValidation).forEach((eventName: EventsWithValidation) => {
+      if (hasValidationEvent(eventName, validationEvents) || restProps[eventName]) {
+        inputElementProps[eventName] = async evt => {
+          evt.persist(); // Needed for async. https://reactjs.org/docs/events.html#event-pooling
+          if (hasValidationEvent(eventName, validationEvents)) {
+            await this.validatorAsync(validationEvents[eventName]).apply(this, [evt]);
+          }
+          if (restProps[eventName]) {
+            restProps[eventName].apply(null, [evt, this.status]);
+          }
+        };
+      }
+    });
+    return inputElementProps;
+  };
+
+  render() {
+    const { validationEvents, className, hideErrorMessage, ...restProps } = this.props;
+    const { error } = this.state;
+    const inputClassName = classNames('gf-form-input', { invalid: this.isInvalid }, className);
+    const inputElementProps = this.populateEventPropsWithStatus(restProps, validationEvents);
+
+    return (
+      <div className="our-custom-wrapper-class">
+        <input {...inputElementProps} className={inputClassName} />
+        {error && !hideErrorMessage && <span>{error}</span>}
+      </div>
+    );
+  }
+}

+ 26 - 0
packages/grafana-ui/src/types/forms.ts

@@ -0,0 +1,26 @@
+export enum InputStatus {
+  Invalid = 'invalid',
+  Valid = 'valid',
+}
+
+export enum InputTypes {
+  Text = 'text',
+  Number = 'number',
+  Password = 'password',
+  Email = 'email',
+}
+
+export enum EventsWithValidation {
+  onBlur = 'onBlur',
+  onFocus = 'onFocus',
+  onChange = 'onChange',
+}
+
+export interface ValidationRule {
+  rule: (valueToValidate: string) => boolean;
+  errorMessage: string;
+}
+
+export interface ValidationEvents {
+  [eventName: string]: ValidationRule[];
+}

+ 1 - 0
packages/grafana-ui/src/types/index.ts

@@ -5,3 +5,4 @@ export * from './plugin';
 export * from './datasource';
 export * from './theme';
 export * from './threshold';
+export * from './forms';

+ 1 - 0
packages/grafana-ui/src/utils/index.ts

@@ -7,3 +7,4 @@ export * from './thresholds';
 export * from './string';
 export * from './deprecationWarning';
 export { getMappedValue } from './valueMappings';
+export * from './validate';

+ 15 - 0
packages/grafana-ui/src/utils/validate.ts

@@ -0,0 +1,15 @@
+import { EventsWithValidation, ValidationEvents, ValidationRule } from '../types';
+
+export const validate = (value: string, validationRules: ValidationRule[]) => {
+  const errors = validationRules.reduce((acc, currentRule) => {
+    if (!currentRule.rule(value)) {
+      return acc.concat(currentRule.errorMessage);
+    }
+    return acc;
+  }, []);
+  return errors.length > 0 ? errors : null;
+};
+
+export const hasValidationEvent = (event: EventsWithValidation, validationEvents?: ValidationEvents) => {
+  return validationEvents && validationEvents[event];
+};