Use shared form context instead (#35897)

fixes: #35429

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Erik Jan de Wit
2024-12-17 14:15:36 +01:00
committed by GitHub
parent 97f4a0d80f
commit 0ed7da7785
5 changed files with 106 additions and 134 deletions

View File

@@ -13,7 +13,7 @@ import {
Tooltip,
} from "@patternfly/react-core";
import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { Controller, FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { useAdminClient } from "../admin-client";
@@ -180,9 +180,10 @@ export const RealmSettingsTabs = () => {
const [tableData, setTableData] = useState<
Record<string, string>[] | undefined
>(undefined);
const { control, setValue, getValues } = useForm({
const form = useForm({
mode: "onChange",
});
const { control, setValue, getValues } = form;
const [key, setKey] = useState(0);
const refreshHeader = () => {
setKey(key + 1);
@@ -304,7 +305,7 @@ export const RealmSettingsTabs = () => {
const clientPoliciesPoliciesTab = useClientPoliciesTab("policies");
return (
<>
<FormProvider {...form}>
<Controller
name="enabled"
defaultValue={true}
@@ -464,6 +465,6 @@ export const RealmSettingsTabs = () => {
)}
</RoutableTabs>
</PageSection>
</>
</FormProvider>
);
};

View File

@@ -6,13 +6,11 @@ import {
PageSection,
Switch,
} from "@patternfly/react-core";
import { useEffect } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormPanel, HelpItem } from "@keycloak/keycloak-ui-shared";
import { FormAccess } from "../components/form/FormAccess";
import { TimeSelector } from "../components/time-selector/TimeSelector";
import { convertToFormValues } from "../util";
import "./realm-settings-section.css";
@@ -27,20 +25,14 @@ export const RealmSettingsSessionsTab = ({
}: RealmSettingsSessionsTabProps) => {
const { t } = useTranslation();
const { setValue, control, handleSubmit, formState } =
useForm<RealmRepresentation>();
const { control, handleSubmit, formState, reset } =
useFormContext<RealmRepresentation>();
const offlineSessionMaxEnabled = useWatch({
control,
name: "offlineSessionMaxLifespanEnabled",
});
const setupForm = () => {
convertToFormValues(realm, setValue);
};
useEffect(setupForm, []);
return (
<PageSection variant="light">
<FormPanel
@@ -378,7 +370,7 @@ export const RealmSettingsSessionsTab = ({
>
{t("save")}
</Button>
<Button variant="link" onClick={setupForm}>
<Button variant="link" onClick={() => reset(realm)}>
{t("revert")}
</Button>
</ActionGroup>

View File

@@ -20,8 +20,8 @@ import {
TextInput,
TextVariants,
} from "@patternfly/react-core";
import { useEffect, useState } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormAccess } from "../components/form/FormAccess";
import {
@@ -30,7 +30,7 @@ import {
} from "../components/time-selector/TimeSelector";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useWhoAmI } from "../context/whoami/WhoAmI";
import { beerify, convertToFormValues, sortProviders } from "../util";
import { beerify, sortProviders } from "../util";
import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled";
import "./realm-settings-section.css";
@@ -38,12 +38,10 @@ import "./realm-settings-section.css";
type RealmSettingsSessionsTabProps = {
realm: RealmRepresentation;
save: (realm: RealmRepresentation) => void;
reset?: () => void;
};
export const RealmSettingsTokensTab = ({
realm,
reset,
save,
}: RealmSettingsSessionsTabProps) => {
const { t } = useTranslation();
@@ -58,8 +56,8 @@ export const RealmSettingsTokensTab = ({
serverInfo.providers!["signature"].providers,
);
const form = useForm<RealmRepresentation>();
const { setValue, control } = form;
const { control, register, reset, formState, handleSubmit } =
useFormContext<RealmRepresentation>();
const offlineSessionMaxEnabled = useWatch({
control,
@@ -79,17 +77,13 @@ export const RealmSettingsTokensTab = ({
defaultValue: false,
});
useEffect(() => {
convertToFormValues(realm, setValue);
}, []);
return (
<PageSection variant="light">
<FormPanel title={t("general")} className="kc-sso-session-template">
<FormAccess
isHorizontal
role="manage-realm"
onSubmit={form.handleSubmit(save)}
onSubmit={handleSubmit(save)}
>
<FormGroup
label={t("defaultSigAlg")}
@@ -104,7 +98,7 @@ export const RealmSettingsTokensTab = ({
<Controller
name="defaultSignatureAlgorithm"
defaultValue={"RS256"}
control={form.control}
control={control}
render={({ field }) => (
<KeycloakSelect
toggleId="kc-default-sig-alg"
@@ -150,7 +144,7 @@ export const RealmSettingsTokensTab = ({
<Controller
name="oauth2DeviceCodeLifespan"
defaultValue={0}
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
id="oAuthDeviceCodeLifespan"
@@ -175,7 +169,7 @@ export const RealmSettingsTokensTab = ({
<Controller
name="oauth2DevicePollingInterval"
defaultValue={0}
control={form.control}
control={control}
render={({ field }) => (
<NumberInput
id="oAuthDevicePollingInterval"
@@ -211,7 +205,7 @@ export const RealmSettingsTokensTab = ({
<TextInput
id="shortVerificationUri"
placeholder={t("shortVerificationUri")}
{...form.register("attributes.shortVerificationUri")}
{...register("attributes.shortVerificationUri")}
/>
</FormGroup>
<FormGroup
@@ -226,7 +220,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="attributes.parRequestUriLifespan"
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
id="parRequestUriLifespan"
@@ -251,7 +245,7 @@ export const RealmSettingsTokensTab = ({
isHorizontal
role="manage-realm"
className="pf-v5-u-mt-lg"
onSubmit={form.handleSubmit(save)}
onSubmit={handleSubmit(save)}
>
<FormGroup
hasNoPaddingTop
@@ -266,7 +260,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="revokeRefreshToken"
control={form.control}
control={control}
defaultValue={false}
render={({ field }) => (
<Switch
@@ -295,7 +289,7 @@ export const RealmSettingsTokensTab = ({
<Controller
name="refreshTokenMaxReuse"
defaultValue={0}
control={form.control}
control={control}
render={({ field }) => (
<NumberInput
type="text"
@@ -323,7 +317,7 @@ export const RealmSettingsTokensTab = ({
isHorizontal
role="manage-realm"
className="pf-v5-u-mt-lg"
onSubmit={form.handleSubmit(save)}
onSubmit={handleSubmit(save)}
>
<FormGroup
label={t("accessTokenLifespan")}
@@ -337,7 +331,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="accessTokenLifespan"
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
validated={
@@ -380,7 +374,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="accessTokenLifespanForImplicitFlow"
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-access-token-lifespan-implicit"
@@ -404,7 +398,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="accessCodeLifespan"
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-client-login-timeout"
@@ -432,7 +426,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="offlineSessionMaxLifespan"
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-offline-session-max"
@@ -455,7 +449,7 @@ export const RealmSettingsTokensTab = ({
isHorizontal
role="manage-realm"
className="pf-v5-u-mt-lg"
onSubmit={form.handleSubmit(save)}
onSubmit={handleSubmit(save)}
>
<FormGroup
label={t("userInitiatedActionLifespan")}
@@ -470,7 +464,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="actionTokenGeneratedByUserLifespan"
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-user-initiated-action-lifespan"
@@ -496,7 +490,7 @@ export const RealmSettingsTokensTab = ({
>
<Controller
name="actionTokenGeneratedByAdminLifespan"
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-default-admin-initiated"
@@ -531,7 +525,7 @@ export const RealmSettingsTokensTab = ({
"actionTokenGeneratedByUserLifespan.verify-email",
)}`}
defaultValue=""
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-email-verification"
@@ -559,7 +553,7 @@ export const RealmSettingsTokensTab = ({
"actionTokenGeneratedByUserLifespan.idp-verify-account-via-email",
)}`}
defaultValue={""}
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-idp-email-verification"
@@ -587,7 +581,7 @@ export const RealmSettingsTokensTab = ({
"actionTokenGeneratedByUserLifespan.reset-credentials",
)}`}
defaultValue={""}
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-forgot-pw"
@@ -615,7 +609,7 @@ export const RealmSettingsTokensTab = ({
"actionTokenGeneratedByUserLifespan.execute-actions",
)}`}
defaultValue={""}
control={form.control}
control={control}
render={({ field }) => (
<TimeSelector
className="kc-execute-actions"
@@ -632,11 +626,11 @@ export const RealmSettingsTokensTab = ({
variant="primary"
type="submit"
data-testid="tokens-tab-save"
isDisabled={!form.formState.isDirty}
isDisabled={!formState.isDirty}
>
{t("save")}
</Button>
<Button variant="link" onClick={reset}>
<Button variant="link" onClick={() => reset(realm)}>
{t("revert")}
</Button>
</ActionGroup>

View File

@@ -7,14 +7,14 @@ import {
TabTitleText,
Tabs,
} from "@patternfly/react-core";
import { useEffect, useMemo, useState } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import { useMemo, useState } from "react";
import { FormProvider, useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormAccess } from "../../components/form/FormAccess";
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
import { useWhoAmI } from "../../context/whoami/WhoAmI";
import { DEFAULT_LOCALE } from "../../i18n/i18n";
import { convertToFormValues, localeToDisplayName } from "../../util";
import { localeToDisplayName } from "../../util";
import { EffectiveMessageBundles } from "./EffectiveMessageBundles";
import { RealmOverrides } from "./RealmOverrides";
@@ -33,8 +33,8 @@ export const LocalizationTab = ({
const { whoAmI } = useWhoAmI();
const [activeTab, setActiveTab] = useState(0);
const form = useForm();
const { setValue, control, handleSubmit, formState } = form;
const form = useFormContext<RealmRepresentation>();
const { control, reset, handleSubmit, formState } = form;
const defaultSupportedLocales = realm.supportedLocales?.length
? realm.supportedLocales
@@ -48,14 +48,7 @@ export const LocalizationTab = ({
return Array.from(new Set(locales));
}, [themeTypes]);
const setupForm = () => {
convertToFormValues(realm, setValue);
setValue("supportedLocales", defaultSupportedLocales);
};
useEffect(setupForm, []);
const watchSupportedLocales: string[] = useWatch({
const watchSupportedLocales = useWatch({
control,
name: "supportedLocales",
defaultValue: defaultSupportedLocales,
@@ -70,7 +63,7 @@ export const LocalizationTab = ({
const defaultLocales = useWatch({
name: "defaultLocale",
control,
defaultValue: realm.defaultLocale ? [realm.defaultLocale] : [],
defaultValue: "en",
});
return (
@@ -122,7 +115,7 @@ export const LocalizationTab = ({
defaultValue: DEFAULT_LOCALE,
}}
data-testid="select-default-locale"
options={watchSupportedLocales.map((l) => ({
options={watchSupportedLocales!.map((l) => ({
key: l,
value: localeToDisplayName(l, whoAmI.getLocale()) || l,
}))}
@@ -139,7 +132,7 @@ export const LocalizationTab = ({
>
{t("save")}
</Button>
<Button variant="link" onClick={setupForm}>
<Button variant="link" onClick={() => reset(realm)}>
{t("revert")}
</Button>
</ActionGroup>
@@ -152,8 +145,8 @@ export const LocalizationTab = ({
data-testid="rs-localization-realm-overrides-tab"
>
<RealmOverrides
internationalizationEnabled={internationalizationEnabled}
watchSupportedLocales={watchSupportedLocales}
internationalizationEnabled={internationalizationEnabled || false}
watchSupportedLocales={watchSupportedLocales || []}
realm={realm}
tableData={tableData}
/>
@@ -166,7 +159,7 @@ export const LocalizationTab = ({
>
<EffectiveMessageBundles
defaultSupportedLocales={defaultSupportedLocales}
defaultLocales={defaultLocales}
defaultLocales={[defaultLocales!]}
/>
</Tab>
</Tabs>

View File

@@ -1,12 +1,9 @@
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { FormProvider, useForm } from "react-hook-form";
import { ActionGroup, Button } from "@patternfly/react-core";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import { ActionGroup, Button } from "@patternfly/react-core";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { FormAccess } from "../../components/form/FormAccess";
import { HelpLinkTextInput } from "./HelpLinkTextInput";
import { convertToFormValues } from "../../util";
import "./security-defences.css";
@@ -17,71 +14,66 @@ type HeadersFormProps = {
export const HeadersForm = ({ realm, save }: HeadersFormProps) => {
const { t } = useTranslation();
const form = useForm();
const form = useFormContext<RealmRepresentation>();
const {
setValue,
reset,
formState: { isDirty },
handleSubmit,
} = form;
const setupForm = () => convertToFormValues(realm, setValue);
useEffect(setupForm, []);
return (
<FormProvider {...form}>
<FormAccess
isHorizontal
role="manage-realm"
className="keycloak__security-defences__form"
onSubmit={handleSubmit(save)}
>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xFrameOptions"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.contentSecurityPolicy"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.contentSecurityPolicyReportOnly"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xContentTypeOptions"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xRobotsTag"
url="https://developers.google.com/search/docs/advanced/robots/robots_meta_tag"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xXSSProtection"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.strictTransportSecurity"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.referrerPolicy"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy"
/>
<FormAccess
isHorizontal
role="manage-realm"
className="keycloak__security-defences__form"
onSubmit={handleSubmit(save)}
>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xFrameOptions"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.contentSecurityPolicy"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.contentSecurityPolicyReportOnly"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xContentTypeOptions"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xRobotsTag"
url="https://developers.google.com/search/docs/advanced/robots/robots_meta_tag"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.xXSSProtection"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.strictTransportSecurity"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security"
/>
<HelpLinkTextInput
fieldName="browserSecurityHeaders.referrerPolicy"
url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy"
/>
<ActionGroup>
<Button
variant="primary"
type="submit"
data-testid="headers-form-tab-save"
isDisabled={!isDirty}
>
{t("save")}
</Button>
<Button variant="link" onClick={setupForm}>
{t("revert")}
</Button>
</ActionGroup>
</FormAccess>
</FormProvider>
<ActionGroup>
<Button
variant="primary"
type="submit"
data-testid="headers-form-tab-save"
isDisabled={!isDirty}
>
{t("save")}
</Button>
<Button variant="link" onClick={() => reset(realm)}>
{t("revert")}
</Button>
</ActionGroup>
</FormAccess>
);
};