changed to use ui-shared (#27933)
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
parent
5a99c558dc
commit
53d52ecf15
6 changed files with 114 additions and 289 deletions
|
@ -29,6 +29,7 @@ export default class PoliciesTab extends CommonPage {
|
||||||
inputClient(clientName: string) {
|
inputClient(clientName: string) {
|
||||||
cy.get("#clients").click();
|
cy.get("#clients").click();
|
||||||
cy.get("ul li").contains(clientName).click();
|
cy.get("ul li").contains(clientName).click();
|
||||||
|
cy.get("#clients").click();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,11 @@ import {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
DropdownToggle,
|
DropdownToggle,
|
||||||
Form,
|
Form,
|
||||||
FormGroup,
|
|
||||||
Select,
|
|
||||||
SelectOption,
|
|
||||||
SelectVariant,
|
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { SelectControl, TextControl } from "ui-shared";
|
||||||
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
|
|
||||||
import useToggle from "../../utils/useToggle";
|
import useToggle from "../../utils/useToggle";
|
||||||
|
|
||||||
import "./search-dropdown.css";
|
import "./search-dropdown.css";
|
||||||
|
@ -42,16 +37,14 @@ export const SearchDropdown = ({
|
||||||
type,
|
type,
|
||||||
}: SearchDropdownProps) => {
|
}: SearchDropdownProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const form = useForm<SearchForm>({ mode: "onChange" });
|
||||||
const {
|
const {
|
||||||
register,
|
|
||||||
control,
|
|
||||||
reset,
|
reset,
|
||||||
formState: { isDirty },
|
formState: { isDirty },
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
} = useForm<SearchForm>({ mode: "onChange" });
|
} = form;
|
||||||
|
|
||||||
const [open, toggle] = useToggle();
|
const [open, toggle] = useToggle();
|
||||||
const [typeOpen, toggleType] = useToggle();
|
|
||||||
|
|
||||||
const submit = (form: SearchForm) => {
|
const submit = (form: SearchForm) => {
|
||||||
toggle();
|
toggle();
|
||||||
|
@ -60,21 +53,6 @@ export const SearchDropdown = ({
|
||||||
|
|
||||||
useEffect(() => reset(search), [search]);
|
useEffect(() => reset(search), [search]);
|
||||||
|
|
||||||
const typeOptions = (value?: string) => [
|
|
||||||
<SelectOption key="empty" value="">
|
|
||||||
{t("allTypes")}
|
|
||||||
</SelectOption>,
|
|
||||||
...(types || []).map((type) => (
|
|
||||||
<SelectOption
|
|
||||||
selected={type.type === value}
|
|
||||||
key={type.type}
|
|
||||||
value={type.type}
|
|
||||||
>
|
|
||||||
{type.name}
|
|
||||||
</SelectOption>
|
|
||||||
)),
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
data-testid="searchdropdown_dorpdown"
|
data-testid="searchdropdown_dorpdown"
|
||||||
|
@ -91,86 +69,39 @@ export const SearchDropdown = ({
|
||||||
}
|
}
|
||||||
isOpen={open}
|
isOpen={open}
|
||||||
>
|
>
|
||||||
|
<FormProvider {...form}>
|
||||||
<Form
|
<Form
|
||||||
isHorizontal
|
isHorizontal
|
||||||
className="keycloak__client_authentication__searchdropdown_form"
|
className="keycloak__client_authentication__searchdropdown_form"
|
||||||
onSubmit={handleSubmit(submit)}
|
onSubmit={handleSubmit(submit)}
|
||||||
>
|
>
|
||||||
<FormGroup label={t("name")} fieldId="name">
|
<TextControl name="name" label={t("name")} />
|
||||||
<KeycloakTextInput
|
|
||||||
id="name"
|
|
||||||
data-testid="searchdropdown_name"
|
|
||||||
{...register("name")}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
{type === "resource" && (
|
{type === "resource" && (
|
||||||
<>
|
<>
|
||||||
<FormGroup label={t("type")} fieldId="type">
|
<TextControl name="type" label={t("type")} />
|
||||||
<KeycloakTextInput
|
<TextControl name="uris" label={t("uris")} />
|
||||||
id="type"
|
<TextControl name="owner" label={t("owner")} />
|
||||||
data-testid="searchdropdown_type"
|
|
||||||
{...register("type")}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup label={t("uris")} fieldId="uri">
|
|
||||||
<KeycloakTextInput
|
|
||||||
id="uri"
|
|
||||||
data-testid="searchdropdown_uri"
|
|
||||||
{...register("uri")}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup label={t("owner")} fieldId="owner">
|
|
||||||
<KeycloakTextInput
|
|
||||||
id="owner"
|
|
||||||
data-testid="searchdropdown_owner"
|
|
||||||
{...register("owner")}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{type !== "resource" && type !== "policy" && (
|
{type !== "resource" && type !== "policy" && (
|
||||||
<FormGroup label={t("resource")} fieldId="resource">
|
<TextControl name="resource" label={t("resource")} />
|
||||||
<KeycloakTextInput
|
|
||||||
id="resource"
|
|
||||||
data-testid="searchdropdown_resource"
|
|
||||||
{...register("resource")}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
{type !== "policy" && (
|
|
||||||
<FormGroup label={t("scope")} fieldId="scope">
|
|
||||||
<KeycloakTextInput
|
|
||||||
id="scope"
|
|
||||||
data-testid="searchdropdown_scope"
|
|
||||||
{...register("scope")}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
)}
|
||||||
|
{type !== "policy" && <TextControl name="scope" label={t("scope")} />}
|
||||||
{type !== "resource" && (
|
{type !== "resource" && (
|
||||||
<FormGroup label={t("type")} fieldId="type">
|
<SelectControl
|
||||||
<Controller
|
|
||||||
name="type"
|
name="type"
|
||||||
defaultValue=""
|
label={t("type")}
|
||||||
control={control}
|
controller={{
|
||||||
render={({ field }) => (
|
defaultValue: "",
|
||||||
<Select
|
|
||||||
toggleId="type"
|
|
||||||
onToggle={toggleType}
|
|
||||||
onSelect={(event, value) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
field.onChange(value);
|
|
||||||
toggleType();
|
|
||||||
}}
|
}}
|
||||||
selections={field.value || t("allTypes")}
|
options={[
|
||||||
variant={SelectVariant.single}
|
{ key: "", value: t("allTypes") },
|
||||||
aria-label={t("type")}
|
...(types || []).map(({ type, name }) => ({
|
||||||
isOpen={typeOpen}
|
key: type!,
|
||||||
>
|
value: name!,
|
||||||
{typeOptions(field.value)}
|
})),
|
||||||
</Select>
|
]}
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
|
||||||
)}
|
)}
|
||||||
<ActionGroup>
|
<ActionGroup>
|
||||||
<Button
|
<Button
|
||||||
|
@ -190,6 +121,7 @@ export const SearchDropdown = ({
|
||||||
</Button>
|
</Button>
|
||||||
</ActionGroup>
|
</ActionGroup>
|
||||||
</Form>
|
</Form>
|
||||||
|
</FormProvider>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type ResourceServerRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceServerRepresentation";
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
Button,
|
Button,
|
||||||
|
@ -5,24 +6,22 @@ import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
PageSection,
|
PageSection,
|
||||||
Radio,
|
Radio,
|
||||||
Switch,
|
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { HelpItem } from "ui-shared";
|
import { HelpItem } from "ui-shared";
|
||||||
|
|
||||||
import { adminClient } from "../../admin-client";
|
import { adminClient } from "../../admin-client";
|
||||||
import type ResourceServerRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceServerRepresentation";
|
import { DefaultSwitchControl } from "../../components/SwitchControl";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { FixedButtonsGroup } from "../../components/form/FixedButtonGroup";
|
import { FixedButtonsGroup } from "../../components/form/FixedButtonGroup";
|
||||||
import { FormAccess } from "../../components/form/FormAccess";
|
import { FormAccess } from "../../components/form/FormAccess";
|
||||||
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
|
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
|
||||||
|
import { useAccess } from "../../context/access/Access";
|
||||||
|
import { useFetch } from "../../utils/useFetch";
|
||||||
import useToggle from "../../utils/useToggle";
|
import useToggle from "../../utils/useToggle";
|
||||||
import { DecisionStrategySelect } from "./DecisionStrategySelect";
|
import { DecisionStrategySelect } from "./DecisionStrategySelect";
|
||||||
import { ImportDialog } from "./ImportDialog";
|
import { ImportDialog } from "./ImportDialog";
|
||||||
import { useFetch } from "../../utils/useFetch";
|
|
||||||
import { useAccess } from "../../context/access/Access";
|
|
||||||
|
|
||||||
const POLICY_ENFORCEMENT_MODES = [
|
const POLICY_ENFORCEMENT_MODES = [
|
||||||
"ENFORCING",
|
"ENFORCING",
|
||||||
|
@ -145,35 +144,12 @@ export const AuthorizationSettings = ({ clientId }: { clientId: string }) => {
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<DecisionStrategySelect isLimited />
|
<DecisionStrategySelect isLimited />
|
||||||
</FormProvider>
|
<DefaultSwitchControl
|
||||||
<FormGroup
|
|
||||||
hasNoPaddingTop
|
|
||||||
label={t("allowRemoteResourceManagement")}
|
|
||||||
fieldId="allowRemoteResourceManagement"
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText={t("allowRemoteResourceManagementHelp")}
|
|
||||||
fieldLabelId="allowRemoteResourceManagement"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name="allowRemoteResourceManagement"
|
name="allowRemoteResourceManagement"
|
||||||
data-testid="allowRemoteResourceManagement"
|
label={t("allowRemoteResourceManagement")}
|
||||||
defaultValue={false}
|
labelIcon={t("allowRemoteResourceManagementHelp")}
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Switch
|
|
||||||
id="allowRemoteResourceManagement"
|
|
||||||
label={t("on")}
|
|
||||||
labelOff={t("off")}
|
|
||||||
isChecked={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
aria-label={t("allowRemoteResourceManagement")}
|
|
||||||
/>
|
/>
|
||||||
)}
|
</FormProvider>
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FixedButtonsGroup
|
<FixedButtonsGroup
|
||||||
name="authenticationSettings"
|
name="authenticationSettings"
|
||||||
reset={() => reset(resource)}
|
reset={() => reset(resource)}
|
||||||
|
|
|
@ -1,114 +1,18 @@
|
||||||
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
|
import { SelectVariant } from "@patternfly/react-core";
|
||||||
import type { ClientQuery } from "@keycloak/keycloak-admin-client/lib/resources/clients";
|
|
||||||
import {
|
|
||||||
FormGroup,
|
|
||||||
Select,
|
|
||||||
SelectOption,
|
|
||||||
SelectVariant,
|
|
||||||
} from "@patternfly/react-core";
|
|
||||||
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 { ClientSelect } from "../../../components/client/ClientSelect";
|
||||||
|
|
||||||
import { adminClient } from "../../../admin-client";
|
|
||||||
import { useFetch } from "../../../utils/useFetch";
|
|
||||||
|
|
||||||
export const Client = () => {
|
export const Client = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const {
|
|
||||||
control,
|
|
||||||
getValues,
|
|
||||||
formState: { errors },
|
|
||||||
} = useFormContext();
|
|
||||||
const values: string[] | undefined = getValues("clients");
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [clients, setClients] = useState<ClientRepresentation[]>([]);
|
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
|
|
||||||
useFetch(
|
|
||||||
async () => {
|
|
||||||
const params: ClientQuery = {
|
|
||||||
max: 20,
|
|
||||||
};
|
|
||||||
if (search) {
|
|
||||||
params.clientId = search;
|
|
||||||
params.search = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values?.length && !search) {
|
|
||||||
return await Promise.all(
|
|
||||||
values.map(
|
|
||||||
(id: string) =>
|
|
||||||
adminClient.clients.findOne({ id }) as ClientRepresentation,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return await adminClient.clients.find(params);
|
|
||||||
},
|
|
||||||
setClients,
|
|
||||||
[search],
|
|
||||||
);
|
|
||||||
|
|
||||||
const convert = (clients: ClientRepresentation[]) =>
|
|
||||||
clients.map((option) => (
|
|
||||||
<SelectOption
|
|
||||||
key={option.id!}
|
|
||||||
value={option.id}
|
|
||||||
selected={values?.includes(option.id!)}
|
|
||||||
>
|
|
||||||
{option.clientId}
|
|
||||||
</SelectOption>
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<ClientSelect
|
||||||
label={t("clients")}
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem helpText={t("policyClientHelp")} fieldLabelId="client" />
|
|
||||||
}
|
|
||||||
fieldId="clients"
|
|
||||||
helperTextInvalid={t("requiredClient")}
|
|
||||||
validated={errors.clients ? "error" : "default"}
|
|
||||||
isRequired
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name="clients"
|
name="clients"
|
||||||
|
label={t("clients")}
|
||||||
|
helpText={t("policyClientHelp")}
|
||||||
|
required
|
||||||
defaultValue={[]}
|
defaultValue={[]}
|
||||||
control={control}
|
|
||||||
rules={{
|
|
||||||
validate: (value) => value.length > 0,
|
|
||||||
}}
|
|
||||||
render={({ field }) => (
|
|
||||||
<Select
|
|
||||||
toggleId="clients"
|
|
||||||
variant={SelectVariant.typeaheadMulti}
|
variant={SelectVariant.typeaheadMulti}
|
||||||
typeAheadAriaLabel={t("clients")}
|
|
||||||
onToggle={(open) => setOpen(open)}
|
|
||||||
isOpen={open}
|
|
||||||
selections={field.value}
|
|
||||||
aria-label={t("selectClients")}
|
|
||||||
onFilter={(_, value) => {
|
|
||||||
setSearch(value);
|
|
||||||
return convert(clients);
|
|
||||||
}}
|
|
||||||
onSelect={(_, v) => {
|
|
||||||
const option = v.toString();
|
|
||||||
if (field.value.includes(option)) {
|
|
||||||
field.onChange(
|
|
||||||
field.value.filter((item: string) => item !== option),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
field.onChange([...field.value, option]);
|
|
||||||
}
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{convert(clients)}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
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 { SelectVariant } from "@patternfly/react-core";
|
import { SelectProps, SelectVariant } from "@patternfly/react-core";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { SelectControl } from "ui-shared";
|
import { SelectControl } from "ui-shared";
|
||||||
|
@ -9,7 +9,7 @@ import { adminClient } from "../../admin-client";
|
||||||
import { useFetch } from "../../utils/useFetch";
|
import { useFetch } from "../../utils/useFetch";
|
||||||
import type { ComponentProps } from "../dynamic/components";
|
import type { ComponentProps } from "../dynamic/components";
|
||||||
|
|
||||||
type ClientSelectProps = ComponentProps & {};
|
type ClientSelectProps = ComponentProps & Pick<SelectProps, "variant">;
|
||||||
|
|
||||||
export const ClientSelect = ({
|
export const ClientSelect = ({
|
||||||
name,
|
name,
|
||||||
|
@ -18,6 +18,7 @@ export const ClientSelect = ({
|
||||||
defaultValue,
|
defaultValue,
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
required = false,
|
required = false,
|
||||||
|
variant = SelectVariant.typeahead,
|
||||||
}: ClientSelectProps) => {
|
}: ClientSelectProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
@ -54,12 +55,12 @@ export const ClientSelect = ({
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onFilter={(value) => setSearch(value)}
|
onFilter={(value) => setSearch(value)}
|
||||||
variant={SelectVariant.typeahead}
|
variant={variant}
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
options={[
|
options={clients.map(({ id, clientId }) => ({
|
||||||
{ key: "", value: t("none") },
|
key: id!,
|
||||||
...clients.map(({ id, clientId }) => ({ key: id!, value: clientId! })),
|
value: clientId!,
|
||||||
]}
|
}))}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
import { useState } from "react";
|
|
||||||
import {
|
|
||||||
Controller,
|
|
||||||
ControllerProps,
|
|
||||||
FieldValues,
|
|
||||||
FieldPath,
|
|
||||||
useFormContext,
|
|
||||||
UseControllerProps,
|
|
||||||
} from "react-hook-form";
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectOption,
|
SelectOption,
|
||||||
|
@ -14,6 +5,15 @@ import {
|
||||||
SelectVariant,
|
SelectVariant,
|
||||||
ValidatedOptions,
|
ValidatedOptions,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
ControllerProps,
|
||||||
|
FieldPath,
|
||||||
|
FieldValues,
|
||||||
|
UseControllerProps,
|
||||||
|
useFormContext,
|
||||||
|
} from "react-hook-form";
|
||||||
import { FormLabel } from "./FormLabel";
|
import { FormLabel } from "./FormLabel";
|
||||||
|
|
||||||
export type SelectControlOption = {
|
export type SelectControlOption = {
|
||||||
|
@ -21,6 +21,8 @@ export type SelectControlOption = {
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type OptionType = string[] | SelectControlOption[];
|
||||||
|
|
||||||
export type SelectControlProps<
|
export type SelectControlProps<
|
||||||
T extends FieldValues,
|
T extends FieldValues,
|
||||||
P extends FieldPath<T> = FieldPath<T>,
|
P extends FieldPath<T> = FieldPath<T>,
|
||||||
|
@ -37,12 +39,17 @@ export type SelectControlProps<
|
||||||
UseControllerProps<T, P> & {
|
UseControllerProps<T, P> & {
|
||||||
name: string;
|
name: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
options: string[] | SelectControlOption[];
|
options: OptionType;
|
||||||
labelIcon?: string;
|
labelIcon?: string;
|
||||||
controller: Omit<ControllerProps, "name" | "render">;
|
controller: Omit<ControllerProps, "name" | "render">;
|
||||||
onFilter?: (value: string) => void;
|
onFilter?: (value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isString = (option: SelectControlOption | string): option is string =>
|
||||||
|
typeof option === "string";
|
||||||
|
const key = (option: SelectControlOption | string) =>
|
||||||
|
isString(option) ? option : option.key;
|
||||||
|
|
||||||
export const SelectControl = <
|
export const SelectControl = <
|
||||||
T extends FieldValues,
|
T extends FieldValues,
|
||||||
P extends FieldPath<T> = FieldPath<T>,
|
P extends FieldPath<T> = FieldPath<T>,
|
||||||
|
@ -68,19 +75,19 @@ export const SelectControl = <
|
||||||
option.toString().toLowerCase().startsWith(lowercasePrefix),
|
option.toString().toLowerCase().startsWith(lowercasePrefix),
|
||||||
)
|
)
|
||||||
.map((option) => (
|
.map((option) => (
|
||||||
<SelectOption
|
<SelectOption key={key(option)} value={key(option)}>
|
||||||
key={typeof option === "string" ? option : option.key}
|
{isString(option) ? option : option.value}
|
||||||
value={typeof option === "string" ? option : option.key}
|
|
||||||
>
|
|
||||||
{typeof option === "string" ? option : option.value}
|
|
||||||
</SelectOption>
|
</SelectOption>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
const isSelectBasedOptions = (
|
||||||
|
options: OptionType,
|
||||||
|
): options is SelectControlOption[] => typeof options[0] !== "string";
|
||||||
return (
|
return (
|
||||||
<FormLabel
|
<FormLabel
|
||||||
name={name}
|
name={name}
|
||||||
label={label}
|
label={label}
|
||||||
isRequired={controller.rules?.required === true}
|
isRequired={!!controller.rules?.required}
|
||||||
error={errors[name]}
|
error={errors[name]}
|
||||||
labelIcon={labelIcon}
|
labelIcon={labelIcon}
|
||||||
>
|
>
|
||||||
|
@ -94,8 +101,8 @@ export const SelectControl = <
|
||||||
toggleId={name.slice(name.lastIndexOf(".") + 1)}
|
toggleId={name.slice(name.lastIndexOf(".") + 1)}
|
||||||
onToggle={(isOpen) => setOpen(isOpen)}
|
onToggle={(isOpen) => setOpen(isOpen)}
|
||||||
selections={
|
selections={
|
||||||
typeof options[0] !== "string"
|
isSelectBasedOptions(options)
|
||||||
? (options as SelectControlOption[])
|
? options
|
||||||
.filter((o) =>
|
.filter((o) =>
|
||||||
Array.isArray(value)
|
Array.isArray(value)
|
||||||
? value.includes(o.key)
|
? value.includes(o.key)
|
||||||
|
@ -104,11 +111,15 @@ export const SelectControl = <
|
||||||
.map((o) => o.value)
|
.map((o) => o.value)
|
||||||
: value
|
: value
|
||||||
}
|
}
|
||||||
onSelect={(_, v) => {
|
onSelect={(event, v) => {
|
||||||
if (variant === "typeaheadmulti") {
|
event.stopPropagation();
|
||||||
|
if (Array.isArray(value)) {
|
||||||
const option = v.toString();
|
const option = v.toString();
|
||||||
if (value.includes(option)) {
|
const key = isSelectBasedOptions(options)
|
||||||
onChange(value.filter((item: string) => item !== option));
|
? options.find((o) => o.value === option)?.key
|
||||||
|
: option;
|
||||||
|
if (value.includes(key)) {
|
||||||
|
onChange(value.filter((item: string) => item !== key));
|
||||||
} else {
|
} else {
|
||||||
onChange([...value, option]);
|
onChange([...value, option]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue