From 8a72d3a50d09a4d9cb8b84f65325975cc80071a4 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Fri, 11 Apr 2025 08:40:14 +0200 Subject: [PATCH 01/13] export typeahead utils --- src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.ts b/src/index.ts index 1aed903..434f057 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,5 +8,9 @@ export * from "./lib/types/LabelValueOption"; export * from "./lib/DatePickerInput"; export * from "./lib/ColorPickerInput"; export * from "./lib/helpers/dateUtils"; +export * from "./lib/helpers/mui"; +export * from "./lib/helpers/typeahead"; +export * from "./lib/hooks/useDebounceHook"; +export * from "./lib/hooks/useSafeNameId"; export { useFormContext } from "./lib/context/FormContext"; From f8dc2b0a903de315cb0b7d6e9032d320b15674a3 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Wed, 16 Apr 2025 12:38:57 +0200 Subject: [PATCH 02/13] integrate placeholder fix and changelog --- CHANGELOG.md | 8 ++++++++ .../component/Typeahead/AsyncTypeaheadInput.cy.tsx | 6 +++--- .../component/Typeahead/StaticTypeaheadInput.cy.tsx | 9 +++------ src/index.ts | 1 - src/lib/AsyncTypeaheadInput.tsx | 2 +- src/lib/StaticTypeaheadInput.tsx | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4503f74..c71d00b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Export typeahead helpers and `useDebounceHook` hook. + +### Fixed + +- Hide placeholder on multiple `AsyncTypeAheadInput` and `StaticTypeAheadInput` when at least one option is selected. + ## [3.1.1] - 2025-04-10 ### Fixed diff --git a/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx b/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx index 99dbe00..05f8998 100644 --- a/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx +++ b/cypress/cypress/component/Typeahead/AsyncTypeaheadInput.cy.tsx @@ -641,9 +641,6 @@ it("placeholder", () => { onSubmit={() => { // Nothing to do }} - defaultValues={{ - [name]: simpleOptions, - }} > { , ); + cy.get(`#${name}`).should("have.attr", "placeholder", placeholder); + simpleOptions.slice(0, 2).forEach((option) => selectOption(name, option)); + cy.get(`#${name}`).should("not.have.attr", "placeholder"); }); it("test on input change", () => { diff --git a/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx b/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx index 8ec1066..ad64378 100644 --- a/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx +++ b/cypress/cypress/component/Typeahead/StaticTypeaheadInput.cy.tsx @@ -398,12 +398,7 @@ it("placeholder", () => { cy.mount(
-
+ @@ -411,6 +406,8 @@ it("placeholder", () => { ); cy.get(`#${name}`).should("have.attr", "placeholder", placeholder); + simpleOptions.slice(0, 2).forEach((option) => selectOption(name, option)); + cy.get(`#${name}`).should("not.have.attr", "placeholder"); }); it("test on input change", () => { diff --git a/src/index.ts b/src/index.ts index 434f057..8831d0a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,5 @@ export * from "./lib/helpers/dateUtils"; export * from "./lib/helpers/mui"; export * from "./lib/helpers/typeahead"; export * from "./lib/hooks/useDebounceHook"; -export * from "./lib/hooks/useSafeNameId"; export { useFormContext } from "./lib/context/FormContext"; diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index 42f8662..c2892a2 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -206,7 +206,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr hideValidationMessage={hideValidationMessage} useBootstrapStyle={useBootstrapStyle} helpText={helpText} - placeholder={placeholder} + placeholder={multiple && value.length > 0 ? undefined : placeholder} paginationIcon={paginationIcon} paginationText={paginationText} variant={variant} diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index b72e47c..ad59824 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -169,7 +169,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput hideValidationMessage={hideValidationMessage} useBootstrapStyle={useBootstrapStyle} helpText={helpText} - placeholder={placeholder} + placeholder={multiple && value.length > 0 ? undefined : placeholder} paginationIcon={paginationIcon} paginationText={paginationText} variant={variant} From a8573843aaf2a7d2b9f1de219c05ce930b328464 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Wed, 16 Apr 2025 15:27:23 +0200 Subject: [PATCH 03/13] adjust typescript --- src/lib/AsyncTypeaheadInput.tsx | 10 ++++++---- src/lib/StaticTypeaheadInput.tsx | 4 +++- src/lib/helpers/typeahead.tsx | 20 ++++++++++---------- src/lib/hooks/useDebounceHook.ts | 4 ++-- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index c2892a2..cab92da 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -69,8 +69,8 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr autocompleteProps, } = props; - const [options, setOptions] = useState(defaultOptions); - const [value, setValue] = useState(defaultSelected); + const [options, setOptions] = useState(defaultOptions); + const [value, setValue] = useState(defaultSelected); const [page, setPage] = useState(1); const [loadMoreOptions, setLoadMoreOptions] = useState(limitResults !== undefined && limitResults < defaultOptions.length); const { name, id } = useSafeNameId(props.name ?? "", props.id); @@ -107,7 +107,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr defaultSelected.map((x) => (typeof x === "string" ? x : x.value)), ); }, - updateValues: (options: TypeaheadOption[]) => { + updateValues: (options: TypeaheadOptions) => { const values = convertAutoCompleteOptionsToStringArray(options); const finalValue = multiple ? values : values[0]; setValue(options); @@ -171,7 +171,9 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr field.onBlur(); }} onChange={(_e, value) => { - const optionsArray = value ? (Array.isArray(value) ? value : [value]) : undefined; + // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) + // however, the component is not intended to be used with mixed types + const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; setValue(optionsArray ?? []); const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index ad59824..715281a 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -140,7 +140,9 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput field.onBlur(); }} onChange={(_, value) => { - const optionsArray = value ? (Array.isArray(value) ? value : [value]) : undefined; + // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) + // however, the component is not intended to be used with mixed types + const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; clearErrors(field.name); diff --git a/src/lib/helpers/typeahead.tsx b/src/lib/helpers/typeahead.tsx index 7aef6be..de85e7a 100644 --- a/src/lib/helpers/typeahead.tsx +++ b/src/lib/helpers/typeahead.tsx @@ -1,12 +1,12 @@ import { AutocompleteRenderOptionState } from "@mui/material/Autocomplete"; import { LabelValueOption } from "../types/LabelValueOption"; -import { TypeaheadOption } from "../types/Typeahead"; +import { TypeaheadOption, TypeaheadOptions } from "../types/Typeahead"; import AutosuggestHighlightMatch from "autosuggest-highlight/match"; import AutosuggestHighlightParse from "autosuggest-highlight/parse"; -const isStringArray = (options: TypeaheadOption[]): boolean => options.length > 0 && options.every((value) => typeof value === "string"); +const isStringArray = (options: TypeaheadOptions): boolean => options.length > 0 && (options as TypeaheadOption[]).every((value) => typeof value === "string"); -const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOption[] | undefined): string[] => { +const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOptions | undefined): string[] => { if (!options) { return []; } @@ -18,29 +18,29 @@ const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOption[] | un return (options as LabelValueOption[]).map((option) => option.value) as string[]; }; -const getSingleAutoCompleteValue = (options: TypeaheadOption[], fieldValue: string | number | undefined): TypeaheadOption[] => { +const getSingleAutoCompleteValue = (options: TypeaheadOptions, fieldValue: string | number | undefined): TypeaheadOptions => { if (fieldValue == undefined) { return []; } - return options.filter((x) => + return (options as TypeaheadOption[]).filter((x) => // loose equality check to handle different types between form value and option value typeof x === "string" ? x == fieldValue : x.value == fieldValue, - ); + ) as TypeaheadOptions; }; -const getMultipleAutoCompleteValue = (options: TypeaheadOption[], fieldValue: (string | number)[] | undefined): TypeaheadOption[] => { +const getMultipleAutoCompleteValue = (options: TypeaheadOptions, fieldValue: (string | number)[] | undefined): TypeaheadOptions => { if (fieldValue == undefined) { return []; } - return options.filter((x) => + return (options as TypeaheadOption[]).filter((x: TypeaheadOption) => typeof x === "string" ? fieldValue.includes(x) : // ensure that form values matches options values even if they are of different types fieldValue.map(String).includes(String(x.value as string | number)), - ); + ) as TypeaheadOptions; }; -const sortOptionsByGroup = (options: TypeaheadOption[]): TypeaheadOption[] => +const sortOptionsByGroup = (options: TypeaheadOptions): TypeaheadOptions => options.sort((x, y) => (typeof x === "string" ? x : x.group?.name ?? "").localeCompare(typeof y === "string" ? y : y.group?.name ?? "")); const groupOptions = (option: TypeaheadOption): string => (typeof option === "string" ? option : option.group?.name ?? ""); diff --git a/src/lib/hooks/useDebounceHook.ts b/src/lib/hooks/useDebounceHook.ts index c97f72c..31d4595 100644 --- a/src/lib/hooks/useDebounceHook.ts +++ b/src/lib/hooks/useDebounceHook.ts @@ -1,12 +1,12 @@ import { useEffect, useRef, useState } from "react"; -import { TypeaheadOption, TypeaheadOptions } from "../types/Typeahead"; +import { TypeaheadOptions } from "../types/Typeahead"; interface DebounceSearch { query: string; delay: number; } -const useDebounceHook = (queryFn: (query: string) => Promise, setOptions: (results: TypeaheadOption[]) => void) => { +const useDebounceHook = (queryFn: (query: string) => Promise, setOptions: (results: TypeaheadOptions) => void) => { const queryRef = useRef(""); const [isLoading, setIsLoading] = useState(false); const [debounceSearch, setDebounceSearch] = useState(undefined); From e1267ef282dd50afce477920d143f4a4ee555a8c Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Mon, 28 Apr 2025 09:34:08 +0200 Subject: [PATCH 04/13] prepare-pr --- CHANGELOG.md | 1 + src/lib/AsyncTypeaheadInput.tsx | 2 +- src/lib/StaticTypeaheadInput.tsx | 2 +- src/lib/helpers/typeahead.tsx | 5 +++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c71d00b..dc11842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Export typeahead helpers and `useDebounceHook` hook. +- Typeahead helpers return type from `TypeaheadOption[]` to `TypeaheadOptions`, since typeaheads are not intended to be used with mixed `string` and `LabelValueOption` options. ### Fixed diff --git a/src/lib/AsyncTypeaheadInput.tsx b/src/lib/AsyncTypeaheadInput.tsx index cab92da..c2850f3 100644 --- a/src/lib/AsyncTypeaheadInput.tsx +++ b/src/lib/AsyncTypeaheadInput.tsx @@ -173,7 +173,7 @@ const AsyncTypeaheadInput = (props: AsyncTypeaheadInputPr onChange={(_e, value) => { // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) // however, the component is not intended to be used with mixed types - const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; + const optionsArray = value ? ((Array.isArray(value) ? value : [value]) as TypeaheadOptions) : undefined; setValue(optionsArray ?? []); const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index 715281a..9e29134 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -142,7 +142,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput onChange={(_, value) => { // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) // however, the component is not intended to be used with mixed types - const optionsArray = value ? (Array.isArray(value) ? value : [value]) as TypeaheadOptions : undefined; + const optionsArray = value ? ((Array.isArray(value) ? value : [value]) as TypeaheadOptions) : undefined; const values = convertAutoCompleteOptionsToStringArray(optionsArray); const finalValue = multiple ? values : values[0]; clearErrors(field.name); diff --git a/src/lib/helpers/typeahead.tsx b/src/lib/helpers/typeahead.tsx index de85e7a..d552b32 100644 --- a/src/lib/helpers/typeahead.tsx +++ b/src/lib/helpers/typeahead.tsx @@ -4,7 +4,8 @@ import { TypeaheadOption, TypeaheadOptions } from "../types/Typeahead"; import AutosuggestHighlightMatch from "autosuggest-highlight/match"; import AutosuggestHighlightParse from "autosuggest-highlight/parse"; -const isStringArray = (options: TypeaheadOptions): boolean => options.length > 0 && (options as TypeaheadOption[]).every((value) => typeof value === "string"); +const isStringArray = (options: TypeaheadOptions): boolean => + options.length > 0 && (options as TypeaheadOption[]).every((value) => typeof value === "string"); const convertAutoCompleteOptionsToStringArray = (options: TypeaheadOptions | undefined): string[] => { if (!options) { @@ -32,7 +33,7 @@ const getMultipleAutoCompleteValue = (options: TypeaheadOptions, fieldValue: (st if (fieldValue == undefined) { return []; } - return (options as TypeaheadOption[]).filter((x: TypeaheadOption) => + return (options as TypeaheadOption[]).filter((x) => typeof x === "string" ? fieldValue.includes(x) : // ensure that form values matches options values even if they are of different types From 12211f1448a0b73a6d7b8e9795a8e7633706d4ae Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Wed, 30 Apr 2025 11:41:20 +0200 Subject: [PATCH 05/13] fixed --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc11842..99f5cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Export typeahead helpers and `useDebounceHook` hook. -- Typeahead helpers return type from `TypeaheadOption[]` to `TypeaheadOptions`, since typeaheads are not intended to be used with mixed `string` and `LabelValueOption` options. ### Fixed - Hide placeholder on multiple `AsyncTypeAheadInput` and `StaticTypeAheadInput` when at least one option is selected. +- Typeahead helpers return type from `TypeaheadOption[]` to `TypeaheadOptions`, since typeaheads are not intended to be used with mixed `string` and `LabelValueOption` options. ## [3.1.1] - 2025-04-10 From 736a9237812f6f69804e7d8857eb94a673c6c76c Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Tue, 13 Jan 2026 12:48:53 +0100 Subject: [PATCH 06/13] test --- rollup.config.mjs | 2 +- src/lib/StaticTypeaheadInput.tsx | 95 ++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/rollup.config.mjs b/rollup.config.mjs index 563e5af..9bb3c6f 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -26,7 +26,7 @@ const plugins = [ terser({ output: { comments: false }, compress: { - drop_console: true, + drop_console: false, }, }), ]; diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index 1e23df8..e4067cf 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { FieldValues, useController } from "react-hook-form"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { CommonTypeaheadProps, StaticTypeaheadAutocompleteProps, TypeaheadOption, TypeaheadOptions } from "./types/Typeahead"; @@ -100,6 +100,22 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput [fieldValue, multiple, options, fixedOptions], ); + const handleOnChange = useCallback( + (value: string | LabelValueOption | TypeaheadOption[] | null) => { + // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) + // however, the component is not intended to be used with mixed types + const optionsArray = getOptionsFromValue(value, fixedOptions, withFixedOptionsInValue); + const values = convertAutoCompleteOptionsToStringArray(optionsArray); + const finalValue = multiple ? values : values[0]; + clearErrors(field.name); + if (onChange) { + onChange(finalValue); + } + field.onChange(finalValue); + }, + [clearErrors, field, fixedOptions, multiple, onChange, withFixedOptionsInValue], + ); + validateFixedOptions(fixedOptions, multiple, autocompleteProps, withFixedOptionsInValue, value); useEffect(() => { @@ -108,6 +124,8 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput } }, [options, page, limitResults]); + console.log("Rendering StaticTypeaheadInput:", { name, id, value, fieldValue, options }); + return ( (props: StaticTypeaheadInput field.onBlur(); }} onChange={(_, value) => { - // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) - // however, the component is not intended to be used with mixed types - const optionsArray = getOptionsFromValue(value, fixedOptions, withFixedOptionsInValue); - const values = convertAutoCompleteOptionsToStringArray(optionsArray); - const finalValue = multiple ? values : values[0]; - clearErrors(field.name); - if (onChange) { - onChange(finalValue); - } - field.onChange(finalValue); + handleOnChange(value); }} onInputChange={(_e, value, reason) => { if (onInputChange) { @@ -171,36 +180,40 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput } }} renderOption={highlightOptions ? renderHighlightedOptionFunction : undefined} - renderInput={(params) => ( - 0 ? undefined : placeholder} - paginationIcon={paginationIcon} - paginationText={paginationText} - variant={variant} - limitResults={limitResults} - loadMoreOptions={loadMoreOptions} - setPage={setPage} - {...params} - inputRef={(elem) => { - if (innerRef) { - innerRef.current = elem as HTMLInputElement; - } - ref(elem); - }} - /> - )} + renderInput={(params) => { + console.log("re-render the renderInput with params:", params); + + return ( + 0 ? undefined : placeholder} + paginationIcon={paginationIcon} + paginationText={paginationText} + variant={variant} + limitResults={limitResults} + loadMoreOptions={loadMoreOptions} + setPage={setPage} + {...params} + inputRef={(elem) => { + if (innerRef) { + innerRef.current = elem as HTMLInputElement; + } + ref(elem); + }} + /> + ); + }} renderTags={createTagRenderer(fixedOptions, autocompleteProps)} /> From dac96eed461e25586a76a2e33bd25a9d6a5040dc Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Tue, 13 Jan 2026 13:50:23 +0100 Subject: [PATCH 07/13] no watch test --- src/lib/StaticTypeaheadInput.tsx | 98 ++++++++++++++------------------ 1 file changed, 43 insertions(+), 55 deletions(-) diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index e4067cf..63b9091 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { FieldValues, useController } from "react-hook-form"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { CommonTypeaheadProps, StaticTypeaheadAutocompleteProps, TypeaheadOption, TypeaheadOptions } from "./types/Typeahead"; @@ -72,7 +72,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput const [loadMoreOptions, setLoadMoreOptions] = useState(limitResults !== undefined && limitResults < options.length); const { name, id } = useSafeNameId(props.name ?? "", props.id); - const { control, disabled: formDisabled, getFieldState, clearErrors, watch } = useFormContext(); + const { control, disabled: formDisabled, getFieldState, clearErrors } = useFormContext(); const { field: { ref, ...field }, } = useController({ @@ -91,7 +91,7 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput [limitResults, page, options], ); - const fieldValue = watch(name) as string | number | string[] | number[] | undefined; + const fieldValue = field.value as string | number | string[] | number[] | undefined; const value = useMemo( () => multiple @@ -100,22 +100,6 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput [fieldValue, multiple, options, fixedOptions], ); - const handleOnChange = useCallback( - (value: string | LabelValueOption | TypeaheadOption[] | null) => { - // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) - // however, the component is not intended to be used with mixed types - const optionsArray = getOptionsFromValue(value, fixedOptions, withFixedOptionsInValue); - const values = convertAutoCompleteOptionsToStringArray(optionsArray); - const finalValue = multiple ? values : values[0]; - clearErrors(field.name); - if (onChange) { - onChange(finalValue); - } - field.onChange(finalValue); - }, - [clearErrors, field, fixedOptions, multiple, onChange, withFixedOptionsInValue], - ); - validateFixedOptions(fixedOptions, multiple, autocompleteProps, withFixedOptionsInValue, value); useEffect(() => { @@ -135,7 +119,6 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput > {...autocompleteProps} - {...field} id={id} multiple={multiple} groupBy={useGroupBy ? groupOptions : undefined} @@ -172,7 +155,16 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput field.onBlur(); }} onChange={(_, value) => { - handleOnChange(value); + // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) + // however, the component is not intended to be used with mixed types + const optionsArray = getOptionsFromValue(value, fixedOptions, withFixedOptionsInValue); + const values = convertAutoCompleteOptionsToStringArray(optionsArray); + const finalValue = multiple ? values : values[0]; + clearErrors(field.name); + if (onChange) { + onChange(finalValue); + } + field.onChange(finalValue); }} onInputChange={(_e, value, reason) => { if (onInputChange) { @@ -180,40 +172,36 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput } }} renderOption={highlightOptions ? renderHighlightedOptionFunction : undefined} - renderInput={(params) => { - console.log("re-render the renderInput with params:", params); - - return ( - 0 ? undefined : placeholder} - paginationIcon={paginationIcon} - paginationText={paginationText} - variant={variant} - limitResults={limitResults} - loadMoreOptions={loadMoreOptions} - setPage={setPage} - {...params} - inputRef={(elem) => { - if (innerRef) { - innerRef.current = elem as HTMLInputElement; - } - ref(elem); - }} - /> - ); - }} + renderInput={(params) => ( + 0 ? undefined : placeholder} + paginationIcon={paginationIcon} + paginationText={paginationText} + variant={variant} + limitResults={limitResults} + loadMoreOptions={loadMoreOptions} + setPage={setPage} + {...params} + inputRef={(elem) => { + if (innerRef) { + innerRef.current = elem as HTMLInputElement; + } + ref(elem); + }} + /> + )} renderTags={createTagRenderer(fixedOptions, autocompleteProps)} /> From ac6b4c8d5d24cd20238552f5024e82925d08dc8b Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Tue, 13 Jan 2026 14:40:42 +0100 Subject: [PATCH 08/13] try to isolate --- src/lib/StaticTypeaheadInput.tsx | 190 ++++++++++++++++--------------- 1 file changed, 99 insertions(+), 91 deletions(-) diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index 63b9091..218a283 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -30,7 +30,7 @@ interface StaticTypeaheadInputProps extends CommonTypeahe } // eslint-disable-next-line complexity -const StaticTypeaheadInput = (props: StaticTypeaheadInputProps) => { +const AutoComplete = (props: StaticTypeaheadInputProps) => { const { options, multiple, @@ -66,13 +66,14 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput fixedOptions, withFixedOptionsInValue = true, innerRef, + name, + id, } = props; const [page, setPage] = useState(1); const [loadMoreOptions, setLoadMoreOptions] = useState(limitResults !== undefined && limitResults < options.length); - const { name, id } = useSafeNameId(props.name ?? "", props.id); - const { control, disabled: formDisabled, getFieldState, clearErrors } = useFormContext(); + const { control, disabled: formDisabled, getFieldState, clearErrors } = useFormContext(); const { field: { ref, ...field }, } = useController({ @@ -108,7 +109,100 @@ const StaticTypeaheadInput = (props: StaticTypeaheadInput } }, [options, page, limitResults]); - console.log("Rendering StaticTypeaheadInput:", { name, id, value, fieldValue, options }); + return ( + + {...autocompleteProps} + id={id} + multiple={multiple} + groupBy={useGroupBy ? groupOptions : undefined} + options={useGroupBy ? sortOptionsByGroup(paginatedOptions) : paginatedOptions} + isOptionEqualToValue={ + autocompleteProps?.isOptionEqualToValue ?? + ((option, value) => (typeof option === "string" ? option === value : option.value === (value as LabelValueOption).value)) + } + getOptionKey={ + autocompleteProps?.getOptionKey ?? + ((option: TypeaheadOption) => (typeof option === "string" ? option : `${option.label}-${option.value ?? ""}`)) + } + disableCloseOnSelect={multiple} + value={resolveInputValue(multiple, fixedOptions, withFixedOptionsInValue, value)} + getOptionLabel={(option: TypeaheadOption) => (typeof option === "string" ? option : option.label)} + getOptionDisabled={(option) => + getOptionDisabled?.(option) || + (useGroupBy && isDisabledGroup(option)) || + (typeof option === "string" ? false : (option.disabled ?? false)) + } + disabled={isDisabled} + readOnly={readOnly} + selectOnFocus={markAllOnFocus} + style={inputGroupStyle} + className={className} + autoSelect={autoSelect} + autoHighlight={autoHighlight} + onClose={readOnly ? undefined : onClose} + onOpen={readOnly ? undefined : onOpen} + onBlur={() => { + if (onBlur) { + onBlur(); + } + field.onBlur(); + }} + onChange={(_, value) => { + // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) + // however, the component is not intended to be used with mixed types + const optionsArray = getOptionsFromValue(value, fixedOptions, withFixedOptionsInValue); + const values = convertAutoCompleteOptionsToStringArray(optionsArray); + const finalValue = multiple ? values : values[0]; + clearErrors(field.name); + if (onChange) { + onChange(finalValue); + } + field.onChange(finalValue); + }} + onInputChange={(_e, value, reason) => { + if (onInputChange) { + onInputChange(value, reason); + } + }} + renderOption={highlightOptions ? renderHighlightedOptionFunction : undefined} + renderInput={(params) => ( + 0 ? undefined : placeholder} + paginationIcon={paginationIcon} + paginationText={paginationText} + variant={variant} + limitResults={limitResults} + loadMoreOptions={loadMoreOptions} + setPage={setPage} + {...params} + inputRef={(elem) => { + if (innerRef) { + innerRef.current = elem as HTMLInputElement; + } + ref(elem); + }} + /> + )} + renderTags={createTagRenderer(fixedOptions, autocompleteProps)} + /> + ); +}; + +const StaticTypeaheadInput = (props: StaticTypeaheadInputProps) => { + const { label, useBootstrapStyle = false } = props; + const { name, id } = useSafeNameId(props.name ?? "", props.id); return ( (props: StaticTypeaheadInput labelStyle={useBootstrapStyle ? { color: "#8493A5", fontSize: 14 } : undefined} layout="muiInput" > - - {...autocompleteProps} - id={id} - multiple={multiple} - groupBy={useGroupBy ? groupOptions : undefined} - options={useGroupBy ? sortOptionsByGroup(paginatedOptions) : paginatedOptions} - isOptionEqualToValue={ - autocompleteProps?.isOptionEqualToValue ?? - ((option, value) => (typeof option === "string" ? option === value : option.value === (value as LabelValueOption).value)) - } - getOptionKey={ - autocompleteProps?.getOptionKey ?? - ((option: TypeaheadOption) => (typeof option === "string" ? option : `${option.label}-${option.value ?? ""}`)) - } - disableCloseOnSelect={multiple} - value={resolveInputValue(multiple, fixedOptions, withFixedOptionsInValue, value)} - getOptionLabel={(option: TypeaheadOption) => (typeof option === "string" ? option : option.label)} - getOptionDisabled={(option) => - getOptionDisabled?.(option) || - (useGroupBy && isDisabledGroup(option)) || - (typeof option === "string" ? false : (option.disabled ?? false)) - } - disabled={isDisabled} - readOnly={readOnly} - selectOnFocus={markAllOnFocus} - style={inputGroupStyle} - className={className} - autoSelect={autoSelect} - autoHighlight={autoHighlight} - onClose={readOnly ? undefined : onClose} - onOpen={readOnly ? undefined : onOpen} - onBlur={() => { - if (onBlur) { - onBlur(); - } - field.onBlur(); - }} - onChange={(_, value) => { - // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) - // however, the component is not intended to be used with mixed types - const optionsArray = getOptionsFromValue(value, fixedOptions, withFixedOptionsInValue); - const values = convertAutoCompleteOptionsToStringArray(optionsArray); - const finalValue = multiple ? values : values[0]; - clearErrors(field.name); - if (onChange) { - onChange(finalValue); - } - field.onChange(finalValue); - }} - onInputChange={(_e, value, reason) => { - if (onInputChange) { - onInputChange(value, reason); - } - }} - renderOption={highlightOptions ? renderHighlightedOptionFunction : undefined} - renderInput={(params) => ( - 0 ? undefined : placeholder} - paginationIcon={paginationIcon} - paginationText={paginationText} - variant={variant} - limitResults={limitResults} - loadMoreOptions={loadMoreOptions} - setPage={setPage} - {...params} - inputRef={(elem) => { - if (innerRef) { - innerRef.current = elem as HTMLInputElement; - } - ref(elem); - }} - /> - )} - renderTags={createTagRenderer(fixedOptions, autocompleteProps)} - /> + {...props} id={id} name={props.name} /> ); }; From 0d752ec2b26b77e46ea07910c34383733e2f0cba Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Tue, 13 Jan 2026 16:20:57 +0100 Subject: [PATCH 09/13] try to memorize the form --- src/lib/Form.tsx | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/lib/Form.tsx b/src/lib/Form.tsx index 598f281..aaa55c3 100644 --- a/src/lib/Form.tsx +++ b/src/lib/Form.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import { ReactNode, useMemo } from "react"; import { DeepPartial, FieldValues, Resolver, SubmitHandler, useForm, UseFormReturn } from "react-hook-form"; import { jsonIsoDateReviver } from "./helpers/dateUtils"; import { FormContext, FormContextProps } from "./context/FormContext"; @@ -80,8 +80,41 @@ const Form = ({ const formMethods = useForm({ resolver, defaultValues: revivedDefaultValues }); const autoSubmitHandler = useAutoSubmit({ onSubmit, formMethods, autoSubmitConfig }); + // Memoize the object passed to function children to avoid creating new reference each render + const formPropsForChildren = useMemo( + () => ({ + ...formMethods, + disabled, + requiredFields, + hideValidationMessages, + disableAriaAutocomplete, + }), + [formMethods, disabled, requiredFields, hideValidationMessages, disableAriaAutocomplete], + ); + + // Memoize context value to prevent unnecessary re-renders of context consumers + const contextValue = useMemo( + () => ({ + requiredFields, + disabled, + hideValidationMessages, + disableAriaAutocomplete, + ...formMethods, + }), + [formMethods, requiredFields, disabled, hideValidationMessages, disableAriaAutocomplete], + ); + + // Only recompute children if the props object changes + const resolvedChildren = useMemo( + () => (typeof children === "function" ? children(formPropsForChildren) : children), + [children, formPropsForChildren], + ); + + console.log("Rendering Form", { contextValue }); + console.log("Rendering Form", { formPropsForChildren }); + return ( - +
{ if (formRef) { @@ -92,9 +125,7 @@ const Form = ({ method="POST" autoComplete={autoComplete} > - {children instanceof Function - ? children({ ...formMethods, disabled, requiredFields, hideValidationMessages, disableAriaAutocomplete }) - : children} + {resolvedChildren}
); From 1d590ea7fc910b50f2dc92951129921ba0086fd2 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Tue, 13 Jan 2026 17:48:12 +0100 Subject: [PATCH 10/13] ref --- src/lib/StaticTypeaheadInput.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index 218a283..c33eba8 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -109,6 +109,10 @@ const AutoComplete = (props: StaticTypeaheadInputProps } }, [options, page, limitResults]); + useEffect(() => { + console.log("Rendering StaticTypeaheadInput", { value }); + }, [value]); + return ( {...autocompleteProps} @@ -164,6 +168,7 @@ const AutoComplete = (props: StaticTypeaheadInputProps onInputChange(value, reason); } }} + ref={ref} renderOption={highlightOptions ? renderHighlightedOptionFunction : undefined} renderInput={(params) => ( Date: Tue, 13 Jan 2026 18:10:50 +0100 Subject: [PATCH 11/13] no use-controller --- src/lib/StaticTypeaheadInput.tsx | 217 +++++++++++++++---------------- 1 file changed, 104 insertions(+), 113 deletions(-) diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index c33eba8..a701565 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ import { useEffect, useMemo, useState } from "react"; -import { FieldValues, useController } from "react-hook-form"; +import { FieldValues, Controller } from "react-hook-form"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { CommonTypeaheadProps, StaticTypeaheadAutocompleteProps, TypeaheadOption, TypeaheadOptions } from "./types/Typeahead"; import { useFormContext } from "./context/FormContext"; @@ -29,7 +29,6 @@ interface StaticTypeaheadInputProps extends CommonTypeahe autocompleteProps?: StaticTypeaheadAutocompleteProps; } -// eslint-disable-next-line complexity const AutoComplete = (props: StaticTypeaheadInputProps) => { const { options, @@ -73,18 +72,7 @@ const AutoComplete = (props: StaticTypeaheadInputProps const [page, setPage] = useState(1); const [loadMoreOptions, setLoadMoreOptions] = useState(limitResults !== undefined && limitResults < options.length); - const { control, disabled: formDisabled, getFieldState, clearErrors } = useFormContext(); - const { - field: { ref, ...field }, - } = useController({ - name, - control, - rules: { - validate: { - required: () => getFieldState(name)?.error?.message, - }, - }, - }); + const { control, disabled: formDisabled, clearErrors } = useFormContext(); const isDisabled = useMemo(() => formDisabled || disabled, [formDisabled, disabled]); const paginatedOptions = useMemo( @@ -92,16 +80,10 @@ const AutoComplete = (props: StaticTypeaheadInputProps [limitResults, page, options], ); - const fieldValue = field.value as string | number | string[] | number[] | undefined; - const value = useMemo( - () => - multiple - ? getMultipleAutoCompleteValue(combineOptions(options, fixedOptions), fieldValue as string[] | number[] | undefined) - : getSingleAutoCompleteValue(options, fieldValue as string | number | undefined), - [fieldValue, multiple, options, fixedOptions], - ); - - validateFixedOptions(fixedOptions, multiple, autocompleteProps, withFixedOptionsInValue, value); + // Validate fixed options upfront + useEffect(() => { + validateFixedOptions(fixedOptions, multiple, autocompleteProps, withFixedOptionsInValue, []); + }, [fixedOptions, multiple, autocompleteProps, withFixedOptionsInValue]); useEffect(() => { if (limitResults !== undefined) { @@ -109,98 +91,107 @@ const AutoComplete = (props: StaticTypeaheadInputProps } }, [options, page, limitResults]); - useEffect(() => { - console.log("Rendering StaticTypeaheadInput", { value }); - }, [value]); - return ( - - {...autocompleteProps} - id={id} - multiple={multiple} - groupBy={useGroupBy ? groupOptions : undefined} - options={useGroupBy ? sortOptionsByGroup(paginatedOptions) : paginatedOptions} - isOptionEqualToValue={ - autocompleteProps?.isOptionEqualToValue ?? - ((option, value) => (typeof option === "string" ? option === value : option.value === (value as LabelValueOption).value)) - } - getOptionKey={ - autocompleteProps?.getOptionKey ?? - ((option: TypeaheadOption) => (typeof option === "string" ? option : `${option.label}-${option.value ?? ""}`)) - } - disableCloseOnSelect={multiple} - value={resolveInputValue(multiple, fixedOptions, withFixedOptionsInValue, value)} - getOptionLabel={(option: TypeaheadOption) => (typeof option === "string" ? option : option.label)} - getOptionDisabled={(option) => - getOptionDisabled?.(option) || - (useGroupBy && isDisabledGroup(option)) || - (typeof option === "string" ? false : (option.disabled ?? false)) - } - disabled={isDisabled} - readOnly={readOnly} - selectOnFocus={markAllOnFocus} - style={inputGroupStyle} - className={className} - autoSelect={autoSelect} - autoHighlight={autoHighlight} - onClose={readOnly ? undefined : onClose} - onOpen={readOnly ? undefined : onOpen} - onBlur={() => { - if (onBlur) { - onBlur(); - } - field.onBlur(); - }} - onChange={(_, value) => { - // value is typed as Autocomplete (aka TypeaheadOption) or an array of Autocomplete (aka TypeaheadOption[]) - // however, the component is not intended to be used with mixed types - const optionsArray = getOptionsFromValue(value, fixedOptions, withFixedOptionsInValue); - const values = convertAutoCompleteOptionsToStringArray(optionsArray); - const finalValue = multiple ? values : values[0]; - clearErrors(field.name); - if (onChange) { - onChange(finalValue); - } - field.onChange(finalValue); - }} - onInputChange={(_e, value, reason) => { - if (onInputChange) { - onInputChange(value, reason); - } + { + const fieldState = control.getFieldState(name); + return fieldState?.error?.message; + }, + }, }} - ref={ref} - renderOption={highlightOptions ? renderHighlightedOptionFunction : undefined} - renderInput={(params) => ( - 0 ? undefined : placeholder} - paginationIcon={paginationIcon} - paginationText={paginationText} - variant={variant} - limitResults={limitResults} - loadMoreOptions={loadMoreOptions} - setPage={setPage} - {...params} - inputRef={(elem) => { - if (innerRef) { - innerRef.current = elem as HTMLInputElement; + render={({ field }) => { + const fieldValue = field.value; + const value = multiple + ? getMultipleAutoCompleteValue(combineOptions(options, fixedOptions), fieldValue as string[] | number[] | undefined) + : getSingleAutoCompleteValue(options, fieldValue as string | number | undefined); + + return ( + + {...autocompleteProps} + {...field} + id={id} + multiple={multiple} + groupBy={useGroupBy ? groupOptions : undefined} + options={useGroupBy ? sortOptionsByGroup(paginatedOptions) : paginatedOptions} + isOptionEqualToValue={ + autocompleteProps?.isOptionEqualToValue ?? + ((option, value) => (typeof option === "string" ? option === value : option.value === (value as LabelValueOption).value)) } - ref(elem); - }} - /> - )} - renderTags={createTagRenderer(fixedOptions, autocompleteProps)} + getOptionKey={ + autocompleteProps?.getOptionKey ?? + ((option: TypeaheadOption) => (typeof option === "string" ? option : `${option.label}-${option.value ?? ""}`)) + } + disableCloseOnSelect={multiple} + value={resolveInputValue(multiple, fixedOptions, withFixedOptionsInValue, value)} + getOptionLabel={(option: TypeaheadOption) => (typeof option === "string" ? option : option.label)} + getOptionDisabled={(option) => + getOptionDisabled?.(option) || + (useGroupBy && isDisabledGroup(option)) || + (typeof option === "string" ? false : (option.disabled ?? false)) + } + disabled={isDisabled} + readOnly={readOnly} + selectOnFocus={markAllOnFocus} + style={inputGroupStyle} + className={className} + autoSelect={autoSelect} + autoHighlight={autoHighlight} + onClose={readOnly ? undefined : onClose} + onOpen={readOnly ? undefined : onOpen} + onBlur={() => { + if (onBlur) { + onBlur(); + } + field.onBlur(); + }} + onChange={(_, selectedValue) => { + const optionsArray = getOptionsFromValue(selectedValue, fixedOptions, withFixedOptionsInValue); + const values = convertAutoCompleteOptionsToStringArray(optionsArray); + const finalValue = multiple ? values : values[0]; + clearErrors(field.name); + if (onChange) { + onChange(finalValue); + } + field.onChange(finalValue); + }} + onInputChange={(_e, value, reason) => { + onInputChange?.(value, reason); + }} + renderOption={highlightOptions ? renderHighlightedOptionFunction : undefined} + renderInput={(params) => ( + 0 ? undefined : placeholder} + paginationIcon={paginationIcon} + paginationText={paginationText} + variant={variant} + limitResults={limitResults} + loadMoreOptions={loadMoreOptions} + setPage={setPage} + inputRef={(elem) => { + if (innerRef) innerRef.current = elem as HTMLInputElement; + field.ref(elem); + }} + /> + )} + renderTags={createTagRenderer(fixedOptions, autocompleteProps)} + /> + ); + }} /> ); }; From 8601bfa7f348860eb806180d642867f27a92ef14 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Tue, 13 Jan 2026 18:40:02 +0100 Subject: [PATCH 12/13] get rid of onChange --- src/lib/StaticTypeaheadInput.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index a701565..348dd66 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ import { useEffect, useMemo, useState } from "react"; -import { FieldValues, Controller } from "react-hook-form"; +import { FieldValues, Controller, Path, PathValue } from "react-hook-form"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { CommonTypeaheadProps, StaticTypeaheadAutocompleteProps, TypeaheadOption, TypeaheadOptions } from "./types/Typeahead"; import { useFormContext } from "./context/FormContext"; @@ -72,7 +72,7 @@ const AutoComplete = (props: StaticTypeaheadInputProps const [page, setPage] = useState(1); const [loadMoreOptions, setLoadMoreOptions] = useState(limitResults !== undefined && limitResults < options.length); - const { control, disabled: formDisabled, clearErrors } = useFormContext(); + const { control, disabled: formDisabled, clearErrors, setValue } = useFormContext(); const isDisabled = useMemo(() => formDisabled || disabled, [formDisabled, disabled]); const paginatedOptions = useMemo( @@ -156,7 +156,7 @@ const AutoComplete = (props: StaticTypeaheadInputProps if (onChange) { onChange(finalValue); } - field.onChange(finalValue); + setValue(field.name, finalValue as PathValue>, { shouldValidate: true }); }} onInputChange={(_e, value, reason) => { onInputChange?.(value, reason); From 0d67085d9998015f053cd4f32be334745b608755 Mon Sep 17 00:00:00 2001 From: Michele Masciave Date: Tue, 13 Jan 2026 18:58:55 +0100 Subject: [PATCH 13/13] try to remove on change to test --- src/lib/StaticTypeaheadInput.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/StaticTypeaheadInput.tsx b/src/lib/StaticTypeaheadInput.tsx index 348dd66..78c5f6b 100644 --- a/src/lib/StaticTypeaheadInput.tsx +++ b/src/lib/StaticTypeaheadInput.tsx @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ import { useEffect, useMemo, useState } from "react"; -import { FieldValues, Controller, Path, PathValue } from "react-hook-form"; +import { FieldValues, Controller } from "react-hook-form"; import { useSafeNameId } from "src/lib/hooks/useSafeNameId"; import { CommonTypeaheadProps, StaticTypeaheadAutocompleteProps, TypeaheadOption, TypeaheadOptions } from "./types/Typeahead"; import { useFormContext } from "./context/FormContext"; @@ -72,7 +72,7 @@ const AutoComplete = (props: StaticTypeaheadInputProps const [page, setPage] = useState(1); const [loadMoreOptions, setLoadMoreOptions] = useState(limitResults !== undefined && limitResults < options.length); - const { control, disabled: formDisabled, clearErrors, setValue } = useFormContext(); + const { control, disabled: formDisabled, clearErrors } = useFormContext(); const isDisabled = useMemo(() => formDisabled || disabled, [formDisabled, disabled]); const paginatedOptions = useMemo( @@ -156,7 +156,6 @@ const AutoComplete = (props: StaticTypeaheadInputProps if (onChange) { onChange(finalValue); } - setValue(field.name, finalValue as PathValue>, { shouldValidate: true }); }} onInputChange={(_e, value, reason) => { onInputChange?.(value, reason);