diff --git a/packages/docusaurus-theme-live-codeblock/package.json b/packages/docusaurus-theme-live-codeblock/package.json
index ca0447d68d..797512dc7e 100644
--- a/packages/docusaurus-theme-live-codeblock/package.json
+++ b/packages/docusaurus-theme-live-codeblock/package.json
@@ -16,6 +16,7 @@
"@docusaurus/core": "2.0.0-alpha.70",
"@philpl/buble": "^0.19.7",
"clsx": "^1.1.1",
+ "joi": "^17.4.0",
"parse-numeric-range": "^1.2.0",
"prism-react-renderer": "^1.1.1",
"react-live": "^2.2.3"
diff --git a/packages/docusaurus-theme-live-codeblock/src/__tests__/validateThemeConfig.test.js b/packages/docusaurus-theme-live-codeblock/src/__tests__/validateThemeConfig.test.js
new file mode 100644
index 0000000000..ce0fdf0400
--- /dev/null
+++ b/packages/docusaurus-theme-live-codeblock/src/__tests__/validateThemeConfig.test.js
@@ -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]"`,
+ );
+ });
+});
diff --git a/packages/docusaurus-theme-live-codeblock/src/index.js b/packages/docusaurus-theme-live-codeblock/src/index.js
index d222af3d25..052e75d0a0 100644
--- a/packages/docusaurus-theme-live-codeblock/src/index.js
+++ b/packages/docusaurus-theme-live-codeblock/src/index.js
@@ -6,8 +6,9 @@
*/
const path = require('path');
+const {validateThemeConfig} = require('./validateThemeConfig');
-module.exports = function () {
+function theme() {
return {
name: 'docusaurus-theme-live-codeblock',
@@ -25,4 +26,8 @@ module.exports = function () {
};
},
};
-};
+}
+
+module.exports = theme;
+
+theme.validateThemeConfig = validateThemeConfig;
diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.js b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.js
index 84891e1fce..882b07ff0e 100644
--- a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.js
+++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/index.js
@@ -11,11 +11,56 @@ import clsx from 'clsx';
import Translate from '@docusaurus/Translate';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import usePrismTheme from '@theme/hooks/usePrismTheme';
-
import styles from './styles.module.css';
+function Header({translateId, description, text}) {
+ return (
+
+
+ {text}
+
+
+ );
+}
+
+function ResultWithHeader() {
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+}
+
+function EditorWithHeader() {
+ return (
+ <>
+
+
+ >
+ );
+}
+
export default function Playground({children, transformCode, ...props}) {
- const {isClient} = useDocusaurusContext();
+ const {
+ isClient,
+ siteConfig: {
+ themeConfig: {
+ liveCodeBlock: {playgroundPosition},
+ },
+ },
+ } = useDocusaurusContext();
const prismTheme = usePrismTheme();
return (
@@ -26,33 +71,17 @@ export default function Playground({children, transformCode, ...props}) {
transformCode={transformCode || ((code) => `${code};`)}
theme={prismTheme}
{...props}>
-
-
- Live Editor
-
-
-
-
-
- Result
-
-
-
-
-
-
+ {playgroundPosition === 'top' ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
);
diff --git a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css
index 6f5cf0805c..6750142321 100644
--- a/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css
+++ b/packages/docusaurus-theme-live-codeblock/src/theme/Playground/styles.module.css
@@ -14,21 +14,18 @@
padding: 0.75rem;
text-transform: uppercase;
font-weight: bold;
+ background: var(--ifm-color-emphasis-200);
+ color: var(--ifm-color-content);
font-size: var(--ifm-code-font-size);
}
-.playgroundEditorHeader {
+.playgroundHeader:first-of-type {
background: var(--ifm-color-emphasis-600);
color: var(--ifm-color-content-inverse);
border-top-left-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 {
font: var(--ifm-code-font-size) / var(--ifm-pre-line-height)
var(--ifm-font-family-monospace) !important;
@@ -38,7 +35,10 @@
.playgroundPreview {
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;
}
+
+.playgroundPreview:last-of-type, .playgroundEditor:last-of-type {
+ border-bottom-left-radius: var(--ifm-global-radius);
+ border-bottom-right-radius: var(--ifm-global-radius);
+}
diff --git a/packages/docusaurus-theme-live-codeblock/src/validateThemeConfig.js b/packages/docusaurus-theme-live-codeblock/src/validateThemeConfig.js
new file mode 100644
index 0000000000..7b294874d6
--- /dev/null
+++ b/packages/docusaurus-theme-live-codeblock/src/validateThemeConfig.js
@@ -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);
+};
diff --git a/website/docs/api/themes/theme-live-codeblock.md b/website/docs/api/themes/theme-live-codeblock.md
index 9ee615cec0..8a676646f4 100644
--- a/website/docs/api/themes/theme-live-codeblock.md
+++ b/website/docs/api/themes/theme-live-codeblock.md
@@ -9,3 +9,20 @@ This theme provides a `@theme/CodeBlock` component that is powered by react-live
```bash npm2yarn
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',
+ },
+ },
+};
+```
diff --git a/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx b/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx
index 724f9dc989..270f92d05a 100644
--- a/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx
+++ b/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx
@@ -292,6 +292,8 @@ function Clock(props) {
}
```
+### 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.
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js
index f83ea28561..cddd0d0a53 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -281,6 +281,9 @@ const LocaleConfigs = isI18nStaging
],
],
themeConfig: {
+ liveCodeBlock: {
+ playgroundPosition: 'bottom',
+ },
hideableSidebar: true,
colorMode: {
defaultMode: 'light',