fix comment thread tooltip button nesting

This commit is contained in:
hjlarry 2026-04-13 10:33:42 +08:00
parent e3b72df552
commit a9ec9936cc
2 changed files with 79 additions and 45 deletions

View File

@ -82,7 +82,16 @@ vi.mock('@/app/components/base/ui/dropdown-menu', () => ({
vi.mock('@/app/components/base/ui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}</>,
TooltipTrigger: ({ children }: { children: React.ReactNode }) => <>{children}</>,
TooltipTrigger: ({
children,
render,
...props
}: React.ComponentProps<'button'> & { children?: React.ReactNode, render?: React.ReactNode }) => {
if (render)
return <>{render}</>
return <button type="button" {...props}>{children}</button>
},
TooltipContent: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}))
@ -188,6 +197,23 @@ describe('CommentThread', () => {
expect(onClose).toHaveBeenCalledTimes(2)
})
it('does not nest header action buttons inside tooltip trigger buttons', () => {
const { container } = render(
<CommentThread
comment={createComment()}
onClose={vi.fn()}
onDelete={vi.fn()}
onResolve={vi.fn()}
onPrev={vi.fn()}
onNext={vi.fn()}
canGoPrev
canGoNext
/>,
)
expect(container.querySelector('button button')).toBeNull()
})
it('submits reply and updates preview hovering state on mouse enter/leave', async () => {
const onReply = vi.fn()
const { container } = render(

View File

@ -379,66 +379,74 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
</div>
<div className="flex items-center gap-1">
<Tooltip>
<TooltipTrigger>
<button
type="button"
disabled={loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onDelete}
aria-label={t('comments.aria.deleteComment', { ns: 'workflow' })}
>
<RiDeleteBinLine className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipTrigger
render={(
<button
type="button"
disabled={loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onDelete}
aria-label={t('comments.aria.deleteComment', { ns: 'workflow' })}
>
<RiDeleteBinLine className="h-4 w-4" />
</button>
)}
/>
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
{t('comments.aria.deleteComment', { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger>
<button
type="button"
disabled={comment.resolved || loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onResolve}
aria-label={t('comments.aria.resolveComment', { ns: 'workflow' })}
>
{comment.resolved ? <RiCheckboxCircleFill className="h-4 w-4" /> : <RiCheckboxCircleLine className="h-4 w-4" />}
</button>
</TooltipTrigger>
<TooltipTrigger
render={(
<button
type="button"
disabled={comment.resolved || loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onResolve}
aria-label={t('comments.aria.resolveComment', { ns: 'workflow' })}
>
{comment.resolved ? <RiCheckboxCircleFill className="h-4 w-4" /> : <RiCheckboxCircleLine className="h-4 w-4" />}
</button>
)}
/>
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
{t('comments.aria.resolveComment', { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
<Divider type="vertical" className="h-3.5" />
<Tooltip>
<TooltipTrigger>
<button
type="button"
disabled={!canGoPrev || loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onPrev}
aria-label={t('comments.aria.previousComment', { ns: 'workflow' })}
>
<RiArrowUpSLine className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipTrigger
render={(
<button
type="button"
disabled={!canGoPrev || loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onPrev}
aria-label={t('comments.aria.previousComment', { ns: 'workflow' })}
>
<RiArrowUpSLine className="h-4 w-4" />
</button>
)}
/>
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
{t('comments.aria.previousComment', { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger>
<button
type="button"
disabled={!canGoNext || loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onNext}
aria-label={t('comments.aria.nextComment', { ns: 'workflow' })}
>
<RiArrowDownSLine className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipTrigger
render={(
<button
type="button"
disabled={!canGoNext || loading}
className={cn('flex h-6 w-6 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled disabled:hover:bg-transparent disabled:hover:text-text-disabled')}
onClick={onNext}
aria-label={t('comments.aria.nextComment', { ns: 'workflow' })}
>
<RiArrowDownSLine className="h-4 w-4" />
</button>
)}
/>
<TooltipContent placement="top" popupClassName="!px-2 !py-1.5">
{t('comments.aria.nextComment', { ns: 'workflow' })}
</TooltipContent>