diff --git a/src/components/Markdown/Markdown.test.tsx b/src/components/Markdown/Markdown.test.tsx index b5b84e3e..10d5e5bf 100644 --- a/src/components/Markdown/Markdown.test.tsx +++ b/src/components/Markdown/Markdown.test.tsx @@ -103,6 +103,19 @@ describe('Markdown', () => { const { getByText } = render() expect(getByText('This is a blockquote.')).toBeDefined() }) + + it('renders a horizontal rule', () => { + const text = 'First paragraph\n---\nSecond paragraph' + const { container } = render() + + const paragraphs = container.querySelectorAll('p') + expect(paragraphs.length).toBe(2) + expect(paragraphs[0]?.textContent).toBe('First paragraph') + expect(paragraphs[1]?.textContent).toBe('Second paragraph') + + const hr = container.querySelector('hr') + expect(hr).toBeDefined() + }) }) describe('Markdown code blocks', () => { diff --git a/src/components/Markdown/Markdown.tsx b/src/components/Markdown/Markdown.tsx index 8e0b299d..81a27d91 100644 --- a/src/components/Markdown/Markdown.tsx +++ b/src/components/Markdown/Markdown.tsx @@ -17,6 +17,7 @@ type Token = | { type: 'list', ordered: boolean, items: Token[][] } | { type: 'blockquote', children: Token[] } | { type: 'codeblock', language?: string, content: string } + | { type: 'hr' } function parseMarkdown(text: string): Token[] { const tokens: Token[] = [] @@ -50,6 +51,13 @@ function parseMarkdown(text: string): Token[] { continue } + // Horizontal rule + if (/^---+$/.test(line.trim())) { + tokens.push({ type: 'hr' }) + i++ + continue + } + // Heading const headingMatch = /^(#{1,6})\s+(.*)/.exec(line) if (headingMatch !== null) { @@ -110,6 +118,7 @@ function parseMarkdown(text: string): Token[] { if (ln.startsWith('>')) break // blockquote if (/^(#{1,6})\s+/.test(ln)) break // heading if (/^(\s*)([-*+–•‣◦○⚬]|\d+\.)\s+/.test(ln)) break // list item + if (/^---+$/.test(ln.trim())) break // horizontal rule paraLines.push(ln) i++ @@ -499,6 +508,8 @@ function renderTokens(tokens: Token[], keyPrefix = ''): ReactNode[] { { key }, createElement('code', null, token.content) ) + case 'hr': + return createElement('hr', { key }) default: return null }