Fix Kanban unread bell dismissal (#2163)
This commit is contained in:
parent
c9b12d1618
commit
9e30fe31bd
|
|
@ -0,0 +1,113 @@
|
|||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import type { ReactNode } from 'react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { Repo, Worktree, WorktreeCardProperty } from '../../../../shared/types'
|
||||
|
||||
const fetchHostedReviewForBranch = vi.fn()
|
||||
const fetchIssue = vi.fn()
|
||||
const openModal = vi.fn()
|
||||
const updateWorktreeMeta = vi.fn()
|
||||
|
||||
let worktreeCardProperties: WorktreeCardProperty[] = ['status', 'unread']
|
||||
|
||||
vi.mock('@/store', () => ({
|
||||
useAppStore: (selector: (state: unknown) => unknown) =>
|
||||
selector({
|
||||
deleteStateByWorktreeId: {},
|
||||
fetchHostedReviewForBranch,
|
||||
fetchIssue,
|
||||
gitConflictOperationByWorktree: {},
|
||||
hostedReviewCache: {},
|
||||
issueCache: {},
|
||||
openModal,
|
||||
remoteBranchConflictByWorktreeId: {},
|
||||
settings: null,
|
||||
sshConnectionStates: new Map(),
|
||||
sshTargetLabels: new Map(),
|
||||
updateWorktreeMeta,
|
||||
worktreeCardProperties
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/worktree-activation', () => ({
|
||||
activateAndRevealWorktree: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/components/ui/tooltip', () => ({
|
||||
Tooltip: ({ children }: { children: ReactNode }) => <>{children}</>,
|
||||
TooltipContent: ({ children }: { children: ReactNode }) => <>{children}</>,
|
||||
TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}</>
|
||||
}))
|
||||
|
||||
vi.mock('./use-worktree-activity-status', () => ({
|
||||
useWorktreeActivityStatus: () => 'idle'
|
||||
}))
|
||||
|
||||
vi.mock('./CacheTimer', () => ({
|
||||
default: () => null
|
||||
}))
|
||||
|
||||
vi.mock('./WorktreeCardAgents', () => ({
|
||||
default: () => null
|
||||
}))
|
||||
|
||||
vi.mock('./SshDisconnectedDialog', () => ({
|
||||
SshDisconnectedDialog: () => null
|
||||
}))
|
||||
|
||||
vi.mock('./WorktreeContextMenu', () => ({
|
||||
default: ({ children }: { children: ReactNode }) => <>{children}</>,
|
||||
CLOSE_ALL_CONTEXT_MENUS_EVENT: 'orca:test-close-context-menus',
|
||||
WORKTREE_CONTEXT_MENU_SCOPE_ATTR: 'data-orca-context-menu-scope'
|
||||
}))
|
||||
|
||||
function makeRepo(): Repo {
|
||||
return {
|
||||
id: 'repo-1',
|
||||
path: '/repo',
|
||||
displayName: 'orca',
|
||||
badgeColor: '#999999',
|
||||
addedAt: 1
|
||||
}
|
||||
}
|
||||
|
||||
function makeWorktree(overrides: Partial<Worktree> = {}): Worktree {
|
||||
return {
|
||||
id: 'repo-1::/repo/worktrees/quick-action',
|
||||
repoId: 'repo-1',
|
||||
path: '/repo/worktrees/quick-action',
|
||||
displayName: 'Quick action',
|
||||
branch: 'quick-action',
|
||||
head: 'abc123',
|
||||
isBare: false,
|
||||
isMainWorktree: false,
|
||||
comment: '',
|
||||
linkedIssue: null,
|
||||
linkedPR: null,
|
||||
linkedLinearIssue: null,
|
||||
isArchived: false,
|
||||
isUnread: true,
|
||||
isPinned: false,
|
||||
sortOrder: 0,
|
||||
lastActivityAt: 1,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('WorktreeCard quick actions', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
worktreeCardProperties = ['status', 'unread']
|
||||
})
|
||||
|
||||
it('marks the unread toggle as a workspace-board-preserving action', async () => {
|
||||
const { default: WorktreeCard } = await import('./WorktreeCard')
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
<WorktreeCard worktree={makeWorktree()} repo={makeRepo()} isActive={false} />
|
||||
)
|
||||
|
||||
expect(markup).toContain('aria-label="Mark as read"')
|
||||
expect(markup).toContain('data-workspace-board-preserve-open=""')
|
||||
})
|
||||
})
|
||||
|
|
@ -336,6 +336,15 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
[isDeleting, isMultiSelected, selectedWorktrees, worktree.id]
|
||||
)
|
||||
|
||||
const stopQuickActionPointerPropagation = useCallback(
|
||||
(event: React.PointerEvent<HTMLButtonElement>) => {
|
||||
// Why: the Kanban board is dismissed by document-level pointer handling.
|
||||
// Quick card actions mutate metadata, but must not count as card activation.
|
||||
event.stopPropagation()
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
// Why: the 'unread' card property is the user's opt-out. When off, we render
|
||||
// as if the workspace is read so bold emphasis never appears. The persisted
|
||||
// `worktree.isUnread` flag is unchanged; only the rendering changes.
|
||||
|
|
@ -386,6 +395,8 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
<TooltipTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
data-workspace-board-preserve-open=""
|
||||
onPointerDown={stopQuickActionPointerPropagation}
|
||||
onClick={handleToggleUnreadQuick}
|
||||
className={cn(
|
||||
'group/unread flex size-4 cursor-pointer items-center justify-center rounded transition-all',
|
||||
|
|
|
|||
Loading…
Reference in New Issue