Skip to content

Commit 5cd816b

Browse files
committed
fix(web): refine open files sidebar controls
1 parent 4118ba2 commit 5cd816b

14 files changed

Lines changed: 112 additions & 76 deletions

packages/web/src/features/code-editor/index.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ describe("CodeEditorHost", () => {
460460
);
461461
});
462462

463-
const heading = screen.getByRole("heading", { level: 2, name: "Open Editors (1)" });
463+
const heading = screen.getByRole("heading", { level: 2, name: "Open Files (1)" });
464464
const section = heading.closest("section") as HTMLElement;
465465
const closeAll = within(section).getByRole("button", { name: "Close all" });
466466
expect(closeAll).toBeEnabled();

packages/web/src/features/workspace/index.test.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ describe("WorkspacePage", () => {
411411
</Provider>
412412
);
413413

414-
await screen.findByText(/Open Editors|/i);
414+
await screen.findByText(/Open Files|/i);
415415

416416
const explorerButton = screen.getByRole("button", { name: /Explorer|/i });
417417
expect(explorerButton).toHaveAttribute("aria-pressed", "true");
@@ -1703,9 +1703,9 @@ describe("WorkspacePage", () => {
17031703
await screen.findByTestId("code-editor-host");
17041704
expect(screen.queryByTestId("agent-panes")).not.toBeInTheDocument();
17051705

1706-
const heading = screen.getByRole("heading", { level: 2, name: /(Open Editors|)/i });
1706+
const heading = screen.getByRole("heading", { level: 2, name: /(Open Files|)/i });
17071707
const section = heading.closest("section") as HTMLElement;
1708-
expect(heading).toHaveTextContent(/Open Editors|/i);
1708+
expect(heading).toHaveTextContent(/Open Files|/i);
17091709
expect(within(section).getByText("1")).toHaveClass("workspace-open-editors__count");
17101710
fireEvent.click(within(section).getByRole("button", { name: /Close all|/i }));
17111711

@@ -1862,7 +1862,7 @@ describe("WorkspacePage", () => {
18621862

18631863
await screen.findByTestId("code-editor-host");
18641864

1865-
const heading = screen.getByRole("heading", { level: 2, name: /(Open Editors|)/i });
1865+
const heading = screen.getByRole("heading", { level: 2, name: /(Open Files|)/i });
18661866
const section = heading.closest("section") as HTMLElement;
18671867
fireEvent.click(within(section).getByRole("button", { name: /Close all|/i }));
18681868

@@ -1891,7 +1891,7 @@ describe("WorkspacePage", () => {
18911891
});
18921892
});
18931893

1894-
it("clearing the final open editor from Open Editors preserves an active commit preview", async () => {
1894+
it("clearing the final open editor from Open Files preserves an active commit preview", async () => {
18951895
const sendCommand = vi.fn().mockImplementation(async (op: string) => {
18961896
if (op === "git.status") {
18971897
return {

packages/web/src/features/workspace/views/desktop/workspace-desktop-view.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ vi.mock("../../../../lib/i18n", () => ({
1818
"workspace.sidebar.source_control": "Source Control",
1919
"workspace.sidebar.label": "Workspace Sidebar",
2020
"workspace.sidebar.workspace": "Workspace",
21-
"workspace.sidebar.open_editors": "Open Editors",
21+
"workspace.sidebar.open_editors": "Open Files",
2222
"workspace.no_workspace": "No workspace",
2323
"workspace.search.empty": "Type to search across file contents",
2424
"workspace.search.placeholder": "Search",

packages/web/src/features/workspace/views/mobile/mobile-explorer-panel.test.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ vi.mock("../../../../lib/i18n", () => ({
1919
"workspace.sidebar.workspace": "Workspace",
2020
"workspace.sidebar.workspace_expand_label": "Expand Workspace",
2121
"workspace.sidebar.workspace_collapse_label": "Collapse Workspace",
22-
"workspace.sidebar.open_editors": "Open Editors",
22+
"workspace.sidebar.open_editors": "Open Files",
2323
"file.new_file": "New File",
2424
"file.new_folder": "New Folder",
2525
"file.collapse_all": "Collapse All",
@@ -29,15 +29,15 @@ vi.mock("../../../../lib/i18n", () => ({
2929
};
3030

3131
if (key === "workspace.open_editors.title_with_count") {
32-
return `${params?.title ?? "Open Editors"} (${params?.count ?? 0})`;
32+
return `${params?.title ?? "Open Files"} (${params?.count ?? 0})`;
3333
}
3434

3535
if (key === "workspace.open_editors.expand_label") {
36-
return "Expand Open Editors";
36+
return "Expand Open Files";
3737
}
3838

3939
if (key === "workspace.open_editors.collapse_label") {
40-
return "Collapse Open Editors";
40+
return "Collapse Open Files";
4141
}
4242

4343
if (key === "workspace.open_editors.close_path") {
@@ -120,7 +120,7 @@ describe("MobileExplorerPanel", () => {
120120

121121
const headings = screen.getAllByRole("heading", { level: 2 });
122122
expect(headings[0]).toHaveTextContent(/Quick Jump|/i);
123-
expect(headings[1]).toHaveTextContent(/Open Editors|/i);
123+
expect(headings[1]).toHaveTextContent(/Open Files|/i);
124124
expect(headings[2]).toHaveTextContent(/Workspace|/i);
125125

126126
expect(screen.getByRole("button", { name: "README.md" })).toBeInTheDocument();
@@ -205,15 +205,15 @@ describe("MobileExplorerPanel", () => {
205205
</Provider>
206206
);
207207

208-
const heading = screen.getByRole("heading", { level: 2, name: /(Open Editors|)/i });
209-
expect(heading).toHaveTextContent(/Open Editors|/i);
208+
const heading = screen.getByRole("heading", { level: 2, name: /(Open Files|)/i });
209+
expect(heading).toHaveTextContent(/Open Files|/i);
210210

211211
const section = heading.closest("section") as HTMLElement;
212212
expect(within(section).getByText("2")).toBeInTheDocument();
213213

214214
expect(
215215
within(section).getByRole("button", {
216-
name: /Collapse Open Editors|Expand Open Editors||/i,
216+
name: /Collapse Open Files|Expand Open Files||/i,
217217
})
218218
).toHaveAttribute("aria-expanded", "true");
219219
expect(
@@ -236,7 +236,7 @@ describe("MobileExplorerPanel", () => {
236236
"workspace-open-editors__item--active"
237237
);
238238
expect(within(section).queryByRole("button", { name: "src/alpha.tsx" })).toBeNull();
239-
expect(heading).toHaveTextContent(/Open Editors|/i);
239+
expect(heading).toHaveTextContent(/Open Files|/i);
240240
expect(within(section).getByText("1")).toBeInTheDocument();
241241
expect(Object.keys(store.get(openFilesAtomFamily("ws-test")))).toEqual(["src/beta.tsx"]);
242242
expect(store.get(activeFilePathAtomFamily("ws-test"))).toBeNull();

packages/web/src/features/workspace/views/mobile/workspace-mobile-view.test.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ vi.mock("../../../../lib/i18n", () => ({
5656
"mobile.sheet.dismiss": "Dismiss sheet",
5757
"action.close_all": "Close all",
5858
"workspace.sidebar.explorer": "Explorer",
59-
"workspace.sidebar.open_editors": "Open Editors",
60-
"workspace.open_editors.collapse_label": "Collapse Open Editors",
61-
"workspace.open_editors.expand_label": "Expand Open Editors",
59+
"workspace.sidebar.open_editors": "Open Files",
60+
"workspace.open_editors.collapse_label": "Collapse Open Files",
61+
"workspace.open_editors.expand_label": "Expand Open Files",
6262
"workspace.sidebar.search": "Search",
6363
"workspace.sidebar.source_control": "Source Control",
6464
};
@@ -68,7 +68,7 @@ vi.mock("../../../../lib/i18n", () => ({
6868
}
6969

7070
if (key === "workspace.open_editors.title_with_count") {
71-
return `${params?.title ?? "Open Editors"} (${params?.count ?? "0"})`;
71+
return `${params?.title ?? "Open Files"} (${params?.count ?? "0"})`;
7272
}
7373

7474
if (key === "workspace.open_editors.close_path") {
@@ -451,7 +451,7 @@ describe("WorkspaceMobileView", () => {
451451

452452
fireEvent.click(screen.getByRole("button", { name: "Files" }));
453453
const openEditorsSection = screen
454-
.getByRole("heading", { level: 2, name: "Open Editors (1)" })
454+
.getByRole("heading", { level: 2, name: "Open Files (1)" })
455455
.closest("section") as HTMLElement;
456456
fireEvent.click(within(openEditorsSection).getByRole("button", { name: "Close all" }));
457457

packages/web/src/features/workspace/views/shared/explorer-panel.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ vi.mock("../../../../lib/i18n", () => ({
1313
const translations: Record<string, string> = {
1414
"workspace.sidebar.explorer": "Explorer",
1515
"workspace.sidebar.workspace": "Workspace",
16-
"workspace.sidebar.open_editors": "Open Editors",
16+
"workspace.sidebar.open_editors": "Open Files",
1717
"workspace.sidebar.workspace_expand_label": "Expand Workspace",
1818
"workspace.sidebar.workspace_collapse_label": "Collapse Workspace",
1919
"file.new_file": "New File",
@@ -24,15 +24,15 @@ vi.mock("../../../../lib/i18n", () => ({
2424
};
2525

2626
if (key === "workspace.open_editors.title_with_count") {
27-
return `${params?.title ?? "Open Editors"} (${params?.count ?? 0})`;
27+
return `${params?.title ?? "Open Files"} (${params?.count ?? 0})`;
2828
}
2929

3030
if (key === "workspace.open_editors.expand_label") {
31-
return "Expand Open Editors";
31+
return "Expand Open Files";
3232
}
3333

3434
if (key === "workspace.open_editors.collapse_label") {
35-
return "Collapse Open Editors";
35+
return "Collapse Open Files";
3636
}
3737

3838
if (key === "workspace.open_editors.close_path") {

packages/web/src/features/workspace/views/shared/open-editors-section.test.tsx

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { OpenEditorsSection } from "./open-editors-section";
2424
vi.mock("../../../../lib/i18n", () => ({
2525
useTranslation: () => (key: string, params?: Record<string, string | number>) => {
2626
if (key === "common.cancel") return "Cancel";
27-
if (key === "workspace.sidebar.open_editors") return "Open Editors";
27+
if (key === "workspace.sidebar.open_editors") return "Open Files";
2828
if (key === "action.close") return "Close";
2929
if (key === "action.close_all") return "Close all";
3030
if (key === "code_editor.unsaved_changes") return "Unsaved changes";
@@ -34,13 +34,13 @@ vi.mock("../../../../lib/i18n", () => ({
3434
}
3535
if (key === "code_editor.discard_and_close") return "Discard and Close";
3636
if (key === "workspace.open_editors.close_all_unsaved_description") {
37-
return `${params?.count ?? 0} open editors have unsaved changes.`;
37+
return `${params?.count ?? 0} open files have unsaved changes.`;
3838
}
3939
if (key === "workspace.open_editors.title_with_count") {
40-
return `${params?.title ?? "Open Editors"} (${params?.count ?? 0})`;
40+
return `${params?.title ?? "Open Files"} (${params?.count ?? 0})`;
4141
}
42-
if (key === "workspace.open_editors.expand_label") return "Expand Open Editors";
43-
if (key === "workspace.open_editors.collapse_label") return "Collapse Open Editors";
42+
if (key === "workspace.open_editors.expand_label") return "Expand Open Files";
43+
if (key === "workspace.open_editors.collapse_label") return "Collapse Open Files";
4444
if (key === "workspace.open_editors.close_path") {
4545
return `Close ${params?.path ?? ""}`;
4646
}
@@ -113,12 +113,12 @@ describe("OpenEditorsSection", () => {
113113
it("shows file count, toggles collapse, closes a non-active row, and closes all", () => {
114114
const { store } = renderSection();
115115

116-
const heading = screen.getByRole("heading", { level: 2, name: "Open Editors (3)" });
117-
expect(heading).toHaveTextContent("Open Editors");
116+
const heading = screen.getByRole("heading", { level: 2, name: "Open Files (3)" });
117+
expect(heading).toHaveTextContent("Open Files");
118118

119119
const section = heading.closest("section") as HTMLElement;
120120
expect(within(section).getByText("3")).toHaveClass("workspace-open-editors__count");
121-
const toggle = within(section).getByRole("button", { name: /collapse open editors/i });
121+
const toggle = within(section).getByRole("button", { name: /collapse open files/i });
122122

123123
expect(toggle).toHaveAttribute("aria-expanded", "true");
124124

@@ -142,7 +142,7 @@ describe("OpenEditorsSection", () => {
142142
]);
143143

144144
fireEvent.click(toggle);
145-
expect(within(section).getByRole("button", { name: "Expand Open Editors" })).toHaveAttribute(
145+
expect(within(section).getByRole("button", { name: "Expand Open Files" })).toHaveAttribute(
146146
"aria-expanded",
147147
"false"
148148
);
@@ -152,7 +152,7 @@ describe("OpenEditorsSection", () => {
152152
})
153153
).toBeNull();
154154

155-
fireEvent.click(within(section).getByRole("button", { name: /expand open editors/i }));
155+
fireEvent.click(within(section).getByRole("button", { name: /expand open files/i }));
156156

157157
const readmeRow = within(section)
158158
.getByRole("button", { name: "README.md" })
@@ -165,21 +165,25 @@ describe("OpenEditorsSection", () => {
165165
]);
166166
expect(store.get(activeFilePathAtomFamily("ws-test"))).toBe("src/beta.ts");
167167

168-
fireEvent.click(within(section).getByRole("button", { name: "Close all" }));
168+
const closeAllButton = within(section).getByRole("button", { name: "Close all" });
169+
expect(closeAllButton).toHaveTextContent(/^$/);
170+
expect(closeAllButton.querySelector("svg")).toBeTruthy();
171+
172+
fireEvent.click(closeAllButton);
169173

170174
expect(store.get(openFilesAtomFamily("ws-test"))).toEqual({});
171175
expect(store.get(activeFilePathAtomFamily("ws-test"))).toBeNull();
172176
});
173177

174-
it("keeps the header visible but hides the body when there are no open editors", () => {
178+
it("keeps the header visible but hides the body when there are no open files", () => {
175179
renderSection({});
176180

177-
const heading = screen.getByRole("heading", { level: 2, name: "Open Editors (0)" });
178-
expect(heading).toHaveTextContent("Open Editors");
181+
const heading = screen.getByRole("heading", { level: 2, name: "Open Files (0)" });
182+
expect(heading).toHaveTextContent("Open Files");
179183

180184
const section = heading.closest("section") as HTMLElement;
181185
expect(within(section).getByText("0")).toHaveClass("workspace-open-editors__count");
182-
const toggle = within(section).getByRole("button", { name: "Expand Open Editors" });
186+
const toggle = within(section).getByRole("button", { name: "Expand Open Files" });
183187

184188
expect(toggle).toBeDisabled();
185189
expect(toggle).not.toHaveAttribute("aria-expanded");
@@ -194,13 +198,13 @@ describe("OpenEditorsSection", () => {
194198

195199
renderSection({}, "src/pending.ts");
196200

197-
const heading = screen.getByRole("heading", { level: 2, name: "Open Editors (1)" });
198-
expect(heading).toHaveTextContent("Open Editors");
201+
const heading = screen.getByRole("heading", { level: 2, name: "Open Files (1)" });
202+
expect(heading).toHaveTextContent("Open Files");
199203

200204
const section = heading.closest("section") as HTMLElement;
201205
expect(within(section).getByText("1")).toHaveClass("workspace-open-editors__count");
202206

203-
expect(within(section).getByRole("button", { name: "Collapse Open Editors" })).toHaveAttribute(
207+
expect(within(section).getByRole("button", { name: "Collapse Open Files" })).toHaveAttribute(
204208
"aria-expanded",
205209
"true"
206210
);
@@ -215,7 +219,7 @@ describe("OpenEditorsSection", () => {
215219
draftStore.set(openEditorPathsAtomFamily("ws-test"), ["src/app.tsx", "README.md"]);
216220
});
217221

218-
const heading = screen.getByRole("heading", { level: 2, name: "Open Editors (2)" });
222+
const heading = screen.getByRole("heading", { level: 2, name: "Open Files (2)" });
219223
const section = heading.closest("section") as HTMLElement;
220224
const rowButtons = Array.from(
221225
section.querySelectorAll<HTMLButtonElement>(".workspace-open-editors__item")
@@ -254,7 +258,7 @@ describe("OpenEditorsSection", () => {
254258
expect(store.get(activeFilePathAtomFamily("ws-test"))).toBe("README.md");
255259
});
256260

257-
it("marks dirty open editors and confirms before closing a dirty row", () => {
261+
it("marks dirty open files and confirms before closing a dirty row", () => {
258262
const { store } = renderSection(
259263
{
260264
"biome.jsonc": createDirtyFile("biome.jsonc"),
@@ -283,7 +287,7 @@ describe("OpenEditorsSection", () => {
283287
expect(store.get(activeFilePathAtomFamily("ws-test"))).toBeNull();
284288
});
285289

286-
it("confirms before closing all when open editors include dirty files", () => {
290+
it("confirms before closing all when open files include dirty files", () => {
287291
const { store } = renderSection(
288292
{
289293
"src/clean.ts": createFile("src/clean.ts"),
@@ -299,7 +303,7 @@ describe("OpenEditorsSection", () => {
299303
"src/dirty.ts",
300304
]);
301305
expect(screen.getByRole("dialog", { name: "Discard unsaved changes?" })).toBeInTheDocument();
302-
expect(screen.getByText("1 open editors have unsaved changes.")).toBeInTheDocument();
306+
expect(screen.getByText("1 open files have unsaved changes.")).toBeInTheDocument();
303307

304308
fireEvent.click(screen.getByRole("button", { name: "Discard and Close" }));
305309

packages/web/src/features/workspace/views/shared/open-editors-section.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,16 @@ export function OpenEditorsSection({ workspaceId, onSelectFile, title }: OpenEdi
141141
{openEditorPaths.length}
142142
</span>
143143
</div>
144-
<button
145-
type="button"
146-
className="workspace-sidebar-section__action workspace-open-editors__close-all"
147-
disabled={openEditorPaths.length === 0}
148-
onClick={requestCloseAll}
149-
title={headingLabel}
150-
>
151-
{t("action.close_all")}
152-
</button>
144+
<Tooltip content={t("action.close_all")}>
145+
<IconButton
146+
aria-label={t("action.close_all")}
147+
className="workspace-open-editors__close-all"
148+
disabled={openEditorPaths.length === 0}
149+
icon={<X size={14} />}
150+
size="sm"
151+
onClick={requestCloseAll}
152+
/>
153+
</Tooltip>
153154
</div>
154155
{isExpanded ? (
155156
<div className="workspace-open-editors">

packages/web/src/features/workspace/views/shared/search-panel.test.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,7 @@ describe("SearchPanel", () => {
197197
fireEvent.change(screen.getByRole("textbox", { name: /Files to Exclude|/i }), {
198198
target: { value: "**/*.spec.tsx" },
199199
});
200-
expect(
201-
screen.queryByRole("switch", { name: /Only Open Editors|/i })
202-
).toBeNull();
200+
expect(screen.queryByRole("switch", { name: /Only Open Files|/i })).toBeNull();
203201
fireEvent.click(
204202
screen.getByRole("button", {
205203
name: /Use Exclude Settings and Ignore Files|使/i,
@@ -296,9 +294,7 @@ describe("SearchPanel", () => {
296294
expect(replaceCompound?.contains(expandedDetailsToggle)).toBe(false);
297295
expect(excludeCompound).not.toBeNull();
298296
expect(excludeCompound?.contains(ignoreToggle)).toBe(true);
299-
expect(
300-
screen.queryByRole("switch", { name: /Only Open Editors|/i })
301-
).toBeNull();
297+
expect(screen.queryByRole("switch", { name: /Only Open Files|/i })).toBeNull();
302298

303299
fireEvent.click(expandedDetailsToggle);
304300
const collapsedDetailsToggle = screen.getByRole("button", {

packages/web/src/lib/i18n.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import fs from "node:fs";
33
import path from "node:path";
44
import { describe, expect, it } from "vitest";
5+
import en from "../locales/en.json";
56
import zh from "../locales/zh.json";
67

78
function flattenKeys(value: unknown, prefix = ""): string[] {
@@ -36,6 +37,15 @@ function collectSourceFiles(dir: string): string[] {
3637
}
3738

3839
describe("i18n coverage", () => {
40+
it("labels the open editor section as open files in user-facing copy", () => {
41+
expect(zh.workspace.sidebar.open_editors).toBe("打开的文件");
42+
expect(zh.workspace.open_editors.expand_label).toBe("展开打开的文件");
43+
expect(zh.workspace.open_editors.collapse_label).toBe("收起打开的文件");
44+
expect(en.workspace.sidebar.open_editors).toBe("Open Files");
45+
expect(en.workspace.open_editors.expand_label).toBe("Expand Open Files");
46+
expect(en.workspace.open_editors.collapse_label).toBe("Collapse Open Files");
47+
});
48+
3949
it("resolves every static translation key used in source files", () => {
4050
const localeKeys = new Set(flattenKeys(zh));
4151
const sourceRoot = path.resolve(__dirname, "..");

0 commit comments

Comments
 (0)