Skip to content

Commit 59e1bd3

Browse files
authored
fix: add image component caption as children (#918)
[![PR App][icn]][demo] | Fix RM-10049 :-------------------:|:----------: ## 🧰 Changes cleaner way to add/update image caption ## 🧬 QA & Testing - [Broken on production][prod]. - [Working in this PR app][demo]. [demo]: https://markdown-pr-PR_NUMBER.herokuapp.com [prod]: https://SUBDOMAIN.readme.io [icn]: https://user-images.githubusercontent.com/886627/160426047-1bee9488-305a-4145-bb2b-09d8b757d38a.svg
1 parent e680bd8 commit 59e1bd3

File tree

7 files changed

+95
-30
lines changed

7 files changed

+95
-30
lines changed

__tests__/transformers/embeds.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
import { mdast } from '../../index';
3+
4+
describe('embeds transformer', () => {
5+
it('converts a link with a title of "@embed" to an embed-block', () => {
6+
const md = `
7+
[alt](https://example.com/cool.pdf "@embed")
8+
`;
9+
const tree = mdast(md);
10+
11+
expect(tree.children[0].type).toBe('embed-block');
12+
expect(tree.children[0].data.hProperties.title).toBe('alt');
13+
});
14+
15+
});

__tests__/transformers/images.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { mdast } from '../../index';
2+
3+
describe('images transformer', () => {
4+
it('converts single children images of paragraphs to an image-block', () => {
5+
const md = `
6+
![alt](https://example.com/image.jpg)
7+
`;
8+
const tree = mdast(md);
9+
10+
expect(tree.children[0].type).toBe('image-block');
11+
expect(tree.children[0].data.hProperties.src).toBe('https://example.com/image.jpg');
12+
});
13+
14+
it('can parse the caption markdown to children', () => {
15+
const md = `
16+
<Image src="https://example.com/image.jpg" caption="**this** is *markdown*" />
17+
`;
18+
const tree = mdast(md);
19+
20+
expect(tree.children[0].children[0].children[0].type).toBe('strong');
21+
expect(tree.children[0].children[0].children[2].type).toBe('emphasis');
22+
});
23+
});

components/Image/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ interface ImageProps {
1111
title?: string;
1212
width?: string;
1313
lazy?: boolean;
14+
children?: [React.ReactElement];
1415
}
1516

1617
const Image = (Props: ImageProps) => {
17-
const [lightbox, setLightbox] = React.useState(false);
1818
const {
1919
align = '',
2020
alt = '',
@@ -26,8 +26,11 @@ const Image = (Props: ImageProps) => {
2626
title = '',
2727
width = 'auto',
2828
lazy = false,
29+
children,
2930
} = Props;
3031

32+
const [lightbox, setLightbox] = React.useState(false);
33+
3134
if (className === 'emoji') {
3235
return <img src={src} width={width} height={height} title={title} alt={alt} loading={lazy ? 'lazy' : 'eager'} />;
3336
}
@@ -76,7 +79,7 @@ const Image = (Props: ImageProps) => {
7679
alt={alt}
7780
loading={lazy ? 'lazy' : 'eager'}
7881
/>
79-
<figcaption>{caption}</figcaption>
82+
<figcaption>{children || caption}</figcaption>
8083
</figure>
8184
</span>
8285
</span>

processor/transform/images.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,56 @@
11
import { visit } from 'unist-util-visit';
22
import { Node, Paragraph, Parents } from 'mdast';
3+
import { MdxJsxFlowElement } from 'mdast-util-mdx';
34

45
import { NodeTypes } from '../../enums';
56
import { ImageBlock } from '../../types';
7+
import { getAttrs } from '../utils';
8+
import { mdast } from '../../lib';
69

7-
const imageTransformer = () => {
8-
return (tree: Node) => {
9-
visit(tree, 'paragraph', (node: Paragraph, i: number, parent: Parents) => {
10-
// check if inline or already transformed
11-
if (parent.type !== 'root' || node.children?.length > 1 || node.children[0].type !== 'image') return;
12-
const [{ alt, url, title }] = node.children as any;
13-
14-
const newNode = {
15-
type: NodeTypes.imageBlock,
16-
alt,
17-
title,
18-
url,
19-
data: {
20-
hName: 'img',
21-
hProperties: {
22-
...(alt && { alt }),
23-
src: url,
24-
...(title && { title }),
25-
},
10+
const imageTransformer = () => (tree: Node) => {
11+
visit(tree, 'paragraph', (node: Paragraph, i: number, parent: Parents) => {
12+
// check if inline
13+
if (
14+
parent.type !== 'root' ||
15+
node.children?.length > 1 ||
16+
node.children[0].type !== 'image'
17+
)
18+
return;
19+
20+
const [{ alt, url, title }] = node.children as any;
21+
22+
const newNode = {
23+
type: NodeTypes.imageBlock,
24+
alt,
25+
title,
26+
url,
27+
children: [],
28+
data: {
29+
hName: 'img',
30+
hProperties: {
31+
...(alt && { alt }),
32+
src: url,
33+
...(title && { title }),
2634
},
27-
position: node.position,
28-
} as ImageBlock;
35+
},
36+
position: node.position,
37+
} as ImageBlock;
38+
39+
parent.children.splice(i, 1, newNode);
40+
});
41+
42+
const isImage = (node: MdxJsxFlowElement) => node.name === 'Image';
43+
44+
visit(tree, isImage, (node: MdxJsxFlowElement) => {
45+
const attrs = getAttrs<ImageBlock['data']['hProperties']>(node);
46+
47+
if (attrs.caption) {
48+
node.children = mdast(attrs.caption).children;
49+
}
50+
51+
});
2952

30-
parent.children.splice(i, 1, newNode);
31-
});
32-
};
53+
return tree;
3354
};
3455

3556
export default imageTransformer;

processor/transform/inject-components.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { visit } from 'unist-util-visit';
33
import { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx';
44
import { Transform } from 'mdast-util-from-markdown';
55
import { Parents } from 'mdast';
6+
import { isMDXElement } from '../utils';
67

78
interface Options {
89
components?: MdastComponents;
@@ -18,8 +19,7 @@ const inject =
1819
};
1920

2021
const injectComponents = (opts: Options) => (): Transform => tree => {
21-
visit(tree, 'mdxJsxFlowElement', inject(opts));
22-
visit(tree, 'mdxJsxTextElement', inject(opts));
22+
visit(tree, isMDXElement, inject(opts));
2323

2424
return tree;
2525
};

processor/transform/readme-components.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import { Callout, EmbedBlock, HTMLBlock, ImageBlock } from 'types';
77
import { visit } from 'unist-util-visit';
88

99
import { getAttrs, isMDXElement, getChildren, formatHTML } from '../utils';
10+
import { mdast } from '../../lib';
1011

1112
const types = {
1213
Callout: NodeTypes['callout'],
1314
Code: 'code',
1415
CodeTabs: NodeTypes['codeTabs'],
1516
EmbedBlock: NodeTypes['embed-block'],
1617
Glossary: NodeTypes['glossary'],
17-
ImageBlock: NodeTypes['image-block'],
18+
ImageBlock: NodeTypes.imageBlock,
1819
HTMLBlock: NodeTypes.htmlBlock,
1920
Table: 'table',
2021
Variable: NodeTypes['variable'],
@@ -52,9 +53,11 @@ const coerceJsxToMd =
5253
const { position } = node;
5354
const { alt = '', url, title = null } = getAttrs<Pick<ImageBlock, 'alt' | 'title' | 'url'>>(node);
5455
const attrs = getAttrs<ImageBlock['data']['hProperties']>(node);
56+
5557
const mdNode: ImageBlock = {
5658
alt,
5759
position,
60+
children: attrs.caption ? mdast(attrs.caption).children : node.children as any,
5861
title,
5962
type: NodeTypes.imageBlock,
6063
url: url || attrs.src,

types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ interface HTMLBlock extends Node {
5656
};
5757
}
5858

59-
interface ImageBlock extends Node {
59+
interface ImageBlock extends Parent {
6060
type: NodeTypes.imageBlock;
6161
url: string;
6262
alt: string;

0 commit comments

Comments
 (0)