Remove useless React fragments (#1062)

This commit is contained in:
Jon Koops 2021-08-26 14:15:28 +02:00 committed by GitHub
parent b15c240d9e
commit e062603ff2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
62 changed files with 3157 additions and 3328 deletions

View file

@ -30,6 +30,8 @@ module.exports = {
// react/prop-types cannot handle generic props, so we need to disable it. // react/prop-types cannot handle generic props, so we need to disable it.
// https://github.com/yannickcr/eslint-plugin-react/issues/2777#issuecomment-814968432 // https://github.com/yannickcr/eslint-plugin-react/issues/2777#issuecomment-814968432
"react/prop-types": "off", "react/prop-types": "off",
// Prevent fragments from being added that have only a single child.
"react/jsx-no-useless-fragment": "error"
}, },
overrides: [ overrides: [
{ {

View file

@ -27,37 +27,27 @@ export const Header = () => {
const adminClient = useAdminClient(); const adminClient = useAdminClient();
const { t } = useTranslation(); const { t } = useTranslation();
const ManageAccountDropdownItem = () => { const ManageAccountDropdownItem = () =>
return ( adminClient.keycloak ? (
<> <DropdownItem
{adminClient.keycloak && ( key="manage account"
<DropdownItem id="manage-account"
key="manage account" onClick={() => adminClient.keycloak?.accountManagement()}
id="manage-account" >
onClick={() => adminClient.keycloak?.accountManagement()} {t("manageAccount")}
> </DropdownItem>
{t("manageAccount")} ) : null;
</DropdownItem>
)}
</>
);
};
const SignOutDropdownItem = () => { const SignOutDropdownItem = () =>
return ( adminClient.keycloak ? (
<> <DropdownItem
{adminClient.keycloak && ( id="sign-out"
<DropdownItem key="sign out"
id="sign-out" onClick={() => adminClient.keycloak?.logout({ redirectUri: "" })}
key="sign out" >
onClick={() => adminClient.keycloak?.logout({ redirectUri: "" })} {t("signOut")}
> </DropdownItem>
{t("signOut")} ) : null;
</DropdownItem>
)}
</>
);
};
const ServerInfoDropdownItem = () => { const ServerInfoDropdownItem = () => {
const { realm } = useRealm(); const { realm } = useRealm();

View file

@ -107,22 +107,20 @@ export const ClientScopesSection = () => {
}); });
const TypeSelector = (scope: ClientScopeDefaultOptionalType) => ( const TypeSelector = (scope: ClientScopeDefaultOptionalType) => (
<> <CellDropdown
<CellDropdown clientScope={scope}
clientScope={scope} type={scope.type}
type={scope.type} all
all onSelect={async (value) => {
onSelect={async (value) => { try {
try { await changeScope(adminClient, scope, value);
await changeScope(adminClient, scope, value); addAlert(t("clientScopeSuccess"), AlertVariant.success);
addAlert(t("clientScopeSuccess"), AlertVariant.success); refresh();
refresh(); } catch (error) {
} catch (error) { addError("client-scopes:clientScopeError", error);
addError("client-scopes:clientScopeError", error); }
} }}
}} />
/>
</>
); );
const ClientScopeDetailLink = ({ const ClientScopeDetailLink = ({
@ -130,14 +128,12 @@ export const ClientScopesSection = () => {
type, type,
name, name,
}: ClientScopeDefaultOptionalType) => ( }: ClientScopeDefaultOptionalType) => (
<> <Link
<Link key={id}
key={id} to={toClientScope({ realm, id: id!, type, tab: "settings" })}
to={toClientScope({ realm, id: id!, type, tab: "settings" })} >
> {name}
{name} </Link>
</Link>
</>
); );
return ( return (
<> <>

View file

@ -128,10 +128,10 @@ export const AddMapperDialog = (props: AddMapperDialogProps) => {
<DataListItemCells <DataListItemCells
dataListCells={[ dataListCells={[
<DataListCell key={`name-${mapper.id}`}> <DataListCell key={`name-${mapper.id}`}>
<>{mapper.name}</> {mapper.name}
</DataListCell>, </DataListCell>,
<DataListCell key={`helpText-${mapper.id}`}> <DataListCell key={`helpText-${mapper.id}`}>
<>{mapper.helpText}</> {mapper.helpText}
</DataListCell>, </DataListCell>,
]} ]}
/> />

View file

@ -99,11 +99,9 @@ export const MapperList = ({ clientScope, refresh }: MapperListProps) => {
); );
const MapperLink = ({ id, name }: Row) => ( const MapperLink = ({ id, name }: Row) => (
<> <Link to={toMapper({ realm, id: clientScope.id!, mapperId: id! })}>
<Link to={toMapper({ realm, id: clientScope.id!, mapperId: id! })}> {name}
{name} </Link>
</Link>
</>
); );
return ( return (

View file

@ -164,40 +164,36 @@ export const MappingDetails = () => {
onSubmit={handleSubmit(save)} onSubmit={handleSubmit(save)}
role="manage-clients" role="manage-clients"
> >
<> {!mapperId.match(isGuid) && (
{!mapperId.match(isGuid) && ( <FormGroup
<FormGroup label={t("common:name")}
label={t("common:name")} labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText="client-scopes-help:mapperName"
helpText="client-scopes-help:mapperName" forLabel={t("common:name")}
forLabel={t("common:name")} forID="name"
forID="name" />
/> }
} fieldId="name"
fieldId="name" isRequired
isRequired validated={
errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={register({ required: true })}
type="text"
id="name"
name="name"
validated={ validated={
errors.name errors.name
? ValidatedOptions.error ? ValidatedOptions.error
: ValidatedOptions.default : ValidatedOptions.default
} }
helperTextInvalid={t("common:required")} />
> </FormGroup>
<TextInput )}
ref={register({ required: true })}
type="text"
id="name"
name="name"
validated={
errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
</FormGroup>
)}
</>
<FormGroup <FormGroup
label={t("realmRolePrefix")} label={t("realmRolePrefix")}
labelIcon={ labelIcon={

View file

@ -38,217 +38,215 @@ export const ClientSettings = ({ save, reset }: ClientSettingsProps) => {
); );
return ( return (
<> <ScrollForm
<ScrollForm className="pf-u-px-lg"
className="pf-u-px-lg" sections={[
sections={[ t("generalSettings"),
t("generalSettings"), t("capabilityConfig"),
t("capabilityConfig"), t("accessSettings"),
t("accessSettings"), t("loginSettings"),
t("loginSettings"), ]}
]} >
> <Form isHorizontal>
<Form isHorizontal> <ClientDescription />
<ClientDescription /> </Form>
</Form> <CapabilityConfig />
<CapabilityConfig /> <FormAccess isHorizontal role="manage-clients">
<FormAccess isHorizontal role="manage-clients"> <FormGroup
<FormGroup label={t("rootUrl")}
label={t("rootUrl")} fieldId="kc-root-url"
fieldId="kc-root-url" labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText="clients-help:rootUrl"
helpText="clients-help:rootUrl" forLabel={t("rootUrl")}
forLabel={t("rootUrl")} forID="kc-root-url"
forID="kc-root-url"
/>
}
>
<TextInput
type="text"
id="kc-root-url"
name="rootUrl"
ref={register}
/> />
</FormGroup> }
<FormGroup >
label={t("validRedirectUri")} <TextInput
fieldId="kc-redirect" type="text"
labelIcon={ id="kc-root-url"
<HelpItem name="rootUrl"
helpText="clients-help:validRedirectURIs" ref={register}
forLabel={t("validRedirectUri")}
forID={t(`common:helpLabel`, { label: t("validRedirectUri") })}
/>
}
>
<MultiLineInput
name="redirectUris"
aria-label={t("validRedirectUri")}
addButtonLabel="clients:addRedirectUri"
/>
</FormGroup>
<FormGroup
label={t("homeURL")}
fieldId="kc-home-url"
labelIcon={
<HelpItem
helpText="clients-help:homeURL"
forLabel={t("homeURL")}
forID={t(`common:helpLabel`, { label: t("homeURL") })}
/>
}
>
<TextInput
type="text"
id="kc-home-url"
name="baseUrl"
ref={register}
/>
</FormGroup>
<FormGroup
label={t("webOrigins")}
fieldId="kc-web-origins"
labelIcon={
<HelpItem
helpText="clients-help:webOrigins"
forLabel={t("webOrigins")}
forID={t(`common:helpLabel`, { label: t("webOrigins") })}
/>
}
>
<MultiLineInput
name="webOrigins"
aria-label={t("webOrigins")}
addButtonLabel="clients:addWebOrigins"
/>
</FormGroup>
<FormGroup
label={t("adminURL")}
fieldId="kc-admin-url"
labelIcon={
<HelpItem
helpText="clients-help:adminURL"
forLabel={t("adminURL")}
forID="kc-admin-url"
/>
}
>
<TextInput
type="text"
id="kc-admin-url"
name="adminUrl"
ref={register}
/>
</FormGroup>
</FormAccess>
<FormAccess isHorizontal role="manage-clients">
<FormGroup
label={t("loginTheme")}
labelIcon={
<HelpItem
helpText="clients-help:loginTheme"
forLabel={t("loginTheme")}
forID="loginTheme"
/>
}
fieldId="loginTheme"
>
<Controller
name="attributes.login_theme"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
toggleId="loginTheme"
onToggle={() => setLoginThemeOpen(!loginThemeOpen)}
onSelect={(_, value) => {
onChange(value as string);
setLoginThemeOpen(false);
}}
selections={value || t("common:choose")}
variant={SelectVariant.single}
aria-label={t("loginTheme")}
isOpen={loginThemeOpen}
>
<SelectOption key="empty" value="">
{t("common:choose")}
</SelectOption>
<>
{loginThemes?.map((theme) => (
<SelectOption
selected={theme.name === value}
key={theme.name}
value={theme.name}
/>
))}
</>
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("consentRequired")}
fieldId="kc-consent"
hasNoPaddingTop
>
<Controller
name="consentRequired"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch
id="kc-consent-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value}
onChange={onChange}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("displayOnClient")}
fieldId="kc-display-on-client"
hasNoPaddingTop
>
<Controller
name="attributes.display-on-consent-screen"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch
id="kc-display-on-client-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange("" + value)}
isDisabled={!consentRequired}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("consentScreenText")}
fieldId="kc-consent-screen-text"
>
<TextArea
id="kc-consent-screen-text"
name="attributes.consent-screen-text"
ref={register}
isDisabled={
!(consentRequired && displayOnConsentScreen === "true")
}
/>
</FormGroup>
<SaveReset
className="keycloak__form_actions"
name="settings"
save={save}
reset={reset}
/> />
</FormAccess> </FormGroup>
</ScrollForm> <FormGroup
</> label={t("validRedirectUri")}
fieldId="kc-redirect"
labelIcon={
<HelpItem
helpText="clients-help:validRedirectURIs"
forLabel={t("validRedirectUri")}
forID={t(`common:helpLabel`, { label: t("validRedirectUri") })}
/>
}
>
<MultiLineInput
name="redirectUris"
aria-label={t("validRedirectUri")}
addButtonLabel="clients:addRedirectUri"
/>
</FormGroup>
<FormGroup
label={t("homeURL")}
fieldId="kc-home-url"
labelIcon={
<HelpItem
helpText="clients-help:homeURL"
forLabel={t("homeURL")}
forID={t(`common:helpLabel`, { label: t("homeURL") })}
/>
}
>
<TextInput
type="text"
id="kc-home-url"
name="baseUrl"
ref={register}
/>
</FormGroup>
<FormGroup
label={t("webOrigins")}
fieldId="kc-web-origins"
labelIcon={
<HelpItem
helpText="clients-help:webOrigins"
forLabel={t("webOrigins")}
forID={t(`common:helpLabel`, { label: t("webOrigins") })}
/>
}
>
<MultiLineInput
name="webOrigins"
aria-label={t("webOrigins")}
addButtonLabel="clients:addWebOrigins"
/>
</FormGroup>
<FormGroup
label={t("adminURL")}
fieldId="kc-admin-url"
labelIcon={
<HelpItem
helpText="clients-help:adminURL"
forLabel={t("adminURL")}
forID="kc-admin-url"
/>
}
>
<TextInput
type="text"
id="kc-admin-url"
name="adminUrl"
ref={register}
/>
</FormGroup>
</FormAccess>
<FormAccess isHorizontal role="manage-clients">
<FormGroup
label={t("loginTheme")}
labelIcon={
<HelpItem
helpText="clients-help:loginTheme"
forLabel={t("loginTheme")}
forID="loginTheme"
/>
}
fieldId="loginTheme"
>
<Controller
name="attributes.login_theme"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
toggleId="loginTheme"
onToggle={() => setLoginThemeOpen(!loginThemeOpen)}
onSelect={(_, value) => {
onChange(value as string);
setLoginThemeOpen(false);
}}
selections={value || t("common:choose")}
variant={SelectVariant.single}
aria-label={t("loginTheme")}
isOpen={loginThemeOpen}
>
<SelectOption key="empty" value="">
{t("common:choose")}
</SelectOption>
{/* The type for the children of Select are incorrect, so we need a fragment here. */}
{/* eslint-disable-next-line react/jsx-no-useless-fragment */}
<>
{loginThemes?.map((theme) => (
<SelectOption
selected={theme.name === value}
key={theme.name}
value={theme.name}
/>
))}
</>
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("consentRequired")}
fieldId="kc-consent"
hasNoPaddingTop
>
<Controller
name="consentRequired"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch
id="kc-consent-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value}
onChange={onChange}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("displayOnClient")}
fieldId="kc-display-on-client"
hasNoPaddingTop
>
<Controller
name="attributes.display-on-consent-screen"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch
id="kc-display-on-client-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange("" + value)}
isDisabled={!consentRequired}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("consentScreenText")}
fieldId="kc-consent-screen-text"
>
<TextArea
id="kc-consent-screen-text"
name="attributes.consent-screen-text"
ref={register}
isDisabled={!(consentRequired && displayOnConsentScreen === "true")}
/>
</FormGroup>
<SaveReset
className="keycloak__form_actions"
name="settings"
save={save}
reset={reset}
/>
</FormAccess>
</ScrollForm>
); );
}; };

View file

@ -70,27 +70,23 @@ export const ClientsSection = () => {
}); });
const ClientDetailLink = (client: ClientRepresentation) => ( const ClientDetailLink = (client: ClientRepresentation) => (
<> <Link
<Link key={client.id}
key={client.id} to={toClient({ realm, clientId: client.id!, tab: "settings" })}
to={toClient({ realm, clientId: client.id!, tab: "settings" })} >
> {client.clientId}
{client.clientId} {!client.enabled && (
{!client.enabled && ( <Badge key={`${client.id}-disabled`} isRead className="pf-u-ml-sm">
<Badge key={`${client.id}-disabled`} isRead className="pf-u-ml-sm"> {t("common:disabled")}
{t("common:disabled")} </Badge>
</Badge> )}
)} </Link>
</Link>
</>
); );
const ClientDescription = (client: ClientRepresentation) => ( const ClientDescription = (client: ClientRepresentation) => (
<> <TableText wrapModifier="truncate">
<TableText wrapModifier="truncate"> {emptyFormatter()(client.description)}
{emptyFormatter()(client.description)} </TableText>
</TableText>
</>
); );
return ( return (

View file

@ -38,264 +38,260 @@ export const CapabilityConfig = ({
unWrap={unWrap} unWrap={unWrap}
className="keycloak__capability-config__form" className="keycloak__capability-config__form"
> >
<> {protocol === "openid-connect" && (
{protocol === "openid-connect" && ( <>
<> <FormGroup
<FormGroup hasNoPaddingTop
hasNoPaddingTop label={t("clientAuthentication")}
label={t("clientAuthentication")} fieldId="kc-authentication"
fieldId="kc-authentication" labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText="clients-help:authentication"
helpText="clients-help:authentication" forLabel={t("authentication")}
forLabel={t("authentication")} forID={t(`common:helpLabel`, { label: t("authentication") })}
forID={t(`common:helpLabel`, { label: t("authentication") })}
/>
}
>
<Controller
name="publicClient"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<Switch
data-testid="authentication"
id="kc-authentication-switch"
name="publicClient"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={!value}
onChange={(value) => {
onChange(!value);
if (!value) {
setValue("authorizationServicesEnabled", false);
setValue("serviceAccountsEnabled", false);
}
}}
/>
)}
/> />
</FormGroup> }
<FormGroup >
hasNoPaddingTop <Controller
label={t("clientAuthorization")} name="publicClient"
fieldId="kc-authorization" defaultValue={false}
labelIcon={ control={control}
<HelpItem render={({ onChange, value }) => (
helpText="clients-help:authorization" <Switch
forLabel={t("authorization")} data-testid="authentication"
forID={t(`common:helpLabel`, { label: t("authorization") })} id="kc-authentication-switch"
name="publicClient"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={!value}
onChange={(value) => {
onChange(!value);
if (!value) {
setValue("authorizationServicesEnabled", false);
setValue("serviceAccountsEnabled", false);
}
}}
/> />
} )}
> />
<Controller </FormGroup>
name="authorizationServicesEnabled" <FormGroup
defaultValue={false} hasNoPaddingTop
control={control} label={t("clientAuthorization")}
render={({ onChange, value }) => ( fieldId="kc-authorization"
<Switch labelIcon={
data-testid="authorization" <HelpItem
id="kc-authorization-switch" helpText="clients-help:authorization"
name="authorizationServicesEnabled" forLabel={t("authorization")}
label={t("common:on")} forID={t(`common:helpLabel`, { label: t("authorization") })}
labelOff={t("common:off")}
isChecked={value && !clientAuthentication}
onChange={(value) => {
onChange(value);
if (value) {
setValue("serviceAccountsEnabled", true);
}
}}
isDisabled={clientAuthentication}
/>
)}
/> />
</FormGroup> }
<FormGroup >
hasNoPaddingTop <Controller
label={t("authenticationFlow")} name="authorizationServicesEnabled"
fieldId="kc-flow" defaultValue={false}
> control={control}
<Grid> render={({ onChange, value }) => (
<GridItem lg={4} sm={6}> <Switch
<Controller data-testid="authorization"
name="standardFlowEnabled" id="kc-authorization-switch"
defaultValue={true} name="authorizationServicesEnabled"
control={control} label={t("common:on")}
render={({ onChange, value }) => ( labelOff={t("common:off")}
<InputGroup> isChecked={value && !clientAuthentication}
<Checkbox onChange={(value) => {
data-testid="standard" onChange(value);
label={t("standardFlow")} if (value) {
id="kc-flow-standard" setValue("serviceAccountsEnabled", true);
name="standardFlowEnabled" }
isChecked={value} }}
onChange={onChange} isDisabled={clientAuthentication}
/>
<HelpItem
helpText="clients-help:standardFlow"
forLabel={t("standardFlow")}
forID={t(`common:helpLabel`, {
label: t("standardFlow"),
})}
/>
</InputGroup>
)}
/>
</GridItem>
<GridItem lg={8} sm={6}>
<Controller
name="directAccessGrantsEnabled"
defaultValue={true}
control={control}
render={({ onChange, value }) => (
<InputGroup>
<Checkbox
data-testid="direct"
label={t("directAccess")}
id="kc-flow-direct"
name="directAccessGrantsEnabled"
isChecked={value}
onChange={onChange}
/>
<HelpItem
helpText="clients-help:directAccess"
forLabel={t("directAccess")}
forID={t(`common:helpLabel`, {
label: t("directAccess"),
})}
/>
</InputGroup>
)}
/>
</GridItem>
<GridItem lg={4} sm={6}>
<Controller
name="implicitFlowEnabled"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<InputGroup>
<Checkbox
data-testid="implicit"
label={t("implicitFlow")}
id="kc-flow-implicit"
name="implicitFlowEnabled"
isChecked={value}
onChange={onChange}
/>
<HelpItem
helpText="clients-help:implicitFlow"
forLabel={t("implicitFlow")}
forID={t(`common:helpLabel`, {
label: t("implicitFlow"),
})}
/>
</InputGroup>
)}
/>
</GridItem>
<GridItem lg={8} sm={6}>
<Controller
name="serviceAccountsEnabled"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<InputGroup>
<Checkbox
data-testid="service-account"
label={t("serviceAccount")}
id="kc-flow-service-account"
name="serviceAccountsEnabled"
isChecked={
value || (clientAuthentication && authorization)
}
onChange={onChange}
isDisabled={
(clientAuthentication && !authorization) ||
(!clientAuthentication && authorization)
}
/>
<HelpItem
helpText="clients-help:serviceAccount"
forLabel={t("serviceAccount")}
forID={t(`common:helpLabel`, {
label: t("serviceAccount"),
})}
/>
</InputGroup>
)}
/>
</GridItem>
</Grid>
</FormGroup>
</>
)}
</>
<>
{protocol === "saml" && (
<>
<FormGroup
labelIcon={
<HelpItem
helpText="clients-help:encryptAssertions"
forLabel={t("encryptAssertions")}
forID={t(`common:helpLabel`, {
label: t("encryptAssertions"),
})}
/> />
} )}
label={t("encryptAssertions")} />
fieldId="kc-encrypt" </FormGroup>
hasNoPaddingTop <FormGroup
> hasNoPaddingTop
<Controller label={t("authenticationFlow")}
name="attributes.saml_encrypt" fieldId="kc-flow"
control={control} >
defaultValue="false" <Grid>
render={({ onChange, value }) => ( <GridItem lg={4} sm={6}>
<Switch <Controller
data-testid="encrypt" name="standardFlowEnabled"
id="kc-encrypt" defaultValue={true}
label={t("common:on")} control={control}
labelOff={t("common:off")} render={({ onChange, value }) => (
isChecked={value === "true"} <InputGroup>
onChange={(value) => onChange("" + value)} <Checkbox
/> data-testid="standard"
)} label={t("standardFlow")}
/> id="kc-flow-standard"
</FormGroup> name="standardFlowEnabled"
<FormGroup isChecked={value}
labelIcon={ onChange={onChange}
<HelpItem />
helpText="clients-help:clientSignature" <HelpItem
forLabel={t("clientSignature")} helpText="clients-help:standardFlow"
forID={t(`common:helpLabel`, { label: t("clientSignature") })} forLabel={t("standardFlow")}
forID={t(`common:helpLabel`, {
label: t("standardFlow"),
})}
/>
</InputGroup>
)}
/> />
} </GridItem>
label={t("clientSignature")} <GridItem lg={8} sm={6}>
fieldId="kc-client-signature" <Controller
hasNoPaddingTop name="directAccessGrantsEnabled"
> defaultValue={true}
<Controller control={control}
name="attributes.saml_client_signature" render={({ onChange, value }) => (
control={control} <InputGroup>
defaultValue="false" <Checkbox
render={({ onChange, value }) => ( data-testid="direct"
<Switch label={t("directAccess")}
data-testid="client-signature" id="kc-flow-direct"
id="kc-client-signature" name="directAccessGrantsEnabled"
label={t("common:on")} isChecked={value}
labelOff={t("common:off")} onChange={onChange}
isChecked={value === "true"} />
onChange={(value) => onChange("" + value)} <HelpItem
/> helpText="clients-help:directAccess"
)} forLabel={t("directAccess")}
forID={t(`common:helpLabel`, {
label: t("directAccess"),
})}
/>
</InputGroup>
)}
/>
</GridItem>
<GridItem lg={4} sm={6}>
<Controller
name="implicitFlowEnabled"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<InputGroup>
<Checkbox
data-testid="implicit"
label={t("implicitFlow")}
id="kc-flow-implicit"
name="implicitFlowEnabled"
isChecked={value}
onChange={onChange}
/>
<HelpItem
helpText="clients-help:implicitFlow"
forLabel={t("implicitFlow")}
forID={t(`common:helpLabel`, {
label: t("implicitFlow"),
})}
/>
</InputGroup>
)}
/>
</GridItem>
<GridItem lg={8} sm={6}>
<Controller
name="serviceAccountsEnabled"
defaultValue={false}
control={control}
render={({ onChange, value }) => (
<InputGroup>
<Checkbox
data-testid="service-account"
label={t("serviceAccount")}
id="kc-flow-service-account"
name="serviceAccountsEnabled"
isChecked={
value || (clientAuthentication && authorization)
}
onChange={onChange}
isDisabled={
(clientAuthentication && !authorization) ||
(!clientAuthentication && authorization)
}
/>
<HelpItem
helpText="clients-help:serviceAccount"
forLabel={t("serviceAccount")}
forID={t(`common:helpLabel`, {
label: t("serviceAccount"),
})}
/>
</InputGroup>
)}
/>
</GridItem>
</Grid>
</FormGroup>
</>
)}
{protocol === "saml" && (
<>
<FormGroup
labelIcon={
<HelpItem
helpText="clients-help:encryptAssertions"
forLabel={t("encryptAssertions")}
forID={t(`common:helpLabel`, {
label: t("encryptAssertions"),
})}
/> />
</FormGroup> }
</> label={t("encryptAssertions")}
)} fieldId="kc-encrypt"
</> hasNoPaddingTop
>
<Controller
name="attributes.saml_encrypt"
control={control}
defaultValue="false"
render={({ onChange, value }) => (
<Switch
data-testid="encrypt"
id="kc-encrypt"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange("" + value)}
/>
)}
/>
</FormGroup>
<FormGroup
labelIcon={
<HelpItem
helpText="clients-help:clientSignature"
forLabel={t("clientSignature")}
forID={t(`common:helpLabel`, { label: t("clientSignature") })}
/>
}
label={t("clientSignature")}
fieldId="kc-client-signature"
hasNoPaddingTop
>
<Controller
name="attributes.saml_client_signature"
control={control}
defaultValue="false"
render={({ onChange, value }) => (
<Switch
data-testid="client-signature"
id="kc-client-signature"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value === "true"}
onChange={(value) => onChange("" + value)}
/>
)}
/>
</FormGroup>
</>
)}
</FormAccess> </FormAccess>
); );
}; };

