-
Notifications
You must be signed in to change notification settings - Fork 839
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: Add support for simplified interactive demo syntax using code blocks #7907
Changes from all commits
adcb4ca
4406614
9daa6de
e09ca6d
bc1ac47
76c5535
7e988fb
af96d49
740cbe5
1d4b1e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
diff --git a/dist/index.js b/dist/index.js | ||
index 7bcfee33d9cfaa6c5f9d7da40335ec2551f932b6..4c29438cb94412d25aed193f9c86f20ccebb9009 100644 | ||
--- a/dist/index.js | ||
+++ b/dist/index.js | ||
@@ -168,7 +168,7 @@ var import_sucrase = require("sucrase"); | ||
var defaultTransforms = ["jsx", "imports"]; | ||
function transform(opts = {}) { | ||
const transforms = Array.isArray(opts.transforms) ? opts.transforms.filter(Boolean) : defaultTransforms; | ||
- return (code) => (0, import_sucrase.transform)(code, { transforms }).code; | ||
+ return (code) => (0, import_sucrase.transform)(code, { transforms, jsxPragma: 'jsx' }).code; | ||
} | ||
|
||
// src/utils/transpile/errorBoundary.tsx | ||
diff --git a/dist/index.mjs b/dist/index.mjs | ||
index 44a3fbed4fc0545b75235add7369a262e8ee5220..6cfad17b4ce81553bac1243b7421f5f75ade43fc 100644 | ||
--- a/dist/index.mjs | ||
+++ b/dist/index.mjs | ||
@@ -127,7 +127,7 @@ import { transform as _transform } from "sucrase"; | ||
var defaultTransforms = ["jsx", "imports"]; | ||
function transform(opts = {}) { | ||
const transforms = Array.isArray(opts.transforms) ? opts.transforms.filter(Boolean) : defaultTransforms; | ||
- return (code) => _transform(code, { transforms }).code; | ||
+ return (code) => _transform(code, { transforms, jsxPragma: 'jsx' }).code; | ||
} | ||
|
||
// src/utils/transpile/errorBoundary.tsx |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,11 +3,16 @@ import { LivePreview } from 'react-live'; | |
import BrowserOnly from '@docusaurus/BrowserOnly'; | ||
import ErrorBoundary from '@docusaurus/ErrorBoundary'; | ||
import { ErrorBoundaryErrorMessageFallback } from '@docusaurus/theme-common'; | ||
import { UseEuiTheme, EuiFlexGroup, useEuiTheme } from '@elastic/eui'; | ||
import { UseEuiTheme, useEuiTheme, EuiPaddingSize, euiPaddingSize } from '@elastic/eui'; | ||
import { CSSProperties } from 'react'; | ||
|
||
export interface DemoPreviewProps { | ||
padding?: EuiPaddingSize; | ||
} | ||
|
||
const getPreviewStyles = (euiTheme: UseEuiTheme) => ({ | ||
previewWrapper: css` | ||
padding: ${euiTheme.euiTheme.size.l}; | ||
padding: var(--docs-demo-preview-padding); | ||
border-radius: var(--docs-demo-border-radius); | ||
`, | ||
}); | ||
|
@@ -21,16 +26,21 @@ const PreviewLoader = () => ( | |
<div>Loading...</div> | ||
); | ||
|
||
export const DemoPreview = () => { | ||
export const DemoPreview = ({ padding = 'l' }: DemoPreviewProps) => { | ||
const euiTheme = useEuiTheme(); | ||
const styles = getPreviewStyles(euiTheme); | ||
const paddingSize = euiPaddingSize(euiTheme, padding); | ||
|
||
const style = { | ||
'--docs-demo-preview-padding': paddingSize, | ||
} as CSSProperties; | ||
Comment on lines
+34
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Passing this with a CSS variable is technically more performant than recomputing and reapplying the whole style with Emotion. Not that it matters here, but I was experimenting with the approaches of passing dynamic data to CSS without Emotion and really liked this solution. There are many optimizations browsers could do with this to reduce style recalculations when the value changes. It would be faster to define the property using |
||
|
||
return ( | ||
<BrowserOnly fallback={<PreviewLoader />}> | ||
{() => ( | ||
<> | ||
<ErrorBoundary fallback={(params: any) => <ErrorBoundaryErrorMessageFallback {...params} />}> | ||
<div css={styles.previewWrapper}> | ||
<div css={styles.previewWrapper} style={style}> | ||
<LivePreview /> | ||
</div> | ||
</ErrorBoundary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React, { isValidElement, type ReactNode } from 'react'; | ||
import { EuiCodeBlock } from '@elastic/eui'; | ||
import type { Props } from '@theme/CodeBlock'; | ||
import { Demo } from '../../components/demo'; | ||
|
||
/** | ||
* Best attempt to make the children a plain string so it is copyable. If there | ||
* are react elements, we will not be able to copy the content, and it will | ||
* return `children` as-is; otherwise, it concatenates the string children | ||
* together. | ||
*/ | ||
function maybeStringifyChildren(children: ReactNode): ReactNode { | ||
if (React.Children.toArray(children).some((el) => isValidElement(el))) { | ||
return children; | ||
} | ||
// The children is now guaranteed to be one/more plain strings | ||
return Array.isArray(children) ? children.join('') : (children as string); | ||
} | ||
|
||
export default function CodeBlock({ | ||
children: rawChildren, | ||
metastring, | ||
className, | ||
...props | ||
}: Props): JSX.Element { | ||
const children = maybeStringifyChildren(rawChildren); | ||
const language = className?.replace('language-', '') || undefined; | ||
|
||
if (metastring?.startsWith('interactive')) { | ||
return <Demo {...props}>{children}</Demo>; | ||
} | ||
|
||
return ( | ||
<EuiCodeBlock | ||
{...props} | ||
fontSize="m" | ||
overflowHeight={450} | ||
language={language} | ||
isCopyable | ||
> | ||
{children} | ||
</EuiCodeBlock> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,6 +118,31 @@ declare module '@theme-original/EditThisPage' { | |
export default function EditThisPage(props: Props): JSX.Element; | ||
} | ||
|
||
// original: https://github.com/facebook/docusaurus/blob/fa743c81defd24e22eae45c81bd79eb8ec2c4ef0/packages/docusaurus-theme-classic/src/theme-classic.d.ts#L364 | ||
declare module '@theme/CodeBlock' { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ So far I've used the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I specifically used Here's the documentation on differences between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for the types we could update the other cases that I added with |
||
import type { ReactNode } from 'react'; | ||
|
||
export interface Props { | ||
readonly children: ReactNode; | ||
readonly className?: string; | ||
readonly metastring?: string; | ||
readonly title?: string; | ||
readonly language?: string; | ||
readonly showLineNumbers?: boolean; | ||
} | ||
|
||
export default function CodeBlock(props: Props): JSX.Element; | ||
} | ||
|
||
// original: https://github.com/facebook/docusaurus/blob/8b877d27d4b1bcd5c2ee13dde8332407a1c26447/packages/docusaurus-theme-classic/src/theme-classic.d.ts#L510 | ||
declare module '@theme/MDXComponents/Code' { | ||
import type {ComponentProps} from 'react'; | ||
|
||
export interface Props extends ComponentProps<'code'> {} | ||
|
||
export default function MDXCode(props: Props): JSX.Element; | ||
} | ||
|
||
// original: https://github.com/facebook/docusaurus/blob/fa743c81defd24e22eae45c81bd79eb8ec2c4ef0/packages/docusaurus-theme-classic/src/theme-classic.d.ts#L563 | ||
declare module '@theme-original/DocSidebar' { | ||
import type { PropSidebarItem } from '@docusaurus/plugin-content-docs'; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed temporarily because
react-live
doesn't support passing arguments tosucrase
(the client-side JS/TS transpiler). I'm planning to add this functionality toreact-live
in a PR after we finish this milestone.Defining
jsxPragma
here configures sucrase to emitjsx([...])
instead of the regularReact.createElement([...])
and control what thejsx
is - in our case it's the Emotionjsx
wrapper. We inject the customjsx
here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So awesome this patching functionality!