Skip to content

Commit a23ddcc

Browse files
authored
Markdown: single column table (#262)
1 parent 97d0998 commit a23ddcc

File tree

3 files changed

+53
-9
lines changed

3 files changed

+53
-9
lines changed

src/components/Markdown/Markdown.test.tsx

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe('Markdown', () => {
8888
expect(getByText('Hyp').tagName).toBe('A')
8989
})
9090

91-
it('renders multiple links in one line', () => {
91+
it('multiple links in one line', () => {
9292
const text = 'Check out [Hyp](https://hyperparam.app) on [GitHub](https://github.com/hyparam).'
9393
const { getAllByRole, getByText } = render(<Markdown text={text} />)
9494
expect(getByText('Hyp')).toBeDefined()
@@ -103,18 +103,27 @@ describe('Markdown', () => {
103103
const { getByText } = render(<Markdown text={text} />)
104104
expect(getByText('This is a blockquote.')).toBeDefined()
105105
})
106+
})
106107

108+
describe('Markdown horizontal rules', () => {
107109
it('renders a horizontal rule', () => {
108110
const text = 'First paragraph\n---\nSecond paragraph'
109-
const { container } = render(<Markdown text={text} />)
111+
const { container, getByText, queryByRole } = render(<Markdown text={text} />)
110112

111-
const paragraphs = container.querySelectorAll('p')
112-
expect(paragraphs.length).toBe(2)
113-
expect(paragraphs[0]?.textContent).toBe('First paragraph')
114-
expect(paragraphs[1]?.textContent).toBe('Second paragraph')
113+
expect(container.querySelector('hr')).toBeDefined()
114+
expect(queryByRole('separator')).toBeDefined()
115+
expect(getByText('First paragraph')).toBeDefined()
116+
expect(getByText('Second paragraph')).toBeDefined()
117+
})
115118

116-
const hr = container.querySelector('hr')
117-
expect(hr).toBeDefined()
119+
it('horizontal rule must be entire line', () => {
120+
const text = 'First paragraph\n\n--- dashes\n\nSecond paragraph'
121+
const { container, getByText, queryByRole } = render(<Markdown text={text} />)
122+
expect(container.querySelector('hr')).toBeNull()
123+
expect(queryByRole('separator')).toBeNull()
124+
expect(getByText('First paragraph')).toBeDefined()
125+
expect(getByText('--- dashes')).toBeDefined()
126+
expect(getByText('Second paragraph')).toBeDefined()
118127
})
119128
})
120129

@@ -453,4 +462,20 @@ describe('Markdown with tables', () => {
453462
expect(queryByRole('table')).toBeNull() // no table
454463
expect(getByText('not | a | table')).toBeDefined()
455464
})
465+
466+
it('single column table', () => {
467+
const text = '| Only Header |\n|-------------|\n| Single cell |'
468+
const { getByText, getByRole } = render(<Markdown text={text} />)
469+
expect(getByRole('table')).toBeDefined()
470+
expect(getByText('Only Header')).toBeDefined()
471+
expect(getByText('Single cell')).toBeDefined()
472+
})
473+
474+
it('table with no leading or trailing pipes', () => {
475+
const text = 'Header1 | Header2\n------- | -------\nData1 | Data2'
476+
const { getByText, getByRole } = render(<Markdown text={text} />)
477+
expect(getByRole('table')).toBeDefined()
478+
expect(getByText('Header1')).toBeDefined()
479+
expect(getByText('Data2')).toBeDefined()
480+
})
456481
})

src/components/Markdown/Markdown.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ function parseMarkdown(text: string): Token[] {
112112
const sepLine = lines[i + 1] ?? ''
113113
// Check if the next line is a valid table separator
114114
// Extended markdown alignment syntax: |:--|, |:--:|, |--:|
115-
if (/^\s*\|?(\s*:?-+:?\s*\|)+\s*:?-+:?\s*\|?\s*$/.test(sepLine)) {
115+
const tableSepRegex = /^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/
116+
if (tableSepRegex.test(sepLine)) {
116117
// collect header cells
117118
const headerCells = splitTableRow(line)
118119
i += 2

src/components/MarkdownView/MarkdownView.module.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,21 @@
3030
margin-bottom: 8px;
3131
margin-top: 16px;
3232
}
33+
34+
.markdownView table {
35+
border-collapse: collapse;
36+
width: 100%;
37+
}
38+
.markdownView th,
39+
.markdownView td {
40+
border-bottom: 1px solid #333;
41+
padding: 8px;
42+
text-align: left;
43+
}
44+
.markdownView th {
45+
border-bottom: 2px solid #333;
46+
font-weight: 500;
47+
}
48+
.markdownView tr:last-child td {
49+
border-bottom: none;
50+
}

0 commit comments

Comments
 (0)