* partially solves issue #422 * fixed section * added not before now set / clear alerts
This commit is contained in:
parent
a81164ee2a
commit
3b1d89fedb
8 changed files with 216 additions and 217 deletions
|
@ -24,8 +24,7 @@ import { useRealm } from "./context/realm-context/RealmContext";
|
||||||
import { useAdminClient, asyncStateFetch } from "./context/auth/AdminClient";
|
import { useAdminClient, asyncStateFetch } from "./context/auth/AdminClient";
|
||||||
import { ErrorRenderer } from "./components/error/ErrorRenderer";
|
import { ErrorRenderer } from "./components/error/ErrorRenderer";
|
||||||
|
|
||||||
// This must match the id given as scrollableSelector in scroll-form
|
export const mainPageContentId = "kc-main-content-page-container";
|
||||||
const mainPageContentId = "kc-main-content-page-container";
|
|
||||||
|
|
||||||
const AppContexts = ({ children }: { children: ReactNode }) => (
|
const AppContexts = ({ children }: { children: ReactNode }) => (
|
||||||
<AccessContextProvider>
|
<AccessContextProvider>
|
||||||
|
|
|
@ -8,8 +8,6 @@ import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
Button,
|
Button,
|
||||||
ButtonVariant,
|
ButtonVariant,
|
||||||
Card,
|
|
||||||
CardBody,
|
|
||||||
ExpandableSection,
|
ExpandableSection,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
Split,
|
Split,
|
||||||
|
@ -37,9 +35,10 @@ import { AddHostDialog } from "./advanced/AddHostDialog";
|
||||||
import { FineGrainSamlEndpointConfig } from "./advanced/FineGrainSamlEndpointConfig";
|
import { FineGrainSamlEndpointConfig } from "./advanced/FineGrainSamlEndpointConfig";
|
||||||
import { AuthenticationOverrides } from "./advanced/AuthenticationOverrides";
|
import { AuthenticationOverrides } from "./advanced/AuthenticationOverrides";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
|
import { SaveOptions } from "./ClientDetails";
|
||||||
|
|
||||||
type AdvancedProps = {
|
type AdvancedProps = {
|
||||||
save: () => void;
|
save: (options?: SaveOptions) => void;
|
||||||
client: ClientRepresentation;
|
client: ClientRepresentation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,9 +67,9 @@ export const AdvancedTab = ({
|
||||||
const refresh = () => setKey(new Date().getTime());
|
const refresh = () => setKey(new Date().getTime());
|
||||||
const [nodes, setNodes] = useState(registeredNodes || {});
|
const [nodes, setNodes] = useState(registeredNodes || {});
|
||||||
|
|
||||||
const setNotBefore = (time: number) => {
|
const setNotBefore = (time: number, messageKey: string) => {
|
||||||
setValue(revocationFieldName, time);
|
setValue(revocationFieldName, time);
|
||||||
save();
|
save({ messageKey });
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseResult = (result: GlobalRequestResult, prefixKey: string) => {
|
const parseResult = (result: GlobalRequestResult, prefixKey: string) => {
|
||||||
|
@ -178,96 +177,95 @@ export const AdvancedTab = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollForm sections={sections}>
|
<ScrollForm sections={sections}>
|
||||||
<Card>
|
<>
|
||||||
<CardBody>
|
<Text className="pf-u-py-lg">
|
||||||
<Text>
|
<Trans i18nKey="clients-help:notBeforeIntro">
|
||||||
<Trans i18nKey="clients-help:notBeforeIntro">
|
In order to successfully push setup url on
|
||||||
In order to successfully push setup url on
|
<Link to={`/${realm}/clients/${id}/settings`}>{t("settings")}</Link>
|
||||||
<Link to={`/${realm}/clients/${id}/settings`}>
|
tab
|
||||||
{t("settings")}
|
</Trans>
|
||||||
</Link>
|
</Text>
|
||||||
tab
|
<FormAccess role="manage-clients" isHorizontal>
|
||||||
</Trans>
|
<FormGroup
|
||||||
</Text>
|
label={t("notBefore")}
|
||||||
</CardBody>
|
fieldId="kc-not-before"
|
||||||
<CardBody>
|
labelIcon={
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
<HelpItem
|
||||||
<FormGroup
|
helpText="clients-help:notBefore"
|
||||||
label={t("notBefore")}
|
forLabel={t("notBefore")}
|
||||||
fieldId="kc-not-before"
|
forID="kc-not-before"
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText="clients-help:notBefore"
|
|
||||||
forLabel={t("notBefore")}
|
|
||||||
forID="kc-not-before"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TextInput
|
|
||||||
type="text"
|
|
||||||
id="kc-not-before"
|
|
||||||
name="notBefore"
|
|
||||||
isReadOnly
|
|
||||||
value={formatDate()}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
}
|
||||||
<ActionGroup>
|
>
|
||||||
<Button
|
<TextInput
|
||||||
id="setToNow"
|
type="text"
|
||||||
variant="tertiary"
|
id="kc-not-before"
|
||||||
onClick={() => setNotBefore(moment.now() / 1000)}
|
name="notBefore"
|
||||||
>
|
isReadOnly
|
||||||
{t("setToNow")}
|
value={formatDate()}
|
||||||
</Button>
|
/>
|
||||||
<Button
|
</FormGroup>
|
||||||
id="clear"
|
<ActionGroup>
|
||||||
variant="tertiary"
|
<Button
|
||||||
onClick={() => setNotBefore(0)}
|
id="setToNow"
|
||||||
>
|
variant="tertiary"
|
||||||
{t("clear")}
|
onClick={() => {
|
||||||
</Button>
|
setNotBefore(moment.now() / 1000, "notBeforeSetToNow");
|
||||||
<Button id="push" variant="secondary" onClick={push}>
|
}}
|
||||||
{t("push")}
|
|
||||||
</Button>
|
|
||||||
</ActionGroup>
|
|
||||||
</FormAccess>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
<Card>
|
|
||||||
<CardBody>
|
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
|
||||||
<FormGroup
|
|
||||||
label={t("nodeReRegistrationTimeout")}
|
|
||||||
fieldId="kc-node-reregistration-timeout"
|
|
||||||
labelIcon={
|
|
||||||
<HelpItem
|
|
||||||
helpText="clients-help:nodeReRegistrationTimeout"
|
|
||||||
forLabel={t("nodeReRegistrationTimeout")}
|
|
||||||
forID="nodeReRegistrationTimeout"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Split hasGutter>
|
{t("setToNow")}
|
||||||
<SplitItem>
|
</Button>
|
||||||
<Controller
|
<Button
|
||||||
name="nodeReRegistrationTimeout"
|
id="clear"
|
||||||
defaultValue=""
|
variant="tertiary"
|
||||||
control={control}
|
onClick={() => {
|
||||||
render={({ onChange, value }) => (
|
setNotBefore(0, "notBeforeNowClear");
|
||||||
<TimeSelector value={value} onChange={onChange} />
|
}}
|
||||||
)}
|
>
|
||||||
/>
|
{t("clear")}
|
||||||
</SplitItem>
|
</Button>
|
||||||
<SplitItem>
|
<Button id="push" variant="secondary" onClick={push}>
|
||||||
<Button variant={ButtonVariant.secondary} onClick={save}>
|
{t("push")}
|
||||||
{t("common:save")}
|
</Button>
|
||||||
</Button>
|
</ActionGroup>
|
||||||
</SplitItem>
|
</FormAccess>
|
||||||
</Split>
|
</>
|
||||||
</FormGroup>
|
<>
|
||||||
</FormAccess>
|
<FormAccess role="manage-clients" isHorizontal>
|
||||||
</CardBody>
|
<FormGroup
|
||||||
<CardBody>
|
label={t("nodeReRegistrationTimeout")}
|
||||||
|
fieldId="kc-node-reregistration-timeout"
|
||||||
|
labelIcon={
|
||||||
|
<HelpItem
|
||||||
|
helpText="clients-help:nodeReRegistrationTimeout"
|
||||||
|
forLabel={t("nodeReRegistrationTimeout")}
|
||||||
|
forID="nodeReRegistrationTimeout"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Split hasGutter>
|
||||||
|
<SplitItem>
|
||||||
|
<Controller
|
||||||
|
name="nodeReRegistrationTimeout"
|
||||||
|
defaultValue=""
|
||||||
|
control={control}
|
||||||
|
render={({ onChange, value }) => (
|
||||||
|
<TimeSelector value={value} onChange={onChange} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SplitItem>
|
||||||
|
<SplitItem>
|
||||||
|
<Button
|
||||||
|
variant={ButtonVariant.secondary}
|
||||||
|
onClick={() => save()}
|
||||||
|
>
|
||||||
|
{t("common:save")}
|
||||||
|
</Button>
|
||||||
|
</SplitItem>
|
||||||
|
</Split>
|
||||||
|
</FormGroup>
|
||||||
|
</FormAccess>
|
||||||
|
<>
|
||||||
<DeleteNodeConfirm />
|
<DeleteNodeConfirm />
|
||||||
<AddHostDialog
|
<AddHostDialog
|
||||||
clientId={id!}
|
clientId={id!}
|
||||||
|
@ -345,105 +343,90 @@ export const AdvancedTab = ({
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</ExpandableSection>
|
</ExpandableSection>
|
||||||
</CardBody>
|
</>
|
||||||
</Card>
|
</>
|
||||||
<Card>
|
<>
|
||||||
{protocol === openIdConnect && (
|
{protocol === openIdConnect && (
|
||||||
<>
|
<>
|
||||||
<CardBody>
|
<Text className="pf-u-py-lg">
|
||||||
<Text>
|
{t("clients-help:fineGrainOpenIdConnectConfiguration")}
|
||||||
{t("clients-help:fineGrainOpenIdConnectConfiguration")}
|
</Text>
|
||||||
</Text>
|
<FineGrainOpenIdConnect
|
||||||
</CardBody>
|
control={control}
|
||||||
<CardBody>
|
save={() => save()}
|
||||||
<FineGrainOpenIdConnect
|
reset={() =>
|
||||||
control={control}
|
convertToFormValues(attributes, "attributes", setValue)
|
||||||
save={save}
|
}
|
||||||
reset={() =>
|
/>
|
||||||
convertToFormValues(attributes, "attributes", setValue)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CardBody>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{protocol !== openIdConnect && (
|
{protocol !== openIdConnect && (
|
||||||
<>
|
<>
|
||||||
<CardBody>
|
<Text className="pf-u-py-lg">
|
||||||
<Text>{t("clients-help:fineGrainSamlEndpointConfig")}</Text>
|
{t("clients-help:fineGrainSamlEndpointConfig")}
|
||||||
</CardBody>
|
</Text>
|
||||||
|
<FineGrainSamlEndpointConfig
|
||||||
<CardBody>
|
|
||||||
<FineGrainSamlEndpointConfig
|
|
||||||
control={control}
|
|
||||||
save={save}
|
|
||||||
reset={() =>
|
|
||||||
convertToFormValues(attributes, "attributes", setValue)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CardBody>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
{protocol === openIdConnect && (
|
|
||||||
<Card>
|
|
||||||
<CardBody>
|
|
||||||
<Text>{t("clients-help:openIdConnectCompatibilityModes")}</Text>
|
|
||||||
</CardBody>
|
|
||||||
<CardBody>
|
|
||||||
<OpenIdConnectCompatibilityModes
|
|
||||||
control={control}
|
control={control}
|
||||||
save={save}
|
save={() => save()}
|
||||||
reset={() =>
|
reset={() =>
|
||||||
resetFields(["exclude-session-state-from-auth-response"])
|
convertToFormValues(attributes, "attributes", setValue)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</>
|
||||||
</Card>
|
)}
|
||||||
)}
|
</>
|
||||||
<Card>
|
{protocol === openIdConnect && (
|
||||||
<CardBody>
|
<>
|
||||||
<Text>
|
<Text className="pf-u-py-lg">
|
||||||
{t("clients-help:advancedSettings" + toUpperCase(protocol!))}
|
{t("clients-help:openIdConnectCompatibilityModes")}
|
||||||
</Text>
|
</Text>
|
||||||
</CardBody>
|
<OpenIdConnectCompatibilityModes
|
||||||
<CardBody>
|
|
||||||
<AdvancedSettings
|
|
||||||
protocol={protocol}
|
|
||||||
control={control}
|
control={control}
|
||||||
save={save}
|
save={() => save()}
|
||||||
reset={() => {
|
reset={() =>
|
||||||
resetFields([
|
resetFields(["exclude-session-state-from-auth-response"])
|
||||||
"saml-assertion-lifespan",
|
}
|
||||||
"access-token-lifespan",
|
|
||||||
"tls-client-certificate-bound-access-tokens",
|
|
||||||
"pkce-code-challenge-method",
|
|
||||||
]);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</CardBody>
|
</>
|
||||||
</Card>
|
)}
|
||||||
<Card>
|
<>
|
||||||
<CardBody>
|
<Text className="pf-u-py-lg">
|
||||||
<Text>{t("clients-help:authenticationOverrides")}</Text>
|
{t("clients-help:advancedSettings" + toUpperCase(protocol!))}
|
||||||
</CardBody>
|
</Text>
|
||||||
<CardBody>
|
<AdvancedSettings
|
||||||
<AuthenticationOverrides
|
protocol={protocol}
|
||||||
protocol={protocol}
|
control={control}
|
||||||
control={control}
|
save={() => save()}
|
||||||
save={save}
|
reset={() => {
|
||||||
reset={() => {
|
resetFields([
|
||||||
setValue(
|
"saml-assertion-lifespan",
|
||||||
"authenticationFlowBindingOverrides.browser",
|
"access-token-lifespan",
|
||||||
authenticationFlowBindingOverrides?.browser
|
"tls-client-certificate-bound-access-tokens",
|
||||||
);
|
"pkce-code-challenge-method",
|
||||||
setValue(
|
]);
|
||||||
"authenticationFlowBindingOverrides.direct_grant",
|
}}
|
||||||
authenticationFlowBindingOverrides?.direct_grant
|
/>
|
||||||
);
|
</>
|
||||||
}}
|
<>
|
||||||
/>
|
<Text className="pf-u-py-lg">
|
||||||
</CardBody>
|
{t("clients-help:authenticationOverrides")}
|
||||||
</Card>
|
</Text>
|
||||||
|
<AuthenticationOverrides
|
||||||
|
protocol={protocol}
|
||||||
|
control={control}
|
||||||
|
save={() => save()}
|
||||||
|
reset={() => {
|
||||||
|
setValue(
|
||||||
|
"authenticationFlowBindingOverrides.browser",
|
||||||
|
authenticationFlowBindingOverrides?.browser
|
||||||
|
);
|
||||||
|
setValue(
|
||||||
|
"authenticationFlowBindingOverrides.direct_grant",
|
||||||
|
authenticationFlowBindingOverrides?.direct_grant
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
</ScrollForm>
|
</ScrollForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -110,6 +110,11 @@ export type ClientForm = Omit<
|
||||||
webOrigins: MultiLine[];
|
webOrigins: MultiLine[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SaveOptions = {
|
||||||
|
confirmed?: boolean;
|
||||||
|
messageKey?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const ClientDetails = () => {
|
export const ClientDetails = () => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
@ -180,7 +185,12 @@ export const ClientDetails = () => {
|
||||||
);
|
);
|
||||||
}, [clientId]);
|
}, [clientId]);
|
||||||
|
|
||||||
const save = async (confirmed: boolean | undefined = false) => {
|
const save = async (
|
||||||
|
{ confirmed = false, messageKey = "clientSaveSuccess" }: SaveOptions = {
|
||||||
|
confirmed: false,
|
||||||
|
messageKey: "clientSaveSuccess",
|
||||||
|
}
|
||||||
|
) => {
|
||||||
if (await form.trigger()) {
|
if (await form.trigger()) {
|
||||||
if (
|
if (
|
||||||
client?.publicClient &&
|
client?.publicClient &&
|
||||||
|
@ -208,7 +218,7 @@ export const ClientDetails = () => {
|
||||||
await adminClient.clients.update({ id: clientId }, newClient);
|
await adminClient.clients.update({ id: clientId }, newClient);
|
||||||
setupForm(newClient);
|
setupForm(newClient);
|
||||||
setClient(newClient);
|
setClient(newClient);
|
||||||
addAlert(t("clientSaveSuccess"), AlertVariant.success);
|
addAlert(t(messageKey), AlertVariant.success);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addAlert(`${t("clientSaveError")} '${error}'`, AlertVariant.danger);
|
addAlert(`${t("clientSaveError")} '${error}'`, AlertVariant.danger);
|
||||||
}
|
}
|
||||||
|
@ -231,7 +241,7 @@ export const ClientDetails = () => {
|
||||||
})}
|
})}
|
||||||
open={changeAuthenticatorOpen}
|
open={changeAuthenticatorOpen}
|
||||||
toggleDialog={toggleChangeAuthenticator}
|
toggleDialog={toggleChangeAuthenticator}
|
||||||
onConfirm={() => save(true)}
|
onConfirm={() => save({ confirmed: true })}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{t("changeAuthenticatorConfirm", {
|
{t("changeAuthenticatorConfirm", {
|
||||||
|
@ -272,7 +282,7 @@ export const ClientDetails = () => {
|
||||||
eventKey="settings"
|
eventKey="settings"
|
||||||
title={<TabTitleText>{t("common:settings")}</TabTitleText>}
|
title={<TabTitleText>{t("common:settings")}</TabTitleText>}
|
||||||
>
|
>
|
||||||
<ClientSettings save={save} />
|
<ClientSettings save={() => save()} />
|
||||||
</Tab>
|
</Tab>
|
||||||
{publicClient && (
|
{publicClient && (
|
||||||
<Tab
|
<Tab
|
||||||
|
@ -280,7 +290,7 @@ export const ClientDetails = () => {
|
||||||
eventKey="credentials"
|
eventKey="credentials"
|
||||||
title={<TabTitleText>{t("credentials")}</TabTitleText>}
|
title={<TabTitleText>{t("credentials")}</TabTitleText>}
|
||||||
>
|
>
|
||||||
<Credentials clientId={clientId} save={save} />
|
<Credentials clientId={clientId} save={() => save()} />
|
||||||
</Tab>
|
</Tab>
|
||||||
)}
|
)}
|
||||||
<Tab
|
<Tab
|
||||||
|
|
|
@ -126,7 +126,7 @@ export const AdvancedSettings = ({
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="attributes.pkce-code-challenge-method"
|
name="attributes.pkce-code-challenge-method"
|
||||||
defaultValue={false}
|
defaultValue=""
|
||||||
control={control}
|
control={control}
|
||||||
render={({ onChange, value }) => (
|
render={({ onChange, value }) => (
|
||||||
<Select
|
<Select
|
||||||
|
@ -138,10 +138,12 @@ export const AdvancedSettings = ({
|
||||||
onChange(value);
|
onChange(value);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
selections={[value]}
|
selections={[value || t("common:choose")]}
|
||||||
>
|
>
|
||||||
{["", "S256", "plain"].map((v) => (
|
{["", "S256", "plain"].map((v) => (
|
||||||
<SelectOption key={v} value={v} />
|
<SelectOption key={v} value={v}>
|
||||||
|
{v || t("common:choose")}
|
||||||
|
</SelectOption>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -134,6 +134,8 @@
|
||||||
"notBefore": "Not before",
|
"notBefore": "Not before",
|
||||||
"setToNow": "Set to now",
|
"setToNow": "Set to now",
|
||||||
"noAdminUrlSet": "No push sent. No admin URI configured or no registered cluster nodes available",
|
"noAdminUrlSet": "No push sent. No admin URI configured or no registered cluster nodes available",
|
||||||
|
"notBeforeSetToNow": "Not Before set for client",
|
||||||
|
"notBeforeNowClear": "Not Before cleared for client",
|
||||||
"notBeforePushFail": "Failed to push \"not before\" to: {{failedNodes}}",
|
"notBeforePushFail": "Failed to push \"not before\" to: {{failedNodes}}",
|
||||||
"notBeforePushSuccess": "Successfully push \"not before\" to: {{successNodes}}",
|
"notBeforePushSuccess": "Successfully push \"not before\" to: {{successNodes}}",
|
||||||
"testClusterFail": "Failed verified availability for: {{failedNodes}}. Fix or unregister failed cluster nodes and try again",
|
"testClusterFail": "Failed verified availability for: {{failedNodes}}. Fix or unregister failed cluster nodes and try again",
|
||||||
|
|
|
@ -1,27 +1,37 @@
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { Title } from "@patternfly/react-core";
|
import {
|
||||||
|
Card,
|
||||||
|
CardBody,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
Title,
|
||||||
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
import "./form-panel.css";
|
import "./form-panel.css";
|
||||||
|
|
||||||
interface FormPanelProps extends React.HTMLProps<HTMLFormElement> {
|
type FormPanelProps = {
|
||||||
title: string;
|
title: string;
|
||||||
scrollId: string;
|
scrollId: string;
|
||||||
}
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
export const FormPanel = (props: FormPanelProps) => {
|
export const FormPanel = ({ title, children, scrollId }: FormPanelProps) => {
|
||||||
const { title, children, scrollId, ...rest } = props;
|
|
||||||
return (
|
return (
|
||||||
<section {...rest} className="kc-form-panel__panel">
|
<Card isFlat className="kc-form-panel__panel">
|
||||||
<Title
|
<CardHeader>
|
||||||
headingLevel="h4"
|
<CardTitle tabIndex={0}>
|
||||||
size="xl"
|
<Title
|
||||||
className="kc-form-panel__title"
|
headingLevel="h4"
|
||||||
id={scrollId}
|
size="xl"
|
||||||
tabIndex={0} // so that jumpLink sends focus to the section for a11y
|
className="kc-form-panel__title"
|
||||||
>
|
id={scrollId}
|
||||||
{title}
|
tabIndex={0} // so that jumpLink sends focus to the section for a11y
|
||||||
</Title>
|
>
|
||||||
{children}
|
{title}
|
||||||
</section>
|
</Title>
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody>{children}</CardBody>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
PageSection,
|
PageSection,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
|
import { mainPageContentId } from "../../App";
|
||||||
import { FormPanel } from "./FormPanel";
|
import { FormPanel } from "./FormPanel";
|
||||||
import "./scroll-form.css";
|
import "./scroll-form.css";
|
||||||
|
|
||||||
|
@ -16,9 +17,6 @@ type ScrollFormProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This must match the page id created in App.tsx unless another page section has been given hasScrollableContent
|
|
||||||
const mainPageContentId = "#kc-main-content-page-container";
|
|
||||||
|
|
||||||
const spacesToHyphens = (string: string): string => {
|
const spacesToHyphens = (string: string): string => {
|
||||||
return string.replace(/\s+/g, "-");
|
return string.replace(/\s+/g, "-");
|
||||||
};
|
};
|
||||||
|
@ -42,7 +40,7 @@ export const ScrollForm = ({ sections, children }: ScrollFormProps) => {
|
||||||
isVertical
|
isVertical
|
||||||
// scrollableSelector has to point to the id of the element whose scrollTop changes
|
// scrollableSelector has to point to the id of the element whose scrollTop changes
|
||||||
// to scroll the entire main section, it has to be the pf-c-page__main
|
// to scroll the entire main section, it has to be the pf-c-page__main
|
||||||
scrollableSelector={mainPageContentId}
|
scrollableSelector={`#${mainPageContentId}`}
|
||||||
label={t("jumpToSection")}
|
label={t("jumpToSection")}
|
||||||
offset={100}
|
offset={100}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
.kc-form-panel__panel {
|
.kc-form-panel__panel {
|
||||||
padding-top: var(--pf-global--spacer--lg);
|
margin-top: var(--pf-global--spacer--lg);
|
||||||
padding-bottom: var(--pf-global--spacer--2xl);
|
padding-bottom: var(--pf-global--spacer--2xl);
|
||||||
max-width: 768px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kc-form-panel__title {
|
|
||||||
margin-bottom: var(--pf-global--spacer--lg);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue