testomatio-editor-blocks 0.4.30 → 0.4.31

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.
@@ -1,4 +1,5 @@
1
1
  import { isLinkInlineContent, isStyledTextInlineContent, } from "@blocknote/core";
2
+ import { resolveFileDisplayUrl } from "./fileDisplayUrl";
2
3
  import { isStepsHeading } from "./blocks/step";
3
4
  const BASE_BLOCK_PROPS = {
4
5
  textAlignment: "left",
@@ -269,9 +270,8 @@ function serializeBlock(block, ctx, orderedIndex, stepIndex) {
269
270
  case "file": {
270
271
  const url = block.props.url || "";
271
272
  const name = block.props.name || "";
272
- const caption = block.props.caption || "";
273
273
  if (url) {
274
- const displayUrl = caption || url;
274
+ const displayUrl = resolveFileDisplayUrl(name, url);
275
275
  lines.push(`[![${name}](${displayUrl})](${url})`);
276
276
  }
277
277
  return flattenWithBlankLine(lines, true);
@@ -1230,7 +1230,6 @@ export function markdownToBlocks(markdown) {
1230
1230
  type: "file",
1231
1231
  props: {
1232
1232
  name: fileMatch[1] || "",
1233
- caption: fileMatch[2] || "",
1234
1233
  url: fileMatch[3],
1235
1234
  },
1236
1235
  children: [],
@@ -0,0 +1,3 @@
1
+ export type FileDisplayUrlResolver = (fileName: string) => string;
2
+ export declare function setFileDisplayUrlResolver(fn: FileDisplayUrlResolver | null): void;
3
+ export declare function resolveFileDisplayUrl(fileName: string, fallbackUrl: string): string;
@@ -0,0 +1,15 @@
1
+ let resolver = null;
2
+ export function setFileDisplayUrlResolver(fn) {
3
+ resolver = fn;
4
+ }
5
+ export function resolveFileDisplayUrl(fileName, fallbackUrl) {
6
+ var _a;
7
+ if (resolver) {
8
+ return resolver(fileName);
9
+ }
10
+ const ext = ((_a = fileName.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || "";
11
+ if (ext) {
12
+ return `/images/file-type-icons/${ext}.svg`;
13
+ }
14
+ return fallbackUrl;
15
+ }
@@ -5,5 +5,6 @@ export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
5
5
  export { blocksToMarkdown, markdownToBlocks, type CustomEditorBlock, type CustomPartialBlock, } from "./editor/customMarkdownConverter";
6
6
  export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, type StepSuggestion, type StepJsonApiDocument, type StepJsonApiResource, } from "./editor/stepAutocomplete";
7
7
  export { useStepImageUpload, setImageUploadHandler, type StepImageUploadHandler, } from "./editor/stepImageUpload";
8
+ export { setFileDisplayUrlResolver, resolveFileDisplayUrl, type FileDisplayUrlResolver, } from "./editor/fileDisplayUrl";
8
9
  export { createMarkdownPasteHandler } from "./editor/createMarkdownPasteHandler";
9
10
  export declare const testomatioEditorClassName = "markdown testomatio-editor";
package/package/index.js CHANGED
@@ -5,5 +5,6 @@ export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";
5
5
  export { blocksToMarkdown, markdownToBlocks, } from "./editor/customMarkdownConverter";
6
6
  export { useStepAutocomplete, parseStepsFromJsonApi, setStepsFetcher, } from "./editor/stepAutocomplete";
7
7
  export { useStepImageUpload, setImageUploadHandler, } from "./editor/stepImageUpload";
8
+ export { setFileDisplayUrlResolver, resolveFileDisplayUrl, } from "./editor/fileDisplayUrl";
8
9
  export { createMarkdownPasteHandler } from "./editor/createMarkdownPasteHandler";
9
10
  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.4.30",
3
+ "version": "0.4.31",
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",
@@ -6,6 +6,7 @@ import {
6
6
  type CustomEditorBlock,
7
7
  type CustomPartialBlock,
8
8
  } from "./customMarkdownConverter";
9
+ import { setFileDisplayUrlResolver } from "./fileDisplayUrl";
9
10
 
10
11
  const baseProps = {
11
12
  textAlignment: "left" as const,
@@ -1515,7 +1516,12 @@ describe("markdownToBlocks", () => {
1515
1516
  });
1516
1517
 
1517
1518
  describe("file block serialization", () => {
1518
- it("serializes a file block with name, caption (display_url), and url", () => {
1519
+ it("serializes a file block using display url resolver", () => {
1520
+ setFileDisplayUrlResolver((name: string) => {
1521
+ const ext = name.split(".").pop()?.toLowerCase() || "";
1522
+ return `/images/file-type-icons/${ext}.svg`;
1523
+ });
1524
+
1519
1525
  const blocks: CustomEditorBlock[] = [
1520
1526
  {
1521
1527
  id: "1",
@@ -1524,7 +1530,7 @@ describe("file block serialization", () => {
1524
1530
  ...baseProps,
1525
1531
  url: "https://example.com/file.pdf",
1526
1532
  name: "report.pdf",
1527
- caption: "/images/file-type-icons/pdf.svg",
1533
+ caption: "",
1528
1534
  },
1529
1535
  content: undefined as any,
1530
1536
  children: [],
@@ -1532,9 +1538,11 @@ describe("file block serialization", () => {
1532
1538
  ];
1533
1539
  const md = blocksToMarkdown(blocks);
1534
1540
  expect(md).toBe("[![report.pdf](/images/file-type-icons/pdf.svg)](https://example.com/file.pdf)");
1541
+
1542
+ setFileDisplayUrlResolver(null);
1535
1543
  });
1536
1544
 
1537
- it("falls back to url when caption is empty", () => {
1545
+ it("falls back to url when no resolver is set", () => {
1538
1546
  const blocks: CustomEditorBlock[] = [
1539
1547
  {
1540
1548
  id: "1",
@@ -1550,7 +1558,7 @@ describe("file block serialization", () => {
1550
1558
  },
1551
1559
  ];
1552
1560
  const md = blocksToMarkdown(blocks);
1553
- expect(md).toBe("[![file.pdf](https://example.com/file.pdf)](https://example.com/file.pdf)");
1561
+ expect(md).toBe("[![file.pdf](/images/file-type-icons/pdf.svg)](https://example.com/file.pdf)");
1554
1562
  });
1555
1563
 
1556
1564
  it("outputs nothing when url is empty", () => {
@@ -1580,7 +1588,6 @@ describe("file block parsing", () => {
1580
1588
  expect(blocks).toHaveLength(1);
1581
1589
  expect(blocks[0].type).toBe("file");
1582
1590
  expect((blocks[0].props as any).name).toBe("report.pdf");
1583
- expect((blocks[0].props as any).caption).toBe("/images/file-type-icons/pdf.svg");
1584
1591
  expect((blocks[0].props as any).url).toBe("https://example.com/file.pdf");
1585
1592
  });
1586
1593
 
@@ -1601,6 +1608,11 @@ describe("file block parsing", () => {
1601
1608
  });
1602
1609
 
1603
1610
  it("round-trips file blocks through serialize and parse", () => {
1611
+ setFileDisplayUrlResolver((name: string) => {
1612
+ const ext = name.split(".").pop()?.toLowerCase() || "";
1613
+ return `/images/file-type-icons/${ext}.svg`;
1614
+ });
1615
+
1604
1616
  const blocks: CustomEditorBlock[] = [
1605
1617
  {
1606
1618
  id: "1",
@@ -1609,7 +1621,7 @@ describe("file block parsing", () => {
1609
1621
  ...baseProps,
1610
1622
  url: "https://example.com/doc.xlsx",
1611
1623
  name: "doc.xlsx",
1612
- caption: "/images/file-type-icons/xlsx.svg",
1624
+ caption: "",
1613
1625
  },
1614
1626
  content: undefined as any,
1615
1627
  children: [],
@@ -1621,6 +1633,7 @@ describe("file block parsing", () => {
1621
1633
  expect(parsed[0].type).toBe("file");
1622
1634
  expect((parsed[0].props as any).url).toBe("https://example.com/doc.xlsx");
1623
1635
  expect((parsed[0].props as any).name).toBe("doc.xlsx");
1624
- expect((parsed[0].props as any).caption).toBe("/images/file-type-icons/xlsx.svg");
1636
+
1637
+ setFileDisplayUrlResolver(null);
1625
1638
  });
1626
1639
  });
@@ -8,6 +8,7 @@ import type {
8
8
  PartialBlock,
9
9
  Styles,
10
10
  } from "@blocknote/core";
11
+ import { resolveFileDisplayUrl } from "./fileDisplayUrl";
11
12
  import type { customSchema } from "./customSchema";
12
13
  import { isStepsHeading } from "./blocks/step";
13
14
 
@@ -347,9 +348,8 @@ function serializeBlock(
347
348
  case "file": {
348
349
  const url = (block.props as any).url || "";
349
350
  const name = (block.props as any).name || "";
350
- const caption = (block.props as any).caption || "";
351
351
  if (url) {
352
- const displayUrl = caption || url;
352
+ const displayUrl = resolveFileDisplayUrl(name, url);
353
353
  lines.push(`[![${name}](${displayUrl})](${url})`);
354
354
  }
355
355
  return flattenWithBlankLine(lines, true);
@@ -1472,7 +1472,6 @@ export function markdownToBlocks(markdown: string): CustomPartialBlock[] {
1472
1472
  type: "file",
1473
1473
  props: {
1474
1474
  name: fileMatch[1] || "",
1475
- caption: fileMatch[2] || "",
1476
1475
  url: fileMatch[3],
1477
1476
  },
1478
1477
  children: [],
@@ -0,0 +1,18 @@
1
+ export type FileDisplayUrlResolver = (fileName: string) => string;
2
+
3
+ let resolver: FileDisplayUrlResolver | null = null;
4
+
5
+ export function setFileDisplayUrlResolver(fn: FileDisplayUrlResolver | null) {
6
+ resolver = fn;
7
+ }
8
+
9
+ export function resolveFileDisplayUrl(fileName: string, fallbackUrl: string): string {
10
+ if (resolver) {
11
+ return resolver(fileName);
12
+ }
13
+ const ext = fileName.split(".").pop()?.toLowerCase() || "";
14
+ if (ext) {
15
+ return `/images/file-type-icons/${ext}.svg`;
16
+ }
17
+ return fallbackUrl;
18
+ }
package/src/index.ts CHANGED
@@ -30,6 +30,12 @@ export {
30
30
  type StepImageUploadHandler,
31
31
  } from "./editor/stepImageUpload";
32
32
 
33
+ export {
34
+ setFileDisplayUrlResolver,
35
+ resolveFileDisplayUrl,
36
+ type FileDisplayUrlResolver,
37
+ } from "./editor/fileDisplayUrl";
38
+
33
39
  export { createMarkdownPasteHandler } from "./editor/createMarkdownPasteHandler";
34
40
 
35
41
  export const testomatioEditorClassName = "markdown testomatio-editor";