Skip to content

fix(sidebar): clean up collapsed header panel layout#2878

Open
fercgomes wants to merge 2 commits into
mainfrom
posthog-code/fix-collapsed-sidebar-header-overlap
Open

fix(sidebar): clean up collapsed header panel layout#2878
fercgomes wants to merge 2 commits into
mainfrom
posthog-code/fix-collapsed-sidebar-header-overlap

Conversation

@fercgomes

@fercgomes fercgomes commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Problem

When the sidebar is collapsed, the header's left panel — the strip holding the Bluebird button and the sidebar toggle — renders broken. Reported symptoms:

  • New-task screen: the input recenters but a leftover sidebar-looking stub stays at the top-left ("the sidebar doesn't disappear").
  • Task open: the Bluebird button slides behind the macOS window controls (traffic lights), and the leftover header chrome reads as the "new task button" floating on top of the task contents.

These come from the HeaderRow.tsx portion of #2872 (which, per its own description, was "not run locally"). The sidebar body is correctly clipped to 0 width there, but the header panel's collapse logic was off:

  1. The macOS traffic-light clearance was added to the panel's width while the buttons stayed right-aligned (justify="end"). Because the Bluebird + toggle cluster is wider than the base collapsed width, right-aligning let its left edge drift back under the traffic lights.
  2. The panel kept its right border when collapsed, so a vertical divider dangled in the header with no sidebar body beneath it — reading as a leftover sidebar stub over the page.
Screen.Recording.2026-06-23.at.17.14.09.mov

Changes

  • headerSidebarPanel.ts (new): pure, testable getHeaderSidebarPanelLayout({ sidebarOpen, sidebarWidth, isMac }) that derives width / minWidth / paddingLeft / justify / border. When collapsed it reserves the macOS traffic-light strip as real left padding and left-aligns the buttons (a wide "Bluebird" label now grows rightward into empty space, never leftward under the controls), and drops the divider (it only belongs while the sidebar body is visible below it). Open-state behavior is unchanged.
  • HeaderRow.tsx: uses the helper instead of inline style math; the panel constants moved into the helper.
  • headerSidebarPanel.test.ts (new): parameterized tests across all open/collapsed × macOS/non-macOS states, plus regression guards (no traffic-light padding while open; no divider when collapsed).

How did you test this?

  • pnpm --filter @posthog/ui typecheck — clean
  • npx biome check on the changed files — clean
  • vitest — 6/6 pass

Could not run the Electron GUI in the dev sandbox (no display / nothing on CDP), so the layout logic is covered by unit tests + static analysis. Worth a quick manual check: collapse the sidebar on macOS and confirm Bluebird clears the traffic lights and no divider/stub remains over the content.

🤖 Generated with Claude Code

PR #2872 clipped the sidebar body when collapsed but its companion HeaderRow
change (shipped "not run locally") left the header's left panel — the strip
holding the Bluebird button and sidebar toggle — in a broken state when
collapsed:

- On macOS the traffic-light clearance was added to the panel's width while the
  buttons stayed right-aligned, so the wider Bluebird+toggle cluster drifted
  back under the window controls.
- The panel kept its right border when collapsed, leaving a divider dangling in
  the header with no sidebar body beneath it — reading as a leftover sidebar
  stub floating over the page content.

Fix: extract the panel layout into a pure, testable
`getHeaderSidebarPanelLayout` helper. When collapsed it now reserves the macOS
traffic-light strip as real left padding and left-aligns the buttons (so a wide
label grows rightward into empty space, never leftward under the controls), and
drops the divider (which only belongs while the sidebar body is visible below
it). Open-state behavior is unchanged.

Verified: typecheck, biome check, and 6 new unit tests covering all
open/collapsed × macOS/non-macOS states pass.

Generated-By: PostHog Code
Task-Id: f4b5e35b-f65b-4c17-a78a-f7076a8a271c
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 10d3725.

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix(sidebar): clean up collapsed header ..." | Re-trigger Greptile

Comment on lines +62 to +82
it("never reserves traffic-light padding while the sidebar is open", () => {
expect(
getHeaderSidebarPanelLayout({
sidebarOpen: true,
sidebarWidth: 256,
isMac: true,
}).paddingLeft,
).toBeUndefined();
});

it("drops the divider when collapsed so it can't dangle over the page", () => {
for (const isMac of [true, false]) {
expect(
getHeaderSidebarPanelLayout({
sidebarOpen: false,
sidebarWidth: 256,
isMac,
}).showBorder,
).toBe(false);
}
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant regression guards and non-parameterized loop

Both standalone it() guards test assertions already fully covered by the it.each block above: the "open on macOS" and "open off macOS" cases already assert paddingLeft: undefined, and both collapsed cases already assert showBorder: false. Keeping them adds noise without adding failure coverage (simplicity rule 4: no superfluous parts).

Additionally, the for (const isMac of [true, false]) loop in the second guard is exactly the pattern the team prefers to express as a parameterised .each — though since the cases are already covered above, the cleanest fix is simply to remove both standalone tests.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

The two standalone it() guards duplicated assertions already covered by the
it.each block (which asserts the full layout object via toEqual for every
state), and one used a for-loop where the team prefers it.each. Remove them and
fold the regression intent (traffic-light clearance, dropped divider) into the
parameterized case names instead. Per Greptile review feedback on #2878.

Generated-By: PostHog Code
Task-Id: f4b5e35b-f65b-4c17-a78a-f7076a8a271c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants