Skip to content
Closed
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
6 changes: 2 additions & 4 deletions src/components/Doc.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import Markdown from "react-markdown";
import { MiniMarkdown } from "./MiniMarkdown";

const headers = {
js: "JS",
Expand Down Expand Up @@ -50,9 +50,7 @@ export class Doc extends React.Component<DocProps> {
{doc &&
<div className="SpecDoc">
<div className="SpecDoc__doc" data-wd-key='spec-field-doc'>
<Markdown components={{
a: ({node: _node, href, children, ...props}) => <a href={href} target="_blank" {...props}>{children}</a>,
}}>{doc}</Markdown>
<MiniMarkdown>{doc}</MiniMarkdown>
</div>
{renderValues &&
<ul className="SpecDoc__values">
Expand Down
83 changes: 83 additions & 0 deletions src/components/MiniMarkdown.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
import { MiniMarkdown, parseMarkdown } from "./MiniMarkdown";

function collectDocs(value: unknown): string[] {
if (!value || typeof value !== "object") return [];

const ownDoc = "doc" in value && typeof value.doc === "string" ? [value.doc] : [];

return ownDoc.concat(Object.values(value).flatMap(collectDocs));
}

describe("parseMarkdown", () => {
it("renders the syntax used by style-spec documentation", () => {
const [paragraph, list, code] = parseMarkdown(
"A [`property`](https://example.com/MapLibre_style) with **details**.\n\n- [Example](assets/example.svg)\n\n```json\n{\"value\": true}\n```"
);

expect(paragraph).toMatchObject({
type: "paragraph",
children: [
{ type: "text", value: "A " },
{
type: "link",
href: "https://example.com/MapLibre_style",
children: [{ type: "code", value: "property" }]
},
{ type: "text", value: " with " },
{ type: "strong", children: [{ type: "text", value: "details" }] },
{ type: "text", value: "." }
]
});
expect(list).toMatchObject({
type: "unordered-list",
items: [[{
type: "link",
href: "assets/example.svg",
children: [{ type: "text", value: "Example" }]
}]]
});
expect(code).toMatchObject({
type: "code",
language: "json",
value: "{\"value\": true}"
});
});

it("keeps unsafe links as plain text", () => {
const [paragraph] = parseMarkdown("[unsafe](javascript:alert(1))");
expect(paragraph).toMatchObject({
type: "paragraph",
children: [{ type: "text", value: "[unsafe](javascript:alert(1))" }]
});
});

it("does not interpret underscores inside ordinary words", () => {
const [paragraph] = parseMarkdown("The `font_face` property and font_face value.");
expect(paragraph).toMatchObject({
type: "paragraph",
children: [
{ type: "text", value: "The " },
{ type: "code", value: "font_face" },
{ type: "text", value: " property and font_face value." }
]
});
});

it("renders every current style-spec document", () => {
const docs = collectDocs(latest);
expect(docs).toHaveLength(620);

const html = docs
.map(doc => renderToStaticMarkup(React.createElement(MiniMarkdown, { children: doc })))
.join("");

expect(html).not.toContain("undefined");
expect(html).toContain('href="https://en.wikipedia.org/wiki/Khmer_script"');
expect(html).toContain("<img");
expect(html).toContain("language-json");
});
});
Loading