From 23b063cd6b72d1007a80bc37bcdc7f43777f1a81 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli <36352093+GuySartorelli@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:34:29 +1300 Subject: [PATCH] FIX Allow callout and children in code blocks (#275) --- src/components/CalloutBlock.tsx | 29 +++++++++++++++++++++++++++++ src/types/index.ts | 5 +++++ src/utils/cleanCalloutTags.ts | 30 ++++++++---------------------- src/utils/parseCalloutTags.ts | 23 +++++++++++++++++++++++ src/utils/parseHTML.ts | 8 ++++++-- 5 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 src/components/CalloutBlock.tsx create mode 100644 src/utils/parseCalloutTags.ts diff --git a/src/components/CalloutBlock.tsx b/src/components/CalloutBlock.tsx new file mode 100644 index 0000000..e9cb11f --- /dev/null +++ b/src/components/CalloutBlock.tsx @@ -0,0 +1,29 @@ +import React, { StatelessComponent } from 'react'; +import { CalloutBlockProps } from '../types'; + + +const getCalloutClass = (type: string): string => { + switch (type) { + case 'hint': + return 'success'; + case 'notice': + return 'warning'; + case 'alert': + return 'danger'; + case 'note': + return 'info' + default: + return type; + } +}; + +const CalloutBlock: StatelessComponent = ({ type, content }) => { + return ( +
+
{content}
+
+ ); + +}; + +export default CalloutBlock; \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 6ed99e3..2e573aa 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -52,6 +52,11 @@ export interface HierarchyQuery { } }; +export interface CalloutBlockProps { + type: string; + content: any; +} + export interface ChildrenOfProps { folderName?: string; exclude?: string; diff --git a/src/utils/cleanCalloutTags.ts b/src/utils/cleanCalloutTags.ts index 60b6146..f3f02d9 100644 --- a/src/utils/cleanCalloutTags.ts +++ b/src/utils/cleanCalloutTags.ts @@ -1,27 +1,13 @@ -const getCalloutClass = (type: string): string => { - switch (type) { - case 'hint': - return 'success'; - case 'notice': - return 'warning'; - case 'alert': - return 'danger'; - case 'note': - return 'info' - default: - return type; - } -}; +/** + * Removes the paragraph tags from around callout blocks so they end up being valid HTML + * + * @param html + * @returns + */ const cleanCalloutTags = (html: string): string => { return html.replace( - /(?:

\s*)(\[(hint|warning|info|alert|notice|note)\])(.*?)(\[\/(hint|warning|info|alert|notice|note)\])(?:\s*<\/p>)/gs, - (_, tag, type, content) => ` -

-
- ${content} -
-
- ` + /(?:

\s*)((?:\[(hint|warning|info|alert|notice|note)\]).*?(?:\[\/(hint|warning|info|alert|notice|note)\]))(?:\s*<\/p>)/gs, + (_, callout) => callout ); }; diff --git a/src/utils/parseCalloutTags.ts b/src/utils/parseCalloutTags.ts new file mode 100644 index 0000000..f223217 --- /dev/null +++ b/src/utils/parseCalloutTags.ts @@ -0,0 +1,23 @@ +import { ReactElement, createElement } from 'react'; +import CalloutBlock from '../components/CalloutBlock'; + +/** + * Turn [hint] and other callouts into a proper React component. + * @param data + */ +const parseCalloutTags = (data: any): ReactElement|false => { + let matches; + + matches = data.match(/\[(hint|warning|info|alert|notice|note)\](.*?)\[\/(?:hint|warning|info|alert|notice|note)\]/s); + + if (matches) { + const type = matches[1]; + const content = matches[2]; + + return createElement(CalloutBlock, { type, content }); + } + + return false; +}; + +export default parseCalloutTags; \ No newline at end of file diff --git a/src/utils/parseHTML.ts b/src/utils/parseHTML.ts index df93fb4..5d91084 100644 --- a/src/utils/parseHTML.ts +++ b/src/utils/parseHTML.ts @@ -9,6 +9,7 @@ import { ReactElement } from 'react'; import rewriteTable from './rewriteTable'; import rewriteHeader from './rewriteHeader'; import cleanHeaders from './cleanHeaders'; +import parseCalloutTags from './parseCalloutTags'; /** * Replace all the [CHILDREN] with proper React components. @@ -38,9 +39,12 @@ const parseHTML = (html: string): ReactElement | ReactElement[] | string => { return rewriteHeader(domNode); } } - if (domNode.data) { + if (domNode.data && domNode.parent?.name !== 'code') { const { data } = domNode; - return parseChildrenOf(data); + if (data.match(/\[CHILDREN.*?\]/)) { + return parseChildrenOf(data); + } + return parseCalloutTags(data); } return false;