View file

@ -21,52 +21,50 @@ export const SignedJWT = () => {
const [open, isOpen] = useState(false); const [open, isOpen] = useState(false);
return ( return (
<> <FormGroup
<FormGroup label={t("signatureAlgorithm")}
label={t("signatureAlgorithm")} fieldId="kc-signature-algorithm"
fieldId="kc-signature-algorithm" labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText="clients-help:signature-algorithm"
helpText="clients-help:signature-algorithm" forLabel={t("signatureAlgorithm")}
forLabel={t("signatureAlgorithm")} forID="kc-signature-algorithm"
forID="kc-signature-algorithm"
/>
}
>
<Controller
name="attributes.token-endpoint-auth-signing-alg"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
maxHeight={200}
toggleId="kc-signature-algorithm"
onToggle={() => isOpen(!open)}
onSelect={(_, value) => {
onChange(value as string);
isOpen(false);
}}
selections={value || t("anyAlgorithm")}
variant={SelectVariant.single}
aria-label={t("signatureAlgorithm")}
isOpen={open}
>
<SelectOption selected={value === ""} key="any" value="">
{t("anyAlgorithm")}
</SelectOption>
<>
{providers.map((option) => (
<SelectOption
selected={option === value}
key={option}
value={option}
/>
))}
</>
</Select>
)}
/> />
</FormGroup> }
</> >
<Controller
name="attributes.token-endpoint-auth-signing-alg"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<Select
maxHeight={200}
toggleId="kc-signature-algorithm"
onToggle={() => isOpen(!open)}
onSelect={(_, value) => {
onChange(value as string);
isOpen(false);
}}
selections={value || t("anyAlgorithm")}
variant={SelectVariant.single}
aria-label={t("signatureAlgorithm")}
isOpen={open}
>
<SelectOption selected={value === ""} key="any" value="">
{t("anyAlgorithm")}
</SelectOption>
<>
{providers.map((option) => (
<SelectOption
selected={option === value}
key={option}
value={option}
/>
))}
</>
</Select>
)}
/>
</FormGroup>
); );
}; };

View file

@ -153,32 +153,29 @@ export const Keys = ({ clientId, save }: KeysProps) => {
)} )}
/> />
</FormGroup> </FormGroup>
{useJwksUrl !== "true" && ( {useJwksUrl !== "true" &&
<> (keyInfo ? (
{keyInfo ? ( <FormGroup
<FormGroup label={t("certificate")}
label={t("certificate")} fieldId="certificate"
fieldId="certificate" labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText="clients-help:certificate"
helpText="clients-help:certificate" forLabel={t("certificate")}
forLabel={t("certificate")} forID="certificate"
forID="certificate"
/>
}
>
<TextArea
readOnly
rows={5}
id="certificate"
value={keyInfo.certificate}
/> />
</FormGroup> }
) : ( >
"No client certificate configured" <TextArea
)} readOnly
</> rows={5}
)} id="certificate"
value={keyInfo.certificate}
/>
</FormGroup>
) : (
"No client certificate configured"
))}
{useJwksUrl === "true" && ( {useJwksUrl === "true" && (
<FormGroup <FormGroup
label={t("jwksUrl")} label={t("jwksUrl")}

View file

@ -140,27 +140,25 @@ export const ClientScopes = ({ clientId, protocol }: ClientScopesProps) => {
}; };
const TypeSelector = (scope: Row) => ( const TypeSelector = (scope: Row) => (
<> <CellDropdown
<CellDropdown clientScope={scope}
clientScope={scope} type={scope.type}
type={scope.type} onSelect={async (value) => {
onSelect={async (value) => { try {
try { await changeScope(
await changeScope( adminClient,
adminClient, clientId,
clientId, scope,
scope, scope.type,
scope.type, value as ClientScope
value as ClientScope );
); addAlert(t("clientScopeSuccess"), AlertVariant.success);
addAlert(t("clientScopeSuccess"), AlertVariant.success); refresh();
refresh(); } catch (error) {
} catch (error) { addError("clients:clientScopeError", error);
addError("clients:clientScopeError", error); }
} }}
}} />
/>
</>
); );
return ( return (

View file

@ -97,18 +97,14 @@ export const ServiceAccount = ({ client }: ServiceAccountProps) => {
addError("clients:roleMappingUpdatedError", error); addError("clients:roleMappingUpdatedError", error);
} }
}; };
return ( return serviceAccount ? (
<> <RoleMapping
{serviceAccount && ( name={client.clientId!}
<RoleMapping id={serviceAccount.id!}
name={client.clientId!} type="service-account"
id={serviceAccount.id!} loader={loader}
type="service-account" save={assignRoles}
loader={loader} onHideRolesToggle={() => setHide(!hide)}
save={assignRoles} />
onHideRolesToggle={() => setHide(!hide)} ) : null;
/>
)}
</>
);
}; };

View file

@ -75,60 +75,73 @@ export const AttributesForm = ({
}, [fields]); }, [fields]);
return ( return (
<> <FormAccess role="manage-realm" onSubmit={handleSubmit(save)}>
<FormAccess role="manage-realm" onSubmit={handleSubmit(save)}> <TableComposable
<TableComposable className="kc-attributes__table"
className="kc-attributes__table" aria-label="Role attribute keys and values"
aria-label="Role attribute keys and values" variant="compact"
variant="compact" borders={false}
borders={false} >
> <Thead>
<Thead> <Tr>
<Tr> <Th id="key" width={40}>
<Th id="key" width={40}> {columns[0]}
{columns[0]} </Th>
</Th> <Th id="value" width={40}>
<Th id="value" width={40}> {columns[1]}
{columns[1]} </Th>
</Th> </Tr>
</Tr> </Thead>
</Thead> <Tbody>
<Tbody> {fields.map((attribute, rowIndex) => (
{fields.map((attribute, rowIndex) => ( <Tr key={attribute.id}>
<Tr key={attribute.id}> <Td
key={`${attribute.id}-key`}
id={`text-input-${rowIndex}-key`}
dataLabel={columns[0]}
>
<TextInput
name={`attributes[${rowIndex}].key`}
ref={register()}
aria-label="key-input"
defaultValue={attribute.key}
validated={
errors.attributes?.[rowIndex] ? "error" : "default"
}
/>
</Td>
<Td
key={`${attribute}-value`}
id={`text-input-${rowIndex}-value`}
dataLabel={columns[1]}
>
<TextInput
name={`attributes[${rowIndex}].value`}
ref={register()}
aria-label="value-input"
defaultValue={attribute.value}
/>
</Td>
{rowIndex !== fields.length - 1 && fields.length - 1 !== 0 && (
<Td <Td
key={`${attribute.id}-key`} key="minus-button"
id={`text-input-${rowIndex}-key`} id={`kc-minus-button-${rowIndex}`}
dataLabel={columns[0]} dataLabel={columns[2]}
> >
<TextInput <Button
name={`attributes[${rowIndex}].key`} id={`minus-button-${rowIndex}`}
ref={register()} aria-label={`remove ${attribute.key} with value ${attribute.value} `}
aria-label="key-input" variant="link"
defaultValue={attribute.key} className="kc-attributes__minus-icon"
validated={ onClick={() => remove(rowIndex)}
errors.attributes?.[rowIndex] ? "error" : "default"
}
/>
</Td>
<Td
key={`${attribute}-value`}
id={`text-input-${rowIndex}-value`}
dataLabel={columns[1]}
>
<TextInput
name={`attributes[${rowIndex}].value`}
ref={register()}
aria-label="value-input"
defaultValue={attribute.value}
/>
</Td>
{rowIndex !== fields.length - 1 && fields.length - 1 !== 0 && (
<Td
key="minus-button"
id={`kc-minus-button-${rowIndex}`}
dataLabel={columns[2]}
> >
<MinusCircleIcon />
</Button>
</Td>
)}
{rowIndex === fields.length - 1 && (
<Td key="add-button" id="add-button" dataLabel={columns[2]}>
{fields.length !== 1 && (
<Button <Button
id={`minus-button-${rowIndex}`} id={`minus-button-${rowIndex}`}
aria-label={`remove ${attribute.key} with value ${attribute.value} `} aria-label={`remove ${attribute.key} with value ${attribute.value} `}
@ -138,55 +151,36 @@ export const AttributesForm = ({
> >
<MinusCircleIcon /> <MinusCircleIcon />
</Button> </Button>
</Td> )}
)} </Td>
{rowIndex === fields.length - 1 && ( )}
<Td key="add-button" id="add-button" dataLabel={columns[2]}>
{fields.length !== 1 && (
<Button
id={`minus-button-${rowIndex}`}
aria-label={`remove ${attribute.key} with value ${attribute.value} `}
variant="link"
className="kc-attributes__minus-icon"
onClick={() => remove(rowIndex)}
>
<MinusCircleIcon />
</Button>
)}
</Td>
)}
</Tr>
))}
<Tr>
<Td>
<Button
aria-label={t("roles:addAttributeText")}
id="plus-icon"
variant="link"
className="kc-attributes__plus-icon"
onClick={() => append({ key: "", value: "" })}
icon={<PlusCircleIcon />}
isDisabled={!watchLast}
>
{t("roles:addAttributeText")}
</Button>
</Td>
</Tr> </Tr>
</Tbody> ))}
</TableComposable> <Tr>
<ActionGroup className="kc-attributes__action-group"> <Td>
<Button variant="primary" type="submit" isDisabled={!watchLast}> <Button
{t("common:save")} aria-label={t("roles:addAttributeText")}
</Button> id="plus-icon"
<Button variant="link"
onClick={reset} className="kc-attributes__plus-icon"
variant="link" onClick={() => append({ key: "", value: "" })}
isDisabled={!formState.isDirty} icon={<PlusCircleIcon />}
> isDisabled={!watchLast}
{t("common:revert")} >
</Button> {t("roles:addAttributeText")}
</ActionGroup> </Button>
</FormAccess> </Td>
</> </Tr>
</Tbody>
</TableComposable>
<ActionGroup className="kc-attributes__action-group">
<Button variant="primary" type="submit" isDisabled={!watchLast}>
{t("common:save")}
</Button>
<Button onClick={reset} variant="link" isDisabled={!formState.isDirty}>
{t("common:revert")}
</Button>
</ActionGroup>
</FormAccess>
); );
}; };

View file

@ -22,37 +22,31 @@ export const GroupBreadCrumbs = () => {
}); });
}, [history]); }, [history]);
return ( return subGroups.length !== 0 ? (
<> <Breadcrumb>
{subGroups.length !== 0 && ( <BreadcrumbItem key="home">
<Breadcrumb> <Link to={`/${realm}/groups`}>{t("groups")}</Link>
<BreadcrumbItem key="home"> </BreadcrumbItem>
<Link to={`/${realm}/groups`}>{t("groups")}</Link> {subGroups.map((group, i) => {
</BreadcrumbItem> const isLastGroup = i === subGroups.length - 1;
{subGroups.map((group, i) => { return (
const isLastGroup = i === subGroups.length - 1; <BreadcrumbItem key={group.id} isActive={isLastGroup}>
return ( {!isLastGroup && (
<BreadcrumbItem key={group.id} isActive={isLastGroup}> <Link
{!isLastGroup && ( to={location.pathname.substr(
<Link 0,
to={location.pathname.substr( location.pathname.indexOf(group.id!) + group.id!.length
0,
location.pathname.indexOf(group.id!) + group.id!.length
)}
onClick={() => remove(group)}
>
{group.name}
</Link>
)} )}
{isLastGroup && onClick={() => remove(group)}
(group.id === "search" >
? group.name {group.name}
: t("groups:groupDetails"))} </Link>
</BreadcrumbItem> )}
); {isLastGroup &&
})} (group.id === "search" ? group.name : t("groups:groupDetails"))}
</Breadcrumb> </BreadcrumbItem>
)} );
</> })}
); </Breadcrumb>
) : null;
}; };

View file

@ -37,7 +37,7 @@ export const PageBreadCrumbs = () => {
{crumbs.map(({ match, breadcrumb: crumb }, i) => ( {crumbs.map(({ match, breadcrumb: crumb }, i) => (
<BreadcrumbItem key={i} isActive={crumbs.length - 1 === i}> <BreadcrumbItem key={i} isActive={crumbs.length - 1 === i}>
{crumbs.length - 1 !== i && <Link to={match.url}>{crumb}</Link>} {crumbs.length - 1 !== i && <Link to={match.url}>{crumb}</Link>}
{crumbs.length - 1 === i && <>{crumb}</>} {crumbs.length - 1 === i && crumb}
</BreadcrumbItem> </BreadcrumbItem>
))} ))}
</Breadcrumb> </Breadcrumb>

View file

@ -24,11 +24,9 @@ export const ErrorRenderer = ({ error, resetErrorBoundary }: FallbackProps) => {
/> />
} }
actionLinks={ actionLinks={
<React.Fragment> <AlertActionLink onClick={resetErrorBoundary}>
<AlertActionLink onClick={resetErrorBoundary}> {t("retry")}
{t("retry")} </AlertActionLink>
</AlertActionLink>
</React.Fragment>
} }
></Alert> ></Alert>
</PageSection> </PageSection>

View file

