Unify save-buttons in admin-ui (#30119)
* #30118 Unify save-buttons in admin-ui Signed-off-by: Andreas Blaettlinger <bln1imb@bosch.com> * #30118 Unify save-buttons in admin-ui Signed-off-by: Andreas Blaettlinger <bln1imb@bosch.com> * Introduced props for naming the buttons in FixedButtonGroup Signed-off-by: Andreas Blaettlinger <bln1imb@bosch.com> --------- Signed-off-by: Andreas Blaettlinger <bln1imb@bosch.com>
This commit is contained in:
parent
5ad3abaa96
commit
2a88d01e5e
9 changed files with 45 additions and 85 deletions
|
@ -1,5 +1,5 @@
|
||||||
export default class AttributesTab {
|
export default class AttributesTab {
|
||||||
#saveAttributeBtn = "save-attributes";
|
#saveAttributeBtn = "attributes-save";
|
||||||
#attributesTab = "attributes";
|
#attributesTab = "attributes";
|
||||||
#emptyState = "attributes-empty-state";
|
#emptyState = "attributes-empty-state";
|
||||||
#addAttributeBtn: string;
|
#addAttributeBtn: string;
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default class KeyValueInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
cy.findByTestId("save-attributes").click();
|
cy.findByTestId("attributes-save").click();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ enum RealmSettingsTab {
|
||||||
|
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
export default class RealmSettingsPage extends CommonPage {
|
export default class RealmSettingsPage extends CommonPage {
|
||||||
generalSaveBtn = "general-tab-save";
|
generalSaveBtn = "realmSettingsGeneralTab-save";
|
||||||
generalRevertBtn = "general-tab-revert";
|
generalRevertBtn = "realmSettingsGeneralTab-revert";
|
||||||
themesSaveBtn = "themes-tab-save";
|
themesSaveBtn = "themes-tab-save";
|
||||||
loginTab = "rs-login-tab";
|
loginTab = "rs-login-tab";
|
||||||
emailTab = "rs-email-tab";
|
emailTab = "rs-email-tab";
|
||||||
|
|
|
@ -21,9 +21,9 @@ export default class CreateUserPage {
|
||||||
this.addUserBtn = "add-user";
|
this.addUserBtn = "add-user";
|
||||||
this.joinGroupsBtn = "join-groups-button";
|
this.joinGroupsBtn = "join-groups-button";
|
||||||
this.joinBtn = "join-button";
|
this.joinBtn = "join-button";
|
||||||
this.createBtn = "create-user";
|
this.createBtn = "user-creation-save";
|
||||||
this.saveBtn = "save-user";
|
this.saveBtn = "user-creation-save";
|
||||||
this.cancelBtn = "cancel-create-user";
|
this.cancelBtn = "user-creation-revert";
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region General Settings
|
//#region General Settings
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default class UserDetailsPage extends PageObject {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.saveBtn = "save-user";
|
this.saveBtn = "user-creation-save";
|
||||||
this.cancelBtn = "cancel-create-user";
|
this.cancelBtn = "cancel-create-user";
|
||||||
this.emailInput = "email";
|
this.emailInput = "email";
|
||||||
this.emailValue = () => "example" + "_" + uuid() + "@example.com";
|
this.emailValue = () => "example" + "_" + uuid() + "@example.com";
|
||||||
|
|
|
@ -7,7 +7,9 @@ import style from "./fixed-buttons.module.css";
|
||||||
type FixedButtonGroupProps = ActionGroupProps & {
|
type FixedButtonGroupProps = ActionGroupProps & {
|
||||||
name: string;
|
name: string;
|
||||||
save?: () => void;
|
save?: () => void;
|
||||||
|
saveText?: string;
|
||||||
reset?: () => void;
|
reset?: () => void;
|
||||||
|
resetText?: string;
|
||||||
isSubmit?: boolean;
|
isSubmit?: boolean;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
};
|
};
|
||||||
|
@ -15,7 +17,9 @@ type FixedButtonGroupProps = ActionGroupProps & {
|
||||||
export const FixedButtonsGroup = ({
|
export const FixedButtonsGroup = ({
|
||||||
name,
|
name,
|
||||||
save,
|
save,
|
||||||
|
saveText,
|
||||||
reset,
|
reset,
|
||||||
|
resetText,
|
||||||
isSubmit = false,
|
isSubmit = false,
|
||||||
isActive = true,
|
isActive = true,
|
||||||
children,
|
children,
|
||||||
|
@ -31,7 +35,7 @@ export const FixedButtonsGroup = ({
|
||||||
onClick={() => save?.()}
|
onClick={() => save?.()}
|
||||||
type={isSubmit ? "submit" : "button"}
|
type={isSubmit ? "submit" : "button"}
|
||||||
>
|
>
|
||||||
{t("save")}
|
{!saveText ? t("save") : saveText}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{reset && (
|
{reset && (
|
||||||
|
@ -41,7 +45,7 @@ export const FixedButtonsGroup = ({
|
||||||
variant="link"
|
variant="link"
|
||||||
onClick={() => reset()}
|
onClick={() => reset()}
|
||||||
>
|
>
|
||||||
{t("revert")}
|
{!resetText ? t("revert") : resetText}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
||||||
import { ActionGroup, Button } from "@patternfly/react-core";
|
|
||||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { FormAccess } from "../form/FormAccess";
|
import { FormAccess } from "../form/FormAccess";
|
||||||
import type { KeyValueType } from "./key-value-convert";
|
import type { KeyValueType } from "./key-value-convert";
|
||||||
import { KeyValueInput } from "./KeyValueInput";
|
import { KeyValueInput } from "./KeyValueInput";
|
||||||
|
import { FixedButtonsGroup } from "../form/FixedButtonGroup";
|
||||||
|
|
||||||
export type AttributeForm = Omit<RoleRepresentation, "attributes"> & {
|
export type AttributeForm = Omit<RoleRepresentation, "attributes"> & {
|
||||||
attributes?: KeyValueType[];
|
attributes?: KeyValueType[];
|
||||||
|
@ -28,12 +27,8 @@ export const AttributesForm = ({
|
||||||
name = "attributes",
|
name = "attributes",
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
}: AttributesFormProps) => {
|
}: AttributesFormProps) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const noSaveCancelButtons = !save && !reset;
|
const noSaveCancelButtons = !save && !reset;
|
||||||
const {
|
const { handleSubmit } = form;
|
||||||
formState: { isDirty },
|
|
||||||
handleSubmit,
|
|
||||||
} = form;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormAccess
|
<FormAccess
|
||||||
|
@ -45,19 +40,7 @@ export const AttributesForm = ({
|
||||||
<KeyValueInput name={name} isDisabled={isDisabled} />
|
<KeyValueInput name={name} isDisabled={isDisabled} />
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
{!noSaveCancelButtons && (
|
{!noSaveCancelButtons && (
|
||||||
<ActionGroup className="kc-attributes__action-group">
|
<FixedButtonsGroup name="attributes" reset={reset} isActive isSubmit />
|
||||||
<Button
|
|
||||||
data-testid="save-attributes"
|
|
||||||
variant="primary"
|
|
||||||
type="submit"
|
|
||||||
isDisabled={!isDirty}
|
|
||||||
>
|
|
||||||
{t("save")}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={reset} variant="link" isDisabled={!isDirty}>
|
|
||||||
{t("revert")}
|
|
||||||
</Button>
|
|
||||||
</ActionGroup>
|
|
||||||
)}
|
)}
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,8 +4,6 @@ import {
|
||||||
UserProfileConfig,
|
UserProfileConfig,
|
||||||
} from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata";
|
} from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
|
||||||
Button,
|
|
||||||
ClipboardCopy,
|
ClipboardCopy,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
PageSection,
|
PageSection,
|
||||||
|
@ -27,6 +25,7 @@ import { FormattedLink } from "../components/external-link/FormattedLink";
|
||||||
import { FormAccess } from "../components/form/FormAccess";
|
import { FormAccess } from "../components/form/FormAccess";
|
||||||
import { KeyValueInput } from "../components/key-value-form/KeyValueInput";
|
import { KeyValueInput } from "../components/key-value-form/KeyValueInput";
|
||||||
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
||||||
|
import { FixedButtonsGroup } from "../components/form/FixedButtonGroup";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import {
|
import {
|
||||||
addTrailingSlash,
|
addTrailingSlash,
|
||||||
|
@ -105,7 +104,7 @@ function RealmSettingsGeneralTabForm({
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
setValue,
|
setValue,
|
||||||
formState: { isDirty, errors },
|
formState: { errors },
|
||||||
} = form;
|
} = form;
|
||||||
const isFeatureEnabled = useIsFeatureEnabled();
|
const isFeatureEnabled = useIsFeatureEnabled();
|
||||||
const isOrganizationsEnabled = isFeatureEnabled(Feature.Organizations);
|
const isOrganizationsEnabled = isFeatureEnabled(Feature.Organizations);
|
||||||
|
@ -266,23 +265,12 @@ function RealmSettingsGeneralTabForm({
|
||||||
</StackItem>
|
</StackItem>
|
||||||
</Stack>
|
</Stack>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<ActionGroup>
|
<FixedButtonsGroup
|
||||||
<Button
|
name="realmSettingsGeneralTab"
|
||||||
variant="primary"
|
reset={setupForm}
|
||||||
type="submit"
|
isActive
|
||||||
data-testid="general-tab-save"
|
isSubmit
|
||||||
isDisabled={!isDirty}
|
/>
|
||||||
>
|
|
||||||
{t("save")}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
data-testid="general-tab-revert"
|
|
||||||
variant="link"
|
|
||||||
onClick={setupForm}
|
|
||||||
>
|
|
||||||
{t("revert")}
|
|
||||||
</Button>
|
|
||||||
</ActionGroup>
|
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
|
|
|
@ -4,14 +4,12 @@ import { UserProfileMetadata } from "@keycloak/keycloak-admin-client/lib/defs/us
|
||||||
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
|
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
|
||||||
import {
|
import {
|
||||||
FormErrorText,
|
FormErrorText,
|
||||||
FormSubmitButton,
|
|
||||||
HelpItem,
|
HelpItem,
|
||||||
SwitchControl,
|
SwitchControl,
|
||||||
TextControl,
|
TextControl,
|
||||||
UserProfileFields,
|
UserProfileFields,
|
||||||
} from "@keycloak/keycloak-ui-shared";
|
} from "@keycloak/keycloak-ui-shared";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
Button,
|
Button,
|
||||||
Chip,
|
Chip,
|
||||||
|
@ -26,7 +24,6 @@ import { TFunction } from "i18next";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, FormProvider, UseFormReturn } from "react-hook-form";
|
import { Controller, FormProvider, UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { useAdminClient } from "../admin-client";
|
import { useAdminClient } from "../admin-client";
|
||||||
import { DefaultSwitchControl } from "../components/SwitchControl";
|
import { DefaultSwitchControl } from "../components/SwitchControl";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
@ -39,7 +36,9 @@ import useFormatDate from "../utils/useFormatDate";
|
||||||
import { FederatedUserLink } from "./FederatedUserLink";
|
import { FederatedUserLink } from "./FederatedUserLink";
|
||||||
import { UserFormFields, toUserFormFields } from "./form-state";
|
import { UserFormFields, toUserFormFields } from "./form-state";
|
||||||
import { toUsers } from "./routes/Users";
|
import { toUsers } from "./routes/Users";
|
||||||
|
import { FixedButtonsGroup } from "../components/form/FixedButtonGroup";
|
||||||
import { RequiredActionMultiSelect } from "./user-credentials/RequiredActionMultiSelect";
|
import { RequiredActionMultiSelect } from "./user-credentials/RequiredActionMultiSelect";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export type BruteForced = {
|
export type BruteForced = {
|
||||||
isBruteForceProtected?: boolean;
|
isBruteForceProtected?: boolean;
|
||||||
|
@ -79,15 +78,15 @@ export const UserForm = ({
|
||||||
const { whoAmI } = useWhoAmI();
|
const { whoAmI } = useWhoAmI();
|
||||||
const currentLocale = whoAmI.getLocale();
|
const currentLocale = whoAmI.getLocale();
|
||||||
|
|
||||||
const { handleSubmit, setValue, watch, control, reset, formState } = form;
|
const { handleSubmit, setValue, control, reset, formState } = form;
|
||||||
const { errors } = formState;
|
const { errors } = formState;
|
||||||
|
|
||||||
const watchUsernameInput = watch("username");
|
|
||||||
const [selectedGroups, setSelectedGroups] = useState<GroupRepresentation[]>(
|
const [selectedGroups, setSelectedGroups] = useState<GroupRepresentation[]>(
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [locked, setLocked] = useState(isLocked);
|
const [locked, setLocked] = useState(isLocked);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue("requiredActions", user?.requiredActions || []);
|
setValue("requiredActions", user?.requiredActions || []);
|
||||||
|
@ -132,6 +131,14 @@ export const UserForm = ({
|
||||||
setOpen(!open);
|
setOpen(!open);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onFormReset = () => {
|
||||||
|
if (user?.id) {
|
||||||
|
reset(toUserFormFields(user));
|
||||||
|
} else {
|
||||||
|
navigate(toUsers({ realm: realm.realm! }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormAccess
|
<FormAccess
|
||||||
isHorizontal
|
isHorizontal
|
||||||
|
@ -327,37 +334,15 @@ export const UserForm = ({
|
||||||
)}
|
)}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ActionGroup>
|
|
||||||
<FormSubmitButton
|
|
||||||
formState={formState}
|
|
||||||
data-testid={!user?.id ? "create-user" : "save-user"}
|
|
||||||
isDisabled={
|
|
||||||
!user?.id &&
|
|
||||||
!watchUsernameInput &&
|
|
||||||
realm.registrationEmailAsUsername === false
|
|
||||||
}
|
|
||||||
allowNonDirty
|
|
||||||
allowInvalid
|
|
||||||
>
|
|
||||||
{user?.id ? t("save") : t("create")}
|
|
||||||
</FormSubmitButton>
|
|
||||||
<Button
|
|
||||||
data-testid="cancel-create-user"
|
|
||||||
variant="link"
|
|
||||||
onClick={user?.id ? () => reset(toUserFormFields(user)) : undefined}
|
|
||||||
component={
|
|
||||||
!user?.id
|
|
||||||
? (props) => (
|
|
||||||
<Link {...props} to={toUsers({ realm: realm.realm! })} />
|
|
||||||
)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{user?.id ? t("revert") : t("cancel")}
|
|
||||||
</Button>
|
|
||||||
</ActionGroup>
|
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
<FixedButtonsGroup
|
||||||
|
name="user-creation"
|
||||||
|
saveText={user?.id ? t("save") : t("create")}
|
||||||
|
reset={onFormReset}
|
||||||
|
resetText={user?.id ? t("revert") : t("cancel")}
|
||||||
|
isActive
|
||||||
|
isSubmit
|
||||||
|
/>
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue