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

Import files to theme-live-codeblock scope #2807

Closed
oze4 opened this issue May 24, 2020 · 14 comments
Closed

Import files to theme-live-codeblock scope #2807

oze4 opened this issue May 24, 2020 · 14 comments
Assignees
Labels
bug An error in the Docusaurus core causing instability or issues with its execution

Comments

@oze4
Copy link
Contributor

oze4 commented May 24, 2020

🐛 Bug Report

Import statements in theme-live-codeblock not working. I read the react-live docs and it does look like they do support importing modules - does docusaurus expose the needed scope?

Have you read the Contributing Guidelines on issues?

Yes

To Reproduce

  1. Configure theme-live-codeblock according to these instructions
  2. Install @material-ui/core
  3. Create file.mdx attempt to import components from @material-ui/core
  4. Attempted to import within .mdx file as well as the ``` jsx live block

Expected behavior

Render the imported component

Actual Behavior

  • When the import statement is at the top of .mdx (keep in mind I can use the imported component within the .mdx file just fine, just can't use it within the ```jsx live block..):

image

  • When the import statement is inside the ```jsx live block:

image

Your Environment

  • Docusaurus version used: 2.0.0-alpha.55
  • Environment name and version (e.g. Chrome 78.0.3904.108, Node.js 10.17.0): Node: v13.12.0, Firefox: 76.0.1
  • Operating system and version (desktop or mobile): Catalina 10.15.4
// from package.json
"dependencies": {
  "@docusaurus/core": "^2.0.0-alpha.55",
  "@docusaurus/preset-classic": "^2.0.0-alpha.55",
  "@docusaurus/theme-live-codeblock": "^2.0.0-alpha.39",
  "@material-ui/core": "^4.10.0",
  "@material-ui/icons": "^4.9.1",
  "classnames": "^2.2.6",
},

Reproducible Demo

@oze4 oze4 added bug An error in the Docusaurus core causing instability or issues with its execution status: needs triage This issue has not been triaged by maintainers labels May 24, 2020
@oze4
Copy link
Contributor Author

oze4 commented May 24, 2020

After browsing through the repo, it doesn't look like docusaurus lets you configure react-live scope..

As a workaround, this is what I did:

Slightly modified Playground
/**
 * 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.
 */

import React from 'react';

import {
  LiveProvider,
  LiveEditor,
  LiveError,
  LivePreview
} from 'react-live'

import classnames from 'classnames';
import usePrismTheme from './usePrismTheme.js';

import * as styles from './styles.module.css';

function Playground({children, /* theme, */ transformCode, ...props}) {
  const theme = usePrismTheme();

  return (
    <LiveProvider
      code={children.replace(/\n$/, '')}
      transformCode={transformCode || ((code) => `${code};`)}
      theme={theme}
      {...props}>
      <div
        className={classnames(
          styles.playgroundHeader,
          styles.playgroundEditorHeader,
        )}>
        Live Editor
      </div>
      <LiveEditor />
      <div
        className={classnames(
          styles.playgroundHeader,
          styles.playgroundPreviewHeader,
        )}>
        Result
      </div>
      <div className={styles.playgroundPreview}>
        <LivePreview />
        <LiveError />
      </div>
    </LiveProvider>
  );
}

export default Playground;
NOT modified usePrismTheme
/**
 * 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.
 */

import defaultTheme from 'prism-react-renderer/themes/palenight';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useThemeContext from '@theme/hooks/useThemeContext';

const usePrismTheme = () => {
  const {
    siteConfig: {
      themeConfig: {prism = {}},
    },
  } = useDocusaurusContext();
  const {isDarkTheme} = useThemeContext();
  const lightModeTheme = prism.theme || defaultTheme;
  const darkModeTheme = prism.darkTheme || lightModeTheme;
  const prismTheme = isDarkTheme ? darkModeTheme : lightModeTheme;

  return prismTheme;
};

export default usePrismTheme;
NOT modified styles.module.css
/**
 * 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.
 */

 .playgroundHeader {
  letter-spacing: 0.08rem;
  padding: 0.75rem;
  text-transform: uppercase;
  font-weight: bold;
}

.playgroundEditorHeader {
  background: var(--ifm-color-emphasis-600);
  color: var(--ifm-color-content-inverse);
}

.playgroundPreviewHeader {
  background: var(--ifm-color-emphasis-200);
  color: var(--ifm-color-content);
}

.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);
  position: relative;
  padding: 1rem;
}

From there, I import the Playground into an .mdx file and it worked! I did try to use react-live components on their own, which worked, but styling was not right. That is why I copied those components - to keep styling.

MyFile.mdx:

---
id: some_id
title: My Live Code Demo 
---

import LiveCodeBlock from '../Playground';
import { Button } from '@material-ui/core';

### Custom components

<LiveCodeBlock scope={{ Button }}>
{`
function MyButton() {
  return <Button color="secondary" onClick={() => alert("Hello, world!")}>Click me</Button>
}
`}
</LiveCodeBlock>

Which works perfectly!

image

@oze4
Copy link
Contributor Author

oze4 commented May 24, 2020

Final Solution

Ultimately, with my solution above, there were still some minor differences in styling.. So I started digging into the codebase and it looks like my solution very similar (in theory, at least lol) to swizzling.

image

Despite the warning, swizzling MDXComponents was the easiest way to accomplish this:

yarn swizzle @docusaurus/theme-classic MDXComponents
This creates a file at `src/theme/MDXComponents/index.js` - (click on me to see edited file)
/**
 * 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.
 */

import React from 'react';
import Link from '@docusaurus/Link';
import CodeBlock from '@theme/CodeBlock';
import Heading from '@theme/Heading';

import styles from './styles.module.css';

//-------------------------------------------------------/
////// Import the component you need in scope
import Button from "@material-ui/core/Button";

//-------------------------------------------------------/
////// Object with components I need in scope
const SCOPE = {
  Button,
}

export default {
  code: (props) => {
    const {children} = props;
    if (typeof children === 'string') {

      //-------------------------------------------------------/
      return <CodeBlock {...props} scope={SCOPE} />;
      /** 
       * REPLACED THE LINE BELOW WITH THE LINE ABOVE 
       */
      //// return <CodeBlock {...props} />;
      //-------------------------------------------------------/

    }
    return children;
  },
  a: (props) => {
    if (/\.[^./]+$/.test(props.href)) {
      // eslint-disable-next-line jsx-a11y/anchor-has-content
      return <a {...props} />;
    }
    return <Link {...props} />;
  },
  pre: (props) => <div className={styles.mdxCodeBlock} {...props} />,
  h1: Heading('h1'),
  h2: Heading('h2'),
  h3: Heading('h3'),
  h4: Heading('h4'),
  h5: Heading('h5'),
  h6: Heading('h6'),
};

Then you can just use it in your markdown:

``` jsx live
function ButtonTest() {
    return (
        <Button onClick={() => alert('omg it worked')} color="secondary">Click Me</Button>
    )
}
```

This worked perfectly for me - no need for a custom component now.

@oze4 oze4 closed this as completed May 24, 2020
@slorber slorber reopened this May 24, 2020
@slorber
Copy link
Collaborator

slorber commented May 24, 2020

Hi,

I'm going to reopen because this is something I noticed inspecting the code 3 days ago, and wanted to fix.

We should be able to import components in MDX, and they should become automatically available in the playgrounds of the mdx page. Afaik my discussions with Chris Biscardy there's a useComponents() in MDX that can be called to get the comps to pass to react-live scope.

@slorber slorber self-assigned this May 24, 2020
@oze4
Copy link
Contributor Author

oze4 commented May 24, 2020

I'm not too familiar with the libraries/technology used in this repo (lerna, mdx, react-live..) Hell, I just started with docusaurus like 3 days ago...but if there's anything you need help with, feel free to reach out.

@oze4
Copy link
Contributor Author

oze4 commented May 27, 2020

Hey @slorber - just wanted to let you know that it doesn't look like that useMDXComponents hook returns imported components. It looks like it returns the data already in /MDXComponents/index.js - it doesn't return imported components. I'm not sure if there's a way to have MDX tell us what has been imported or not (so that we can add it to scope).. FYI.

@slorber
Copy link
Collaborator

slorber commented May 28, 2020

Unfortunately you are right @oze4

I found back my discussion with Chris Biscardi here: https://twitter.com/sebastienlorber/status/1252899627126358016

There is a useMDXScope hook, but it's not from MDX, it's from MDX, it's from the Gatsby MDX plugin.

I've looked at its code and it's probably not so easy to port it fast to Docusaurus, as it uses a babel plugin to extract imports from the MDX generated JSX, write them to disk in a dedicated file...

https://github.com/gatsbyjs/gatsby/blob/cd150b5e5264a5a75f2abc27e3430c55bfcd4e41/packages/gatsby-plugin-mdx/gatsby/on-create-node.js

Will probably come back to this later when we have shipped more urgent features.


In the meantime, I think we still can make something about it, like allowing to more easily provide components to the react-live scope with swizzle. The scope may not be per-MDX file, but having a global scope is probably good enough for most usecases.

Also, afaik we don't have yet a "wrapRootElement" API but I've seen this is something we want. This would enable to use the MDXProvider

@slorber
Copy link
Collaborator

slorber commented May 28, 2020

@oze4 I've made a PR to support more officially a workflow to provide some components to the react-live scope. It's not as good as the gatsby integration as I hoped (as you have to configure available scope globally) but I think it's good enough.

Can you tell me if this solves your usecase?

I think it would permit you to swizzle a "smaller" part of the live editor plugin

#2826

@oze4
Copy link
Contributor Author

oze4 commented May 28, 2020

Hey @slorber - I did notice that Gatsby uses a custom hook, useMDXScope from the gatsby-plugin-mdx which I did test out as well. Unless I'm using it incorrectly, the useMDXScope hook also did not update with imported components - it returned the same data as useMDXComponents. (After all it looks like useMDXScope is a wrapper for useMDXComponents?)

When I imported a component in an .mdx file, the useMDXScope hook did not pick up on that. Again, I could be using it incorrectly, but that is also something I looked into. While it looks like they have the 'right idea', I'm not sure it's working as intended.

Thanks for everything!

Edit: FYI - this was one of the articles I found, which I used as part of my testing.

@slorber
Copy link
Collaborator

slorber commented May 28, 2020

Yes it's normal as it's proprietary to gatsby, it assumes the gatsby-node.js code has run and written something to the FS, otherwise it just returns no additional context :)

@oze4
Copy link
Contributor Author

oze4 commented May 28, 2020

Awesome. Thanks for your input. What's weird is that I was using it in Gatsby and it was still returning no additional context. I wanted to see how they handled this so I built out a little demo Gatsby site..

With that being said, it's probably something I was doing wrong.

Cheers!

@slorber
Copy link
Collaborator

slorber commented May 29, 2020

Didn't test with Gatsby either but I guess it should work ^^ Does the doc of the PR I wrote seems good for your usecase?

@oze4
Copy link
Contributor Author

oze4 commented May 29, 2020

Yes sir. Thank you!

lex111 pushed a commit that referenced this issue Jun 4, 2020
* feat(v2): add components to react-live scope (#2807)

* fix admonition issue + improve react-live scope doc

* fix again admonition :(

* remove forwarding of mdx components to react-live scope (for now)

* remove useless dep
@yangshun yangshun removed the status: needs triage This issue has not been triaged by maintainers label Jun 5, 2020
@lex111
Copy link
Contributor

lex111 commented Jun 6, 2020

Implemented in #2826.

@lex111 lex111 closed this as completed Jun 6, 2020
@slorber
Copy link
Collaborator

slorber commented Jun 18, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug An error in the Docusaurus core causing instability or issues with its execution
Projects
None yet
Development

No branches or pull requests

4 participants