Skip to content

Commit ee3ca4c

Browse files
Highlighting tests (#38)
* add tests for highlighting functionality * add test for wrongly ordered page nums * test error boundry
1 parent 2811a11 commit ee3ca4c

File tree

13 files changed

+383
-181
lines changed

13 files changed

+383
-181
lines changed

examples/vite_basic/src/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ const App = () => {
1818
handleFileChange,
1919
} = useFileLoader(AVAILABLE_FILES[0]);
2020

21+
console.log("test page ", testPage);
22+
23+
if (testPage) {
24+
testPage.children = [undefined];
25+
}
26+
2127
// Get backrefs for the currently selected file
2228
const currentBackrefs = selectedFile?.backrefs || [];
2329

typescript/src/renderer/JsonDocRenderer.tsx

Lines changed: 12 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
import "./styles/index.css";
2-
import React, { useEffect } from "react";
2+
import React from "react";
33

44
import { Page } from "@/models/generated";
5-
import { loadPage } from "@/serialization/loader";
65

76
import { BlockRenderer } from "./components/BlockRenderer";
8-
import { PageDelimiter } from "./components/PageDelimiter";
9-
import { JsonViewPanel } from "./components/dev/JsonViewPanel";
10-
import { RendererProvider } from "./context/RendererContext";
11-
import { HighlightNavigation } from "./components/HighlightNavigation";
12-
import { useHighlights } from "./hooks/useHighlights";
7+
import { RendererContainer } from "./components/RendererContainer";
138
import { Backref } from "./utils/highlightUtils";
149
import { GlobalErrorBoundary } from "./components/ErrorBoundary";
1510

@@ -40,101 +35,22 @@ export const JsonDocRenderer = ({
4035
backrefs = [],
4136
onError,
4237
}: JsonDocRendererProps) => {
43-
// Use the modular hooks for highlight management
44-
const { highlightCount, currentActiveIndex, navigateToHighlight } =
45-
useHighlights({
46-
backrefs,
47-
});
48-
49-
useEffect(() => {
50-
try {
51-
//TODO: this is not throwing for invalid page object (one that doesn't follow schema)
52-
loadPage(page);
53-
} catch (_) {
54-
// console.log("error ", error);
55-
}
56-
}, [page]);
57-
58-
// return null;
59-
const renderedContent = (
60-
<div className="json-doc-page">
61-
{/* Page icon */}
62-
{page.icon && (
63-
<div className="json-doc-page-icon">
64-
{page.icon.type === "emoji" && page.icon.emoji}
65-
</div>
66-
)}
67-
{/* Page title */}
68-
{page.properties?.title && (
69-
<h1 className="json-doc-page-title" data-page-id={page.id}>
70-
{page.properties.title.title?.[0]?.plain_text || "Untitled"}
71-
</h1>
72-
)}
73-
{/* Page children blocks */}
74-
{page.children && page.children.length > 0 && (
75-
<div className="json-doc-page-content">
76-
{page.children.map((block: any, index: number) => {
77-
const currentPageNum = block.metadata?.origin?.page_num;
78-
const nextPageNum =
79-
index < page.children.length - 1
80-
? (page.children[index + 1]?.metadata as any)?.origin?.page_num
81-
: null;
82-
83-
// Show delimiter after the last block of each page
84-
const showPageDelimiter =
85-
currentPageNum &&
86-
(nextPageNum !== currentPageNum ||
87-
index === page.children.length - 1);
88-
89-
return (
90-
<React.Fragment key={block.id || index}>
91-
<BlockRenderer
92-
block={block}
93-
depth={0}
94-
components={components}
95-
/>
96-
97-
{showPageDelimiter && !components?.page_delimiter && (
98-
<PageDelimiter pageNumber={currentPageNum} />
99-
)}
100-
{showPageDelimiter && components?.page_delimiter && (
101-
<components.page_delimiter pageNumber={currentPageNum} />
102-
)}
103-
</React.Fragment>
104-
);
105-
})}
106-
</div>
107-
)}
108-
</div>
109-
);
110-
38+
console.log("theme: ", theme);
11139
return (
11240
<div
11341
className={`jsondoc-theme-${theme}`}
11442
data-testid="jsondoc-renderer-root"
11543
>
11644
<GlobalErrorBoundary onError={onError}>
117-
<RendererProvider value={{ devMode, resolveImageUrl }}>
118-
<div
119-
className={`json-doc-renderer${className ? " " + className : ""}`}
120-
>
121-
{viewJson ? (
122-
<div className="flex h-screen">
123-
<JsonViewPanel data={page} />
124-
</div>
125-
) : (
126-
renderedContent
127-
)}
128-
{/* Show highlight navigation when there are highlights */}
129-
{highlightCount > 0 && (
130-
<HighlightNavigation
131-
highlightCount={highlightCount}
132-
onNavigate={navigateToHighlight}
133-
currentIndex={currentActiveIndex}
134-
/>
135-
)}
136-
</div>
137-
</RendererProvider>
45+
<RendererContainer
46+
page={page}
47+
className={className}
48+
components={components}
49+
devMode={devMode}
50+
resolveImageUrl={resolveImageUrl}
51+
viewJson={viewJson}
52+
backrefs={backrefs}
53+
/>
13854
</GlobalErrorBoundary>
13955
</div>
14056
);

typescript/src/renderer/components/ErrorBoundary.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface GlobalErrorBoundaryProps {
99
function GlobalErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
1010
console.log("error ", error);
1111
return (
12-
<div className="json-doc-error-boundary">
12+
<div className="json-doc-error-boundary" role="alert">
1313
<div className="json-doc-error-content">
1414
<h2>Document Failed to Load</h2>
1515
<p>Something went wrong while rendering this document.</p>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import React, { useEffect } from "react";
2+
3+
import { Page } from "@/models/generated";
4+
import { loadPage } from "@/serialization/loader";
5+
6+
import { RendererProvider } from "../context/RendererContext";
7+
import { useHighlights } from "../hooks/useHighlights";
8+
import { Backref } from "../utils/highlightUtils";
9+
10+
import { HighlightNavigation } from "./HighlightNavigation";
11+
import { JsonViewPanel } from "./dev/JsonViewPanel";
12+
import { BlockRenderer } from "./BlockRenderer";
13+
import { PageDelimiter } from "./PageDelimiter";
14+
15+
interface RendererContainerProps {
16+
page: Page;
17+
className?: string;
18+
components?: React.ComponentProps<typeof BlockRenderer>["components"] & {
19+
page_delimiter: React.ComponentType<{
20+
pageNumber: number;
21+
}>;
22+
};
23+
devMode?: boolean;
24+
resolveImageUrl?: (url: string) => Promise<string>;
25+
viewJson?: boolean;
26+
backrefs?: Backref[];
27+
}
28+
29+
export const RendererContainer: React.FC<RendererContainerProps> = ({
30+
page,
31+
className = "",
32+
components,
33+
devMode = false,
34+
resolveImageUrl,
35+
viewJson = false,
36+
backrefs = [],
37+
}) => {
38+
// Use the modular hooks for highlight management
39+
const { highlightCount, currentActiveIndex, navigateToHighlight } =
40+
useHighlights({
41+
backrefs,
42+
});
43+
44+
useEffect(() => {
45+
try {
46+
//TODO: this is not throwing for invalid page object (one that doesn't follow schema)
47+
loadPage(page);
48+
} catch (_) {
49+
// console.log("error ", error);
50+
}
51+
}, [page]);
52+
53+
const renderedContent = (
54+
<div className="json-doc-page">
55+
{/* Page icon */}
56+
{page.icon && (
57+
<div className="json-doc-page-icon">
58+
{page.icon.type === "emoji" && page.icon.emoji}
59+
</div>
60+
)}
61+
{/* Page title */}
62+
{page.properties?.title && (
63+
<h1
64+
className="json-doc-page-title"
65+
data-page-id={page.id}
66+
role="heading"
67+
>
68+
{page.properties.title.title?.[0]?.plain_text || "Untitled"}
69+
</h1>
70+
)}
71+
{/* Page children blocks */}
72+
{page.children && page.children.length > 0 && (
73+
<div className="json-doc-page-content">
74+
{page.children.map((block: any, index: number) => {
75+
const currentPageNum = block.metadata?.origin?.page_num;
76+
const nextPageNum =
77+
index < page.children.length - 1
78+
? (page.children[index + 1]?.metadata as any)?.origin?.page_num
79+
: null;
80+
81+
// Show delimiter after the last block of each page
82+
const showPageDelimiter =
83+
currentPageNum &&
84+
(nextPageNum !== currentPageNum ||
85+
index === page.children.length - 1);
86+
87+
return (
88+
<React.Fragment key={block.id || index}>
89+
<BlockRenderer
90+
block={block}
91+
depth={0}
92+
components={components}
93+
/>
94+
95+
{showPageDelimiter && !components?.page_delimiter && (
96+
<PageDelimiter pageNumber={currentPageNum} />
97+
)}
98+
{showPageDelimiter && components?.page_delimiter && (
99+
<components.page_delimiter pageNumber={currentPageNum} />
100+
)}
101+
</React.Fragment>
102+
);
103+
})}
104+
</div>
105+
)}
106+
</div>
107+
);
108+
109+
return (
110+
<RendererProvider value={{ devMode, resolveImageUrl }}>
111+
<div className={`json-doc-renderer${className ? " " + className : ""}`}>
112+
{viewJson ? (
113+
<div className="flex h-screen">
114+
<JsonViewPanel data={page} />
115+
</div>
116+
) : (
117+
renderedContent
118+
)}
119+
{/* Show highlight navigation when there are highlights */}
120+
{highlightCount > 0 && (
121+
<HighlightNavigation
122+
highlightCount={highlightCount}
123+
onNavigate={navigateToHighlight}
124+
currentIndex={currentActiveIndex}
125+
/>
126+
)}
127+
</div>
128+
</RendererProvider>
129+
);
130+
};

typescript/src/renderer/utils/highlightUtils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export function createHighlightSpan(text: string): HTMLSpanElement {
1717
const highlightSpan = document.createElement("span");
1818
highlightSpan.className = "json-doc-highlight";
1919
highlightSpan.textContent = text;
20+
highlightSpan.role = "note";
2021
return highlightSpan;
2122
}
2223

0 commit comments

Comments
 (0)