From 8b73ef5836f277a668e061373a1c819302c1e6a1 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Wed, 22 Apr 2026 22:49:16 +0200 Subject: [PATCH] feat: add sidebar folder rename and delete actions --- src/components/FolderTree.test.tsx | 25 ++++++ src/components/folder-tree/FolderTreeRow.tsx | 82 +++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/src/components/FolderTree.test.tsx b/src/components/FolderTree.test.tsx index 1ab87d5d..0d9fddc5 100644 --- a/src/components/FolderTree.test.tsx +++ b/src/components/FolderTree.test.tsx @@ -91,6 +91,31 @@ describe('FolderTree', () => { expect(onStartRenameFolder).toHaveBeenCalledWith('areas') }) + it('shows inline rename and delete actions for folders', () => { + const onDeleteFolder = vi.fn() + const onStartRenameFolder = vi.fn() + const onSelect = vi.fn() + render( + , + ) + + fireEvent.click(screen.getByTestId('rename-folder-btn:projects')) + fireEvent.click(screen.getByTestId('delete-folder-btn:projects')) + + expect(onSelect).toHaveBeenNthCalledWith(1, { kind: 'folder', path: 'projects' }) + expect(onStartRenameFolder).toHaveBeenCalledWith('projects') + expect(onSelect).toHaveBeenNthCalledWith(2, { kind: 'folder', path: 'projects' }) + expect(onDeleteFolder).toHaveBeenCalledWith('projects') + }) + it('shows the rename input when a folder is being renamed', () => { render( void onOpenMenu: FolderTreeRowProps['onOpenMenu'] onSelect: () => void onStartRenameFolder?: (folderPath: string) => void @@ -72,11 +76,12 @@ function FolderItemRow({ }) { const hasChildren = node.children.length > 0 const expandLabel = isExpanded ? `Collapse ${node.name}` : `Expand ${node.name}` + const hasActions = !!onStartRenameFolder || !!onDeleteFolder return (
onToggle(node.path)} /> + {hasActions && ( +
+ {onStartRenameFolder && ( + { + onSelect() + onStartRenameFolder(node.path) + }} + > + + + )} + {onDeleteFolder && ( + { + onSelect() + onDeleteFolder(node.path) + }} + > + + + )} +
+ )}
) } @@ -137,13 +174,52 @@ function FolderToggleButton({ ) } +function FolderActionButton({ + ariaLabel, + children, + destructive = false, + onClick, + testId, + title, +}: { + ariaLabel: string + children: ReactNode + destructive?: boolean + onClick: () => void + testId: string + title: string +}) { + return ( + + ) +} + function FolderSelectButton({ + hasActions, isExpanded, isSelected, node, onSelect, onStartRenameFolder, }: { + hasActions: boolean isExpanded: boolean isSelected: boolean node: FolderNode @@ -156,6 +232,7 @@ function FolderSelectButton({ variant="ghost" className={cn( 'h-7 flex-1 justify-start gap-2 rounded-[5px] px-2 py-0 text-left text-[13px]', + hasActions && 'pr-12', isSelected ? 'font-medium text-primary hover:text-primary' : 'hover:text-foreground', )} title={node.path} @@ -258,6 +335,7 @@ export const FolderTreeRow = memo(function FolderTreeRow({ isExpanded={isExpanded} isSelected={isSelected} node={node} + onDeleteFolder={onDeleteFolder} onOpenMenu={onOpenMenu} onSelect={selectFolder} onStartRenameFolder={onStartRenameFolder}