converted client select to ui-shared (#27799)

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-03-14 13:41:36 +01:00 committed by GitHub
parent 42244d2a67
commit 63bd46b8cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 66 deletions

View file

@ -1,15 +1,9 @@
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation"; import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import type { ClientQuery } from "@keycloak/keycloak-admin-client/lib/resources/clients"; import type { ClientQuery } from "@keycloak/keycloak-admin-client/lib/resources/clients";
import { import { SelectVariant } from "@patternfly/react-core";
FormGroup,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { useState } from "react"; import { useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem } from "ui-shared"; import { SelectControl } from "ui-shared";
import { adminClient } from "../../admin-client"; import { adminClient } from "../../admin-client";
import { useFetch } from "../../utils/useFetch"; import { useFetch } from "../../utils/useFetch";
@ -26,12 +20,7 @@ export const ClientSelect = ({
required = false, required = false,
}: ClientSelectProps) => { }: ClientSelectProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const {
control,
formState: { errors },
} = useFormContext();
const [open, setOpen] = useState(false);
const [clients, setClients] = useState<ClientRepresentation[]>([]); const [clients, setClients] = useState<ClientRepresentation[]>([]);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
@ -50,51 +39,27 @@ export const ClientSelect = ({
[search], [search],
); );
const convert = (clients: ClientRepresentation[]) => [
<SelectOption key="empty" value="">
{t("none")}
</SelectOption>,
...clients.map((option) => (
<SelectOption key={option.id} value={option.clientId} />
)),
];
return ( return (
<FormGroup <SelectControl
name={name!}
label={t(label!)} label={t(label!)}
isRequired={required} labelIcon={t(helpText!)}
labelIcon={<HelpItem helpText={t(helpText!)} fieldLabelId={label!} />} controller={{
fieldId={name!} defaultValue: defaultValue || "",
validated={errors[name!] ? "error" : "default"} rules: {
helperTextInvalid={t("required")} required: {
> value: required,
<Controller message: t("required"),
name={name!} },
defaultValue={defaultValue || ""} },
control={control} }}
rules={required ? { required: true } : {}} onFilter={(value) => setSearch(value)}
render={({ field }) => ( variant={SelectVariant.typeahead}
<Select isDisabled={isDisabled}
toggleId={name} options={[
variant={SelectVariant.typeahead} { key: "", value: t("none") },
onToggle={(open) => setOpen(open)} ...clients.map(({ id, clientId }) => ({ key: id!, value: clientId! })),
isOpen={open} ]}
isDisabled={isDisabled} />
selections={field.value}
onFilter={(_, value) => {
setSearch(value);
return convert(clients);
}}
onSelect={(_, value) => {
field.onChange(value.toString());
setOpen(false);
}}
typeAheadAriaLabel={t(label!)}
>
{convert(clients)}
</Select>
)}
/>
</FormGroup>
); );
}; };

View file

@ -26,7 +26,13 @@ export type SelectControlProps<
P extends FieldPath<T> = FieldPath<T>, P extends FieldPath<T> = FieldPath<T>,
> = Omit< > = Omit<
SelectProps, SelectProps,
"name" | "onToggle" | "selections" | "onSelect" | "onClear" | "isOpen" | "name"
| "onToggle"
| "selections"
| "onSelect"
| "onClear"
| "isOpen"
| "onFilter"
> & > &
UseControllerProps<T, P> & { UseControllerProps<T, P> & {
name: string; name: string;
@ -34,6 +40,7 @@ export type SelectControlProps<
options: string[] | SelectControlOption[]; options: string[] | SelectControlOption[];
labelIcon?: string; labelIcon?: string;
controller: Omit<ControllerProps, "name" | "render">; controller: Omit<ControllerProps, "name" | "render">;
onFilter?: (value: string) => void;
}; };
export const SelectControl = < export const SelectControl = <
@ -46,6 +53,7 @@ export const SelectControl = <
controller, controller,
variant = SelectVariant.single, variant = SelectVariant.single,
labelIcon, labelIcon,
onFilter,
...rest ...rest
}: SelectControlProps<T, P>) => { }: SelectControlProps<T, P>) => {
const { const {
@ -53,6 +61,15 @@ export const SelectControl = <
formState: { errors }, formState: { errors },
} = useFormContext(); } = useFormContext();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const convert = () =>
options.map((option) => (
<SelectOption
key={typeof option === "string" ? option : option.key}
value={typeof option === "string" ? option : option.key}
>
{typeof option === "string" ? option : option.value}
</SelectOption>
));
return ( return (
<FormLabel <FormLabel
name={name} name={name}
@ -98,20 +115,17 @@ export const SelectControl = <
} }
: undefined : undefined
} }
onFilter={(_, value) => {
onFilter?.(value);
return convert();
}}
isOpen={open} isOpen={open}
variant={variant} variant={variant}
validated={ validated={
errors[name] ? ValidatedOptions.error : ValidatedOptions.default errors[name] ? ValidatedOptions.error : ValidatedOptions.default
} }
> >
{options.map((option) => ( {convert()}
<SelectOption
key={typeof option === "string" ? option : option.key}
value={typeof option === "string" ? option : option.key}
>
{typeof option === "string" ? option : option.value}
</SelectOption>
))}
</Select> </Select>
)} )}
/> />