mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-13 00:57:53 +02:00
feat(v2): Add playgroundPosition config for live codeblock (#4328)
* docs(v2): Add configuration parameter to allow putting Result before Editor in @docusaurus/theme-live-codeblock * update doc * refactor as playgroundPosition Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
f772c17bfc
commit
98a4b3a65c
9 changed files with 215 additions and 39 deletions
|
@ -16,6 +16,7 @@
|
||||||
"@docusaurus/core": "2.0.0-alpha.70",
|
"@docusaurus/core": "2.0.0-alpha.70",
|
||||||
"@philpl/buble": "^0.19.7",
|
"@philpl/buble": "^0.19.7",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
|
"joi": "^17.4.0",
|
||||||
"parse-numeric-range": "^1.2.0",
|
"parse-numeric-range": "^1.2.0",
|
||||||
"prism-react-renderer": "^1.1.1",
|
"prism-react-renderer": "^1.1.1",
|
||||||
"react-live": "^2.2.3"
|
"react-live": "^2.2.3"
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const {validateThemeConfig, DEFAULT_CONFIG} = require('../validateThemeConfig');
|
||||||
|
|
||||||
|
function testValidateThemeConfig(themeConfig) {
|
||||||
|
function validate(schema, cfg) {
|
||||||
|
const {value, error} = schema.validate(cfg, {
|
||||||
|
convert: false,
|
||||||
|
});
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return validateThemeConfig({validate, themeConfig});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('validateThemeConfig', () => {
|
||||||
|
test('undefined config', () => {
|
||||||
|
const liveCodeBlock = undefined;
|
||||||
|
expect(testValidateThemeConfig({liveCodeBlock})).toEqual({
|
||||||
|
liveCodeBlock: {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unexist config', () => {
|
||||||
|
expect(testValidateThemeConfig({})).toEqual({
|
||||||
|
liveCodeBlock: {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('empty config', () => {
|
||||||
|
const liveCodeBlock = {};
|
||||||
|
expect(testValidateThemeConfig({liveCodeBlock})).toEqual({
|
||||||
|
liveCodeBlock: {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('playgroundPosition top', () => {
|
||||||
|
const liveCodeBlock = {
|
||||||
|
playgroundPosition: 'top',
|
||||||
|
};
|
||||||
|
expect(testValidateThemeConfig({liveCodeBlock})).toEqual({
|
||||||
|
liveCodeBlock: {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
...liveCodeBlock,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('playgroundPosition bottom', () => {
|
||||||
|
const liveCodeBlock = {
|
||||||
|
playgroundPosition: 'bottom',
|
||||||
|
};
|
||||||
|
expect(testValidateThemeConfig({liveCodeBlock})).toEqual({
|
||||||
|
liveCodeBlock: {
|
||||||
|
...DEFAULT_CONFIG,
|
||||||
|
...liveCodeBlock,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('playgroundPosition invalid string', () => {
|
||||||
|
const liveCodeBlock = {playgroundPosition: 'invalid'};
|
||||||
|
expect(() =>
|
||||||
|
testValidateThemeConfig({liveCodeBlock}),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`"\\"liveCodeBlock.playgroundPosition\\" must be one of [top, bottom]"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('playgroundPosition invalid boolean', () => {
|
||||||
|
const liveCodeBlock = {playgroundPosition: true};
|
||||||
|
expect(() =>
|
||||||
|
testValidateThemeConfig({liveCodeBlock}),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`"\\"liveCodeBlock.playgroundPosition\\" must be one of [top, bottom]"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,8 +6,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const {validateThemeConfig} = require('./validateThemeConfig');
|
||||||
|
|
||||||
module.exports = function () {
|
function theme() {
|
||||||
return {
|
return {
|
||||||
name: 'docusaurus-theme-live-codeblock',
|
name: 'docusaurus-theme-live-codeblock',
|
||||||
|
|
||||||
|
@ -25,4 +26,8 @@ module.exports = function () {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
module.exports = theme;
|
||||||
|
|
||||||
|
theme.validateThemeConfig = validateThemeConfig;
|
||||||
|
|
|
@ -11,11 +11,56 @@ import clsx from 'clsx';
|
||||||
import Translate from '@docusaurus/Translate';
|
import Translate from '@docusaurus/Translate';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
import usePrismTheme from '@theme/hooks/usePrismTheme';
|
import usePrismTheme from '@theme/hooks/usePrismTheme';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
function Header({translateId, description, text}) {
|
||||||
|
return (
|
||||||
|
<div className={clsx(styles.playgroundHeader)}>
|
||||||
|
<Translate id={translateId} description={description}>
|
||||||
|
{text}
|
||||||
|
</Translate>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResultWithHeader() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header
|
||||||
|
translateId="theme.Playground.result"
|
||||||
|
description="The result label of the live codeblocks"
|
||||||
|
text="Result"
|
||||||
|
/>
|
||||||
|
<div className={styles.playgroundPreview}>
|
||||||
|
<LivePreview />
|
||||||
|
<LiveError />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditorWithHeader() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header
|
||||||
|
translateId="theme.Playground.liveEditor"
|
||||||
|
description="The live editor label of the live codeblocks"
|
||||||
|
text="Live Editor"
|
||||||
|
/>
|
||||||
|
<LiveEditor className={styles.playgroundEditor} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function Playground({children, transformCode, ...props}) {
|
export default function Playground({children, transformCode, ...props}) {
|
||||||
const {isClient} = useDocusaurusContext();
|
const {
|
||||||
|
isClient,
|
||||||
|
siteConfig: {
|
||||||
|
themeConfig: {
|
||||||
|
liveCodeBlock: {playgroundPosition},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} = useDocusaurusContext();
|
||||||
const prismTheme = usePrismTheme();
|
const prismTheme = usePrismTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -26,33 +71,17 @@ export default function Playground({children, transformCode, ...props}) {
|
||||||
transformCode={transformCode || ((code) => `${code};`)}
|
transformCode={transformCode || ((code) => `${code};`)}
|
||||||
theme={prismTheme}
|
theme={prismTheme}
|
||||||
{...props}>
|
{...props}>
|
||||||
<div
|
{playgroundPosition === 'top' ? (
|
||||||
className={clsx(
|
<>
|
||||||
styles.playgroundHeader,
|
<ResultWithHeader />
|
||||||
styles.playgroundEditorHeader,
|
<EditorWithHeader />
|
||||||
)}>
|
</>
|
||||||
<Translate
|
) : (
|
||||||
id="theme.Playground.liveEditor"
|
<>
|
||||||
description="The live editor label of the live codeblocks">
|
<EditorWithHeader />
|
||||||
Live Editor
|
<ResultWithHeader />
|
||||||
</Translate>
|
</>
|
||||||
</div>
|
)}
|
||||||
<LiveEditor className={styles.playgroundEditor} />
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
styles.playgroundHeader,
|
|
||||||
styles.playgroundPreviewHeader,
|
|
||||||
)}>
|
|
||||||
<Translate
|
|
||||||
id="theme.Playground.result"
|
|
||||||
description="The result label of the live codeblocks">
|
|
||||||
Result
|
|
||||||
</Translate>
|
|
||||||
</div>
|
|
||||||
<div className={styles.playgroundPreview}>
|
|
||||||
<LivePreview />
|
|
||||||
<LiveError />
|
|
||||||
</div>
|
|
||||||
</LiveProvider>
|
</LiveProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,21 +14,18 @@
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
background: var(--ifm-color-emphasis-200);
|
||||||
|
color: var(--ifm-color-content);
|
||||||
font-size: var(--ifm-code-font-size);
|
font-size: var(--ifm-code-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.playgroundEditorHeader {
|
.playgroundHeader:first-of-type {
|
||||||
background: var(--ifm-color-emphasis-600);
|
background: var(--ifm-color-emphasis-600);
|
||||||
color: var(--ifm-color-content-inverse);
|
color: var(--ifm-color-content-inverse);
|
||||||
border-top-left-radius: var(--ifm-global-radius);
|
border-top-left-radius: var(--ifm-global-radius);
|
||||||
border-top-right-radius: var(--ifm-global-radius);
|
border-top-right-radius: var(--ifm-global-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.playgroundPreviewHeader {
|
|
||||||
background: var(--ifm-color-emphasis-200);
|
|
||||||
color: var(--ifm-color-content);
|
|
||||||
}
|
|
||||||
|
|
||||||
.playgroundEditor {
|
.playgroundEditor {
|
||||||
font: var(--ifm-code-font-size) / var(--ifm-pre-line-height)
|
font: var(--ifm-code-font-size) / var(--ifm-pre-line-height)
|
||||||
var(--ifm-font-family-monospace) !important;
|
var(--ifm-font-family-monospace) !important;
|
||||||
|
@ -38,7 +35,10 @@
|
||||||
|
|
||||||
.playgroundPreview {
|
.playgroundPreview {
|
||||||
border: 1px solid var(--ifm-color-emphasis-200);
|
border: 1px solid var(--ifm-color-emphasis-200);
|
||||||
border-bottom-left-radius: var(--ifm-global-radius);
|
|
||||||
border-bottom-right-radius: var(--ifm-global-radius);
|
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.playgroundPreview:last-of-type, .playgroundEditor:last-of-type {
|
||||||
|
border-bottom-left-radius: var(--ifm-global-radius);
|
||||||
|
border-bottom-right-radius: var(--ifm-global-radius);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
const DEFAULT_CONFIG = {
|
||||||
|
playgroundPosition: 'bottom',
|
||||||
|
};
|
||||||
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
||||||
|
|
||||||
|
const Schema = Joi.object({
|
||||||
|
liveCodeBlock: Joi.object({
|
||||||
|
playgroundPosition: Joi.string()
|
||||||
|
.equal('top', 'bottom')
|
||||||
|
.default(DEFAULT_CONFIG.playgroundPosition),
|
||||||
|
})
|
||||||
|
.label('themeConfig.liveCodeBlock')
|
||||||
|
.default(DEFAULT_CONFIG),
|
||||||
|
});
|
||||||
|
exports.Schema = Schema;
|
||||||
|
|
||||||
|
exports.validateThemeConfig = function ({validate, themeConfig}) {
|
||||||
|
return validate(Schema, themeConfig);
|
||||||
|
};
|
|
@ -9,3 +9,20 @@ This theme provides a `@theme/CodeBlock` component that is powered by react-live
|
||||||
```bash npm2yarn
|
```bash npm2yarn
|
||||||
npm install --save @docusaurus/theme-live-codeblock
|
npm install --save @docusaurus/theme-live-codeblock
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
```jsx title="docusaurus.config.js"
|
||||||
|
module.exports = {
|
||||||
|
plugins: ['@docusaurus/theme-live-codeblock'],
|
||||||
|
themeConfig: {
|
||||||
|
liveCodeBlock: {
|
||||||
|
/**
|
||||||
|
* The position of the live playground, above or under the editor
|
||||||
|
* Possible values: "top" | "bottom"
|
||||||
|
*/
|
||||||
|
playgroundPosition: 'bottom',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
|
@ -292,6 +292,8 @@ function Clock(props) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Imports
|
||||||
|
|
||||||
:::caution react-live and imports
|
:::caution react-live and imports
|
||||||
|
|
||||||
It is not possible to import components directly from the react-live code editor, you have to define available imports upfront.
|
It is not possible to import components directly from the react-live code editor, you have to define available imports upfront.
|
||||||
|
|
|
@ -281,6 +281,9 @@ const LocaleConfigs = isI18nStaging
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
|
liveCodeBlock: {
|
||||||
|
playgroundPosition: 'bottom',
|
||||||
|
},
|
||||||
hideableSidebar: true,
|
hideableSidebar: true,
|
||||||
colorMode: {
|
colorMode: {
|
||||||
defaultMode: 'light',
|
defaultMode: 'light',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue