Skip to content

Commit eed95fb

Browse files
committed
feat(knowledge): 笔记阅读视图标题栏挂到体系入口(触点②接入)
1 parent 9c1d5fe commit eed95fb

4 files changed

Lines changed: 81 additions & 2 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// @vitest-environment jsdom
2+
/**
3+
* NoteReadingView.test.tsx — 标题栏扩展插槽 headerExtra(v0.13.7 触点②)。
4+
*
5+
* @ai-context: 覆盖标题栏插槽契约——headerExtra 渲染于「编辑」与「删除」之间,
6+
* 且未传时不渲染(回归:既有标题栏按钮布局不受影响)。
7+
*/
8+
import { afterEach, describe, expect, it, vi } from "vitest";
9+
import { cleanup, render, screen } from "@testing-library/react";
10+
import type { Note } from "../types";
11+
import NoteReadingView from "./NoteReadingView";
12+
13+
const baseNote = (): Note => ({
14+
id: 1,
15+
title: "标题",
16+
content: "# 正文\n内容段落",
17+
source: "session",
18+
session_id: 42,
19+
rule_version: null,
20+
purify_stats: null,
21+
tags: "[]",
22+
properties: null,
23+
pin: 0,
24+
group_id: null,
25+
created_at: 1,
26+
updated_at: 2,
27+
});
28+
29+
const noop = vi.fn();
30+
31+
const renderView = (headerExtra?: React.ReactNode) =>
32+
render(
33+
<NoteReadingView
34+
note={baseNote()}
35+
editing={false}
36+
headerExtra={headerExtra}
37+
onEdit={noop}
38+
onPinToggle={noop}
39+
onDelete={noop}
40+
onTagClick={noop}
41+
onOpenSession={noop}
42+
onTaskToggle={noop}
43+
onImageOpen={noop}
44+
/>,
45+
);
46+
47+
afterEach(() => cleanup());
48+
49+
describe("NoteReadingView 标题栏扩展插槽", () => {
50+
it("headerExtra 渲染于「删除」按钮之前", () => {
51+
renderView(<span data-testid="header-extra">🧭 挂到体系</span>);
52+
const extra = screen.getByTestId("header-extra");
53+
expect(extra).toBeTruthy();
54+
// 位置契约:extra 在编辑按钮之后、删除按钮之前
55+
const editBtn = screen.getByRole("button", { name: /编辑/ });
56+
const delBtn = screen.getByRole("button", { name: "删除" });
57+
const posOf = (a: Element, b: Element) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING;
58+
expect(posOf(editBtn, extra)).toBeTruthy();
59+
expect(posOf(extra, delBtn)).toBeTruthy();
60+
});
61+
62+
it("未传 headerExtra 时标题栏正常渲染(回归)", () => {
63+
renderView(undefined);
64+
expect(screen.getByRole("button", { name: /编辑/ })).toBeTruthy();
65+
expect(screen.getByRole("button", { name: "删除" })).toBeTruthy();
66+
// 固定按钮内容为 📌(title=「固定」是可访问名称兜底)
67+
expect(screen.getByRole("button", { name: "📌" })).toBeTruthy();
68+
});
69+
});

‎app/src/components/NoteReadingView.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ interface Props {
2222
editor?: ReactNode;
2323
/** H3:版本时间线/知识补充等辅助面板插槽(阅读态显示) */
2424
auxPanels?: ReactNode;
25+
/** 标题栏右侧扩展插槽(v0.13.7 触点② 挂到体系) */
26+
headerExtra?: ReactNode;
2527
onEdit: () => void;
2628
onPinToggle: () => void;
2729
onDelete: () => void;
@@ -32,7 +34,7 @@ interface Props {
3234
}
3335

3436
export default function NoteReadingView({
35-
note, editing, editor, auxPanels,
37+
note, editing, editor, auxPanels, headerExtra,
3638
onEdit, onPinToggle, onDelete, onTagClick, onOpenSession, onTaskToggle, onImageOpen,
3739
}: Props) {
3840
// A2:搜索高亮(M6:匹配集合=渲染产物只读查询,计数经此状态驱动)
@@ -161,6 +163,7 @@ export default function NoteReadingView({
161163
>
162164
✏ 编辑(Ctrl+E)
163165
</button>
166+
{headerExtra}
164167
<button
165168
onClick={onDelete}
166169
style={{ fontSize: 12, color: "#dc2626", cursor: "pointer", padding: "4px 10px", borderRadius: 4, border: "1px solid #fecaca", background: "#fff" }}

‎app/src/pages/NotesPage.test.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ describe("NotesPage 编辑完成即时刷新", () => {
5959
case "get_note":
6060
return store;
6161
default:
62-
// 子面板(EnrichPanel/VersionPanel 等)挂载期查询——空值兜底不抛错
62+
// 子面板(EnrichPanel/VersionPanel/NoteLinkToSystem 等)挂载期查询——空值兜底不抛错
6363
if (cmd === "note_versions_list") return [];
6464
if (cmd === "note_versions_usage") return [];
65+
if (cmd === "list_knowledge_systems") return [];
66+
if (cmd === "list_knowledge_links") return [];
6567
return null;
6668
}
6769
});

‎app/src/pages/NotesPage.tsx‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import NoteReadingView from "../components/NoteReadingView";
2525
import ImagePreviewOverlay from "../components/ImagePreviewOverlay";
2626
import VersionPanel from "../components/VersionPanel";
2727
import EnrichPanel from "../components/EnrichPanel";
28+
import NoteLinkToSystem from "../components/NoteLinkToSystem";
2829
import { useNoteAttention } from "../components/useNoteAttention";
2930

3031
interface Props {
@@ -354,6 +355,10 @@ export default function NotesPage({ focusNoteId, onOpenSessions, onOpenSystem }:
354355
/>
355356
}
356357
auxPanels={auxPanels}
358+
headerExtra={
359+
// v0.13.7 触点②:标题栏「挂到体系」入口;key=note.id 切笔记重置内部态
360+
<NoteLinkToSystem key={`link-${selected.id}`} noteId={selected.id} onChanged={() => void handleNoteChanged()} />
361+
}
357362
onEdit={() => setEditing(true)}
358363
onPinToggle={() => void runPinToggle(selected)}
359364
onDelete={() => void runDelete(selected.id)}

0 commit comments

Comments
 (0)