Skip to content

Commit

Permalink
refactor(theme): split admonitions, make swizzle easier, better retro…
Browse files Browse the repository at this point in the history
…compatibility (#7945)

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
  • Loading branch information
slorber and Josh-Cena committed Sep 7, 2022
1 parent f141552 commit 6f63ffe
Show file tree
Hide file tree
Showing 32 changed files with 914 additions and 236 deletions.
2 changes: 1 addition & 1 deletion packages/docusaurus-mdx-loader/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export type MDXPlugin =
[Plugin<any[]>, any] | Plugin<any[]>;

export type MDXOptions = {
admonitions: boolean | AdmonitionOptions;
admonitions: boolean | Partial<AdmonitionOptions>;
remarkPlugins: MDXPlugin[];
rehypePlugins: MDXPlugin[];
beforeDefaultRemarkPlugins: MDXPlugin[];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`admonitions remark plugin base 1`] = `
exports[`admonitions remark plugin add custom keyword 1`] = `
"<p>The blog feature enables you to deploy in no time a full-featured blog.</p>
<admonition title="Sample Title" type="info"><p>Check the <a href="./api/plugins/plugin-content-blog.md">Blog Plugin API Reference documentation</a> for an exhaustive list of options.</p></admonition>
<h2>Initial setup {#initial-setup}</h2>
Expand All @@ -11,11 +11,9 @@ exports[`admonitions remark plugin base 1`] = `
<p>++++</p>"
`;

exports[`admonitions remark plugin custom keywords 1`] = `
exports[`admonitions remark plugin base 1`] = `
"<p>The blog feature enables you to deploy in no time a full-featured blog.</p>
<p>:::info Sample Title</p>
<p>Check the <a href="./api/plugins/plugin-content-blog.md">Blog Plugin API Reference documentation</a> for an exhaustive list of options.</p>
<p>:::</p>
<admonition title="Sample Title" type="info"><p>Check the <a href="./api/plugins/plugin-content-blog.md">Blog Plugin API Reference documentation</a> for an exhaustive list of options.</p></admonition>
<h2>Initial setup {#initial-setup}</h2>
<p>To set up your site's blog, start by creating a <code>blog</code> directory.</p>
<admonition type="tip"><p>Use the <strong><a href="introduction.md#fast-track">Fast Track</a></strong> to understand Docusaurus in <strong>5 minutes ⏱</strong>!</p><p>Use <strong><a href="https://docusaurus.new">docusaurus.new</a></strong> to test Docusaurus immediately in your browser!</p></admonition>
Expand All @@ -38,7 +36,33 @@ exports[`admonitions remark plugin custom tag 1`] = `
<admonition type="tip"><p>Admonition with different syntax</p></admonition>"
`;

exports[`admonitions remark plugin default behavior for custom keyword 1`] = `
"<p>The blog feature enables you to deploy in no time a full-featured blog.</p>
<p>:::info Sample Title</p>
<p>Check the <a href="./api/plugins/plugin-content-blog.md">Blog Plugin API Reference documentation</a> for an exhaustive list of options.</p>
<p>:::</p>
<h2>Initial setup {#initial-setup}</h2>
<p>To set up your site's blog, start by creating a <code>blog</code> directory.</p>
<admonition type="tip"><p>Use the <strong><a href="introduction.md#fast-track">Fast Track</a></strong> to understand Docusaurus in <strong>5 minutes ⏱</strong>!</p><p>Use <strong><a href="https://docusaurus.new">docusaurus.new</a></strong> to test Docusaurus immediately in your browser!</p></admonition>
<p>++++tip</p>
<p>Admonition with different syntax</p>
<p>++++</p>"
`;

exports[`admonitions remark plugin interpolation 1`] = `
"<p>Test admonition with interpolated title/body</p>
<admonition type="tip"><mdxAdmonitionTitle>My <code>interpolated</code> <strong>title</strong> &#x3C;button style={{color: "red"}} onClick={() => alert("click")}>test</mdxAdmonitionTitle><p><code>body</code> <strong>interpolated</strong> content</p></admonition>"
`;

exports[`admonitions remark plugin replace custom keyword 1`] = `
"<p>The blog feature enables you to deploy in no time a full-featured blog.</p>
<p>:::info Sample Title</p>
<p>Check the <a href="./api/plugins/plugin-content-blog.md">Blog Plugin API Reference documentation</a> for an exhaustive list of options.</p>
<p>:::</p>
<h2>Initial setup {#initial-setup}</h2>
<p>To set up your site's blog, start by creating a <code>blog</code> directory.</p>
<admonition type="tip"><p>Use the <strong><a href="introduction.md#fast-track">Fast Track</a></strong> to understand Docusaurus in <strong>5 minutes ⏱</strong>!</p><p>Use <strong><a href="https://docusaurus.new">docusaurus.new</a></strong> to test Docusaurus immediately in your browser!</p></admonition>
<p>++++tip</p>
<p>Admonition with different syntax</p>
<p>++++</p>"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,34 @@ describe('admonitions remark plugin', () => {
expect(result).toMatchSnapshot();
});

it('custom keywords', async () => {
const result = await processFixture('base', {keywords: ['tip']});
it('default behavior for custom keyword', async () => {
const result = await processFixture('base', {
keywords: ['tip'],
// extendDefaults: false, // By default we don't extend
});
expect(result).toMatchSnapshot();
});

it('add custom keyword', async () => {
const result = await processFixture('base', {
keywords: ['tip'],
extendDefaults: true,
});
expect(result).toMatchSnapshot();
});

it('replace custom keyword', async () => {
const result = await processFixture('base', {
keywords: ['tip'],
extendDefaults: false,
});
expect(result).toMatchSnapshot();
});

it('custom tag', async () => {
const result = await processFixture('base', {tag: '++++'});
const result = await processFixture('base', {
tag: '++++',
});
expect(result).toMatchSnapshot();
});

Expand Down
21 changes: 19 additions & 2 deletions packages/docusaurus-mdx-loader/src/remark/admonitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ import type {Literal} from 'mdast';

const NEWLINE = '\n';

// TODO not ideal option shape
// First let upgrade to MDX 2.0
// Maybe we'll want to provide different tags for different admonition types?
// Also maybe rename "keywords" to "types"?
export type AdmonitionOptions = {
tag: string;
keywords: string[];
extendDefaults: boolean;
};

export const DefaultAdmonitionOptions: AdmonitionOptions = {
Expand All @@ -29,16 +34,28 @@ export const DefaultAdmonitionOptions: AdmonitionOptions = {
'important',
'caution',
],
extendDefaults: false, // TODO make it true by default: breaking change
};

function escapeRegExp(s: string): string {
return s.replace(/[-[\]{}()*+?.\\^$|/]/g, '\\$&');
}

function normalizeOptions(
options: Partial<AdmonitionOptions>,
providedOptions: Partial<AdmonitionOptions>,
): AdmonitionOptions {
return {...DefaultAdmonitionOptions, ...options};
const options = {...DefaultAdmonitionOptions, ...providedOptions};

// By default it makes more sense to append keywords to the default ones
// Adding custom keywords is more common than disabling existing ones
if (options.extendDefaults) {
options.keywords = [
...DefaultAdmonitionOptions.keywords,
...options.keywords,
];
}

return options;
}

// This string value does not matter much
Expand Down
139 changes: 138 additions & 1 deletion packages/docusaurus-theme-classic/src/getSwizzleConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,113 @@ import type {SwizzleConfig} from '@docusaurus/types';
export default function getSwizzleConfig(): SwizzleConfig {
return {
components: {
'Admonition/Icon': {
actions: {
eject: 'safe',
wrap: 'forbidden', // Can't wrap a folder
},
description: 'The folder containing all admonition icons',
},
'Admonition/Icon/Caution': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The admonition caution icon',
},
'Admonition/Icon/Danger': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The admonition danger icon',
},
'Admonition/Icon/Info': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The admonition info icon',
},
'Admonition/Icon/Note': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The admonition note icon',
},
'Admonition/Icon/Tip': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description: 'The admonition tip icon',
},
'Admonition/Layout': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The standard admonition layout applied to all default admonition types',
},
'Admonition/Type': {
actions: {
eject: 'safe',
wrap: 'forbidden',
},
description:
'The folder containing all the admonition type components.',
},
'Admonition/Type/Caution': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The component responsible for rendering a :::caution admonition type',
},
'Admonition/Type/Danger': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The component responsible for rendering a :::danger admonition type',
},
'Admonition/Type/Info': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The component responsible for rendering a :::info admonition type',
},
'Admonition/Type/Note': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The component responsible for rendering a :::note admonition type',
},
'Admonition/Type/Tip': {
actions: {
eject: 'safe',
wrap: 'safe',
},
description:
'The component responsible for rendering a :::tip admonition type',
},
'Admonition/Types': {
actions: {
eject: 'safe',
// TODO the swizzle CLI should provide a way to wrap such objects
wrap: 'forbidden',
},
description:
'The object mapping admonition type to a React component.\nUse it to add custom admonition type components, or replace existing ones.\nCan be ejected or wrapped (only manually, see our documentation).',
},
CodeBlock: {
actions: {
eject: 'safe',
Expand All @@ -20,6 +127,14 @@ export default function getSwizzleConfig(): SwizzleConfig {
description:
'The component used to render multi-line code blocks, generally used in Markdown files.',
},
'CodeBlock/Content': {
actions: {
eject: 'unsafe',
wrap: 'forbidden',
},
description:
'The folder containing components responsible for rendering different types of CodeBlock content.',
},
ColorModeToggle: {
actions: {
eject: 'safe',
Expand All @@ -36,6 +151,17 @@ export default function getSwizzleConfig(): SwizzleConfig {
description:
'The component responsible for rendering a list of sidebar items cards.\nNotable used on the category generated-index pages.',
},
'DocItem/TOC': {
actions: {
// Forbidden because it's a parent folder, makes the CLI crash atm
// TODO the CLI should rather support --eject
// Subfolders can be swizzled
eject: 'forbidden',
wrap: 'forbidden',
},
description:
'The DocItem TOC is not directly swizzle-able, but you can swizzle its sub-components.',
},
DocSidebar: {
actions: {
eject: 'unsafe', // Too much technical code in sidebar, not very safe atm
Expand Down Expand Up @@ -101,6 +227,17 @@ export default function getSwizzleConfig(): SwizzleConfig {
},
description: 'The footer logo',
},
Icon: {
actions: {
// Forbidden because it's a parent folder, makes the CLI crash atm
// TODO the CLI should rather support --eject
// Subfolders can be swizzled
eject: 'forbidden',
wrap: 'forbidden',
},
description:
'The Icon folder is not directly swizzle-able, but you can swizzle its sub-components.',
},
'Icon/Arrow': {
actions: {
eject: 'safe',
Expand Down Expand Up @@ -220,7 +357,7 @@ export default function getSwizzleConfig(): SwizzleConfig {
wrap: 'forbidden',
},
description:
'The Navbar item components mapping. Can be ejected to add custom navbar item types. See https://github.com/facebook/docusaurus/issues/7227.',
'The Navbar item components mapping. Can be ejected to add custom navbar item types.\nSee https://github.com/facebook/docusaurus/issues/7227.',
},
NotFound: {
actions: {
Expand Down

0 comments on commit 6f63ffe

Please sign in to comment.