From 334901364690081cb1b6faa9b927eccfaef7e8ed Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 18 Oct 2023 14:45:03 +1100 Subject: [PATCH] [React] Improvements to API forms (#5742) * Remove read_only field from API OPTIONS - 'read_only' is a reserved field for some Mantine components * Fixing further errors for related field model * Handle bad values for numericalValue * Fix for default values for API forms * Fix for choice field - Do not set form default value when constructing element * Tweak for PartDetail page --- src/frontend/src/components/forms/ApiForm.tsx | 18 ++++++---- .../components/forms/fields/ApiFormField.tsx | 21 +++++++++-- .../components/forms/fields/ChoiceField.tsx | 2 -- .../forms/fields/RelatedModelField.tsx | 36 +++++++++++++------ src/frontend/src/functions/forms.tsx | 6 +++- src/frontend/src/pages/part/PartDetail.tsx | 2 +- 6 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/frontend/src/components/forms/ApiForm.tsx b/src/frontend/src/components/forms/ApiForm.tsx index bb875382d9..50dc588f01 100644 --- a/src/frontend/src/components/forms/ApiForm.tsx +++ b/src/frontend/src/components/forms/ApiForm.tsx @@ -136,13 +136,19 @@ export function ApiForm({ useEffect(() => { // Provide initial form data Object.entries(props.fields ?? {}).forEach(([fieldName, field]) => { - if (field.value !== undefined) { + // fieldDefinition is supplied by the API, and can serve as a backup + let fieldDefinition = fieldDefinitions[fieldName] ?? {}; + + let v = + field.value ?? + field.default ?? + fieldDefinition.value ?? + fieldDefinition.default ?? + undefined; + + if (v !== undefined) { form.setValues({ - [fieldName]: field.value - }); - } else if (field.default !== undefined) { - form.setValues({ - [fieldName]: field.default + [fieldName]: v }); } }); diff --git a/src/frontend/src/components/forms/fields/ApiFormField.tsx b/src/frontend/src/components/forms/fields/ApiFormField.tsx index c03c456a64..79b71bfa42 100644 --- a/src/frontend/src/components/forms/fields/ApiFormField.tsx +++ b/src/frontend/src/components/forms/fields/ApiFormField.tsx @@ -68,6 +68,7 @@ export type ApiFormFieldType = { choices?: any[]; hidden?: boolean; disabled?: boolean; + read_only?: boolean; placeholder?: string; description?: string; preFieldContent?: JSX.Element | (() => JSX.Element); @@ -115,6 +116,10 @@ export function constructField({ break; } + // Clear out the 'read_only' attribute + def.disabled = def.disabled ?? def.read_only ?? false; + delete def['read_only']; + return def; } @@ -190,16 +195,26 @@ export function ApiFormField({ // Coerce the value to a numerical value const numericalValue: number | undefined = useMemo(() => { + let val = 0; + switch (definition.field_type) { case 'integer': - return parseInt(value); + val = parseInt(value) ?? 0; + break; case 'decimal': case 'float': case 'number': - return parseFloat(value); + val = parseFloat(value) ?? 0; + break; default: - return undefined; + break; } + + if (isNaN(val) || !isFinite(val)) { + val = 0; + } + + return val; }, [value]); // Construct the individual field diff --git a/src/frontend/src/components/forms/fields/ChoiceField.tsx b/src/frontend/src/components/forms/fields/ChoiceField.tsx index 589cf83dd6..0395072590 100644 --- a/src/frontend/src/components/forms/fields/ChoiceField.tsx +++ b/src/frontend/src/components/forms/fields/ChoiceField.tsx @@ -34,8 +34,6 @@ export function ChoiceField({ definitions: definitions }); - form.setValues({ [fieldName]: def.value ?? def.default }); - return def; }, [fieldName, field, definitions]); diff --git a/src/frontend/src/components/forms/fields/RelatedModelField.tsx b/src/frontend/src/components/forms/fields/RelatedModelField.tsx index aa29fc65ae..9b3231ccf0 100644 --- a/src/frontend/src/components/forms/fields/RelatedModelField.tsx +++ b/src/frontend/src/components/forms/fields/RelatedModelField.tsx @@ -37,16 +37,18 @@ export function RelatedModelField({ // Extract field definition from provided data // Where user has provided specific data, override the API definition - const definition: ApiFormFieldType = useMemo( - () => - constructField({ - form: form, - field: field, - fieldName: fieldName, - definitions: definitions - }), - [form.values, field, definitions] - ); + const definition: ApiFormFieldType = useMemo(() => { + let def = constructField({ + form: form, + field: field, + fieldName: fieldName, + definitions: definitions + }); + + // Remove the 'read_only' attribute (causes issues with Mantine) + delete def['read_only']; + return def; + }, [form.values, field, definitions]); // Keep track of the primary key value for this field const [pk, setPk] = useState(null); @@ -170,8 +172,20 @@ export function RelatedModelField({ } } + /* Construct a "cut-down" version of the definition, + * which does not include any attributes that the lower components do not recognize + */ + const fieldDefinition = useMemo(() => { + return { + ...definition, + onValueChange: undefined, + adjustFilters: undefined, + read_only: undefined + }; + }, [definition]); + return ( - +