1import { expect, it } from "vite-plus/test";
2import { userEvent } from "vite-plus/test/browser";
3import { createWorkspaceFocusController } from "../lib/workspaceFocus";
4import { shouldRefocusComposerOnWindowFocus } from "./ChatView.logic";
5
6it("keeps keyboard focus on a sidebar row on window return, but not a clicked row", async () => {
7 const list = document.createElement("ul");
8 const row = (name: string) => {
9 const element = document.createElement("div");
10 element.setAttribute("role", "button");
11 element.tabIndex = 0;
12 element.dataset.sidebarRow = name;
13 element.textContent = name;
14 list.append(element);
15 return element;
16 };
17 const clicked = row("Clicked thread");
18 const tabbed = row("Tabbed thread");
19 document.body.append(list);
20 using _list = { [Symbol.dispose]: () => list.remove() };
21
22 await userEvent.click(clicked);
23 expect(document.activeElement).toBe(clicked);
24 expect(shouldRefocusComposerOnWindowFocus(document.activeElement)).toBe(true);
25
26 await userEvent.tab();
27 expect(document.activeElement).toBe(tabbed);
28 expect(shouldRefocusComposerOnWindowFocus(document.activeElement)).toBe(false);
29});
30
31it("keeps focus in a region with its own keys on window return", async () => {
32 const region = document.createElement("div");
33 region.tabIndex = -1;
34 region.setAttribute("aria-keyshortcuts", "J K");
35 region.textContent = "Guide prose";
36 document.body.append(region);
37 using _region = { [Symbol.dispose]: () => region.remove() };
38
39 await userEvent.click(region);
40 expect(document.activeElement).toBe(region);
41 expect(shouldRefocusComposerOnWindowFocus(document.activeElement)).toBe(false);
42});
43
44it("refocuses the composer on window return from a clicked area after a bare key press", async () => {
45 const controller = createWorkspaceFocusController(document);
46 const root = document.createElement("div");
47 root.innerHTML =
48 '<nav data-workspace-area="sidebar" data-workspace-order="0" tabindex="-1"><button>Thread</button></nav>' +
49 '<section data-workspace-area="conversation" data-workspace-order="1" tabindex="-1" style="height: 200px"></section>';
50 document.body.append(root);
51 using _root = {
52 [Symbol.dispose]: () => {
53 controller.dispose();
54 root.remove();
55 },
56 };
57 const conversation = root.querySelector("section")!;
58
59 await userEvent.click(conversation);
60 expect(document.activeElement).toBe(conversation);
61 // A window switch can deliver a lone modifier key without moving focus.
62 await userEvent.keyboard("{Shift}");
63 expect(
64 shouldRefocusComposerOnWindowFocus(document.activeElement),
65 "a clicked area counts as reached from the keyboard after a bare key press",
66 ).toBe(true);
67
68 await userEvent.keyboard("{F6}");
69 controller.move(-1);
70 await userEvent.keyboard("{F6}");
71 controller.move(1);
72 expect(document.activeElement).toBe(conversation);
73 expect(shouldRefocusComposerOnWindowFocus(document.activeElement)).toBe(false);
74});