chore: add copy action to workflow log errors (#40011)

This commit is contained in:
Joel 2026-08-05 13:15:59 +08:00 committed by GitHub
parent 7dd7b3dfa8
commit 72b30ff313
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 6 deletions

View File

@ -21,8 +21,16 @@ vi.mock('@/app/components/base/markdown', () => ({
}))
vi.mock('@/app/components/workflow/run/status-container', () => ({
default: ({ status, children }: { status: string; children?: React.ReactNode }) => (
<div data-status={status} data-testid="status-container">
default: ({
status,
children,
copyContent,
}: {
status: string
children?: React.ReactNode
copyContent?: string
}) => (
<div data-copy-content={copyContent} data-status={status} data-testid="status-container">
{children}
</div>
),
@ -62,6 +70,10 @@ describe('OutputPanel', () => {
render(<OutputPanel error="Execution failed" />)
expect(screen.getByTestId('status-container')).toHaveAttribute('data-status', 'failed')
expect(screen.getByTestId('status-container')).toHaveAttribute(
'data-copy-content',
'Execution failed',
)
expect(screen.getByText('Execution failed')).toBeInTheDocument()
})

View File

@ -1,8 +1,18 @@
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import useTheme from '@/hooks/use-theme'
import { Theme } from '@/types/app'
import StatusContainer from '../status-container'
const copy = vi.fn()
vi.mock('foxact/use-clipboard', () => ({
useClipboard: () => ({
copied: false,
copy,
}),
}))
vi.mock('@/hooks/use-theme', () => ({
default: vi.fn(),
}))
@ -34,4 +44,19 @@ describe('StatusContainer', () => {
).toBeInTheDocument()
})
})
it('copies the supplied content from the status action', async () => {
const user = userEvent.setup()
render(
<StatusContainer status="failed" copyContent="Execution failed">
Execution failed
</StatusContainer>,
)
await user.click(
screen.getByRole('button', { name: 'appOverview.overview.appInfo.embedded.copy' }),
)
expect(copy).toHaveBeenCalledWith('Execution failed')
})
})

View File

@ -53,7 +53,9 @@ const OutputPanel: FC<OutputPanelProps> = ({ isRunning, outputs, error, height }
)}
{!isRunning && error && (
<div className="px-4">
<StatusContainer status="failed">{error}</StatusContainer>
<StatusContainer status="failed" copyContent={error}>
{error}
</StatusContainer>
</div>
)}
{!isRunning && !outputs && (

View File

@ -1,22 +1,26 @@
'use client'
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import CopyFeedback from '@/app/components/base/copy-feedback'
import useTheme from '@/hooks/use-theme'
import { Theme } from '@/types/app'
type Props = {
readonly status: string
readonly children?: React.ReactNode
readonly copyContent?: string
}
const StatusContainer: FC<Props> = ({ status, children }) => {
const StatusContainer: FC<Props> = ({ status, children, copyContent }) => {
const { theme } = useTheme()
const isCopyable = copyContent !== undefined
return (
<div
role="status"
className={cn(
'relative rounded-lg border border-workflow-display-disabled-border-1 px-3 py-2.5 system-xs-regular break-all',
'group/status relative rounded-lg border border-workflow-display-disabled-border-1 px-3 py-2.5 system-xs-regular break-all',
isCopyable && 'focus-within:pr-10 hover:pr-10 [@media(hover:none)]:pr-10',
status === 'succeeded' &&
'border-[rgba(23,178,106,0.8)] bg-workflow-display-success-bg bg-[url(~@/app/components/workflow/run/assets/bg-line-success.svg)] text-text-success',
status === 'succeeded' &&
@ -69,13 +73,18 @@ const StatusContainer: FC<Props> = ({ status, children }) => {
>
<div
className={cn(
'absolute top-0 left-0 h-12.5 w-[65%] bg-no-repeat',
'pointer-events-none absolute top-0 left-0 h-12.5 w-[65%] bg-no-repeat',
theme === Theme.light && 'bg-[url(~@/app/components/workflow/run/assets/highlight.svg)]',
theme === Theme.dark &&
'bg-[url(~@/app/components/workflow/run/assets/highlight-dark.svg)]',
)}
></div>
{children}
{isCopyable && (
<div className="pointer-events-none absolute top-1.5 right-1.5 z-10 opacity-0 transition-opacity group-focus-within/status:pointer-events-auto group-focus-within/status:opacity-100 group-hover/status:pointer-events-auto group-hover/status:opacity-100 [@media(hover:none)]:pointer-events-auto [@media(hover:none)]:opacity-100">
<CopyFeedback content={copyContent} />
</div>
)}
</div>
)
}