feat(form): Add controlled mode to Select component

Refactors the `Select` component to support being used as a controlled component by accepting a `value` prop. This allows its state to be managed externally, for example, by React Hook Form.

This change was necessary to properly implement the `clearable` functionality within forms. Previously, clearing the select would only update its internal state, not the form's state.

- Added `value` and `useEffect` to sync with external state.
- Updated the `onClear` handler to propagate the change via `onChange`.
- Modified `AdminListFilters` to use the `Select` component in a controlled manner, passing `watch` and `setValue` from React Hook Form.
- Adjusted the filter layout to be more responsive.
This commit is contained in:
AmirReza Jamali
2025-10-13 15:17:18 +03:30
parent 19a3f72325
commit 0084b131f8
4 changed files with 47 additions and 12 deletions
+20 -3
View File
@@ -3,7 +3,7 @@
import { ChevronUpIcon } from "@/assets/icons";
import { cn } from "@/lib/utils";
import { ILabelValue } from "@/types/shared";
import { useId, useState, type ChangeEvent } from "react";
import { useEffect, useId, useState, type ChangeEvent } from "react";
import {
type FieldErrors,
type FieldValues,
@@ -24,8 +24,9 @@ type PropsType<T extends FieldValues> = {
required?: boolean;
clearable?: boolean;
onClear?: () => void;
value?: string;
} & (
| { placeholder?: string; defaultValue: string }
| { placeholder?: string; defaultValue?: string }
| { placeholder: string; defaultValue?: string }
) &
Partial<UseFormRegisterReturn>;
@@ -46,6 +47,7 @@ export function Select<T extends FieldValues>({
ref,
clearable = false,
onClear,
value: controlledValue,
...props
}: PropsType<T>) {
const id = useId();
@@ -54,10 +56,23 @@ export function Select<T extends FieldValues>({
const [value, setValue] = useState(defaultValue || "");
const error = errors && errors[name];
useEffect(() => {
if (controlledValue !== undefined) {
setValue(controlledValue);
setIsOptionSelected(!!controlledValue);
}
}, [controlledValue]);
const handleClear = () => {
setValue("");
setIsOptionSelected(false);
onClear?.();
if (onChange) {
const event = {
target: { value: "" },
} as ChangeEvent<HTMLSelectElement>;
onChange(event);
}
};
return (
@@ -83,7 +98,9 @@ export function Select<T extends FieldValues>({
ref={ref}
value={value}
onChange={(e) => {
setValue(e.target.value);
if (controlledValue === undefined) {
setValue(e.target.value);
}
setIsOptionSelected(!!e.target.value);
onChange?.(e);
}}