created bookmarkable tabs component (#296)

This commit is contained in:
Erik Jan de Wit 2021-01-13 21:47:40 +01:00 committed by GitHub
parent 45cda0a969
commit 0d9b4ff054
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 103 additions and 51 deletions

View file

@ -12,7 +12,6 @@ import {
SelectVariant,
Switch,
Tab,
Tabs,
TabTitleText,
TextInput,
ValidatedOptions,
@ -26,6 +25,7 @@ import {
useAdminClient,
asyncStateFetch,
} from "../../context/auth/AdminClient";
import { KeycloakTabs } from "../../components/keycloak-tabs/KeycloakTabs";
import { useAlerts } from "../../components/alert/Alerts";
import { useLoginProviders } from "../../context/server-info/ServerInfoProvider";
import { ViewHeader } from "../../components/view-header/ViewHeader";
@ -39,7 +39,6 @@ export const ClientScopeForm = () => {
>();
const history = useHistory();
const [clientScope, setClientScope] = useState<ClientScopeRepresentation>();
const [activeTab, setActiveTab] = useState(0);
const adminClient = useAdminClient();
const providers = useLoginProviders();
@ -103,13 +102,9 @@ export const ClientScopeForm = () => {
/>
<PageSection variant="light">
<Tabs
activeKey={activeTab}
onSelect={(_, key) => setActiveTab(key as number)}
isBox
>
<KeycloakTabs isBox>
<Tab
eventKey={0}
eventKey="settings"
title={<TabTitleText>{t("common:settings")}</TabTitleText>}
>
<Form
@ -312,14 +307,14 @@ export const ClientScopeForm = () => {
</Tab>
<Tab
isHidden={!id}
eventKey={1}
eventKey="mappers"
title={<TabTitleText>{t("mappers")}</TabTitleText>}
>
{clientScope && (
<MapperList clientScope={clientScope} refresh={refresh} />
)}
</Tab>
</Tabs>
</KeycloakTabs>
</PageSection>
</>
);

View file

@ -6,7 +6,6 @@ import {
PageSection,
Spinner,
Tab,
Tabs,
TabTitleText,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
@ -33,6 +32,7 @@ import {
import { ClientScopes } from "./scopes/ClientScopes";
import { EvaluateScopes } from "./scopes/EvaluateScopes";
import { ServiceAccount } from "./service-account/ServiceAccount";
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
type ClientDetailHeaderProps = {
onChange: (...event: any[]) => void;
@ -106,8 +106,6 @@ export const ClientDetails = () => {
const { id } = useParams<{ id: string }>();
const [activeTab, setActiveTab] = useState(0);
const [activeTab2, setActiveTab2] = useState(30);
const [client, setClient] = useState<ClientRepresentation>();
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
@ -203,57 +201,49 @@ export const ClientDetails = () => {
)}
/>
<PageSection variant="light">
<Tabs
activeKey={activeTab}
onSelect={(_, key) => setActiveTab(key as number)}
isBox
>
<KeycloakTabs isBox>
<Tab
eventKey={0}
eventKey="settings"
title={<TabTitleText>{t("common:settings")}</TabTitleText>}
>
<ClientSettings form={form} save={save} />
</Tab>
{publicClient && (
<Tab
eventKey={1}
eventKey="credentials"
title={<TabTitleText>{t("credentials")}</TabTitleText>}
>
<Credentials clientId={id} form={form} save={save} />
</Tab>
)}
<Tab
eventKey={2}
eventKey="clientScopes"
title={<TabTitleText>{t("clientScopes")}</TabTitleText>}
>
<Tabs
activeKey={activeTab2}
isSecondary
onSelect={(_, key) => setActiveTab2(key as number)}
>
<KeycloakTabs paramName="subtab" isSecondary>
<Tab
eventKey={30}
eventKey="setup"
title={<TabTitleText>{t("setup")}</TabTitleText>}
>
<ClientScopes clientId={id} protocol={client!.protocol!} />
</Tab>
<Tab
eventKey={31}
eventKey="evaluate"
title={<TabTitleText>{t("evaluate")}</TabTitleText>}
>
<EvaluateScopes clientId={id} protocol={client!.protocol!} />
</Tab>
</Tabs>
</KeycloakTabs>
</Tab>
{client && client.serviceAccountsEnabled && (
<Tab
eventKey={3}
eventKey="serviceAccount"
title={<TabTitleText>{t("serviceAccount")}</TabTitleText>}
>
<ServiceAccount clientId={id} />
</Tab>
)}
</Tabs>
</KeycloakTabs>
</PageSection>
</>
);

View file

@ -0,0 +1,53 @@
import React, { Children, isValidElement } from "react";
import { useHistory, useRouteMatch } from "react-router-dom";
import { TabProps, Tabs, TabsProps } from "@patternfly/react-core";
type KeycloakTabsProps = Omit<TabsProps, "ref" | "activeKey" | "onSelect"> & {
paramName?: string;
};
const createUrl = (
path: string,
params: { [index: string]: string }
): string => {
let url = path;
for (const key in params) {
const value = params[key];
if (url.indexOf(key) !== -1) {
url = url.replace(new RegExp(`:${key}\\??`), value || "");
} else {
url += `/${value}`;
}
}
return url;
};
export const KeycloakTabs = ({
paramName = "tab",
children,
...rest
}: KeycloakTabsProps) => {
const match = useRouteMatch();
const params = match.params as { [index: string]: string };
const history = useHistory();
const firstTab = Children.toArray(children)[0];
const tab =
params[paramName] ||
(isValidElement<TabProps>(firstTab) && firstTab.props.eventKey) ||
"";
return (
<Tabs
activeKey={tab}
onSelect={(_, key) =>
history.push(
createUrl(match.path, { ...params, [paramName]: key as string })
)
}
{...rest}
>
{children}
</Tabs>
);
};

View file

@ -5,7 +5,6 @@ import {
Label,
PageSection,
Tab,
Tabs,
TabTitleText,
ToolbarItem,
} from "@patternfly/react-core";
@ -19,13 +18,13 @@ import { RealmContext } from "../context/realm-context/RealmContext";
import { InfoCircleIcon } from "@patternfly/react-icons";
import { AdminEvents } from "./AdminEvents";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
export const EventsSection = () => {
const { t } = useTranslation("events");
const adminClient = useAdminClient();
const { realm } = useContext(RealmContext);
const [activeTab, setActiveTab] = useState(0);
const [key, setKey] = useState("");
const refresh = () => setKey(`${new Date().getTime()}`);
@ -55,13 +54,9 @@ export const EventsSection = () => {
<>
<ViewHeader titleKey="events:title" subKey="events:eventExplain" />
<PageSection variant="light">
<Tabs
activeKey={activeTab}
onSelect={(_, key) => setActiveTab(key as number)}
isBox
>
<KeycloakTabs isBox>
<Tab
eventKey={0}
eventKey="userEvents"
title={<TabTitleText>{t("userEvents")}</TabTitleText>}
>
<KeycloakDataTable
@ -110,12 +105,12 @@ export const EventsSection = () => {
/>
</Tab>
<Tab
eventKey={1}
eventKey="adminEvents"
title={<TabTitleText>{t("adminEvents")}</TabTitleText>}
>
<AdminEvents />
</Tab>
</Tabs>
</KeycloakTabs>
</PageSection>
</>
);

View file

@ -6,7 +6,6 @@ import {
DropdownItem,
PageSection,
Tab,
Tabs,
TabTitleText,
} from "@patternfly/react-core";
import { useTranslation } from "react-i18next";
@ -20,6 +19,7 @@ import { ViewHeader } from "../components/view-header/ViewHeader";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { RealmRoleForm } from "./RealmRoleForm";
import { useRealm } from "../context/realm-context/RealmContext";
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
const arrayToAttributes = (attributeArray: KeyValueType[]) => {
const initValue: { [index: string]: string[] } = {};
@ -50,7 +50,6 @@ export const RealmRoleTabs = () => {
const history = useHistory();
const [name, setName] = useState("");
const adminClient = useAdminClient();
const [activeTab, setActiveTab] = useState(0);
const { realm } = useRealm();
const { id } = useParams<{ id: string }>();
@ -145,24 +144,20 @@ export const RealmRoleTabs = () => {
/>
<PageSection variant="light">
{id && (
<Tabs
activeKey={activeTab}
onSelect={(_, key) => setActiveTab(key as number)}
isBox
>
<KeycloakTabs isBox>
<Tab
eventKey={0}
eventKey="details"
title={<TabTitleText>{t("details")}</TabTitleText>}
>
<RealmRoleForm form={form} save={save} editMode={true} />
</Tab>
<Tab
eventKey={1}
eventKey="attributes"
title={<TabTitleText>{t("attributes")}</TabTitleText>}
>
<RoleAttributes form={form} save={save} />
</Tab>
</Tabs>
</KeycloakTabs>
)}
{!id && <RealmRoleForm form={form} save={save} editMode={false} />}
</PageSection>

View file

@ -63,6 +63,12 @@ export const routes: RoutesFn = (t: TFunction) => [
breadcrumb: t("clients:clientSettings"),
access: "view-clients",
},
{
path: "/:realm/clients/:id/:tab?/:subtab?",
component: ClientDetails,
breadcrumb: null,
access: "view-clients",
},
{
path: "/:realm/client-scopes/new",
component: ClientScopeForm,
@ -75,6 +81,12 @@ export const routes: RoutesFn = (t: TFunction) => [
breadcrumb: t("client-scopes:clientScopeDetails"),
access: "view-clients",
},
{
path: "/:realm/client-scopes/:id/:tab",
component: ClientScopeForm,
breadcrumb: null,
access: "view-clients",
},
{
path: "/:realm/client-scopes/:scopeId/oidc-role-name-mapper",
component: RoleMappingForm,
@ -117,6 +129,12 @@ export const routes: RoutesFn = (t: TFunction) => [
breadcrumb: t("roles:roleDetails"),
access: "view-realm",
},
{
path: "/:realm/roles/:id/:tab",
component: RealmRoleTabs,
breadcrumb: null,
access: "view-realm",
},
{
path: "/:realm/users",
component: UsersSection,
@ -144,6 +162,12 @@ export const routes: RoutesFn = (t: TFunction) => [
breadcrumb: t("events:title"),
access: "view-events",
},
{
path: "/:realm/events/:tab",
component: EventsSection,
breadcrumb: null,
access: "view-events",
},
{
path: "/:realm/realm-settings",
component: RealmSettingsSection,