-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: enable custom anchor links (#590)
- Loading branch information
1 parent
f9b4b9c
commit 725707a
Showing
11 changed files
with
258 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import * as matchers from "@testing-library/jest-dom/matchers"; | ||
import { cleanup } from "@testing-library/react"; | ||
import { afterEach, beforeAll, expect, vi } from "vitest"; | ||
|
||
expect.extend(matchers); | ||
|
||
beforeAll(() => { | ||
vi.mock("next/router", () => require("next-router-mock")); | ||
}); | ||
|
||
afterEach(() => { | ||
cleanup(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { createElement } from "react"; | ||
import renderer from "react-test-renderer"; | ||
import { MdxContent } from "../MdxContent"; | ||
import { serializeMdxContent } from "../mdx"; | ||
|
||
async function renderMdxContent(content: string): Promise<renderer.ReactTestRendererJSON> { | ||
const serializedContent = await serializeMdxContent(content, true); | ||
const result = renderer.create(createElement(MdxContent, { mdx: serializedContent })).toJSON(); | ||
|
||
assert(result != null); | ||
assert(!Array.isArray(result)); | ||
|
||
return result; | ||
} | ||
|
||
describe("serializeMdxContent", () => { | ||
describe("custom html", () => { | ||
it("should render custom html", async () => { | ||
const result = await renderMdxContent("<div>Hello world!</div>"); | ||
expect(result.type).toBe("div"); | ||
expect(result.children).toEqual(["Hello world!"]); | ||
}); | ||
|
||
it("should render custom html with JSX", async () => { | ||
const result = await renderMdxContent('<div style={{ display: "none" }}>Hello world!</div>'); | ||
expect(result.type).toBe("div"); | ||
expect(result.props.style).toEqual({ display: "none" }); | ||
}); | ||
|
||
it("should render custom html with className", async () => { | ||
const result = await renderMdxContent('<div className="testing">Hello world!</div>'); | ||
expect(result.type).toBe("div"); | ||
expect(result.props.className).toEqual("testing"); | ||
}); | ||
|
||
it("should render custom html with className 2", async () => { | ||
const result = await renderMdxContent('<div class="testing">Hello world!</div>'); | ||
expect(result.type).toBe("div"); | ||
expect(result.props.className).toEqual("testing"); | ||
}); | ||
|
||
it("should render custom html with CSS styles", async () => { | ||
const result = await renderMdxContent('<div style="display: none">Hello world!</div>'); | ||
expect(result.type).toBe("div"); | ||
expect(result.props.style).toEqual({ display: "none" }); | ||
}); | ||
}); | ||
|
||
describe("headings", () => { | ||
it("should generate automatic anchor link", async () => { | ||
const result = await renderMdxContent("### Hello world!"); | ||
expect(result.type).toBe("h3"); | ||
expect(result.props.id).toBe("hello-world"); | ||
}); | ||
|
||
it("should generate with automatic anchor link with special letters", async () => { | ||
const result = await renderMdxContent("### Hello world ✅"); | ||
expect(result.type).toBe("h3"); | ||
expect(result.props.id).toBe("hello-world-"); | ||
}); | ||
|
||
it("should generate with a custom anchor link", async () => { | ||
const mdxContent = "### Hello world! [#custom-anchor]"; | ||
|
||
const result = await renderMdxContent(mdxContent); | ||
|
||
expect(result.type).toBe("h3"); | ||
expect(result.props.id).toBe("custom-anchor"); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Element } from "hast"; | ||
import { Heading } from "mdast"; | ||
import { State } from "mdast-util-to-hast"; | ||
|
||
export function customHeadingHandler(state: State, node: Heading): Element { | ||
let id: string | undefined; | ||
const children = state.all(node).map((child) => { | ||
if (child.type === "text") { | ||
// extract [#anchor] from heading text | ||
const match = child.value.match(/^(.*?)(?:\s*\[#([^\]]+)\])?$/); | ||
const [, text, anchor] = match || []; | ||
if (text != null && anchor != null) { | ||
id = anchor; | ||
return { | ||
...child, | ||
value: text, | ||
}; | ||
} | ||
} | ||
|
||
return child; | ||
}); | ||
const result: Element = { | ||
type: "element", | ||
tagName: "h" + node.depth, | ||
properties: { id }, | ||
children, | ||
}; | ||
state.patch(node, result); | ||
return state.applyData(node, result); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { camelCase } from "lodash-es"; | ||
import StyleToObject from "style-to-object"; | ||
import { emitDatadogError } from "../analytics/datadogRum"; | ||
|
||
export function parseStringStyle(value: unknown): Record<string, string> | undefined { | ||
if (typeof value !== "string") { | ||
return undefined; | ||
} | ||
|
||
const result: Record<string, string> = {}; | ||
|
||
try { | ||
StyleToObject(value, replacer); | ||
} catch (e) { | ||
// eslint-disable-next-line no-console | ||
console.error("Failed to parse style", e); | ||
|
||
emitDatadogError(e, { | ||
context: "FernSyntaxHighlighter", | ||
errorSource: "parseStyle", | ||
errorDescription: "Failed to parse style originating from shiki", | ||
data: { value }, | ||
}); | ||
|
||
return undefined; | ||
} | ||
|
||
function replacer(name: string, value: string) { | ||
let key = name; | ||
|
||
if (key.slice(0, 2) !== "--") { | ||
if (key.slice(0, 4) === "-ms-") { | ||
key = "ms-" + key.slice(4); | ||
} | ||
key = camelCase(key); | ||
} | ||
|
||
result[key] = value; | ||
} | ||
|
||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.