diff --git a/js/apps/admin-ui/src/components/client/ClientSelect.tsx b/js/apps/admin-ui/src/components/client/ClientSelect.tsx index 96dca4c6f9..56ced6bd63 100644 --- a/js/apps/admin-ui/src/components/client/ClientSelect.tsx +++ b/js/apps/admin-ui/src/components/client/ClientSelect.tsx @@ -1,15 +1,9 @@ import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation"; import type { ClientQuery } from "@keycloak/keycloak-admin-client/lib/resources/clients"; -import { - FormGroup, - Select, - SelectOption, - SelectVariant, -} from "@patternfly/react-core"; +import { SelectVariant } from "@patternfly/react-core"; import { useState } from "react"; -import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { HelpItem } from "ui-shared"; +import { SelectControl } from "ui-shared"; import { adminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; @@ -26,12 +20,7 @@ export const ClientSelect = ({ required = false, }: ClientSelectProps) => { const { t } = useTranslation(); - const { - control, - formState: { errors }, - } = useFormContext(); - const [open, setOpen] = useState(false); const [clients, setClients] = useState([]); const [search, setSearch] = useState(""); @@ -50,51 +39,27 @@ export const ClientSelect = ({ [search], ); - const convert = (clients: ClientRepresentation[]) => [ - - {t("none")} - , - ...clients.map((option) => ( - - )), - ]; - return ( - } - fieldId={name!} - validated={errors[name!] ? "error" : "default"} - helperTextInvalid={t("required")} - > - ( - - )} - /> - + labelIcon={t(helpText!)} + controller={{ + defaultValue: defaultValue || "", + rules: { + required: { + value: required, + message: t("required"), + }, + }, + }} + onFilter={(value) => setSearch(value)} + variant={SelectVariant.typeahead} + isDisabled={isDisabled} + options={[ + { key: "", value: t("none") }, + ...clients.map(({ id, clientId }) => ({ key: id!, value: clientId! })), + ]} + /> ); }; diff --git a/js/libs/ui-shared/src/controls/SelectControl.tsx b/js/libs/ui-shared/src/controls/SelectControl.tsx index 6d0671a615..01d9697084 100644 --- a/js/libs/ui-shared/src/controls/SelectControl.tsx +++ b/js/libs/ui-shared/src/controls/SelectControl.tsx @@ -26,7 +26,13 @@ export type SelectControlProps< P extends FieldPath = FieldPath, > = Omit< SelectProps, - "name" | "onToggle" | "selections" | "onSelect" | "onClear" | "isOpen" + | "name" + | "onToggle" + | "selections" + | "onSelect" + | "onClear" + | "isOpen" + | "onFilter" > & UseControllerProps & { name: string; @@ -34,6 +40,7 @@ export type SelectControlProps< options: string[] | SelectControlOption[]; labelIcon?: string; controller: Omit; + onFilter?: (value: string) => void; }; export const SelectControl = < @@ -46,6 +53,7 @@ export const SelectControl = < controller, variant = SelectVariant.single, labelIcon, + onFilter, ...rest }: SelectControlProps) => { const { @@ -53,6 +61,15 @@ export const SelectControl = < formState: { errors }, } = useFormContext(); const [open, setOpen] = useState(false); + const convert = () => + options.map((option) => ( + + {typeof option === "string" ? option : option.value} + + )); return ( { + onFilter?.(value); + return convert(); + }} isOpen={open} variant={variant} validated={ errors[name] ? ValidatedOptions.error : ValidatedOptions.default } > - {options.map((option) => ( - - {typeof option === "string" ? option : option.value} - - ))} + {convert()} )} />