Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit d117c82

Browse files
authored
Keep the main page stable when selecting a space (#4040)
1 parent ff4d70b commit d117c82

2 files changed

Lines changed: 37 additions & 31 deletions

File tree

packages/ui/src/features/canvas/components/ChannelsList.test.tsx

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
showChannelList,
5353
showChannelPane,
5454
} from "@posthog/ui/features/canvas/stores/channelPaneStore";
55+
import { useCurrentChannelStore } from "@posthog/ui/features/canvas/stores/currentChannelStore";
5556
import { useSidebarStore } from "@posthog/ui/features/sidebar/sidebarStore";
5657
import { ChannelsList } from "./ChannelsList";
5758

@@ -81,6 +82,16 @@ describe("ChannelsList", () => {
8182
useSidebarStore.setState({ collapsedSections: new Set() });
8283
});
8384

85+
it("opens a space in the sidebar without navigating the main window", async () => {
86+
const user = userEvent.setup();
87+
renderList();
88+
89+
await user.click(screen.getByText("engineering"));
90+
91+
expect(useCurrentChannelStore.getState().currentChannelId).toBe(ENG.id);
92+
expect(mocks.navigate).not.toHaveBeenCalled();
93+
});
94+
8495
it("pins #me above the channels, with its ⌘1 shortcut", () => {
8596
renderList();
8697
const me = screen.getByText("me");
@@ -182,10 +193,8 @@ describe("ChannelsList", () => {
182193
await user.type(screen.getByLabelText("Search spaces"), "eng");
183194
await user.keyboard("{Enter}");
184195

185-
expect(mocks.navigate).toHaveBeenCalledWith({
186-
to: "/website/$channelId",
187-
params: { channelId: ENG.id },
188-
});
196+
expect(useCurrentChannelStore.getState().currentChannelId).toBe(ENG.id);
197+
expect(mocks.navigate).not.toHaveBeenCalled();
189198
});
190199

191200
it("moves the highlight with the arrow keys", async () => {
@@ -197,10 +206,8 @@ describe("ChannelsList", () => {
197206
await user.type(screen.getByLabelText("Search spaces"), "e");
198207
await user.keyboard("{ArrowDown}{Enter}");
199208

200-
expect(mocks.navigate).toHaveBeenCalledWith({
201-
to: "/website/$channelId",
202-
params: { channelId: ENG.id },
203-
});
209+
expect(useCurrentChannelStore.getState().currentChannelId).toBe(ENG.id);
210+
expect(mocks.navigate).not.toHaveBeenCalled();
204211
});
205212

206213
// Base UI's clear button is a tabIndex=-1 decoration by default, which left
@@ -247,10 +254,8 @@ describe("ChannelsList", () => {
247254
await user.click(screen.getByLabelText("Search spaces"));
248255
await user.keyboard("{ArrowDown}{Enter}");
249256

250-
expect(mocks.navigate).toHaveBeenCalledWith({
251-
to: "/website/$channelId",
252-
params: { channelId: ENG.id },
253-
});
257+
expect(useCurrentChannelStore.getState().currentChannelId).toBe(ENG.id);
258+
expect(mocks.navigate).not.toHaveBeenCalled();
254259
});
255260

256261
// Base UI resets the highlight when the pointer leaves a row, and
@@ -266,10 +271,8 @@ describe("ChannelsList", () => {
266271
await user.unhover(row);
267272
await user.keyboard("{Enter}");
268273

269-
expect(mocks.navigate).toHaveBeenCalledWith({
270-
to: "/website/$channelId",
271-
params: { channelId: ENG.id },
272-
});
274+
expect(useCurrentChannelStore.getState().currentChannelId).toBe(ENG.id);
275+
expect(mocks.navigate).not.toHaveBeenCalled();
273276
});
274277

275278
// A kept-mounted collapsed row would still be an option, so ↓ would walk
@@ -323,10 +326,8 @@ describe("ChannelsList", () => {
323326
// it would have been the row after it.
324327
await user.keyboard("{ArrowDown}{Enter}");
325328

326-
expect(mocks.navigate).toHaveBeenCalledWith({
327-
to: "/website/$channelId",
328-
params: { channelId: ENG.id },
329-
});
329+
expect(useCurrentChannelStore.getState().currentChannelId).toBe(ENG.id);
330+
expect(mocks.navigate).not.toHaveBeenCalled();
330331
});
331332

332333
it("selects a stale query so the next keystroke replaces it", async () => {

packages/ui/src/features/canvas/components/ChannelsList.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ function ChannelMenu({
393393
);
394394
}
395395

396-
// One channel in the list: a "# name" row that navigates to the channel home.
396+
// One channel in the list: a "# name" row that opens its sidebar.
397397
// No expansion — the channel's surfaces live in the in-channel top nav.
398398
function ChannelSection({
399399
channel,
@@ -434,8 +434,8 @@ function ChannelSection({
434434

435435
return (
436436
<Box className="group/chan relative" {...hoverProps}>
437-
{/* A single, non-expandable row: the "# name" navigates straight to the
438-
channel home. Right-clicking opens the same actions as the "..." menu. */}
437+
{/* A single, non-expandable row: the "# name" opens the channel sidebar.
438+
Right-clicking opens the same actions as the "..." menu. */}
439439
<ContextMenu>
440440
<ContextMenuTrigger
441441
render={
@@ -635,6 +635,7 @@ function useOpenPersonalChannel(): {
635635
openPersonalChannel: () => Promise<void>;
636636
isCreating: boolean;
637637
} {
638+
const spacesLayout = useChannelsLayout();
638639
const navigate = useNavigate();
639640
const setCurrentChannel = useCurrentChannelStore((s) => s.setCurrentChannel);
640641
const { channels } = useChannels();
@@ -656,18 +657,20 @@ function useOpenPersonalChannel(): {
656657
if (!channelId) return;
657658
showChannelPane();
658659
setCurrentChannel(channelId);
659-
void navigate({ to: "/website/$channelId", params: { channelId } });
660+
if (!spacesLayout) {
661+
void navigate({ to: "/website/$channelId", params: { channelId } });
662+
}
660663
};
661664

662665
return { ensureFolderId, openPersonalChannel, isCreating };
663666
}
664667

665668
/**
666-
* Navigating into a channel, shared by the tree rows and the search results.
667-
* Slides before navigating: the route effect would get there too, but not until
668-
* the navigation resolves.
669+
* Opening a channel, shared by the tree rows and the search results. In the
670+
* Spaces layout this scopes the sidebar without moving the main window.
669671
*/
670672
function useOpenChannel(): (channel: Channel) => void {
673+
const spacesLayout = useChannelsLayout();
671674
const navigate = useNavigate();
672675
const setCurrentChannel = useCurrentChannelStore((s) => s.setCurrentChannel);
673676

@@ -679,10 +682,12 @@ function useOpenChannel(): (channel: Channel) => void {
679682
});
680683
showChannelPane();
681684
setCurrentChannel(channel.id);
682-
void navigate({
683-
to: "/website/$channelId",
684-
params: { channelId: channel.id },
685-
});
685+
if (!spacesLayout) {
686+
void navigate({
687+
to: "/website/$channelId",
688+
params: { channelId: channel.id },
689+
});
690+
}
686691
};
687692
}
688693

0 commit comments

Comments
 (0)