Skip to content

Commit cc70d94

Browse files
committed
fix: preserve sidebar row session selection
1 parent 21607a9 commit cc70d94

2 files changed

Lines changed: 80 additions & 1 deletion

File tree

frontend/src/lib/components/sidebar/SessionItem.svelte

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@
223223
224224
function handleSessionClick(e: MouseEvent) {
225225
if (
226-
e.detail === 0 ||
227226
e.metaKey ||
228227
e.ctrlKey ||
229228
e.shiftKey ||
@@ -236,6 +235,26 @@
236235
sessions.selectSession(session.id);
237236
}
238237
238+
function handleRowClick(e: MouseEvent) {
239+
if (
240+
e.metaKey ||
241+
e.ctrlKey ||
242+
e.shiftKey ||
243+
e.altKey ||
244+
e.button !== 0
245+
) {
246+
return;
247+
}
248+
const target = e.target;
249+
if (!(target instanceof Element)) {
250+
return;
251+
}
252+
if (target.closest("a, button, input")) {
253+
return;
254+
}
255+
sessions.selectSession(session.id);
256+
}
257+
239258
$effect(() => {
240259
if (!contextMenu) return;
241260
function handler() {
@@ -266,6 +285,7 @@
266285

267286
<!-- svelte-ignore a11y_no_static_element_interactions -->
268287
<!-- svelte-ignore a11y_no_static_element_interactions -->
288+
<!-- svelte-ignore a11y_click_events_have_key_events -->
269289
<div
270290
class="session-item"
271291
class:active={isActive}
@@ -276,6 +296,7 @@
276296
data-session-id={session.id}
277297
aria-current={isActive ? "page" : undefined}
278298
style:padding-left="{8 + depth * 16}px"
299+
onclick={handleRowClick}
279300
oncontextmenu={handleContextMenu}
280301
>
281302
<!-- Tree expand/collapse or connector -->

frontend/src/lib/components/sidebar/SessionList.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,64 @@ describe("SessionList visible hydration", () => {
443443
expect(link?.getAttribute("href")).toBe("/sessions/native-session");
444444
});
445445

446+
it("keeps keyboard-style anchor activation on the SPA session path", async () => {
447+
const selectSession = vi
448+
.spyOn(sessions, "selectSession")
449+
.mockImplementation(() => {});
450+
sessions.sessions = [
451+
makeSession({
452+
id: "keyboard-session",
453+
display_name: "Keyboard target",
454+
is_index_only: false,
455+
}),
456+
];
457+
vi.spyOn(sessions, "hydrateVisibleSessions").mockResolvedValue(
458+
undefined,
459+
);
460+
461+
component = mount(SessionList, { target: document.body });
462+
await tick();
463+
464+
const link = document.querySelector<HTMLAnchorElement>(
465+
".session-info-link",
466+
);
467+
expect(link).not.toBeNull();
468+
const click = new MouseEvent("click", {
469+
bubbles: true,
470+
cancelable: true,
471+
detail: 0,
472+
});
473+
link!.dispatchEvent(click);
474+
475+
expect(click.defaultPrevented).toBe(true);
476+
expect(selectSession).toHaveBeenCalledWith("keyboard-session");
477+
});
478+
479+
it("keeps the non-link parts of the row selectable", async () => {
480+
const selectSession = vi
481+
.spyOn(sessions, "selectSession")
482+
.mockImplementation(() => {});
483+
sessions.sessions = [
484+
makeSession({
485+
id: "row-session",
486+
display_name: "Row target",
487+
is_index_only: false,
488+
}),
489+
];
490+
vi.spyOn(sessions, "hydrateVisibleSessions").mockResolvedValue(
491+
undefined,
492+
);
493+
494+
component = mount(SessionList, { target: document.body });
495+
await tick();
496+
497+
const sideMeta = document.querySelector<HTMLElement>(".side-meta");
498+
expect(sideMeta).not.toBeNull();
499+
sideMeta!.click();
500+
501+
expect(selectSession).toHaveBeenCalledWith("row-session");
502+
});
503+
446504
it("opens the same canonical href from the context menu in a new tab", async () => {
447505
const openSpy = vi
448506
.spyOn(window, "open")

0 commit comments

Comments
 (0)