diff --git a/src/npm-fastui/src/components/FormField.tsx b/src/npm-fastui/src/components/FormField.tsx index d1b30c78..475ef750 100644 --- a/src/npm-fastui/src/components/FormField.tsx +++ b/src/npm-fastui/src/components/FormField.tsx @@ -24,7 +24,7 @@ interface FormFieldInputProps extends FormFieldInput { } export const FormFieldInputComp: FC = (props) => { - const { name, placeholder, required, htmlType, locked, autocomplete } = props + const { name, placeholder, required, htmlType, locked, autocomplete, step } = props return (
@@ -39,6 +39,7 @@ export const FormFieldInputComp: FC = (props) => { disabled={locked} placeholder={placeholder} autoComplete={autocomplete} + step={step} aria-describedby={descId(props)} /> diff --git a/src/npm-fastui/src/models.d.ts b/src/npm-fastui/src/models.d.ts index a0ca91cd..e8aeefb2 100644 --- a/src/npm-fastui/src/models.d.ts +++ b/src/npm-fastui/src/models.d.ts @@ -353,6 +353,7 @@ export interface FormFieldInput { initial?: string | number placeholder?: string autocomplete?: string + step?: number | 'any' type: 'FormFieldInput' } export interface FormFieldTextarea { diff --git a/src/python-fastui/fastui/components/forms.py b/src/python-fastui/fastui/components/forms.py index a18a9c4c..29032e0e 100644 --- a/src/python-fastui/fastui/components/forms.py +++ b/src/python-fastui/fastui/components/forms.py @@ -32,6 +32,7 @@ class FormFieldInput(BaseFormField): initial: _t.Union[str, float, None] = None placeholder: _t.Union[str, None] = None autocomplete: _t.Union[str, None] = None + step: _t.Union[float, _t.Literal['any'], None] = None type: _t.Literal['FormFieldInput'] = 'FormFieldInput' diff --git a/src/python-fastui/fastui/json_schema.py b/src/python-fastui/fastui/json_schema.py index 8779421d..f51653a9 100644 --- a/src/python-fastui/fastui/json_schema.py +++ b/src/python-fastui/fastui/json_schema.py @@ -199,6 +199,7 @@ def json_schema_field_to_field( initial=schema.get('default'), autocomplete=schema.get('autocomplete'), description=schema.get('description'), + step=schema.get('step', get_default_step(schema)), class_name=schema.get('className'), ) @@ -379,6 +380,14 @@ def input_html_type(schema: JsonSchemaField) -> InputHtmlType: raise ValueError(f'Unknown schema: {schema}') from e +def get_default_step(schema: JsonSchemaField) -> _t.Union[_t.Literal['any'], None]: + key = schema['type'] + if key == 'integer': + return None + if key == 'number': + return 'any' + + def schema_is_field(schema: JsonSchemaConcrete) -> _ta.TypeGuard[JsonSchemaField]: """ Determine if a schema is a field `JsonSchemaField` diff --git a/src/python-fastui/tests/test_forms.py b/src/python-fastui/tests/test_forms.py index b0919fad..2f74c585 100644 --- a/src/python-fastui/tests/test_forms.py +++ b/src/python-fastui/tests/test_forms.py @@ -469,3 +469,37 @@ def test_form_textarea_form_fields(): } ], } + + +class FormNumbersDefaultStep(BaseModel): + size: int + cost: float + + +def test_form_numbers_default_step(): + m = components.ModelForm(model=FormNumbersDefaultStep, submit_url='/foobar') + + assert m.model_dump(by_alias=True, exclude_none=True) == { + 'submitUrl': '/foobar', + 'method': 'POST', + 'type': 'ModelForm', + 'formFields': [ + { + 'name': 'size', + 'title': ['Size'], + 'required': True, + 'locked': False, + 'htmlType': 'number', + 'type': 'FormFieldInput', + }, + { + 'name': 'cost', + 'title': ['Cost'], + 'required': True, + 'locked': False, + 'htmlType': 'number', + 'step': 'any', + 'type': 'FormFieldInput', + }, + ], + }