Skip to content

Commit c7e0ad7

Browse files
committed
tests passing!
1 parent a62235e commit c7e0ad7

15 files changed

+232
-139
lines changed

src/npm-fastui/src/components/FormField.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@ import Select, { StylesConfig } from 'react-select'
55
import { ClassName, useClassName } from '../hooks/className'
66
import { debounce, useRequest } from '../tools'
77

8+
type PrivateOnChange = () => void
9+
810
interface BaseFormFieldProps {
911
name: string
10-
title: string[]
12+
title: string | string[]
1113
required: boolean
1214
locked: boolean
1315
error?: string
1416
description?: string
1517
displayMode?: 'default' | 'inline'
1618
className?: ClassName
17-
onChange?: () => void
19+
onChange?: PrivateOnChange
1820
}
1921

2022
export type FormFieldProps =
@@ -239,6 +241,7 @@ function findDefault(options: SelectOptions, value?: string): SelectOption | und
239241
interface FormFieldSelectSearchProps extends BaseFormFieldProps {
240242
type: 'FormFieldSelectSearch'
241243
searchUrl: string
244+
/** @TJS-type integer */
242245
debounce?: number
243246
initial?: SelectOption
244247
multiple?: boolean
@@ -302,7 +305,10 @@ export const FormFieldSelectSearchComp: FC<FormFieldSelectSearchProps> = (props)
302305
}
303306

304307
const Label: FC<FormFieldProps> = (props) => {
305-
const { title } = props
308+
let { title } = props
309+
if (!Array.isArray(title)) {
310+
title = [title]
311+
}
306312
return (
307313
<label htmlFor={inputId(props)} className={useClassName(props, { el: 'label' })}>
308314
{title.map((t, i) => (

src/npm-fastui/src/components/Iframe.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@ import { ClassName } from '../hooks/className'
44

55
export interface IframeProps {
66
type: 'Iframe'
7+
/**
8+
* @format uri
9+
* @maxLength 2083
10+
* @minLength 1
11+
*/
712
src: string
13+
/** @TJS-type ["string", "integer"] */
814
width?: string | number
15+
/** @TJS-type ["string", "integer"] */
916
height?: string | number
1017
title?: string
1118
className?: ClassName

src/npm-fastui/src/components/display.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum DisplayMode {
2121

2222
export interface DisplayProps {
2323
type: 'Display'
24+
/** @TJS-type JSON */
2425
value?: JsonData
2526
mode?: DisplayMode
2627
title?: string

src/npm-fastui/src/components/form.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { FormFieldProps } from './FormField'
1212

1313
interface BaseFormProps {
1414
formFields: FormFieldProps[]
15+
/** @TJS-type object */
1516
initial?: Record<string, any>
1617
submitUrl: string
1718
footer?: boolean | FastProps[]

src/npm-fastui/src/components/heading.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { slugify } from '../tools'
55

66
export interface HeadingProps {
77
type: 'Heading'
8+
/**
9+
* @TJS-enum [1, 2, 3, 4, 5, 6]
10+
* @TJS-type integer
11+
*/
812
level: 1 | 2 | 3 | 4 | 5 | 6
913
htmlId?: string
1014
className?: ClassName

src/npm-fastui/src/components/image.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ export interface ImageProps {
77
type: 'Image'
88
src: string
99
alt?: string
10+
/** @TJS-type ["string", "integer"] */
1011
width?: number | string
12+
/** @TJS-type ["string", "integer"] */
1113
height?: number | string
1214
referrerPolicy?:
1315
| 'no-referrer'

src/npm-fastui/src/components/pagination.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ import { LinkListComp } from './LinkList'
77

88
export interface PaginationProps {
99
type: 'Pagination'
10+
/** @TJS-type integer */
1011
page: number
12+
/** @TJS-type integer */
1113
pageSize: number
14+
/** @TJS-type integer */
1215
total: number
16+
/** @TJS-type integer */
1317
pageCount: number
1418
className?: ClassName
1519
}

src/python-fastui/fastui/components/__init__.py

+32-37
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class Link(pydantic.BaseModel, extra='forbid'):
128128
components: '_t.List[AnyComponent]'
129129
on_click: _t.Union[events.AnyEvent, None] = pydantic.Field(default=None, serialization_alias='onClick')
130130
mode: _t.Union[_t.Literal['navbar', 'tabs', 'vertical', 'pagination'], None] = None
131-
active: _t.Union[bool, str, None] = None
131+
active: _t.Union[str, bool, None] = None
132132
locked: _t.Union[bool, None] = None
133133
class_name: _class_name.ClassName = None
134134
type: _t.Literal['Link'] = 'Link'
@@ -154,7 +154,7 @@ class Modal(pydantic.BaseModel, extra='forbid'):
154154
body: '_t.List[AnyComponent]'
155155
footer: '_t.Union[_t.List[AnyComponent], None]' = None
156156
open_trigger: _t.Union[events.PageEvent, None] = pydantic.Field(default=None, serialization_alias='openTrigger')
157-
open_context: _t.Union[events.EventContext, None] = pydantic.Field(default=None, serialization_alias='openContext')
157+
open_context: _t.Union[events.ContextType, None] = pydantic.Field(default=None, serialization_alias='openContext')
158158
class_name: _class_name.ClassName = None
159159
type: _t.Literal['Modal'] = 'Modal'
160160

@@ -174,9 +174,9 @@ class ServerLoad(pydantic.BaseModel, extra='forbid'):
174174
class Image(pydantic.BaseModel, extra='forbid'):
175175
src: str
176176
alt: _t.Union[str, None] = None
177-
width: _t.Union[int, float, str, None] = None
178-
height: _t.Union[int, float, str, None] = None
179-
referrerpolicy: _t.Union[
177+
width: _t.Union[str, int, None] = None
178+
height: _t.Union[str, int, None] = None
179+
referrer_policy: _t.Union[
180180
_t.Literal[
181181
'no-referrer',
182182
'no-referrer-when-downgrade',
@@ -188,7 +188,7 @@ class Image(pydantic.BaseModel, extra='forbid'):
188188
'unsafe-url',
189189
],
190190
None,
191-
] = None
191+
] = pydantic.Field(None, serialization_alias='referrerPolicy')
192192
loading: _t.Union[_t.Literal['eager', 'lazy'], None] = None
193193
on_click: _t.Union[events.AnyEvent, None] = pydantic.Field(default=None, serialization_alias='onClick')
194194
class_name: _class_name.ClassName = None
@@ -200,37 +200,32 @@ class Iframe(pydantic.BaseModel, extra='forbid'):
200200
title: _t.Union[str, None] = None
201201
width: _t.Union[str, int, None] = None
202202
height: _t.Union[str, int, None] = None
203+
class_name: _class_name.ClassName = None
203204
type: _t.Literal['Iframe'] = 'Iframe'
204205

205206

206-
AnyComponent = _te.TypeAliasType(
207-
'AnyComponent',
208-
_te.Annotated[
209-
_t.Union[
210-
Text,
211-
Paragraph,
212-
PageTitle,
213-
Div,
214-
Page,
215-
Heading,
216-
Markdown,
217-
Code,
218-
Button,
219-
Link,
220-
LinkList,
221-
Navbar,
222-
Modal,
223-
ServerLoad,
224-
Table,
225-
Pagination,
226-
Display,
227-
Details,
228-
Form,
229-
ModelForm,
230-
Image,
231-
Iframe,
232-
FormField,
233-
],
234-
pydantic.Field(discriminator='type'),
235-
],
236-
)
207+
AnyComponent = _t.Union[
208+
Text,
209+
Paragraph,
210+
PageTitle,
211+
Div,
212+
Page,
213+
Heading,
214+
Markdown,
215+
Code,
216+
Button,
217+
Link,
218+
LinkList,
219+
Navbar,
220+
Modal,
221+
ServerLoad,
222+
Table,
223+
Pagination,
224+
Display,
225+
Details,
226+
Form,
227+
ModelForm,
228+
Image,
229+
Iframe,
230+
FormField,
231+
]

src/python-fastui/fastui/components/display.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313

1414

1515
class DisplayMode(str, enum.Enum):
16-
"""
17-
How to a value.
18-
"""
19-
2016
auto = 'auto' # default, same as None below
2117
plain = 'plain'
2218
datetime = 'datetime'
@@ -30,6 +26,7 @@ class DisplayMode(str, enum.Enum):
3026

3127
class DisplayBase(pydantic.BaseModel, ABC, defer_build=True):
3228
mode: _t.Union[DisplayMode, None] = None
29+
title: _t.Union[str, None] = None
3330
on_click: _t.Union[events.AnyEvent, None] = pydantic.Field(default=None, serialization_alias='onClick')
3431

3532

@@ -39,7 +36,6 @@ class DisplayLookup(DisplayBase, extra='forbid'):
3936
"""
4037

4138
field: str
42-
title: _t.Union[str, None] = None
4339
# percentage width - 0 to 100, specific to tables
4440
table_width_percent: _t.Union[_te.Annotated[int, _at.Interval(ge=0, le=100)], None] = pydantic.Field(
4541
default=None, serialization_alias='tableWidthPercent'
@@ -51,7 +47,7 @@ class Display(DisplayBase, extra='forbid'):
5147
Description of how to display a value, either in a table or detail view.
5248
"""
5349

54-
value: _t.Any
50+
value: _t.Any = pydantic.Field(json_schema_extra={'type': 'JSON'})
5551
type: _t.Literal['Display'] = 'Display'
5652

5753

src/python-fastui/fastui/components/forms.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,27 @@
1010
if _t.TYPE_CHECKING:
1111
from . import AnyComponent
1212

13+
# # alphabetical order matches typescript-json-schema
14+
# InputHtmlType = _t.Literal['date', 'datetime-local', 'email', 'number', 'password', 'text', 'time', 'url']
1315
InputHtmlType = _t.Literal['text', 'date', 'datetime-local', 'time', 'email', 'url', 'number', 'password']
1416

1517

1618
class BaseFormField(pydantic.BaseModel, ABC, defer_build=True):
1719
name: str
18-
title: _t.Union[str, _t.List[str]]
20+
title: _t.Union[_t.List[str], str]
1921
required: bool = False
2022
error: _t.Union[str, None] = None
2123
locked: bool = False
2224
description: _t.Union[str, None] = None
25+
display_mode: _t.Union[_t.Literal['default', 'inline'], None] = pydantic.Field(
26+
default=None, serialization_alias='displayMode'
27+
)
2328
class_name: _class_name.ClassName = None
2429

2530

2631
class FormFieldInput(BaseFormField):
2732
html_type: InputHtmlType = pydantic.Field(default='text', serialization_alias='htmlType')
28-
initial: _t.Union[str, int, float, None] = None
33+
initial: _t.Union[str, float, None] = None
2934
placeholder: _t.Union[str, None] = None
3035
type: _t.Literal['FormFieldInput'] = 'FormFieldInput'
3136

@@ -43,7 +48,7 @@ class FormFieldFile(BaseFormField):
4348

4449

4550
class FormFieldSelect(BaseFormField):
46-
options: _t.Union[_t.List[forms.SelectOption], _t.List[forms.SelectGroup]]
51+
options: forms.SelectOptions
4752
multiple: _t.Union[bool, None] = None
4853
initial: _t.Union[str, None] = None
4954
vanilla: _t.Union[bool, None] = None
@@ -72,7 +77,7 @@ class BaseForm(pydantic.BaseModel, ABC, defer_build=True, extra='forbid'):
7277
default=None, serialization_alias='displayMode'
7378
)
7479
submit_on_change: _t.Union[bool, None] = pydantic.Field(default=None, serialization_alias='submitOnChange')
75-
footer: '_t.Union[bool, _t.List[AnyComponent], None]' = None
80+
footer: '_t.Union[_t.List[AnyComponent], bool, None]' = None
7681
class_name: _class_name.ClassName = None
7782

7883
@pydantic.model_validator(mode='after')

src/python-fastui/fastui/components/tables.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def fill_columns(self) -> _te.Self:
4141

4242
class Pagination(pydantic.BaseModel):
4343
page: int
44-
page_size: int
44+
page_size: int = pydantic.Field(serialization_alias='pageSize')
4545
total: int
4646
class_name: _class_name.ClassName = None
4747
type: _t.Literal['Pagination'] = 'Pagination'

src/python-fastui/fastui/events.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
from typing import Dict, Literal, Union
22

33
from pydantic import BaseModel, Field
4-
from typing_extensions import Annotated, TypeAlias
4+
from typing_extensions import Annotated, TypeAliasType
55

6-
EventContext: TypeAlias = Dict[str, Union[str, int]]
6+
ContextType = TypeAliasType('ContextType', Dict[str, Union[str, int]])
77

88

99
class PageEvent(BaseModel):
1010
name: str
1111
push_path: Union[str, None] = Field(default=None, serialization_alias='pushPath')
12-
context: Union[EventContext, None] = None
12+
context: Union[ContextType, None] = None
1313
clear: Union[bool, None] = None
1414
type: Literal['page'] = 'page'
1515

src/python-fastui/fastui/forms.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,11 @@ class SelectGroup(_te.TypedDict):
170170
options: _t.List[SelectOption]
171171

172172

173+
SelectOptions = _te.TypeAliasType('SelectOptions', _t.Union[_t.List[SelectOption], _t.List[SelectGroup]])
174+
175+
173176
class SelectSearchResponse(pydantic.BaseModel):
174-
options: _t.Union[_t.List[SelectOption], _t.List[SelectGroup]]
177+
options: SelectOptions
175178

176179

177180
NestedDict: _te.TypeAlias = 'dict[str | int, NestedDict | str | list[str] | ds.UploadFile | list[ds.UploadFile]]'

0 commit comments

Comments
 (0)