Skip to content

Commit 6b7448a

Browse files
authored
fix(Form): remove validation on field removal (#200)
1 parent a52828d commit 6b7448a

File tree

7 files changed

+69
-21
lines changed

7 files changed

+69
-21
lines changed

.changeset/metal-baboons-double.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Form is no longer validated on field removal (bugfix)

.changeset/soft-monkeys-marry.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Validation rules in Form now allows to return complex markup in error messages.

src/components/forms/Form/ComplexForm.stories.tsx

+48-8
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import { StoryFn } from '@storybook/react';
22
import { linkTo } from '@storybook/addon-links';
33

44
import {
5-
Submit,
6-
TextInput,
7-
Select,
5+
Block,
6+
Checkbox,
7+
CheckboxGroup,
88
ComboBox,
9+
Field,
910
Form,
10-
Radio,
1111
Item,
12-
Field,
13-
Checkbox,
14-
CheckboxGroup,
1512
PasswordInput,
13+
Radio,
14+
Select,
15+
Submit,
1616
Switch,
17-
Block,
17+
TextInput,
1818
} from '../../../index';
1919
import { NumberInput } from '../NumberInput/NumberInput';
2020
import { baseProps } from '../../../stories/lists/baseProps';
@@ -26,6 +26,44 @@ export default {
2626
parameters: { controls: { exclude: baseProps } },
2727
};
2828

29+
const ComplexErrorTemplate: StoryFn<typeof Form> = (args) => {
30+
const [form] = Form.useForm();
31+
32+
return (
33+
<Form
34+
form={form}
35+
{...args}
36+
onSubmit={(v) => {
37+
console.log('onSubmit:', v);
38+
}}
39+
onValuesChange={(v) => {
40+
console.log('onChange', v);
41+
}}
42+
>
43+
<Field
44+
name="text"
45+
label="Text input"
46+
rules={[
47+
{ required: true, message: 'This field is required' },
48+
() => ({
49+
validator(rule, value) {
50+
return value.length >= 8
51+
? Promise.resolve()
52+
: Promise.reject(
53+
<>
54+
This field should be <b>at least 8 symbols</b> long
55+
</>,
56+
);
57+
},
58+
}),
59+
]}
60+
>
61+
<TextInput />
62+
</Field>
63+
</Form>
64+
);
65+
};
66+
2967
const Template: StoryFn<typeof Form> = (args) => {
3068
const [form] = Form.useForm();
3169

@@ -185,3 +223,5 @@ export const FormInsideDialog: StoryFn = () => {
185223
};
186224

187225
export const Default = Template.bind({});
226+
227+
export const ComplexErrorMessage: StoryFn = ComplexErrorTemplate.bind({});

src/components/forms/Form/Field.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export interface CubeFieldProps<T extends FieldTypes>
125125
/** The form instance */
126126
form?: CubeFormInstance<T>;
127127
/** The message for the field or text for the error */
128-
message?: string;
128+
message?: ReactNode;
129129
/** The description for the field */
130130
description?: ReactNode;
131131
/** Tooltip for the label that explains something. */

src/components/forms/Form/types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { ReactNode } from 'react';
2+
13
export type FieldTypes = {
24
[key: string]: any;
35
};
46

57
export type CubeFieldData<Name extends string | number | symbol, Value> = {
68
readonly name: Name;
7-
errors: string[];
9+
errors: ReactNode[];
810
value?: Value;
911
inputValue?: Value;
1012
touched?: boolean;

src/components/forms/Form/useForm.tsx

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useRef, useState } from 'react';
1+
import { ReactNode, useRef, useState } from 'react';
22

33
import { dotize } from '../../../tasty';
44

@@ -216,19 +216,17 @@ export class CubeFormInstance<
216216
}
217217
})
218218
.catch((err) => {
219-
if (!field.errors || !isEqual(field.errors, [err])) {
220-
field.errors = [err];
219+
field.errors = [err];
221220

222-
this.forceReRender();
223-
}
221+
this.forceReRender();
224222

225223
return Promise.reject([err]);
226224
});
227225
}
228226

229227
validateFields<Names extends (keyof T)[]>(names?: Names): Promise<any> {
230228
const fieldsList = names || Object.keys(this.fields);
231-
const errorList: { name: string; errors: string[] }[] = [];
229+
const errorList: { name: string; errors: ReactNode[] }[] = [];
232230

233231
return Promise.allSettled(
234232
fieldsList.map((name) => {
@@ -271,7 +269,7 @@ export class CubeFormInstance<
271269
return !!field.touched;
272270
}
273271

274-
getFieldError<Name extends keyof T>(name: Name): string[] {
272+
getFieldError<Name extends keyof T>(name: Name): ReactNode[] {
275273
const field = this.getFieldInstance(name);
276274

277275
if (!field) return [];
@@ -297,8 +295,6 @@ export class CubeFormInstance<
297295
if (!skipRender) {
298296
this.forceReRender();
299297
}
300-
301-
this.validateFields().catch(() => {});
302298
}
303299

304300
setFields<Names extends keyof T>(newFields: SetFieldsArrType<T, Names>[]) {

src/components/forms/Form/validation.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,11 @@ export async function applyRules(value, rules, form) {
144144

145145
for (let rule of rules) {
146146
await applyRule(value, rule, form).catch((err) => {
147-
if (typeof err !== 'string') {
147+
if (err instanceof Error) {
148148
err = err?.message || rule.message;
149149
}
150150

151-
throw err;
151+
throw err || rule.message;
152152
});
153153
}
154154
}

0 commit comments

Comments
 (0)