Use password field + copy to clipboard button (#2524)
This commit is contained in:
parent
b5b087e5a4
commit
47c696edf9
3 changed files with 84 additions and 50 deletions
|
@ -2,13 +2,16 @@ import React from "react";
|
|||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Button,
|
||||
ClipboardCopy,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Split,
|
||||
SplitItem,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
|
||||
import type { UseFormMethods } from "react-hook-form";
|
||||
import { PasswordInput } from "../../components/password-input/PasswordInput";
|
||||
import { CopyToClipboardButton } from "../scopes/CopyToClipboardButton";
|
||||
|
||||
export type ClientSecretProps = {
|
||||
secret: string;
|
||||
|
@ -23,9 +26,14 @@ export const ClientSecret = ({ secret, toggle, form }: ClientSecretProps) => {
|
|||
<FormGroup label={t("clientSecret")} fieldId="kc-client-secret">
|
||||
<Split hasGutter>
|
||||
<SplitItem isFilled>
|
||||
<ClipboardCopy id="kc-client-secret" isReadOnly>
|
||||
{secret}
|
||||
</ClipboardCopy>
|
||||
<InputGroup>
|
||||
<PasswordInput id="kc-client-secret" value={secret} isReadOnly />
|
||||
<CopyToClipboardButton
|
||||
text={secret}
|
||||
label="clientSecret"
|
||||
variant="control"
|
||||
/>
|
||||
</InputGroup>
|
||||
</SplitItem>
|
||||
<SplitItem>
|
||||
<Button
|
||||
|
|
69
src/clients/scopes/CopyToClipboardButton.tsx
Normal file
69
src/clients/scopes/CopyToClipboardButton.tsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ClipboardCopyButton,
|
||||
ClipboardCopyButtonProps,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import useSetTimeout from "../../utils/useSetTimeout";
|
||||
|
||||
enum CopyState {
|
||||
Ready,
|
||||
Copied,
|
||||
Error,
|
||||
}
|
||||
|
||||
type CopyToClipboardButtonProps = Pick<ClipboardCopyButtonProps, "variant"> & {
|
||||
label: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const CopyToClipboardButton = ({
|
||||
label,
|
||||
text,
|
||||
variant = "plain",
|
||||
}: CopyToClipboardButtonProps) => {
|
||||
const { t } = useTranslation("clients");
|
||||
const setTimeout = useSetTimeout();
|
||||
|
||||
const [copy, setCopy] = useState(CopyState.Ready);
|
||||
|
||||
const copyMessage = useMemo(() => {
|
||||
switch (copy) {
|
||||
case CopyState.Ready:
|
||||
return t("copyToClipboard");
|
||||
case CopyState.Copied:
|
||||
return t("copySuccess");
|
||||
case CopyState.Error:
|
||||
return t("clipboardCopyError");
|
||||
}
|
||||
}, [copy]);
|
||||
|
||||
useEffect(() => {
|
||||
if (copy !== CopyState.Ready) {
|
||||
return setTimeout(() => setCopy(CopyState.Ready), 1000);
|
||||
}
|
||||
}, [copy]);
|
||||
|
||||
const copyToClipboard = async (text: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopy(CopyState.Copied);
|
||||
} catch (error) {
|
||||
setCopy(CopyState.Error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ClipboardCopyButton
|
||||
id={`copy-button-${label}`}
|
||||
textId={label}
|
||||
aria-label={t("copyToClipboard")}
|
||||
onClick={() => copyToClipboard(text)}
|
||||
exitDelay={600}
|
||||
variant={variant}
|
||||
>
|
||||
{copyMessage}
|
||||
</ClipboardCopyButton>
|
||||
);
|
||||
};
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ClipboardCopyButton,
|
||||
CodeBlock,
|
||||
CodeBlockAction,
|
||||
EmptyState,
|
||||
|
@ -11,7 +10,7 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
|
||||
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
|
||||
import useSetTimeout from "../../utils/useSetTimeout";
|
||||
import { CopyToClipboardButton } from "./CopyToClipboardButton";
|
||||
|
||||
type GeneratedCodeTabProps = {
|
||||
user?: UserRepresentation;
|
||||
|
@ -19,61 +18,19 @@ type GeneratedCodeTabProps = {
|
|||
label: string;
|
||||
};
|
||||
|
||||
enum CopyState {
|
||||
Ready,
|
||||
Copied,
|
||||
Error,
|
||||
}
|
||||
|
||||
export const GeneratedCodeTab = ({
|
||||
text,
|
||||
user,
|
||||
label,
|
||||
}: GeneratedCodeTabProps) => {
|
||||
const { t } = useTranslation("clients");
|
||||
const setTimeout = useSetTimeout();
|
||||
const [copy, setCopy] = useState(CopyState.Ready);
|
||||
const copyMessage = useMemo(() => {
|
||||
switch (copy) {
|
||||
case CopyState.Ready:
|
||||
return t("copyToClipboard");
|
||||
case CopyState.Copied:
|
||||
return t("copySuccess");
|
||||
case CopyState.Error:
|
||||
return t("clipboardCopyError");
|
||||
}
|
||||
}, [copy]);
|
||||
|
||||
useEffect(() => {
|
||||
if (copy !== CopyState.Ready) {
|
||||
return setTimeout(() => setCopy(CopyState.Ready), 1000);
|
||||
}
|
||||
}, [copy]);
|
||||
|
||||
const copyToClipboard = async (text: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopy(CopyState.Copied);
|
||||
} catch (error) {
|
||||
setCopy(CopyState.Error);
|
||||
}
|
||||
};
|
||||
|
||||
return user ? (
|
||||
<CodeBlock
|
||||
id={label}
|
||||
actions={
|
||||
<CodeBlockAction>
|
||||
<ClipboardCopyButton
|
||||
id={`copy-button-${label}`}
|
||||
textId={label}
|
||||
aria-label={t("copyToClipboard")}
|
||||
onClick={() => copyToClipboard(text)}
|
||||
exitDelay={600}
|
||||
variant="plain"
|
||||
>
|
||||
{copyMessage}
|
||||
</ClipboardCopyButton>
|
||||
<CopyToClipboardButton text={text} label={label} />
|
||||
</CodeBlockAction>
|
||||
}
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue