Skip to content

Commit 9eb22c6

Browse files
authored
feat: Form.List support rules (#195)
* init list rule define * support error of List * add test case
1 parent e72849a commit 9eb22c6

File tree

5 files changed

+82
-13
lines changed

5 files changed

+82
-13
lines changed

examples/list.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,20 @@ const Demo = () => {
2525
>
2626
<Form.Field shouldUpdate>{() => JSON.stringify(form.getFieldsValue(), null, 2)}</Form.Field>
2727

28-
<List name="users">
29-
{(fields, { add, remove }) => {
28+
<List
29+
name="users"
30+
rules={[
31+
{
32+
message: 'Must have at least 2 user!',
33+
validator: async (_, value) => {
34+
if (value.length < 2) {
35+
throw new Error();
36+
}
37+
},
38+
},
39+
]}
40+
>
41+
{(fields, { add, remove }, { errors }) => {
3042
console.log('Demo Fields:', fields);
3143
return (
3244
<div>
@@ -49,6 +61,12 @@ const Demo = () => {
4961
</LabelField>
5062
))}
5163

64+
<ul>
65+
{errors.map(err => (
66+
<li key={err}>{err}</li>
67+
))}
68+
</ul>
69+
5270
<button
5371
type="button"
5472
onClick={() => {

src/List.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from 'react';
22
import warning from 'rc-util/lib/warning';
3-
import { InternalNamePath, NamePath, StoreValue } from './interface';
3+
import { InternalNamePath, NamePath, StoreValue, ValidatorRule, Meta } from './interface';
44
import FieldContext from './FieldContext';
55
import Field from './Field';
66
import { move, getNamePath } from './utils/valueUtil';
@@ -19,10 +19,16 @@ interface ListOperations {
1919

2020
interface ListProps {
2121
name: NamePath;
22-
children?: (fields: ListField[], operations: ListOperations) => JSX.Element | React.ReactNode;
22+
rules?: ValidatorRule[];
23+
validateTrigger?: string | string[] | false;
24+
children?: (
25+
fields: ListField[],
26+
operations: ListOperations,
27+
meta: Meta,
28+
) => JSX.Element | React.ReactNode;
2329
}
2430

25-
const List: React.FunctionComponent<ListProps> = ({ name, children }) => {
31+
const List: React.FunctionComponent<ListProps> = ({ name, children, rules, validateTrigger }) => {
2632
const context = React.useContext(FieldContext);
2733
const keyRef = React.useRef({
2834
keys: [],
@@ -48,8 +54,8 @@ const List: React.FunctionComponent<ListProps> = ({ name, children }) => {
4854

4955
return (
5056
<FieldContext.Provider value={{ ...context, prefixName }}>
51-
<Field name={[]} shouldUpdate={shouldUpdate}>
52-
{({ value = [], onChange }) => {
57+
<Field name={[]} shouldUpdate={shouldUpdate} rules={rules} validateTrigger={validateTrigger}>
58+
{({ value = [], onChange }, meta) => {
5359
const { getFieldValue } = context;
5460
const getNewValue = () => {
5561
const values = getFieldValue(prefixName || []) as StoreValue[];
@@ -142,6 +148,7 @@ const List: React.FunctionComponent<ListProps> = ({ name, children }) => {
142148
},
143149
),
144150
operations,
151+
meta,
145152
);
146153
}}
147154
</Field>

src/interface.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ type Validator = (
5050

5151
export type RuleRender = (form: FormInstance) => RuleObject;
5252

53+
export interface ValidatorRule {
54+
message?: string | ReactElement;
55+
validator: Validator;
56+
}
57+
5358
interface BaseRule {
5459
enum?: StoreValue[];
5560
len?: number;
@@ -60,19 +65,20 @@ interface BaseRule {
6065
required?: boolean;
6166
transform?: (value: StoreValue) => StoreValue;
6267
type?: RuleType;
63-
validator?: Validator;
6468
whitespace?: boolean;
6569

6670
/** Customize rule level `validateTrigger`. Must be subset of Field `validateTrigger` */
6771
validateTrigger?: string | string[];
6872
}
6973

70-
interface ArrayRule extends Omit<BaseRule, 'type'> {
74+
type AggregationRule = BaseRule & Partial<ValidatorRule>;
75+
76+
interface ArrayRule extends Omit<AggregationRule, 'type'> {
7177
type: 'array';
7278
defaultField?: RuleObject;
7379
}
7480

75-
export type RuleObject = BaseRule | ArrayRule;
81+
export type RuleObject = AggregationRule | ArrayRule;
7682

7783
export type Rule = RuleObject | RuleRender;
7884

src/utils/validateUtil.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export function validateRules(
181181
callback();
182182
})
183183
.catch(err => {
184-
callback(err);
184+
callback(err || ' ');
185185
});
186186
}
187187
},

tests/list.test.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { resetWarned } from 'rc-util/lib/warning';
55
import Form, { Field, List } from '../src';
66
import { Input } from './common/InfoField';
77
import { changeValue, getField } from './common';
8+
import timeout from './common/timeout';
89

910
describe('Form.List', () => {
1011
let form;
1112

12-
function generateForm(renderList, formProps) {
13+
function generateForm(renderList, formProps, listProps) {
1314
const wrapper = mount(
1415
<div>
1516
<Form
@@ -18,7 +19,9 @@ describe('Form.List', () => {
1819
}}
1920
{...formProps}
2021
>
21-
<List name="list">{renderList}</List>
22+
<List name="list" {...listProps}>
23+
{renderList}
24+
</List>
2225
</Form>
2326
</div>,
2427
);
@@ -567,4 +570,39 @@ describe('Form.List', () => {
567570
wrapper.update();
568571
expect(wrapper.find(Input)).toHaveLength(1);
569572
});
573+
574+
it('list support validator', async () => {
575+
let operation;
576+
let currentMeta;
577+
let currentValue;
578+
579+
const [wrapper] = generateForm(
580+
(_, opt, meta) => {
581+
operation = opt;
582+
currentMeta = meta;
583+
return null;
584+
},
585+
null,
586+
{
587+
rules: [
588+
{
589+
validator(_, value) {
590+
currentValue = value;
591+
return Promise.reject();
592+
},
593+
message: 'Bamboo Light',
594+
},
595+
],
596+
},
597+
);
598+
599+
await act(async () => {
600+
operation.add();
601+
await timeout();
602+
wrapper.update();
603+
});
604+
605+
expect(currentValue).toEqual([undefined]);
606+
expect(currentMeta.errors).toEqual(['Bamboo Light']);
607+
});
570608
});

0 commit comments

Comments
 (0)