@ -192,7 +192,7 @@ export const GroupPickerDialog = ({
{group.name} {group.name}
</Button> </Button>
)} )}
{navigation.length - 1 === i && <>{group.name}</>} {navigation.length - 1 === i && group.name}
</BreadcrumbItem> </BreadcrumbItem>
))} ))}
</Breadcrumb> </Breadcrumb>
@ -255,7 +255,7 @@ export const GroupPickerDialog = ({
dataListCells={[ dataListCells={[
<DataListCell key={`name-${group.id}`}> <DataListCell key={`name-${group.id}`}>
{filter === "" ? ( {filter === "" ? (
<>{group.name}</> group.name
) : ( ) : (
<GroupPath group={findSubGroup(group, filter)} /> <GroupPath group={findSubGroup(group, filter)} />
)} )}

View file

@ -23,30 +23,24 @@ export const HelpItem = ({
}: HelpItemProps) => { }: HelpItemProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { enabled } = useHelp(); const { enabled } = useHelp();
return ( return enabled ? (
<> <Popover
{enabled && ( bodyContent={isValidElement(helpText) ? helpText : t(helpText as string)}
<Popover >
bodyContent={ <>
isValidElement(helpText) ? helpText : t(helpText as string) {!unWrap && (
} <button
> id={id}
<> aria-label={t(`helpLabel`, { label: forLabel })}
{!unWrap && ( onClick={(e) => e.preventDefault()}
<button aria-describedby={forID}
id={id} className="pf-c-form__group-label-help"
aria-label={t(`helpLabel`, { label: forLabel })} >
onClick={(e) => e.preventDefault()} <HelpIcon noVerticalAlign={noVerticalAlign} />
aria-describedby={forID} </button>
className="pf-c-form__group-label-help" )}
> {unWrap && <HelpIcon noVerticalAlign={noVerticalAlign} />}
<HelpIcon noVerticalAlign={noVerticalAlign} /> </>
</button> </Popover>
)} ) : null;
{unWrap && <HelpIcon noVerticalAlign={noVerticalAlign} />}
</>
</Popover>
)}
</>
);
}; };

View file

@ -40,42 +40,40 @@ export const ListEmptyState = ({
icon, icon,
}: ListEmptyStateProps) => { }: ListEmptyStateProps) => {
return ( return (
<> <EmptyState data-testid="empty-state" variant="large">
<EmptyState data-testid="empty-state" variant="large"> {hasIcon && isSearchVariant ? (
{hasIcon && isSearchVariant ? ( <EmptyStateIcon icon={SearchIcon} />
<EmptyStateIcon icon={SearchIcon} /> ) : (
) : ( hasIcon && <EmptyStateIcon icon={icon ? icon : PlusCircleIcon} />
hasIcon && <EmptyStateIcon icon={icon ? icon : PlusCircleIcon} /> )}
)} <Title headingLevel="h1" size="lg">
<Title headingLevel="h1" size="lg"> {message}
{message} </Title>
</Title> <EmptyStateBody>{instructions}</EmptyStateBody>
<EmptyStateBody>{instructions}</EmptyStateBody> {primaryActionText && (
{primaryActionText && ( <Button
<Button data-testid={`${message
data-testid={`${message .replace(/\W+/g, "-")
.replace(/\W+/g, "-") .toLowerCase()}-empty-action`}
.toLowerCase()}-empty-action`} variant="primary"
variant="primary" onClick={onPrimaryAction}
onClick={onPrimaryAction} >
> {primaryActionText}
{primaryActionText} </Button>
</Button> )}
)} {secondaryActions && (
{secondaryActions && ( <EmptyStateSecondaryActions>
<EmptyStateSecondaryActions> {secondaryActions.map((action) => (
{secondaryActions.map((action) => ( <Button
<Button key={action.text}
key={action.text} variant={action.type || ButtonVariant.secondary}
variant={action.type || ButtonVariant.secondary} onClick={action.onClick}
onClick={action.onClick} >
> {action.text}
{action.text} </Button>
</Button> ))}
))} </EmptyStateSecondaryActions>
</EmptyStateSecondaryActions> )}
)} </EmptyState>
</EmptyState>
</>
); );
}; };

View file

@ -427,12 +427,7 @@ export function KeycloakDataTable<T>({
{loading && <Loading />} {loading && <Loading />}
</PaginatingTableToolbar> </PaginatingTableToolbar>
)} )}
<> {!loading && (!data || data?.length === 0) && search === "" && emptyState}
{!loading &&
(!data || data?.length === 0) &&
search === "" &&
emptyState}
</>
</> </>
); );
} }

View file

@ -73,7 +73,7 @@ export const PaginatingTableToolbar: FunctionComponent<TableToolbarProps> = ({
} }
subToolbar={subToolbar} subToolbar={subToolbar}
toolbarItemFooter={ toolbarItemFooter={
count !== 0 ? <ToolbarItem>{pagination("bottom")}</ToolbarItem> : <></> count !== 0 ? <ToolbarItem>{pagination("bottom")}</ToolbarItem> : null
} }
inputGroupName={inputGroupName} inputGroupName={inputGroupName}
inputGroupPlaceholder={inputGroupPlaceholder} inputGroupPlaceholder={inputGroupPlaceholder}

View file

@ -1,6 +1,5 @@
import React, { import React, {
FormEvent, FormEvent,
Fragment,
FunctionComponent, FunctionComponent,
ReactNode, ReactNode,
useState, useState,
@ -74,35 +73,33 @@ export const TableToolbar: FunctionComponent<TableToolbarProps> = ({
<> <>
<Toolbar> <Toolbar>
<ToolbarContent> <ToolbarContent>
<Fragment> {inputGroupName && (
{inputGroupName && ( <ToolbarItem>
<ToolbarItem> <InputGroup>
<InputGroup> {searchTypeComponent}
{searchTypeComponent} {inputGroupPlaceholder && (
{inputGroupPlaceholder && ( <>
<> <TextInput
<TextInput name={inputGroupName}
name={inputGroupName} id={inputGroupName}
id={inputGroupName} type="search"
type="search" aria-label={t("search")}
aria-label={t("search")} placeholder={inputGroupPlaceholder}
placeholder={inputGroupPlaceholder} onChange={handleInputChange}
onChange={handleInputChange} onKeyDown={handleKeyDown}
onKeyDown={handleKeyDown} />
/> <Button
<Button variant={ButtonVariant.control}
variant={ButtonVariant.control} aria-label={t("search")}
aria-label={t("search")} onClick={onSearch}
onClick={onSearch} >
> <SearchIcon />
<SearchIcon /> </Button>
</Button> </>
</> )}
)} </InputGroup>
</InputGroup> </ToolbarItem>
</ToolbarItem> )}
)}
</Fragment>
{toolbarItem} {toolbarItem}
</ToolbarContent> </ToolbarContent>
</Toolbar> </Toolbar>

View file

@ -101,7 +101,7 @@ export const ViewHeader = ({
</Badge>{" "} </Badge>{" "}
</Fragment> </Fragment>
)} )}
{isValidElement(badge.text) && <>{badge.text}</>}{" "} {isValidElement(badge.text) && badge.text}{" "}
</Fragment> </Fragment>
))} ))}
</LevelItem> </LevelItem>

View file

@ -125,14 +125,10 @@ const Dashboard = () => {
{feature}{" "} {feature}{" "}
{isExperimentalFeature(feature) ? ( {isExperimentalFeature(feature) ? (
<Label color="orange">{t("experimental")}</Label> <Label color="orange">{t("experimental")}</Label>
) : ( ) : null}
<></>
)}
{isPreviewFeature(feature) ? ( {isPreviewFeature(feature) ? (
<Label color="blue">{t("preview")}</Label> <Label color="blue">{t("preview")}</Label>
) : ( ) : null}
<></>
)}
</ListItem> </ListItem>
))} ))}
</List> </List>

View file

@ -137,165 +137,161 @@ export const AdminEvents = () => {
}; };
const LinkResource = (row: AdminEventRepresentation) => ( const LinkResource = (row: AdminEventRepresentation) => (
<> <Truncate text={row.resourcePath}>
<Truncate text={row.resourcePath}> {(text) => (
{(text) => ( <>
<> {row.resourceType !== "COMPONENT" && (
{row.resourceType !== "COMPONENT" && ( <Link
<Link to={`/${realm}/${row.resourcePath}${
to={`/${realm}/${row.resourcePath}${ row.resourceType !== "GROUP" ? "/settings" : ""
row.resourceType !== "GROUP" ? "/settings" : "" }`}
}`} >
> {text}
{text} </Link>
</Link> )}
)} {row.resourceType === "COMPONENT" && <span>{text}</span>}
{row.resourceType === "COMPONENT" && <span>{text}</span>} </>
</> )}
)} </Truncate>
</Truncate>
</>
); );
const adminEventSearchFormDisplay = () => { const adminEventSearchFormDisplay = () => {
return ( return (
<> <Flex
<Flex direction={{ default: "column" }}
direction={{ default: "column" }} spaceItems={{ default: "spaceItemsNone" }}
spaceItems={{ default: "spaceItemsNone" }} >
> <FlexItem>
<FlexItem> <Dropdown
<Dropdown id="admin-events-search-select"
id="admin-events-search-select" data-testid="AdminEventsSearchSelector"
data-testid="AdminEventsSearchSelector" className="pf-u-ml-md"
className="pf-u-ml-md" toggle={
toggle={ <DropdownToggle
<DropdownToggle data-testid="adminEventsSearchSelectorToggle"
data-testid="adminEventsSearchSelectorToggle" onToggle={(isOpen) => setSearchDropdownOpen(isOpen)}
onToggle={(isOpen) => setSearchDropdownOpen(isOpen)} className="keycloak__events_search_selector_dropdown__toggle"
className="keycloak__events_search_selector_dropdown__toggle"
>
{t("searchForAdminEvent")}
</DropdownToggle>
}
isOpen={searchDropdownOpen}
>
<Form
isHorizontal
className="keycloak__admin_events_search__form"
data-testid="searchForm"
> >
<FormGroup {t("searchForAdminEvent")}
label={t("resourceType")} </DropdownToggle>
fieldId="kc-resourceType" }
className="keycloak__events_search__form_multiline_label" isOpen={searchDropdownOpen}
> >
<Select <Form
variant={SelectVariant.single} isHorizontal
onToggle={(isOpen) => setSelectOpen(isOpen)} className="keycloak__admin_events_search__form"
isOpen={selectOpen} data-testid="searchForm"
></Select>
</FormGroup>
<FormGroup
label={t("operationType")}
fieldId="kc-operationType"
className="keycloak__events_search__form_multiline_label"
>
<Select
variant={SelectVariant.single}
onToggle={(isOpen) => setSelectOpen(isOpen)}
isOpen={selectOpen}
></Select>
</FormGroup>
<FormGroup
label={t("user")}
fieldId="kc-user"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-user"
name="user"
data-testid="user-searchField"
/>
</FormGroup>
<FormGroup
label={t("realm")}
fieldId="kc-realm"
className="keycloak__events_search__form_label"
>
<Select
variant={SelectVariant.single}
onToggle={(isOpen) => setSelectOpen(isOpen)}
isOpen={selectOpen}
></Select>
</FormGroup>
<FormGroup
label={t("ipAddress")}
fieldId="kc-ipAddress"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-ipAddress"
name="ipAddress"
data-testid="ipAddress-searchField"
/>
</FormGroup>
<FormGroup
label={t("dateFrom")}
fieldId="kc-dateFrom"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-dateFrom"
name="dateFrom"
className="pf-c-form-control pf-m-icon pf-m-calendar"
placeholder="yyyy-MM-dd"
data-testid="dateFrom-searchField"
/>
</FormGroup>
<FormGroup
label={t("dateTo")}
fieldId="kc-dateTo"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-dateTo"
name="dateTo"
className="pf-c-form-control pf-m-icon pf-m-calendar"
placeholder="yyyy-MM-dd"
data-testid="dateTo-searchField"
/>
</FormGroup>
<ActionGroup>
<Button
className="keycloak__admin_events_search__form_btn"
variant={"primary"}
data-testid="search-events-btn"
isDisabled={!isDirty}
>
{t("searchAdminEventsBtn")}
</Button>
</ActionGroup>
</Form>
</Dropdown>
<Button
className="pf-u-ml-md"
onClick={refresh}
data-testid="refresh-btn"
> >
{t("refresh")} <FormGroup
</Button> label={t("resourceType")}
</FlexItem> fieldId="kc-resourceType"
</Flex> className="keycloak__events_search__form_multiline_label"
</> >
<Select
variant={SelectVariant.single}
onToggle={(isOpen) => setSelectOpen(isOpen)}
isOpen={selectOpen}
></Select>
</FormGroup>
<FormGroup
label={t("operationType")}
fieldId="kc-operationType"
className="keycloak__events_search__form_multiline_label"
>
<Select
variant={SelectVariant.single}
onToggle={(isOpen) => setSelectOpen(isOpen)}
isOpen={selectOpen}
></Select>
</FormGroup>
<FormGroup
label={t("user")}
fieldId="kc-user"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-user"
name="user"
data-testid="user-searchField"
/>
</FormGroup>
<FormGroup
label={t("realm")}
fieldId="kc-realm"
className="keycloak__events_search__form_label"
>
<Select
variant={SelectVariant.single}
onToggle={(isOpen) => setSelectOpen(isOpen)}
isOpen={selectOpen}
></Select>
</FormGroup>
<FormGroup
label={t("ipAddress")}
fieldId="kc-ipAddress"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-ipAddress"
name="ipAddress"
data-testid="ipAddress-searchField"
/>
</FormGroup>
<FormGroup
label={t("dateFrom")}
fieldId="kc-dateFrom"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-dateFrom"
name="dateFrom"
className="pf-c-form-control pf-m-icon pf-m-calendar"
placeholder="yyyy-MM-dd"
data-testid="dateFrom-searchField"
/>
</FormGroup>
<FormGroup
label={t("dateTo")}
fieldId="kc-dateTo"
className="keycloak__events_search__form_label"
>
<TextInput
ref={register()}
type="text"
id="kc-dateTo"
name="dateTo"
className="pf-c-form-control pf-m-icon pf-m-calendar"
placeholder="yyyy-MM-dd"
data-testid="dateTo-searchField"
/>
</FormGroup>
<ActionGroup>
<Button
className="keycloak__admin_events_search__form_btn"
variant={"primary"}
data-testid="search-events-btn"
isDisabled={!isDirty}
>
{t("searchAdminEventsBtn")}
</Button>
</ActionGroup>
</Form>
</Dropdown>
<Button
className="pf-u-ml-md"
onClick={refresh}
data-testid="refresh-btn"
>
{t("refresh")}
</Button>
</FlexItem>
</Flex>
); );
}; };

View file

