Skip to content

Commit

Permalink
Merge pull request #380 from panichevoleg/feature/716703-improve-form…
Browse files Browse the repository at this point in the history
…-validation

Improve validation of Form
  • Loading branch information
klaidigorishti authored Jun 22, 2022
2 parents ce0979d + bf90c07 commit 50975c4
Show file tree
Hide file tree
Showing 21 changed files with 54 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Precise UI Changelog

## 2.1.14

- Improve validation of `Form`

## 2.1.13

- Fix WCAG error: Empty table header in case of JSX element
Expand Down
8 changes: 7 additions & 1 deletion mlc_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
"ignorePatterns": [
{
"pattern": "^https://stackoverflow.com/tags/"
},
{
"pattern": "^https://guides.github.com/"
}
],
"aliveStatusCodes": [429, 200]
"aliveStatusCodes": [
429,
200
]
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "precise-ui",
"version": "2.1.13",
"version": "2.1.14",
"description": "Precise UI React component library powered by Styled Components.",
"keywords": [
"react",
Expand Down
4 changes: 4 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,10 @@ export interface InputProps<T> extends StandardProps {
* Sets maximum lenngth of input field.
*/
maxLength?: number;
/**
* List of fields, that need to be revalidated when the field value is changed
*/
validateWith?: Array<string>;
}

export interface LabeledInputProps<T> extends InputProps<T> {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Autocomplete/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ class AutocompleteInt<T> extends React.Component<SupportedAutocompleteProps<T> &
const { onChange, name = '', form } = this.props;

if (!this.state.controlled) {
form ? form.change({ name, value }) : this.setState({ value });
form ? form.change({ name, value, validateWith: this.props.validateWith }) : this.setState({ value });
}

suggestionSelected ? this.hide() : this.show();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export class AutocompleteTagBuilderInt<T> extends React.Component<
form.change({
name,
value: newValue,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/Checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export class CheckboxInt extends React.PureComponent<CheckboxProps, CheckboxStat
form.change({
name,
value: checked,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/ColorPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class ColorPickerInt extends React.PureComponent<ColorPickerProps & FormContextP
form.change({
name,
value: (state as Pick<ColorPickerState, 'value'>).value,
validateWith: this.props.validateWith,
});
} else {
this.setState(state);
Expand Down
1 change: 1 addition & 0 deletions src/components/DateField/DateFieldInt.part.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ class DateFieldInt extends React.Component<DateFieldProps, DateFieldState> {
form.change({
name,
value,
validateWith: this.props.validateWith,
});
this.onOpenChange(true);
});
Expand Down
1 change: 1 addition & 0 deletions src/components/DropdownField/DropdownFieldInt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ export class DropdownFieldInt extends React.Component<DropdownFieldProps & FormC
form.change({
name,
value,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
2 changes: 2 additions & 0 deletions src/components/Dropzone/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class DropzoneInt extends React.Component<DropzoneProps & FormContextProps, Drop
form.change({
name,
value: multiple ? [...this.state.value, ...files] : files,
validateWith: this.props.validateWith,
});
} else {
this.setState(
Expand Down Expand Up @@ -250,6 +251,7 @@ class DropzoneInt extends React.Component<DropzoneProps & FormContextProps, Drop
form.change({
name,
value: this.state.value.filter(file => f !== file),
validateWith: this.props.validateWith,
});
} else {
this.setState(
Expand Down
2 changes: 2 additions & 0 deletions src/components/FileSelect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class FileSelectInt extends React.Component<FileSelectProps & FormContextProps,
form.change({
name,
value: getFiles(multiple ? [...this.state.value] : [], files),
validateWith: this.props.validateWith,
});
} else {
this.setState(
Expand All @@ -130,6 +131,7 @@ class FileSelectInt extends React.Component<FileSelectProps & FormContextProps,
form.change({
name,
value: this.state.value.filter(file => f !== file),
validateWith: this.props.validateWith,
});
} else {
this.setState(
Expand Down
27 changes: 16 additions & 11 deletions src/components/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export interface FormProps<FormValues> extends StandardProps {
/**
* Rules for validating fields values.
*/
validationRules?: { [T in keyof FormValues]?: (value: any) => React.ReactChild | true };
validationRules?: { [T in keyof FormValues]?: (value: any, formValues: FormValues) => React.ReactChild | true };
/**
* Event emitted when a field of the form changed.
*/
Expand Down Expand Up @@ -215,9 +215,9 @@ export class Form<Values extends FormValuesData> extends React.Component<FormPro
}
}

private getError(name: string, value: any) {
private getError(name: string, value: any, current: Values) {
const validator = this.props.validationRules && this.props.validationRules[name];
const validationResult = validator ? validator(value) : true;
const validationResult = validator ? validator(value, current) : true;
const error = validationResult === true ? undefined : validationResult;
return error;
}
Expand All @@ -231,8 +231,8 @@ export class Form<Values extends FormValuesData> extends React.Component<FormPro
}
}

private setError({ name, value }: FormValueChange) {
const error = this.getError(name, value);
private setError({ name, value }: FormValueChange, current: Values) {
const error = this.getError(name, value, current);
this.setFieldError(name, error);

this.setState({ errors: { ...this.state.errors, [name]: error } });
Expand All @@ -244,12 +244,13 @@ export class Form<Values extends FormValuesData> extends React.Component<FormPro

for (const key of keys as Array<Extract<keyof Values, string>>) {
const value = current[key];
const error = this.getError(key, value);
const error = this.getError(key, value, current);
errors[key] = error;
this.setFieldError(key, error);
}

this.setState({ errors });
return errors;
}

private createContext(): FormContextType {
Expand All @@ -267,7 +268,11 @@ export class Form<Values extends FormValuesData> extends React.Component<FormPro
this.setValues(proposed, changed);
}

this.setError(field);
this.setError(field, proposed);

(field.validateWith || []).forEach((fieldName: string) => {
return this.setError({ name: fieldName, value: proposed[fieldName] }, proposed);
});

if (typeof onChange === 'function') {
onChange({
Expand All @@ -286,14 +291,14 @@ export class Form<Values extends FormValuesData> extends React.Component<FormPro
let error: React.ReactChild | undefined;
if (name in current) {
const value = current[name];
error = this.getError(name, value);
error = this.getError(name, value, current);
field.setState({
value,
});
} else {
const value = field.state.value;
current[name as Extract<keyof Values, string>] = value;
error = this.getError(name, value);
error = this.getError(name, value, current);
}

if (error) {
Expand All @@ -310,9 +315,9 @@ export class Form<Values extends FormValuesData> extends React.Component<FormPro

private submit = (e: React.FormEvent<HTMLFormElement>) => {
const { onSubmit, disabled } = this.props;
const { current, changed, errors } = this.state;
const { current, changed } = this.state;

this.setErrors(current);
const errors = this.setErrors(current);

if (!disabled && typeof onSubmit === 'function') {
const arrayErrors = this.getErrorsAsArray(errors);
Expand Down
5 changes: 5 additions & 0 deletions src/components/RadioButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export interface RadioButtonProps extends StandardProps {
* Name of the radio button within a radio button group.
*/
name?: string;
/**
* List of fields, that need to be revalidated when the field value is changed
*/
validateWith?: Array<string>;
}

export interface RadioButtonIntProps extends RadioButtonProps {
Expand Down Expand Up @@ -213,6 +217,7 @@ export class RadioButtonInt extends React.PureComponent<RadioButtonIntProps & Fo
form.change({
name,
value,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/RadioButtonGroup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class RadioButtonGroupInt extends React.PureComponent<RadioButtonGroupProps & Fo
form.change({
name,
value,
validateWith: this.props.validateWith,
});
} else if (value) {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/Rating/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class RatingInt extends React.Component<RatingProps & FormContextProps, RatingSt
form.change({
name,
value,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/Slider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class SliderInt extends React.PureComponent<SliderProps & FormContextProps, Slid
form.change({
name,
value,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/TagBuilder/TagBuilderInt.part.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ export class TagBuilderInt extends React.Component<TagBuilderProps & FormContext
form.change({
name,
value,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/TextField/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ class TextFieldInt extends React.Component<TextFieldProps & FormContextProps, Te
form.change({
name,
value,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/components/Toggle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class ToggleInt extends React.PureComponent<ToggleProps & FormContextProps, Togg
form.change({
name,
value: status,
validateWith: this.props.validateWith,
});
} else {
this.setState({
Expand Down
1 change: 1 addition & 0 deletions src/contexts/FormContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface FormValueNotifier {
export interface FormValueChange {
name: string;
value: any;
validateWith?: Array<string>;
}

export interface FormContextType {
Expand Down

0 comments on commit 50975c4

Please sign in to comment.