testomatio-editor-blocks 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -127,13 +127,8 @@ setGlobalStepSuggestionsFetcher(async () => {
127
127
  return res.json();
128
128
  });
129
129
 
130
- // Image upload handler used by Step Data / Expected fields
131
- setGlobalStepImageUploadHandler(async (image: Blob) => {
132
- const formData = new FormData();
133
- formData.append("file", image);
134
- const res = await fetch("https://api.testomatio.com/v1/uploads", { method: "POST", body: formData });
135
- return res.json(); // must resolve to { url: "https://..." }
136
- });
130
+ // Image upload uses BlockNote's `uploadFile` handler you pass to `useCreateBlockNote`.
131
+ // No extra setup is required for step fields.
137
132
  ```
138
133
 
139
134
  Step suggestions accept either an array of `{ id, title, ... }` or the JSON:API shape:
@@ -6,7 +6,20 @@ export function setGlobalStepSuggestionsFetcher(fetcher) {
6
6
  cachedSuggestions = [];
7
7
  }
8
8
  export function useStepAutocomplete() {
9
- const [suggestions, setSuggestions] = useState(cachedSuggestions);
9
+ const [suggestions, setSuggestions] = useState(() => {
10
+ if (cachedSuggestions.length > 0) {
11
+ return cachedSuggestions;
12
+ }
13
+ if (globalFetcher) {
14
+ const result = globalFetcher();
15
+ if (!result || typeof result.then !== "function") {
16
+ const normalized = normalizeStepSuggestions(result);
17
+ cachedSuggestions = normalized;
18
+ return normalized;
19
+ }
20
+ }
21
+ return [];
22
+ });
10
23
  useEffect(() => {
11
24
  if (suggestions.length > 0) {
12
25
  return;
@@ -1,5 +1,5 @@
1
1
  export type StepImageUploadHandler = (image: Blob) => Promise<{
2
2
  url: string;
3
3
  }>;
4
- export declare function setGlobalStepImageUploadHandler(handler: StepImageUploadHandler | null): void;
4
+ export declare function setImageUploadHandler(handler: StepImageUploadHandler | null): void;
5
5
  export declare function useStepImageUpload(): StepImageUploadHandler | null;
@@ -1,12 +1,7 @@
1
- import { useEffect, useState } from "react";
2
- let globalUploadHandler = null;
3
- export function setGlobalStepImageUploadHandler(handler) {
4
- globalUploadHandler = handler;
1
+ let imageUploadHandler = null;
2
+ export function setImageUploadHandler(handler) {
3
+ imageUploadHandler = handler;
5
4
  }
6
5
  export function useStepImageUpload() {
7
- const [handler, setHandler] = useState(globalUploadHandler);
8
- useEffect(() => {
9
- setHandler(globalUploadHandler);
10
- }, []);
11
- return handler;
6
+ return imageUploadHandler;
12
7
  }
@@ -1,5 +1,5 @@
1
1
  export { customSchema, type CustomSchema, type CustomBlock, type CustomEditor, } from "./editor/customSchema";
2
2
  export { blocksToMarkdown, markdownToBlocks, type CustomEditorBlock, type CustomPartialBlock, } from "./editor/customMarkdownConverter";
3
3
  export { useStepAutocomplete, parseStepsFromJsonApi, setGlobalStepSuggestionsFetcher, type StepSuggestion, type StepJsonApiDocument, type StepJsonApiResource, } from "./editor/stepAutocomplete";
4
- export { useStepImageUpload, setGlobalStepImageUploadHandler, type StepImageUploadHandler, } from "./editor/stepImageUpload";
4
+ export { useStepImageUpload, setImageUploadHandler, type StepImageUploadHandler, } from "./editor/stepImageUpload";
5
5
  export declare const testomatioEditorClassName = "markdown testomatio-editor";
package/package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { customSchema, } from "./editor/customSchema";
2
2
  export { blocksToMarkdown, markdownToBlocks, } from "./editor/customMarkdownConverter";
3
3
  export { useStepAutocomplete, parseStepsFromJsonApi, setGlobalStepSuggestionsFetcher, } from "./editor/stepAutocomplete";
4
- export { useStepImageUpload, setGlobalStepImageUploadHandler, } from "./editor/stepImageUpload";
4
+ export { useStepImageUpload, setImageUploadHandler, } from "./editor/stepImageUpload";
5
5
  export const testomatioEditorClassName = "markdown testomatio-editor";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testomatio-editor-blocks",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Custom BlockNote schema, markdown conversion helpers, and UI for Testomatio-style test cases and steps.",
5
5
  "type": "module",
6
6
  "main": "./package/index.js",
package/src/App.tsx CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  } from "./editor/customMarkdownConverter";
20
20
  import { customSchema, type CustomEditor } from "./editor/customSchema";
21
21
  import { setGlobalStepSuggestionsFetcher, type StepJsonApiDocument } from "./editor/stepAutocomplete";
22
- import { setGlobalStepImageUploadHandler } from "./editor/stepImageUpload";
22
+ import { setImageUploadHandler } from "./editor/stepImageUpload";
23
23
  import "./App.css";
24
24
 
25
25
  const focusTestStepTitle = (editor: CustomEditor | null | undefined, blockId?: string) => {
@@ -348,13 +348,27 @@ function App() {
348
348
  useEffect(() => {
349
349
  // Demo defaults: configure global handlers so the editor works without manual providers.
350
350
  setGlobalStepSuggestionsFetcher(() => DEMO_STEP_FIXTURES);
351
- setGlobalStepImageUploadHandler(uploadStepImage);
351
+
352
+ const handler = editor?.uploadFile
353
+ ? async (file: Blob) => {
354
+ const result = await editor.uploadFile!(file as File);
355
+ if (typeof result === "string") {
356
+ return { url: result };
357
+ }
358
+ if (result && typeof result === "object" && "url" in result && typeof (result as any).url === "string") {
359
+ return { url: (result as any).url as string };
360
+ }
361
+ throw new Error("uploadFile did not return a URL");
362
+ }
363
+ : uploadStepImage;
364
+
365
+ setImageUploadHandler(handler);
352
366
 
353
367
  return () => {
354
368
  setGlobalStepSuggestionsFetcher(null);
355
- setGlobalStepImageUploadHandler(null);
369
+ setImageUploadHandler(null);
356
370
  };
357
- }, [uploadStepImage]);
371
+ }, [editor, uploadStepImage]);
358
372
 
359
373
  const createTestCaseBlock = useMemo<() => CustomPartialBlock>(() => {
360
374
  return () => ({
@@ -1,5 +1,8 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { parseStepsFromJsonApi } from "./stepAutocomplete";
3
+ import { renderToStaticMarkup } from "react-dom/server";
4
+ import React from "react";
5
+ import { setGlobalStepSuggestionsFetcher, useStepAutocomplete } from "./stepAutocomplete";
3
6
 
4
7
  describe("parseStepsFromJsonApi", () => {
5
8
  it("converts JSON:API resources into step suggestions", () => {
@@ -80,4 +83,21 @@ describe("parseStepsFromJsonApi", () => {
80
83
  "Click submit",
81
84
  ]);
82
85
  });
86
+
87
+ it("reads suggestions from the global fetcher via useStepAutocomplete", () => {
88
+ setGlobalStepSuggestionsFetcher(() => [{ id: "1", title: "Global step" }]);
89
+
90
+ let seen: any[] = [];
91
+ const Probe = () => {
92
+ seen = useStepAutocomplete();
93
+ return null;
94
+ };
95
+
96
+ renderToStaticMarkup(React.createElement(Probe));
97
+
98
+ expect(seen).toEqual([{ id: "1", title: "Global step" }]);
99
+
100
+ // reset for other tests
101
+ setGlobalStepSuggestionsFetcher(null);
102
+ });
83
103
  });
@@ -46,7 +46,20 @@ export function setGlobalStepSuggestionsFetcher(fetcher: StepSuggestionsFetcher
46
46
  }
47
47
 
48
48
  export function useStepAutocomplete(): StepSuggestion[] {
49
- const [suggestions, setSuggestions] = useState<StepSuggestion[]>(cachedSuggestions);
49
+ const [suggestions, setSuggestions] = useState<StepSuggestion[]>(() => {
50
+ if (cachedSuggestions.length > 0) {
51
+ return cachedSuggestions;
52
+ }
53
+ if (globalFetcher) {
54
+ const result = globalFetcher();
55
+ if (!result || typeof (result as Promise<unknown>).then !== "function") {
56
+ const normalized = normalizeStepSuggestions(result as StepInput);
57
+ cachedSuggestions = normalized;
58
+ return normalized;
59
+ }
60
+ }
61
+ return [];
62
+ });
50
63
 
51
64
  useEffect(() => {
52
65
  if (suggestions.length > 0) {
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { renderToStaticMarkup } from "react-dom/server";
3
+ import React from "react";
4
+ import { setImageUploadHandler, useStepImageUpload, type StepImageUploadHandler } from "./stepImageUpload";
5
+
6
+ describe("image upload handler hook", () => {
7
+ it("returns the configured upload handler", async () => {
8
+ const handler: StepImageUploadHandler = async () => ({ url: "https://example.com/image.png" });
9
+ setImageUploadHandler(handler);
10
+
11
+ let seen: any;
12
+ const Probe = () => {
13
+ seen = useStepImageUpload();
14
+ return null;
15
+ };
16
+
17
+ renderToStaticMarkup(React.createElement(Probe));
18
+
19
+ expect(seen).toBe(handler);
20
+ const result = await (seen as StepImageUploadHandler)(new Blob(["demo"], { type: "image/png" }));
21
+ expect(result).toEqual({ url: "https://example.com/image.png" });
22
+
23
+ setImageUploadHandler(null);
24
+ });
25
+ });
@@ -0,0 +1,11 @@
1
+ export type StepImageUploadHandler = (image: Blob) => Promise<{ url: string }>;
2
+
3
+ let imageUploadHandler: StepImageUploadHandler | null = null;
4
+
5
+ export function setImageUploadHandler(handler: StepImageUploadHandler | null) {
6
+ imageUploadHandler = handler;
7
+ }
8
+
9
+ export function useStepImageUpload(): StepImageUploadHandler | null {
10
+ return imageUploadHandler;
11
+ }
package/src/index.ts CHANGED
@@ -23,7 +23,7 @@ export {
23
23
 
24
24
  export {
25
25
  useStepImageUpload,
26
- setGlobalStepImageUploadHandler,
26
+ setImageUploadHandler,
27
27
  type StepImageUploadHandler,
28
28
  } from "./editor/stepImageUpload";
29
29
 
@@ -1,19 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export type StepImageUploadHandler = (image: Blob) => Promise<{ url: string }>;
4
-
5
- let globalUploadHandler: StepImageUploadHandler | null = null;
6
-
7
- export function setGlobalStepImageUploadHandler(handler: StepImageUploadHandler | null) {
8
- globalUploadHandler = handler;
9
- }
10
-
11
- export function useStepImageUpload(): StepImageUploadHandler | null {
12
- const [handler, setHandler] = useState<StepImageUploadHandler | null>(globalUploadHandler);
13
-
14
- useEffect(() => {
15
- setHandler(globalUploadHandler);
16
- }, []);
17
-
18
- return handler;
19
- }