Add useToggle hook to manage toggleable state (#1600)

This commit is contained in:
Jon Koops 2021-11-30 14:07:44 +01:00 committed by GitHub
parent 31e1415758
commit 61b2689864
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 197 additions and 112 deletions

61
package-lock.json generated
View file

@ -38,6 +38,7 @@
"@testing-library/cypress": "^8.0.2",
"@testing-library/jest-dom": "^5.15.1",
"@testing-library/react": "^12.1.1",
"@testing-library/react-hooks": "^7.0.2",
"@types/dagre": "^0.7.45",
"@types/file-saver": "^2.0.4",
"@types/lodash": "^4.14.177",
@ -4483,6 +4484,35 @@
"react-dom": "*"
}
},
"node_modules/@testing-library/react-hooks": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz",
"integrity": "sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"@types/react": ">=16.9.0",
"@types/react-dom": ">=16.9.0",
"@types/react-test-renderer": ">=16.9.0",
"react-error-boundary": "^3.1.0"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0",
"react-test-renderer": ">=16.9.0"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-test-renderer": {
"optional": true
}
}
},
"node_modules/@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@ -5002,6 +5032,15 @@
"@types/react-router": "*"
}
},
"node_modules/@types/react-test-renderer": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz",
"integrity": "sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/resolve": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@ -24660,6 +24699,19 @@
"@testing-library/dom": "^8.0.0"
}
},
"@testing-library/react-hooks": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz",
"integrity": "sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==",
"dev": true,
"requires": {
"@babel/runtime": "^7.12.5",
"@types/react": ">=16.9.0",
"@types/react-dom": ">=16.9.0",
"@types/react-test-renderer": ">=16.9.0",
"react-error-boundary": "^3.1.0"
}
},
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@ -25176,6 +25228,15 @@
"@types/react-router": "*"
}
},
"@types/react-test-renderer": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz",
"integrity": "sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/resolve": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",

View file

@ -54,6 +54,7 @@
"@testing-library/cypress": "^8.0.2",
"@testing-library/jest-dom": "^5.15.1",
"@testing-library/react": "^12.1.1",
"@testing-library/react-hooks": "^7.0.2",
"@types/dagre": "^0.7.45",
"@types/file-saver": "^2.0.4",
"@types/lodash": "^4.14.177",

View file

