Skip to content

Commit 47ddf6f

Browse files
authored
feat: add Markdown component (#325)
1 parent 1797b36 commit 47ddf6f

File tree

6 files changed

+92
-0
lines changed

6 files changed

+92
-0
lines changed

dev/pages/Markdown.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Markdown } from '../../packages/react-components/src/Markdown.js';
2+
3+
const markdown = `
4+
# H1 – Main Title
5+
6+
## H2 – Section Title
7+
8+
### H3 – Sub-section
9+
10+
Plain paragraph text with **bold**, *italic*, ***bold-italic***, ~~strikethrough~~, and \`inline code\`.
11+
12+
> Blockquote:
13+
> This line should render as a quoted paragraph.
14+
15+
1. First ordered item
16+
2. Second ordered item
17+
1. Nested ordered item
18+
19+
- Unordered list item
20+
- Nested unordered item
21+
22+
\`\`\`js
23+
// Fenced code block with syntax highlighting
24+
function greet(name = "world") {
25+
console.log(\`Hello, ${name}!\`);
26+
}
27+
\`\`\`
28+
`;
29+
30+
export default function ButtonPage() {
31+
return <Markdown>{markdown}</Markdown>;
32+
}

package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-components/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"@vaadin/list-box": "24.8.0-alpha16",
6060
"@vaadin/lit-renderer": "24.8.0-alpha16",
6161
"@vaadin/login": "24.8.0-alpha16",
62+
"@vaadin/markdown": "24.8.0-alpha16",
6263
"@vaadin/master-detail-layout": "24.8.0-alpha16",
6364
"@vaadin/menu-bar": "24.8.0-alpha16",
6465
"@vaadin/message-input": "24.8.0-alpha16",
@@ -297,6 +298,10 @@
297298
"types": "./LoginOverlay.d.ts",
298299
"default": "./LoginOverlay.js"
299300
},
301+
"./Markdown.js": {
302+
"types": "./Markdown.d.ts",
303+
"default": "./Markdown.js"
304+
},
300305
"./MasterDetailLayout.js": {
301306
"types": "./MasterDetailLayout.d.ts",
302307
"default": "./MasterDetailLayout.js"
@@ -453,6 +458,7 @@
453458
"./ListBox": "./ListBox.js",
454459
"./LoginForm": "./LoginForm.js",
455460
"./LoginOverlay": "./LoginOverlay.js",
461+
"./Markdown": "./Markdown.js",
456462
"./MasterDetailLayout": "./MasterDetailLayout.js",
457463
"./MenuBar": "./MenuBar.js",
458464
"./Message": "./Message.js",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { forwardRef, type ForwardedRef, type ReactElement, type RefAttributes } from 'react';
2+
import {
3+
Markdown as _Markdown,
4+
type MarkdownProps as _MarkdownProps,
5+
type MarkdownElement,
6+
} from './generated/Markdown.js';
7+
8+
export * from './generated/Markdown.js';
9+
10+
export type MarkdownProps = Partial<Omit<_MarkdownProps, 'children' | 'content'>> &
11+
Readonly<{
12+
children?: string | null;
13+
}>;
14+
15+
function Markdown({ children, ...props }: MarkdownProps, ref: ForwardedRef<MarkdownElement>): ReactElement | null {
16+
return <_Markdown {...props} ref={ref} content={children ?? ''}></_Markdown>;
17+
}
18+
19+
const ForwardedMarkdown = forwardRef(Markdown) as (
20+
props: MarkdownProps & RefAttributes<MarkdownElement>,
21+
) => ReactElement | null;
22+
23+
export { ForwardedMarkdown as Markdown };

test/Markdown.spec.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { afterEach, describe, expect, it } from 'vitest';
2+
import { render } from 'vitest-browser-react';
3+
import { Markdown } from '../packages/react-components/src/Markdown.js';
4+
5+
describe('Markdown', () => {
6+
it('should render child markdown', () => {
7+
render(<Markdown># Foobar</Markdown>);
8+
9+
const markdown = document.querySelector('vaadin-markdown')!;
10+
expect(markdown).to.exist;
11+
expect(markdown.querySelector('h1')).to.exist;
12+
expect(markdown.querySelector('h1')).to.have.text('Foobar');
13+
});
14+
15+
it('should render child string markdown', () => {
16+
render(<Markdown>{'# Foobar'}</Markdown>);
17+
18+
const markdown = document.querySelector('vaadin-markdown')!;
19+
expect(markdown).to.exist;
20+
expect(markdown.querySelector('h1')).to.exist;
21+
expect(markdown.querySelector('h1')).to.have.text('Foobar');
22+
});
23+
});

test/typings/api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import type { SubMenuItem } from '../../packages/react-components/src/MenuBar.js
4444
import { Popover, PopoverElement } from '../../packages/react-components/src/Popover.js';
4545
import { TabSheet, TabSheetElement, TabSheetTab } from '../../packages/react-components/src/TabSheet.js';
4646
import type { TabElement } from '../../packages/react-components/src/Tab.js';
47+
import { Markdown, MarkdownElement } from '../../packages/react-components/src/Markdown.js';
4748

4849
const assertType = <TExpected>(value: TExpected) => value;
4950
const assertOmitted = <C, T>(prop: keyof Omit<C, keyof T>) => prop;
@@ -288,3 +289,8 @@ assertType<string | undefined>(tabSheetTabProps['aria-label']);
288289
assertType<boolean | undefined>(tabSheetTabProps.disabled);
289290
assertOmitted<TabElement, TabSheetTabProps>('value');
290291
assertOmitted<TabElement, TabSheetTabProps>('selected');
292+
293+
const markdownProps = React.createElement(Markdown, {}).props;
294+
type MarkdownProps = typeof markdownProps;
295+
assertType<string | null | undefined>(markdownProps.children);
296+
assertOmitted<MarkdownElement, MarkdownProps>('content');

0 commit comments

Comments
 (0)