({
value,
optionConfig,
disabled,
placeholder,
onChange,
}: {
value: string;
optionConfig: OptionConfig;
disabled?: boolean;
placeholder?: string;
onChange: (value: string) => void;
})
| 257 | } |
| 258 | |
| 259 | function SearchableSelect({ |
| 260 | value, |
| 261 | optionConfig, |
| 262 | disabled, |
| 263 | placeholder, |
| 264 | onChange, |
| 265 | }: { |
| 266 | value: string; |
| 267 | optionConfig: OptionConfig; |
| 268 | disabled?: boolean; |
| 269 | placeholder?: string; |
| 270 | onChange: (value: string) => void; |
| 271 | }) { |
| 272 | const { t } = useTranslation(); |
| 273 | const [search, setSearch] = useState(""); |
| 274 | const [open, setOpen] = useState(false); |
| 275 | const [options, setOptions] = useState<SearchableSelectOption[]>([]); |
| 276 | const [loading, setLoading] = useState(false); |
| 277 | const triggerRef = useRef<HTMLButtonElement>(null); |
| 278 | const dropdownRef = useRef<HTMLDivElement>(null); |
| 279 | const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
| 280 | |
| 281 | const selectedLabel = useMemo(() => { |
| 282 | const found = options.find((o) => o.value === value); |
| 283 | if (found) return found.label; |
| 284 | if (optionConfig.fallback) |
| 285 | return optionConfig.fallback(value)?.label ?? value; |
| 286 | return value || ""; |
| 287 | }, [options, value, optionConfig]); |
| 288 | |
| 289 | const doSearch = useCallback( |
| 290 | async (q: string) => { |
| 291 | if (!optionConfig.search) return; |
| 292 | setLoading(true); |
| 293 | try { |
| 294 | const resp = await optionConfig.search({ |
| 295 | search: q, |
| 296 | pageToken: "", |
| 297 | pageSize: getDefaultPagination(), |
| 298 | }); |
| 299 | setOptions(resp.options as SearchableSelectOption[]); |
| 300 | } finally { |
| 301 | setLoading(false); |
| 302 | } |
| 303 | }, |
| 304 | [optionConfig] |
| 305 | ); |
| 306 | |
| 307 | const initializedRef = useRef(false); |
| 308 | useEffect(() => { |
| 309 | if (initializedRef.current) return; |
| 310 | initializedRef.current = true; |
| 311 | if (!value) return; |
| 312 | if (optionConfig.fetch) { |
| 313 | optionConfig.fetch([value]).then((opts) => { |
| 314 | setOptions(opts as SearchableSelectOption[]); |
| 315 | }); |
| 316 | } else if (optionConfig.search) { |
nothing calls this directly
no test coverage detected