mirror of
https://github.com/langgenius/dify.git
synced 2026-09-21 13:20:52 +08:00
fix(web): improve debug plugin copy accessibility (#40035)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
import { cleanup, render, screen, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import KeyValueItem from '../key-value-item'
|
||||
|
||||
@@ -34,9 +35,20 @@ describe('KeyValueItem', () => {
|
||||
expect(screen.queryByText('sk-secret')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('copies actual value (not masked) when copy button is clicked', () => {
|
||||
it('associates the label with the copy action and announces the result', async () => {
|
||||
vi.useRealTimers()
|
||||
const user = userEvent.setup()
|
||||
render(<KeyValueItem label="Key" value="sk-secret" maskedValue="sk-***" />)
|
||||
fireEvent.click(screen.getByRole('button', { name: 'common.operation.copy' }))
|
||||
|
||||
const keyGroup = screen.getByRole('group', { name: 'Key' })
|
||||
const copyButton = within(keyGroup).getByRole('button', {
|
||||
name: 'common.operation.copy: Key',
|
||||
})
|
||||
|
||||
await user.click(copyButton)
|
||||
|
||||
expect(mockCopy).toHaveBeenCalledWith('sk-secret')
|
||||
expect(copyButton).toHaveAccessibleName('common.operation.copy: Key')
|
||||
expect(within(keyGroup).getByRole('status')).toHaveTextContent('common.operation.copied: Key')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useId, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { CopyCheck } from '../../base/icons/src/vender/line/files'
|
||||
|
||||
@@ -24,6 +24,7 @@ function KeyValueItem({
|
||||
}: Props) {
|
||||
const { t } = useTranslation()
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
const labelId = useId()
|
||||
const handleCopy = useCallback(() => {
|
||||
copy(value)
|
||||
setIsCopied(true)
|
||||
@@ -40,11 +41,16 @@ function KeyValueItem({
|
||||
}
|
||||
}, [isCopied])
|
||||
|
||||
const copyLabel = t(($) => $[`operation.${isCopied ? 'copied' : 'copy'}`], { ns: 'common' })
|
||||
const copiedLabel = t(($) => $['operation.copied'], { ns: 'common' })
|
||||
const copyLabel = t(($) => $['operation.copy'], { ns: 'common' })
|
||||
const copyButtonLabel = `${copyLabel}: ${label}`
|
||||
const copyStatus = `${copiedLabel}: ${label}`
|
||||
const tooltipLabel = isCopied ? copiedLabel : copyLabel
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
<div role="group" aria-labelledby={labelId} className="flex items-center gap-1">
|
||||
<span
|
||||
id={labelId}
|
||||
className={cn(
|
||||
'flex flex-col items-start justify-center system-xs-medium text-text-tertiary',
|
||||
labelWidthClassName,
|
||||
@@ -63,7 +69,7 @@ function KeyValueItem({
|
||||
render={
|
||||
<Button
|
||||
variant="ghost"
|
||||
aria-label={copyLabel}
|
||||
aria-label={copyButtonLabel}
|
||||
className="size-6 p-0"
|
||||
onClick={handleCopy}
|
||||
>
|
||||
@@ -78,9 +84,12 @@ function KeyValueItem({
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<TooltipContent placement="top">{copyLabel}</TooltipContent>
|
||||
<TooltipContent placement="top">{tooltipLabel}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<span role="status" aria-atomic="true" className="sr-only">
|
||||
{isCopied ? copyStatus : ''}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user