@truedat/core 8.11.3 → 8.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/core",
|
|
3
|
-
"version": "8.11.
|
|
3
|
+
"version": "8.11.5",
|
|
4
4
|
"description": "Truedat Web Core",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css",
|
|
@@ -53,50 +53,50 @@
|
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@testing-library/dom": "^10.4.
|
|
57
|
-
"@testing-library/jest-dom": "^6.6.
|
|
58
|
-
"@testing-library/react": "^16.3.
|
|
59
|
-
"@testing-library/user-event": "^14.6.
|
|
60
|
-
"@truedat/test": "8.11.
|
|
56
|
+
"@testing-library/dom": "^10.4.0",
|
|
57
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
58
|
+
"@testing-library/react": "^16.3.0",
|
|
59
|
+
"@testing-library/user-event": "^14.6.1",
|
|
60
|
+
"@truedat/test": "8.11.4",
|
|
61
61
|
"identity-obj-proxy": "^3.0.0",
|
|
62
62
|
"jest": "^29.7.0",
|
|
63
63
|
"redux-saga-test-plan": "^4.0.6"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@apollo/client": "^3.13.
|
|
67
|
-
"@tiptap/core": "
|
|
68
|
-
"@tiptap/extension-heading": "
|
|
69
|
-
"@tiptap/extension-link": "
|
|
70
|
-
"@tiptap/pm": "
|
|
71
|
-
"@tiptap/react": "
|
|
72
|
-
"@tiptap/starter-kit": "
|
|
66
|
+
"@apollo/client": "^3.13.8",
|
|
67
|
+
"@tiptap/core": "3.31.3",
|
|
68
|
+
"@tiptap/extension-heading": "3.31.3",
|
|
69
|
+
"@tiptap/extension-link": "3.31.3",
|
|
70
|
+
"@tiptap/pm": "3.31.3",
|
|
71
|
+
"@tiptap/react": "3.31.3",
|
|
72
|
+
"@tiptap/starter-kit": "3.31.3",
|
|
73
73
|
"@xyflow/react": "^12.6.4",
|
|
74
|
-
"axios": "^1.15.
|
|
75
|
-
"dompurify": "^3.4.
|
|
76
|
-
"elkjs": "^0.10.
|
|
74
|
+
"axios": "^1.15.0",
|
|
75
|
+
"dompurify": "^3.4.11",
|
|
76
|
+
"elkjs": "^0.10.0",
|
|
77
77
|
"graphql": "^16.11.0",
|
|
78
|
-
"lodash": "^4.17.
|
|
79
|
-
"marked": "^17.0.
|
|
78
|
+
"lodash": "^4.17.21",
|
|
79
|
+
"marked": "^17.0.3",
|
|
80
80
|
"moment": "^2.30.1",
|
|
81
81
|
"path-to-regexp": "^8.2.0",
|
|
82
82
|
"prop-types": "^15.8.1",
|
|
83
83
|
"query-string": "^7.1.3",
|
|
84
|
-
"react": "^19.2.
|
|
84
|
+
"react": "^19.2.7",
|
|
85
85
|
"react-csv": "^2.2.2",
|
|
86
|
-
"react-dom": "^19.2.
|
|
86
|
+
"react-dom": "^19.2.7",
|
|
87
87
|
"react-dropzone": "^14.3.8",
|
|
88
88
|
"react-hook-form": "^7.56.4",
|
|
89
|
-
"react-intl": "^7.1.
|
|
90
|
-
"react-moment": "^1.1.
|
|
89
|
+
"react-intl": "^7.1.11",
|
|
90
|
+
"react-moment": "^1.1.3",
|
|
91
91
|
"react-redux": "^9.2.0",
|
|
92
|
-
"react-router": "^8.3.
|
|
92
|
+
"react-router": "^8.3.0",
|
|
93
93
|
"redux": "^5.0.1",
|
|
94
94
|
"redux-saga": "^1.3.0",
|
|
95
95
|
"redux-saga-routines": "^3.2.3",
|
|
96
96
|
"reselect": "^5.1.1",
|
|
97
97
|
"semantic-ui-react": "^3.0.0-beta.2",
|
|
98
|
-
"swr": "^2.3.
|
|
99
|
-
"turndown": "^7.2.
|
|
98
|
+
"swr": "^2.3.3",
|
|
99
|
+
"turndown": "^7.2.2"
|
|
100
100
|
},
|
|
101
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "d7c5a489590a23c1b5722dafbd2e2d5902303ed9"
|
|
102
102
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRef, useEffect, useState, useCallback } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
|
-
import { useEditor, EditorContent } from "@tiptap/react";
|
|
4
|
+
import { useEditor, useEditorState, EditorContent } from "@tiptap/react";
|
|
5
5
|
import StarterKit from "@tiptap/starter-kit";
|
|
6
6
|
import Link from "@tiptap/extension-link";
|
|
7
7
|
import Heading from "@tiptap/extension-heading";
|
|
@@ -151,7 +151,7 @@ export function MarkdownEditor({
|
|
|
151
151
|
const { formatMessage } = useIntl();
|
|
152
152
|
const [linkModalOpen, setLinkModalOpen] = useState(false);
|
|
153
153
|
const [linkUrl, setLinkUrl] = useState("");
|
|
154
|
-
const [
|
|
154
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
155
155
|
const lastEmittedMarkdownRef = useRef(value);
|
|
156
156
|
const editorRef = useRef(null);
|
|
157
157
|
const initialContent = markdownToHtml(value);
|
|
@@ -161,6 +161,8 @@ export function MarkdownEditor({
|
|
|
161
161
|
content: initialContent,
|
|
162
162
|
editable: !disabled,
|
|
163
163
|
immediatelyRender: false,
|
|
164
|
+
onFocus: () => setIsFocused(true),
|
|
165
|
+
onBlur: () => setIsFocused(false),
|
|
164
166
|
editorProps: {
|
|
165
167
|
handlePaste(view, event) {
|
|
166
168
|
const editorInstance = editorRef.current;
|
|
@@ -216,18 +218,23 @@ export function MarkdownEditor({
|
|
|
216
218
|
editor.setEditable(!disabled);
|
|
217
219
|
}, [disabled, editor]);
|
|
218
220
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
221
|
+
const toolbarState = useEditorState({
|
|
222
|
+
editor,
|
|
223
|
+
selector: ({ editor: ed }) => {
|
|
224
|
+
if (!ed) return null;
|
|
225
|
+
return {
|
|
226
|
+
bold: ed.isActive("bold"),
|
|
227
|
+
italic: ed.isActive("italic"),
|
|
228
|
+
underline: ed.isActive("underline"),
|
|
229
|
+
heading2: ed.isActive("heading", { level: 2 }),
|
|
230
|
+
heading3: ed.isActive("heading", { level: 3 }),
|
|
231
|
+
bulletList: ed.isActive("bulletList"),
|
|
232
|
+
orderedList: ed.isActive("orderedList"),
|
|
233
|
+
link: ed.isActive("link"),
|
|
234
|
+
hasSelection: !ed.state.selection.empty,
|
|
235
|
+
};
|
|
236
|
+
},
|
|
237
|
+
});
|
|
231
238
|
|
|
232
239
|
const openLinkModal = useCallback(
|
|
233
240
|
(url = null) => {
|
|
@@ -279,7 +286,7 @@ export function MarkdownEditor({
|
|
|
279
286
|
[editor],
|
|
280
287
|
);
|
|
281
288
|
|
|
282
|
-
if (!editor) {
|
|
289
|
+
if (!editor || !toolbarState) {
|
|
283
290
|
return null;
|
|
284
291
|
}
|
|
285
292
|
|
|
@@ -292,7 +299,7 @@ export function MarkdownEditor({
|
|
|
292
299
|
) : null}
|
|
293
300
|
<Menu size="small" attached="top" className="markdown-editor-toolbar">
|
|
294
301
|
<Menu.Item
|
|
295
|
-
active={
|
|
302
|
+
active={isFocused && toolbarState.bold}
|
|
296
303
|
onMouseDown={(e) => {
|
|
297
304
|
e.preventDefault();
|
|
298
305
|
editor.chain().focus().toggleBold().run();
|
|
@@ -301,7 +308,7 @@ export function MarkdownEditor({
|
|
|
301
308
|
<Icon name="bold" />
|
|
302
309
|
</Menu.Item>
|
|
303
310
|
<Menu.Item
|
|
304
|
-
active={
|
|
311
|
+
active={isFocused && toolbarState.italic}
|
|
305
312
|
onMouseDown={(e) => {
|
|
306
313
|
e.preventDefault();
|
|
307
314
|
editor.chain().focus().toggleItalic().run();
|
|
@@ -310,7 +317,7 @@ export function MarkdownEditor({
|
|
|
310
317
|
<Icon name="italic" />
|
|
311
318
|
</Menu.Item>
|
|
312
319
|
<Menu.Item
|
|
313
|
-
active={
|
|
320
|
+
active={isFocused && toolbarState.underline}
|
|
314
321
|
onMouseDown={(e) => {
|
|
315
322
|
e.preventDefault();
|
|
316
323
|
editor.chain().focus().toggleUnderline().run();
|
|
@@ -319,6 +326,7 @@ export function MarkdownEditor({
|
|
|
319
326
|
<Icon name="underline" />
|
|
320
327
|
</Menu.Item>
|
|
321
328
|
<Menu.Item
|
|
329
|
+
active={isFocused && toolbarState.heading2}
|
|
322
330
|
onMouseDown={(e) => {
|
|
323
331
|
e.preventDefault();
|
|
324
332
|
editor.chain().focus().toggleHeading({ level: 2 }).run();
|
|
@@ -327,6 +335,7 @@ export function MarkdownEditor({
|
|
|
327
335
|
<Icon name="heading" size="large" />
|
|
328
336
|
</Menu.Item>
|
|
329
337
|
<Menu.Item
|
|
338
|
+
active={isFocused && toolbarState.heading3}
|
|
330
339
|
onMouseDown={(e) => {
|
|
331
340
|
e.preventDefault();
|
|
332
341
|
editor.chain().focus().toggleHeading({ level: 3 }).run();
|
|
@@ -335,7 +344,7 @@ export function MarkdownEditor({
|
|
|
335
344
|
<Icon name="heading" size="small" />
|
|
336
345
|
</Menu.Item>
|
|
337
346
|
<Menu.Item
|
|
338
|
-
active={
|
|
347
|
+
active={isFocused && toolbarState.bulletList}
|
|
339
348
|
onMouseDown={(e) => {
|
|
340
349
|
e.preventDefault();
|
|
341
350
|
editor.chain().focus().toggleBulletList().run();
|
|
@@ -344,7 +353,7 @@ export function MarkdownEditor({
|
|
|
344
353
|
<Icon name="list ul" />
|
|
345
354
|
</Menu.Item>
|
|
346
355
|
<Menu.Item
|
|
347
|
-
active={
|
|
356
|
+
active={isFocused && toolbarState.orderedList}
|
|
348
357
|
onMouseDown={(e) => {
|
|
349
358
|
e.preventDefault();
|
|
350
359
|
editor.chain().focus().toggleOrderedList().run();
|
|
@@ -353,11 +362,11 @@ export function MarkdownEditor({
|
|
|
353
362
|
<Icon name="list ol" />
|
|
354
363
|
</Menu.Item>
|
|
355
364
|
<Menu.Item
|
|
356
|
-
active={
|
|
357
|
-
disabled={!hasSelection && !
|
|
365
|
+
active={isFocused && toolbarState.link}
|
|
366
|
+
disabled={!toolbarState.hasSelection && !toolbarState.link}
|
|
358
367
|
onMouseDown={(e) => {
|
|
359
368
|
e.preventDefault();
|
|
360
|
-
if (hasSelection ||
|
|
369
|
+
if (toolbarState.hasSelection || toolbarState.link) {
|
|
361
370
|
openLinkModal();
|
|
362
371
|
}
|
|
363
372
|
}}
|
|
@@ -374,7 +383,7 @@ export function MarkdownEditor({
|
|
|
374
383
|
onAccept={handleLinkAccept}
|
|
375
384
|
validate={validateLinkUrl}
|
|
376
385
|
initialValue={linkUrl}
|
|
377
|
-
allowEmpty={
|
|
386
|
+
allowEmpty={toolbarState.link}
|
|
378
387
|
header={
|
|
379
388
|
<>
|
|
380
389
|
<Icon name="linkify" />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { waitFor } from "@testing-library/react";
|
|
1
|
+
import { act, fireEvent, waitFor } from "@testing-library/react";
|
|
2
2
|
import { render } from "@truedat/test/render";
|
|
3
3
|
import {
|
|
4
4
|
MarkdownReader,
|
|
@@ -165,6 +165,60 @@ describe("<MarkdownReader />", () => {
|
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
describe("<MarkdownEditor />", () => {
|
|
168
|
+
it.each([
|
|
169
|
+
["bold", 0],
|
|
170
|
+
["italic", 1],
|
|
171
|
+
["underline", 2],
|
|
172
|
+
["heading 2", 3],
|
|
173
|
+
["heading 3", 4],
|
|
174
|
+
["bullet list", 5],
|
|
175
|
+
["ordered list", 6],
|
|
176
|
+
])("updates %s highlighting without typing", async (_, index) => {
|
|
177
|
+
const { container } = render(<MarkdownEditor value="Hello" />);
|
|
178
|
+
await waitFor(() =>
|
|
179
|
+
expect(container.querySelector(".tiptap")).toBeInTheDocument(),
|
|
180
|
+
);
|
|
181
|
+
const button = container.querySelectorAll(".markdown-editor-toolbar .item")[
|
|
182
|
+
index
|
|
183
|
+
];
|
|
184
|
+
expect(button).not.toHaveClass("active");
|
|
185
|
+
fireEvent.focus(container.querySelector(".tiptap"));
|
|
186
|
+
fireEvent.mouseDown(button);
|
|
187
|
+
expect(button).toHaveClass("active");
|
|
188
|
+
fireEvent.mouseDown(button);
|
|
189
|
+
expect(button).not.toHaveClass("active");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("updates highlighting when the cursor moves between formatted and plain text", async () => {
|
|
193
|
+
const { container } = render(<MarkdownEditor value="**Bold** plain" />);
|
|
194
|
+
await waitFor(() =>
|
|
195
|
+
expect(container.querySelector(".tiptap")).toBeInTheDocument(),
|
|
196
|
+
);
|
|
197
|
+
const editor = container.querySelector(".tiptap").editor;
|
|
198
|
+
const bold = container.querySelector(".markdown-editor-toolbar .item");
|
|
199
|
+
fireEvent.focus(container.querySelector(".tiptap"));
|
|
200
|
+
act(() => editor.commands.setTextSelection(2));
|
|
201
|
+
expect(bold).toHaveClass("active");
|
|
202
|
+
act(() => editor.commands.setTextSelection(8));
|
|
203
|
+
expect(bold).not.toHaveClass("active");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("only highlights initial formatting while the editor is focused", async () => {
|
|
207
|
+
const { container } = render(<MarkdownEditor value="**Bold** text" />);
|
|
208
|
+
await waitFor(() =>
|
|
209
|
+
expect(container.querySelector(".tiptap")).toBeInTheDocument(),
|
|
210
|
+
);
|
|
211
|
+
const editable = container.querySelector(".tiptap");
|
|
212
|
+
const bold = container.querySelector(".markdown-editor-toolbar .item");
|
|
213
|
+
expect(bold).not.toHaveClass("active");
|
|
214
|
+
fireEvent.focus(editable);
|
|
215
|
+
expect(bold).toHaveClass("active");
|
|
216
|
+
fireEvent.blur(editable);
|
|
217
|
+
expect(bold).not.toHaveClass("active");
|
|
218
|
+
fireEvent.focus(editable);
|
|
219
|
+
expect(bold).toHaveClass("active");
|
|
220
|
+
});
|
|
221
|
+
|
|
168
222
|
it("matches the latest snapshot when editor is ready", async () => {
|
|
169
223
|
const rendered = render(<MarkdownEditor />);
|
|
170
224
|
await waitFor(() => {
|