From 36c00803b0b55c4b2510a061c89c741c60944048 Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 15 Apr 2024 14:42:34 +1000 Subject: [PATCH] Form focus (#7008) * Add 'focus' property for ApiFormProps * Attempt to focus on particular field * Improve setFocus hook - Set the focus at the appropriate time - Auto focus on first field if not specified * Add custom focus field * Update useEffect property list --- src/frontend/src/components/forms/ApiForm.tsx | 60 +++++++++++++------ .../components/forms/fields/ApiFormField.tsx | 6 +- .../src/components/forms/fields/DateField.tsx | 1 + .../forms/fields/RelatedModelField.tsx | 1 + .../src/tables/part/ParametricPartTable.tsx | 2 + .../src/tables/part/PartParameterTable.tsx | 5 +- 6 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/frontend/src/components/forms/ApiForm.tsx b/src/frontend/src/components/forms/ApiForm.tsx index bb8d203d9e..4d02009d29 100644 --- a/src/frontend/src/components/forms/ApiForm.tsx +++ b/src/frontend/src/components/forms/ApiForm.tsx @@ -66,6 +66,7 @@ export interface ApiFormProps { pathParams?: PathParams; method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; fields?: ApiFormFieldSet; + focus?: string; initialData?: FieldValues; submitText?: string; submitColor?: string; @@ -292,7 +293,48 @@ export function ApiForm({ }); initialDataQuery.refetch(); } - }, []); + }, [props.fetchInitialData]); + + const isLoading = useMemo( + () => + isFormLoading || + initialDataQuery.isFetching || + optionsLoading || + isSubmitting || + !props.fields, + [ + isFormLoading, + initialDataQuery.isFetching, + isSubmitting, + props.fields, + optionsLoading + ] + ); + + const [initialFocus, setInitialFocus] = useState(''); + + // Update field focus when the form is loaded + useEffect(() => { + let focusField = props.focus ?? ''; + + if (!focusField) { + // If a focus field is not specified, then focus on the first available field + Object.entries(props.fields ?? {}).forEach(([fieldName, field]) => { + if (focusField || field.read_only || field.disabled || field.hidden) { + return; + } + + focusField = fieldName; + }); + } + + if (isLoading || initialFocus == focusField) { + return; + } + + form.setFocus(focusField); + setInitialFocus(focusField); + }, [props.focus, props.fields, form.setFocus, isLoading, initialFocus]); const submitForm: SubmitHandler = async (data) => { setNonFieldErrors([]); @@ -392,22 +434,6 @@ export function ApiForm({ }); }; - const isLoading = useMemo( - () => - isFormLoading || - initialDataQuery.isFetching || - optionsLoading || - isSubmitting || - !props.fields, - [ - isFormLoading, - initialDataQuery.isFetching, - isSubmitting, - props.fields, - optionsLoading - ] - ); - const onFormError = useCallback>(() => { props.onFormError?.(); }, [props.onFormError]); diff --git a/src/frontend/src/components/forms/fields/ApiFormField.tsx b/src/frontend/src/components/forms/fields/ApiFormField.tsx index 3e3dff7c9c..237ca74fdf 100644 --- a/src/frontend/src/components/forms/fields/ApiFormField.tsx +++ b/src/frontend/src/components/forms/fields/ApiFormField.tsx @@ -188,7 +188,7 @@ export function ApiFormField({ return ( { diff --git a/src/frontend/src/tables/part/ParametricPartTable.tsx b/src/frontend/src/tables/part/ParametricPartTable.tsx index 80a41bdcfe..ec53ab9ba7 100644 --- a/src/frontend/src/tables/part/ParametricPartTable.tsx +++ b/src/frontend/src/tables/part/ParametricPartTable.tsx @@ -132,6 +132,7 @@ export default function ParametricPartTable({ url: ApiEndpoints.part_parameter_list, title: t`Add Part Parameter`, fields: partParameterFields, + focus: 'data', onFormSuccess: (parameter: any) => { updateParameterRecord(selectedPart, parameter); }, @@ -146,6 +147,7 @@ export default function ParametricPartTable({ title: t`Edit Part Parameter`, pk: selectedParameter, fields: partParameterFields, + focus: 'data', onFormSuccess: (parameter: any) => { updateParameterRecord(selectedPart, parameter); } diff --git a/src/frontend/src/tables/part/PartParameterTable.tsx b/src/frontend/src/tables/part/PartParameterTable.tsx index 80e37fbb20..79f514686a 100644 --- a/src/frontend/src/tables/part/PartParameterTable.tsx +++ b/src/frontend/src/tables/part/PartParameterTable.tsx @@ -95,7 +95,9 @@ export function PartParameterTable({ partId }: { partId: any }) { const partParameterFields: ApiFormFieldSet = useMemo(() => { return { - part: {}, + part: { + disabled: true + }, template: {}, data: {} }; @@ -105,6 +107,7 @@ export function PartParameterTable({ partId }: { partId: any }) { url: ApiEndpoints.part_parameter_list, title: t`New Part Parameter`, fields: partParameterFields, + focus: 'template', initialData: { part: partId },