diff --git a/public/app/core/components/Form/Input.test.tsx b/packages/grafana-ui/src/components/Input/Input.test.tsx similarity index 83% rename from public/app/core/components/Form/Input.test.tsx rename to packages/grafana-ui/src/components/Input/Input.test.tsx index 9e903208e80..1d39b594b1c 100644 --- a/public/app/core/components/Form/Input.test.tsx +++ b/packages/grafana-ui/src/components/Input/Input.test.tsx @@ -1,18 +1,16 @@ -import React from 'react'; +import React from 'react'; import renderer from 'react-test-renderer'; import { shallow } from 'enzyme'; -import { Input, EventsWithValidation } from './Input'; -import { ValidationEvents } from 'app/types'; +import { Input } from './Input'; +import { EventsWithValidation } from '../../utils'; +import { ValidationEvents } from '../../types'; const TEST_ERROR_MESSAGE = 'Value must be empty or less than 3 chars'; const testBlurValidation: ValidationEvents = { [EventsWithValidation.onBlur]: [ { rule: (value: string) => { - if (!value || value.length < 3) { - return true; - } - return false; + return !value || value.length < 3; }, errorMessage: TEST_ERROR_MESSAGE, }, diff --git a/packages/grafana-ui/src/components/Input/Input.tsx b/packages/grafana-ui/src/components/Input/Input.tsx index 57d6a753b17..f5f59e265c0 100644 --- a/packages/grafana-ui/src/components/Input/Input.tsx +++ b/packages/grafana-ui/src/components/Input/Input.tsx @@ -1,25 +1,13 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent, ChangeEvent } from 'react'; import classNames from 'classnames'; -import { ValidationEvents, ValidationRule } from '../../types/forms'; +import { validate, EventsWithValidation, hasValidationEvent } from '../../utils'; +import { ValidationEvents, ValidationRule } from '../../types'; 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 { validationEvents?: ValidationEvents; hideErrorMessage?: boolean; @@ -27,7 +15,7 @@ interface Props extends React.HTMLProps { // Override event props and append status as argument onBlur?: (event: React.FocusEvent, status?: InputStatus) => void; onFocus?: (event: React.FocusEvent, status?: InputStatus) => void; - onChange?: (event: React.FormEvent, status?: InputStatus) => void; + onChange?: (event: React.ChangeEvent, status?: InputStatus) => void; } export class Input extends PureComponent { @@ -48,24 +36,24 @@ export class Input extends PureComponent { } validatorAsync = (validationRules: ValidationRule[]) => { - return evt => { + return (evt: ChangeEvent) => { const errors = validate(evt.target.value, validationRules); this.setState(prevState => { - return { - ...prevState, - error: errors ? errors[0] : null, - }; + return { ...prevState, error: errors ? errors[0] : null }; }); }; }; - populateEventPropsWithStatus = (restProps, validationEvents: ValidationEvents) => { + populateEventPropsWithStatus = (restProps: any, validationEvents: ValidationEvents | undefined) => { const inputElementProps = { ...restProps }; - Object.keys(EventsWithValidation).forEach((eventName: EventsWithValidation) => { - if (hasValidationEvent(eventName, validationEvents) || restProps[eventName]) { - inputElementProps[eventName] = async evt => { + if (!validationEvents) { + return inputElementProps; + } + Object.keys(EventsWithValidation).forEach(eventName => { + if (hasValidationEvent(eventName as EventsWithValidation, validationEvents) || restProps[eventName]) { + inputElementProps[eventName] = async (evt: ChangeEvent) => { evt.persist(); // Needed for async. https://reactjs.org/docs/events.html#event-pooling - if (hasValidationEvent(eventName, validationEvents)) { + if (hasValidationEvent(eventName as EventsWithValidation, validationEvents)) { await this.validatorAsync(validationEvents[eventName]).apply(this, [evt]); } if (restProps[eventName]) { diff --git a/public/app/core/components/Form/__snapshots__/Input.test.tsx.snap b/packages/grafana-ui/src/components/Input/__snapshots__/Input.test.tsx.snap similarity index 100% rename from public/app/core/components/Form/__snapshots__/Input.test.tsx.snap rename to packages/grafana-ui/src/components/Input/__snapshots__/Input.test.tsx.snap diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index b8c8d66cead..e20a52f6485 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -25,6 +25,7 @@ export { ValueMappingsEditor } from './ValueMappingsEditor/ValueMappingsEditor'; export { Switch } from './Switch/Switch'; export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult'; export { UnitPicker } from './UnitPicker/UnitPicker'; +export { Input, InputStatus } from './Input/Input'; // Visualizations export { Gauge } from './Gauge/Gauge'; diff --git a/packages/grafana-ui/src/types/forms.ts b/packages/grafana-ui/src/types/forms.ts deleted file mode 100644 index 602ee434ee5..00000000000 --- a/packages/grafana-ui/src/types/forms.ts +++ /dev/null @@ -1,26 +0,0 @@ -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[]; -} diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index 390f8a4db29..c0aede431d0 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -5,4 +5,4 @@ export * from './plugin'; export * from './datasource'; export * from './theme'; export * from './threshold'; -export * from './forms'; +export * from './input'; diff --git a/public/app/types/form.ts b/packages/grafana-ui/src/types/input.ts similarity index 100% rename from public/app/types/form.ts rename to packages/grafana-ui/src/types/input.ts diff --git a/packages/grafana-ui/src/utils/validate.ts b/packages/grafana-ui/src/utils/validate.ts index 20979ae33ff..286ec700577 100644 --- a/packages/grafana-ui/src/utils/validate.ts +++ b/packages/grafana-ui/src/utils/validate.ts @@ -1,15 +1,24 @@ -import { EventsWithValidation, ValidationEvents, ValidationRule } from '../types'; +import { ValidationRule, ValidationEvents } from '../types/input'; + +export enum EventsWithValidation { + onBlur = 'onBlur', + onFocus = 'onFocus', + onChange = 'onChange', +} export const validate = (value: string, validationRules: ValidationRule[]) => { - const errors = validationRules.reduce((acc, currentRule) => { - if (!currentRule.rule(value)) { - return acc.concat(currentRule.errorMessage); - } - return acc; - }, []); + const errors = validationRules.reduce( + (acc, currRule) => { + if (!currRule.rule(value)) { + return acc.concat(currRule.errorMessage); + } + return acc; + }, + [] as string[] + ); return errors.length > 0 ? errors : null; }; -export const hasValidationEvent = (event: EventsWithValidation, validationEvents?: ValidationEvents) => { +export const hasValidationEvent = (event: EventsWithValidation, validationEvents: ValidationEvents | undefined) => { return validationEvents && validationEvents[event]; }; diff --git a/public/app/core/components/Form/Input.tsx b/public/app/core/components/Form/Input.tsx deleted file mode 100644 index 7940f3b1104..00000000000 --- a/public/app/core/components/Form/Input.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { PureComponent } from 'react'; -import classNames from 'classnames'; -import { ValidationEvents, ValidationRule } from 'app/types'; -import { validate, hasValidationEvent } from 'app/core/utils/validate'; - -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 { - validationEvents?: ValidationEvents; - hideErrorMessage?: boolean; - - // Override event props and append status as argument - onBlur?: (event: React.FocusEvent, status?: InputStatus) => void; - onFocus?: (event: React.FocusEvent, status?: InputStatus) => void; - onChange?: (event: React.FormEvent, status?: InputStatus) => void; -} - -export class Input extends PureComponent { - 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 ( -
- - {error && !hideErrorMessage && {error}} -
- ); - } -} diff --git a/public/app/core/components/Form/index.ts b/public/app/core/components/Form/index.ts deleted file mode 100644 index 6322cf3241a..00000000000 --- a/public/app/core/components/Form/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Input } from './Input'; diff --git a/public/app/core/utils/validate.ts b/public/app/core/utils/validate.ts deleted file mode 100644 index c6663882808..00000000000 --- a/public/app/core/utils/validate.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ValidationRule, ValidationEvents } from 'app/types'; -import { EventsWithValidation } from 'app/core/components/Form/Input'; - -export const validate = (value: string, validationRules: ValidationRule[]) => { - const errors = validationRules.reduce((acc, currRule) => { - if (!currRule.rule(value)) { - return acc.concat(currRule.errorMessage); - } - return acc; - }, []); - return errors.length > 0 ? errors : null; -}; - -export const hasValidationEvent = (event: EventsWithValidation, validationEvents: ValidationEvents) => { - return validationEvents && validationEvents[event]; -}; diff --git a/public/app/features/dashboard/panel_editor/QueryOptions.tsx b/public/app/features/dashboard/panel_editor/QueryOptions.tsx index 0d031cb12ba..377582d7ce5 100644 --- a/public/app/features/dashboard/panel_editor/QueryOptions.tsx +++ b/public/app/features/dashboard/panel_editor/QueryOptions.tsx @@ -5,17 +5,12 @@ import React, { PureComponent, ChangeEvent, FocusEvent } from 'react'; import { isValidTimeSpan } from 'app/core/utils/rangeutil'; // Components -import { Switch } from '@grafana/ui'; -import { Input } from 'app/core/components/Form'; -import { EventsWithValidation } from 'app/core/components/Form/Input'; -import { InputStatus } from 'app/core/components/Form/Input'; +import { DataSourceSelectItem, EventsWithValidation, Input, InputStatus, Switch, ValidationEvents } from '@grafana/ui'; import { DataSourceOption } from './DataSourceOption'; import { FormLabel } from '@grafana/ui'; // Types -import { PanelModel } from '../state/PanelModel'; -import { DataSourceSelectItem } from '@grafana/ui/src/types'; -import { ValidationEvents } from 'app/types'; +import { PanelModel } from '../state'; const timeRangeValidationEvents: ValidationEvents = { [EventsWithValidation.onBlur]: [ diff --git a/public/app/types/index.ts b/public/app/types/index.ts index eefba746c61..3bf76aeb3c3 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -12,6 +12,5 @@ export * from './plugins'; export * from './organization'; export * from './appNotifications'; export * from './search'; -export * from './form'; export * from './explore'; export * from './store';