Skip to content

Commit 6524cba

Browse files
authored
feat: parse code blocks children to plain text (#17)
* feat: parse code blocks children to plain text * use contat
1 parent f41d99f commit 6524cba

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ You can provide your own React components to the renderer, both for blocks and m
4040

4141
- **Blocks** are full-width elements, usually at the root of the content. The available options are:
4242
- paragraph
43-
- heading
44-
- list
43+
- heading (receives `level`)
44+
- list (receives `format`)
4545
- quote
46-
- code
47-
- image
48-
- link
46+
- code (receives `plainText`)
47+
- image (receives `image`)
48+
- link (receives `url`)
4949
- **Modifiers** are inline elements, used to change the appearance of fragments of text within a block. The available options are:
5050
- bold
5151
- italic

src/Block.tsx

+34-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,37 @@ interface BlockProps {
1111

1212
const voidTypes = ['image'];
1313

14+
/**
15+
* Add props that are specific to a block type, and not present in that node type
16+
*/
17+
const augmentProps = (content: Node) => {
18+
const { children: childrenNodes, type, ...props } = content;
19+
20+
if (type === 'code') {
21+
// Builds a plain text string from an array of nodes, regardless of links or modifiers
22+
const getPlainText = (children: typeof childrenNodes): string => {
23+
return children.reduce((currentPlainText, node) => {
24+
if (node.type === 'text') {
25+
return currentPlainText.concat(node.text);
26+
}
27+
28+
if (node.type === 'link') {
29+
return currentPlainText.concat(getPlainText(node.children));
30+
}
31+
32+
return currentPlainText;
33+
}, '');
34+
};
35+
36+
return {
37+
...props,
38+
plainText: getPlainText(content.children),
39+
};
40+
}
41+
42+
return props;
43+
};
44+
1445
const Block = ({ content }: BlockProps) => {
1546
const { children: childrenNodes, type, ...props } = content;
1647

@@ -34,8 +65,10 @@ const Block = ({ content }: BlockProps) => {
3465
return <BlockComponent {...props} />;
3566
}
3667

68+
const augmentedProps = augmentProps(content);
69+
3770
return (
38-
<BlockComponent {...props}>
71+
<BlockComponent {...augmentedProps}>
3972
{childrenNodes.map((childNode, index) => {
4073
if (childNode.type === 'text') {
4174
const { type: _type, ...childNodeProps } = childNode;

src/BlocksRenderer.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ type RootNode =
8585
type Node = RootNode | NonTextInlineNode;
8686

8787
// Util to convert a node to the props of the corresponding React component
88-
type GetPropsFromNode<T> = Omit<T, 'type' | 'children'> & { children?: React.ReactNode };
88+
type GetPropsFromNode<T> = Omit<T, 'type' | 'children'> & {
89+
children?: React.ReactNode;
90+
// For code blocks, add a plainText property that is created by this renderer
91+
plainText?: T extends { type: 'code' } ? string : never;
92+
};
8993

9094
// Map of all block types to their matching React component
9195
type BlocksComponents = {
@@ -118,7 +122,7 @@ const defaultComponents: ComponentsContextValue = {
118122
quote: (props) => <blockquote>{props.children}</blockquote>,
119123
code: (props) => (
120124
<pre>
121-
<code>{props.children}</code>
125+
<code>{props.plainText}</code>
122126
</pre>
123127
),
124128
heading: ({ level, children }) => {

tests/BlocksRenderer.test.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -386,5 +386,37 @@ describe('BlocksRenderer', () => {
386386

387387
console.warn = originalWarn;
388388
});
389+
390+
it('parses code blocks to plain text', () => {
391+
render(
392+
<BlocksRenderer
393+
content={[
394+
{
395+
type: 'code',
396+
children: [
397+
{
398+
type: 'text',
399+
text: 'const a = 1;',
400+
},
401+
{
402+
type: 'link',
403+
url: 'https://test.com',
404+
children: [{ type: 'text', text: 'const b = 2;', bold: true }],
405+
},
406+
],
407+
},
408+
]}
409+
blocks={{
410+
code: (props) => (
411+
<pre>
412+
<code>{props.plainText}</code>
413+
</pre>
414+
),
415+
}}
416+
/>
417+
);
418+
419+
expect(screen.getByText('const a = 1;const b = 2;')).toBeInTheDocument();
420+
});
389421
});
390422
});

0 commit comments

Comments
 (0)