Skip to content

Commit 868a1bb

Browse files
committed
Bi-directional data-flow, set spans in Python
1 parent 0fb0df0 commit 868a1bb

File tree

7 files changed

+159
-89
lines changed

7 files changed

+159
-89
lines changed

examples/introduction.ipynb

Lines changed: 90 additions & 84 deletions
Large diffs are not rendered by default.

jupyterannotate/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
# Copyright (c) Stuart Quin.
55
# Distributed under the terms of the Modified BSD License.
66

7-
version_info = (0, 1, 1)
7+
version_info = (0, 1, 2)
88
__version__ = ".".join(map(str, version_info))

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "jupyterannotate",
3-
"version": "0.1.1",
4-
"description": "A Custom Jupyter Widget Library",
3+
"version": "0.1.2",
4+
"description": "A Jupyter Text Annotation Widget",
55
"keywords": [
66
"jupyter",
77
"jupyterlab",

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
setup_args = dict(
6666
name=name,
67-
description="A Custom Jupyter Widget Library",
67+
description="A Jupyter Text Annotation Widget",
6868
version=version,
6969
scripts=glob(pjoin("scripts", "*")),
7070
cmdclass=cmdclass,

src/__tests__/Annotate.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { faker } from "@faker-js/faker";
33

44
import { render, fireEvent, waitFor } from "@testing-library/preact";
55
import Annotate from "../components/Annotate";
6+
import { Span } from "../annotate";
67

78
const RANGE = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
89
const LABELS = RANGE.map(() => faker.random.word());
@@ -27,6 +28,7 @@ describe("Annotate", () => {
2728
labels: LABELS,
2829
initialSpans: [],
2930
onUpdateSpans: jest.fn(),
31+
registerSpanChangeCallback: jest.fn(),
3032
docs: DOCUMENTS,
3133
})
3234
);
@@ -47,6 +49,7 @@ describe("Annotate", () => {
4749
labels: LABELS,
4850
initialSpans: [],
4951
onUpdateSpans,
52+
registerSpanChangeCallback: jest.fn(),
5053
docs,
5154
})
5255
);
@@ -71,4 +74,41 @@ describe("Annotate", () => {
7174
const span = await findByTitle(LABELS[0]);
7275
expect(span.textContent).toEqual(targetText);
7376
});
77+
78+
test("should render spans when Python model changes", async () => {
79+
let spanChangeCallback = (spans: Span[][]) => {
80+
console.log(spans);
81+
};
82+
const onUpdateSpans = jest.fn();
83+
const docs = [faker.lorem.sentence(20)];
84+
const { findByTitle } = render(
85+
h(Annotate, {
86+
labels: LABELS,
87+
initialSpans: [],
88+
onUpdateSpans,
89+
registerSpanChangeCallback: (callback) => {
90+
spanChangeCallback = callback;
91+
},
92+
docs,
93+
})
94+
);
95+
96+
const targetText = docs[0].slice(8, 12);
97+
98+
await waitFor(() => expect(spanChangeCallback).not.toBeNull());
99+
100+
spanChangeCallback([
101+
[
102+
{
103+
start: 8,
104+
end: 12,
105+
text: targetText,
106+
label: { color: "red", text: LABELS[0] },
107+
},
108+
],
109+
]);
110+
111+
const span = await findByTitle(LABELS[0]);
112+
expect(span.textContent).toEqual(targetText);
113+
});
74114
});

src/components/Annotate.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { h, VNode } from "preact";
2-
import { useMemo, useState } from "preact/hooks";
2+
import { useEffect, useMemo, useState } from "preact/hooks";
33
import { ColorLabel, Span } from "../annotate";
44

55
import TopBar from "./TopBar";
@@ -11,12 +11,14 @@ interface Props {
1111
labels: string[];
1212
initialSpans: Span[][];
1313
onUpdateSpans: (span: Span[][]) => void;
14+
registerSpanChangeCallback: (callback: (span: Span[][]) => void) => void;
1415
}
1516

1617
export default function Annotate({
1718
docs,
1819
labels,
1920
initialSpans,
21+
registerSpanChangeCallback,
2022
onUpdateSpans,
2123
}: Props): VNode {
2224
const totalDocs = docs.length;
@@ -32,6 +34,12 @@ export default function Annotate({
3234
return docs[docIndex];
3335
}, [docIndex, docs]);
3436

37+
useEffect(() => {
38+
registerSpanChangeCallback((spans: Span[][]) => {
39+
setDocSpans(spans);
40+
});
41+
}, []);
42+
3543
const onChangeLabel = (label: ColorLabel) => {
3644
setSelectedLabel(label);
3745
};

src/widget.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DOMWidgetModel,
66
DOMWidgetView,
77
ISerializers,
8+
WidgetModel,
89
} from "@jupyter-widgets/base";
910

1011
import { h, render } from "preact";
@@ -35,11 +36,26 @@ export class AnnotateView extends DOMWidgetView {
3536
const labels = this.model.get("labels");
3637
const initialSpans = this.model.get("spans") || [];
3738

39+
const registerSpanChangeCallback = (
40+
callback: (spans: Span[][]) => void
41+
) => {
42+
this.model.on(
43+
"change:spans",
44+
(model: WidgetModel) => {
45+
if (callback) {
46+
callback(model.changed.spans);
47+
}
48+
},
49+
this
50+
);
51+
};
52+
3853
const app = h(
3954
"div",
4055
{ className: "app" },
4156
h(Annotate, {
4257
docs,
58+
registerSpanChangeCallback,
4359
labels,
4460
initialSpans,
4561
onUpdateSpans: (spans: Span[][]) => this.handleChange(spans),

0 commit comments

Comments
 (0)