Skip to content

Commit

Permalink
Merge pull request #299 from atlassian/ARC-2922-update-copy-buttons-l…
Browse files Browse the repository at this point in the history
…ogic-4.3.2

ARC-2922 update copy button logic
  • Loading branch information
anshengxu authored Mar 6, 2024
2 parents cd7b6dc + 6136cbb commit 9e411e8
Showing 1 changed file with 138 additions and 15 deletions.
153 changes: 138 additions & 15 deletions app/jenkins-for-jira-ui/src/components/JenkinsSetup/JenkinsSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Button, { ButtonGroup } from '@atlaskit/button';
import InfoIcon from '@atlaskit/icon/glyph/info';
import CopyIcon from '@atlaskit/icon/glyph/copy';
import OpenIcon from '@atlaskit/icon/glyph/open';
import CheckIcon from '@atlaskit/icon/glyph/check';
import Tooltip from '@atlaskit/tooltip';
import { useHistory, useParams } from 'react-router';
import Spinner from '@atlaskit/spinner';
Expand All @@ -34,7 +35,6 @@ import {
} from './JenkinsSetup.styles';
import { getJenkinsServerWithSecret } from '../../api/getJenkinsServerWithSecret';
import { serverNameFormOuterContainer } from '../ServerNameForm/ServerNameForm.styles';
import { CopiedToClipboard } from '../CopiedToClipboard/CopiedToClipboard';
import { ConnectionFlowHeader } from '../ConnectionWizard/ConnectionFlowHeader';
import { SecretTokenContent, WebhookGuideContent } from '../CopiedToClipboard/CopyToClipboardContent';
import { getWebhookUrl } from '../../common/util/jenkinsConnectionsUtils';
Expand All @@ -47,65 +47,80 @@ import {
} from '../../common/analytics/analytics-events';
import { AnalyticsClient } from '../../common/analytics/analytics-client';
import { ParamTypes } from '../../common/types';
import { CopiedToClipboard } from '../CopiedToClipboard/CopiedToClipboard';

const analyticsClient = new AnalyticsClient();

type CopyProps = {
handleCopyToClipboard: (copyRef: React.RefObject<HTMLDivElement>, elementName?: string) => Promise<void> | void;
handleCopyButton: (copyButton: CopyButtonNameEnum) => void;
primaryButtonName: CopyButtonNameEnum;
copyButtonName: CopyButtonNameEnum;
testId?: string
};

const CopyButton = ({
handleCopyToClipboard,
copyRef,
handleCopyButton,
primaryButtonName,
copyButtonName,
testId
}: CopyProps & { copyRef: React.RefObject<HTMLDivElement> }): JSX.Element => {
const [isCopied, setIsCopied] = useState(false);
const [showIsCopiedToClipboardTooltip, setShowIsCopiedToClipboardTooltip] = useState(false);

useEffect(() => {
let timeoutId: NodeJS.Timeout;
if (isCopied) {
if (showIsCopiedToClipboardTooltip) {
timeoutId = setTimeout(() => {
setIsCopied(false);
setShowIsCopiedToClipboardTooltip(false);
}, 2000);
}
return () => {
if (timeoutId) {
clearTimeout(timeoutId);
}
};
}, [isCopied]);
}, [showIsCopiedToClipboardTooltip]);

return (
<div className={cx(jenkinsSetupCopyButtonContainer)} >
<Button
iconBefore={<CopyIcon label="Copy" size="medium" />}
iconBefore={isCopied ? <CheckIcon label="Copy" size="medium" /> : <CopyIcon label="Copy" size="medium" />}
onClick={() => {
if (copyRef && copyRef.current) {
handleCopyToClipboard(copyRef, testId);
setIsCopied(true);
setShowIsCopiedToClipboardTooltip(true);
handleCopyButton(copyButtonName);
}
}}
appearance= {primaryButtonName === copyButtonName && !isCopied ? 'primary' : 'default'}
testId={testId}
>
Copy
Copy
</Button>

{isCopied && <CopiedToClipboard leftPositionPercent="110%" />}
{showIsCopiedToClipboardTooltip && <CopiedToClipboard leftPositionPercent="110%" />}
</div>
);
};

type MyJenkinsAdminProps = {
handleCopyToClipboard: (copyRef: React.RefObject<HTMLDivElement>, elementName?: string) => Promise<void> | void;
handleCopyButton: (copyButton: CopyButtonNameEnum) => void,
webhookGuideRef: RefObject<HTMLDivElement>,
secretTokenRef: RefObject<HTMLDivElement>
secretTokenRef: RefObject<HTMLDivElement>,
primaryButtonName: CopyButtonNameEnum,
};

const MyJenkinsAdmin = ({
handleCopyToClipboard,
handleCopyButton,
webhookGuideRef,
secretTokenRef
}: CopyProps & MyJenkinsAdminProps): JSX.Element => {
secretTokenRef,
primaryButtonName
}: MyJenkinsAdminProps): JSX.Element => {
const tooltipContent =
'Send this token separately to the webhook URL and step-by-step guide. It\'s best practice to use a secure channel like a password management tool.';

Expand All @@ -118,6 +133,9 @@ const MyJenkinsAdmin = ({
Webhook URL and step-by-step guide
<CopyButton
handleCopyToClipboard={handleCopyToClipboard}
handleCopyButton={handleCopyButton}
primaryButtonName={primaryButtonName}
copyButtonName="nonAdminWebhook"
copyRef={webhookGuideRef}
testId="copy-webhook-url-guide" />
</li>
Expand All @@ -126,25 +144,35 @@ const MyJenkinsAdmin = ({
<Tooltip content={tooltipContent} position="bottom-start">
<InfoIcon label="help" size="small" />
</Tooltip>
<CopyButton handleCopyToClipboard={handleCopyToClipboard} copyRef={secretTokenRef} testId="copy-secret-token-guide" />
<CopyButton
handleCopyToClipboard={handleCopyToClipboard}
handleCopyButton={handleCopyButton}
primaryButtonName={primaryButtonName}
copyButtonName="nonAdminSecret"
copyRef={secretTokenRef} testId="copy-secret-token-guide" />
</li>
</ol>
</div>
);
};

type IAmTheJenkinsAdminProps = {
handleCopyToClipboard: (copyRef: React.RefObject<HTMLDivElement>, elementName?: string) => Promise<void> | void;
handleCopyButton: (copyButton: CopyButtonNameEnum) => void,
siteNameRef: RefObject<HTMLDivElement>,
webhookUrlRef: RefObject<HTMLDivElement>,
secretRef: RefObject<HTMLDivElement>,
primaryButtonName: CopyButtonNameEnum,
};

const IAmTheJenkinsAdmin = ({
handleCopyToClipboard,
handleCopyButton,
siteNameRef,
webhookUrlRef,
secretRef
}: CopyProps & IAmTheJenkinsAdminProps): JSX.Element => {
secretRef,
primaryButtonName
}: IAmTheJenkinsAdminProps): JSX.Element => {
const handleFollowLink = async (e: React.MouseEvent): Promise<void> => {
e.preventDefault();

Expand Down Expand Up @@ -183,17 +211,28 @@ const IAmTheJenkinsAdmin = ({
Site name
<CopyButton
handleCopyToClipboard={handleCopyToClipboard}
handleCopyButton={handleCopyButton}
primaryButtonName={primaryButtonName}
copyButtonName="adminSiteName"
copyRef={siteNameRef} testId="site-name-copy-button" />
</li>
<li className={cx(orderedListItem, jenkinsSetupListItem)}>
Webhook URL
<CopyButton
handleCopyToClipboard={handleCopyToClipboard}
handleCopyButton={handleCopyButton}
primaryButtonName={primaryButtonName}
copyButtonName="adminWebhook"
copyRef={webhookUrlRef} testId="copy-webhook-url" />
</li>
<li className={cx(orderedListItem, jenkinsSetupListItem)}>
Secret
<CopyButton handleCopyToClipboard={handleCopyToClipboard} copyRef={secretRef} testId="copy-secret-token" />
<CopyButton
handleCopyToClipboard={handleCopyToClipboard}
handleCopyButton={handleCopyButton}
primaryButtonName={primaryButtonName}
copyButtonName="adminSecret"
copyRef={secretRef} testId="copy-secret-token" />
</li>
</ol>

Expand All @@ -202,7 +241,25 @@ const IAmTheJenkinsAdmin = ({
);
};

type CopyButtonNameEnum = 'adminSiteName' | 'adminWebhook' | 'adminSecret' | 'nonAdminWebhook' | 'nonAdminSecret';

interface CopyButtonMetadata {
name: CopyButtonNameEnum;
isClicked: boolean;
}

const JenkinsSetup = (): JSX.Element => {
const initialAdminButtonStates: CopyButtonMetadata[] = [
{ name: 'adminSiteName', isClicked: false },
{ name: 'adminWebhook', isClicked: false },
{ name: 'adminSecret', isClicked: false }
];

const initialNonAdminButtonStates: CopyButtonMetadata[] = [
{ name: 'nonAdminWebhook', isClicked: false },
{ name: 'nonAdminSecret', isClicked: false }
];

const history = useHistory();
const { path } = useParams<ParamTypes>();
const webhookGuideRef = useRef<HTMLDivElement>(null);
Expand All @@ -214,12 +271,37 @@ const JenkinsSetup = (): JSX.Element => {
const [serverName, setServerName] = useState('');
const [showMyJenkinsAdmin, setShowMyJenkinsAdmin] = useState(false);
const [showIAmTheJenkinsAdmin, setShowIAmTheJenkinsAdmin] = useState(false);
const [copyAdminButtonStates, setCopyAdminButtonStates] = useState<CopyButtonMetadata[]>(initialAdminButtonStates);
const [copyNonAdminButtonStates, setCopyNonAdminButtonStates] =
useState<CopyButtonMetadata[]>(initialNonAdminButtonStates);
const [webhookUrl, setWebhookUrl] = useState('');
const [secret, setSecret] = useState<string>('');
const [siteName, setSiteName] = useState<string>('');
const [globalPageUrl, setGlobalPageUrl] = useState<string>('');
const [primaryCopyButtonName, setPrimaryCopyButtonName] =
useState<CopyButtonNameEnum>('nonAdminWebhook');
const connectionSettings = settings === 'connection-settings';

const updateAdminCopyButtonState = (key: CopyButtonNameEnum, isClicked: boolean) => {
const existingItemIndex = copyAdminButtonStates.findIndex((item) => item.name === key);

if (existingItemIndex !== -1) {
const updatedButtons = [...copyAdminButtonStates];
updatedButtons[existingItemIndex].isClicked = isClicked;
setCopyAdminButtonStates(updatedButtons);
}
};

const updateNonAdminCopyButtonState = (key: CopyButtonNameEnum, isClicked: boolean) => {
const existingItemIndex = copyNonAdminButtonStates.findIndex((item) => item.name === key);

if (existingItemIndex !== -1) {
const updatedButtons = [...copyNonAdminButtonStates];
updatedButtons[existingItemIndex].isClicked = isClicked;
setCopyNonAdminButtonStates(updatedButtons);
}
};

const getServer = useCallback(async () => {
try {
const { name, secret: retrievedSecret } = await getJenkinsServerWithSecret(uuid);
Expand Down Expand Up @@ -252,6 +334,31 @@ const JenkinsSetup = (): JSX.Element => {
fetchData();
}, [uuid, getServer]);

const findNextPrimaryButtonIndex = (buttonStates: CopyButtonMetadata[]) => {
return buttonStates.findIndex((item) => item.isClicked === false);
};

const handleAdminCopyButton = (copyButtonName: CopyButtonNameEnum) => {
updateAdminCopyButtonState(copyButtonName, true);
const nextPrimaryButtonIndex = findNextPrimaryButtonIndex(copyAdminButtonStates);
if (nextPrimaryButtonIndex !== -1) {
setPrimaryCopyButtonName(copyAdminButtonStates[nextPrimaryButtonIndex].name);
}
};

const handleNonAdminCopyButton = (copyButtonName: CopyButtonNameEnum) => {
updateNonAdminCopyButtonState(copyButtonName, true);
const nextPrimaryButtonIndex = findNextPrimaryButtonIndex(copyNonAdminButtonStates);
if (nextPrimaryButtonIndex !== -1) {
setPrimaryCopyButtonName(copyNonAdminButtonStates[nextPrimaryButtonIndex].name);
}
};

const clearCopiedButtonStates = () => {
setCopyAdminButtonStates(initialAdminButtonStates);
setCopyNonAdminButtonStates(initialNonAdminButtonStates);
};

const handleCopyToClipboard =
async (copyRef: React.RefObject<HTMLDivElement>, elementName?: string) => {
if (copyRef.current) {
Expand Down Expand Up @@ -290,6 +397,10 @@ const JenkinsSetup = (): JSX.Element => {
const handleMyJenkinsAdminClick = async (e: React.MouseEvent) => {
e.preventDefault();

if (!showMyJenkinsAdmin) {
clearCopiedButtonStates();
setPrimaryCopyButtonName('nonAdminWebhook');
}
setShowMyJenkinsAdmin(true);
setShowIAmTheJenkinsAdmin(false);

Expand All @@ -306,6 +417,10 @@ const JenkinsSetup = (): JSX.Element => {
const handleIAmTheJenkinsAdminClick = async (e: React.MouseEvent) => {
e.preventDefault();

if (!showIAmTheJenkinsAdmin) {
clearCopiedButtonStates();
setPrimaryCopyButtonName('adminSiteName');
}
setShowIAmTheJenkinsAdmin(true);
setShowMyJenkinsAdmin(false);

Expand Down Expand Up @@ -343,6 +458,9 @@ const JenkinsSetup = (): JSX.Element => {

const isFetchingData = !serverName || !webhookUrl || !secret;

const isAllRequiredButtonsClicked = copyAdminButtonStates.every((button) => button.isClicked) ||
(copyNonAdminButtonStates.every((button) => button.isClicked));

return (
<div className={cx(connectionFlowContainer)}>
<ConnectionFlowHeader />
Expand Down Expand Up @@ -391,6 +509,8 @@ const JenkinsSetup = (): JSX.Element => {
{showMyJenkinsAdmin ? (
<MyJenkinsAdmin
handleCopyToClipboard={handleCopyToClipboard}
handleCopyButton={handleNonAdminCopyButton}
primaryButtonName={primaryCopyButtonName}
webhookGuideRef={webhookGuideRef}
secretTokenRef={secretTokenRef}
/>
Expand All @@ -399,6 +519,8 @@ const JenkinsSetup = (): JSX.Element => {
{showIAmTheJenkinsAdmin ? (
<IAmTheJenkinsAdmin
handleCopyToClipboard={handleCopyToClipboard}
handleCopyButton={handleAdminCopyButton}
primaryButtonName={primaryCopyButtonName}
siteNameRef={siteNameRef}
webhookUrlRef={webhookUrlRef}
secretRef={secretRef}
Expand All @@ -410,6 +532,7 @@ const JenkinsSetup = (): JSX.Element => {
<Button
type="button"
appearance="primary"
isDisabled={!isAllRequiredButtonsClicked}
onClick={(e) => handleNavigateToConnectionCompleteScreen(e)}
testId="jenkins-set-up-next-btn"
>
Expand Down

0 comments on commit 9e411e8

Please sign in to comment.