fix(sidebar): include Pinned in collapse all projects (#39145)

* fix(sidebar): include Pinned in collapse all projects

* fix(sidebar): keep pinned toggles working outside project grouping
This commit is contained in:
Jinjing
2026-11-06 11:31:29 -08:00
committed by GitHub
parent d2637d1bf9
commit a5ca71c3ba
5 changed files with 33 additions and 11 deletions
@@ -332,7 +332,7 @@ const WorktreeList = React.memo(function WorktreeList({
pinnedDisplayPolicy={pinnedDisplayPolicy}
projectOrderBy={projectOrderBy}
toggleGroup={handleToggleGroup}
onToggleAllProjects={handleToggleAllProjects}
onToggleAllProjects={projectCollapseKeys.length > 0 ? handleToggleAllProjects : undefined}
collapsedGroups={effectiveCollapsedGroups}
handleCreateForRepo={openHeaderCreateComposer}
handleOpenRepoSettings={handleOpenRepoSettings}
@@ -1,7 +1,11 @@
import { expect, it } from 'vitest'
import { getRenderedProjectCollapseKeys } from './project-collapse-all'
import {
areAllProjectsCollapsed,
getRenderedProjectCollapseKeys,
setAllProjectsCollapsed
} from './project-collapse-all'
it('includes project headers without counted workspaces and uses their collapse identities', () => {
it('includes pinned and empty project headers using their collapse identities', () => {
expect(
getRenderedProjectCollapseKeys([
{ type: 'header', key: 'repo:imports', count: 0, icon: { kind: 'project' } },
@@ -23,5 +27,19 @@ it('includes project headers without counted workspaces and uses their collapse
},
{ type: 'header', key: 'pinned', count: 1, icon: { kind: 'pinned' } }
])
).toEqual(['repo:imports', 'repo:inbox', 'repo:pending', 'project:one'])
).toEqual(['repo:imports', 'repo:inbox', 'repo:pending', 'project:one', 'pinned'])
})
it('collapses and expands pinned with projects while preserving unrelated sections', () => {
const keys = getRenderedProjectCollapseKeys([
{ type: 'header', key: 'project:one', count: 1, icon: { kind: 'project' } },
{ type: 'header', key: 'pinned', count: 1, icon: { kind: 'pinned' } }
])
const initial = new Set(['project:one', 'lineage:child', 'status:done'])
expect(areAllProjectsCollapsed(keys, initial)).toBe(false)
const collapsed = setAllProjectsCollapsed(initial, keys, true)
expect(collapsed).toEqual(new Set(['project:one', 'lineage:child', 'status:done', 'pinned']))
expect(areAllProjectsCollapsed(keys, collapsed)).toBe(true)
const expanded = setAllProjectsCollapsed(new Set([...collapsed, 'project:hidden']), keys, false)
expect(expanded).toEqual(new Set(['lineage:child', 'status:done']))
})
@@ -1,4 +1,4 @@
import { isProjectSectionGroupKey } from '../../../../../../shared/sidebar-list/group-keys'
import { isProjectOrPinnedSectionGroupKey } from '../../../../../../shared/sidebar-list/group-keys'
import type { HostSectionRow } from '../../../../../../shared/sidebar-list/host-section-rows'
export function getRenderedProjectCollapseKeys(rows: readonly HostSectionRow[]): string[] {
@@ -8,7 +8,7 @@ export function getRenderedProjectCollapseKeys(rows: readonly HostSectionRow[]):
continue
}
const key = row.collapseKey ?? row.key
if (isProjectSectionGroupKey(key)) {
if (isProjectOrPinnedSectionGroupKey(key)) {
keys.add(key)
}
}
@@ -22,7 +22,7 @@ export function areAllProjectsCollapsed(
return projectKeys.length > 0 && projectKeys.every((key) => collapsedGroups.has(key))
}
// Expanding clears every project key, including nested ones hidden under a collapsed parent.
// Expanding clears pinned and every project key, including hidden nested projects.
export function setAllProjectsCollapsed(
collapsedGroups: ReadonlySet<string>,
projectKeys: readonly string[],
@@ -30,5 +30,5 @@ export function setAllProjectsCollapsed(
): Set<string> {
return collapsed
? new Set([...collapsedGroups, ...projectKeys])
: new Set([...collapsedGroups].filter((key) => !isProjectSectionGroupKey(key)))
: new Set([...collapsedGroups].filter((key) => !isProjectOrPinnedSectionGroupKey(key)))
}
@@ -1,7 +1,7 @@
import type React from 'react'
import '../drag/project-header-drag'
import { isRepoHeaderActionTarget } from '../drag/project-header-drag-contract'
import { isProjectSectionGroupKey } from '../../../../../../shared/sidebar-list/group-keys'
import { isProjectOrPinnedSectionGroupKey } from '../../../../../../shared/sidebar-list/group-keys'
export function stopRepoHeaderKeyboardToggle(event: React.KeyboardEvent<HTMLElement>): void {
if (event.key === 'Enter' || event.key === ' ') {
@@ -31,13 +31,13 @@ export function shouldIgnoreRepoHeaderToggle(event: React.SyntheticEvent<HTMLEle
return isRepoHeaderActionTarget(event.target, event.currentTarget)
}
// Alt/Option-click toggles every project, matching tree views in VS Code and Finder.
// Alt/Option-click toggles all projects and Pinned together.
export function toggleSectionHeaderCollapse(
event: React.MouseEvent | React.KeyboardEvent,
collapseKey: string,
ctx: { toggleGroupWithScrollAnchor: (key: string) => void; toggleAllProjects?: () => void }
): void {
if (event.altKey && ctx.toggleAllProjects && isProjectSectionGroupKey(collapseKey)) {
if (event.altKey && ctx.toggleAllProjects && isProjectOrPinnedSectionGroupKey(collapseKey)) {
ctx.toggleAllProjects()
return
}
+4
View File
@@ -19,6 +19,10 @@ export function isProjectSectionGroupKey(key: string): boolean {
return key.startsWith('repo:') || key.startsWith('project:') || key.startsWith('project-group:')
}
export function isProjectOrPinnedSectionGroupKey(key: string): boolean {
return key === PINNED_GROUP_KEY || isProjectSectionGroupKey(key)
}
export const ALL_GROUP_KEY = 'all'
export const LINEAGE_GROUP_PREFIX = 'lineage:'