Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions __tests__/compilers/compatability.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { mdx } from '../../index';

describe('compatability with RDMD', () => {
it('compiles variable nodes', () => {
const ast = {
type: 'readme-variable',
text: 'parliament',
data: {
hName: 'readme-variable',
hProperties: {
variable: 'parliament',
},
},
};

expect(mdx(ast).trim()).toBe('<Variable name="parliament" />');
});

it('compiles glossary nodes', () => {
const ast = {
type: 'readme-glossary-item',
data: {
hProperties: {
term: 'parliament',
},
},
};

expect(mdx(ast).trim()).toBe('<Glossary>parliament</Glossary>');
});

it('compiles reusable-content nodes', () => {
const ast = {
type: 'reusable-content',
tag: 'Parliament',
};

expect(mdx(ast).trim()).toBe('<Parliament />');
});

it('compiles html comments to JSX comments', () => {
const ast = {
type: 'html',
value: '<!-- commentable -->',
};

expect(mdx(ast).trim()).toBe('{/* commentable */}');
});
});
2 changes: 1 addition & 1 deletion __tests__/components/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe('Components', () => {
component = await run(code1);
({ container } = render(React.createElement(component)));

expect(container.innerHTML).toMatchInlineSnapshot(`"<blockquote class="callout callout_warn" theme="🚧"><h3 class="callout-heading empty"><span class="callout-icon">🚧</span></h3><p>Callout with no title.</p></blockquote>"`);
expect(container.innerHTML).toMatchInlineSnapshot(`"<blockquote class="callout callout_warn" theme="🚧"><h3 class="callout-heading empty"><span class="callout-icon">🚧</span></h3><p></p><p>Callout with no title.</p></blockquote>"`);

cleanup();
});
Expand Down
11 changes: 9 additions & 2 deletions __tests__/parsers/__snapshots__/callouts.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ exports[`Parse RDMD Callouts > renders an info callout 1`] = `
},
},
"type": "text",
"value": "ℹ️ Info Callout",
"value": "Info Callout",
},
],
"position": {
Expand Down Expand Up @@ -72,6 +72,13 @@ exports[`Parse RDMD Callouts > renders an info callout 1`] = `
"type": "paragraph",
},
],
"data": {
"hName": "Callout",
"hProperties": {
"empty": false,
"icon": "ℹ️",
},
},
"position": {
"end": {
"column": 60,
Expand All @@ -84,7 +91,7 @@ exports[`Parse RDMD Callouts > renders an info callout 1`] = `
"offset": 1,
},
},
"type": "blockquote",
"type": "rdme-callout",
},
],
"position": {
Expand Down
32 changes: 10 additions & 22 deletions __tests__/parsers/callouts.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { mdast } from '../../index';

describe.skip('Parse RDMD Callouts', () => {
describe('Parse RDMD Callouts', () => {
it('renders an info callout', () => {
const text = `
> ℹ️ Info Callout
Expand All @@ -10,18 +10,6 @@ describe.skip('Parse RDMD Callouts', () => {
expect(mdast(text)).toMatchSnapshot();
});

it('supports a default theme', () => {
const text = `
> 🥇 Themeless
>
> Lorem ipsum dolor sit amet consectetur adipisicing elit.`;

const tree = mdast(text);

expect(tree.children[0].type).toBe('rdme-callout');
expect(tree.children[0].data.hProperties.theme).toBe('default');
});

it('parses a callout with no title', () => {
const text = `
> ℹ️
Expand All @@ -31,8 +19,8 @@ describe.skip('Parse RDMD Callouts', () => {
const tree = mdast(text);

expect(tree.children[0].type).toBe('rdme-callout');
expect(tree.children[0].data.hProperties.theme).toBe('info');
expect(tree.children[0].data.hProperties.title).toBe('');
expect(tree.children[0].data.hProperties.icon).toBe('ℹ️');
expect(tree.children[0].data.hProperties.empty).toBe(true);
});

describe('edge cases', () => {
Expand All @@ -44,8 +32,8 @@ describe.skip('Parse RDMD Callouts', () => {
`;

const tree = mdast(text);
expect(tree.children[0].data.hProperties.value).toBe('<span>With html!</span>');
expect(tree.children[0].children[1].children[0].type).toBe('html');
expect(tree.children[0].children[1].children[0].children[0].value).toBe('With html!');
expect(tree.children[0].children[1].children[0].type).toBe('mdxJsxTextElement');
});

it('allows trailing spaces after the icon', () => {
Expand All @@ -56,7 +44,7 @@ describe.skip('Parse RDMD Callouts', () => {
const tree = mdast(text);
expect(tree.children[0].data.hProperties.icon).toBe('🛑');
expect(tree.children[0].children[0].children[0].value).toBe(
'Compact headings must be followed by two line breaks before the following block.'
'Compact headings must be followed by two line breaks before the following block.',
);
});
});
Expand All @@ -83,20 +71,20 @@ describe.skip('Parse RDMD Callouts', () => {
expect(tree.children[0].children[0].children[1].type).toBe('rdme-callout');
});

it('does not require a line break between the title and the body', () => {
it('does require a line break between the title and the body', () => {
const text = `
> 💁 Undocumented Behavior
> Lorem ipsum dolor sit amet consectetur adipisicing elit.`;

const tree = mdast(text);
expect(tree.children[0].data.hProperties.title).toBe(
expect(tree.children[0].children[0].children[0].value).toBe(
`Undocumented Behavior
Lorem ipsum dolor sit amet consectetur adipisicing elit.`
Lorem ipsum dolor sit amet consectetur adipisicing elit.`,
);
});
});

describe.skip('emoji modifier support', () => {
describe('emoji modifier support', () => {
const emojis = ['📘', '🚧', '⚠️', '👍', '✅', '❗️', '❗', '🛑', '⁉️', '‼️', 'ℹ️', '⚠'];

emojis.forEach(emoji => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/transformers/callouts.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mdast, hast } from '../../index';
import { mdast } from '../../index';

describe('callouts transformer', () => {
it('can parse callouts', () => {
Expand Down
5 changes: 4 additions & 1 deletion __tests__/transformers/readme-components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ describe('Readme Components Transformer', () => {
const docs = {
['rdme-callout']: {
md: `> 📘 It works!`,
mdx: `<Callout icon="📘" heading="It works!" />`,
mdx: `
<Callout icon="📘">
It works!
</Callout>`,
},
code: {
md: `
Expand Down
11 changes: 4 additions & 7 deletions components/Callout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ interface Props extends React.PropsWithChildren<React.HTMLAttributes<HTMLQuoteEl
attributes?: {};
icon: string;
theme?: string;
heading?: React.ReactElement;
empty?: boolean;
}

const themes: Record<string, string> = {
Expand All @@ -23,20 +23,17 @@ const themes: Record<string, string> = {
};

const Callout = (props: Props) => {
const { attributes, children, icon } = props;

const { attributes, children, icon, empty } = props;
let theme = props.theme || themes[icon] || 'default';
const [heading, ...body] = Array.isArray(children) ? children : [children];
const empty = !heading.props.children;

return (
// @ts-ignore
<blockquote {...attributes} className={`callout callout_${theme}`} theme={icon}>
<h3 className={`callout-heading${empty ? ' empty' : ''}`}>
<span className="callout-icon">{icon}</span>
{!empty && heading}
{empty || children[0]}
</h3>
{body}
{empty ? children : React.Children.toArray(children).slice(1)}
</blockquote>
);
};
Expand Down
6 changes: 4 additions & 2 deletions docs/callout-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ hidden: true
> - was only rendering
> - one child

<Callout heading="Now with MDX" theme="error" icon="🔥">
### Even supports markdown
<Callout theme="error" icon="🔥">
Now with MDX

### Even supports markdown

Much _wow_
</Callout>
5 changes: 4 additions & 1 deletion enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ export enum NodeTypes {
i = 'i',
image = 'image',
htmlBlock = 'html-block',
embed = 'rdme-embed'
embed = 'rdme-embed',
variable = 'readme-variable',
glossary = 'readme-glossary-item',
reusableContent = 'reusable-content',
}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
},
"peerDependencies": {
"@mdx-js/react": "^3.0.0",
"@readme/variable": "^16.0.0",
"@readme/variable": "^16.1.0",
"@tippyjs/react": "^4.1.0",
"react": "16.x || 17.x || 18.x",
"react-dom": "16.x || 17.x || 18.x"
Expand Down
25 changes: 25 additions & 0 deletions processor/compile/compatibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Html } from 'mdast';
import { NodeTypes } from '../../enums';

type CompatNodes =
| { type: NodeTypes.variable; text: string }
| { type: NodeTypes.glossary; data: { hProperties: { term: string } } }
| { type: NodeTypes.reusableContent; tag: string }
| Html;

const compatibility = (node: CompatNodes) => {
switch (node.type) {
case NodeTypes.variable:
return `<Variable name="${node.text}" />`;
case NodeTypes.glossary:
return `<Glossary>${node.data.hProperties.term}</Glossary>`;
case NodeTypes.reusableContent:
return `<${node.tag} />`;
case 'html':
return node.value.replaceAll(/<!--(.*)-->/g, '{/*$1*/}');
default:
throw new Error('Unhandled node type!');
}
};

export default compatibility;
5 changes: 5 additions & 0 deletions processor/compile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import embed from './embed';
import gemoji from './gemoji';
import htmlBlock from './html-block';
import image from './image';
import compatibility from './compatibility';
import { NodeTypes } from '../../enums';

function compilers() {
Expand All @@ -18,6 +19,10 @@ function compilers() {
[NodeTypes.embed]: embed,
[NodeTypes.htmlBlock]: htmlBlock,
[NodeTypes.image]: image,
[NodeTypes.variable]: compatibility,
[NodeTypes.glossary]: compatibility,
[NodeTypes.reusableContent]: compatibility,
html: compatibility,
};

toMarkdownExtensions.push({ extensions: [{ handlers }] });
Expand Down
17 changes: 10 additions & 7 deletions processor/transform/callouts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ const calloutTransformer = () => {

if (icon && match) {
const heading = startText.slice(match.length);

node.children[0].children[0].value = heading;
node.type = NodeTypes.callout;
node.data = {
hName: 'Callout',
hProperties: {
icon,

Object.assign(node, {
type: NodeTypes.callout,
data: {
hName: 'Callout',
hProperties: {
icon,
empty: !heading.length,
},
},
};
});
}
});
};
Expand Down
8 changes: 3 additions & 5 deletions processor/transform/readme-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,15 @@ const coerceJsxToMd =

parent.children[index] = mdNode;
} else if (node.name === 'Callout') {
const { heading, icon } = attributes<{ heading?: string; icon: string }>(node);

const child = mdast(heading);
const { icon, empty = false } = attributes<{ empty?: boolean; icon: string }>(node);

// @ts-ignore
const mdNode: Callout = {
children: [child?.children?.[0], ...node.children].filter(Boolean) as any,
children: node.children as any,
type: NodeTypes.callout,
data: {
hName: node.name,
hProperties: { icon },
hProperties: { icon, empty },
},
position: node.position,
};
Expand Down
1 change: 1 addition & 0 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type Callout = Omit<Blockquote, 'type'> & {
hName: 'Callout';
hProperties: {
icon: string;
empty: boolean;
};
};
};
Expand Down