From 4f08890f7d2dc23f5fd953d8c931528cf5fbd4ed Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Fri, 14 Jun 2024 14:48:04 -0400 Subject: [PATCH] feat(graph): add copy button for entire target configuration --- .../ui-code-block/src/lib/json-code-block.tsx | 47 ++++---------- graph/ui-components/src/index.ts | 1 + .../lib/copy-to-clipboard-button.stories.tsx | 20 ++++++ .../src/lib/copy-to-clipboard-button.tsx | 61 +++++++++++++++++++ .../copy-to-clipboard.stories.tsx | 17 ------ .../copy-to-clipboard/copy-to-clipboard.tsx | 36 ----------- .../lib/project-details/project-details.tsx | 40 ++++++------ .../target-configuration-details-header.tsx | 19 +++--- .../target-configuration-details.tsx | 60 +++++++----------- .../target-executor/target-executor-title.tsx | 28 +++++---- package.json | 2 +- pnpm-lock.yaml | 2 +- 12 files changed, 160 insertions(+), 173 deletions(-) create mode 100644 graph/ui-components/src/lib/copy-to-clipboard-button.stories.tsx create mode 100644 graph/ui-components/src/lib/copy-to-clipboard-button.tsx delete mode 100644 graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.stories.tsx delete mode 100644 graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.tsx diff --git a/graph/ui-code-block/src/lib/json-code-block.tsx b/graph/ui-code-block/src/lib/json-code-block.tsx index 1142025c501570..4f7c7c5077c98b 100644 --- a/graph/ui-code-block/src/lib/json-code-block.tsx +++ b/graph/ui-code-block/src/lib/json-code-block.tsx @@ -1,13 +1,8 @@ -import { - ClipboardDocumentCheckIcon, - ClipboardDocumentIcon, -} from '@heroicons/react/24/outline'; -// @ts-ignore -import { CopyToClipboard } from 'react-copy-to-clipboard'; // @ts-ignore import SyntaxHighlighter, { createElement } from 'react-syntax-highlighter'; -import { JSX, ReactNode, useEffect, useMemo, useState } from 'react'; +import { JSX, ReactNode, useMemo } from 'react'; import { twMerge } from 'tailwind-merge'; +import { CopyToClipboardButton } from '@nx/graph/ui-components'; export function JsonCodeBlockPreTag({ children, @@ -30,45 +25,27 @@ export function JsonCodeBlockPreTag({ export interface JsonCodeBlockProps { data: any; renderSource: (propertyName: string) => ReactNode; + copyTooltipText: string; } export function JsonCodeBlock(props: JsonCodeBlockProps): JSX.Element { - const [copied, setCopied] = useState(false); const jsonString = useMemo( () => JSON.stringify(props.data, null, 2), [props.data] ); - useEffect(() => { - if (!copied) return; - const t = setTimeout(() => { - setCopied(false); - }, 3000); - return () => clearTimeout(t); - }, [copied]); return (
- { - setCopied(true); - }} - > - - + tooltipAlignment="right" + tooltipText={props.copyTooltipText} + className={twMerge( + 'not-prose flex', + 'border border-slate-200 bg-slate-50/50 p-2 dark:border-slate-700 dark:bg-slate-800/60', + 'opacity-0 transition-opacity group-hover:opacity-100' + )} + />
= { + component: CopyToClipboardButton, + title: 'CopyToClipboardButton', +}; +export default meta; + +type Story = StoryObj; + +export const Simple: Story = { + args: { + text: 'Hello, world!', + tooltipAlignment: 'left', + } as CopyToClipboardButtonProps, +}; diff --git a/graph/ui-components/src/lib/copy-to-clipboard-button.tsx b/graph/ui-components/src/lib/copy-to-clipboard-button.tsx new file mode 100644 index 00000000000000..0f7b6675084b01 --- /dev/null +++ b/graph/ui-components/src/lib/copy-to-clipboard-button.tsx @@ -0,0 +1,61 @@ +// @ts-ignore +import { CopyToClipboard } from 'react-copy-to-clipboard'; +import { JSX, ReactNode, useEffect, useState } from 'react'; +import { + ClipboardDocumentCheckIcon, + ClipboardDocumentIcon, +} from '@heroicons/react/24/outline'; + +export interface CopyToClipboardButtonProps { + text: string; + tooltipText?: string; + tooltipAlignment?: 'left' | 'right'; + className?: string; + children?: ReactNode; +} + +export function CopyToClipboardButton({ + text, + tooltipAlignment, + tooltipText, + className, + children, +}: CopyToClipboardButtonProps) { + const [copied, setCopied] = useState(false); + + useEffect(() => { + if (!copied) return; + const t = setTimeout(() => { + setCopied(false); + }, 3000); + return () => clearTimeout(t); + }, [copied]); + + return ( + { + setCopied(true); + }} + > + + + ); +} diff --git a/graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.stories.tsx b/graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.stories.tsx deleted file mode 100644 index 8101be70cfbea3..00000000000000 --- a/graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.stories.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { CopyToClipboard } from './copy-to-clipboard'; - -const meta: Meta = { - component: CopyToClipboard, - title: 'CopyToClipboard', -}; -export default meta; - -type Story = StoryObj; - -export const Simple: Story = { - args: { - onCopy: () => {}, - tooltipAlignment: 'left', - }, -}; diff --git a/graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.tsx b/graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.tsx deleted file mode 100644 index e7cbfdd7ffc38e..00000000000000 --- a/graph/ui-project-details/src/lib/copy-to-clipboard/copy-to-clipboard.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ClipboardIcon } from '@heroicons/react/24/outline'; -import { JSX, useEffect, useState } from 'react'; - -interface CopyToClipboardProps { - onCopy: () => void; - tooltipAlignment?: 'left' | 'right'; -} - -export function CopyToClipboard(props: CopyToClipboardProps): JSX.Element { - const [copied, setCopied] = useState(false); - useEffect(() => { - if (copied) { - const timeout = setTimeout(() => { - setCopied(false); - }, 3000); - return () => clearTimeout(timeout); - } - }); - return ( - - { - e.stopPropagation(); - setCopied(true); - props.onCopy(); - }} - > - - ); -} diff --git a/graph/ui-project-details/src/lib/project-details/project-details.tsx b/graph/ui-project-details/src/lib/project-details/project-details.tsx index 39b9508c6c6c60..bcb6894a14bd81 100644 --- a/graph/ui-project-details/src/lib/project-details/project-details.tsx +++ b/graph/ui-project-details/src/lib/project-details/project-details.tsx @@ -1,5 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars - /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line import type { ProjectGraphProjectNode } from '@nx/devkit'; @@ -71,7 +69,7 @@ export const ProjectDetails = ({ >
@@ -90,17 +88,15 @@ export const ProjectDetails = ({ className="h-6 w-6" />
- - {onViewInProjectGraph && viewInProjectGraphPosition === 'top' && ( - - onViewInProjectGraph({ projectName: project.name }) - } - /> - )}{' '} - + {onViewInProjectGraph && viewInProjectGraphPosition === 'top' && ( + + onViewInProjectGraph({ projectName: project.name }) + } + /> + )}
-
+
{projectData.metadata?.description ? (

@@ -133,16 +129,14 @@ export const ProjectDetails = ({ ) : null}

- - {onViewInProjectGraph && - viewInProjectGraphPosition === 'bottom' && ( - - onViewInProjectGraph({ projectName: project.name }) - } - /> - )}{' '} - + {onViewInProjectGraph && + viewInProjectGraphPosition === 'bottom' && ( + + onViewInProjectGraph({ projectName: project.name }) + } + /> + )}
diff --git a/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx b/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx index 849fe99200e363..b70dade228ff2a 100644 --- a/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx +++ b/graph/ui-project-details/src/lib/target-configuration-details-header/target-configuration-details-header.tsx @@ -1,6 +1,7 @@ /* eslint-disable @nx/enforce-module-boundaries */ // nx-ignore-next-line import type { TargetConfiguration } from '@nx/devkit'; +import { CopyToClipboardButton } from '@nx/graph/ui-components'; import { ChevronDownIcon, ChevronUpIcon, @@ -17,7 +18,6 @@ import { twMerge } from 'tailwind-merge'; import { Pill } from '../pill'; import { TargetTechnologies } from '../target-technologies/target-technologies'; import { SourceInfo } from '../source-info/source-info'; -import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard'; import { getDisplayHeaderFromTargetConfiguration } from '../utils/get-display-header-from-target-configuration'; import { TargetExecutor } from '../target-executor/target-executor'; @@ -53,10 +53,6 @@ export const TargetConfigurationDetailsHeader = ({ onViewInTaskGraph, onNxConnect, }: TargetConfigurationDetailsHeaderProps) => { - const handleCopyClick = async (copyText: string) => { - await window.navigator.clipboard.writeText(copyText); - }; - if (!collapsable) { // when collapsable is false, isCollasped should be false isCollasped = false; @@ -156,6 +152,12 @@ export const TargetConfigurationDetailsHeader = ({
+ {onViewInTaskGraph && (