@ -132,16 +132,12 @@ export const Header = () => {
const KebabDropdown = () => {
const [isDropdownOpen, setDropdownOpen] = useState(false);
const onDropdownToggle = () => {
setDropdownOpen(!isDropdownOpen);
};
return (
<Dropdown
id="user-dropdown-kebab"
isPlain
position="right"
toggle={<KebabToggle onToggle={onDropdownToggle} />}
toggle={<KebabToggle onToggle={setDropdownOpen} />}
isOpen={isDropdownOpen}
dropdownItems={kebabDropdownItems}
/>
@ -152,10 +148,6 @@ export const Header = () => {
const { whoAmI } = useWhoAmI();
const [isDropdownOpen, setDropdownOpen] = useState(false);
const onDropdownToggle = () => {
setDropdownOpen(!isDropdownOpen);
};
return (
<Dropdown
isPlain
@ -163,7 +155,7 @@ export const Header = () => {
id="user-dropdown"
isOpen={isDropdownOpen}
toggle={
<DropdownToggle onToggle={onDropdownToggle}>
<DropdownToggle onToggle={setDropdownOpen}>
{whoAmI.getDisplayName()}
</DropdownToggle>
}

View file

@ -24,6 +24,7 @@ import { useRealm } from "../context/realm-context/RealmContext";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { useAlerts } from "../components/alert/Alerts";
import { toUpperCase } from "../util";
import useToggle from "../utils/useToggle";
import { DuplicateFlowModal } from "./DuplicateFlowModal";
import { toCreateFlow } from "./routes/CreateFlow";
import { toFlow } from "./routes/Flow";
@ -55,7 +56,7 @@ export default function AuthenticationSection() {
const { addAlert, addError } = useAlerts();
const [selectedFlow, setSelectedFlow] = useState<AuthenticationType>();
const [open, setOpen] = useState(false);
const [open, toggleOpen, setOpen] = useToggle();
const loader = async () => {
const clients = await adminClient.clients.find();
@ -202,7 +203,7 @@ export default function AuthenticationSection() {
<DuplicateFlowModal
name={selectedFlow ? selectedFlow.alias! : ""}
description={selectedFlow?.description!}
toggleDialog={() => setOpen(!open)}
toggleDialog={toggleOpen}
onComplete={() => {
refresh();
setOpen(false);

View file

@ -40,6 +40,7 @@ import { AddSubFlowModal, Flow } from "./components/modals/AddSubFlowModal";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { DuplicateFlowModal } from "./DuplicateFlowModal";
import { useRealm } from "../context/realm-context/RealmContext";
import useToggle from "../utils/useToggle";
import { toAuthentication } from "./routes/Authentication";
import { EditFlowModal } from "./EditFlowModal";
@ -69,7 +70,7 @@ export default function FlowDetails() {
const [showAddSubFlowDialog, setShowSubFlowDialog] = useState<boolean>();
const [selectedExecution, setSelectedExecution] =
useState<ExpandableExecution>();
const [open, setOpen] = useState(false);
const [open, toggleOpen, setOpen] = useToggle();
const [edit, setEdit] = useState(false);
useFetch(
@ -280,7 +281,7 @@ export default function FlowDetails() {
<DuplicateFlowModal
name={flow?.alias!}
description={flow?.description!}
toggleDialog={() => setOpen(!open)}
toggleDialog={toggleOpen}
onComplete={() => {
refresh();
setOpen(false);

View file

@ -47,7 +47,7 @@ export const EditFlowDropdown = ({
data-testid={`${execution.displayName}-edit-dropdown`}
isOpen={open}
toggle={
<DropdownToggle onToggle={(open) => setOpen(open)}>
<DropdownToggle onToggle={setOpen}>
<PlusIcon />
</DropdownToggle>
}

View file

@ -28,7 +28,7 @@ export const FlowRequirementDropdown = ({
<Select
className="keycloak__authentication__requirement-dropdown"
variant={SelectVariant.single}
onToggle={() => setOpen(!open)}
onToggle={setOpen}
onSelect={(_event, value) => {
flow.requirement = value.toString();
onChange(flow);

View file

@ -151,7 +151,7 @@ export const AddSubFlowModal = ({
<Select
menuAppendTo="parent"
toggleId="flowType"
onToggle={() => setOpen(!open)}
onToggle={setOpen}
onSelect={(_, value) => {
onChange(value as string);
setOpen(false);
@ -194,9 +194,9 @@ export const AddSubFlowModal = ({
<Select
menuAppendTo="parent"
toggleId="provider"
onToggle={(toggle) => setOpenProvider(toggle)}
onToggle={setOpenProvider}
onSelect={(_, value) => {
onChange(value as string);
onChange(value.toString());
setOpenProvider(false);
}}
selections={value.displayName}

View file

@ -37,9 +37,9 @@ export const FlowType = () => {
render={({ onChange, value }) => (
<Select
toggleId="flowType"
onToggle={() => setOpen(!open)}
onToggle={setOpen}
onSelect={(_, value) => {
onChange(value as string);
onChange(value.toString());
setOpen(false);
}}
selections={t(`top-level-flow-type.${value}`)}

View file

@ -37,7 +37,7 @@ export const ChangeTypeDropdown = ({
selections={[]}
isDisabled={selectedRows.length === 0}
placeholderText={t("changeTypeTo")}
onToggle={(isExpanded) => setOpen(isExpanded)}
onToggle={setOpen}
onSelect={async (_, value) => {
try {
await Promise.all(

View file

@ -221,9 +221,7 @@ export default function ClientScopesSection() {
</ToolbarItem>
<ToolbarItem>
<Dropdown
toggle={
<KebabToggle onToggle={() => setKebabOpen(!kebabOpen)} />
}
toggle={<KebabToggle onToggle={setKebabOpen} />}
isOpen={kebabOpen}
isPlain
dropdownItems={[

View file

@ -1,7 +1,7 @@
/**
* @jest-environment jsdom
*/
import React, { useState } from "react";
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import { Button } from "@patternfly/react-core";
@ -9,6 +9,7 @@ import type { ServerInfoRepresentation } from "@keycloak/keycloak-admin-client/l
import type WhoAmIRepresentation from "@keycloak/keycloak-admin-client/lib/defs/whoAmIRepresentation";
import { ServerInfoContext } from "../../context/server-info/ServerInfoProvider";
import serverInfo from "../../context/server-info/__tests__/mock.json";
import useToggle from "../../utils/useToggle";
import { AddMapperDialog, AddMapperDialogModalProps } from "./MapperDialog";
import { WhoAmI, WhoAmIContext } from "../../context/whoami/WhoAmI";
@ -16,7 +17,7 @@ import whoami from "../../context/whoami/__tests__/mock-whoami.json";
describe("MapperDialog", () => {
const Test = (args: AddMapperDialogModalProps) => {
const [open, setOpen] = useState(false);
const [open, toggleOpen, setOpen] = useToggle();
return (
<ServerInfoContext.Provider
@ -28,11 +29,7 @@ describe("MapperDialog", () => {
whoAmI: new WhoAmI(whoami as WhoAmIRepresentation),
}}
>
<AddMapperDialog
{...args}
open={open}
toggleDialog={() => setOpen(!open)}
/>
<AddMapperDialog {...args} open={open} toggleDialog={toggleOpen} />
<Button onClick={() => setOpen(true)}>
{!open ? "Show" : "Hide"}
</Button>

View file

@ -142,7 +142,7 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
variant={SelectVariant.single}
isOpen={openType}
selections={value}
onToggle={() => setOpenType(!openType)}
onToggle={setOpenType}
onSelect={(_, value) => {
onChange(value);
setOpenType(false);
@ -173,7 +173,7 @@ export const ScopeForm = ({ clientScope, save }: ScopeFormProps) => {
<Select
toggleId="kc-protocol"
required
onToggle={() => isOpen(!open)}
onToggle={isOpen}
onSelect={(_, value) => {
onChange(value as string);
isOpen(false);

View file

@ -72,10 +72,7 @@ export const SearchDropdown = ({
<Dropdown
className="keycloak__client-scopes__searchtype"
toggle={
<DropdownToggle
id="toggle-id"
onToggle={(open) => setSearchToggle(open)}
>
<DropdownToggle id="toggle-id" onToggle={setSearchToggle}>
<FilterIcon /> {t(`clientScopeSearch.${searchType}`)}
</DropdownToggle>
}
@ -110,7 +107,7 @@ export const SearchToolbar = ({
<ToolbarItem>
<Select
className="keycloak__client-scopes__searchtype"
onToggle={(open) => setOpen(open)}
onToggle={setOpen}
isOpen={open}
selections={[
type === AllClientScopes.none
@ -142,7 +139,7 @@ export const SearchToolbar = ({
<ToolbarItem>
<Select
className="keycloak__client-scopes__searchtype"
onToggle={(open) => setOpen(open)}
onToggle={setOpen}
isOpen={open}
selections={[t(`protocolTypes.${protocol}`)]}
onSelect={(_, value) => {

View file

@ -33,6 +33,7 @@ import {
ClientScopeDefaultOptionalType,
} from "../../components/client-scope/ClientScopeTypes";
import { useRealm } from "../../context/realm-context/RealmContext";
import useToggle from "../../utils/useToggle";
import { toMapper } from "../routes/Mapper";
import { toClientScope } from "../routes/ClientScope";
@ -42,7 +43,7 @@ export default function ClientScopeForm() {
useState<ClientScopeDefaultOptionalType>();
const history = useHistory();
const { realm } = useRealm();
const [hide, setHide] = useState(false);
const [hide, toggleHide] = useToggle();
const adminClient = useAdminClient();
const { id, type } = useParams<{ id: string; type: AllClientScopes }>();
@ -249,7 +250,7 @@ export default function ClientScopeForm() {
dropdownItems={
clientScope
? [
<DropdownItem key="delete" onClick={() => toggleDeleteDialog()}>
<DropdownItem key="delete" onClick={toggleDeleteDialog}>
{t("common:delete")}
</DropdownItem>,
]
@ -299,7 +300,7 @@ export default function ClientScopeForm() {
type={"client-scope"}
loader={loader}
save={assignRoles}
onHideRolesToggle={() => setHide(!hide)}
onHideRolesToggle={toggleHide}
/>
</Tab>
</KeycloakTabs>

View file

@ -300,7 +300,7 @@ export const AdvancedTab = ({
/>
<ExpandableSection
toggleText={t("registeredClusterNodes")}
onToggle={() => setExpanded(!expanded)}
onToggle={setExpanded}
isExpanded={expanded}
>
<KeycloakDataTable

View file

@ -43,6 +43,7 @@ import {
convertToFormValues,
exportClient,
} from "../util";
import useToggle from "../utils/useToggle";
import { AdvancedTab } from "./AdvancedTab";
import { ClientSettings } from "./ClientSettings";
import { Credentials } from "./credentials/Credentials";
@ -115,7 +116,7 @@ const ClientDetailHeader = ({
}, [client, t]);
const dropdownItems = [
<DropdownItem key="download" onClick={() => toggleDownloadDialog()}>
<DropdownItem key="download" onClick={toggleDownloadDialog}>
{t("downloadAdapterConfig")}
</DropdownItem>,
<DropdownItem key="export" onClick={() => exportClient(client)}>
@ -127,7 +128,7 @@ const ClientDetailHeader = ({
<DropdownItem
data-testid="delete-client"
key="delete"
onClick={() => toggleDeleteDialog()}
onClick={toggleDeleteDialog}
>
{t("common:delete")}
</DropdownItem>,
@ -180,11 +181,8 @@ export default function ClientDetails() {
const history = useHistory();
const [downloadDialogOpen, setDownloadDialogOpen] = useState(false);
const toggleDownloadDialog = () => setDownloadDialogOpen(!downloadDialogOpen);
const [changeAuthenticatorOpen, setChangeAuthenticatorOpen] = useState(false);
const toggleChangeAuthenticator = () =>
setChangeAuthenticatorOpen(!changeAuthenticatorOpen);
const [downloadDialogOpen, toggleDownloadDialogOpen] = useToggle();
const [changeAuthenticatorOpen, toggleChangeAuthenticatorOpen] = useToggle();
const [clientScopeSubTab, setClientScopeSubTab] = useState(30);
const [authorizationSubTab, setAuthorizationSubTab] = useState(40);
@ -259,7 +257,7 @@ export default function ClientDetails() {
client?.clientAuthenticatorType !== clientAuthenticatorType &&
!confirmed
) {
toggleChangeAuthenticator();
toggleChangeAuthenticatorOpen();
return;
}
const redirectUris = toValue(form.getValues()["redirectUris"]);
@ -343,7 +341,7 @@ export default function ClientDetails() {
clientAuthenticatorType: clientAuthenticatorType,
})}
open={changeAuthenticatorOpen}
toggleDialog={toggleChangeAuthenticator}
toggleDialog={toggleChangeAuthenticatorOpen}
onConfirm={() => save({ confirmed: true })}
>
<>
@ -360,7 +358,7 @@ export default function ClientDetails() {
id={client.id!}
protocol={client.protocol}
open={downloadDialogOpen}
toggleDialog={toggleDownloadDialog}
toggleDialog={toggleDownloadDialogOpen}
/>
<Controller
name="enabled"
@ -373,7 +371,7 @@ export default function ClientDetails() {
client={client}
save={save}
toggleDeleteDialog={toggleDeleteDialog}
toggleDownloadDialog={toggleDownloadDialog}
toggleDownloadDialog={toggleDownloadDialogOpen}
/>
)}
/>

View file

@ -182,9 +182,9 @@ export const ClientSettings = ({
render={({ onChange, value }) => (
<Select
toggleId="loginTheme"
onToggle={() => setLoginThemeOpen(!loginThemeOpen)}
onToggle={setLoginThemeOpen}
onSelect={(_, value) => {
onChange(value as string);
onChange(value.toString());
setLoginThemeOpen(false);
}}
selections={value || t("common:choose")}

View file

@ -42,9 +42,9 @@ export const GeneralSettings = () => {
render={({ onChange, value }) => (
<Select
id="kc-type"
onToggle={() => isOpen(!open)}
onToggle={isOpen}
onSelect={(_, value) => {
onChange(value as string);
onChange(value.toString());
isOpen(false);
}}
selections={value}

View file

@ -78,7 +78,7 @@ export const SamlConfig = () => {
render={({ onChange, value }) => (
<Select
toggleId="samlNameIdFormat"
onToggle={(open) => setNameFormatOpen(open)}
onToggle={setNameFormatOpen}
onSelect={(_, value) => {
onChange(value.toString());
setNameFormatOpen(false);

View file

@ -85,7 +85,7 @@ export const SamlSignature = () => {
render={({ onChange, value }) => (
<Select
toggleId="signatureAlgorithm"
onToggle={(open) => setAlgOpen(open)}
onToggle={setAlgOpen}
onSelect={(_, value) => {
onChange(value.toString());
setAlgOpen(false);
@ -126,7 +126,7 @@ export const SamlSignature = () => {
render={({ onChange, value }) => (
<Select
toggleId="signatureKeyName"
onToggle={(open) => setKeyOpen(open)}
onToggle={setKeyOpen}
onSelect={(_, value) => {
onChange(value.toString());
setKeyOpen(false);
@ -167,7 +167,7 @@ export const SamlSignature = () => {
render={({ onChange, value }) => (
<Select
toggleId="canonicalization"
onToggle={(open) => setCanOpen(open)}
onToggle={setCanOpen}
onSelect={(_, value) => {
onChange(value.toString());
setCanOpen(false);

View file

@ -118,7 +118,7 @@ export const AdvancedSettings = ({
<Select
toggleId="keyForCodeExchange"
variant={SelectVariant.single}
onToggle={() => setOpen(!open)}
onToggle={setOpen}
isOpen={open}
onSelect={(_, value) => {
onChange(value);

View file

@ -76,7 +76,7 @@ export const AuthenticationOverrides = ({
<Select
toggleId="browserFlow"
variant={SelectVariant.single}
onToggle={() => setBrowserFlowOpen(!browserFlowOpen)}
onToggle={setBrowserFlowOpen}
isOpen={browserFlowOpen}
onSelect={(_, value) => {
onChange(value);
@ -109,7 +109,7 @@ export const AuthenticationOverrides = ({
<Select
toggleId="directGrant"
variant={SelectVariant.single}
onToggle={() => setDirectGrantOpen(!directGrantOpen)}
onToggle={setDirectGrantOpen}
isOpen={directGrantOpen}
onSelect={(_, value) => {
onChange(value);

View file

@ -125,7 +125,7 @@ export const FineGrainOpenIdConnect = ({
<Select
toggleId="accessTokenSignatureAlgorithm"
variant={SelectVariant.single}
onToggle={() => setAccessTokenOpen(!accessTokenOpen)}
onToggle={setAccessTokenOpen}
isOpen={accessTokenOpen}
onSelect={(_, value) => {
onChange(value);
@ -157,7 +157,7 @@ export const FineGrainOpenIdConnect = ({
<Select
toggleId="idTokenSignatureAlgorithm"
variant={SelectVariant.single}
onToggle={() => setIdTokenOpen(!idTokenOpen)}
onToggle={setIdTokenOpen}
isOpen={idTokenOpen}
onSelect={(_, value) => {
onChange(value);
@ -189,9 +189,7 @@ export const FineGrainOpenIdConnect = ({
<Select
toggleId="idTokenEncryptionKeyManagementAlgorithm"
variant={SelectVariant.single}
onToggle={() =>
setIdTokenKeyManagementOpen(!idTokenKeyManagementOpen)
}
onToggle={setIdTokenKeyManagementOpen}
isOpen={idTokenKeyManagementOpen}
onSelect={(_, value) => {
onChange(value);
@ -223,7 +221,7 @@ export const FineGrainOpenIdConnect = ({
<Select
toggleId="idTokenEncryptionContentEncryptionAlgorithm"
variant={SelectVariant.single}
onToggle={() => setIdTokenContentOpen(!idTokenContentOpen)}
onToggle={setIdTokenContentOpen}
isOpen={idTokenContentOpen}
onSelect={(_, value) => {
onChange(value);
@ -255,9 +253,7 @@ export const FineGrainOpenIdConnect = ({
<Select
toggleId="userInfoSignedResponseAlgorithm"
variant={SelectVariant.single}
onToggle={() =>
setUserInfoSignedResponseOpen(!userInfoSignedResponseOpen)
}
onToggle={setUserInfoSignedResponseOpen}
isOpen={userInfoSignedResponseOpen}
onSelect={(_, value) => {
onChange(value);
@ -289,9 +285,7 @@ export const FineGrainOpenIdConnect = ({
<Select
toggleId="requestObjectSignatureAlgorithm"
variant={SelectVariant.single}
onToggle={() =>
setRequestObjectSignatureOpen(!requestObjectSignatureOpen)
}
onToggle={setRequestObjectSignatureOpen}
isOpen={requestObjectSignatureOpen}
onSelect={(_, value) => {
onChange(value);
@ -323,9 +317,7 @@ export const FineGrainOpenIdConnect = ({
<Select
toggleId="requestObjectRequired"
variant={SelectVariant.single}
onToggle={() =>
setRequestObjectRequiredOpen(!requestObjectRequiredOpen)
}
onToggle={setRequestObjectRequiredOpen}
isOpen={requestObjectRequiredOpen}
onSelect={(_, value) => {
onChange(value);

View file

@ -67,7 +67,7 @@ export const TokenLifespan = ({
<SplitItem>
<Select
variant={SelectVariant.single}
onToggle={(isExpanded) => setOpen(isExpanded)}
onToggle={setOpen}
isOpen={open}
onSelect={(_, value) => {
onChange(value);

View file

@ -155,7 +155,7 @@ export const Credentials = ({ clientId, save }: CredentialsProps) => {
<Select
toggleId="kc-client-authenticator-type"
required
onToggle={() => isOpen(!open)}
onToggle={isOpen}
onSelect={(_, value) => {
onChange(value as string);
isOpen(false);

View file

@ -40,9 +40,9 @@ export const SignedJWT = () => {
<Select
maxHeight={200}
toggleId="kc-signature-algorithm"
onToggle={() => isOpen(!open)}
onToggle={isOpen}
onSelect={(_, value) => {
onChange(value as string);
onChange(value.toString());
isOpen(false);
}}
selections={value || t("anyAlgorithm")}

View file

@ -61,7 +61,7 @@ export const KeyForm = ({
render={({ onChange, value }) => (
<Select
toggleId="archiveFormat"
onToggle={(isExpanded) => setOpenArchiveFormat(isExpanded)}
onToggle={setOpenArchiveFormat}
onSelect={(_, value) => {
onChange(value.toString());
setOpenArchiveFormat(false);

View file

@ -105,7 +105,7 @@ export const ImportKeyDialog = ({
render={({ onChange, value }) => (
<Select
toggleId="archiveFormat"
onToggle={() => setOpenArchiveFormat(!openArchiveFormat)}
onToggle={setOpenArchiveFormat}
onSelect={(_, value) => {
onChange(value as string);
setOpenArchiveFormat(false);

View file

@ -26,6 +26,7 @@ import type { ClientForm } from "../ClientDetails";
import { GenerateKeyDialog } from "./GenerateKeyDialog";
import { useFetch, useAdminClient } from "../../context/auth/AdminClient";
import { useAlerts } from "../../components/alert/Alerts";
import useToggle from "../../utils/useToggle";
import { ImportKeyDialog, ImportFile } from "./ImportKeyDialog";
import { Certificate } from "./Certificate";
@ -47,8 +48,9 @@ export const Keys = ({ clientId, save }: KeysProps) => {
const { addAlert, addError } = useAlerts();
const [keyInfo, setKeyInfo] = useState<CertificateRepresentation>();
const [openGenerateKeys, setOpenGenerateKeys] = useState(false);
const [openImportKeys, setOpenImportKeys] = useState(false);
const [openGenerateKeys, toggleOpenGenerateKeys, setOpenGenerateKeys] =
useToggle();
const [openImportKeys, toggleOpenImportKeys, setOpenImportKeys] = useToggle();
const useJwksUrl = useWatch({
control,
@ -104,15 +106,12 @@ export const Keys = ({ clientId, save }: KeysProps) => {
<PageSection variant="light" className="keycloak__form">
{openGenerateKeys && (
<GenerateKeyDialog
toggleDialog={() => setOpenGenerateKeys(!openGenerateKeys)}
toggleDialog={toggleOpenGenerateKeys}
save={generate}
/>
)}
{openImportKeys && (
<ImportKeyDialog
toggleDialog={() => setOpenImportKeys(!openImportKeys)}
save={importKey}
/>
<ImportKeyDialog toggleDialog={toggleOpenImportKeys} save={importKey} />
)}
<Card isFlat>
<CardHeader>

View file

@ -26,9 +26,10 @@ import {
clientScopeTypesDropdown,
} from "../../components/client-scope/ClientScopeTypes";
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
import { getProtocolName } from "../utils";
import useToggle from "../../utils/useToggle";
import "./client-scopes.css";
import { getProtocolName } from "../utils";
export type AddScopeDialogProps = {
clientScopes: ClientScopeRepresentation[];
@ -68,11 +69,11 @@ export const AddScopeDialog = ({
const [key, setKey] = useState(0);
const refresh = () => setKey(key + 1);
const [isFilterTypeDropdownOpen, setIsFilterTypeDropdownOpen] =
useState(false);
const [isFilterTypeDropdownOpen, toggleIsFilterTypeDropdownOpen] =
useToggle();
const [isProtocolTypeDropdownOpen, setIsProtocolTypeDropdownOpen] =
useState(false);
const [isProtocolTypeDropdownOpen, toggleIsProtocolTypeDropdownOpen] =
useToggle(false);
useEffect(() => {
refresh();
@ -97,14 +98,6 @@ export const AddScopeDialog = ({
toggleDialog();
};
const onFilterTypeDropdownToggle = () => {
setIsFilterTypeDropdownOpen(!isFilterTypeDropdownOpen);
};
const onProtocolTypeDropdownToggle = () => {
setIsProtocolTypeDropdownOpen(!isProtocolTypeDropdownOpen);
};
const onFilterTypeDropdownSelect = (filterType: string) => {
if (filterType === FilterType.Name) {
setFilterType(FilterType.Protocol);
@ -112,7 +105,7 @@ export const AddScopeDialog = ({
setFilterType(FilterType.Name);
}
setIsFilterTypeDropdownOpen(!isFilterTypeDropdownOpen);
toggleIsFilterTypeDropdownOpen();
};
const onProtocolTypeDropdownSelect = (protocolType: string) => {
@ -124,7 +117,7 @@ export const AddScopeDialog = ({
setProtocolType(ProtocolType.All);
}
setIsProtocolTypeDropdownOpen(!isProtocolTypeDropdownOpen);
toggleIsProtocolTypeDropdownOpen();
};
const protocolTypeOptions = [
@ -226,7 +219,7 @@ export const AddScopeDialog = ({
toggle={
<DropdownToggle
id="toggle-id-9"
onToggle={onFilterTypeDropdownToggle}
onToggle={toggleIsFilterTypeDropdownOpen}
toggleIndicator={CaretDownIcon}
icon={<FilterIcon />}
>
@ -258,7 +251,7 @@ export const AddScopeDialog = ({
toggle={
<DropdownToggle
id="toggle-id-9"
onToggle={onFilterTypeDropdownToggle}
onToggle={toggleIsFilterTypeDropdownOpen}
toggleIndicator={CaretDownIcon}
icon={<FilterIcon />}
>
@ -279,7 +272,7 @@ export const AddScopeDialog = ({
variant={SelectVariant.single}
className="kc-protocolType-select"
aria-label="Select Input"
onToggle={onProtocolTypeDropdownToggle}
onToggle={toggleIsProtocolTypeDropdownOpen}
onSelect={(_, value) =>
onProtocolTypeDropdownSelect(value.toString())
}

View file

@ -0,0 +1,41 @@
/**
* @jest-environment jsdom
*/
import { act, renderHook } from "@testing-library/react-hooks";
import useToggle from "./useToggle";
describe("useToggle", () => {
it("has a default value of false", () => {
const { result } = renderHook(() => useToggle());
const [value] = result.current;
expect(value).toBe(false);
});
it("uses the initial value", () => {
const { result } = renderHook(() => useToggle(true));
const [value] = result.current;
expect(value).toBe(true);
});
it("toggles the value", () => {
const { result } = renderHook(() => useToggle());
const [, toggleValue] = result.current;
act(() => toggleValue());
const [value] = result.current;
expect(value).toBe(true);
});
it("sets the value", () => {
const { result } = renderHook(() => useToggle());
const [, , setValue] = result.current;
act(() => setValue(true));
const [value] = result.current;
expect(value).toBe(true);
});
});

13
src/utils/useToggle.ts Normal file
View file

@ -0,0 +1,13 @@
import { useCallback, useState } from "react";
/**
* A hook that allows you toggle a boolean value, useful for toggle buttons, showing and hiding modals, etc.
*
* @param initialValue The initial value to use, false by default.
*/
export default function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggleValue = useCallback(() => setValue((val) => !val), []);
return [value, toggleValue, setValue] as const;
}