1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

fixes for <Input/>:

- remove duplicated error messages for sync validators
- don't propagate invalid values on change (uncontrolled components only)
- more informative error message for numeric input with min/max info

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2022-06-03 17:58:21 +03:00
parent 27c67c3e2e
commit 36710e2b08
3 changed files with 35 additions and 21 deletions

View File

@ -8,7 +8,7 @@ import { action } from "mobx";
import { observer } from "mobx-react";
import type { UserStore } from "../../../common/user-store";
import { SubTitle } from "../layout/sub-title";
import { Input, InputValidators } from "../input";
import { Input } from "../input";
import { Switch } from "../switch";
import { Select, type SelectOption } from "../select";
import type { ThemeStore } from "../../themes/store";
@ -104,8 +104,8 @@ const NonInjectedTerminal = observer((
theme="round-black"
type="number"
min={10}
validators={InputValidators.isNumber}
value={userStore.terminalConfig.fontSize.toString()}
max={50}
defaultValue={userStore.terminalConfig.fontSize.toString()}
onChange={(value) => userStore.terminalConfig.fontSize = Number(value)}
/>
</section>

View File

@ -14,7 +14,6 @@ import { Tooltip } from "../tooltip";
import * as Validators from "./input_validators";
import type { InputValidator } from "./input_validators";
import isFunction from "lodash/isFunction";
import isBoolean from "lodash/isBoolean";
import uniqueId from "lodash/uniqueId";
import { debounce } from "lodash";
@ -24,7 +23,10 @@ export { InputValidators };
export type { InputValidator };
type InputElement = HTMLInputElement | HTMLTextAreaElement;
type InputElementProps = InputHTMLAttributes<HTMLInputElement> & TextareaHTMLAttributes<HTMLTextAreaElement> & DOMAttributes<InputElement>;
type InputElementProps =
InputHTMLAttributes<HTMLInputElement>
& TextareaHTMLAttributes<HTMLTextAreaElement>
& DOMAttributes<InputElement>;
export interface IconDataFnArg {
isDirty: boolean;
@ -173,22 +175,18 @@ export class Input extends React.Component<InputProps, State> {
error => this.getValidatorError(value, validator) || error,
),
);
} else {
if (!validator.validate(value, this.props)) {
errors.push(this.getValidatorError(value, validator));
}
}
const result = validator.validate(value, this.props);
const isValid = validator.validate(value, this.props);
if (isBoolean(result) && !result) {
if (isValid === false) {
errors.push(this.getValidatorError(value, validator));
} else if (result instanceof Promise) {
} else if (isValid instanceof Promise) {
if (!validationId) {
this.validationId = validationId = uniqueId("validation_id_");
}
asyncValidators.push(
result.then(
isValid.then(
() => null, // don't consider any valid result from promise since we interested in errors only
error => this.getValidatorError(value, validator) || error,
),
@ -266,9 +264,7 @@ export class Input extends React.Component<InputProps, State> {
setDirtyOnChange = debounce(() => this.setDirty(), 500);
onChange(evt: React.ChangeEvent<any>) {
this.props.onChange?.(evt.currentTarget.value, evt);
this.validate();
async onChange(evt: React.ChangeEvent<any>) {
this.autoFitHeight();
this.setDirtyOnChange();
@ -277,6 +273,17 @@ export class Input extends React.Component<InputProps, State> {
if (this.isUncontrolled && this.showMaxLenIndicator) {
this.forceUpdate();
}
const newValue = evt.currentTarget.value;
const eventCopy = { ...evt };
await this.validate(); // validate first
// don't propagate changes for invalid values
// possible only with uncontrolled components (defaultValue={} must be used instead value={})
if (!this.isUncontrolled || (this.isUncontrolled && this.state.valid)) {
this.props.onChange?.(newValue, eventCopy);
}
}
onKeyDown(evt: React.KeyboardEvent<InputElement>) {
@ -299,7 +306,7 @@ export class Input extends React.Component<InputProps, State> {
this.setDirty();
}
if(this.props.blurOnEnter){
if (this.props.blurOnEnter) {
//pressing enter indicates that the edit is complete, we can unfocus now
this.blur();
}
@ -379,7 +386,6 @@ export class Input extends React.Component<InputProps, State> {
multiLine, showValidationLine, validators, theme, maxRows, children, showErrorsAsTooltip,
maxLength, rows, disabled, autoSelectOnFocus, iconLeft, iconRight, contentRight, id,
dirty: _dirty, // excluded from passing to input-element
defaultValue,
trim,
blurOnEnter,
...inputProps

View File

@ -8,7 +8,8 @@ import type { ReactNode } from "react";
import fse from "fs-extra";
import { TypedRegEx } from "typed-regex";
export class AsyncInputValidationError extends Error {}
export class AsyncInputValidationError extends Error {
}
export type InputValidator<IsAsync extends boolean> = {
/**
@ -32,7 +33,7 @@ export type InputValidator<IsAsync extends boolean> = {
message?: undefined;
debounce: number;
}
);
);
export function inputValidator<IsAsync extends boolean = false>(validator: InputValidator<IsAsync>): InputValidator<IsAsync> {
return validator;
@ -52,7 +53,14 @@ export const isEmail = inputValidator({
export const isNumber = inputValidator({
condition: ({ type }) => type === "number",
message: () => `Invalid number`,
message(value, { min, max }) {
const minMax: string = [
typeof min === "number" ? `min: ${min}` : undefined,
typeof max === "number" ? `max: ${max}` : undefined,
].filter(Boolean).join(", ");
return `Invalid number${minMax ? ` (${minMax})` : ""}`;
},
validate: (value, { min, max }) => {
const numVal = +value;