From 9f83c864e052429c3a349a008c0661103b7ccf44 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Fri, 9 Feb 2024 12:14:18 +0000 Subject: [PATCH 01/13] init add formfieldradio --- demo/forms.py | 1 + src/npm-fastui/src/components/FormField.tsx | 58 +++++++++++++++++++ src/npm-fastui/src/components/index.tsx | 3 + src/npm-fastui/src/models.d.ts | 16 +++++ .../fastui/components/__init__.py | 2 + src/python-fastui/fastui/components/forms.py | 14 ++++- src/python-fastui/fastui/json_schema.py | 31 ++++++---- 7 files changed, 114 insertions(+), 11 deletions(-) diff --git a/demo/forms.py b/demo/forms.py index f0f7c7ef..ef81be3f 100644 --- a/demo/forms.py +++ b/demo/forms.py @@ -123,6 +123,7 @@ class ToolEnum(str, enum.Enum): class SelectForm(BaseModel): select_single: ToolEnum = Field(title='Select Single') + select_radio: ToolEnum = Field(title='Select Radio', json_schema_extra={'mode': 'radio'}, description='test ') select_multiple: list[ToolEnum] = Field(title='Select Multiple') search_select_single: str = Field(json_schema_extra={'search_url': '/api/forms/search'}) search_select_multiple: list[str] = Field(json_schema_extra={'search_url': '/api/forms/search'}) diff --git a/src/npm-fastui/src/components/FormField.tsx b/src/npm-fastui/src/components/FormField.tsx index 0e67959b..d9306065 100644 --- a/src/npm-fastui/src/components/FormField.tsx +++ b/src/npm-fastui/src/components/FormField.tsx @@ -12,6 +12,7 @@ import type { SelectOption, SelectOptions, SelectGroup, + FormFieldRadio, } from '../models' import { useClassName } from '../hooks/className' @@ -304,6 +305,62 @@ export const FormFieldSelectSearchComp: FC<FormFieldSelectSearchProps> = (props) ) } +interface FormFieldRadioProps extends FormFieldRadio { + onChange?: PrivateOnChange +} + +export const FormFieldRadioComp: FC<FormFieldRadioProps> = (props) => { + const { name, required, locked, options, initial } = props + const className = useClassName(props) + const inputClassName = useClassName(props, { el: 'input' }) + + return ( + <div className={className}> + <Label {...props} /> + {options.map((option, i) => { + if ('options' in option) { + // option is a SelectGroup + return option.options.map((subOption, j) => ( + <div key={`${i}-${j}`}> + <input + type="radio" + id={`${inputId(props)}-${i}-${j}`} + className={inputClassName} + name={name} + value={subOption.value} + defaultChecked={subOption.value === initial} + required={required} + disabled={locked} + aria-describedby={descId(props)} + /> + <label htmlFor={`${inputId(props)}-${i}-${j}`}>{subOption.label}</label> + </div> + )) + } else { + // option is a SelectOption + return ( + <div key={i}> + <input + type="radio" + id={`${inputId(props)}-${i}`} + className={inputClassName} + name={name} + value={option.value} + defaultChecked={option.value === initial} + required={required} + disabled={locked} + aria-describedby={descId(props)} + /> + <label htmlFor={`${inputId(props)}-${i}`}>{option.label}</label> + </div> + ) + } + })} + <ErrorDescription {...props} /> + </div> + ) +} + const Label: FC<FormFieldProps> = (props) => { let { title } = props if (!Array.isArray(title)) { @@ -327,6 +384,7 @@ export type FormFieldProps = | FormFieldFileProps | FormFieldSelectProps | FormFieldSelectSearchProps + | FormFieldRadio const inputId = (props: FormFieldProps) => `form-field-${props.name}` const descId = (props: FormFieldProps) => (props.description ? `${inputId(props)}-desc` : undefined) diff --git a/src/npm-fastui/src/components/index.tsx b/src/npm-fastui/src/components/index.tsx index 1cd84748..87362056 100644 --- a/src/npm-fastui/src/components/index.tsx +++ b/src/npm-fastui/src/components/index.tsx @@ -21,6 +21,7 @@ import { FormFieldSelectComp, FormFieldSelectSearchComp, FormFieldFileComp, + FormFieldRadioComp, } from './FormField' import { ButtonComp } from './button' import { LinkComp, LinkRender } from './link' @@ -136,6 +137,8 @@ export const AnyComp: FC<FastProps> = (props) => { return <FormFieldSelectComp {...props} /> case 'FormFieldSelectSearch': return <FormFieldSelectSearchComp {...props} /> + case 'FormFieldRadio': + return <FormFieldRadioComp {...props} /> case 'Modal': return <ModalComp {...props} /> case 'Table': diff --git a/src/npm-fastui/src/models.d.ts b/src/npm-fastui/src/models.d.ts index 461118fe..ddabbd90 100644 --- a/src/npm-fastui/src/models.d.ts +++ b/src/npm-fastui/src/models.d.ts @@ -38,6 +38,7 @@ export type FastProps = | FormFieldFile | FormFieldSelect | FormFieldSelectSearch + | FormFieldRadio | ModelForm export type ClassName = | string @@ -324,6 +325,7 @@ export interface Form { | FormFieldFile | FormFieldSelect | FormFieldSelectSearch + | FormFieldRadio )[] type: 'Form' } @@ -422,6 +424,19 @@ export interface FormFieldSelectSearch { placeholder?: string type: 'FormFieldSelectSearch' } +export interface FormFieldRadio { + name: string + title: string[] | string + required?: boolean + error?: string + locked?: boolean + description?: string + displayMode?: 'default' | 'inline' + className?: ClassName + options: SelectOptions + initial?: string + type: 'FormFieldRadio' +} export interface ModelForm { submitUrl: string initial?: { @@ -441,5 +456,6 @@ export interface ModelForm { | FormFieldFile | FormFieldSelect | FormFieldSelectSearch + | FormFieldRadio )[] } diff --git a/src/python-fastui/fastui/components/__init__.py b/src/python-fastui/fastui/components/__init__.py index 2969a05f..dff43178 100644 --- a/src/python-fastui/fastui/components/__init__.py +++ b/src/python-fastui/fastui/components/__init__.py @@ -21,6 +21,7 @@ FormFieldBoolean, FormFieldFile, FormFieldInput, + FormFieldRadio, FormFieldSelect, FormFieldSelectSearch, ModelForm, @@ -65,6 +66,7 @@ 'FormFieldInput', 'FormFieldSelect', 'FormFieldSelectSearch', + 'FormFieldRadio', ) diff --git a/src/python-fastui/fastui/components/forms.py b/src/python-fastui/fastui/components/forms.py index 539fb6b5..d6a0d370 100644 --- a/src/python-fastui/fastui/components/forms.py +++ b/src/python-fastui/fastui/components/forms.py @@ -73,8 +73,20 @@ class FormFieldSelectSearch(BaseFormField): type: _t.Literal['FormFieldSelectSearch'] = 'FormFieldSelectSearch' +class FormFieldRadio(BaseFormField): + options: forms.SelectOptions + initial: _t.Union[str, None] = None + type: _t.Literal['FormFieldRadio'] = 'FormFieldRadio' + + FormField = _t.Union[ - FormFieldInput, FormFieldTextarea, FormFieldBoolean, FormFieldFile, FormFieldSelect, FormFieldSelectSearch + FormFieldInput, + FormFieldTextarea, + FormFieldBoolean, + FormFieldFile, + FormFieldSelect, + FormFieldSelectSearch, + FormFieldRadio, ] diff --git a/src/python-fastui/fastui/json_schema.py b/src/python-fastui/fastui/json_schema.py index eb209e6f..fd271a61 100644 --- a/src/python-fastui/fastui/json_schema.py +++ b/src/python-fastui/fastui/json_schema.py @@ -10,6 +10,7 @@ FormFieldBoolean, FormFieldFile, FormFieldInput, + FormFieldRadio, FormFieldSelect, FormFieldSelectSearch, FormFieldTextarea, @@ -259,16 +260,26 @@ def special_string_field( ) elif enum := schema.get('enum'): enum_labels = schema.get('enum_labels', {}) - return FormFieldSelect( - name=name, - title=title, - placeholder=schema.get('placeholder'), - required=required, - multiple=multiple, - options=[SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum], - initial=schema.get('default'), - description=schema.get('description'), - ) + if schema.get('mode') == 'radio' and not multiple: + return FormFieldRadio( + name=name, + title=title, + required=required, + options=[SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum], + initial=schema.get('default'), + description=schema.get('description'), + ) + else: + return FormFieldSelect( + name=name, + title=title, + placeholder=schema.get('placeholder'), + required=required, + multiple=multiple, + options=[SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum], + initial=schema.get('default'), + description=schema.get('description'), + ) elif search_url := schema.get('search_url'): return FormFieldSelectSearch( search_url=search_url, From 32a3a92e6a06fd243cba4fb35c38561223da6a86 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Fri, 9 Feb 2024 15:32:33 +0000 Subject: [PATCH 02/13] some fixes --- demo/forms.py | 2 +- src/npm-fastui/src/components/FormField.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/forms.py b/demo/forms.py index ef81be3f..26d6f69b 100644 --- a/demo/forms.py +++ b/demo/forms.py @@ -123,7 +123,7 @@ class ToolEnum(str, enum.Enum): class SelectForm(BaseModel): select_single: ToolEnum = Field(title='Select Single') - select_radio: ToolEnum = Field(title='Select Radio', json_schema_extra={'mode': 'radio'}, description='test ') + select_radio: ToolEnum = Field(title='Select Radio', json_schema_extra={'mode': 'radio'}) select_multiple: list[ToolEnum] = Field(title='Select Multiple') search_select_single: str = Field(json_schema_extra={'search_url': '/api/forms/search'}) search_select_multiple: list[str] = Field(json_schema_extra={'search_url': '/api/forms/search'}) diff --git a/src/npm-fastui/src/components/FormField.tsx b/src/npm-fastui/src/components/FormField.tsx index d9306065..43c2a689 100644 --- a/src/npm-fastui/src/components/FormField.tsx +++ b/src/npm-fastui/src/components/FormField.tsx @@ -312,7 +312,7 @@ interface FormFieldRadioProps extends FormFieldRadio { export const FormFieldRadioComp: FC<FormFieldRadioProps> = (props) => { const { name, required, locked, options, initial } = props const className = useClassName(props) - const inputClassName = useClassName(props, { el: 'input' }) + const inputClassName = useClassName(props, { el: 'radio-option' }) return ( <div className={className}> From e060a43f6dc246f8f3abe499be604f979713f791 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Fri, 9 Feb 2024 15:34:32 +0000 Subject: [PATCH 03/13] add to bootstrap package --- src/npm-fastui-bootstrap/src/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/npm-fastui-bootstrap/src/index.tsx b/src/npm-fastui-bootstrap/src/index.tsx index b2fef51f..86ba3169 100644 --- a/src/npm-fastui-bootstrap/src/index.tsx +++ b/src/npm-fastui-bootstrap/src/index.tsx @@ -75,6 +75,7 @@ export const classNameGenerator: ClassNameGenerator = ({ case 'FormFieldBoolean': case 'FormFieldSelect': case 'FormFieldSelectSearch': + case 'FormFieldRadio': case 'FormFieldFile': switch (subElement) { case 'textarea': From 50ca37edf241203e5a6cf998dedc239c13427b5f Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Fri, 9 Feb 2024 15:43:22 +0000 Subject: [PATCH 04/13] add test for radio --- src/python-fastui/tests/test_forms.py | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/python-fastui/tests/test_forms.py b/src/python-fastui/tests/test_forms.py index b0919fad..ea62a71d 100644 --- a/src/python-fastui/tests/test_forms.py +++ b/src/python-fastui/tests/test_forms.py @@ -1,4 +1,5 @@ from contextlib import asynccontextmanager +from enum import Enum from io import BytesIO from typing import List, Tuple, Union @@ -6,7 +7,7 @@ from fastapi import HTTPException from fastui import components from fastui.forms import FormFile, Textarea, fastui_form -from pydantic import BaseModel +from pydantic import BaseModel, Field from starlette.datastructures import FormData, Headers, UploadFile from typing_extensions import Annotated @@ -469,3 +470,35 @@ def test_form_textarea_form_fields(): } ], } + + +def test_form_radio_form_fields(): + class RadioChoices(Enum): + foo = 'foo' + bar = 'bar' + baz = 'baz' + + class FormRadioSelection(BaseModel): + choice: RadioChoices = Field(..., json_schema_extra={'mode': 'radio'}) + + m = components.ModelForm(model=FormRadioSelection, submit_url='/foobar/') + + assert m.model_dump(by_alias=True, exclude_none=True) == { + 'submitUrl': '/foobar/', + 'method': 'POST', + 'type': 'ModelForm', + 'formFields': [ + { + 'name': 'choice', + 'title': ['RadioChoices'], + 'required': True, + 'locked': False, + 'type': 'FormFieldRadio', + 'options': [ + {'label': 'Foo', 'value': 'foo'}, + {'label': 'Bar', 'value': 'bar'}, + {'label': 'Baz', 'value': 'baz'}, + ], + } + ], + } From 9929abed2b5172686e2e34c7fa2898800c8816ea Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Fri, 9 Feb 2024 16:06:15 +0000 Subject: [PATCH 05/13] add tests for select choices forms --- src/python-fastui/tests/test_forms.py | 51 +++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/src/python-fastui/tests/test_forms.py b/src/python-fastui/tests/test_forms.py index ea62a71d..0e78238c 100644 --- a/src/python-fastui/tests/test_forms.py +++ b/src/python-fastui/tests/test_forms.py @@ -472,15 +472,17 @@ def test_form_textarea_form_fields(): } -def test_form_radio_form_fields(): - class RadioChoices(Enum): - foo = 'foo' - bar = 'bar' - baz = 'baz' +class Choices(Enum): + foo = 'foo' + bar = 'bar' + baz = 'baz' + - class FormRadioSelection(BaseModel): - choice: RadioChoices = Field(..., json_schema_extra={'mode': 'radio'}) +class FormRadioSelection(BaseModel): + choice: Choices = Field(..., json_schema_extra={'mode': 'radio'}) + +def test_form_radio_form_fields(): m = components.ModelForm(model=FormRadioSelection, submit_url='/foobar/') assert m.model_dump(by_alias=True, exclude_none=True) == { @@ -502,3 +504,38 @@ class FormRadioSelection(BaseModel): } ], } + + +@pytest.mark.parametrize('multiple', [True, False]) +def test_form_from_select(multiple: bool): + if multiple: + + class FormSelect(BaseModel): + choice: list[Choices] + else: + + class FormSelect(BaseModel): + choice: Choices + + m = components.ModelForm(model=FormSelect, submit_url='/foobar/') + + assert m.model_dump(by_alias=True, exclude_none=True) == { + 'submitUrl': '/foobar/', + 'method': 'POST', + 'type': 'ModelForm', + 'formFields': [ + { + 'name': 'choice', + 'multiple': multiple, + 'title': ['Choice'] if multiple else ['Choices'], + 'required': True, + 'locked': False, + 'type': 'FormFieldSelect', + 'options': [ + {'label': 'Foo', 'value': 'foo'}, + {'label': 'Bar', 'value': 'bar'}, + {'label': 'Baz', 'value': 'baz'}, + ], + } + ], + } From 1bf95803d3cc863cd34a35237ea769e9c4b9b577 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Fri, 9 Feb 2024 16:13:35 +0000 Subject: [PATCH 06/13] fix tests --- src/python-fastui/tests/test_forms.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/python-fastui/tests/test_forms.py b/src/python-fastui/tests/test_forms.py index 0e78238c..7615c59c 100644 --- a/src/python-fastui/tests/test_forms.py +++ b/src/python-fastui/tests/test_forms.py @@ -492,7 +492,7 @@ def test_form_radio_form_fields(): 'formFields': [ { 'name': 'choice', - 'title': ['RadioChoices'], + 'title': ['Choices'], 'required': True, 'locked': False, 'type': 'FormFieldRadio', @@ -511,7 +511,7 @@ def test_form_from_select(multiple: bool): if multiple: class FormSelect(BaseModel): - choice: list[Choices] + choices: list[Choices] else: class FormSelect(BaseModel): @@ -525,9 +525,9 @@ class FormSelect(BaseModel): 'type': 'ModelForm', 'formFields': [ { - 'name': 'choice', + 'name': 'choices' if multiple else 'choice', 'multiple': multiple, - 'title': ['Choice'] if multiple else ['Choices'], + 'title': ['Choices'], 'required': True, 'locked': False, 'type': 'FormFieldSelect', From f63fe009fce54e807e1456bce996e5554cb93943 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Fri, 9 Feb 2024 16:16:17 +0000 Subject: [PATCH 07/13] type needs to be 3.8 compat --- src/python-fastui/tests/test_forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python-fastui/tests/test_forms.py b/src/python-fastui/tests/test_forms.py index 7615c59c..9451647e 100644 --- a/src/python-fastui/tests/test_forms.py +++ b/src/python-fastui/tests/test_forms.py @@ -511,7 +511,7 @@ def test_form_from_select(multiple: bool): if multiple: class FormSelect(BaseModel): - choices: list[Choices] + choices: List[Choices] else: class FormSelect(BaseModel): From b66f657b90c00207dc9e57ee584a1ed3ec44ad71 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Mon, 12 Feb 2024 16:55:50 +0000 Subject: [PATCH 08/13] make option logic computation only once --- src/python-fastui/fastui/json_schema.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/python-fastui/fastui/json_schema.py b/src/python-fastui/fastui/json_schema.py index fd271a61..d21c0f83 100644 --- a/src/python-fastui/fastui/json_schema.py +++ b/src/python-fastui/fastui/json_schema.py @@ -261,11 +261,12 @@ def special_string_field( elif enum := schema.get('enum'): enum_labels = schema.get('enum_labels', {}) if schema.get('mode') == 'radio' and not multiple: + options = [SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum] return FormFieldRadio( name=name, title=title, required=required, - options=[SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum], + options=options, initial=schema.get('default'), description=schema.get('description'), ) @@ -276,7 +277,7 @@ def special_string_field( placeholder=schema.get('placeholder'), required=required, multiple=multiple, - options=[SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum], + options=options, initial=schema.get('default'), description=schema.get('description'), ) From a6875b14c4a3e11e76b9dbfcf628c6071dae61f7 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Mon, 12 Feb 2024 16:56:08 +0000 Subject: [PATCH 09/13] raise if multiple and radio --- src/python-fastui/fastui/json_schema.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/python-fastui/fastui/json_schema.py b/src/python-fastui/fastui/json_schema.py index d21c0f83..b9fe6200 100644 --- a/src/python-fastui/fastui/json_schema.py +++ b/src/python-fastui/fastui/json_schema.py @@ -260,8 +260,10 @@ def special_string_field( ) elif enum := schema.get('enum'): enum_labels = schema.get('enum_labels', {}) - if schema.get('mode') == 'radio' and not multiple: options = [SelectOption(value=v, label=enum_labels.get(v) or as_title(v)) for v in enum] + if schema.get('mode') == 'radio' and multiple: + raise ValueError('Radio buttons are not supported for multiple choice fields') + elif schema.get('mode') == 'radio' and not multiple: return FormFieldRadio( name=name, title=title, From 52dc4595f241645fb45aa2d54a27203f8c0fd154 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Mon, 12 Feb 2024 16:56:36 +0000 Subject: [PATCH 10/13] remove duplication in react component --- src/npm-fastui/src/components/FormField.tsx | 76 +++++++++------------ 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/src/npm-fastui/src/components/FormField.tsx b/src/npm-fastui/src/components/FormField.tsx index 43c2a689..d916f77b 100644 --- a/src/npm-fastui/src/components/FormField.tsx +++ b/src/npm-fastui/src/components/FormField.tsx @@ -310,57 +310,49 @@ interface FormFieldRadioProps extends FormFieldRadio { } export const FormFieldRadioComp: FC<FormFieldRadioProps> = (props) => { - const { name, required, locked, options, initial } = props - const className = useClassName(props) - const inputClassName = useClassName(props, { el: 'radio-option' }) + const { name, required, locked, options, initial } = props; + const className = useClassName(props); + const inputClassName = useClassName(props, { el: 'radio-input' }); + const labelClassName = useClassName(props, { el: 'radio-label' }); + + const renderRadioInput = (option: SelectOption, i: number, j: number | null = null) => { + const index = j !== null ? `${i}-${j}` : `${i}`; + return ( + <div key={index}> + <input + type="radio" + id={`${inputId(props)}-${index}`} + className={inputClassName} + name={name} + value={option.value} + defaultChecked={option.value === initial} + required={required} + disabled={locked} + aria-describedby={descId(props)} + /> + <label htmlFor={`${inputId(props)}-${index}`} className={labelClassName}>{option.label}</label> + </div> + ); + }; + return ( <div className={className}> - <Label {...props} /> + < Label {...props}/> {options.map((option, i) => { - if ('options' in option) { - // option is a SelectGroup - return option.options.map((subOption, j) => ( - <div key={`${i}-${j}`}> - <input - type="radio" - id={`${inputId(props)}-${i}-${j}`} - className={inputClassName} - name={name} - value={subOption.value} - defaultChecked={subOption.value === initial} - required={required} - disabled={locked} - aria-describedby={descId(props)} - /> - <label htmlFor={`${inputId(props)}-${i}-${j}`}>{subOption.label}</label> - </div> - )) + if ('options' in option && option.options) { + return option.options.map((subOption, j) => + renderRadioInput(subOption, i, j) + ); } else { - // option is a SelectOption - return ( - <div key={i}> - <input - type="radio" - id={`${inputId(props)}-${i}`} - className={inputClassName} - name={name} - value={option.value} - defaultChecked={option.value === initial} - required={required} - disabled={locked} - aria-describedby={descId(props)} - /> - <label htmlFor={`${inputId(props)}-${i}`}>{option.label}</label> - </div> - ) + option = option as SelectOption; + return renderRadioInput(option, i, null); } })} <ErrorDescription {...props} /> </div> - ) -} - + ); +}; const Label: FC<FormFieldProps> = (props) => { let { title } = props if (!Array.isArray(title)) { From 391e41488449f9c340b404146f27f23f2d5bfaab Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Mon, 12 Feb 2024 16:57:19 +0000 Subject: [PATCH 11/13] add bootstrap classes for radio buttons --- src/npm-fastui-bootstrap/src/index.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/npm-fastui-bootstrap/src/index.tsx b/src/npm-fastui-bootstrap/src/index.tsx index 86ba3169..2f8b9d4c 100644 --- a/src/npm-fastui-bootstrap/src/index.tsx +++ b/src/npm-fastui-bootstrap/src/index.tsx @@ -99,6 +99,10 @@ export const classNameGenerator: ClassNameGenerator = ({ return 'invalid-feedback' case 'description': return 'form-text' + case 'radio-input': + return 'form-check-input' + case 'radio-label': + return 'form-check-label' default: return { 'mb-3': true, From aee5458af05141c44e0eb1d378d06fd74f8e93fb Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Mon, 12 Feb 2024 17:15:31 +0000 Subject: [PATCH 12/13] lint js --- src/npm-fastui/src/components/FormField.tsx | 33 ++++++++++----------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/npm-fastui/src/components/FormField.tsx b/src/npm-fastui/src/components/FormField.tsx index d916f77b..8d3dffd3 100644 --- a/src/npm-fastui/src/components/FormField.tsx +++ b/src/npm-fastui/src/components/FormField.tsx @@ -310,13 +310,13 @@ interface FormFieldRadioProps extends FormFieldRadio { } export const FormFieldRadioComp: FC<FormFieldRadioProps> = (props) => { - const { name, required, locked, options, initial } = props; - const className = useClassName(props); - const inputClassName = useClassName(props, { el: 'radio-input' }); - const labelClassName = useClassName(props, { el: 'radio-label' }); + const { name, required, locked, options, initial } = props + const className = useClassName(props) + const inputClassName = useClassName(props, { el: 'radio-input' }) + const labelClassName = useClassName(props, { el: 'radio-label' }) const renderRadioInput = (option: SelectOption, i: number, j: number | null = null) => { - const index = j !== null ? `${i}-${j}` : `${i}`; + const index = j !== null ? `${i}-${j}` : `${i}` return ( <div key={index}> <input @@ -330,29 +330,28 @@ export const FormFieldRadioComp: FC<FormFieldRadioProps> = (props) => { disabled={locked} aria-describedby={descId(props)} /> - <label htmlFor={`${inputId(props)}-${index}`} className={labelClassName}>{option.label}</label> + <label htmlFor={`${inputId(props)}-${index}`} className={labelClassName}> + {option.label} + </label> </div> - ); - }; - + ) + } return ( <div className={className}> - < Label {...props}/> + <Label {...props} /> {options.map((option, i) => { if ('options' in option && option.options) { - return option.options.map((subOption, j) => - renderRadioInput(subOption, i, j) - ); + return option.options.map((subOption, j) => renderRadioInput(subOption, i, j)) } else { - option = option as SelectOption; - return renderRadioInput(option, i, null); + option = option as SelectOption + return renderRadioInput(option, i, null) } })} <ErrorDescription {...props} /> </div> - ); -}; + ) +} const Label: FC<FormFieldProps> = (props) => { let { title } = props if (!Array.isArray(title)) { From bce110e79933686bf61e223800108ea8071204d3 Mon Sep 17 00:00:00 2001 From: tim <timothee.heller@habitat.energy> Date: Mon, 12 Feb 2024 17:31:07 +0000 Subject: [PATCH 13/13] fix demo by having radio below multiple --- demo/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/forms.py b/demo/forms.py index 26d6f69b..07fb678f 100644 --- a/demo/forms.py +++ b/demo/forms.py @@ -123,8 +123,8 @@ class ToolEnum(str, enum.Enum): class SelectForm(BaseModel): select_single: ToolEnum = Field(title='Select Single') - select_radio: ToolEnum = Field(title='Select Radio', json_schema_extra={'mode': 'radio'}) select_multiple: list[ToolEnum] = Field(title='Select Multiple') + select_radio: ToolEnum = Field(title='Select Radio', json_schema_extra={'mode': 'radio'}) search_select_single: str = Field(json_schema_extra={'search_url': '/api/forms/search'}) search_select_multiple: list[str] = Field(json_schema_extra={'search_url': '/api/forms/search'})