Skip to content

Commit c835062

Browse files
committed
add list field component, matcher, export, and main.py example entry
fix a couple mismatched var names add an element to the dev section of the readme
1 parent 2cdec65 commit c835062

File tree

6 files changed

+82
-7
lines changed

6 files changed

+82
-7
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ $ npm run dev
3131

3232
Visit [http://127.0.0.1:3000][5] to view the generated form
3333

34+
to also regenerate the pydantic forms ui package, also perform those steps in `pydantic-forms-ui/frontend/packages/pydantic-forms/`
35+
3436
## Contributing
3537

3638
When setting up this repo to contribute initialize the pre-commit hooks using [pre-commit][6] (eq. `brew install pre-commit`).

backend/main.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ class MultiCheckBoxChoices(Choice):
102102
_4 = ("4", "Option 4")
103103

104104

105+
class ListChoices(Choice):
106+
_0 = ("0", "Option 0")
107+
_1 = ("1", "Option 1")
108+
_2 = ("2", "Option 2")
109+
_3 = ("3", "Option 3")
110+
_4 = ("4", "Option 4")
111+
_5 = ("5", "Option 5")
112+
_6 = ("6", "Option 6")
113+
114+
105115
@app.post("/form")
106116
async def form(form_data: list[dict] = []):
107117
def form_generator(state: State):
@@ -119,7 +129,8 @@ class TestForm(FormPage):
119129
# When there are <= 3 choices a radio group will be rendered
120130
radio: RadioChoices = "3"
121131
checkbox: bool = True
122-
multicheckbox: choice_list(MultiCheckBoxChoices, min_items=3)
132+
multicheckbox: choice_list(MultiCheckBoxChoices)
133+
list: choice_list(ListChoices) = "0"
123134

124135
form_data_1 = yield TestForm
125136

frontend/packages/pydantic-forms/src/components/defaultComponentMatchers.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
HiddenField,
1111
IntegerField,
1212
LabelField,
13+
ListField,
1314
MultiCheckboxField,
1415
RadioField,
1516
TextAreaField,
@@ -133,15 +134,25 @@ const defaultComponentMatchers: PydanticComponentMatcher[] = [
133134
isControlledElement: true,
134135
},
135136
matcher(field) {
136-
// Use this component when we have multiple boolean options (5 or more)
137137
return (
138138
field.type === PydanticFormFieldType.ARRAY &&
139139
field.options.length <= 5
140140
);
141141
},
142142
validator: zodValidationPresets.array,
143143
},
144+
{
145+
id: 'list',
146+
ElementMatch: {
147+
Element: ListField,
148+
isControlledElement: true,
149+
},
150+
matcher(field) {
151+
return field.type === PydanticFormFieldType.ARRAY;
152+
},
153+
validator: zodValidationPresets.array,
154+
},
144155
];
145156

146-
// If nothing matches, it defaults to Text field in the mapToComponent function
157+
// If nothing matches, it defaults to Text field in the mapToComponent function
147158
export default defaultComponentMatchers;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Pydantic Forms
3+
*
4+
* List component for multiple choice items
5+
*/
6+
import React, { useState } from 'react';
7+
8+
import {
9+
PydanticFormControlledElementProps,
10+
PydanticFormFieldOption,
11+
} from '@/types';
12+
13+
export const ListField = ({
14+
value,
15+
onChange,
16+
onBlur,
17+
disabled,
18+
pydanticFormField,
19+
}: PydanticFormControlledElementProps & {
20+
options?: Array<{ value: string; label: string }>;
21+
}) => {
22+
const [listItems, setListItems] = useState(value || []);
23+
24+
return (
25+
<div>
26+
<select
27+
onBlur={onBlur}
28+
disabled={disabled}
29+
value={listItems}
30+
onChange={(e) => {
31+
const selectedValues = Array.from(
32+
e.currentTarget.selectedOptions,
33+
(option) => option.value
34+
);
35+
setListItems(selectedValues);
36+
onChange(selectedValues);
37+
}}
38+
multiple
39+
>
40+
{pydanticFormField.options.map(
41+
(option: PydanticFormFieldOption) => (
42+
<option key={option.value} value={option.label}>
43+
{option.label}
44+
</option>
45+
)
46+
)}
47+
</select>
48+
</div>
49+
);
50+
};

frontend/packages/pydantic-forms/src/components/fields/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export * from './HiddenField';
1010
export * from './DropdownField';
1111
export * from './RadioField';
1212
export * from './MultiCheckboxField';
13+
export * from './ListField';

frontend/packages/pydantic-forms/src/components/zodValidations.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,22 @@ export const zodValidationPresets: PydanticFormZodValidationPresets = {
113113
array: (field) => {
114114
const { minimum, maximum } = field?.validation ?? {};
115115

116-
let arraySchema = z.array(z.boolean());
116+
let validationRule = z.array(z.boolean());
117117

118118
if (minimum) {
119-
arraySchema = arraySchema.min(
119+
validationRule = validationRule.min(
120120
minimum,
121121
`Dit veld heeft een minimum waarde van ${minimum}`,
122122
);
123123
}
124124

125125
if (maximum) {
126-
arraySchema = arraySchema.max(
126+
validationRule = validationRule.max(
127127
maximum,
128128
`Dit veld heeft een maximum waarde van ${maximum}`,
129129
);
130130
}
131131

132-
return arraySchema;
132+
return validationRule;
133133
},
134134
};

0 commit comments

Comments
 (0)