Skip to content

Commit

Permalink
fix: add image component caption as children (#918)
Browse files Browse the repository at this point in the history
[![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
  • Loading branch information
jennspencer authored Jun 27, 2024
1 parent e680bd8 commit 59e1bd3
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 30 deletions.
15 changes: 15 additions & 0 deletions __tests__/transformers/embeds.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import { mdast } from '../../index';

describe('embeds transformer', () => {
it('converts a link with a title of "@embed" to an embed-block', () => {
const md = `
[alt](https://example.com/cool.pdf "@embed")
`;
const tree = mdast(md);

expect(tree.children[0].type).toBe('embed-block');
expect(tree.children[0].data.hProperties.title).toBe('alt');
});

});
23 changes: 23 additions & 0 deletions __tests__/transformers/images.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { mdast } from '../../index';

describe('images transformer', () => {
it('converts single children images of paragraphs to an image-block', () => {
const md = `
![alt](https://example.com/image.jpg)
`;
const tree = mdast(md);

expect(tree.children[0].type).toBe('image-block');
expect(tree.children[0].data.hProperties.src).toBe('https://example.com/image.jpg');
});

it('can parse the caption markdown to children', () => {
const md = `
<Image src="https://example.com/image.jpg" caption="**this** is *markdown*" />
`;
const tree = mdast(md);

expect(tree.children[0].children[0].children[0].type).toBe('strong');
expect(tree.children[0].children[0].children[2].type).toBe('emphasis');
});
});
7 changes: 5 additions & 2 deletions components/Image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ interface ImageProps {
title?: string;
width?: string;
lazy?: boolean;
children?: [React.ReactElement];
}

const Image = (Props: ImageProps) => {
const [lightbox, setLightbox] = React.useState(false);
const {
align = '',
alt = '',
Expand All @@ -26,8 +26,11 @@ const Image = (Props: ImageProps) => {
title = '',
width = 'auto',
lazy = false,
children,
} = Props;

const [lightbox, setLightbox] = React.useState(false);

if (className === 'emoji') {
return <img src={src} width={width} height={height} title={title} alt={alt} loading={lazy ? 'lazy' : 'eager'} />;
}
Expand Down Expand Up @@ -76,7 +79,7 @@ const Image = (Props: ImageProps) => {
alt={alt}
loading={lazy ? 'lazy' : 'eager'}
/>
<figcaption>{caption}</figcaption>
<figcaption>{children || caption}</figcaption>
</figure>
</span>
</span>
Expand Down
69 changes: 45 additions & 24 deletions processor/transform/images.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,56 @@
import { visit } from 'unist-util-visit';
import { Node, Paragraph, Parents } from 'mdast';
import { MdxJsxFlowElement } from 'mdast-util-mdx';

import { NodeTypes } from '../../enums';
import { ImageBlock } from '../../types';
import { getAttrs } from '../utils';
import { mdast } from '../../lib';

const imageTransformer = () => {
return (tree: Node) => {
visit(tree, 'paragraph', (node: Paragraph, i: number, parent: Parents) => {
// check if inline or already transformed
if (parent.type !== 'root' || node.children?.length > 1 || node.children[0].type !== 'image') return;
const [{ alt, url, title }] = node.children as any;

const newNode = {
type: NodeTypes.imageBlock,
alt,
title,
url,
data: {
hName: 'img',
hProperties: {
...(alt && { alt }),
src: url,
...(title && { title }),
},
const imageTransformer = () => (tree: Node) => {
visit(tree, 'paragraph', (node: Paragraph, i: number, parent: Parents) => {
// check if inline
if (
parent.type !== 'root' ||
node.children?.length > 1 ||
node.children[0].type !== 'image'
)
return;

const [{ alt, url, title }] = node.children as any;

const newNode = {
type: NodeTypes.imageBlock,
alt,
title,
url,
children: [],
data: {
hName: 'img',
hProperties: {
...(alt && { alt }),
src: url,
...(title && { title }),
},
position: node.position,
} as ImageBlock;
},
position: node.position,
} as ImageBlock;

parent.children.splice(i, 1, newNode);
});

const isImage = (node: MdxJsxFlowElement) => node.name === 'Image';

visit(tree, isImage, (node: MdxJsxFlowElement) => {
const attrs = getAttrs<ImageBlock['data']['hProperties']>(node);

if (attrs.caption) {
node.children = mdast(attrs.caption).children;
}

});

parent.children.splice(i, 1, newNode);
});
};
return tree;
};

export default imageTransformer;
4 changes: 2 additions & 2 deletions processor/transform/inject-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { visit } from 'unist-util-visit';
import { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx';
import { Transform } from 'mdast-util-from-markdown';
import { Parents } from 'mdast';
import { isMDXElement } from '../utils';

interface Options {
components?: MdastComponents;
Expand All @@ -18,8 +19,7 @@ const inject =
};

const injectComponents = (opts: Options) => (): Transform => tree => {
visit(tree, 'mdxJsxFlowElement', inject(opts));
visit(tree, 'mdxJsxTextElement', inject(opts));
visit(tree, isMDXElement, inject(opts));

return tree;
};
Expand Down
5 changes: 4 additions & 1 deletion processor/transform/readme-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { Callout, EmbedBlock, HTMLBlock, ImageBlock } from 'types';
import { visit } from 'unist-util-visit';

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

const types = {
Callout: NodeTypes['callout'],
Code: 'code',
CodeTabs: NodeTypes['codeTabs'],
EmbedBlock: NodeTypes['embed-block'],
Glossary: NodeTypes['glossary'],
ImageBlock: NodeTypes['image-block'],
ImageBlock: NodeTypes.imageBlock,
HTMLBlock: NodeTypes.htmlBlock,
Table: 'table',
Variable: NodeTypes['variable'],
Expand Down Expand Up @@ -52,9 +53,11 @@ const coerceJsxToMd =
const { position } = node;
const { alt = '', url, title = null } = getAttrs<Pick<ImageBlock, 'alt' | 'title' | 'url'>>(node);
const attrs = getAttrs<ImageBlock['data']['hProperties']>(node);

const mdNode: ImageBlock = {
alt,
position,
children: attrs.caption ? mdast(attrs.caption).children : node.children as any,
title,
type: NodeTypes.imageBlock,
url: url || attrs.src,
Expand Down
2 changes: 1 addition & 1 deletion types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ interface HTMLBlock extends Node {
};
}

interface ImageBlock extends Node {
interface ImageBlock extends Parent {
type: NodeTypes.imageBlock;
url: string;
alt: string;
Expand Down

0 comments on commit 59e1bd3

Please sign in to comment.