@surrealdb/ui 1.2.11 → 1.2.13
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 +21 -19
- package/README.md +15 -10
- package/dist/assets/245b1ccaa4927818.svg +3 -0
- package/dist/assets/333e1fe6e67ca4a2.svg +10 -0
- package/dist/assets/4b0e10c655814886.svg +21 -0
- package/dist/assets/9932da008ea5dd32.svg +3 -0
- package/dist/assets/a372168d5789daa2.svg +4 -0
- package/dist/assets/b140e44ee4ae7e35.svg +5 -0
- package/dist/assets/e028075817d928e4.svg +3 -0
- package/dist/assets/ee1065ad5a8f7f14.svg +6 -0
- package/dist/ui.css +1 -1
- package/dist/ui.d.ts +579 -1
- package/dist/ui.js +11553 -8988
- package/dist/ui.js.map +1 -1
- package/doc/code-style.md +264 -0
- package/doc/stacker-plan.md +219 -0
- package/package.json +119 -115
- 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
package/package.json
CHANGED
|
@@ -1,117 +1,121 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
2
|
+
"name": "@surrealdb/ui",
|
|
3
|
+
"version": "1.2.13",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/ui.js"
|
|
10
|
+
},
|
|
11
|
+
"./icons": {
|
|
12
|
+
"types": "./dist/icons.d.ts",
|
|
13
|
+
"import": "./dist/icons.js"
|
|
14
|
+
},
|
|
15
|
+
"./styles.css": {
|
|
16
|
+
"import": "./dist/ui.css"
|
|
17
|
+
},
|
|
18
|
+
"./fonts.css": {
|
|
19
|
+
"import": "./dist/fonts.css"
|
|
20
|
+
},
|
|
21
|
+
"./mixins": {
|
|
22
|
+
"import": "./res/_mixins.scss"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "vite build",
|
|
27
|
+
"storybook:dev": "storybook dev -p 6006 --ci",
|
|
28
|
+
"storybook:build": "storybook build",
|
|
29
|
+
"qc": "oxlint && biome lint --only=correctness/useExhaustiveDependencies . && oxfmt --check .",
|
|
30
|
+
"qa": "oxlint --fix && biome lint --only=correctness/useExhaustiveDependencies --write . && oxfmt .",
|
|
31
|
+
"qau": "oxlint --fix --fix-suggestions && biome lint --only=correctness/useExhaustiveDependencies --write --unsafe . && oxfmt .",
|
|
32
|
+
"qts": "tsc --noEmit",
|
|
33
|
+
"qtsw": "tsc --noEmit --watch",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:unit": "vitest run --project=unit",
|
|
36
|
+
"test:e2e": "vitest run --project=e2e",
|
|
37
|
+
"test:storybook": "vitest run --project=storybook",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"icons": "bun run tools/icon-parser.ts"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
43
|
+
"@codemirror/legacy-modes": "^6.4.2",
|
|
44
|
+
"@fontsource-variable/geist": "^5.2.9",
|
|
45
|
+
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
|
46
|
+
"@lezer/common": "^1.2.1",
|
|
47
|
+
"@lezer/go": "^1.0.0",
|
|
48
|
+
"@lezer/highlight": "^1.2.1",
|
|
49
|
+
"@lezer/html": "^1.3.10",
|
|
50
|
+
"@lezer/javascript": "^1.4.17",
|
|
51
|
+
"@lezer/json": "^1.0.3",
|
|
52
|
+
"@lezer/markdown": "^1.6.3",
|
|
53
|
+
"@lezer/php": "^1.0.2",
|
|
54
|
+
"@lezer/python": "^1.1.14",
|
|
55
|
+
"@lezer/rust": "^1.0.2",
|
|
56
|
+
"@lezer/yaml": "^1.0.3",
|
|
57
|
+
"@replit/codemirror-indentation-markers": "^6.5.3",
|
|
58
|
+
"@surrealdb/lezer": "1.0.0-beta.23",
|
|
59
|
+
"acorn": "^8.16.0",
|
|
60
|
+
"github-slugger": "^2.0.0",
|
|
61
|
+
"react-reverse-portal": "^2.3.0",
|
|
62
|
+
"vite-tsconfig-paths": "^6.0.5"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@biomejs/biome": "2.5.3",
|
|
66
|
+
"@chromatic-com/storybook": "^4.1.3",
|
|
67
|
+
"@laynezh/vite-plugin-lib-assets": "^2.1.3",
|
|
68
|
+
"@storybook/addon-a11y": "^10.1.11",
|
|
69
|
+
"@storybook/addon-docs": "^10.1.11",
|
|
70
|
+
"@storybook/addon-onboarding": "^10.1.11",
|
|
71
|
+
"@storybook/addon-vitest": "^10.1.11",
|
|
72
|
+
"@storybook/react-vite": "^10.1.11",
|
|
73
|
+
"@tanstack/react-virtual": "^3.13.12",
|
|
74
|
+
"@surrealdb/codemirror": "^1.0.3",
|
|
75
|
+
"@types/bun": "^1.3.6",
|
|
76
|
+
"@types/js-yaml": "^4.0.9",
|
|
77
|
+
"@types/jsdom": "^27.0.0",
|
|
78
|
+
"@types/react": "^19.2.2",
|
|
79
|
+
"@types/react-dom": "^19.2.3",
|
|
80
|
+
"@vitejs/plugin-react": "^5.0.2",
|
|
81
|
+
"@vitest/browser-playwright": "^4.0.16",
|
|
82
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
83
|
+
"jsdom": "^28.0.0",
|
|
84
|
+
"mermaid": "^11.14.0",
|
|
85
|
+
"playwright": "^1.57.0",
|
|
86
|
+
"sass-embedded": "^1.92.1",
|
|
87
|
+
"oxfmt": "0.58.0",
|
|
88
|
+
"oxlint": "1.73.0",
|
|
89
|
+
"slate": "^0.120.0",
|
|
90
|
+
"slate-dom": "^0.119.0",
|
|
91
|
+
"slate-react": "^0.120.0",
|
|
92
|
+
"storybook": "^10.1.11",
|
|
93
|
+
"svgo": "^4.0.0",
|
|
94
|
+
"typescript": "^5",
|
|
95
|
+
"vite": "^7.1.5",
|
|
96
|
+
"vite-plugin-dts": "^4.5.4",
|
|
97
|
+
"vitest": "^4.0.16"
|
|
98
|
+
},
|
|
99
|
+
"peerDependencies": {
|
|
100
|
+
"@codemirror/autocomplete": "^6.13.0",
|
|
101
|
+
"@codemirror/commands": "^6.3.3",
|
|
102
|
+
"@codemirror/language": "^6.10.1",
|
|
103
|
+
"@codemirror/legacy-modes": "^6.4.2",
|
|
104
|
+
"@codemirror/lint": "^6.5.0",
|
|
105
|
+
"@codemirror/search": "^6.5.6",
|
|
106
|
+
"@codemirror/state": "^6.4.1",
|
|
107
|
+
"@codemirror/view": "^6.24.1",
|
|
108
|
+
"@mantine/core": "^9.2.2",
|
|
109
|
+
"@mantine/hooks": "^9.2.2",
|
|
110
|
+
"mermaid": "^11",
|
|
111
|
+
"react": "^19",
|
|
112
|
+
"slate": "^0.120.0",
|
|
113
|
+
"slate-dom": "^0.119.0",
|
|
114
|
+
"slate-react": "^0.120.0"
|
|
115
|
+
},
|
|
116
|
+
"peerDependenciesMeta": {
|
|
117
|
+
"mermaid": {
|
|
118
|
+
"optional": true
|
|
119
|
+
}
|
|
120
|
+
}
|
|
117
121
|
}
|
package/res/_mixins.scss
CHANGED
|
@@ -71,17 +71,21 @@
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
@mixin edge-shadow-x($begin: 0px, $end: 0px) {
|
|
74
|
-
mask: linear-gradient(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
mask: linear-gradient(
|
|
75
|
+
to right,
|
|
76
|
+
transparent,
|
|
77
|
+
black $begin,
|
|
78
|
+
black calc(100% - $end),
|
|
79
|
+
transparent
|
|
80
|
+
);
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
@mixin edge-shadow-y($begin: 0px, $end: 0px) {
|
|
82
|
-
mask: linear-gradient(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
mask: linear-gradient(
|
|
85
|
+
to bottom,
|
|
86
|
+
transparent,
|
|
87
|
+
black $begin,
|
|
88
|
+
black calc(100% - $end),
|
|
89
|
+
transparent
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -5,17 +5,17 @@ import { afterEach } from "vitest";
|
|
|
5
5
|
import { page, userEvent } from "vitest/browser";
|
|
6
6
|
|
|
7
7
|
interface MountedStory {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
container: HTMLElement;
|
|
9
|
+
unmount: () => void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const activeMounts = new Set<MountedStory>();
|
|
13
13
|
|
|
14
14
|
afterEach(() => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
for (const mount of activeMounts) {
|
|
16
|
+
mount.unmount();
|
|
17
|
+
}
|
|
18
|
+
activeMounts.clear();
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -24,30 +24,30 @@ afterEach(() => {
|
|
|
24
24
|
* each test automatically; tests can still call `unmount()` early.
|
|
25
25
|
*/
|
|
26
26
|
export function mountStory(node: ReactElement | ComponentType): MountedStory {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
27
|
+
const container = document.createElement("div");
|
|
28
|
+
container.dataset.testRoot = "true";
|
|
29
|
+
document.body.appendChild(container);
|
|
30
|
+
|
|
31
|
+
let root: Root | null = createRoot(container);
|
|
32
|
+
const element = isReactElement(node) ? node : createElement(node);
|
|
33
|
+
root.render(element);
|
|
34
|
+
|
|
35
|
+
const mount: MountedStory = {
|
|
36
|
+
container,
|
|
37
|
+
unmount: () => {
|
|
38
|
+
if (!root) return;
|
|
39
|
+
root.unmount();
|
|
40
|
+
root = null;
|
|
41
|
+
container.remove();
|
|
42
|
+
activeMounts.delete(mount);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
activeMounts.add(mount);
|
|
46
|
+
return mount;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function isReactElement(node: ReactElement | ComponentType): node is ReactElement {
|
|
50
|
-
|
|
50
|
+
return typeof node !== "function" && typeof node === "object";
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -56,27 +56,27 @@ function isReactElement(node: ReactElement | ComponentType): node is ReactElemen
|
|
|
56
56
|
* authoritative state (selection, doc, mode) directly inside assertions.
|
|
57
57
|
*/
|
|
58
58
|
export async function getEditorView(timeoutMs = 5000): Promise<EditorView> {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
while (Date.now() - start < timeoutMs) {
|
|
61
|
+
const dom = document.querySelector<HTMLElement>(".cm-editor");
|
|
62
|
+
if (dom) {
|
|
63
|
+
const view = EditorView.findFromDOM(dom);
|
|
64
|
+
if (view) return view;
|
|
65
|
+
}
|
|
66
|
+
await sleep(20);
|
|
67
|
+
}
|
|
68
|
+
throw new Error("Timed out waiting for the markdown EditorView to mount");
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/** Returns the `.cm-content` editable element once it's in the DOM. */
|
|
72
72
|
export async function getContentElement(timeoutMs = 5000): Promise<HTMLElement> {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
const start = Date.now();
|
|
74
|
+
while (Date.now() - start < timeoutMs) {
|
|
75
|
+
const dom = document.querySelector<HTMLElement>(".cm-content");
|
|
76
|
+
if (dom) return dom;
|
|
77
|
+
await sleep(20);
|
|
78
|
+
}
|
|
79
|
+
throw new Error("Timed out waiting for .cm-content");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
@@ -86,21 +86,21 @@ export async function getContentElement(timeoutMs = 5000): Promise<HTMLElement>
|
|
|
86
86
|
* `view.dispatch({ selection: ... })`.
|
|
87
87
|
*/
|
|
88
88
|
export async function focusEditor(): Promise<EditorView> {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
const view = await getEditorView();
|
|
90
|
+
view.focus();
|
|
91
|
+
await getContentElement();
|
|
92
|
+
return view;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/** Type at the currently focused element. Caller should call `focusEditor()` first. */
|
|
96
96
|
export async function typeText(text: string): Promise<void> {
|
|
97
|
-
|
|
97
|
+
await userEvent.keyboard(text);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/** Convenience: focus the editor and type at the current caret. */
|
|
101
101
|
export async function focusAndType(text: string): Promise<void> {
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
await focusEditor();
|
|
103
|
+
await userEvent.keyboard(text);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
@@ -108,62 +108,62 @@ export async function focusAndType(text: string): Promise<void> {
|
|
|
108
108
|
* and DOM assertions so CodeMirror's measure cycle has time to run.
|
|
109
109
|
*/
|
|
110
110
|
export function nextFrame(): Promise<void> {
|
|
111
|
-
|
|
111
|
+
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/** Sleep helper for the polling loops above. */
|
|
115
115
|
export function sleep(ms: number): Promise<void> {
|
|
116
|
-
|
|
116
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
120
|
* Read the current markdown source out of the live EditorView.
|
|
121
121
|
*/
|
|
122
122
|
export async function readDoc(): Promise<string> {
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
const view = await getEditorView();
|
|
124
|
+
return view.state.doc.toString();
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/** Wait until the editor reports `data-markdown-mode="preview"`. */
|
|
128
128
|
export async function ensurePreviewMode(timeoutMs = 5000): Promise<void> {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
129
|
+
const start = Date.now();
|
|
130
|
+
while (Date.now() - start < timeoutMs) {
|
|
131
|
+
const mode = document.querySelector(".cm-editor")?.getAttribute("data-markdown-mode");
|
|
132
|
+
if (mode === "preview") return;
|
|
133
|
+
await nextFrame();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const preview = page.getByText("Preview", { exact: true });
|
|
137
|
+
|
|
138
|
+
if (preview.elements().length > 0) {
|
|
139
|
+
await preview.click();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const retryStart = Date.now();
|
|
143
|
+
while (Date.now() - retryStart < timeoutMs) {
|
|
144
|
+
const mode = document.querySelector(".cm-editor")?.getAttribute("data-markdown-mode");
|
|
145
|
+
if (mode === "preview") return;
|
|
146
|
+
await nextFrame();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
throw new Error("Timed out waiting for preview mode");
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/** Mount helper: editor view + preview mode + focus. */
|
|
153
153
|
export async function preparePreviewEditor(): Promise<EditorView> {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
const view = await getEditorView();
|
|
155
|
+
await ensurePreviewMode();
|
|
156
|
+
view.focus();
|
|
157
|
+
await nextFrame();
|
|
158
|
+
return view;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
/** Scroll the editor so `pos` is centred in the viewport. */
|
|
162
162
|
export async function scrollEditorTo(view: EditorView, pos: number): Promise<void> {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
view.dispatch({
|
|
164
|
+
effects: EditorView.scrollIntoView(pos, { y: "center" }),
|
|
165
|
+
});
|
|
166
|
+
await nextFrame();
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
export { page, userEvent };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
|
|
2
2
|
import { setProjectAnnotations } from "@storybook/react-vite";
|
|
3
|
+
|
|
3
4
|
import * as projectAnnotations from "../../.storybook/preview";
|
|
4
5
|
|
|
5
6
|
// Calling `setProjectAnnotations` here ensures any `composeStories(...)` invocation
|
|
@@ -19,14 +20,14 @@ setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
|
|
|
19
20
|
// real navigation, so neutralise the browser's default navigation while leaving
|
|
20
21
|
// the app's own click handlers (which place the caret) untouched.
|
|
21
22
|
if (typeof window !== "undefined" && !(window as { __e2eNavGuard?: boolean }).__e2eNavGuard) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
(window as { __e2eNavGuard?: boolean }).__e2eNavGuard = true;
|
|
24
|
+
window.addEventListener(
|
|
25
|
+
"click",
|
|
26
|
+
(event) => {
|
|
27
|
+
const anchor = (event.target as HTMLElement | null)?.closest?.("a[href]");
|
|
28
|
+
if (anchor) event.preventDefault();
|
|
29
|
+
},
|
|
30
|
+
true,
|
|
31
|
+
);
|
|
32
|
+
window.addEventListener("submit", (event) => event.preventDefault(), true);
|
|
32
33
|
}
|