fix(KB-109): fix FileEditor layout and add component tests

- Fix FileEditor to properly use full width with box-sizing and min-width constraints
- Add visible focus and selection states for the textarea-based editor
- Ensure FileEditor correctly responds to theme changes (dark/light mode)
- Add comprehensive FileEditor component tests (60 test lines)
- Include changeset for patch release of @dustinbyrne/kb
This commit is contained in:
gsxdsm
2026-03-30 07:39:07 -07:00
parent 8365986c1d
commit 7b58926894
3 changed files with 73 additions and 8 deletions

View File

@@ -0,0 +1,60 @@
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { FileEditor } from "./FileEditor";
describe("FileEditor", () => {
it("renders textarea with correct class names", () => {
render(<FileEditor content="" onChange={vi.fn()} />);
const textarea = screen.getByRole("textbox");
expect(textarea.classList.contains("file-editor-container")).toBe(true);
expect(textarea.classList.contains("file-editor-textarea")).toBe(true);
});
it("renders with content prop value", () => {
const content = "const x = 42;";
render(<FileEditor content={content} onChange={vi.fn()} />);
const textarea = screen.getByRole("textbox") as HTMLTextAreaElement;
expect(textarea.value).toBe(content);
});
it("calls onChange when text is modified", () => {
const onChange = vi.fn();
render(<FileEditor content="" onChange={onChange} />);
const textarea = screen.getByRole("textbox");
fireEvent.change(textarea, { target: { value: "new content" } });
expect(onChange).toHaveBeenCalledWith("new content");
});
it("respects readOnly prop", () => {
render(<FileEditor content="readonly content" onChange={vi.fn()} readOnly />);
const textarea = screen.getByRole("textbox") as HTMLTextAreaElement;
expect(textarea.readOnly).toBe(true);
});
it("has correct aria-label based on filePath prop", () => {
const filePath = "src/components/App.tsx";
render(<FileEditor content="" onChange={vi.fn()} filePath={filePath} />);
const textarea = screen.getByRole("textbox");
expect(textarea.getAttribute("aria-label")).toBe(`Editor for ${filePath}`);
});
it("has default aria-label when filePath is not provided", () => {
render(<FileEditor content="" onChange={vi.fn()} />);
const textarea = screen.getByRole("textbox");
expect(textarea.getAttribute("aria-label")).toBe("File editor");
});
it("has spellCheck disabled", () => {
render(<FileEditor content="" onChange={vi.fn()} />);
const textarea = screen.getByRole("textbox");
expect(textarea.getAttribute("spellcheck")).toBe("false");
});
it("is not readOnly by default", () => {
render(<FileEditor content="" onChange={vi.fn()} />);
const textarea = screen.getByRole("textbox") as HTMLTextAreaElement;
expect(textarea.readOnly).toBe(false);
});
});

View File

@@ -5634,6 +5634,7 @@ html .column.drag-over * {
width: 100%;
min-width: 100%;
height: 100%;
box-sizing: border-box;
background: var(--bg);
color: var(--text);
font-family: var(--font-mono);
@@ -5648,6 +5649,11 @@ html .column.drag-over * {
tab-size: 2;
}
.file-editor-textarea:focus {
outline: 1px solid var(--border);
outline-offset: -1px;
}
.file-editor-textarea::selection {
background: var(--todo);
color: var(--bg);
@@ -5815,6 +5821,7 @@ html .column.drag-over * {
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
.file-browser {
@@ -5963,14 +5970,7 @@ html .column.drag-over * {
flex: 1;
overflow: hidden;
background: var(--bg);
}
.file-editor-container {
height: 100%;
}
.file-editor-container .cm-editor {
height: 100%;
min-width: 0;
}
.file-browser-footer {