Realm-settings -> User Profile -> Attributes (#2107)
* attributes tab - wip * attributes tab - wip * attributes tab - wip * attributes tab - wip * added dropdown for each attribute row * added kebab logic * added delete dialog - wip * added delete dialog - wip * added delete dialog - wip * draggable rows - wip * draggable rows - wip * draggable rows - wip * refactored draggable rows * refactored draggable rows * added tests * added tests * refactor * refactor * refactor * renamed css file to realm-settings-section.css Co-authored-by: Agnieszka Gancarczyk <agancarc@redhat.com>
This commit is contained in:
parent
42d8c31e84
commit
a6904be9ff
18 changed files with 347 additions and 23 deletions
|
@ -1,5 +1,5 @@
|
|||
import ListingPage from "../support/pages/admin_console/ListingPage";
|
||||
import RealmSettingsPage from "../support/pages/admin_console/manage/realm_settings/RealmSettingsPage";
|
||||
import UserProfile from "../support/pages/admin_console/manage/realm_settings/UserProfile";
|
||||
import SidebarPage from "../support/pages/admin_console/SidebarPage";
|
||||
import LoginPage from "../support/pages/LoginPage";
|
||||
import AdminClient from "../support/util/AdminClient";
|
||||
|
@ -8,15 +8,18 @@ import ModalUtils from "../support/util/ModalUtils";
|
|||
|
||||
const loginPage = new LoginPage();
|
||||
const sidebarPage = new SidebarPage();
|
||||
const realmSettingsPage = new RealmSettingsPage();
|
||||
const userProfileTab = new UserProfile();
|
||||
const adminClient = new AdminClient();
|
||||
const listingPage = new ListingPage();
|
||||
const modalUtils = new ModalUtils();
|
||||
|
||||
// Selectors
|
||||
const getUserProfileTab = () =>
|
||||
cy.findByTestId(realmSettingsPage.userProfileTab);
|
||||
const getAttributesGroupTab = () => cy.findByTestId("attributesGroupTab");
|
||||
const getUserProfileTab = () => userProfileTab.goToTab();
|
||||
const getAttributesTab = () => userProfileTab.goToAttributesTab();
|
||||
const getAttributesGroupTab = () => userProfileTab.goToAttributesGroupTab();
|
||||
const getJsonEditorTab = () => userProfileTab.goToJsonEditorTab();
|
||||
const clickCreateAttributeButton = () =>
|
||||
userProfileTab.createAttributeButtonClick();
|
||||
|
||||
describe("User profile tabs", () => {
|
||||
const realmName = "Realm_" + (Math.random() + 1).toString(36).substring(7);
|
||||
|
@ -36,19 +39,35 @@ describe("User profile tabs", () => {
|
|||
sidebarPage.goToRealmSettings();
|
||||
});
|
||||
|
||||
describe("Attribute groups", () => {
|
||||
it("deletes an attributes group", () => {
|
||||
describe("Attributes sub tab tests", () => {
|
||||
it("Goes to create attribute page", () => {
|
||||
getUserProfileTab();
|
||||
getAttributesTab();
|
||||
clickCreateAttributeButton();
|
||||
cy.get("p").should("have.text", "Create attribute");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Attribute groups sub tab tests", () => {
|
||||
it("Deletes an attributes group", () => {
|
||||
cy.wrap(null).then(() =>
|
||||
adminClient.patchUserProfile(realmName, {
|
||||
groups: [{ name: "Test" }],
|
||||
})
|
||||
);
|
||||
|
||||
getUserProfileTab().click();
|
||||
getAttributesGroupTab().click();
|
||||
getUserProfileTab();
|
||||
getAttributesGroupTab();
|
||||
listingPage.deleteItem("Test");
|
||||
modalUtils.confirmModal();
|
||||
listingPage.itemExist("Test", false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Json Editor sub tab tests", () => {
|
||||
it("Goes to Json Editor tab", () => {
|
||||
getUserProfileTab();
|
||||
getJsonEditorTab();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
export default class UserProfile {
|
||||
private userProfileTab = "rs-user-profile-tab";
|
||||
private attributesTab = "attributesTab";
|
||||
private attributesGroupTab = "attributesGroupTab";
|
||||
private jsonEditorTab = "jsonEditorTab";
|
||||
private createAttributeButton = "createAttributeBtn";
|
||||
private actionsDrpDwn = "actions-dropdown";
|
||||
private deleteDrpDwnOption = "deleteDropdownAttributeItem";
|
||||
private editDrpDwnOption = "editDropdownAttributeItem";
|
||||
|
||||
goToTab() {
|
||||
cy.findByTestId(this.userProfileTab).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
goToAttributesTab() {
|
||||
cy.findByTestId(this.attributesTab).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
goToAttributesGroupTab() {
|
||||
cy.findByTestId(this.attributesGroupTab).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
goToJsonEditorTab() {
|
||||
cy.findByTestId(this.jsonEditorTab).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
createAttributeButtonClick() {
|
||||
cy.findByTestId(this.createAttributeButton).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
selectDropdown() {
|
||||
cy.findByTestId(this.actionsDrpDwn).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
selectDeleteOption() {
|
||||
cy.findByTestId(this.deleteDrpDwnOption).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
selectEditOption() {
|
||||
cy.findByTestId(this.editDrpDwnOption).click();
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -31,7 +31,7 @@ import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
|||
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
||||
import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||
import { PlusCircleIcon, TrashIcon } from "@patternfly/react-icons";
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { toAddExecutor } from "./routes/AddExecutor";
|
||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||
|
|
|
@ -21,7 +21,7 @@ import { useRealm } from "../context/realm-context/RealmContext";
|
|||
import { useWhoAmI } from "../context/whoami/WhoAmI";
|
||||
import { emailRegexPattern } from "../util";
|
||||
import { AddUserEmailModal } from "./AddUserEmailModal";
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
|
||||
type RealmSettingsEmailTabProps = {
|
||||
realm: RealmRepresentation;
|
||||
|
|
|
@ -20,7 +20,7 @@ import { emptyFormatter } from "../util";
|
|||
import { useAdminClient } from "../context/auth/AdminClient";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
import { FilterIcon } from "@patternfly/react-icons";
|
||||
|
||||
type KeyData = KeyMetadataRepresentation & {
|
||||
|
@ -72,7 +72,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
|
|||
|
||||
const activeKeysCopy = keys!.filter((i) => i.status === "ACTIVE");
|
||||
|
||||
return activeKeysCopy?.map((key) => {
|
||||
return activeKeysCopy.map((key) => {
|
||||
const provider = realmComponents.find(
|
||||
(component: ComponentRepresentation) => component.id === key.providerId
|
||||
);
|
||||
|
@ -88,7 +88,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
|
|||
|
||||
const passiveKeys = keys!.filter((i) => i.status === "PASSIVE");
|
||||
|
||||
return passiveKeys?.map((key) => {
|
||||
return passiveKeys.map((key) => {
|
||||
const provider = realmComponents.find(
|
||||
(component: ComponentRepresentation) => component.id === key.providerId
|
||||
);
|
||||
|
@ -104,7 +104,7 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
|
|||
|
||||
const disabledKeys = keys!.filter((i) => i.status === "DISABLED");
|
||||
|
||||
return disabledKeys?.map((key) => {
|
||||
return disabledKeys.map((key) => {
|
||||
const provider = realmComponents!.find(
|
||||
(component: ComponentRepresentation) => component.id === key.providerId
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@ import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/
|
|||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
import type ComponentTypeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentTypeRepresentation";
|
||||
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||
import { useAdminClient } from "../context/auth/AdminClient";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
|
@ -450,7 +450,7 @@ export const KeysProvidersTab = ({
|
|||
}: KeysProps) => {
|
||||
return (
|
||||
<KeysTabInner
|
||||
components={realmComponents?.map((component) => {
|
||||
components={realmComponents.map((component) => {
|
||||
const provider = keyProviderComponentTypes.find(
|
||||
(componentType: ComponentTypeRepresentation) =>
|
||||
component.providerId === componentType.id
|
||||
|
|
23
src/realm-settings/NewAttributeSettings.tsx
Normal file
23
src/realm-settings/NewAttributeSettings.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { PageSection } from "@patternfly/react-core";
|
||||
import React from "react";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormAccess } from "../components/form-access/FormAccess";
|
||||
import "./realm-settings-section.css";
|
||||
|
||||
export default function NewAttributeSettings() {
|
||||
const { t } = useTranslation("realm-settings");
|
||||
|
||||
return (
|
||||
<PageSection variant="light">
|
||||
<FormAccess
|
||||
onSubmit={() => console.log("TODO handle submit")}
|
||||
isHorizontal
|
||||
role="view-realm"
|
||||
className="pf-u-mt-lg"
|
||||
>
|
||||
<p>{t("createAttribute")}</p>
|
||||
</FormAccess>
|
||||
</PageSection>
|
||||
);
|
||||
}
|
|
@ -44,7 +44,7 @@ import { AddClientProfileModal } from "./AddClientProfileModal";
|
|||
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
||||
import { toClientPolicies } from "./routes/ClientPolicies";
|
||||
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
|
||||
type NewClientPolicyForm = Required<ClientPolicyRepresentation>;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import type ClientPolicyRepresentation from "@keycloak/keycloak-admin-client/lib
|
|||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { toAddClientPolicy } from "./routes/AddClientPolicy";
|
||||
import { toEditClientPolicy } from "./routes/EditClientPolicy";
|
||||
|
|
|
@ -26,7 +26,7 @@ import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/li
|
|||
import { toClientProfile } from "./routes/ClientProfile";
|
||||
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
||||
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
|
||||
type ClientProfile = ClientProfileRepresentation & {
|
||||
global: boolean;
|
||||
|
|
|
@ -15,7 +15,7 @@ import { HelpItem } from "../components/help-enabler/HelpItem";
|
|||
import { FormPanel } from "../components/scroll-form/FormPanel";
|
||||
import { TimeSelector } from "../components/time-selector/TimeSelector";
|
||||
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
|
||||
type RealmSettingsSessionsTabProps = {
|
||||
realm: RealmRepresentation;
|
||||
|
|
|
@ -23,7 +23,7 @@ import { TimeSelector } from "../components/time-selector/TimeSelector";
|
|||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||
import { forHumans, interpolateTimespan } from "../util";
|
||||
|
||||
import "./RealmSettingsSection.css";
|
||||
import "./realm-settings-section.css";
|
||||
|
||||
type RealmSettingsSessionsTabProps = {
|
||||
realm: RealmRepresentation;
|
||||
|
|
|
@ -367,6 +367,18 @@ export default {
|
|||
updateMessageBundleSuccess: "Success! Message bundle updated.",
|
||||
updateMessageBundleError: "Error updating message bundle.",
|
||||
addMessageBundleError: "Error creating message bundle, {{error}}",
|
||||
attributeName: "Name",
|
||||
attributeDisplayName: "Display name",
|
||||
attributeGroup: "Attribute group",
|
||||
updatedUserProfileSuccess: "User Profile configuration has been saved",
|
||||
updatedUserProfileError: "User Profile configuration hasn't been saved",
|
||||
createAttribute: "Create attribute",
|
||||
attributesDropdown: "Attributes dropdown",
|
||||
deleteAttributeConfirmTitle: "Delete attribute?",
|
||||
deleteAttributeConfirm:
|
||||
"Are you sure you want to permanently delete the attribute {{attributeName}}?",
|
||||
deleteAttributeSuccess: "Attribute deleted",
|
||||
deleteAttributeError: "",
|
||||
eventType: "Event saved type",
|
||||
searchEventType: "Search saved event type",
|
||||
addSavedTypes: "Add saved types",
|
||||
|
|
|
@ -16,6 +16,7 @@ import { EditClientPolicyRoute } from "./routes/EditClientPolicy";
|
|||
import { NewClientPolicyConditionRoute } from "./routes/AddCondition";
|
||||
import { EditClientPolicyConditionRoute } from "./routes/EditCondition";
|
||||
import { UserProfileRoute } from "./routes/UserProfile";
|
||||
import { AddAttributeRoute } from "./routes/AddAttribute";
|
||||
import { KeysRoute } from "./routes/KeysTab";
|
||||
|
||||
const routes: RouteDef[] = [
|
||||
|
@ -37,6 +38,7 @@ const routes: RouteDef[] = [
|
|||
NewClientPolicyConditionRoute,
|
||||
EditClientPolicyConditionRoute,
|
||||
UserProfileRoute,
|
||||
AddAttributeRoute,
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
|
21
src/realm-settings/routes/AddAttribute.ts
Normal file
21
src/realm-settings/routes/AddAttribute.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import type { LocationDescriptorObject } from "history";
|
||||
import { lazy } from "react";
|
||||
import { generatePath } from "react-router-dom";
|
||||
import type { RouteDef } from "../../route-config";
|
||||
|
||||
export type AddAttributeParams = {
|
||||
realm: string;
|
||||
};
|
||||
|
||||
export const AddAttributeRoute: RouteDef = {
|
||||
path: "/:realm/realm-settings/userProfile/attributes/add-attribute",
|
||||
component: lazy(() => import("../NewAttributeSettings")),
|
||||
breadcrumb: (t) => t("realmSettings"),
|
||||
access: "view-realm",
|
||||
};
|
||||
|
||||
export const toAddAttribute = (
|
||||
params: AddAttributeParams
|
||||
): LocationDescriptorObject => ({
|
||||
pathname: generatePath(AddAttributeRoute.path, params),
|
||||
});
|
192
src/realm-settings/user-profile/AttributesTab.tsx
Normal file
192
src/realm-settings/user-profile/AttributesTab.tsx
Normal file
|
@ -0,0 +1,192 @@
|
|||
import React, { Fragment, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Button,
|
||||
ButtonVariant,
|
||||
Divider,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
KebabToggle,
|
||||
ToolbarItem,
|
||||
} from "@patternfly/react-core";
|
||||
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
|
||||
import { DraggableTable } from "../../authentication/components/DraggableTable";
|
||||
import type { UserProfileAttribute } from "@keycloak/keycloak-admin-client/lib/defs/userProfileConfig";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { toAddAttribute } from "../routes/AddAttribute";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
import { useUserProfile } from "./UserProfileContext";
|
||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||
|
||||
type movedAttributeType = UserProfileAttribute;
|
||||
|
||||
export const AttributesTab = () => {
|
||||
const { config, save } = useUserProfile();
|
||||
const { realm: realmName } = useRealm();
|
||||
const { t } = useTranslation("realm-settings");
|
||||
const history = useHistory();
|
||||
const [attributeToDelete, setAttributeToDelete] =
|
||||
useState<{ name: string }>();
|
||||
const [kebabOpen, setKebabOpen] = useState({
|
||||
status: false,
|
||||
rowKey: "",
|
||||
});
|
||||
|
||||
const executeMove = async (
|
||||
attribute: UserProfileAttribute,
|
||||
newIndex: number
|
||||
) => {
|
||||
const fromIndex = config?.attributes!.findIndex((attr) => {
|
||||
return attr.name === attribute.name;
|
||||
});
|
||||
|
||||
let movedAttribute: movedAttributeType = {};
|
||||
movedAttribute = config?.attributes![fromIndex!]!;
|
||||
config?.attributes!.splice(fromIndex!, 1);
|
||||
config?.attributes!.splice(newIndex, 0, movedAttribute);
|
||||
|
||||
save(
|
||||
{ attributes: config?.attributes! },
|
||||
{
|
||||
successMessageKey: "realm-settings:updatedUserProfileSuccess",
|
||||
errorMessageKey: "realm-settings:updatedUserProfileError",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const goToCreate = () => history.push(toAddAttribute({ realm: realmName }));
|
||||
|
||||
const updatedAttributes = config?.attributes!.filter(
|
||||
(attribute) => attribute.name !== attributeToDelete?.name
|
||||
);
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: t("deleteAttributeConfirmTitle"),
|
||||
messageKey: t("deleteAttributeConfirm", {
|
||||
attributeName: attributeToDelete?.name!,
|
||||
}),
|
||||
continueButtonLabel: t("common:delete"),
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
save(
|
||||
{ attributes: updatedAttributes! },
|
||||
{
|
||||
successMessageKey: "realm-settings:deleteAttributeSuccess",
|
||||
errorMessageKey: "realm-settings:deleteAttributeError",
|
||||
}
|
||||
);
|
||||
setAttributeToDelete({
|
||||
name: "",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (!config) {
|
||||
return <KeycloakSpinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="pf-u-mt-md pf-u-mb-md pf-u-ml-md">
|
||||
<ToolbarItem className="kc-toolbar-attributesTab">
|
||||
<Button
|
||||
data-testid="createAttributeBtn"
|
||||
variant="primary"
|
||||
onClick={goToCreate}
|
||||
>
|
||||
{t("createAttribute")}
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
</div>
|
||||
<Divider />
|
||||
<DeleteConfirm />
|
||||
<DraggableTable
|
||||
keyField="name"
|
||||
onDragFinish={async (nameDragged, items) => {
|
||||
const keys = config.attributes!.map((e) => e.name);
|
||||
const newIndex = items.indexOf(nameDragged);
|
||||
const oldIndex = keys.indexOf(nameDragged);
|
||||
const dragged = config.attributes![oldIndex];
|
||||
if (!dragged.name) return;
|
||||
|
||||
executeMove(dragged, newIndex);
|
||||
}}
|
||||
columns={[
|
||||
{
|
||||
name: "name",
|
||||
displayKey: t("attributeName"),
|
||||
},
|
||||
{
|
||||
name: "displayName",
|
||||
displayKey: t("attributeDisplayName"),
|
||||
},
|
||||
{
|
||||
name: "group",
|
||||
displayKey: t("attributeGroup"),
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
displayKey: "",
|
||||
cellRenderer: (row) => (
|
||||
<Dropdown
|
||||
id={`${row.name}`}
|
||||
label={t("attributesDropdown")}
|
||||
data-testid="actions-dropdown"
|
||||
toggle={
|
||||
<KebabToggle
|
||||
onToggle={(status) =>
|
||||
setKebabOpen({
|
||||
status,
|
||||
rowKey: row.name!,
|
||||
})
|
||||
}
|
||||
id={`toggle-${row.name}`}
|
||||
/>
|
||||
}
|
||||
isOpen={kebabOpen.status && kebabOpen.rowKey === row.name}
|
||||
isPlain
|
||||
dropdownItems={[
|
||||
<DropdownItem
|
||||
key={`edit-dropdown-item-${row.name}`}
|
||||
data-testid="editDropdownAttributeItem"
|
||||
onClick={() => {
|
||||
setKebabOpen({
|
||||
status: false,
|
||||
rowKey: row.name!,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("common:edit")}
|
||||
</DropdownItem>,
|
||||
<Fragment key={`delete-dropdown-${row.name}`}>
|
||||
{row.name !== "email" && row.name !== "username"
|
||||
? [
|
||||
<DropdownItem
|
||||
key={`delete-dropdown-item-${row.name}`}
|
||||
data-testid="deleteDropdownAttributeItem"
|
||||
onClick={() => {
|
||||
toggleDeleteDialog();
|
||||
setAttributeToDelete({
|
||||
name: row.name!,
|
||||
});
|
||||
setKebabOpen({
|
||||
status: false,
|
||||
rowKey: row.name!,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("common:delete")}
|
||||
</DropdownItem>,
|
||||
]
|
||||
: []}
|
||||
</Fragment>,
|
||||
]}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
data={config.attributes!}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -9,6 +9,7 @@ import {
|
|||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
import { toUserProfile } from "../routes/UserProfile";
|
||||
import { AttributesGroupTab } from "./AttributesGroupTab";
|
||||
import { AttributesTab } from "./AttributesTab";
|
||||
import { JsonEditorTab } from "./JsonEditorTab";
|
||||
import { UserProfileProvider } from "./UserProfileContext";
|
||||
|
||||
|
@ -25,11 +26,14 @@ export const UserProfileTab = () => {
|
|||
>
|
||||
<Tab
|
||||
title={<TabTitleText>{t("attributes")}</TabTitleText>}
|
||||
data-testid="attributesTab"
|
||||
{...routableTab({
|
||||
to: toUserProfile({ realm, tab: "attributes" }),
|
||||
history,
|
||||
})}
|
||||
></Tab>
|
||||
>
|
||||
<AttributesTab />
|
||||
</Tab>
|
||||
<Tab
|
||||
title={<TabTitleText>{t("attributesGroup")}</TabTitleText>}
|
||||
data-testid="attributesGroupTab"
|
||||
|
@ -42,6 +46,7 @@ export const UserProfileTab = () => {
|
|||
</Tab>
|
||||
<Tab
|
||||
title={<TabTitleText>{t("jsonEditor")}</TabTitleText>}
|
||||
data-testid="jsonEditorTab"
|
||||
{...routableTab({
|
||||
to: toUserProfile({ realm, tab: "jsonEditor" }),
|
||||
history,
|
||||
|
|
Loading…
Reference in a new issue