mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
react-panel: Clean up input validation and increase code readability
This commit is contained in:
parent
69ae3d2e6a
commit
a8e184c025
@ -1,9 +1,8 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { ValidationRule } from 'app/types';
|
import { ValidationEvents, ValidationRule } from 'app/types';
|
||||||
|
import { validate } from 'app/core/utils/validate';
|
||||||
|
|
||||||
export enum InputStatus {
|
export enum InputStatus {
|
||||||
Default = 'default',
|
|
||||||
Loading = 'loading',
|
|
||||||
Invalid = 'invalid',
|
Invalid = 'invalid',
|
||||||
Valid = 'valid',
|
Valid = 'valid',
|
||||||
}
|
}
|
||||||
@ -15,80 +14,71 @@ export enum InputTypes {
|
|||||||
Email = 'email',
|
Email = 'email',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
export enum EventsWithValidation {
|
||||||
status?: InputStatus;
|
onBlur = 'onBlur',
|
||||||
validationRules: ValidationRule[];
|
onFocus = 'onFocus',
|
||||||
hideErrorMessage?: boolean;
|
onChange = 'onChange',
|
||||||
onBlurWithStatus?: (evt, status: InputStatus) => void;
|
|
||||||
emptyToNull?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const validator = (value: string, validationRules: ValidationRule[]) => {
|
interface Props extends React.HTMLProps<HTMLInputElement> {
|
||||||
const errors = validationRules.reduce((acc, currRule) => {
|
validationEvents: ValidationEvents;
|
||||||
if (!currRule.rule(value)) {
|
hideErrorMessage?: boolean;
|
||||||
return acc.concat(currRule.errorMessage);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
return errors.length > 0 ? errors : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class Input extends PureComponent<Props & 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Input extends PureComponent<Props> {
|
||||||
state = {
|
state = {
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
get status() {
|
get status() {
|
||||||
const { error } = this.state;
|
return this.state.error ? InputStatus.Invalid : InputStatus.Valid;
|
||||||
if (error) {
|
|
||||||
return InputStatus.Invalid;
|
|
||||||
}
|
|
||||||
return InputStatus.Valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onBlurWithValidation = evt => {
|
get isInvalid() {
|
||||||
const { validationRules, onBlurWithStatus, onBlur } = this.props;
|
return this.status === InputStatus.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
let errors = null;
|
validatorAsync = (validationRules: ValidationRule[]) => {
|
||||||
if (validationRules) {
|
return evt => {
|
||||||
errors = validator(evt.currentTarget.value, validationRules);
|
const errors = validate(evt.currentTarget.value, validationRules);
|
||||||
this.setState(prevState => {
|
this.setState(prevState => {
|
||||||
return {
|
return {
|
||||||
...prevState,
|
...prevState,
|
||||||
error: errors ? errors[0] : null,
|
error: errors ? errors[0] : null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
};
|
||||||
|
|
||||||
if (onBlurWithStatus) {
|
populateEventPropsWithStatus = (restProps, validationEvents: ValidationEvents) => {
|
||||||
onBlurWithStatus(evt, errors ? InputStatus.Invalid : InputStatus.Valid);
|
const inputElementProps = { ...restProps };
|
||||||
}
|
Object.keys(EventsWithValidation).forEach(eventName => {
|
||||||
|
inputElementProps[eventName] = async evt => {
|
||||||
if (onBlur) {
|
if (validationEvents[eventName]) {
|
||||||
onBlur(evt);
|
await this.validatorAsync(validationEvents[eventName]).apply(this, [evt]);
|
||||||
}
|
}
|
||||||
|
if (restProps[eventName]) {
|
||||||
|
restProps[eventName].apply(null, [evt, this.status]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return inputElementProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { validationEvents, className, hideErrorMessage, ...restProps } = this.props;
|
||||||
status,
|
|
||||||
validationRules,
|
|
||||||
onBlurWithStatus,
|
|
||||||
onBlur,
|
|
||||||
className,
|
|
||||||
hideErrorMessage,
|
|
||||||
emptyToNull,
|
|
||||||
...restProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { error } = this.state;
|
const { error } = this.state;
|
||||||
|
const inputClassName = 'gf-form-input' + (this.isInvalid ? ' invalid' : '');
|
||||||
let inputClassName = 'gf-form-input';
|
const inputElementProps = this.populateEventPropsWithStatus(restProps, validationEvents);
|
||||||
inputClassName = this.status === InputStatus.Invalid ? inputClassName + ' invalid' : inputClassName;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="our-custom-wrapper-class">
|
<div className="our-custom-wrapper-class">
|
||||||
<input {...restProps} onBlur={this.onBlurWithValidation} className={inputClassName} />
|
<input {...inputElementProps} className={inputClassName} />
|
||||||
{error && !hideErrorMessage && <span>{error}</span>}
|
{error && !hideErrorMessage && <span>{error}</span>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
11
public/app/core/utils/validate.ts
Normal file
11
public/app/core/utils/validate.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ValidationRule } from 'app/types';
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
@ -10,9 +10,9 @@ import config from 'app/core/config';
|
|||||||
import { QueryInspector } from './QueryInspector';
|
import { QueryInspector } from './QueryInspector';
|
||||||
import { Switch } from 'app/core/components/Switch/Switch';
|
import { Switch } from 'app/core/components/Switch/Switch';
|
||||||
import { Input } from 'app/core/components/Form';
|
import { Input } from 'app/core/components/Form';
|
||||||
import { InputStatus } from 'app/core/components/Form/Input';
|
import { InputStatus, EventsWithValidation } from 'app/core/components/Form/Input';
|
||||||
import { isValidTimeSpan } from 'app/core/utils/rangeutil';
|
import { isValidTimeSpan } from 'app/core/utils/rangeutil';
|
||||||
import { ValidationRule } from 'app/types';
|
import { ValidationEvents } from 'app/types';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
@ -42,17 +42,20 @@ interface LoadingPlaceholderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => <h2>{text}</h2>;
|
const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => <h2>{text}</h2>;
|
||||||
const validationRules: ValidationRule[] = [
|
|
||||||
{
|
const timeRangeValidationEvents: ValidationEvents = {
|
||||||
rule: value => {
|
[EventsWithValidation.onBlur]: [
|
||||||
if (!value) {
|
{
|
||||||
return true;
|
rule: value => {
|
||||||
}
|
if (!value) {
|
||||||
return isValidTimeSpan(value);
|
return true;
|
||||||
|
}
|
||||||
|
return isValidTimeSpan(value);
|
||||||
|
},
|
||||||
|
errorMessage: 'Not a valid timespan',
|
||||||
},
|
},
|
||||||
errorMessage: 'Not a valid timespan',
|
],
|
||||||
},
|
};
|
||||||
];
|
|
||||||
|
|
||||||
export class QueriesTab extends PureComponent<Props, State> {
|
export class QueriesTab extends PureComponent<Props, State> {
|
||||||
element: any;
|
element: any;
|
||||||
@ -322,8 +325,8 @@ export class QueriesTab extends PureComponent<Props, State> {
|
|||||||
type="text"
|
type="text"
|
||||||
className="gf-form-input max-width-8"
|
className="gf-form-input max-width-8"
|
||||||
placeholder="1h"
|
placeholder="1h"
|
||||||
onBlurWithStatus={this.onOverrideTime}
|
onBlur={this.onOverrideTime}
|
||||||
validationRules={validationRules}
|
validationEvents={timeRangeValidationEvents}
|
||||||
hideErrorMessage={true}
|
hideErrorMessage={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -338,8 +341,8 @@ export class QueriesTab extends PureComponent<Props, State> {
|
|||||||
type="text"
|
type="text"
|
||||||
className="gf-form-input max-width-8"
|
className="gf-form-input max-width-8"
|
||||||
placeholder="1h"
|
placeholder="1h"
|
||||||
onBlurWithStatus={this.onTimeShift}
|
onBlur={this.onTimeShift}
|
||||||
validationRules={validationRules}
|
validationEvents={timeRangeValidationEvents}
|
||||||
hideErrorMessage={true}
|
hideErrorMessage={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
export interface ValidationRule {
|
export interface ValidationRule {
|
||||||
rule: (value: string) => boolean;
|
rule: (valueToValidate: string) => boolean;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ValidationEvents {
|
||||||
|
[eventName: string]: ValidationRule[];
|
||||||
|
}
|
||||||
|
@ -30,7 +30,7 @@ import {
|
|||||||
AppNotificationTimeout,
|
AppNotificationTimeout,
|
||||||
} from './appNotifications';
|
} from './appNotifications';
|
||||||
import { DashboardSearchHit } from './search';
|
import { DashboardSearchHit } from './search';
|
||||||
import { ValidationRule } from './form';
|
import { ValidationEvents, ValidationRule } from './form';
|
||||||
export {
|
export {
|
||||||
Team,
|
Team,
|
||||||
TeamsState,
|
TeamsState,
|
||||||
@ -89,6 +89,7 @@ export {
|
|||||||
AppNotificationTimeout,
|
AppNotificationTimeout,
|
||||||
DashboardSearchHit,
|
DashboardSearchHit,
|
||||||
UserState,
|
UserState,
|
||||||
|
ValidationEvents,
|
||||||
ValidationRule,
|
ValidationRule,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user