@ -77,17 +77,15 @@ export const GroupTable = () => {
}; };
const GroupNameCell = (group: GroupRepresentation) => ( const GroupNameCell = (group: GroupRepresentation) => (
<> <Link
<Link key={group.id}
key={group.id} to={`${location.pathname}/${group.id}`}
to={`${location.pathname}/${group.id}`} onClick={() => {
onClick={() => { setSubGroups([...subGroups, group]);
setSubGroups([...subGroups, group]); }}
}} >
> {group.name}
{group.name} </Link>
</Link>
</>
); );
const handleModalToggle = () => { const handleModalToggle = () => {

View file

@ -99,11 +99,9 @@ export const Members = () => {
}; };
const UserDetailLink = (user: MembersOf) => ( const UserDetailLink = (user: MembersOf) => (
<> <Link key={user.id} to={toUser({ realm, id: user.id!, tab: "settings" })}>
<Link key={user.id} to={toUser({ realm, id: user.id!, tab: "settings" })}> {user.username}
{user.username} </Link>
</Link>
</>
); );
return ( return (
<> <>

View file

@ -62,17 +62,15 @@ export const SearchGroups = () => {
}; };
const GroupNameCell = (group: SearchGroup) => ( const GroupNameCell = (group: SearchGroup) => (
<> <Link
<Link key={group.id}
key={group.id} to={`/${realm}/groups/search/${group.link}`}
to={`/${realm}/groups/search/${group.link}`} onClick={() =>
onClick={() => setSubGroups([{ name: t("searchGroups"), id: "search" }, group])
setSubGroups([{ name: t("searchGroups"), id: "search" }, group]) }
} >
> {group.name}
{group.name} </Link>
</Link>
</>
); );
const flatten = ( const flatten = (

View file

@ -70,23 +70,21 @@ export const IdentityProvidersSection = () => {
const loader = () => Promise.resolve(_.sortBy(providers, "alias")); const loader = () => Promise.resolve(_.sortBy(providers, "alias"));
const DetailLink = (identityProvider: IdentityProviderRepresentation) => ( const DetailLink = (identityProvider: IdentityProviderRepresentation) => (
<> <Link
<Link key={identityProvider.providerId}
key={identityProvider.providerId} to={`/${realm}/identity-providers/${identityProvider.providerId}/settings`}
to={`/${realm}/identity-providers/${identityProvider.providerId}/settings`} >
> {identityProvider.alias}
{identityProvider.alias} {!identityProvider.enabled && (
{!identityProvider.enabled && ( <Badge
<Badge key={`${identityProvider.providerId}-disabled`}
key={`${identityProvider.providerId}-disabled`} isRead
isRead className="pf-u-ml-sm"
className="pf-u-ml-sm" >
> {t("common:disabled")}
{t("common:disabled")} </Badge>
</Badge> )}
)} </Link>
</Link>
</>
); );
const navigateToCreate = (providerId: string) => const navigateToCreate = (providerId: string) =>

View file

@ -64,6 +64,8 @@ const LoginFlow = ({
aria-label={t(label)} aria-label={t(label)}
isOpen={open} isOpen={open}
> >
{/* The type for the children of Select are incorrect, so we need a fragment here. */}
{/* eslint-disable-next-line react/jsx-no-useless-fragment */}
<> <>
{defaultValue === "" && ( {defaultValue === "" && (
<SelectOption key="empty" value={defaultValue}> <SelectOption key="empty" value={defaultValue}>
@ -71,6 +73,8 @@ const LoginFlow = ({
</SelectOption> </SelectOption>
)} )}
</> </>
{/* The type for the children of Select are incorrect, so we need a fragment here. */}
{/* eslint-disable-next-line react/jsx-no-useless-fragment */}
<> <>
{flows?.map((option) => ( {flows?.map((option) => (
<SelectOption <SelectOption

View file

@ -32,95 +32,90 @@ export const ExtendedNonDiscoverySettings = () => {
const [promptOpen, setPromptOpen] = useState(false); const [promptOpen, setPromptOpen] = useState(false);
return ( return (
<> <ExpandableSection
<ExpandableSection toggleText={t("advanced")}
toggleText={t("advanced")} onToggle={() => setIsExpanded(!isExpanded)}
onToggle={() => setIsExpanded(!isExpanded)} isExpanded={isExpanded}
isExpanded={isExpanded} >
> <Form isHorizontal>
<Form isHorizontal> <SwitchField label="passLoginHint" field="config.loginHint" />
<SwitchField label="passLoginHint" field="config.loginHint" /> <SwitchField label="passCurrentLocale" field="config.uiLocales" />
<SwitchField label="passCurrentLocale" field="config.uiLocales" /> <SwitchField
<SwitchField field="config.backchannelSupported"
field="config.backchannelSupported" label="backchannelLogout"
label="backchannelLogout" />
<SwitchField field="config.disableUserInfo" label="disableUserInfo" />
<TextField field="config.defaultScope" label="scopes" />
<FormGroupField label="prompt">
<Controller
name="config.prompt"
defaultValue={promptOptions[0]}
control={control}
render={({ onChange, value }) => (
<Select
toggleId="prompt"
required
onToggle={() => setPromptOpen(!promptOpen)}
onSelect={(_, value) => {
onChange(value as string);
setPromptOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("prompt")}
isOpen={promptOpen}
>
{promptOptions.map((option) => (
<SelectOption
selected={option === value}
key={option}
value={option}
>
{t(`prompts.${option}`)}
</SelectOption>
))}
</Select>
)}
/> />
<SwitchField field="config.disableUserInfo" label="disableUserInfo" /> </FormGroupField>
<TextField field="config.defaultScope" label="scopes" /> <SwitchField
<FormGroupField label="prompt"> field="config.acceptsPromptNoneForwardFromClient"
<Controller label="acceptsPromptNone"
name="config.prompt" />
defaultValue={promptOptions[0]} <FormGroup
control={control} label={t("allowedClockSkew")}
render={({ onChange, value }) => ( labelIcon={
<Select <HelpItem
toggleId="prompt" helpText={"identity-providers-help:allowedClockSkew"}
required forLabel={t("allowedClockSkew")}
onToggle={() => setPromptOpen(!promptOpen)} forID="allowedClockSkew"
onSelect={(_, value) => {
onChange(value as string);
setPromptOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("prompt")}
isOpen={promptOpen}
>
{promptOptions.map((option) => (
<SelectOption
selected={option === value}
key={option}
value={option}
>
{t(`prompts.${option}`)}
</SelectOption>
))}
</Select>
)}
/> />
</FormGroupField> }
<SwitchField fieldId="allowedClockSkew"
field="config.acceptsPromptNoneForwardFromClient" >
label="acceptsPromptNone" <Controller
/> name="config.allowedClockSkew"
<FormGroup control={control}
label={t("allowedClockSkew")} defaultValue={0}
labelIcon={ render={({ onChange, value }) => (
<HelpItem <NumberInput
helpText={"identity-providers-help:allowedClockSkew"} value={value}
forLabel={t("allowedClockSkew")} data-testid="allowedClockSkew"
forID="allowedClockSkew" onMinus={() => onChange(value - 1)}
onChange={onChange}
onPlus={() => onChange(value + 1)}
inputName="input"
inputAriaLabel={t("allowedClockSkew")}
minusBtnAriaLabel={t("common:minus")}
plusBtnAriaLabel={t("common:plus")}
min={0}
unit={t("common:times.seconds")}
/> />
} )}
fieldId="allowedClockSkew"
>
<Controller
name="config.allowedClockSkew"
control={control}
defaultValue={0}
render={({ onChange, value }) => (
<NumberInput
value={value}
data-testid="allowedClockSkew"
onMinus={() => onChange(value - 1)}
onChange={onChange}
onPlus={() => onChange(value + 1)}
inputName="input"
inputAriaLabel={t("allowedClockSkew")}
minusBtnAriaLabel={t("common:minus")}
plusBtnAriaLabel={t("common:plus")}
min={0}
unit={t("common:times.seconds")}
/>
)}
/>
</FormGroup>
<TextField
field="config.forwardParameters"
label="forwardParameters"
/> />
</Form> </FormGroup>
</ExpandableSection> <TextField field="config.forwardParameters" label="forwardParameters" />
</> </Form>
</ExpandableSection>
); );
}; };

View file

@ -41,6 +41,6 @@ export const FontAwesomeIcon = ({ icon }: FontAwesomeIconProps) => {
/> />
); );
default: default:
return <></>; return null;
} }
}; };

View file

@ -28,7 +28,7 @@ export const AliasRendererComponent = ({
}, [containerId]); }, [containerId]);
if (filterType === "roles" || !containerName) { if (filterType === "roles" || !containerName) {
return <>{name}</>; return name;
} }
if (filterType === "clients" || containerName) { if (filterType === "clients" || containerName) {

View file

@ -134,9 +134,8 @@ export const AssociatedRolesTab = ({
refresh(); refresh();
}, [additionalRoles, isInheritedHidden]); }, [additionalRoles, isInheritedHidden]);
const InheritedRoleName = (role: RoleRepresentation) => { const InheritedRoleName = (role: RoleRepresentation) =>
return <>{inheritanceMap.current[role.id!]}</>; inheritanceMap.current[role.id!];
};
const AliasRenderer = ({ id, name, clientId }: Role) => { const AliasRenderer = ({ id, name, clientId }: Role) => {
return ( return (
@ -201,98 +200,96 @@ export const AssociatedRolesTab = ({
const goToCreate = () => history.push(`${url}/add-role`); const goToCreate = () => history.push(`${url}/add-role`);
return ( return (
<> <PageSection variant="light" padding={{ default: "noPadding" }}>
<PageSection variant="light" padding={{ default: "noPadding" }}> <DeleteConfirm />
<DeleteConfirm /> <DeleteAssociatedRolesConfirm />
<DeleteAssociatedRolesConfirm /> <AssociatedRolesModal
<AssociatedRolesModal onConfirm={addComposites}
onConfirm={addComposites} existingCompositeRoles={additionalRoles}
existingCompositeRoles={additionalRoles} open={open}
open={open} toggleDialog={() => setOpen(!open)}
toggleDialog={() => setOpen(!open)} />
/> <KeycloakDataTable
<KeycloakDataTable key={key}
key={key} loader={loader}
loader={loader} ariaLabelKey="roles:roleList"
ariaLabelKey="roles:roleList" searchPlaceholderKey="roles:searchFor"
searchPlaceholderKey="roles:searchFor" canSelectAll
canSelectAll onSelect={(rows) => {
onSelect={(rows) => { setSelectedRows([...rows]);
setSelectedRows([...rows]); }}
}} toolbarItem={
toolbarItem={ <>
<> <ToolbarItem>
<ToolbarItem> <Checkbox
<Checkbox label="Hide inherited roles"
label="Hide inherited roles" key="associated-roles-check"
key="associated-roles-check" id="kc-hide-inherited-roles-checkbox"
id="kc-hide-inherited-roles-checkbox" onChange={() => setIsInheritedHidden(!isInheritedHidden)}
onChange={() => setIsInheritedHidden(!isInheritedHidden)} isChecked={isInheritedHidden}
isChecked={isInheritedHidden} />
/> </ToolbarItem>
</ToolbarItem> <ToolbarItem>
<ToolbarItem> <Button
<Button key="add-role-button"
key="add-role-button" onClick={() => toggleModal()}
onClick={() => toggleModal()} data-testid="add-role-button"
data-testid="add-role-button" >
> {t("addRole")}
{t("addRole")} </Button>
</Button> </ToolbarItem>
</ToolbarItem> <ToolbarItem>
<ToolbarItem> <Button
<Button variant="link"
variant="link" isDisabled={selectedRows.length === 0}
isDisabled={selectedRows.length === 0} key="remove-role-button"
key="remove-role-button" onClick={() => {
onClick={() => { toggleDeleteAssociatedRolesDialog();
toggleDeleteAssociatedRolesDialog(); }}
}} >
> {t("removeRoles")}
{t("removeRoles")} </Button>
</Button> </ToolbarItem>
</ToolbarItem> </>
</> }
} actions={[
actions={[ {
{ title: t("common:remove"),
title: t("common:remove"), onRowClick: (role) => {
onRowClick: (role) => { setSelectedRows([role]);
setSelectedRows([role]); toggleDeleteDialog();
toggleDeleteDialog();
},
}, },
]} },
columns={[ ]}
{ columns={[
name: "name", {
displayKey: "roles:roleName", name: "name",
cellRenderer: AliasRenderer, displayKey: "roles:roleName",
cellFormatters: [emptyFormatter()], cellRenderer: AliasRenderer,
}, cellFormatters: [emptyFormatter()],
{ },
name: "containerId", {
displayKey: "roles:inheritedFrom", name: "containerId",
cellRenderer: InheritedRoleName, displayKey: "roles:inheritedFrom",
cellFormatters: [emptyFormatter()], cellRenderer: InheritedRoleName,
}, cellFormatters: [emptyFormatter()],
{ },
name: "description", {
displayKey: "common:description", name: "description",
cellFormatters: [emptyFormatter()], displayKey: "common:description",
}, cellFormatters: [emptyFormatter()],
]} },
emptyState={ ]}
<ListEmptyState emptyState={
hasIcon={true} <ListEmptyState
message={t("noRoles")} hasIcon={true}
instructions={t("noRolesInstructions")} message={t("noRoles")}
primaryActionText={t("createRole")} instructions={t("noRolesInstructions")}
onPrimaryAction={goToCreate} primaryActionText={t("createRole")}
/> onPrimaryAction={goToCreate}
} />
/> }
</PageSection> />
</> </PageSection>
); );
}; };

View file

@ -45,61 +45,74 @@ export const RoleAttributes = ({
const watchFirstKey = watch("attributes[0].key", ""); const watchFirstKey = watch("attributes[0].key", "");
return ( return (
<> <FormAccess role="manage-realm">
<FormAccess role="manage-realm"> <TableComposable
<TableComposable className="kc-role-attributes__table"
className="kc-role-attributes__table" aria-label="Role attribute keys and values"
aria-label="Role attribute keys and values" variant="compact"
variant="compact" borders={false}
borders={false} >
> <Thead>
<Thead> <Tr>
<Tr> <Th id="key" width={40}>
<Th id="key" width={40}> {columns[0]}
{columns[0]} </Th>
</Th> <Th id="value" width={40}>
<Th id="value" width={40}> {columns[1]}
{columns[1]} </Th>
</Th> </Tr>
</Tr> </Thead>
</Thead> <Tbody>
<Tbody> {fields.map((attribute, rowIndex) => (
{fields.map((attribute, rowIndex) => ( <Tr key={attribute.id}>
<Tr key={attribute.id}> <Td
key={`${attribute.id}-key`}
id={`text-input-${rowIndex}-key`}
dataLabel={columns[0]}
>
<TextInput
name={`attributes[${rowIndex}].key`}
ref={register()}
aria-label="key-input"
defaultValue={attribute.key}
validated={
errors.attributes?.[rowIndex] ? "error" : "default"
}
/>
</Td>
<Td
key={`${attribute}-value`}
id={`text-input-${rowIndex}-value`}
dataLabel={columns[1]}
>
<TextInput
name={`attributes[${rowIndex}].value`}
ref={register()}
aria-label="value-input"
defaultValue={attribute.value}
validated={errors.description ? "error" : "default"}
/>
</Td>
{rowIndex !== fields.length - 1 && fields.length - 1 !== 0 && (
<Td <Td
key={`${attribute.id}-key`} key="minus-button"
id={`text-input-${rowIndex}-key`} id={`kc-minus-button-${rowIndex}`}
dataLabel={columns[0]} dataLabel={columns[2]}
> >
<TextInput <Button
name={`attributes[${rowIndex}].key`} id={`minus-button-${rowIndex}`}
ref={register()} aria-label={`remove ${attribute.key} with value ${attribute.value} `}
aria-label="key-input" variant="link"
defaultValue={attribute.key} className="kc-role-attributes__minus-icon"
validated={ onClick={() => remove(rowIndex)}
errors.attributes?.[rowIndex] ? "error" : "default"
}
/>
</Td>
<Td
key={`${attribute}-value`}
id={`text-input-${rowIndex}-value`}
dataLabel={columns[1]}
>
<TextInput
name={`attributes[${rowIndex}].value`}
ref={register()}
aria-label="value-input"
defaultValue={attribute.value}
validated={errors.description ? "error" : "default"}
/>
</Td>
{rowIndex !== fields.length - 1 && fields.length - 1 !== 0 && (
<Td
key="minus-button"
id={`kc-minus-button-${rowIndex}`}
dataLabel={columns[2]}
> >
<MinusCircleIcon />
</Button>
</Td>
)}
{rowIndex === fields.length - 1 && (
<Td key="add-button" id="add-button" dataLabel={columns[2]}>
{fields[rowIndex].key === "" && (
<Button <Button
id={`minus-button-${rowIndex}`} id={`minus-button-${rowIndex}`}
aria-label={`remove ${attribute.key} with value ${attribute.value} `} aria-label={`remove ${attribute.key} with value ${attribute.value} `}
@ -109,50 +122,35 @@ export const RoleAttributes = ({
> >
<MinusCircleIcon /> <MinusCircleIcon />
</Button> </Button>
</Td> )}
)} <Button
{rowIndex === fields.length - 1 && ( aria-label={t("roles:addAttributeText")}
<Td key="add-button" id="add-button" dataLabel={columns[2]}> id="plus-icon"
{fields[rowIndex].key === "" && ( variant="link"
<Button className="kc-role-attributes__plus-icon"
id={`minus-button-${rowIndex}`} onClick={() => append({ key: "", value: "" })}
aria-label={`remove ${attribute.key} with value ${attribute.value} `} icon={<PlusCircleIcon />}
variant="link" isDisabled={!formState.isValid}
className="kc-role-attributes__minus-icon" />
onClick={() => remove(rowIndex)} </Td>
> )}
<MinusCircleIcon /> </Tr>
</Button> ))}
)} </Tbody>
<Button </TableComposable>
aria-label={t("roles:addAttributeText")} <ActionGroup className="kc-role-attributes__action-group">
id="plus-icon" <Button
variant="link" data-testid="realm-roles-save-button"
className="kc-role-attributes__plus-icon" variant="primary"
onClick={() => append({ key: "", value: "" })} isDisabled={!watchFirstKey}
icon={<PlusCircleIcon />} onClick={save}
isDisabled={!formState.isValid} >
/> {t("common:save")}
</Td> </Button>
)} <Button onClick={reset} variant="link">
</Tr> {t("common:reload")}
))} </Button>
</Tbody> </ActionGroup>
</TableComposable> </FormAccess>
<ActionGroup className="kc-role-attributes__action-group">
<Button
data-testid="realm-roles-save-button"
variant="primary"
isDisabled={!watchFirstKey}
onClick={save}
>
{t("common:save")}
</Button>
<Button onClick={reset} variant="link">
{t("common:reload")}
</Button>
</ActionGroup>
</FormAccess>
</>
); );
}; };

View file

@ -126,11 +126,7 @@ export const RolesList = ({
ariaLabelKey="roles:roleList" ariaLabelKey="roles:roleList"
searchPlaceholderKey="roles:searchFor" searchPlaceholderKey="roles:searchFor"
isPaginated={paginated} isPaginated={paginated}
toolbarItem={ toolbarItem={<Button onClick={goToCreate}>{t("createRole")}</Button>}
<>
<Button onClick={goToCreate}>{t("createRole")}</Button>
</>
}
actions={[ actions={[
{ {
title: t("common:delete"), title: t("common:delete"),

View file

@ -42,60 +42,22 @@ export const UsersInRoleTab = () => {
const { enabled } = useHelp(); const { enabled } = useHelp();
return ( return (
<> <PageSection data-testid="users-page" variant="light">
<PageSection data-testid="users-page" variant="light"> <KeycloakDataTable
<KeycloakDataTable isPaginated
isPaginated loader={loader}
loader={loader} ariaLabelKey="roles:roleList"
ariaLabelKey="roles:roleList" searchPlaceholderKey=""
searchPlaceholderKey="" toolbarItem={
toolbarItem={ enabled && (
enabled && ( <Popover
<Popover aria-label="Basic popover"
aria-label="Basic popover" position="bottom"
position="bottom" bodyContent={
bodyContent={
<div>
{t("roles:whoWillAppearPopoverText")}
<Button
className="kc-groups-link"
variant="link"
onClick={() => history.push(`/${realm}/groups`)}
>
{t("groups")}
</Button>
{t("or")}
<Button
className="kc-users-link"
variant="link"
onClick={() => history.push(`/${realm}/users`)}
>
{t("users")}.
</Button>
</div>
}
footerContent={t("roles:whoWillAppearPopoverFooterText")}
>
<Button
variant="link"
className="kc-who-will-appear-button"
key="who-will-appear-button"
icon={<QuestionCircleIcon />}
>
{t("roles:whoWillAppearLinkText")}
</Button>
</Popover>
)
}
emptyState={
<ListEmptyState
hasIcon={true}
message={t("noDirectUsers")}
instructions={
<div> <div>
{t("noUsersEmptyStateDescription")} {t("roles:whoWillAppearPopoverText")}
<Button <Button
className="kc-groups-link-empty-state" className="kc-groups-link"
variant="link" variant="link"
onClick={() => history.push(`/${realm}/groups`)} onClick={() => history.push(`/${realm}/groups`)}
> >
@ -103,41 +65,77 @@ export const UsersInRoleTab = () => {
</Button> </Button>
{t("or")} {t("or")}
<Button <Button
className="kc-users-link-empty-state" className="kc-users-link"
variant="link" variant="link"
onClick={() => history.push(`/${realm}/users`)} onClick={() => history.push(`/${realm}/users`)}
> >
{t("users")} {t("users")}.
</Button> </Button>
{t("noUsersEmptyStateDescriptionContinued")}
</div> </div>
} }
/> footerContent={t("roles:whoWillAppearPopoverFooterText")}
} >
columns={[ <Button
{ variant="link"
name: "username", className="kc-who-will-appear-button"
displayKey: "roles:userName", key="who-will-appear-button"
cellFormatters: [emptyFormatter()], icon={<QuestionCircleIcon />}
}, >
{ {t("roles:whoWillAppearLinkText")}
name: "email", </Button>
displayKey: "roles:email", </Popover>
cellFormatters: [emptyFormatter()], )
}, }
{ emptyState={
name: "lastName", <ListEmptyState
displayKey: "roles:lastName", hasIcon={true}
cellFormatters: [emptyFormatter()], message={t("noDirectUsers")}
}, instructions={
{ <div>
name: "firstName", {t("noUsersEmptyStateDescription")}
displayKey: "roles:firstName", <Button
cellFormatters: [upperCaseFormatter(), emptyFormatter()], className="kc-groups-link-empty-state"
}, variant="link"
]} onClick={() => history.push(`/${realm}/groups`)}
/> >
</PageSection> {t("groups")}
</> </Button>
{t("or")}
<Button
className="kc-users-link-empty-state"
variant="link"
onClick={() => history.push(`/${realm}/users`)}
>
{t("users")}
</Button>
{t("noUsersEmptyStateDescriptionContinued")}
</div>
}
/>
}
columns={[
{
name: "username",
displayKey: "roles:userName",
cellFormatters: [emptyFormatter()],
},
{
name: "email",
displayKey: "roles:email",
cellFormatters: [emptyFormatter()],
},
{
name: "lastName",
displayKey: "roles:lastName",
cellFormatters: [emptyFormatter()],
},
{
name: "firstName",
displayKey: "roles:firstName",
cellFormatters: [upperCaseFormatter(), emptyFormatter()],
},
]}
/>
</PageSection>
); );
}; };

View file

@ -49,172 +49,167 @@ export const RealmSettingsGeneralTab = ({
const requireSslTypes = ["all", "external", "none"]; const requireSslTypes = ["all", "external", "none"];
return ( return (
<> <PageSection variant="light">
<PageSection variant="light"> <FormAccess
<FormAccess isHorizontal
isHorizontal role="manage-realm"
role="manage-realm" className="pf-u-mt-lg"
className="pf-u-mt-lg" onSubmit={handleSubmit(save)}
onSubmit={handleSubmit(save)} >
<FormGroup label={t("realmId")} fieldId="kc-realm-id" isRequired>
<Controller
name="realm"
control={control}
defaultValue=""
render={({ onChange, value }) => (
<ClipboardCopy onChange={onChange}>{value}</ClipboardCopy>
)}
/>
</FormGroup>
<FormGroup label={t("displayName")} fieldId="kc-display-name">
<TextInput
type="text"
id="kc-display-name"
name="displayName"
ref={register}
/>
</FormGroup>
<FormGroup label={t("htmlDisplayName")} fieldId="kc-html-display-name">
<TextInput
type="text"
id="kc-html-display-name"
name="displayNameHtml"
ref={register}
/>
</FormGroup>
<FormGroup
label={t("frontendUrl")}
fieldId="kc-frontend-url"
labelIcon={
<HelpItem
helpText="realm-settings-help:frontendUrl"
forLabel={t("frontendUrl")}
forID={t(`common:helpLabel`, { label: t("frontendUrl") })}
/>
}
> >
<FormGroup label={t("realmId")} fieldId="kc-realm-id" isRequired> <TextInput
<Controller type="text"
name="realm" id="kc-frontend-url"
control={control} name="attributes.frontendUrl"
defaultValue="" ref={register}
render={({ onChange, value }) => ( />
<ClipboardCopy onChange={onChange}>{value}</ClipboardCopy> </FormGroup>
)} <FormGroup
label={t("requireSsl")}
fieldId="kc-require-ssl"
labelIcon={
<HelpItem
helpText="realm-settings-help:requireSsl"
forLabel={t("requireSsl")}
forID={t(`common:helpLabel`, { label: t("requireSsl") })}
/> />
</FormGroup> }
<FormGroup label={t("displayName")} fieldId="kc-display-name"> >
<TextInput <Controller
type="text" name="sslRequired"
id="kc-display-name" defaultValue="none"
name="displayName" control={control}
ref={register} render={({ onChange, value }) => (
<Select
toggleId="kc-require-ssl"
onToggle={() => setOpen(!open)}
onSelect={(_, value) => {
onChange(value as string);
setOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("requireSsl")}
isOpen={open}
>
{requireSslTypes.map((sslType) => (
<SelectOption
selected={sslType === value}
key={sslType}
value={sslType}
>
{t(`sslType.${sslType}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
hasNoPaddingTop
label={t("userManagedAccess")}
labelIcon={
<HelpItem
helpText="realm-settings-help:userManagedAccess"
forLabel={t("userManagedAccess")}
forID={t(`common:helpLabel`, { label: t("userManagedAccess") })}
/> />
</FormGroup> }
<FormGroup fieldId="kc-user-manged-access"
label={t("htmlDisplayName")} >
fieldId="kc-html-display-name" <Controller
> name="userManagedAccessAllowed"
<TextInput control={control}
type="text" defaultValue={false}
id="kc-html-display-name" render={({ onChange, value }) => (
name="displayNameHtml" <Switch
ref={register} id="kc-user-managed-access"
/> data-testid="user-managed-access-switch"
</FormGroup> label={t("common:on")}
<FormGroup labelOff={t("common:off")}
label={t("frontendUrl")} isChecked={value}
fieldId="kc-frontend-url" onChange={onChange}
labelIcon={
<HelpItem
helpText="realm-settings-help:frontendUrl"
forLabel={t("frontendUrl")}
forID={t(`common:helpLabel`, { label: t("frontendUrl") })}
/> />
} )}
> />
<TextInput </FormGroup>
type="text" <FormGroup
id="kc-frontend-url" label={t("endpoints")}
name="attributes.frontendUrl" labelIcon={
ref={register} <HelpItem
helpText="realm-settings-help:endpoints"
forLabel={t("endpoints")}
forID={t(`common:helpLabel`, { label: t("endpoints") })}
/> />
</FormGroup> }
<FormGroup fieldId="kc-endpoints"
label={t("requireSsl")} >
fieldId="kc-require-ssl" <Stack>
labelIcon={ <StackItem>
<HelpItem <FormattedLink
helpText="realm-settings-help:requireSsl" href={`${baseUrl}realms/${realmName}/.well-known/openid-configuration`}
forLabel={t("requireSsl")} title={t("openIDEndpointConfiguration")}
forID={t(`common:helpLabel`, { label: t("requireSsl") })}
/> />
} </StackItem>
> <StackItem>
<Controller <FormattedLink
name="sslRequired" href={`${baseUrl}realms/${realmName}/protocol/saml/descriptor`}
defaultValue="none" title={t("samlIdentityProviderMetadata")}
control={control}
render={({ onChange, value }) => (
<Select
toggleId="kc-require-ssl"
onToggle={() => setOpen(!open)}
onSelect={(_, value) => {
onChange(value as string);
setOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("requireSsl")}
isOpen={open}
>
{requireSslTypes.map((sslType) => (
<SelectOption
selected={sslType === value}
key={sslType}
value={sslType}
>
{t(`sslType.${sslType}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
hasNoPaddingTop
label={t("userManagedAccess")}
labelIcon={
<HelpItem
helpText="realm-settings-help:userManagedAccess"
forLabel={t("userManagedAccess")}
forID={t(`common:helpLabel`, { label: t("userManagedAccess") })}
/> />
} </StackItem>
fieldId="kc-user-manged-access" </Stack>
> </FormGroup>
<Controller
name="userManagedAccessAllowed"
control={control}
defaultValue={false}
render={({ onChange, value }) => (
<Switch
id="kc-user-managed-access"
data-testid="user-managed-access-switch"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={value}
onChange={onChange}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("endpoints")}
labelIcon={
<HelpItem
helpText="realm-settings-help:endpoints"
forLabel={t("endpoints")}
forID={t(`common:helpLabel`, { label: t("endpoints") })}
/>
}
fieldId="kc-endpoints"
>
<Stack>
<StackItem>
<FormattedLink
href={`${baseUrl}realms/${realmName}/.well-known/openid-configuration`}
title={t("openIDEndpointConfiguration")}
/>
</StackItem>
<StackItem>
<FormattedLink
href={`${baseUrl}realms/${realmName}/protocol/saml/descriptor`}
title={t("samlIdentityProviderMetadata")}
/>
</StackItem>
</Stack>
</FormGroup>
<ActionGroup> <ActionGroup>
<Button <Button
variant="primary" variant="primary"
type="submit" type="submit"
data-testid="general-tab-save" data-testid="general-tab-save"
isDisabled={!isDirty} isDisabled={!isDirty}
> >
{t("common:save")} {t("common:save")}
</Button> </Button>
<Button variant="link" onClick={reset}> <Button variant="link" onClick={reset}>
{t("common:revert")} {t("common:revert")}
</Button> </Button>
</ActionGroup> </ActionGroup>
</FormAccess> </FormAccess>
</PageSection> </PageSection>
</>
); );
}; };

View file

@ -130,52 +130,46 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
const goToCreate = () => history.push(`${url}/add-role`); const goToCreate = () => history.push(`${url}/add-role`);
const ProviderRenderer = ({ provider }: KeyData) => { const ProviderRenderer = ({ provider }: KeyData) => provider;
return <>{provider}</>;
};
const ButtonRenderer = ({ type, publicKey, certificate }: KeyData) => { const ButtonRenderer = ({ type, publicKey, certificate }: KeyData) => {
if (type === "EC") { if (type === "EC") {
return ( return (
<> <Button
onClick={() => {
togglePublicKeyDialog();
setPublicKey(publicKey!);
}}
variant="secondary"
id="kc-public-key"
>
{t("realm-settings:publicKeys").slice(0, -1)}
</Button>
);
} else if (type === "RSA") {
return (
<div className="button-wrapper">
<Button <Button
onClick={() => { onClick={() => {
togglePublicKeyDialog(); togglePublicKeyDialog();
setPublicKey(publicKey!); setPublicKey(publicKey!);
}} }}
variant="secondary" variant="secondary"
id="kc-public-key" id="kc-rsa-public-key"
> >
{t("realm-settings:publicKeys").slice(0, -1)} {t("realm-settings:publicKeys").slice(0, -1)}
</Button> </Button>
</> <Button
); onClick={() => {
} else if (type === "RSA") { toggleCertificateDialog();
return ( setCertificate(certificate!);
<> }}
<div className="button-wrapper"> variant="secondary"
<Button id="kc-certificate"
onClick={() => { >
togglePublicKeyDialog(); {t("realm-settings:certificate")}
setPublicKey(publicKey!); </Button>
}} </div>
variant="secondary"
id="kc-rsa-public-key"
>
{t("realm-settings:publicKeys").slice(0, -1)}
</Button>
<Button
onClick={() => {
toggleCertificateDialog();
setCertificate(certificate!);
}}
variant="secondary"
id="kc-certificate"
>
{t("realm-settings:certificate")}
</Button>
</div>
</>
); );
} }
}; };
@ -200,93 +194,91 @@ export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
]; ];
return ( return (
<> <PageSection variant="light" padding={{ default: "noPadding" }}>
<PageSection variant="light" padding={{ default: "noPadding" }}> <PublicKeyDialog />
<PublicKeyDialog /> <CertificateDialog />
<CertificateDialog /> <KeycloakDataTable
<KeycloakDataTable isNotCompact={true}
isNotCompact={true} key={key}
key={key} loader={
loader={ filterType === "Active keys"
filterType === "Active keys" ? activeKeysLoader
? activeKeysLoader : filterType === "Passive keys"
: filterType === "Passive keys" ? passiveKeysLoader
? passiveKeysLoader : filterType === "Disabled keys"
: filterType === "Disabled keys" ? disabledKeysLoader
? disabledKeysLoader : loader
: loader }
} ariaLabelKey="realm-settings:keysList"
ariaLabelKey="realm-settings:keysList" searchPlaceholderKey="realm-settings:searchKey"
searchPlaceholderKey="realm-settings:searchKey" searchTypeComponent={
searchTypeComponent={ <Select
<Select width={300}
width={300} data-testid="filter-type-select"
data-testid="filter-type-select" isOpen={filterDropdownOpen}
isOpen={filterDropdownOpen} className="kc-filter-type-select"
className="kc-filter-type-select" variant={SelectVariant.single}
variant={SelectVariant.single} onToggle={() => setFilterDropdownOpen(!filterDropdownOpen)}
onToggle={() => setFilterDropdownOpen(!filterDropdownOpen)} toggleIcon={<FilterIcon />}
toggleIcon={<FilterIcon />} onSelect={(_, value) => {
onSelect={(_, value) => { setFilterType(value as string);
setFilterType(value as string); refresh();
refresh(); setFilterDropdownOpen(false);
setFilterDropdownOpen(false); }}
}} selections={filterType}
selections={filterType} >
> {options}
{options} </Select>
</Select> }
} canSelectAll
canSelectAll columns={[
columns={[ {
{ name: "algorithm",
name: "algorithm", displayKey: "realm-settings:algorithm",
displayKey: "realm-settings:algorithm", cellFormatters: [emptyFormatter()],
cellFormatters: [emptyFormatter()], transforms: [cellWidth(15)],
transforms: [cellWidth(15)], },
}, {
{ name: "type",
name: "type", displayKey: "realm-settings:type",
displayKey: "realm-settings:type", cellFormatters: [emptyFormatter()],
cellFormatters: [emptyFormatter()], transforms: [cellWidth(10)],
transforms: [cellWidth(10)], },
}, {
{ name: "kid",
name: "kid", displayKey: "realm-settings:kid",
displayKey: "realm-settings:kid", cellFormatters: [emptyFormatter()],
cellFormatters: [emptyFormatter()], transforms: [cellWidth(10)],
transforms: [cellWidth(10)], },
}, {
{ name: "provider",
name: "provider", displayKey: "realm-settings:provider",
displayKey: "realm-settings:provider", cellRenderer: ProviderRenderer,
cellRenderer: ProviderRenderer, cellFormatters: [emptyFormatter()],
cellFormatters: [emptyFormatter()], transforms: [cellWidth(10)],
transforms: [cellWidth(10)], },
}, {
{ name: "publicKeys",
name: "publicKeys", displayKey: "realm-settings:publicKeys",
displayKey: "realm-settings:publicKeys", cellRenderer: ButtonRenderer,
cellRenderer: ButtonRenderer, cellFormatters: [],
cellFormatters: [], transforms: [cellWidth(20)],
transforms: [cellWidth(20)], },
}, ]}
]} isSearching={!!filterType}
isSearching={!!filterType} emptyState={
emptyState={ <ListEmptyState
<ListEmptyState hasIcon={true}
hasIcon={true} message={t("realm-settings:noKeys")}
message={t("realm-settings:noKeys")} instructions={
instructions={ t(`realm-settings:noKeysDescription`) +
t(`realm-settings:noKeysDescription`) + `${filterType.toLocaleLowerCase()}.`
`${filterType.toLocaleLowerCase()}.` }
} primaryActionText={t("createRole")}
primaryActionText={t("createRole")} onPrimaryAction={goToCreate}
onPrimaryAction={goToCreate} />
/> }
} />
/> </PageSection>
</PageSection>
</>
); );
}; };

View file

@ -256,60 +256,58 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => {
<DeleteConfirm /> <DeleteConfirm />
<PageSection variant="light" padding={{ default: "noPadding" }}> <PageSection variant="light" padding={{ default: "noPadding" }}>
<Toolbar> <Toolbar>
<> <ToolbarGroup className="providers-toolbar">
<ToolbarGroup className="providers-toolbar"> <ToolbarItem>
<ToolbarItem> <InputGroup>
<InputGroup> <TextInput
<TextInput name={"inputGroupName"}
name={"inputGroupName"} id={"inputGroupName"}
id={"inputGroupName"} type="search"
type="search" aria-label={t("common:search")}
aria-label={t("common:search")} placeholder={t("common:search")}
placeholder={t("common:search")} onChange={handleInputChange}
onChange={handleInputChange} onKeyDown={handleKeyDown}
onKeyDown={handleKeyDown}
/>
<Button
variant={ButtonVariant.control}
aria-label={t("common:search")}
>
<SearchIcon />
</Button>
</InputGroup>
</ToolbarItem>
<ToolbarItem>
<Dropdown
data-testid="addProviderDropdown"
className="add-provider-dropdown"
isOpen={providerDropdownOpen}
toggle={
<DropdownToggle
onToggle={(val) => setProviderDropdownOpen(val)}
isPrimary
>
{t("realm-settings:addProvider")}
</DropdownToggle>
}
dropdownItems={[
providerTypes.map((item) => (
<DropdownItem
onClick={() => {
handleModalToggle();
setProviderDropdownOpen(false);
setDefaultConsoleDisplayName(item);
}}
data-testid={`option-${item}`}
key={item}
>
{item}
</DropdownItem>
)),
]}
/> />
</ToolbarItem> <Button
</ToolbarGroup> variant={ButtonVariant.control}
</> aria-label={t("common:search")}
>
<SearchIcon />
</Button>
</InputGroup>
</ToolbarItem>
<ToolbarItem>
<Dropdown
data-testid="addProviderDropdown"
className="add-provider-dropdown"
isOpen={providerDropdownOpen}
toggle={
<DropdownToggle
onToggle={(val) => setProviderDropdownOpen(val)}
isPrimary
>
{t("realm-settings:addProvider")}
</DropdownToggle>
}
dropdownItems={[
providerTypes.map((item) => (
<DropdownItem
onClick={() => {
handleModalToggle();
setProviderDropdownOpen(false);
setDefaultConsoleDisplayName(item);
}}
data-testid={`option-${item}`}
key={item}
>
{item}
</DropdownItem>
)),
]}
/>
</ToolbarItem>
</ToolbarGroup>
</Toolbar> </Toolbar>
<DataList <DataList
aria-label={t("groups")} aria-label={t("groups")}
@ -375,21 +373,19 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => {
data-testid="provider-name" data-testid="provider-name"
key={`name-${idx}`} key={`name-${idx}`}
> >
<> <Link
<Link key={component.name}
key={component.name} data-testid="provider-name-link"
data-testid="provider-name-link" to={`${url}/${component.id}/${component.providerId}/settings`}
to={`${url}/${component.id}/${component.providerId}/settings`} >
> {component.name}
{component.name} </Link>
</Link>
</>
</DataListCell>, </DataListCell>,
<DataListCell key={`providerId-${idx}`}> <DataListCell key={`providerId-${idx}`}>
<>{component.providerId}</> {component.providerId}
</DataListCell>, </DataListCell>,
<DataListCell key={`providerDescription-${idx}`}> <DataListCell key={`providerDescription-${idx}`}>
<>{component.providerDescription}</> {component.providerDescription}
</DataListCell>, </DataListCell>,
<DataListAction <DataListAction
aria-labelledby="data-list-action" aria-labelledby="data-list-action"

View file

@ -18,198 +18,193 @@ export const RealmSettingsLoginTab = ({
const { t } = useTranslation("realm-settings"); const { t } = useTranslation("realm-settings");
return ( return (
<> <PageSection variant="light">
<PageSection variant="light"> <FormPanel className="kc-login-screen" title="Login screen customization">
<FormPanel <FormAccess isHorizontal role="manage-realm">
className="kc-login-screen" <FormGroup
title="Login screen customization" label={t("userRegistration")}
> fieldId="kc-user-reg"
<FormAccess isHorizontal role="manage-realm"> labelIcon={
<FormGroup <HelpItem
label={t("userRegistration")} helpText={t("userRegistrationHelpText")}
fieldId="kc-user-reg" forLabel={t("userRegistration")}
labelIcon={ forID={t(`common:helpLabel`, {
<HelpItem label: t("userRegistration"),
helpText={t("userRegistrationHelpText")} })}
forLabel={t("userRegistration")}
forID={t(`common:helpLabel`, {
label: t("userRegistration"),
})}
/>
}
hasNoPaddingTop
>
<Switch
id="kc-user-reg-switch"
data-testid="user-reg-switch"
name="registrationAllowed"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={realm?.registrationAllowed}
onChange={(value) => {
save({ ...realm, registrationAllowed: value });
}}
/> />
</FormGroup> }
<FormGroup hasNoPaddingTop
label={t("forgotPassword")} >
fieldId="kc-forgot-pw" <Switch
labelIcon={ id="kc-user-reg-switch"
<HelpItem data-testid="user-reg-switch"
helpText={t("forgotPasswordHelpText")} name="registrationAllowed"
forLabel={t("forgotPassword")} label={t("common:on")}
forID={t(`common:helpLabel`, { label: t("forgotPassword") })} labelOff={t("common:off")}
/> isChecked={realm?.registrationAllowed}
} onChange={(value) => {
hasNoPaddingTop save({ ...realm, registrationAllowed: value });
> }}
<Switch />
id="kc-forgot-pw-switch" </FormGroup>
data-testid="forgot-pw-switch" <FormGroup
name="resetPasswordAllowed" label={t("forgotPassword")}
label={t("common:on")} fieldId="kc-forgot-pw"
labelOff={t("common:off")} labelIcon={
isChecked={realm?.resetPasswordAllowed} <HelpItem
onChange={(value) => { helpText={t("forgotPasswordHelpText")}
save({ ...realm, resetPasswordAllowed: value }); forLabel={t("forgotPassword")}
}} forID={t(`common:helpLabel`, { label: t("forgotPassword") })}
/> />
</FormGroup> }
<FormGroup hasNoPaddingTop
label={t("rememberMe")} >
fieldId="kc-remember-me" <Switch
labelIcon={ id="kc-forgot-pw-switch"
<HelpItem data-testid="forgot-pw-switch"
helpText={t("rememberMeHelpText")} name="resetPasswordAllowed"
forLabel={t("rememberMe")} label={t("common:on")}
forID={t(`common:helpLabel`, { label: t("rememberMe") })} labelOff={t("common:off")}
/> isChecked={realm?.resetPasswordAllowed}
} onChange={(value) => {
hasNoPaddingTop save({ ...realm, resetPasswordAllowed: value });
> }}
<Switch />
id="kc-remember-me-switch" </FormGroup>
data-testid="remember-me-switch" <FormGroup
name="rememberMe" label={t("rememberMe")}
label={t("common:on")} fieldId="kc-remember-me"
labelOff={t("common:off")} labelIcon={
isChecked={realm?.rememberMe} <HelpItem
onChange={(value) => { helpText={t("rememberMeHelpText")}
save({ ...realm, rememberMe: value }); forLabel={t("rememberMe")}
}} forID={t(`common:helpLabel`, { label: t("rememberMe") })}
/> />
</FormGroup> }
</FormAccess> hasNoPaddingTop
</FormPanel> >
<FormPanel className="kc-email-settings" title="Email settings"> <Switch
<FormAccess isHorizontal role="manage-realm"> id="kc-remember-me-switch"
<FormGroup data-testid="remember-me-switch"
label={t("emailAsUsername")} name="rememberMe"
fieldId="kc-email-as-username" label={t("common:on")}
labelIcon={ labelOff={t("common:off")}
<HelpItem isChecked={realm?.rememberMe}
helpText={t("emailAsUsernameHelpText")} onChange={(value) => {
forLabel={t("emailAsUsername")} save({ ...realm, rememberMe: value });
forID={t(`common:helpLabel`, { label: t("emailAsUsername") })} }}
/> />
} </FormGroup>
hasNoPaddingTop </FormAccess>
> </FormPanel>
<Switch <FormPanel className="kc-email-settings" title="Email settings">
id="kc-email-as-username-switch" <FormAccess isHorizontal role="manage-realm">
data-testid="email-as-username-switch" <FormGroup
name="registrationEmailAsUsername" label={t("emailAsUsername")}
label={t("common:on")} fieldId="kc-email-as-username"
labelOff={t("common:off")} labelIcon={
isChecked={realm?.registrationEmailAsUsername} <HelpItem
onChange={(value) => { helpText={t("emailAsUsernameHelpText")}
save({ ...realm, registrationEmailAsUsername: value }); forLabel={t("emailAsUsername")}
}} forID={t(`common:helpLabel`, { label: t("emailAsUsername") })}
/> />
</FormGroup> }
<FormGroup hasNoPaddingTop
label={t("loginWithEmail")} >
fieldId="kc-login-with-email" <Switch
labelIcon={ id="kc-email-as-username-switch"
<HelpItem data-testid="email-as-username-switch"
helpText={t("loginWithEmailHelpText")} name="registrationEmailAsUsername"
forLabel={t("loginWithEmail")} label={t("common:on")}
forID={t(`common:helpLabel`, { label: t("loginWithEmail") })} labelOff={t("common:off")}
/> isChecked={realm?.registrationEmailAsUsername}
} onChange={(value) => {
hasNoPaddingTop save({ ...realm, registrationEmailAsUsername: value });
> }}
<Switch />
id="kc-login-with-email-switch" </FormGroup>
data-testid="login-with-email-switch" <FormGroup
name="loginWithEmailAllowed" label={t("loginWithEmail")}
label={t("common:on")} fieldId="kc-login-with-email"
labelOff={t("common:off")} labelIcon={
isChecked={realm?.loginWithEmailAllowed} <HelpItem
onChange={(value) => { helpText={t("loginWithEmailHelpText")}
save({ ...realm, loginWithEmailAllowed: value }); forLabel={t("loginWithEmail")}
}} forID={t(`common:helpLabel`, { label: t("loginWithEmail") })}
/> />
</FormGroup> }
<FormGroup hasNoPaddingTop
label={t("duplicateEmails")} >
fieldId="kc-duplicate-emails" <Switch
labelIcon={ id="kc-login-with-email-switch"
<HelpItem data-testid="login-with-email-switch"
helpText={t("duplicateEmailsHelpText")} name="loginWithEmailAllowed"
forLabel={t("duplicateEmails")} label={t("common:on")}
forID={t(`common:helpLabel`, { label: t("duplicateEmails") })} labelOff={t("common:off")}
/> isChecked={realm?.loginWithEmailAllowed}
} onChange={(value) => {
hasNoPaddingTop save({ ...realm, loginWithEmailAllowed: value });
> }}
<Switch />
id="kc-duplicate-emails-switch" </FormGroup>
data-testid="duplicate-emails-switch" <FormGroup
label={t("common:on")} label={t("duplicateEmails")}
labelOff={t("common:off")} fieldId="kc-duplicate-emails"
name="duplicateEmailsAllowed" labelIcon={
isChecked={ <HelpItem
realm?.duplicateEmailsAllowed && helpText={t("duplicateEmailsHelpText")}
!realm?.loginWithEmailAllowed && forLabel={t("duplicateEmails")}
!realm?.registrationEmailAsUsername forID={t(`common:helpLabel`, { label: t("duplicateEmails") })}
}
onChange={(value) => {
save({ ...realm, duplicateEmailsAllowed: value });
}}
isDisabled={
realm?.loginWithEmailAllowed ||
realm?.registrationEmailAsUsername
}
/> />
</FormGroup> }
<FormGroup hasNoPaddingTop
label={t("verifyEmail")} >
fieldId="kc-verify-email" <Switch
labelIcon={ id="kc-duplicate-emails-switch"
<HelpItem data-testid="duplicate-emails-switch"
helpText={t("verifyEmailHelpText")} label={t("common:on")}
forLabel={t("verifyEmail")} labelOff={t("common:off")}
forID={t(`common:helpLabel`, { label: t("verifyEmail") })} name="duplicateEmailsAllowed"
/> isChecked={
realm?.duplicateEmailsAllowed &&
!realm?.loginWithEmailAllowed &&
!realm?.registrationEmailAsUsername
} }
hasNoPaddingTop onChange={(value) => {
> save({ ...realm, duplicateEmailsAllowed: value });
<Switch }}
id="kc-verify-email-switch" isDisabled={
data-testid="verify-email-switch" realm?.loginWithEmailAllowed ||
name="verifyEmail" realm?.registrationEmailAsUsername
label={t("common:on")} }
labelOff={t("common:off")} />
isChecked={realm?.verifyEmail} </FormGroup>
onChange={(value) => { <FormGroup
save({ ...realm, verifyEmail: value }); label={t("verifyEmail")}
}} fieldId="kc-verify-email"
labelIcon={
<HelpItem
helpText={t("verifyEmailHelpText")}
forLabel={t("verifyEmail")}
forID={t(`common:helpLabel`, { label: t("verifyEmail") })}
/> />
</FormGroup> }
</FormAccess> hasNoPaddingTop
</FormPanel> >
</PageSection> <Switch
</> id="kc-verify-email-switch"
data-testid="verify-email-switch"
name="verifyEmail"
label={t("common:on")}
labelOff={t("common:off")}
isChecked={realm?.verifyEmail}
onChange={(value) => {
save({ ...realm, verifyEmail: value });
}}
/>
</FormGroup>
</FormAccess>
</FormPanel>
</PageSection>
); );
}; };

View file

@ -53,19 +53,17 @@ export const EditProviderCrumb = () => {
const { realm } = useRealm(); const { realm } = useRealm();
return ( return (
<> <Breadcrumb>
<Breadcrumb> <BreadcrumbItem
<BreadcrumbItem render={(props) => (
render={(props) => ( <Link {...props} to={toRealmSettings({ realm, tab: "keys" })}>
<Link {...props} to={toRealmSettings({ realm, tab: "keys" })}> {t("keys")}
{t("keys")} </Link>
</Link> )}
)} />
/> <BreadcrumbItem>{t("providers")}</BreadcrumbItem>
<BreadcrumbItem>{t("providers")}</BreadcrumbItem> <BreadcrumbItem isActive>{t("editProvider")}</BreadcrumbItem>
<BreadcrumbItem isActive>{t("editProvider")}</BreadcrumbItem> </Breadcrumb>
</Breadcrumb>
</>
); );
}; };

View file

@ -70,337 +70,294 @@ export const RealmSettingsSessionsTab = ({
}; };
return ( return (
<> <PageSection variant="light">
<PageSection variant="light"> <FormPanel
<FormPanel title={t("SSOSessionSettings")}
title={t("SSOSessionSettings")} className="kc-sso-session-template"
className="kc-sso-session-template" >
<FormAccess
isHorizontal
role="manage-realm"
onSubmit={handleSubmit(save)}
> >
<FormAccess <FormGroup
isHorizontal label={t("SSOSessionIdle")}
role="manage-realm" fieldId="SSOSessionIdle"
onSubmit={handleSubmit(save)} labelIcon={
<HelpItem
helpText="realm-settings-help:ssoSessionIdle"
forLabel={t("SSOSessionIdle")}
forID="SSOSessionIdle"
id="SSOSessionIdle"
/>
}
> >
<FormGroup <Controller
label={t("SSOSessionIdle")} name="ssoSessionIdleTimeout"
fieldId="SSOSessionIdle" defaultValue={realm?.ssoSessionIdleTimeout}
labelIcon={ control={control}
<HelpItem render={({ onChange, value }) => (
helpText="realm-settings-help:ssoSessionIdle" <TimeSelector
forLabel={t("SSOSessionIdle")} className="kc-sso-session-idle"
forID="SSOSessionIdle" data-testid="sso-session-idle-input"
id="SSOSessionIdle" aria-label="sso-session-idle-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/> />
} )}
> />
<Controller </FormGroup>
name="ssoSessionIdleTimeout"
defaultValue={realm?.ssoSessionIdleTimeout}
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-sso-session-idle"
data-testid="sso-session-idle-input"
aria-label="sso-session-idle-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("SSOSessionMax")} label={t("SSOSessionMax")}
fieldId="SSOSessionMax" fieldId="SSOSessionMax"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:ssoSessionMax" helpText="realm-settings-help:ssoSessionMax"
forLabel={t("SSOSessionMax")} forLabel={t("SSOSessionMax")}
forID="SSOSessionMax" forID="SSOSessionMax"
id="SSOSessionMax" id="SSOSessionMax"
/>
}
>
<Controller
name="ssoSessionMaxLifespan"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-sso-session-max"
data-testid="sso-session-max-input"
aria-label="sso-session-max-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
>
<Controller
name="ssoSessionMaxLifespan"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-sso-session-max"
data-testid="sso-session-max-input"
aria-label="sso-session-max-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("SSOSessionIdleRememberMe")} label={t("SSOSessionIdleRememberMe")}
fieldId="SSOSessionIdleRememberMe" fieldId="SSOSessionIdleRememberMe"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:ssoSessionIdleRememberMe" helpText="realm-settings-help:ssoSessionIdleRememberMe"
forLabel={t("SSOSessionIdleRememberMe")} forLabel={t("SSOSessionIdleRememberMe")}
forID="SSOSessionIdleRememberMe" forID="SSOSessionIdleRememberMe"
id="SSOSessionIdleRememberMe" id="SSOSessionIdleRememberMe"
/>
}
>
<Controller
name="ssoSessionIdleTimeoutRememberMe"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-sso-session-idle-remember-me"
data-testid="sso-session-idle-remember-me-input"
aria-label="sso-session-idle-remember-me-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
>
<Controller
name="ssoSessionIdleTimeoutRememberMe"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-sso-session-idle-remember-me"
data-testid="sso-session-idle-remember-me-input"
aria-label="sso-session-idle-remember-me-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("SSOSessionMaxRememberMe")} label={t("SSOSessionMaxRememberMe")}
fieldId="SSOSessionMaxRememberMe" fieldId="SSOSessionMaxRememberMe"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:ssoSessionMaxRememberMe" helpText="realm-settings-help:ssoSessionMaxRememberMe"
forLabel={t("SSOSessionMaxRememberMe")} forLabel={t("SSOSessionMaxRememberMe")}
forID="SSOSessionMaxRememberMe" forID="SSOSessionMaxRememberMe"
id="SSOSessionMaxRememberMe" id="SSOSessionMaxRememberMe"
/>
}
>
<Controller
name="ssoSessionMaxLifespanRememberMe"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-sso-session-max-remember-me"
aria-label="sso-session-max-remember-me-input"
data-testid="sso-session-max-remember-me-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
</FormAccess> >
</FormPanel> <Controller
<FormPanel name="ssoSessionMaxLifespanRememberMe"
title={t("clientSessionSettings")} defaultValue=""
className="kc-client-session-template" control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-sso-session-max-remember-me"
aria-label="sso-session-max-remember-me-input"
data-testid="sso-session-max-remember-me-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
</FormAccess>
</FormPanel>
<FormPanel
title={t("clientSessionSettings")}
className="kc-client-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={handleSubmit(save)}
> >
<FormAccess <FormGroup
isHorizontal label={t("clientSessionIdle")}
role="manage-realm" fieldId="clientSessionIdle"
className="pf-u-mt-lg" labelIcon={
onSubmit={handleSubmit(save)} <HelpItem
> helpText="realm-settings-help:clientSessionIdle"
<FormGroup forLabel={t("clientSessionIdle")}
label={t("clientSessionIdle")} forID="clientSessionIdle"
fieldId="clientSessionIdle" id="clientSessionIdle"
labelIcon={
<HelpItem
helpText="realm-settings-help:clientSessionIdle"
forLabel={t("clientSessionIdle")}
forID="clientSessionIdle"
id="clientSessionIdle"
/>
}
>
<Controller
name="clientSessionIdleTimeout"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-client-session-idle"
data-testid="client-session-idle-input"
aria-label="client-session-idle-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
>
<Controller
name="clientSessionIdleTimeout"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-client-session-idle"
data-testid="client-session-idle-input"
aria-label="client-session-idle-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("clientSessionMax")} label={t("clientSessionMax")}
fieldId="clientSessionMax" fieldId="clientSessionMax"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:clientSessionMax" helpText="realm-settings-help:clientSessionMax"
forLabel={t("clientSessionMax")} forLabel={t("clientSessionMax")}
forID="clientSessionMax" forID="clientSessionMax"
id="clientSessionMax" id="clientSessionMax"
/>
}
>
<Controller
name="clientSessionMaxLifespan"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-client-session-max"
data-testid="client-session-max-input"
aria-label="client-session-max-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
</FormAccess>
</FormPanel>
<FormPanel
title={t("offlineSessionSettings")}
className="kc-offline-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={handleSubmit(save)}
> >
<FormGroup <Controller
label={t("offlineSessionIdle")} name="clientSessionMaxLifespan"
fieldId="offlineSessionIdle" defaultValue=""
labelIcon={ control={control}
<HelpItem render={({ onChange, value }) => (
helpText="realm-settings-help:offlineSessionIdle" <TimeSelector
forLabel={t("offlineSessionIdle")} className="kc-client-session-max"
forID="offlineSessionIdle" data-testid="client-session-max-input"
id="offlineSessionIdle" aria-label="client-session-max-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/> />
} )}
> />
<Controller </FormGroup>
name="offlineSessionIdleTimeout" </FormAccess>
defaultValue="" </FormPanel>
control={control} <FormPanel
render={({ onChange, value }) => ( title={t("offlineSessionSettings")}
<TimeSelector className="kc-offline-session-template"
className="kc-offline-session-idle" >
data-testid="offline-session-idle-input" <FormAccess
aria-label="offline-session-idle-input" isHorizontal
value={value} role="manage-realm"
onChange={onChange} className="pf-u-mt-lg"
units={["minutes", "hours", "days"]} onSubmit={handleSubmit(save)}
/> >
)} <FormGroup
label={t("offlineSessionIdle")}
fieldId="offlineSessionIdle"
labelIcon={
<HelpItem
helpText="realm-settings-help:offlineSessionIdle"
forLabel={t("offlineSessionIdle")}
forID="offlineSessionIdle"
id="offlineSessionIdle"
/> />
</FormGroup> }
>
<Controller
name="offlineSessionIdleTimeout"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-offline-session-idle"
data-testid="offline-session-idle-input"
aria-label="offline-session-idle-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup <FormGroup
hasNoPaddingTop hasNoPaddingTop
label={t("offlineSessionMaxLimited")} label={t("offlineSessionMaxLimited")}
fieldId="kc-offlineSessionMaxLimited" fieldId="kc-offlineSessionMaxLimited"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:offlineSessionMaxLimited" helpText="realm-settings-help:offlineSessionMaxLimited"
forLabel={t("offlineSessionMaxLimited")} forLabel={t("offlineSessionMaxLimited")}
forID="offlineSessionMaxLimited" forID="offlineSessionMaxLimited"
id="offlineSessionMaxLimited" id="offlineSessionMaxLimited"
/>
}
>
<Controller
name="offlineSessionMaxLifespanEnabled"
control={control}
defaultValue={false}
render={({ onChange, value }) => (
<Switch
id="kc-offline-session-max"
data-testid="offline-session-max-switch"
aria-label="offline-session-max-switch"
label={t("common:enabled")}
labelOff={t("common:disabled")}
isChecked={value}
onChange={(value) => onChange(value.toString())}
/>
)}
/> />
</FormGroup> }
{offlineSessionMaxEnabled && (
<FormGroup
label={t("offlineSessionMax")}
fieldId="offlineSessionMax"
id="offline-session-max-label"
labelIcon={
<HelpItem
helpText="realm-settings-help:offlineSessionMax"
forLabel={t("offlineSessionMax")}
forID="offlineSessionMax"
id="offlineSessionMax"
/>
}
>
<Controller
name="offlineSessionMaxLifespan"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-offline-session-max"
data-testid="offline-session-max-input"
aria-label="offline-session-max-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
)}
</FormAccess>
</FormPanel>
<FormPanel
className="kc-login-settings-template"
title={t("loginSettings")}
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={handleSubmit(save)}
> >
<Controller
name="offlineSessionMaxLifespanEnabled"
control={control}
defaultValue={false}
render={({ onChange, value }) => (
<Switch
id="kc-offline-session-max"
data-testid="offline-session-max-switch"
aria-label="offline-session-max-switch"
label={t("common:enabled")}
labelOff={t("common:disabled")}
isChecked={value}
onChange={(value) => onChange(value.toString())}
/>
)}
/>
</FormGroup>
{offlineSessionMaxEnabled && (
<FormGroup <FormGroup
label={t("loginTimeout")} label={t("offlineSessionMax")}
id="kc-login-timeout-label" fieldId="offlineSessionMax"
fieldId="offlineSessionIdle" id="offline-session-max-label"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:loginTimeout" helpText="realm-settings-help:offlineSessionMax"
forLabel={t("loginTimeout")} forLabel={t("offlineSessionMax")}
forID="loginTimeout" forID="offlineSessionMax"
id="loginTimeout" id="offlineSessionMax"
/> />
} }
> >
<Controller <Controller
name="accessCodeLifespanLogin" name="offlineSessionMaxLifespan"
defaultValue="" defaultValue=""
control={control} control={control}
render={({ onChange, value }) => ( render={({ onChange, value }) => (
<TimeSelector <TimeSelector
className="kc-login-timeout" className="kc-offline-session-max"
data-testid="login-timeout-input" data-testid="offline-session-max-input"
aria-label="login-timeout-input" aria-label="offline-session-max-input"
value={value} value={value}
onChange={onChange} onChange={onChange}
units={["minutes", "hours", "days"]} units={["minutes", "hours", "days"]}
@ -408,51 +365,92 @@ export const RealmSettingsSessionsTab = ({
)} )}
/> />
</FormGroup> </FormGroup>
<FormGroup )}
label={t("loginActionTimeout")} </FormAccess>
fieldId="loginActionTimeout" </FormPanel>
id="login-action-timeout-label" <FormPanel
labelIcon={ className="kc-login-settings-template"
<HelpItem title={t("loginSettings")}
helpText="realm-settings-help:loginActionTimeout" >
forLabel={t("loginActionTimeout")} <FormAccess
forID="loginActionTimeout" isHorizontal
id="loginActionTimeout" role="manage-realm"
/> className="pf-u-mt-lg"
} onSubmit={handleSubmit(save)}
> >
<Controller <FormGroup
name="accessCodeLifespanUserAction" label={t("loginTimeout")}
defaultValue="" id="kc-login-timeout-label"
control={control} fieldId="offlineSessionIdle"
render={({ onChange, value }) => ( labelIcon={
<TimeSelector <HelpItem
className="kc-login-action-timeout" helpText="realm-settings-help:loginTimeout"
data-testid="login-action-timeout-input" forLabel={t("loginTimeout")}
aria-label="login-action-timeout-input" forID="loginTimeout"
value={value} id="loginTimeout"
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
<ActionGroup> >
<Button <Controller
variant="primary" name="accessCodeLifespanLogin"
type="submit" defaultValue=""
data-testid="sessions-tab-save" control={control}
isDisabled={!formState.isDirty} render={({ onChange, value }) => (
> <TimeSelector
{t("common:save")} className="kc-login-timeout"
</Button> data-testid="login-timeout-input"
<Button variant="link" onClick={reset}> aria-label="login-timeout-input"
{t("common:revert")} value={value}
</Button> onChange={onChange}
</ActionGroup> units={["minutes", "hours", "days"]}
</FormAccess> />
</FormPanel> )}
</PageSection> />
</> </FormGroup>
<FormGroup
label={t("loginActionTimeout")}
fieldId="loginActionTimeout"
id="login-action-timeout-label"
labelIcon={
<HelpItem
helpText="realm-settings-help:loginActionTimeout"
forLabel={t("loginActionTimeout")}
forID="loginActionTimeout"
id="loginActionTimeout"
/>
}
>
<Controller
name="accessCodeLifespanUserAction"
defaultValue=""
control={control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-login-action-timeout"
data-testid="login-action-timeout-input"
aria-label="login-action-timeout-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<ActionGroup>
<Button
variant="primary"
type="submit"
data-testid="sessions-tab-save"
isDisabled={!formState.isDirty}
>
{t("common:save")}
</Button>
<Button variant="link" onClick={reset}>
{t("common:revert")}
</Button>
</ActionGroup>
</FormAccess>
</FormPanel>
</PageSection>
); );
}; };

View file

@ -55,314 +55,308 @@ export const RealmSettingsThemesTab = ({
}); });
return ( return (
<> <PageSection variant="light">
<PageSection variant="light"> <FormAccess
<FormAccess isHorizontal
isHorizontal role="manage-realm"
role="manage-realm" className="pf-u-mt-lg"
className="pf-u-mt-lg" onSubmit={handleSubmit(save)}
onSubmit={handleSubmit(save)} >
<FormGroup
label={t("loginTheme")}
fieldId="kc-login-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:loginTheme"
forLabel={t("loginTheme")}
forID="kc-login-theme"
/>
}
> >
<FormGroup <Controller
label={t("loginTheme")} name="loginTheme"
fieldId="kc-login-theme" control={control}
labelIcon={ defaultValue=""
<HelpItem render={({ onChange, value }) => (
helpText="realm-settings-help:loginTheme" <Select
forLabel={t("loginTheme")} toggleId="kc-login-theme"
forID="kc-login-theme" onToggle={() => setLoginThemeOpen(!loginThemeOpen)}
/> onSelect={(_, value) => {
} onChange(value as string);
> setLoginThemeOpen(false);
<Controller }}
name="loginTheme" selections={value}
control={control} variant={SelectVariant.single}
defaultValue="" aria-label={t("loginTheme")}
render={({ onChange, value }) => ( isOpen={loginThemeOpen}
<Select placeholderText="Select a theme"
toggleId="kc-login-theme" data-testid="select-login-theme"
onToggle={() => setLoginThemeOpen(!loginThemeOpen)}
onSelect={(_, value) => {
onChange(value as string);
setLoginThemeOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("loginTheme")}
isOpen={loginThemeOpen}
placeholderText="Select a theme"
data-testid="select-login-theme"
>
{themeTypes.login.map((theme, idx) => (
<SelectOption
selected={theme.name === value}
key={`login-theme-${idx}`}
value={theme.name}
>
{t(`${theme.name}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("accountTheme")}
fieldId="kc-account-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:accountTheme"
forLabel={t("accountTheme")}
forID="kc-account-theme"
/>
}
>
<Controller
name="accountTheme"
control={control}
defaultValue=""
render={({ onChange, value }) => (
<Select
toggleId="kc-account-theme"
onToggle={() => setAccountThemeOpen(!accountThemeOpen)}
onSelect={(_, value) => {
onChange(value as string);
setAccountThemeOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("accountTheme")}
isOpen={accountThemeOpen}
placeholderText="Select a theme"
data-testid="select-account-theme"
>
{themeTypes.account.map((theme, idx) => (
<SelectOption
selected={theme.name === value}
key={`account-theme-${idx}`}
value={theme.name}
>
{t(`${theme.name}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("adminTheme")}
fieldId="kc-admin-console-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:adminConsoleTheme"
forLabel={t("adminTheme")}
forID="kc-admin-console-theme"
/>
}
>
<Controller
name="adminTheme"
control={control}
defaultValue=""
render={({ onChange, value }) => (
<Select
toggleId="kc-admin-console-theme"
onToggle={() =>
setAdminConsoleThemeOpen(!adminConsoleThemeOpen)
}
onSelect={(_, value) => {
onChange(value as string);
setAdminConsoleThemeOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("adminConsoleTheme")}
isOpen={adminConsoleThemeOpen}
placeholderText="Select a theme"
data-testid="select-admin-theme"
>
{themeTypes.admin.map((theme, idx) => (
<SelectOption
selected={theme.name === value}
key={`admin-theme-${idx}`}
value={theme.name}
>
{t(`${theme.name}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("emailTheme")}
fieldId="kc-email-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:emailTheme"
forLabel={t("emailTheme")}
forID="kc-email-theme"
/>
}
>
<Controller
name="emailTheme"
control={control}
defaultValue=""
render={({ onChange, value }) => (
<Select
toggleId="kc-email-theme"
onToggle={() => setEmailThemeOpen(!emailThemeOpen)}
onSelect={(_, value) => {
onChange(value as string);
setEmailThemeOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("emailTheme")}
isOpen={emailThemeOpen}
placeholderText="Select a theme"
data-testid="select-email-theme"
>
{themeTypes.email.map((theme, idx) => (
<SelectOption
selected={theme.name === value}
key={`email-theme-${idx}`}
value={theme.name}
>
{t(`${theme.name}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("internationalization")}
fieldId="kc-internationalization"
>
<Controller
name="internationalizationEnabled"
control={control}
defaultValue={false}
render={({ onChange, value }) => (
<Switch
id="kc-t-internationalization"
label={t("common:enabled")}
labelOff={t("common:disabled")}
isChecked={value}
data-testid={
value
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={onChange}
/>
)}
/>
</FormGroup>
{internationalizationEnabled && (
<>
<FormGroup
label={t("supportedLocales")}
fieldId="kc-t-supported-locales"
> >
<Controller {themeTypes.login.map((theme, idx) => (
name="supportedLocales" <SelectOption
control={control} selected={theme.name === value}
defaultValue={themeTypes?.account![0].locales} key={`login-theme-${idx}`}
render={({ value, onChange }) => ( value={theme.name}
<Select >
toggleId="kc-t-supported-locales" {t(`${theme.name}`)}
onToggle={() => { </SelectOption>
setSupportedLocalesOpen(!supportedLocalesOpen); ))}
}} </Select>
onSelect={(_, v) => { )}
const option = v as string; />
if (!value) { </FormGroup>
onChange([option]); <FormGroup
} else if (value!.includes(option)) { label={t("accountTheme")}
onChange( fieldId="kc-account-theme"
value.filter((item: string) => item !== option) labelIcon={
); <HelpItem
} else { helpText="realm-settings-help:accountTheme"
onChange([...value, option]); forLabel={t("accountTheme")}
} forID="kc-account-theme"
}} />
onClear={() => { }
onChange([]); >
}} <Controller
selections={value} name="accountTheme"
variant={SelectVariant.typeaheadMulti} control={control}
aria-label={t("supportedLocales")} defaultValue=""
isOpen={supportedLocalesOpen} render={({ onChange, value }) => (
placeholderText={"Select locales"} <Select
> toggleId="kc-account-theme"
{themeTypes?.login![0].locales.map( onToggle={() => setAccountThemeOpen(!accountThemeOpen)}
(locale: string, idx: number) => ( onSelect={(_, value) => {
<SelectOption onChange(value as string);
selected={true} setAccountThemeOpen(false);
key={`locale-${idx}`} }}
value={locale} selections={value}
> variant={SelectVariant.single}
{t(`allSupportedLocales.${locale}`)} aria-label={t("accountTheme")}
</SelectOption> isOpen={accountThemeOpen}
) placeholderText="Select a theme"
)} data-testid="select-account-theme"
</Select> >
)} {themeTypes.account.map((theme, idx) => (
/> <SelectOption
</FormGroup> selected={theme.name === value}
<FormGroup label={t("defaultLocale")} fieldId="kc-default-locale"> key={`account-theme-${idx}`}
<Controller value={theme.name}
name="defaultLocale" >
control={control} {t(`${theme.name}`)}
defaultValue="" </SelectOption>
render={({ onChange, value }) => ( ))}
<Select </Select>
toggleId="kc-t-default-locale" )}
onToggle={() => setDefaultLocaleOpen(!defaultLocaleOpen)} />
onSelect={(_, value) => { </FormGroup>
onChange(value as string); <FormGroup
setDefaultLocaleOpen(false); label={t("adminTheme")}
}} fieldId="kc-admin-console-theme"
selections={value && t(`allSupportedLocales.${value}`)} labelIcon={
variant={SelectVariant.single} <HelpItem
aria-label={t("defaultLocale")} helpText="realm-settings-help:adminConsoleTheme"
isOpen={defaultLocaleOpen} forLabel={t("adminTheme")}
placeholderText="Select one" forID="kc-admin-console-theme"
data-testid="select-default-locale" />
> }
{watchSupportedLocales.map( >
(locale: string, idx: number) => ( <Controller
<SelectOption name="adminTheme"
key={`default-locale-${idx}`} control={control}
value={locale} defaultValue=""
> render={({ onChange, value }) => (
{t(`allSupportedLocales.${locale}`)} <Select
</SelectOption> toggleId="kc-admin-console-theme"
) onToggle={() =>
)} setAdminConsoleThemeOpen(!adminConsoleThemeOpen)
</Select> }
)} onSelect={(_, value) => {
/> onChange(value as string);
</FormGroup> setAdminConsoleThemeOpen(false);
</> }}
)} selections={value}
<ActionGroup> variant={SelectVariant.single}
<Button aria-label={t("adminConsoleTheme")}
variant="primary" isOpen={adminConsoleThemeOpen}
type="submit" placeholderText="Select a theme"
data-testid="themes-tab-save" data-testid="select-admin-theme"
>
{themeTypes.admin.map((theme, idx) => (
<SelectOption
selected={theme.name === value}
key={`admin-theme-${idx}`}
value={theme.name}
>
{t(`${theme.name}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("emailTheme")}
fieldId="kc-email-theme"
labelIcon={
<HelpItem
helpText="realm-settings-help:emailTheme"
forLabel={t("emailTheme")}
forID="kc-email-theme"
/>
}
>
<Controller
name="emailTheme"
control={control}
defaultValue=""
render={({ onChange, value }) => (
<Select
toggleId="kc-email-theme"
onToggle={() => setEmailThemeOpen(!emailThemeOpen)}
onSelect={(_, value) => {
onChange(value as string);
setEmailThemeOpen(false);
}}
selections={value}
variant={SelectVariant.single}
aria-label={t("emailTheme")}
isOpen={emailThemeOpen}
placeholderText="Select a theme"
data-testid="select-email-theme"
>
{themeTypes.email.map((theme, idx) => (
<SelectOption
selected={theme.name === value}
key={`email-theme-${idx}`}
value={theme.name}
>
{t(`${theme.name}`)}
</SelectOption>
))}
</Select>
)}
/>
</FormGroup>
<FormGroup
label={t("internationalization")}
fieldId="kc-internationalization"
>
<Controller
name="internationalizationEnabled"
control={control}
defaultValue={false}
render={({ onChange, value }) => (
<Switch
id="kc-t-internationalization"
label={t("common:enabled")}
labelOff={t("common:disabled")}
isChecked={value}
data-testid={
value
? "internationalization-enabled"
: "internationalization-disabled"
}
onChange={onChange}
/>
)}
/>
</FormGroup>
{internationalizationEnabled && (
<>
<FormGroup
label={t("supportedLocales")}
fieldId="kc-t-supported-locales"
> >
{t("common:save")} <Controller
</Button> name="supportedLocales"
<Button variant="link" onClick={reset}> control={control}
{t("common:revert")} defaultValue={themeTypes?.account![0].locales}
</Button> render={({ value, onChange }) => (
</ActionGroup> <Select
</FormAccess> toggleId="kc-t-supported-locales"
</PageSection> onToggle={() => {
</> setSupportedLocalesOpen(!supportedLocalesOpen);
}}
onSelect={(_, v) => {
const option = v as string;
if (!value) {
onChange([option]);
} else if (value!.includes(option)) {
onChange(
value.filter((item: string) => item !== option)
);
} else {
onChange([...value, option]);
}
}}
onClear={() => {
onChange([]);
}}
selections={value}
variant={SelectVariant.typeaheadMulti}
aria-label={t("supportedLocales")}
isOpen={supportedLocalesOpen}
placeholderText={"Select locales"}
>
{themeTypes?.login![0].locales.map(
(locale: string, idx: number) => (
<SelectOption
selected={true}
key={`locale-${idx}`}
value={locale}
>
{t(`allSupportedLocales.${locale}`)}
</SelectOption>
)
)}
</Select>
)}
/>
</FormGroup>
<FormGroup label={t("defaultLocale")} fieldId="kc-default-locale">
<Controller
name="defaultLocale"
control={control}
defaultValue=""
render={({ onChange, value }) => (
<Select
toggleId="kc-t-default-locale"
onToggle={() => setDefaultLocaleOpen(!defaultLocaleOpen)}
onSelect={(_, value) => {
onChange(value as string);
setDefaultLocaleOpen(false);
}}
selections={value && t(`allSupportedLocales.${value}`)}
variant={SelectVariant.single}
aria-label={t("defaultLocale")}
isOpen={defaultLocaleOpen}
placeholderText="Select one"
data-testid="select-default-locale"
>
{watchSupportedLocales.map(
(locale: string, idx: number) => (
<SelectOption
key={`default-locale-${idx}`}
value={locale}
>
{t(`allSupportedLocales.${locale}`)}
</SelectOption>
)
)}
</Select>
)}
/>
</FormGroup>
</>
)}
<ActionGroup>
<Button variant="primary" type="submit" data-testid="themes-tab-save">
{t("common:save")}
</Button>
<Button variant="link" onClick={reset}>
{t("common:revert")}
</Button>
</ActionGroup>
</FormAccess>
</PageSection>
); );
}; };

View file

@ -101,302 +101,259 @@ export const RealmSettingsTokensTab = ({
} }
}; };
return ( return (
<> <PageSection variant="light">
<PageSection variant="light"> <FormPanel
<FormPanel title={t("realm-settings:general")}
title={t("realm-settings:general")} className="kc-sso-session-template"
className="kc-sso-session-template" >
<FormAccess
isHorizontal
role="manage-realm"
onSubmit={form.handleSubmit(save)}
> >
<FormAccess <FormGroup
isHorizontal label={t("defaultSigAlg")}
role="manage-realm" fieldId="kc-default-signature-algorithm"
onSubmit={form.handleSubmit(save)} labelIcon={
> <HelpItem
<FormGroup helpText="realm-settings-help:defaultSigAlg"
label={t("defaultSigAlg")} forLabel={t("defaultSigAlg")}
fieldId="kc-default-signature-algorithm" forID={t("common:helpLabel", { label: t("algorithm") })}
labelIcon={
<HelpItem
helpText="realm-settings-help:defaultSigAlg"
forLabel={t("defaultSigAlg")}
forID={t("common:helpLabel", { label: t("algorithm") })}
/>
}
>
<Controller
name="defaultSignatureAlgorithm"
defaultValue={"RS256"}
control={form.control}
render={({ onChange, value }) => (
<Select
toggleId="kc-default-sig-alg"
onToggle={() =>
setDefaultSigAlgDrpdwnOpen(!defaultSigAlgDrpdwnIsOpen)
}
onSelect={(_, value) => {
onChange(value.toString());
setDefaultSigAlgDrpdwnOpen(false);
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("defaultSigAlg")}
isOpen={defaultSigAlgDrpdwnIsOpen}
data-testid="select-default-sig-alg"
>
{defaultSigAlgOptions!.map((p, idx) => (
<SelectOption
selected={p === value}
key={`default-sig-alg-${idx}`}
value={p}
></SelectOption>
))}
</Select>
)}
/> />
</FormGroup> }
</FormAccess> >
</FormPanel> <Controller
<FormPanel name="defaultSignatureAlgorithm"
title={t("realm-settings:refreshTokens")} defaultValue={"RS256"}
className="kc-client-session-template" control={form.control}
render={({ onChange, value }) => (
<Select
toggleId="kc-default-sig-alg"
onToggle={() =>
setDefaultSigAlgDrpdwnOpen(!defaultSigAlgDrpdwnIsOpen)
}
onSelect={(_, value) => {
onChange(value.toString());
setDefaultSigAlgDrpdwnOpen(false);
}}
selections={[value.toString()]}
variant={SelectVariant.single}
aria-label={t("defaultSigAlg")}
isOpen={defaultSigAlgDrpdwnIsOpen}
data-testid="select-default-sig-alg"
>
{defaultSigAlgOptions!.map((p, idx) => (
<SelectOption
selected={p === value}
key={`default-sig-alg-${idx}`}
value={p}
></SelectOption>
))}
</Select>
)}
/>
</FormGroup>
</FormAccess>
</FormPanel>
<FormPanel
title={t("realm-settings:refreshTokens")}
className="kc-client-session-template"
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={form.handleSubmit(save)}
> >
<FormAccess <FormGroup
isHorizontal hasNoPaddingTop
role="manage-realm" label={t("revokeRefreshToken")}
className="pf-u-mt-lg" fieldId="kc-revoke-refresh-token"
onSubmit={form.handleSubmit(save)} labelIcon={
<HelpItem
helpText="realm-settings-help:revokeRefreshToken"
forLabel={t("revokeRefreshToken")}
forID="revokeRefreshToken"
id="revokeRefreshToken"
/>
}
> >
<FormGroup <Controller
hasNoPaddingTop name="revokeRefreshToken"
label={t("revokeRefreshToken")} control={form.control}
fieldId="kc-revoke-refresh-token" defaultValue={false}
labelIcon={ render={({ onChange, value }) => (
<HelpItem <Switch
helpText="realm-settings-help:revokeRefreshToken" id="kc-revoke-refresh-token"
forLabel={t("revokeRefreshToken")} data-testid="revoke-refresh-token-switch"
forID="revokeRefreshToken" aria-label="revoke-refresh-token-switch"
id="revokeRefreshToken" label={t("common:enabled")}
labelOff={t("common:disabled")}
isChecked={value}
onChange={onChange}
/> />
} )}
> />
<Controller </FormGroup>
name="revokeRefreshToken" <FormGroup
control={form.control} label={t("refreshTokenMaxReuse")}
defaultValue={false} labelIcon={
render={({ onChange, value }) => ( <HelpItem
<Switch helpText="realm-settings-help:refreshTokenMaxReuse"
id="kc-revoke-refresh-token" forLabel={t("refreshTokenMaxReuse")}
data-testid="revoke-refresh-token-switch" forID="refreshTokenMaxReuse"
aria-label="revoke-refresh-token-switch"
label={t("common:enabled")}
labelOff={t("common:disabled")}
isChecked={value}
onChange={onChange}
/>
)}
/> />
</FormGroup> }
<FormGroup fieldId="refreshTokenMaxReuse"
label={t("refreshTokenMaxReuse")} >
labelIcon={ <Controller
<HelpItem name="refreshTokenMaxReuse"
helpText="realm-settings-help:refreshTokenMaxReuse" defaultValue={0}
forLabel={t("refreshTokenMaxReuse")} control={form.control}
forID="refreshTokenMaxReuse" render={({ onChange, value }) => (
<NumberInput
type="text"
id="refreshTokenMaxReuseMs"
value={value}
onPlus={() => onChange(value + 1)}
onMinus={() => onChange(value - 1)}
onChange={(event) =>
onChange(Number((event.target as HTMLInputElement).value))
}
/> />
} )}
fieldId="refreshTokenMaxReuse" />
> </FormGroup>
<Controller </FormAccess>
name="refreshTokenMaxReuse" </FormPanel>
defaultValue={0} <FormPanel
control={form.control} title={t("realm-settings:accessTokens")}
render={({ onChange, value }) => ( className="kc-offline-session-template"
<NumberInput >
type="text" <FormAccess
id="refreshTokenMaxReuseMs" isHorizontal
value={value} role="manage-realm"
onPlus={() => onChange(value + 1)} className="pf-u-mt-lg"
onMinus={() => onChange(value - 1)} onSubmit={form.handleSubmit(save)}
onChange={(event) =>
onChange(Number((event.target as HTMLInputElement).value))
}
/>
)}
/>
</FormGroup>
</FormAccess>
</FormPanel>
<FormPanel
title={t("realm-settings:accessTokens")}
className="kc-offline-session-template"
> >
<FormAccess <FormGroup
isHorizontal label={t("accessTokenLifespan")}
role="manage-realm" fieldId="accessTokenLifespan"
className="pf-u-mt-lg" helperText={`It is recommended for this value to be shorter than the SSO session idle timeout: ${interpolateTimespan(
onSubmit={form.handleSubmit(save)} forHumans(realm?.ssoSessionIdleTimeout!)
> )}`}
<FormGroup labelIcon={
label={t("accessTokenLifespan")} <HelpItem
fieldId="accessTokenLifespan" helpText="realm-settings-help:accessTokenLifespan"
helperText={`It is recommended for this value to be shorter than the SSO session idle timeout: ${interpolateTimespan( forLabel={t("accessTokenLifespan")}
forHumans(realm?.ssoSessionIdleTimeout!) forID="accessTokenLifespan"
)}`} id="accessTokenLifespan"
labelIcon={
<HelpItem
helpText="realm-settings-help:accessTokenLifespan"
forLabel={t("accessTokenLifespan")}
forID="accessTokenLifespan"
id="accessTokenLifespan"
/>
}
>
<Controller
name="accessTokenLifespan"
defaultValue=""
helperTextInvalid={t("common:required")}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
validated={
value > realm?.ssoSessionIdleTimeout!
? "warning"
: "default"
}
className="kc-access-token-lifespan"
data-testid="access-token-lifespan-input"
aria-label="access-token-lifespan"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
>
<Controller
name="accessTokenLifespan"
defaultValue=""
helperTextInvalid={t("common:required")}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
validated={
value > realm?.ssoSessionIdleTimeout!
? "warning"
: "default"
}
className="kc-access-token-lifespan"
data-testid="access-token-lifespan-input"
aria-label="access-token-lifespan"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("accessTokenLifespanImplicitFlow")} label={t("accessTokenLifespanImplicitFlow")}
fieldId="accessTokenLifespanImplicitFlow" fieldId="accessTokenLifespanImplicitFlow"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:accessTokenLifespanImplicitFlow" helpText="realm-settings-help:accessTokenLifespanImplicitFlow"
forLabel={t("accessTokenLifespanImplicitFlow")} forLabel={t("accessTokenLifespanImplicitFlow")}
forID="accessTokenLifespanImplicitFlow" forID="accessTokenLifespanImplicitFlow"
id="accessTokenLifespanImplicitFlow" id="accessTokenLifespanImplicitFlow"
/>
}
>
<Controller
name="accessTokenLifespanForImplicitFlow"
defaultValue=""
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-access-token-lifespan-implicit"
data-testid="access-token-lifespan-implicit-input"
aria-label="access-token-lifespan-implicit"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
<FormGroup
label={t("clientLoginTimeout")}
fieldId="clientLoginTimeout"
labelIcon={
<HelpItem
helpText="realm-settings-help:clientLoginTimeout"
forLabel={t("clientLoginTimeout")}
forID="clientLoginTimeout"
id="clientLoginTimeout"
/>
}
>
<Controller
name="accessCodeLifespan"
defaultValue=""
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-client-login-timeout"
data-testid="client-login-timeout-input"
aria-label="client-login-timeout"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
{offlineSessionMaxEnabled && (
<FormGroup
label={t("offlineSessionMax")}
fieldId="offlineSessionMax"
id="offline-session-max-label"
labelIcon={
<HelpItem
helpText="realm-settings-help:offlineSessionMax"
forLabel={t("offlineSessionMax")}
forID="offlineSessionMax"
id="offlineSessionMax"
/>
}
>
<Controller
name="offlineSessionMaxLifespan"
defaultValue=""
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-offline-session-max"
data-testid="offline-session-max-input"
aria-label="offline-session-max-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
)}
</FormAccess>
</FormPanel>
<FormPanel
className="kc-login-settings-template"
title={t("actionTokens")}
>
<FormAccess
isHorizontal
role="manage-realm"
className="pf-u-mt-lg"
onSubmit={form.handleSubmit(save)}
> >
<Controller
name="accessTokenLifespanForImplicitFlow"
defaultValue=""
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-access-token-lifespan-implicit"
data-testid="access-token-lifespan-implicit-input"
aria-label="access-token-lifespan-implicit"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("clientLoginTimeout")}
fieldId="clientLoginTimeout"
labelIcon={
<HelpItem
helpText="realm-settings-help:clientLoginTimeout"
forLabel={t("clientLoginTimeout")}
forID="clientLoginTimeout"
id="clientLoginTimeout"
/>
}
>
<Controller
name="accessCodeLifespan"
defaultValue=""
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-client-login-timeout"
data-testid="client-login-timeout-input"
aria-label="client-login-timeout"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
{offlineSessionMaxEnabled && (
<FormGroup <FormGroup
label={t("userInitiatedActionLifespan")} label={t("offlineSessionMax")}
id="kc-user-initiated-action-lifespan" fieldId="offlineSessionMax"
fieldId="userInitiatedActionLifespan" id="offline-session-max-label"
labelIcon={ labelIcon={
<HelpItem <HelpItem
helpText="realm-settings-help:userInitiatedActionLifespan" helpText="realm-settings-help:offlineSessionMax"
forLabel={t("userInitiatedActionLifespan")} forLabel={t("offlineSessionMax")}
forID="userInitiatedActionLifespan" forID="offlineSessionMax"
id="userInitiatedActionLifespan" id="offlineSessionMax"
/> />
} }
> >
<Controller <Controller
name="actionTokenGeneratedByUserLifespan" name="offlineSessionMaxLifespan"
defaultValue={""} defaultValue=""
control={form.control} control={form.control}
render={({ onChange, value }) => ( render={({ onChange, value }) => (
<TimeSelector <TimeSelector
className="kc-user-initiated-action-lifespan" className="kc-offline-session-max"
data-testid="user-initiated-action-lifespan" data-testid="offline-session-max-input"
aria-label="user-initiated-action-lifespan" aria-label="offline-session-max-input"
value={value} value={value}
onChange={onChange} onChange={onChange}
units={["minutes", "hours", "days"]} units={["minutes", "hours", "days"]}
@ -404,141 +361,182 @@ export const RealmSettingsTokensTab = ({
)} )}
/> />
</FormGroup> </FormGroup>
<FormGroup )}
label={t("defaultAdminInitiated")} </FormAccess>
fieldId="defaultAdminInitiated" </FormPanel>
id="default-admin-initiated-label" <FormPanel
labelIcon={ className="kc-login-settings-template"
<HelpItem title={t("actionTokens")}
helpText="realm-settings-help:defaultAdminInitiatedActionLifespan" >
forLabel={t("defaultAdminInitiated")} <FormAccess
forID="defaultAdminInitiated" isHorizontal
id="defaultAdminInitiated" role="manage-realm"
className="pf-u-mt-lg"
onSubmit={form.handleSubmit(save)}
>
<FormGroup
label={t("userInitiatedActionLifespan")}
id="kc-user-initiated-action-lifespan"
fieldId="userInitiatedActionLifespan"
labelIcon={
<HelpItem
helpText="realm-settings-help:userInitiatedActionLifespan"
forLabel={t("userInitiatedActionLifespan")}
forID="userInitiatedActionLifespan"
id="userInitiatedActionLifespan"
/>
}
>
<Controller
name="actionTokenGeneratedByUserLifespan"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-user-initiated-action-lifespan"
data-testid="user-initiated-action-lifespan"
aria-label="user-initiated-action-lifespan"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/> />
} )}
> />
<Controller </FormGroup>
name="actionTokenGeneratedByAdminLifespan" <FormGroup
defaultValue={""} label={t("defaultAdminInitiated")}
control={form.control} fieldId="defaultAdminInitiated"
render={({ onChange, value }) => ( id="default-admin-initiated-label"
<TimeSelector labelIcon={
className="kc-default-admin-initiated" <HelpItem
data-testid="default-admin-initated-input" helpText="realm-settings-help:defaultAdminInitiatedActionLifespan"
aria-label="default-admin-initated-input" forLabel={t("defaultAdminInitiated")}
value={value} forID="defaultAdminInitiated"
onChange={onChange} id="defaultAdminInitiated"
units={["minutes", "hours", "days"]}
/>
)}
/> />
</FormGroup> }
<Text >
className="kc-override-action-tokens-subtitle" <Controller
component={TextVariants.h1} name="actionTokenGeneratedByAdminLifespan"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-default-admin-initiated"
data-testid="default-admin-initated-input"
aria-label="default-admin-initated-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<Text
className="kc-override-action-tokens-subtitle"
component={TextVariants.h1}
>
{t("overrideActionTokens")}
</Text>
<FormGroup
label={t("emailVerification")}
fieldId="emailVerification"
id="email-verification"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-verify-email"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-email-verification"
data-testid="email-verification-input"
aria-label="email-verification-input"
value={value}
onChange={(value: any) => onChange(value.toString())}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("idpAccountEmailVerification")}
fieldId="idpAccountEmailVerification"
id="idp-acct-label"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-idp-verify-account-via-email"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-idp-email-verification"
data-testid="idp-email-verification-input"
aria-label="idp-email-verification"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("forgotPassword")}
fieldId="forgotPassword"
id="forgot-password-label"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-reset-credentials"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-forgot-pw"
data-testid="forgot-pw-input"
aria-label="forgot-pw-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("executeActions")}
fieldId="executeActions"
id="execute-actions"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-execute-actions"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-execute-actions"
data-testid="execute-actions-input"
aria-label="execute-actions-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<ActionGroup>
<Button
variant="primary"
type="submit"
data-testid="tokens-tab-save"
isDisabled={!form.formState.isDirty}
> >
{t("overrideActionTokens")} {t("common:save")}
</Text> </Button>
<FormGroup <Button variant="link" onClick={reset}>
label={t("emailVerification")} {t("common:revert")}
fieldId="emailVerification" </Button>
id="email-verification" </ActionGroup>
> </FormAccess>
<Controller </FormPanel>
name="attributes.actionTokenGeneratedByUserLifespan-verify-email" </PageSection>
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-email-verification"
data-testid="email-verification-input"
aria-label="email-verification-input"
value={value}
onChange={(value: any) => onChange(value.toString())}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("idpAccountEmailVerification")}
fieldId="idpAccountEmailVerification"
id="idp-acct-label"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-idp-verify-account-via-email"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-idp-email-verification"
data-testid="idp-email-verification-input"
aria-label="idp-email-verification"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("forgotPassword")}
fieldId="forgotPassword"
id="forgot-password-label"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-reset-credentials"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-forgot-pw"
data-testid="forgot-pw-input"
aria-label="forgot-pw-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("executeActions")}
fieldId="executeActions"
id="execute-actions"
>
<Controller
name="attributes.actionTokenGeneratedByUserLifespan-execute-actions"
defaultValue={""}
control={form.control}
render={({ onChange, value }) => (
<TimeSelector
className="kc-execute-actions"
data-testid="execute-actions-input"
aria-label="execute-actions-input"
value={value}
onChange={onChange}
units={["minutes", "hours", "days"]}
/>
)}
/>
</FormGroup>
<ActionGroup>
<Button
variant="primary"
type="submit"
data-testid="tokens-tab-save"
isDisabled={!form.formState.isDirty}
>
{t("common:save")}
</Button>
<Button variant="link" onClick={reset}>
{t("common:revert")}
</Button>
</ActionGroup>
</FormAccess>
</FormPanel>
</PageSection>
</>
); );
}; };

View file

@ -39,15 +39,13 @@ export function EventsTypeTable({
onSelect={onSelect ? onSelect : undefined} onSelect={onSelect ? onSelect : undefined}
canSelectAll={!!onSelect} canSelectAll={!!onSelect}
toolbarItem={ toolbarItem={
<> addTypes && (
{addTypes && ( <ToolbarItem>
<ToolbarItem> <Button id="addTypes" onClick={addTypes} data-testid="addTypes">
<Button id="addTypes" onClick={addTypes} data-testid="addTypes"> {t("addSavedTypes")}
{t("addSavedTypes")} </Button>
</Button> </ToolbarItem>
</ToolbarItem> )
)}
</>
} }
actions={ actions={
!onDelete !onDelete

View file

@ -195,20 +195,18 @@ export const AESGeneratedForm = ({
/> />
)} )}
{editMode && ( {editMode && (
<> <TextInput
<TextInput ref={form.register()}
ref={form.register()} type="text"
type="text" id="name"
id="name" name="name"
name="name" defaultValue={providerId}
defaultValue={providerId} validated={
validated={ form.errors.name
form.errors.name ? ValidatedOptions.error
? ValidatedOptions.error : ValidatedOptions.default
: ValidatedOptions.default }
} />
/>
</>
)} )}
</FormGroup> </FormGroup>
<FormGroup <FormGroup

View file

@ -194,20 +194,18 @@ export const ECDSAGeneratedForm = ({
/> />
)} )}
{editMode && ( {editMode && (
<> <TextInput
<TextInput ref={form.register()}
ref={form.register()} type="text"
type="text" id="name"
id="name" name="name"
name="name" defaultValue={providerId}
defaultValue={providerId} validated={
validated={ form.errors.name
form.errors.name ? ValidatedOptions.error
? ValidatedOptions.error : ValidatedOptions.default
: ValidatedOptions.default }
} />
/>
</>
)} )}
</FormGroup> </FormGroup>
<FormGroup <FormGroup

View file

@ -202,20 +202,18 @@ export const HMACGeneratedForm = ({
/> />
)} )}
{editMode && ( {editMode && (
<> <TextInput
<TextInput ref={form.register()}
ref={form.register()} type="text"
type="text" id="name"
id="name" name="name"
name="name" defaultValue={providerId}
defaultValue={providerId} validated={
validated={ form.errors.name
form.errors.name ? ValidatedOptions.error
? ValidatedOptions.error : ValidatedOptions.default
: ValidatedOptions.default }
} />
/>
</>
)} )}
</FormGroup> </FormGroup>
<FormGroup <FormGroup

View file

@ -209,20 +209,18 @@ export const JavaKeystoreForm = ({
/> />
)} )}
{editMode && ( {editMode && (
<> <TextInput
<TextInput ref={form.register()}
ref={form.register()} type="text"
type="text" id="name"
id="name" name="name"
name="name" defaultValue={providerId}
defaultValue={providerId} validated={
validated={ form.errors.name
form.errors.name ? ValidatedOptions.error
? ValidatedOptions.error : ValidatedOptions.default
: ValidatedOptions.default }
} />
/>
</>
)} )}
</FormGroup> </FormGroup>
<FormGroup <FormGroup

View file

@ -203,20 +203,18 @@ export const RSAGeneratedForm = ({
/> />
)} )}
{editMode && ( {editMode && (
<> <TextInput
<TextInput ref={form.register()}
ref={form.register()} type="text"
type="text" id="name"
id="name" name="name"
name="name" defaultValue={providerId}
defaultValue={providerId} validated={
validated={ form.errors.name
form.errors.name ? ValidatedOptions.error
? ValidatedOptions.error : ValidatedOptions.default
: ValidatedOptions.default }
} />
/>
</>
)} )}
</FormGroup> </FormGroup>
<FormGroup <FormGroup

View file

@ -212,20 +212,18 @@ export const RSAForm = ({
/> />
)} )}
{editMode && ( {editMode && (
<> <TextInput
<TextInput ref={form.register()}
ref={form.register()} type="text"
type="text" id="name"
id="name" name="name"
name="name" defaultValue={providerId}
defaultValue={providerId} validated={
validated={ form.errors.name
form.errors.name ? ValidatedOptions.error
? ValidatedOptions.error : ValidatedOptions.default
: ValidatedOptions.default }
} />
/>
</>
)} )}
</FormGroup> </FormGroup>
<FormGroup <FormGroup

View file

@ -316,9 +316,7 @@ export const KerberosSettingsRequired = ({
)} )}
></Controller> ></Controller>
</FormGroup> </FormGroup>
) : ( ) : null}
<></>
)}
<FormGroup <FormGroup
label={t("updateFirstLogin")} label={t("updateFirstLogin")}

View file

@ -15,33 +15,31 @@ export const LdapMapperHardcodedLdapGroup = ({
const helpText = useTranslation("user-federation-help").t; const helpText = useTranslation("user-federation-help").t;
return ( return (
<> <FormGroup
<FormGroup label={t("group")}
label={t("group")} labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText={helpText("groupHelp")}
helpText={helpText("groupHelp")} forLabel={t("group")}
forLabel={t("group")} forID="kc-group"
forID="kc-group"
/>
}
fieldId="kc-group"
isRequired
>
<TextInput
isRequired
type="text"
id="kc-group"
data-testid="mapper-group-fld"
name="config.group[0]"
ref={form.register({ required: true })}
validated={
form.errors.config?.group
? ValidatedOptions.error
: ValidatedOptions.default
}
/> />
</FormGroup> }
</> fieldId="kc-group"
isRequired
>
<TextInput
isRequired
type="text"
id="kc-group"
data-testid="mapper-group-fld"
name="config.group[0]"
ref={form.register({ required: true })}
validated={
form.errors.config?.group
? ValidatedOptions.error
: ValidatedOptions.default
}
/>
</FormGroup>
); );
}; };

View file

@ -76,9 +76,7 @@ export const LdapMapperList = () => {
}; };
const MapperLink = (mapper: ComponentRepresentation) => ( const MapperLink = (mapper: ComponentRepresentation) => (
<> <Link to={`${getUrl(url)}/${mapper.id}`}>{mapper.name}</Link>
<Link to={`${getUrl(url)}/${mapper.id}`}>{mapper.name}</Link>
</>
); );
return ( return (

View file

@ -15,35 +15,33 @@ export const LdapMapperMsadUserAccount = ({
const helpText = useTranslation("user-federation-help").t; const helpText = useTranslation("user-federation-help").t;
return ( return (
<> <FormGroup
<FormGroup label={t("passwordPolicyHintsEnabled")}
label={t("passwordPolicyHintsEnabled")} labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText={helpText("passwordPolicyHintsEnabledHelp")}
helpText={helpText("passwordPolicyHintsEnabledHelp")} forLabel={t("passwordPolicyHintsEnabled")}
forLabel={t("passwordPolicyHintsEnabled")} forID="kc-pw-policy-hints-enabled"
forID="kc-pw-policy-hints-enabled" />
}
fieldId="kc-der-formatted"
hasNoPaddingTop
>
<Controller
name="config.ldap-password-policy-hints-enabled"
defaultValue={["false"]}
control={form.control}
render={({ onChange, value }) => (
<Switch
id={"kc-pw-policy-hints-enabled"}
isDisabled={false}
onChange={(value) => onChange([`${value}`])}
isChecked={value[0] === "true"}
label={t("common:on")}
labelOff={t("common:off")}
/> />
} )}
fieldId="kc-der-formatted" ></Controller>
hasNoPaddingTop </FormGroup>
>
<Controller
name="config.ldap-password-policy-hints-enabled"
defaultValue={["false"]}
control={form.control}
render={({ onChange, value }) => (
<Switch
id={"kc-pw-policy-hints-enabled"}
isDisabled={false}
onChange={(value) => onChange([`${value}`])}
isChecked={value[0] === "true"}
label={t("common:on")}
labelOff={t("common:off")}
/>
)}
></Controller>
</FormGroup>
</>
); );
}; };

View file

@ -173,39 +173,35 @@ export const LdapMapperUserAttribute = ({
></Controller> ></Controller>
</FormGroup> </FormGroup>
{mapperType === "certificate-ldap-mapper" ? ( {mapperType === "certificate-ldap-mapper" ? (
<> <FormGroup
<FormGroup label={t("derFormatted")}
label={t("derFormatted")} labelIcon={
labelIcon={ <HelpItem
<HelpItem helpText={helpText("derFormattedHelp")}
helpText={helpText("derFormattedHelp")} forLabel={t("derFormatted")}
forLabel={t("derFormatted")} forID="kc-der-formatted"
forID="kc-der-formatted" />
}
fieldId="kc-der-formatted"
hasNoPaddingTop
>
<Controller
name="config.is-der-formatted"
defaultValue={["false"]}
control={form.control}
render={({ onChange, value }) => (
<Switch
id={"kc-der-formatted"}
isDisabled={false}
onChange={(value) => onChange([`${value}`])}
isChecked={value[0] === "true"}
label={t("common:on")}
labelOff={t("common:off")}
/> />
} )}
fieldId="kc-der-formatted" ></Controller>
hasNoPaddingTop </FormGroup>
> ) : null}
<Controller
name="config.is-der-formatted"
defaultValue={["false"]}
control={form.control}
render={({ onChange, value }) => (
<Switch
id={"kc-der-formatted"}
isDisabled={false}
onChange={(value) => onChange([`${value}`])}
isChecked={value[0] === "true"}
label={t("common:on")}
labelOff={t("common:off")}
/>
)}
></Controller>
</FormGroup>
</>
) : (
<></>
)}
</> </>
); );
}; };

View file

@ -190,9 +190,7 @@ export const SettingsCache = ({
)} )}
></Controller> ></Controller>
</FormGroup> </FormGroup>
) : ( ) : null}
<></>
)}
{_.isEqual(cachePolicyType, ["EVICT_DAILY"]) || {_.isEqual(cachePolicyType, ["EVICT_DAILY"]) ||
_.isEqual(cachePolicyType, ["EVICT_WEEKLY"]) ? ( _.isEqual(cachePolicyType, ["EVICT_WEEKLY"]) ? (
<> <>
@ -269,9 +267,7 @@ export const SettingsCache = ({
></Controller> ></Controller>
</FormGroup> </FormGroup>
</> </>
) : ( ) : null}
<></>
)}
{_.isEqual(cachePolicyType, ["MAX_LIFESPAN"]) ? ( {_.isEqual(cachePolicyType, ["MAX_LIFESPAN"]) ? (
<FormGroup <FormGroup
label={t("maxLifespan")} label={t("maxLifespan")}
@ -292,9 +288,7 @@ export const SettingsCache = ({
data-testid="kerberos-cache-lifespan" data-testid="kerberos-cache-lifespan"
/> />
</FormGroup> </FormGroup>
) : ( ) : null}
<></>
)}
</FormAccess> </FormAccess>
</> </>
); );

View file

@ -353,28 +353,26 @@ export const UserForm = ({
typeAheadAriaLabel="Select an action" typeAheadAriaLabel="Select an action"
control={control} control={control}
render={() => ( render={() => (
<> <InputGroup>
<InputGroup> <ChipGroup categoryName={" "}>
<ChipGroup categoryName={" "}> {selectedGroups.map((currentChip) => (
{selectedGroups.map((currentChip) => ( <Chip
<Chip key={currentChip.id}
key={currentChip.id} onClick={() => deleteItem(currentChip.name!)}
onClick={() => deleteItem(currentChip.name!)} >
> {currentChip.path}
{currentChip.path} </Chip>
</Chip> ))}
))} </ChipGroup>
</ChipGroup> <Button
<Button id="kc-join-groups-button"
id="kc-join-groups-button" onClick={toggleModal}
onClick={toggleModal} variant="secondary"
variant="secondary" data-testid="join-groups-button"
data-testid="join-groups-button" >
> {t("users:joinGroups")}
{t("users:joinGroups")} </Button>
</Button> </InputGroup>
</InputGroup>
</>
)} )}
/> />
</FormGroup> </FormGroup>

View file

@ -189,9 +189,7 @@ export const UserGroups = () => {
refresh(); refresh();
}, [isDirectMembership]); }, [isDirectMembership]);
const AliasRenderer = (group: GroupRepresentation) => { const AliasRenderer = (group: GroupRepresentation) => group.name;
return <>{group.name}</>;
};
const toggleModal = () => { const toggleModal = () => {
setOpen(!open); setOpen(!open);
@ -232,17 +230,15 @@ export const UserGroups = () => {
directMembershipList.length === 0 || directMembershipList.length === 0 ||
isDirectMembership; isDirectMembership;
return ( return (
<> canLeaveGroup && (
{canLeaveGroup && ( <Button
<Button data-testid={`leave-${group.name}`}
data-testid={`leave-${group.name}`} onClick={() => leave(group)}
onClick={() => leave(group)} variant="link"
variant="link" >
> {t("leave")}
{t("leave")} </Button>
</Button> )
)}
</>
); );
}; };

View file

@ -63,14 +63,12 @@ export const UsersSection = () => {
); );
const UserDetailLink = (user: UserRepresentation) => ( const UserDetailLink = (user: UserRepresentation) => (
<> <Link
<Link key={user.username}
key={user.username} to={toUser({ realm: realmName, id: user.id!, tab: "settings" })}
to={toUser({ realm: realmName, id: user.id!, tab: "settings" })} >
> {user.username}
{user.username} </Link>
</Link>
</>
); );
const loader = async (first?: number, max?: number, search?: string) => { const loader = async (first?: number, max?: number, search?: string) => {