Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/editor/blocks/step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ const writeStepViewMode = (mode: StepViewMode) => {
}
};

/**
* Returns true when a normalised (lowercased, trailing-punctuation-stripped)
* heading text looks like a "Steps" heading.
* Accepted forms: steps, step, step(s).
*/
export function isStepsHeading(text: string): boolean {
return /^step(s|\(s\))?$/.test(text);
}

export const isEmptyParagraph = (b: any): boolean =>
b.type === "paragraph" &&
(!Array.isArray(b.content) ||
Expand Down Expand Up @@ -80,7 +89,7 @@ export function canInsertStepOrSnippet(
.trim()
.toLowerCase()
.replace(/[:\-–—]$/, "");
return text === "steps";
return isStepsHeading(text);
}
return false;
}
Expand Down Expand Up @@ -162,7 +171,7 @@ export const stepBlock = createReactBlockSpec(
.trim()
.toLowerCase()
.replace(/[:\-–—]$/, "");
return text === "steps";
return isStepsHeading(text);
}
return false;
}
Expand Down
34 changes: 34 additions & 0 deletions src/editor/customMarkdownConverter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,40 @@ describe("markdownToBlocks", () => {
]);
});

it("parses steps under a 'Step' heading (singular)", () => {
const markdown = ["## Step", "", "* Open the app", "* Click login"].join("\n");
const blocks = markdownToBlocks(markdown);
const stepBlocks = blocks.filter((block) => block.type === "testStep");
expect(stepBlocks).toHaveLength(2);
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "Open the app" });
expect(stepBlocks[1].props).toMatchObject({ stepTitle: "Click login" });
});

it("parses steps under a 'Step(s)' heading", () => {
const markdown = ["# Step(s)", "", "1. First step", "2. Second step"].join("\n");
const blocks = markdownToBlocks(markdown);
const stepBlocks = blocks.filter((block) => block.type === "testStep");
expect(stepBlocks).toHaveLength(2);
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "First step", listStyle: "ordered" });
expect(stepBlocks[1].props).toMatchObject({ stepTitle: "Second step", listStyle: "ordered" });
});

it("parses steps under an h4 'step' heading (lowercase)", () => {
const markdown = ["#### step", "", "* Do something"].join("\n");
const blocks = markdownToBlocks(markdown);
const stepBlocks = blocks.filter((block) => block.type === "testStep");
expect(stepBlocks).toHaveLength(1);
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "Do something" });
});

it("parses steps under a 'Step:' heading with trailing colon", () => {
const markdown = ["### Step:", "", "* Verify output"].join("\n");
const blocks = markdownToBlocks(markdown);
const stepBlocks = blocks.filter((block) => block.type === "testStep");
expect(stepBlocks).toHaveLength(1);
expect(stepBlocks[0].props).toMatchObject({ stepTitle: "Verify output" });
});

it("handles multiple steps with expected results without extra asterisks", () => {
const markdown = [
"### Preconditions",
Expand Down
3 changes: 2 additions & 1 deletion src/editor/customMarkdownConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
Styles,
} from "@blocknote/core";
import type { customSchema } from "./customSchema";
import { isStepsHeading } from "./blocks/step";

// Types derived from the custom schema so the converter stays type-safe when the schema evolves.
type Schema = typeof customSchema;
Expand Down Expand Up @@ -1413,7 +1414,7 @@ export function markdownToBlocks(markdown: string): CustomPartialBlock[] {
const headingText = inlineContentToPlainText(headingBlock.content as any);
const normalizedHeading = headingText.trim().toLowerCase();

if (normalizedHeading.replace(/[:\-–—]$/, "") === "steps") {
if (isStepsHeading(normalizedHeading.replace(/[:\-–—]$/, ""))) {
stepsHeadingLevel = headingLevel;
} else if (
stepsHeadingLevel !== null &&
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export {
type CustomBlock,
type CustomEditor,
} from "./editor/customSchema";
export { stepBlock, canInsertStepOrSnippet } from "./editor/blocks/step";
export { stepBlock, canInsertStepOrSnippet, isStepsHeading } from "./editor/blocks/step";
export { snippetBlock } from "./editor/blocks/snippet";
export { markdownToHtml, htmlToMarkdown } from "./editor/blocks/markdown";

Expand Down
Loading