@surrealdb/ui 1.2.12 → 1.2.14
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/.claude/launch.json +17 -0
- package/.oxfmtrc.json +27 -0
- package/.oxlintrc.json +23 -0
- package/.zed/settings.json +34 -34
- package/AGENTS.md +25 -19
- package/CLAUDE.md +111 -0
- package/README.md +15 -10
- package/dist/icons.d.ts +4 -0
- package/dist/icons.js +91 -89
- package/dist/icons.js.map +1 -1
- package/dist/ui.css +1 -1
- package/dist/ui.d.ts +259 -1
- package/dist/ui.js +9807 -12265
- package/dist/ui.js.map +1 -1
- package/doc/code-style.md +264 -0
- package/doc/release.md +102 -0
- package/doc/stacker-plan.md +219 -0
- package/package.json +120 -116
- package/release-notes.md +0 -0
- package/res/_mixins.scss +15 -11
- package/tests/_setup/e2e-helpers.tsx +85 -85
- package/tests/_setup/portable-stories.ts +11 -10
- package/tests/e2e/MarkdownEditor/content-blocks.test.tsx +140 -139
- package/tests/e2e/MarkdownEditor/edits.test.tsx +101 -100
- package/tests/e2e/MarkdownEditor/heading-fold.test.tsx +35 -34
- package/tests/e2e/MarkdownEditor/hybrid-widgets.test.tsx +178 -177
- package/tests/e2e/MarkdownEditor/image-on-image.test.tsx +50 -49
- package/tests/e2e/MarkdownEditor/jsx-block-content.test.tsx +195 -194
- package/tests/e2e/MarkdownEditor/jsx-highlight.test.tsx +47 -46
- package/tests/e2e/MarkdownEditor/jsx-inline-badges.test.tsx +35 -34
- package/tests/e2e/MarkdownEditor/jsx-inline-click.test.tsx +39 -38
- package/tests/e2e/MarkdownEditor/jsx-selection.test.tsx +28 -27
- package/tests/e2e/MarkdownEditor/link-placeholder.test.tsx +49 -48
- package/tests/e2e/MarkdownEditor/media-align.test.tsx +40 -39
- package/tests/e2e/MarkdownEditor/media-edit.test.tsx +49 -48
- package/tests/e2e/MarkdownEditor/media-sizing.test.tsx +108 -107
- package/tests/e2e/MarkdownEditor/modes.test.tsx +79 -78
- package/tests/e2e/MarkdownEditor/onimage-parity.test.tsx +29 -28
- package/tests/e2e/MarkdownEditor/regressions.test.tsx +194 -193
- package/tests/e2e/MarkdownEditor/slash-commands.test.tsx +81 -80
- package/tests/e2e/MarkdownEditor/table-click.test.tsx +36 -35
- package/tests/e2e/MarkdownEditor/table-controls.test.tsx +44 -43
- package/tests/e2e/MarkdownEditor/table-format.test.tsx +28 -27
- package/tests/e2e/MarkdownEditor/undo-redo.test.tsx +22 -21
- package/tests/e2e/MarkdownViewer/parity.test.tsx +144 -143
- package/tests/e2e/MarkdownViewer/render.test.tsx +20 -19
- package/tests/e2e/Stacker/stacker.test.tsx +304 -0
- package/tests/unit/Editor/helpers.test.ts +26 -26
- package/tests/unit/MarkdownConformance/error-isolation.test.tsx +26 -25
- package/tests/unit/MarkdownConformance/indentation.test.tsx +20 -19
- package/tests/unit/MarkdownConformance/inline-code-fuzz.test.tsx +255 -0
- package/tests/unit/MarkdownConformance/jsx-attributes.test.tsx +39 -38
- package/tests/unit/MarkdownConformance/jsx-fuzz.test.tsx +122 -122
- package/tests/unit/MarkdownConformance/links.test.tsx +78 -54
- package/tests/unit/MarkdownConformance/render-helpers.tsx +60 -60
- package/tests/unit/MarkdownEditor/code-info.test.ts +58 -58
- package/tests/unit/MarkdownEditor/decorations.test.ts +423 -421
- package/tests/unit/MarkdownEditor/editor-ready.test.ts +22 -22
- package/tests/unit/MarkdownEditor/html-descriptors.test.ts +70 -70
- package/tests/unit/MarkdownEditor/indented-fence.test.ts +80 -80
- package/tests/unit/MarkdownEditor/jsx-attr-scan.test.ts +87 -87
- package/tests/unit/MarkdownEditor/jsx-block-widget.test.ts +51 -51
- package/tests/unit/MarkdownEditor/jsx-tag-grammar.test.ts +63 -63
- package/tests/unit/MarkdownEditor/list-indent.test.ts +67 -67
- package/tests/unit/MarkdownEditor/slash-commands.test.ts +150 -150
- package/tests/unit/MarkdownEditor/table-format.test.ts +67 -67
- package/tests/unit/MarkdownEditor/table.test.ts +96 -96
- package/tests/unit/MarkdownEditor/triggers.test.ts +167 -167
- package/tests/unit/MarkdownEditor/widget-store.test.ts +90 -90
- package/tests/unit/MarkdownViewer/callout.test.tsx +42 -42
- package/tests/unit/MarkdownViewer/code-highlight.test.tsx +41 -41
- package/tests/unit/MarkdownViewer/code-title.test.tsx +45 -45
- package/tests/unit/MarkdownViewer/features.test.tsx +129 -129
- package/tests/unit/MarkdownViewer/headings.test.tsx +28 -28
- package/tests/unit/MarkdownViewer/indented-jsx-children.test.tsx +53 -53
- package/tests/unit/MarkdownViewer/jsx-block-nesting.test.tsx +137 -137
- package/tests/unit/MarkdownViewer/jsx.test.tsx +188 -188
- package/tests/unit/MarkdownViewer/list-bullets.test.tsx +31 -31
- package/tests/unit/MarkdownViewer/list-code.test.tsx +45 -45
- package/tests/unit/MarkdownViewer/preserve-newlines.test.tsx +109 -109
- package/tests/unit/MarkdownViewer/read-time.test.ts +13 -13
- package/tests/unit/MarkdownViewer/renderers.test.tsx +75 -75
- package/tests/unit/MarkdownViewer/runnable.test.tsx +51 -51
- package/tests/unit/MarkdownViewer/ssr.test.tsx +56 -55
- package/tests/unit/MarkdownViewer/syntax-highlighting.test.tsx +43 -42
- package/tests/unit/MarkdownViewer/tabs-fenced-code.test.tsx +120 -120
- package/tests/unit/MarkdownViewer/tabs.test.tsx +83 -82
- package/tests/unit/Stacker/drag.test.ts +21 -0
- package/tests/unit/Stacker/helpers.test.ts +105 -0
- package/tests/unit/Stacker/reducer.test.ts +107 -0
- package/tools/duplicate-vector-mappings.json +141 -141
- package/tools/glass-export-overrides.json +6 -6
- package/tools/icon-parser.ts +41 -40
- package/tools/integration-gradient-exports.json +55 -55
- package/tools/picto-compare.ts +32 -32
- package/tools/picto-merge-urls.ts +19 -19
- package/tools/picto-sync.ts +543 -543
- package/tools/picto-verify-gradients.ts +50 -50
- package/tools/vector-export-nodes.json +975 -975
|
@@ -2,191 +2,192 @@ import { EditorSelection } from "@codemirror/state";
|
|
|
2
2
|
import * as MarkdownStories from "@src/stories/playground/Markdown.stories";
|
|
3
3
|
import { composeStories } from "@storybook/react-vite";
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
5
|
+
|
|
5
6
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
mountStory,
|
|
8
|
+
nextFrame,
|
|
9
|
+
preparePreviewEditor,
|
|
10
|
+
scrollEditorTo,
|
|
11
|
+
userEvent,
|
|
11
12
|
} from "../../_setup/e2e-helpers";
|
|
12
13
|
import { md } from "../../_setup/markdown-classes";
|
|
13
14
|
|
|
14
15
|
const Stories = composeStories(MarkdownStories);
|
|
15
16
|
|
|
16
17
|
function findLineStart(doc: string, prefix: string): number {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const idx = doc.indexOf(prefix);
|
|
19
|
+
if (idx < 0) throw new Error(`prefix not found in doc: ${prefix}`);
|
|
20
|
+
return idx;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
describe("MarkdownEditor / preview widgets", () => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
24
|
+
it("reveals heading marks when the caret enters the heading line and hides them when it leaves", async () => {
|
|
25
|
+
mountStory(<Stories.Editor />);
|
|
26
|
+
const view = await preparePreviewEditor();
|
|
27
|
+
|
|
28
|
+
// Caret far away from the H1 → marks should be hidden.
|
|
29
|
+
view.dispatch({ selection: EditorSelection.cursor(view.state.doc.length) });
|
|
30
|
+
await nextFrame();
|
|
31
|
+
expect(document.querySelectorAll(`.${md.markHidden}`).length).toBeGreaterThan(0);
|
|
32
|
+
|
|
33
|
+
// Move caret onto the H1 line → marks revealed for that block.
|
|
34
|
+
const headingStart = findLineStart(view.state.doc.toString(), "# Welcome");
|
|
35
|
+
await scrollEditorTo(view, headingStart);
|
|
36
|
+
view.dispatch({ selection: EditorSelection.cursor(headingStart + 4) });
|
|
37
|
+
await nextFrame();
|
|
38
|
+
expect(document.querySelectorAll(`.${md.markShown}`).length).toBeGreaterThan(0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("keeps list bullets consistent after hopping the caret across items several times", async () => {
|
|
42
|
+
mountStory(<Stories.Editor />);
|
|
43
|
+
const view = await preparePreviewEditor();
|
|
44
|
+
const docStr = view.state.doc.toString();
|
|
45
|
+
|
|
46
|
+
const items = ["- First item", "- Second item", "- Third item"];
|
|
47
|
+
const positions = items.map((line) => findLineStart(docStr, line) + 2);
|
|
48
|
+
const listPos = findLineStart(docStr, "- First item");
|
|
49
|
+
|
|
50
|
+
// Park the caret away from the list so all three bullets render as widgets.
|
|
51
|
+
view.dispatch({ selection: EditorSelection.cursor(0) });
|
|
52
|
+
await scrollEditorTo(view, listPos);
|
|
53
|
+
await nextFrame();
|
|
54
|
+
|
|
55
|
+
let baselineBullets = 0;
|
|
56
|
+
for (let i = 0; i < 40; i++) {
|
|
57
|
+
baselineBullets = document.querySelectorAll(`.${md.listBullet}`).length;
|
|
58
|
+
if (baselineBullets >= 3) break;
|
|
59
|
+
await nextFrame();
|
|
60
|
+
}
|
|
61
|
+
expect(baselineBullets).toBeGreaterThanOrEqual(3);
|
|
62
|
+
|
|
63
|
+
// Hop between list items several times. With prefix-scoped hybrid reveal,
|
|
64
|
+
// the bullet widgets stay visible while the caret is in the item text;
|
|
65
|
+
// the pre-fix regression was: bullets stuck behind or disappeared once you
|
|
66
|
+
// moved the caret around a few times.
|
|
67
|
+
for (let pass = 0; pass < 4; pass++) {
|
|
68
|
+
for (let i = 0; i < positions.length; i++) {
|
|
69
|
+
view.dispatch({ selection: EditorSelection.cursor(positions[i] ?? 0) });
|
|
70
|
+
await nextFrame();
|
|
71
|
+
|
|
72
|
+
const bullets = document.querySelectorAll(`.${md.listBullet}`).length;
|
|
73
|
+
expect(bullets).toBe(baselineBullets);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Caret on the list mark (the `-`) drops that item's bullet widget only.
|
|
78
|
+
const dashPos = findLineStart(docStr, "- First item");
|
|
79
|
+
view.dispatch({ selection: EditorSelection.cursor(dashPos) });
|
|
80
|
+
await nextFrame();
|
|
81
|
+
expect(document.querySelectorAll(`.${md.listBullet}`).length).toBe(baselineBullets - 1);
|
|
82
|
+
|
|
83
|
+
// After leaving the list entirely all bullets must be back.
|
|
84
|
+
view.dispatch({ selection: EditorSelection.cursor(0) });
|
|
85
|
+
await nextFrame();
|
|
86
|
+
expect(document.querySelectorAll(`.${md.listBullet}`).length).toBe(baselineBullets);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("shows the right callout title when moving focus between consecutive callouts", async () => {
|
|
90
|
+
mountStory(<Stories.Editor />);
|
|
91
|
+
const view = await preparePreviewEditor();
|
|
92
|
+
const docStr = view.state.doc.toString();
|
|
93
|
+
|
|
94
|
+
const noteBody = findLineStart(docStr, "> Callouts use the GFM");
|
|
95
|
+
const warningBody = findLineStart(docStr, "> Legacy");
|
|
96
|
+
const tipBody = findLineStart(docStr, "> All systems go");
|
|
97
|
+
const calloutPos = findLineStart(docStr, "> [!note]");
|
|
98
|
+
|
|
99
|
+
// Park caret outside any callout first so all three headers render.
|
|
100
|
+
view.dispatch({ selection: EditorSelection.cursor(0) });
|
|
101
|
+
await scrollEditorTo(view, calloutPos);
|
|
102
|
+
await nextFrame();
|
|
103
|
+
|
|
104
|
+
const allHeaders = () =>
|
|
105
|
+
Array.from(document.querySelectorAll<HTMLElement>(`.${md.calloutHeader}`));
|
|
106
|
+
const headerForKind = (kind: string) =>
|
|
107
|
+
allHeaders().find((el) => el.getAttribute("data-callout-kind") === kind) ?? null;
|
|
108
|
+
|
|
109
|
+
let initial = allHeaders();
|
|
110
|
+
for (let i = 0; i < 40 && initial.length < 3; i++) {
|
|
111
|
+
await nextFrame();
|
|
112
|
+
initial = allHeaders();
|
|
113
|
+
}
|
|
114
|
+
expect(initial.length).toBe(3);
|
|
115
|
+
expect(headerForKind("note")?.textContent).toContain("Heads up");
|
|
116
|
+
expect(headerForKind("warning")?.textContent).toContain("Be careful");
|
|
117
|
+
expect(headerForKind("tip")?.textContent).toContain("Tip");
|
|
118
|
+
|
|
119
|
+
// `[!tip]` has no title, falls back to the visual label.
|
|
120
|
+
|
|
121
|
+
// Hop between the three callouts repeatedly. After each hop the
|
|
122
|
+
// header for the active callout disappears (caret on its block); the
|
|
123
|
+
// other two keep their correct titles. Bug regression: titles would
|
|
124
|
+
// swap or vanish.
|
|
125
|
+
const positions = [
|
|
126
|
+
{ pos: noteBody + 2, hidden: "note" as const },
|
|
127
|
+
{ pos: warningBody + 2, hidden: "warning" as const },
|
|
128
|
+
{ pos: tipBody + 2, hidden: "tip" as const },
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
for (let pass = 0; pass < 4; pass++) {
|
|
132
|
+
for (const { pos, hidden } of positions) {
|
|
133
|
+
view.dispatch({ selection: EditorSelection.cursor(pos) });
|
|
134
|
+
await nextFrame();
|
|
135
|
+
|
|
136
|
+
const visible = allHeaders();
|
|
137
|
+
expect(visible.length).toBe(2);
|
|
138
|
+
expect(headerForKind(hidden)).toBeNull();
|
|
139
|
+
|
|
140
|
+
if (hidden !== "note") {
|
|
141
|
+
expect(headerForKind("note")?.textContent).toContain("Heads up");
|
|
142
|
+
}
|
|
143
|
+
if (hidden !== "warning") {
|
|
144
|
+
expect(headerForKind("warning")?.textContent).toContain("Be careful");
|
|
145
|
+
}
|
|
146
|
+
if (hidden !== "tip") {
|
|
147
|
+
expect(headerForKind("tip")?.textContent).toContain("Tip");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("clicking a callout header focuses the editor with the caret in that callout", async () => {
|
|
154
|
+
mountStory(<Stories.Editor />);
|
|
155
|
+
const view = await preparePreviewEditor();
|
|
156
|
+
const docStr = view.state.doc.toString();
|
|
157
|
+
const calloutPos = findLineStart(docStr, "> [!warning]");
|
|
158
|
+
|
|
159
|
+
// Caret far away so all callout headers render as widgets.
|
|
160
|
+
view.dispatch({ selection: EditorSelection.cursor(0) });
|
|
161
|
+
await scrollEditorTo(view, calloutPos);
|
|
162
|
+
view.contentDOM.blur();
|
|
163
|
+
await nextFrame();
|
|
164
|
+
|
|
165
|
+
let warningHeader: HTMLElement | undefined;
|
|
166
|
+
for (let i = 0; i < 40; i++) {
|
|
167
|
+
warningHeader = Array.from(
|
|
168
|
+
document.querySelectorAll<HTMLElement>(`.${md.calloutHeader}`),
|
|
169
|
+
).find((el) => el.getAttribute("data-callout-kind") === "warning");
|
|
170
|
+
if (warningHeader) break;
|
|
171
|
+
await nextFrame();
|
|
172
|
+
}
|
|
173
|
+
expect(warningHeader).toBeDefined();
|
|
174
|
+
if (!warningHeader) throw new Error("warning callout header not rendered");
|
|
175
|
+
|
|
176
|
+
// Use a real pointer gesture so CodeMirror's input layer sees it
|
|
177
|
+
// (synthetic MouseEvent dispatch does not always reach CM6's
|
|
178
|
+
// contentDOM listeners through the widget's atomic range).
|
|
179
|
+
await userEvent.click(warningHeader);
|
|
180
|
+
await nextFrame();
|
|
181
|
+
await nextFrame();
|
|
182
|
+
|
|
183
|
+
const warningHeaderRange = {
|
|
184
|
+
from: docStr.indexOf("> [!warning]"),
|
|
185
|
+
to: docStr.indexOf("Be careful") + "Be careful".length,
|
|
186
|
+
};
|
|
187
|
+
expect(warningHeaderRange.from).toBeGreaterThan(-1);
|
|
188
|
+
|
|
189
|
+
const sel = view.state.selection.main;
|
|
190
|
+
expect(sel.from).toBeGreaterThanOrEqual(warningHeaderRange.from);
|
|
191
|
+
expect(sel.to).toBeLessThanOrEqual(warningHeaderRange.to + 1);
|
|
192
|
+
});
|
|
192
193
|
});
|
|
@@ -3,70 +3,71 @@ import type { MediaDescriptor } from "@src/lib/markdown";
|
|
|
3
3
|
import { MarkdownEditor } from "@src/primitives/MarkdownEditor";
|
|
4
4
|
import { MANTINE_THEME } from "@src/theme/mantine";
|
|
5
5
|
import { describe, expect, it } from "vitest";
|
|
6
|
+
|
|
6
7
|
import { getEditorView, mountStory, nextFrame } from "../../_setup/e2e-helpers";
|
|
7
8
|
|
|
8
9
|
function toCdn(node: MediaDescriptor): MediaDescriptor {
|
|
9
|
-
|
|
10
|
+
return { ...node, src: `https://cdn.surrealdb.com/${node.src}.auto` };
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
function toCdnVideo(node: MediaDescriptor): MediaDescriptor {
|
|
13
|
-
|
|
14
|
+
return { ...node, src: `https://cdn.surrealdb.com/${node.src}.mp4` };
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
function mount(doc: string, onResolveMedia: (node: MediaDescriptor) => MediaDescriptor): void {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
mountStory(
|
|
19
|
+
<MantineProvider
|
|
20
|
+
theme={MANTINE_THEME}
|
|
21
|
+
cssVariablesResolver={v8CssVariablesResolver}
|
|
22
|
+
forceColorScheme="dark"
|
|
23
|
+
>
|
|
24
|
+
<Paper
|
|
25
|
+
p="md"
|
|
26
|
+
withBorder
|
|
27
|
+
>
|
|
28
|
+
<MarkdownEditor
|
|
29
|
+
mode="preview"
|
|
30
|
+
document={doc}
|
|
31
|
+
onResolveMedia={onResolveMedia}
|
|
32
|
+
autoFocus={false}
|
|
33
|
+
mih={200}
|
|
34
|
+
/>
|
|
35
|
+
</Paper>
|
|
36
|
+
</MantineProvider>,
|
|
37
|
+
);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
describe("MarkdownEditor / onImage resolution", () => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
it("resolves image widget sources through onResolveMedia (CDN code → URL)", async () => {
|
|
42
|
+
// Leading paragraph keeps the caret off the image so the widget renders.
|
|
43
|
+
mount("Intro\n\n", toCdn);
|
|
44
|
+
await getEditorView();
|
|
45
|
+
await nextFrame();
|
|
46
|
+
await nextFrame();
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
let img: HTMLImageElement | null = null;
|
|
49
|
+
for (let i = 0; i < 40 && !img; i++) {
|
|
50
|
+
img = document.querySelector<HTMLImageElement>('img[src*="cdn.surrealdb.com"]');
|
|
51
|
+
if (!img) await nextFrame();
|
|
52
|
+
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
expect(img).not.toBeNull();
|
|
55
|
+
expect(img?.getAttribute("src")).toBe("https://cdn.surrealdb.com/cdn-code-123.auto");
|
|
56
|
+
});
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
it("renders a video widget when onResolveMedia resolves the source to a video", async () => {
|
|
59
|
+
mount("Intro\n\n", toCdnVideo);
|
|
60
|
+
await getEditorView();
|
|
61
|
+
await nextFrame();
|
|
62
|
+
await nextFrame();
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
let video: HTMLVideoElement | null = null;
|
|
65
|
+
for (let i = 0; i < 40 && !video; i++) {
|
|
66
|
+
video = document.querySelector<HTMLVideoElement>("video");
|
|
67
|
+
if (!video) await nextFrame();
|
|
68
|
+
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
expect(video).not.toBeNull();
|
|
71
|
+
expect(document.body.innerHTML).toContain("cdn.surrealdb.com/cdn-video-123.mp4");
|
|
72
|
+
});
|
|
72
73
|
});
|