Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: tailwind #1052

Merged
merged 24 commits into from
Feb 10, 2025
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.
1 change: 1 addition & 0 deletions __tests__/browser/markdown.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('visual regression tests', () => {
'tables',
'codeBlockTests',
'tableOfContentsTests',
'tailwindRootTests',
'varsTest',
];

Expand Down
8 changes: 4 additions & 4 deletions __tests__/compilers/compatability.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'node:fs';
import { vi } from 'vitest';
import { render, screen } from '@testing-library/react';

import { mdx, migrate, compile, run } from '../../index';
import { mdx, compile, run } from '../../index';
import { migrate } from '../helpers';

describe('compatability with RDMD', () => {
Expand Down Expand Up @@ -226,7 +226,7 @@ This is an image: <img src="http://example.com/#\\>" >

it('should wrap raw <style> tags in an <HTMLBlock>', async () => {
const converted = migrate(rawStyle);
const compiled = compile(converted);
const compiled = await compile(converted);
const Component = (await run(compiled)).default;
render(
<div className="markdown-body">
Expand All @@ -238,7 +238,7 @@ This is an image: <img src="http://example.com/#\\>" >

it('should wrap raw <script> tags in an <HTMLBlock>', async () => {
const converted = migrate(rawScript);
const compiled = compile(converted);
const compiled = await compile(converted);
const Component = (await run(compiled)).default;
render(
<div className="markdown-body">
Expand All @@ -254,7 +254,7 @@ This is an image: <img src="http://example.com/#\\>" >
*/
const spy = vi.spyOn(console, 'log');
const converted = migrate(magicScript);
const compiled = compile(converted);
const compiled = await compile(converted);
const Component = await run(compiled);
render(
<div className="markdown-body">
Expand Down
2 changes: 1 addition & 1 deletion __tests__/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const silenceConsole =
};

export const execute = async (doc: string, compileOpts = {}, runOpts = {}, { getDefault = true } = {}) => {
const code = compile(doc, compileOpts);
const code = await compile(doc, compileOpts);
const mod = await run(code, runOpts);

return getDefault ? mod.default : mod;
Expand Down
50 changes: 46 additions & 4 deletions __tests__/plugins/toc.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('toc transformer', () => {

## Second Subheading
`;
const { Toc } = await run(compile(md));
const { Toc } = await run(await compile(md));

render(<Toc />);

Expand All @@ -35,10 +35,10 @@ describe('toc transformer', () => {
CommonInfo: '## Common Heading',
};
const executed = {
CommonInfo: await run(compile('## Common Heading')),
CommonInfo: await run(await compile('## Common Heading')),
};

const { Toc } = await run(compile(md, { components }), { components: executed });
const { Toc } = await run(await compile(md, { components }), { components: executed });

render(<Toc />);

Expand All @@ -51,11 +51,53 @@ describe('toc transformer', () => {
const md = `
# [Title](http://example.com)
`;
const { Toc } = await run(compile(md));
const { Toc } = await run(await compile(md));

render(<Toc />);

expect(screen.findByText('Title')).toBeDefined();
expect(screen.queryByText('[', { exact: false })).toBeNull();
});

it('does not inject a toc if one already exists', async () => {
const md = `
## Test Heading

export const toc = [
{
"type": "element",
"tagName": "h2",
"properties": {
"id": "test-heading"
},
"children": [
{
"type": "text",
"value": "Modified Table",
}
],
}
]
`;

const { toc } = await run(await compile(md));

expect(toc).toMatchInlineSnapshot(`
[
{
"children": [
{
"type": "text",
"value": "Modified Table",
},
],
"properties": {
"id": "test-heading",
},
"tagName": "h2",
"type": "element",
},
]
`);
});
});
49 changes: 49 additions & 0 deletions __tests__/transformers/tailwind.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { compile, run } from '../../index';

describe('tailwind transformer', () => {
it('should parse a stylesheet', async () => {
const testComponent = `
export const Styled = () => <div className="bg-blue-500 text-white p-4">Hello, World!</div>;
`;
const md = `<Styled />`;

const TestComponent = await run(await compile(testComponent));
const { stylesheet } = await run(
await compile(md, { components: { TestComponent: testComponent, Styled: testComponent }, useTailwind: true }),
{
components: { TestComponent },
},
);

// @fixme: I can't get vitest to bundle css as a string, so this at least
// asserts that the stylesheet is building?
expect(stylesheet).toMatchInlineSnapshot(`
"/*! tailwindcss v4.0.3 | MIT License | https://tailwindcss.com */
@layer theme, base, components, utilities;
@layer theme;
@layer utilities;
"
`);
});

it('should not throw an exception if a stylesheet is already defined', async () => {
const testComponent = `
export const Styled = () => <div className="bg-blue-500 text-white p-4">Hello, World!</div>;
`;
const md = `
<Styled />

export const stylesheet = ".test { color: red; }";
`;

const TestComponent = await run(await compile(testComponent));
const { stylesheet } = await run(
await compile(md, { components: { TestComponent: testComponent, Styled: testComponent }, useTailwind: true }),
{
components: { TestComponent },
},
);

expect(stylesheet).toMatchInlineSnapshot(`".test { color: red; }"`);
});
});
16 changes: 9 additions & 7 deletions components/CodeTabs/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
@import '~codemirror/theme/neo.css';

@mixin CodeTabs {
$bgc-pre: #F6F8FA;
$bgc-pre-dark: #242E34;
$bgc-pre: #f6f8fa;
$bgc-pre-dark: #242e34;
$bgc-bar: darken(desaturate($bgc-pre, 17.46), 4.31);
$bgc-bar-dark: lighten(desaturate($bgc-pre-dark, 17.46), 4.31);
$radius: var(--md-code-radius, var(--markdown-radius, 3px));
Expand Down Expand Up @@ -36,18 +36,18 @@
}
button {
white-space: nowrap;
transition: .123s ease;
transition: 0.123s ease;
-webkit-appearance: none;
appearance: none;
display: inline-block;
line-height: 2;
padding: .5em 1em;
padding: 0.5em 1em;
border: none;
background: transparent;
outline: none;
color: inherit;
font: inherit;
font-size: .75em;
font-size: 0.75em;
cursor: pointer;
}
}
Expand All @@ -70,15 +70,17 @@
}

&-toolbar button:not(.CodeTabs_active):hover {
background: rgba(0, 0, 0, .075);
background: rgba(0, 0, 0, 0.075);
}

pre {
border-radius: 0 0 $radius $radius !important;
background: $bgc-pre;
background: var(--md-code-background, $bgc-pre);
margin-bottom: 0;
&:not(.CodeTabs_active) { display: none }
&:not(.CodeTabs_active) {
display: none;
}
}

&.theme-dark pre {
Expand Down
18 changes: 18 additions & 0 deletions components/Style/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { createPortal } from 'react-dom';

interface Props {
stylesheet?: string;
}

const Style = ({ stylesheet }: Props) => {
const hasDom = typeof document !== 'undefined';

if (!stylesheet) {
return null;
}

return hasDom ? createPortal(<style>{stylesheet}</style>, document.head) : <style>{stylesheet}</style>;
};

export default Style;
9 changes: 9 additions & 0 deletions components/TailwindRoot/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

const TailwindRoot = ({ children, flow }) => {
const Tag = flow ? 'div' : 'span';

return <Tag className="readme-tailwind">{children}</Tag>;
};

export default TailwindRoot;
2 changes: 2 additions & 0 deletions components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export { default as Glossary } from './Glossary';
export { default as HTMLBlock } from './HTMLBlock';
export { default as Heading } from './Heading';
export { default as Image } from './Image';
export { default as Style } from './Style';
export { default as Table } from './Table';
export { default as Tabs, Tab } from './Tabs';
export { default as TableOfContents } from './TableOfContents';
export { default as TailwindRoot } from './TailwindRoot';
7 changes: 7 additions & 0 deletions docs/tailwind-root-tests.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Styling Custom Components with Tailwind

```
<StyledComponent />
```

<StyledComponent />
36 changes: 12 additions & 24 deletions example/Doc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,19 @@ import * as mdx from '../index';
import docs from './docs';
import RenderError from './RenderError';
import { MDXContent } from 'mdx/types';

const components = {
Demo: `
## This is a Demo Component!

> 📘 It can render JSX components!
`,
Test: `
export const Test = ({ color = 'thistle' } = {}) => {
return <div style={{ backgroundColor: color }}>
Hello, World!
</div>;
};

export default Test;
`,
MultipleExports: `
export const One = () => "One";

export const Two = () => "Two";
`,
};
import components from './components';

const executedComponents = {};
let componentsByExport = { ...components };
Object.entries(components).forEach(async ([tag, body]) => {
executedComponents[tag] = await mdx.run(mdx.compile(body));
const mod = await mdx.run(await mdx.compile(body));

executedComponents[tag] = mod;
Object.keys(mod).forEach(subTag => {
if (['toc', 'Toc', 'default', 'stylesheet'].includes(subTag)) return;

componentsByExport[subTag] = body;
});
});

const terms = [
Expand Down Expand Up @@ -86,7 +73,8 @@ const Doc = () => {
};

try {
const code = mdx.compile(doc, { ...opts, components });
// @ts-ignore
const code = await mdx.compile(doc, { ...opts, components: componentsByExport, useTailwind: true });
const content = await mdx.run(code, { components: executedComponents, terms, variables });

setError(() => null);
Expand Down
30 changes: 30 additions & 0 deletions example/components.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const components = {
Demo: `
## This is a Demo Component!

> 📘 It can render JSX components!
`,
Test: `
export const Test = ({ color = 'thistle' } = {}) => {
return <div style={{ backgroundColor: color }}>
Hello, World!
</div>;
};

export default Test;
`,
MultipleExports: `
export const One = () => "One";

export const Two = () => "Two";
`,
TailwindRootTest: `
export const StyledComponent = () => {
return <div className="bg-blue-500 text-white p-4">
Hello, World!
</div>;
}
`,
};

export default components;
3 changes: 3 additions & 0 deletions example/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import tableOfContentsTests from '../docs/table-of-contents-tests.md';
// @ts-ignore
import tables from '../docs/tables.md';
// @ts-ignore
import tailwindRootTests from '../docs/tailwind-root-tests.mdx';
// @ts-ignore
import varsTest from '../docs/variable-tests.md';

const lowerCase = (str: string) =>
Expand All @@ -59,6 +61,7 @@ const fixtures = Object.entries({
sanitizingTests,
tableOfContentsTests,
tables,
tailwindRootTests,
varsTest,
}).reduce((memo, [sym, doc]) => {
const name = lowerCase(sym);
Expand Down
Loading