Use user select component (#4011)

This commit is contained in:
Erik Jan de Wit 2022-12-13 06:40:49 -05:00 committed by GitHub
parent 91acceacd0
commit be59ed1447
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 78 deletions

View file

@ -1,3 +1,7 @@
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
import type ProtocolMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/protocolMapperRepresentation";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import type { ProtocolMapperTypeRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/serverInfoRepesentation";
import { import {
ClipboardCopy, ClipboardCopy,
Form, Form,
@ -18,16 +22,14 @@ import {
TextContent, TextContent,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { QuestionCircleIcon } from "@patternfly/react-icons"; import { QuestionCircleIcon } from "@patternfly/react-icons";
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
import type ProtocolMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/protocolMapperRepresentation";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import type { ProtocolMapperTypeRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/serverInfoRepesentation";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useHelp } from "../../components/help-enabler/HelpHeader"; import { useHelp } from "../../components/help-enabler/HelpHeader";
import { HelpItem } from "../../components/help-enabler/HelpItem"; import { HelpItem } from "../../components/help-enabler/HelpItem";
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable"; import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
import { UserSelect } from "../../components/users/UserSelect";
import { useAdminClient, useFetch } from "../../context/auth/AdminClient"; import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext"; import { useRealm } from "../../context/realm-context/RealmContext";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider"; import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
@ -120,14 +122,9 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
ClientScopeRepresentation[] ClientScopeRepresentation[]
>([]); >([]);
const [isScopeOpen, setIsScopeOpen] = useState(false); const [isScopeOpen, setIsScopeOpen] = useState(false);
const [isUserOpen, setIsUserOpen] = useState(false);
const [selected, setSelected] = useState<string[]>([prefix]); const [selected, setSelected] = useState<string[]>([prefix]);
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const [userItems, setUserItems] = useState<JSX.Element[]>([]);
const [userSearch, setUserSearch] = useState("");
const [user, setUser] = useState<UserRepresentation>();
const [key, setKey] = useState(""); const [key, setKey] = useState("");
const refresh = () => setKey(`${new Date().getTime()}`); const refresh = () => setKey(`${new Date().getTime()}`);
const [effectiveRoles, setEffectiveRoles] = useState<RoleRepresentation[]>( const [effectiveRoles, setEffectiveRoles] = useState<RoleRepresentation[]>(
@ -146,39 +143,14 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
const tabContent4 = useRef(null); const tabContent4 = useRef(null);
const tabContent5 = useRef(null); const tabContent5 = useRef(null);
const form = useForm();
useFetch( useFetch(
() => adminClient.clients.listOptionalClientScopes({ id: clientId }), () => adminClient.clients.listOptionalClientScopes({ id: clientId }),
(optionalClientScopes) => setSelectableScopes(optionalClientScopes), (optionalClientScopes) => setSelectableScopes(optionalClientScopes),
[] []
); );
const toString = (user: UserRepresentation) => {
return (
t("common:fullName", {
givenName: user.firstName,
familyName: user.lastName,
}).trim() ||
user.username ||
""
);
};
useFetch(
() => adminClient.users.find({ search: userSearch }),
(users) =>
setUserItems(
users
.map((user) => {
user.toString = function () {
return toString(this);
};
return user;
})
.map((user) => <SelectOption key={user.id} value={user} />)
),
[userSearch]
);
useFetch( useFetch(
async () => { async () => {
const scope = selected.join(" "); const scope = selected.join(" ");
@ -218,22 +190,23 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
useFetch( useFetch(
async () => { async () => {
const scope = selected.join(" "); const scope = selected.join(" ");
const user = form.getValues("user");
if (!user) return []; if (!user) return [];
return await Promise.all([ return await Promise.all([
adminClient.clients.evaluateGenerateAccessToken({ adminClient.clients.evaluateGenerateAccessToken({
id: clientId, id: clientId,
userId: user.id!, userId: user[0],
scope, scope,
}), }),
adminClient.clients.evaluateGenerateUserInfo({ adminClient.clients.evaluateGenerateUserInfo({
id: clientId, id: clientId,
userId: user.id!, userId: user[0],
scope, scope,
}), }),
adminClient.clients.evaluateGenerateIdToken({ adminClient.clients.evaluateGenerateIdToken({
id: clientId, id: clientId,
userId: user.id!, userId: user[0],
scope, scope,
}), }),
]); ]);
@ -243,7 +216,7 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
setUserInfo(prettyPrintJSON(userInfo)); setUserInfo(prettyPrintJSON(userInfo));
setIdToken(prettyPrintJSON(idToken)); setIdToken(prettyPrintJSON(idToken));
}, },
[user, selected] [form.getValues("user"), selected]
); );
return ( return (
@ -301,39 +274,16 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
</SplitItem> </SplitItem>
</Split> </Split>
</FormGroup> </FormGroup>
<FormGroup <FormProvider {...form}>
label={t("user")} <UserSelect
fieldId="user" name="user"
labelIcon={ label="users"
<HelpItem
helpText="clients-help:user" helpText="clients-help:user"
fieldLabelId="clients:user" defaultValue=""
/>
}
>
<Select
toggleId="user"
variant={SelectVariant.typeahead} variant={SelectVariant.typeahead}
typeAheadAriaLabel={t("user")} isRequired
onToggle={() => setIsUserOpen(!isUserOpen)}
onFilter={(e) => {
const value = e?.target.value || "";
setUserSearch(value);
return userItems;
}}
onClear={() => {
setUser(undefined);
setUserSearch("");
}}
selections={[user]}
onSelect={(_, value) => {
setUser(value as UserRepresentation);
setUserSearch("");
setIsUserOpen(false);
}}
isOpen={isUserOpen}
/> />
</FormGroup> </FormProvider>
</Form> </Form>
</PageSection> </PageSection>
@ -365,7 +315,7 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
> >
<GeneratedCodeTab <GeneratedCodeTab
text={accessToken} text={accessToken}
user={user} user={form.getValues("user")}
label="generatedAccessToken" label="generatedAccessToken"
/> />
</TabContent> </TabContent>
@ -378,7 +328,7 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
> >
<GeneratedCodeTab <GeneratedCodeTab
text={idToken} text={idToken}
user={user} user={form.getValues("user")}
label="generatedIdToken" label="generatedIdToken"
/> />
</TabContent> </TabContent>
@ -391,7 +341,7 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
> >
<GeneratedCodeTab <GeneratedCodeTab
text={userInfo} text={userInfo}
user={user} user={form.getValues("user")}
label="generatedUserInfo" label="generatedUserInfo"
/> />
</TabContent> </TabContent>

View file

@ -1,4 +1,4 @@
import { useState } from "react"; import { useCallback, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Controller, useFormContext } from "react-hook-form"; import { Controller, useFormContext } from "react-hook-form";
import { import {
@ -7,6 +7,7 @@ import {
Select, Select,
SelectVariant, SelectVariant,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { debounce } from "lodash-es";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import type { UserQuery } from "@keycloak/keycloak-admin-client/lib/resources/users"; import type { UserQuery } from "@keycloak/keycloak-admin-client/lib/resources/users";
@ -42,6 +43,7 @@ export const UserSelect = ({
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const { adminClient } = useAdminClient(); const { adminClient } = useAdminClient();
const debounceFn = useCallback(debounce(setSearch, 1000), []);
useFetch( useFetch(
() => { () => {
@ -107,12 +109,14 @@ export const UserSelect = ({
isOpen={open} isOpen={open}
selections={value} selections={value}
onFilter={(_, value) => { onFilter={(_, value) => {
setSearch(value); debounceFn(value);
return convert(users); return convert(users);
}} }}
onSelect={(_, v) => { onSelect={(_, v) => {
const option = v.toString(); const option = v.toString();
if (value.includes(option)) { if (variant !== SelectVariant.typeaheadMulti) {
onChange([option]);
} else if (value.includes(option)) {
onChange(value.filter((item: string) => item !== option)); onChange(value.filter((item: string) => item !== option));
} else { } else {
onChange([...value, option]); onChange([...value, option]);