diff --git a/packages/@ourworldindata/grapher/src/sourcesTab/SourcesTab.tsx b/packages/@ourworldindata/grapher/src/sourcesTab/SourcesTab.tsx index 43c4276ca03..812c4791130 100644 --- a/packages/@ourworldindata/grapher/src/sourcesTab/SourcesTab.tsx +++ b/packages/@ourworldindata/grapher/src/sourcesTab/SourcesTab.tsx @@ -2,7 +2,6 @@ import { Bounds, DEFAULT_BOUNDS, MarkdownTextWrap, - linkify, OwidOrigin, uniq, excludeNullish, @@ -14,9 +13,6 @@ import { faPencilAlt } from "@fortawesome/free-solid-svg-icons" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js" import { CoreColumn, OwidColumnDef } from "@ourworldindata/core-table" -const formatText = (s: string): string => - linkify(s).replace(/(?:\r\n|\r|\n)/g, "
") - export interface SourcesTabManager { adminBaseUrl?: string columnsWithSources: CoreColumn[] @@ -110,11 +106,12 @@ export class SourcesTab extends React.Component<{ // metadata V2 shortDescription Variable description - + + + ) : null} {coverage ? ( @@ -162,35 +159,34 @@ export class SourcesTab extends React.Component<{ source.dataPublishedBy ? ( Data published by - + + + ) : null} {source.dataPublisherSource ? ( Data publisher's source - + + + ) : null} {source.link ? ( Link - + + + ) : null} {retrievedDate ? ( @@ -202,12 +198,12 @@ export class SourcesTab extends React.Component<{ {source.additionalInfo && ( -

+

+ +

)} ) diff --git a/packages/@ourworldindata/utils/src/GdocsUtils.ts b/packages/@ourworldindata/utils/src/GdocsUtils.ts index 0848ec4a2ce..5f3e391e2c7 100644 --- a/packages/@ourworldindata/utils/src/GdocsUtils.ts +++ b/packages/@ourworldindata/utils/src/GdocsUtils.ts @@ -1,8 +1,14 @@ import { spansToUnformattedPlainText } from "./Util.js" import { gdocUrlRegex } from "./GdocsConstants.js" -import { OwidGdocLinkJSON, Span } from "./owidTypes.js" +import { EnrichedBlockText, OwidGdocLinkJSON, Span } from "./owidTypes.js" import { Url } from "./urls/Url.js" import urlSlug from "url-slug" +import { + EveryMarkdownNode, + MarkdownRoot, + mdParser, +} from "./MarkdownTextWrap/parser.js" +import { P, match } from "ts-pattern" export function getLinkType(urlString: string): OwidGdocLinkJSON["linkType"] { const url = Url.fromURL(urlString) @@ -40,3 +46,149 @@ export function getUrlTarget(urlString: string): string { export function convertHeadingTextToId(headingText: Span[]): string { return urlSlug(spansToUnformattedPlainText(headingText)) } + +const convertMarkdownNodeToSpan = (node: EveryMarkdownNode): Span[] => { + return match(node) + .with( + { + type: "text", + }, + (n) => [ + { + spanType: "span-simple-text" as const, + text: n.value, + } as Span, + ] + ) + .with( + { + type: "textSegments", + }, + (n) => n.children.flatMap(convertMarkdownNodeToSpan) as Span[] + ) + .with( + { + type: "newline", + }, + () => [ + { + spanType: "span-simple-text" as const, + text: "\n", + } as Span, + ] + ) + .with( + { + type: "whitespace", + }, + () => [ + { + spanType: "span-simple-text" as const, + text: " ", + } as Span, + ] + ) + .with( + { + type: "detailOnDemand", + }, + (n) => [ + { + spanType: "span-dod" as const, + id: n.term, + children: n.children.flatMap(convertMarkdownNodeToSpan), + } as Span, + ] + ) + .with( + { + type: "markdownLink", + }, + (n) => [ + { + spanType: "span-link" as const, + url: n.href, + children: n.children.flatMap(convertMarkdownNodeToSpan), + } as Span, + ] + ) + .with( + { + type: "plainUrl", + }, + (n) => [ + { + spanType: "span-link" as const, + url: n.href, + children: [ + { + spanType: "span-simple-text" as const, + text: n.href, + }, + ], + } as Span, + ] + ) + .with( + { + type: "bold", + }, + (n) => [ + { + spanType: "span-bold" as const, + children: n.children.flatMap(convertMarkdownNodeToSpan), + } as Span, + ] + ) + .with( + { + type: P.union("italic", "plainItalic", "italicWithoutBold"), + }, + (n) => [ + { + spanType: "span-italic" as const, + children: n.children.flatMap(convertMarkdownNodeToSpan), + } as Span, + ] + ) + .with( + { + type: P.union("bold", "plainBold", "boldWithoutItalic"), + }, + (n) => [ + { + spanType: "span-bold" as const, + children: n.children.flatMap(convertMarkdownNodeToSpan), + } as Span, + ] + ) + .exhaustive() + //.otherwise(() => ({ spanType: "span-simple-text" as const, text: "" })) +} + +const convertMarkdownNodesToSpans = (nodes: MarkdownRoot): Span[] => + nodes.children.flatMap(convertMarkdownNodeToSpan) + +export const markdownToEnrichedTextBlock = ( + markdown: string +): EnrichedBlockText => { + const parsedMarkdown = mdParser.markdown.parse(markdown) + if (parsedMarkdown.status) { + const spans = convertMarkdownNodesToSpans(parsedMarkdown.value) + return { + type: "text", + value: spans, + parseErrors: [], + } + } else + return { + type: "text", + value: [], + parseErrors: [ + { + message: `Failed to parse markdown - expected ${parsedMarkdown.expected} at ${parsedMarkdown.index}`, + isWarning: false, + }, + ], + } +} diff --git a/packages/@ourworldindata/utils/src/index.ts b/packages/@ourworldindata/utils/src/index.ts index 71d7dc2a93b..2111fbd5bf0 100644 --- a/packages/@ourworldindata/utils/src/index.ts +++ b/packages/@ourworldindata/utils/src/index.ts @@ -608,6 +608,7 @@ export { getUrlTarget, checkIsInternalLink, convertHeadingTextToId, + markdownToEnrichedTextBlock, } from "./GdocsUtils.js" export { diff --git a/site/DataPageV2Content.tsx b/site/DataPageV2Content.tsx index c0d0719f23b..982e4b0cf96 100644 --- a/site/DataPageV2Content.tsx +++ b/site/DataPageV2Content.tsx @@ -10,13 +10,9 @@ import { ArticleBlocks } from "./gdocs/ArticleBlocks.js" import { RelatedCharts } from "./blocks/RelatedCharts.js" import { DataPageV2ContentFields, - mdParser, - MarkdownRoot, - EveryMarkdownNode, - Span, - EnrichedBlockText, excludeNullish, slugify, + markdownToEnrichedTextBlock, } from "@ourworldindata/utils" import { AttachmentsContext, DocumentContext } from "./gdocs/OwidGdoc.js" import StickyNav from "./blocks/StickyNav.js" @@ -24,158 +20,12 @@ import cx from "classnames" import { DebugProvider } from "./gdocs/DebugContext.js" import { CodeSnippet } from "./blocks/CodeSnippet.js" import dayjs from "dayjs" -import { P, match } from "ts-pattern" declare global { interface Window { _OWID_DATAPAGEV2_PROPS: DataPageV2ContentFields _OWID_GRAPHER_CONFIG: GrapherInterface } } - -const convertMarkdownNodeToSpan = (node: EveryMarkdownNode): Span[] => { - return match(node) - .with( - { - type: "text", - }, - (n) => [ - { - spanType: "span-simple-text" as const, - text: n.value, - } as Span, - ] - ) - .with( - { - type: "textSegments", - }, - (n) => n.children.flatMap(convertMarkdownNodeToSpan) as Span[] - ) - .with( - { - type: "newline", - }, - () => [ - { - spanType: "span-simple-text" as const, - text: "\n", - } as Span, - ] - ) - .with( - { - type: "whitespace", - }, - () => [ - { - spanType: "span-simple-text" as const, - text: " ", - } as Span, - ] - ) - .with( - { - type: "detailOnDemand", - }, - (n) => [ - { - spanType: "span-dod" as const, - id: n.term, - children: n.children.flatMap(convertMarkdownNodeToSpan), - } as Span, - ] - ) - .with( - { - type: "markdownLink", - }, - (n) => [ - { - spanType: "span-link" as const, - url: n.href, - children: n.children.flatMap(convertMarkdownNodeToSpan), - } as Span, - ] - ) - .with( - { - type: "plainUrl", - }, - (n) => [ - { - spanType: "span-link" as const, - url: n.href, - children: [ - { - spanType: "span-simple-text" as const, - text: n.href, - }, - ], - } as Span, - ] - ) - .with( - { - type: "bold", - }, - (n) => [ - { - spanType: "span-bold" as const, - children: n.children.flatMap(convertMarkdownNodeToSpan), - } as Span, - ] - ) - .with( - { - type: P.union("italic", "plainItalic", "italicWithoutBold"), - }, - (n) => [ - { - spanType: "span-italic" as const, - children: n.children.flatMap(convertMarkdownNodeToSpan), - } as Span, - ] - ) - .with( - { - type: P.union("bold", "plainBold", "boldWithoutItalic"), - }, - (n) => [ - { - spanType: "span-bold" as const, - children: n.children.flatMap(convertMarkdownNodeToSpan), - } as Span, - ] - ) - .exhaustive() - //.otherwise(() => ({ spanType: "span-simple-text" as const, text: "" })) -} - -const convertMarkdownNodesToSpans = (nodes: MarkdownRoot) => - nodes.children.flatMap(convertMarkdownNodeToSpan) - -const markdownToEnrichedTextBlock = (markdown: string): EnrichedBlockText => { - const parsedMarkdown = mdParser.markdown.parse(markdown) - if (parsedMarkdown.status) { - const spans = convertMarkdownNodesToSpans(parsedMarkdown.value) - return { - type: "text", - value: spans, - parseErrors: [], - } - } else - return { - type: "text", - value: [], - parseErrors: [ - { - message: `Failed to parse markdown - expected ${parsedMarkdown.expected} at ${parsedMarkdown.index}`, - isWarning: false, - }, - ], - } -} - export const OWID_DATAPAGE_CONTENT_ROOT_ID = "owid-datapageJson-root" export const DataPageV2Content = ({