mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Copied from new timepicker and unified component branch
This commit is contained in:
parent
c0eb140297
commit
384e11fd68
@ -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,
|
||||
},
|
@ -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<HTMLInputElement> {
|
||||
validationEvents?: ValidationEvents;
|
||||
hideErrorMessage?: boolean;
|
||||
@ -27,7 +15,7 @@ interface Props extends React.HTMLProps<HTMLInputElement> {
|
||||
// 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;
|
||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>, status?: InputStatus) => void;
|
||||
}
|
||||
|
||||
export class Input extends PureComponent<Props> {
|
||||
@ -48,24 +36,24 @@ export class Input extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
validatorAsync = (validationRules: ValidationRule[]) => {
|
||||
return evt => {
|
||||
return (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
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]) {
|
||||
|
@ -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';
|
||||
|
@ -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[];
|
||||
}
|
@ -5,4 +5,4 @@ export * from './plugin';
|
||||
export * from './datasource';
|
||||
export * from './theme';
|
||||
export * from './threshold';
|
||||
export * from './forms';
|
||||
export * from './input';
|
||||
|
@ -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];
|
||||
};
|
||||
|
@ -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<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>
|
||||
);
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { Input } from './Input';
|
@ -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];
|
||||
};
|
@ -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]: [
|
||||
|
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user