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

is there a way to display different MIME types already supported by JupyterLab? #4

Closed
chekos opened this issue Apr 21, 2019 · 14 comments

Comments

@chekos
Copy link

chekos commented Apr 21, 2019

Hi!
I'm using this course-starter template for a project and bumped into this issue:
altair

At first, I figured I could just change the way altair renders it's charts like this:
Screen Shot 2019-04-21 at 8 29 32 AM
changed it to alt.renderers.enable('kaggle')

which does add an altair visualization to the page but it doesn't display.
Screen Shot 2019-04-21 at 8 31 43 AM

Looking at the console I find an error that says "requirejs not defined"


Is there anyway to "import" jupyterlab renderers into the course's website as well? This would allow not just altair visualizations to render but other MIMEtypes as well (i.e. plotly, bokeh, etc).

pd: If this is more of a juniper issue I'm happy to close it here and ask there 👍. Thanks again for open-sourcing such an amazing tool. This is going to make tech instruction a lot more accessible across the globe.

@ines
Copy link
Owner

ines commented Apr 21, 2019

I think this a good place for this issue – it does use a component based on my Juniper implementation under the hood, but it's all included in this repo to make it easier to adapt.

I'm no expert on Jupyterlab renderers, but a good place to start might be here:

const renderers = standardRendererFactories.filter(factory =>
factory.mimeTypes.includes('text/latex') ? window.MathJax : true
)
const outputArea = new OutputArea({
model: new OutputAreaModel({ trusted: true }),
rendermime: new RenderMimeRegistry({ initialFactories: renderers }),
})

The standardRendererFactories include HTML, images, SVG, Markdown, text and JavaScript. If you know where to find the Altair JupyterLab renderer factory, you could import it here and then append it to renderers.

Also, regarding the "require is not defined" error, I think this might happen because the kaggle renderer is a notebook renderer and not a JupyerLab renderer (see here)?

@chekos
Copy link
Author

chekos commented Apr 21, 2019

Thanks! You are right about the kaggle renderer, it is a notebook renderer.

Recent versions of JupyterLab come with a vega renderer included by default - which is what altair uses under the hood.

I found this in the JupyterLab repo: https://github.com/jupyterlab/jupyterlab/tree/master/packages/vega5-extension
and it's NPM package https://npm.taobao.org/package/@jupyterlab/vega5-extension

I'm going to try to import and append like you suggested.

@ines
Copy link
Owner

ines commented Apr 21, 2019

@chekos Okay cool, fingers crossed 🤞 This part here looks super promising: https://github.com/jupyterlab/jupyterlab/blob/master/packages/vega5-extension/src/index.ts#L132

It follows the same format as the other existing renderers (I just had a quick look earlier and added console.log(renderers) in the code snipped I posted above).

@chekos
Copy link
Author

chekos commented Apr 21, 2019

I no longer get the <vega lite object> error on the output widget. Instead of the last Vega5 link i shared, I imported the renderer from @jupyterlab/vega3-extension

appended it to the renderers list but I get this error:
Screen Shot 2019-04-21 at 12 43 12 PM

I don't know a lot about JavaScript but from what I can tell this is an error in the vega3 renderer code?
I got it from JupyterLab's jupyter-renderers repo

@ines
Copy link
Owner

ines commented Apr 21, 2019

Yeah, somewhere something is null – do you have a repo with your config and dependencies I can try out? Maybe I can find the missing piece.

@chekos
Copy link
Author

chekos commented Apr 21, 2019

that'd be amazing!

my repo is at https://github.com/chekos/curso-analisis-de-datos-con-python

it's currently being deployed at https://lucid-goldberg-f4026c.netlify.com/

I've only changed a few things after I imported your template repo but in terms of the code that I'm running (from the screenshots) that's in chapter 1 - Getting Started

@ines
Copy link
Owner

ines commented Apr 21, 2019

Alright, so here's my hack – no idea what the implications of this are, but it does work around the underlying issue (which is that there's no URL resolver – I'm not 100% sure what this would look like in the JupyterLab ecosystem, but it's a set of methods that return a promise, so I wrote two methods that return a promise 😅)

renderers.push(vegaRenderer)

const mockResolver = {
    resolveUrl: () => new Promise(resolve => resolve('')),
    getDownloadUrl: () => new Promise(resolve => resolve('')),
}

const outputArea = new OutputArea({
    model: new OutputAreaModel({ trusted: true }),
    rendermime: new RenderMimeRegistry({ initialFactories: renderers, resolver: mockResolver }),
})

The output now looks like this, so you probably want to add some CSS classes to style it accordingly. You can add those in the theme.sass or in src/styles/index.sass.

Screenshot 2019-04-22 at 00 14 50

The markup looks pretty straightforward:

Screenshot 2019-04-22 at 00 16 07

@chekos
Copy link
Author

chekos commented Apr 21, 2019

😱 Thank you so much!

I just implemented it and it works! This is great!!!
Yes, I might change the background of the output cell for this one course specifically but this would be a great example of how altair works. I've seen a lot of people post their newly made charts on Twitter only to find out they're transparent and not white like they seem in the Jupyter environment.

thank you so much! I'm so excited to work on this!

@chekos chekos closed this as completed Apr 21, 2019
@joelostblom
Copy link

joelostblom commented Dec 2, 2020

Update: I opened explosion/spacy-course#92 since I think this is due to the more general issue of the scitp tags. Vegalite has also changed how the plots are displayed and the jupyter plugin has been deprecated as it is no longer needed, and thus this workaround didn't work for me.


Thanks for making this course framework @ines, it is really great! I am stuck in a similar situation as described here when trying to display Altair/Vegalite plots. Unfortunately I don't see the error message reported above and I could not get it to work trying the suggested fix.

I have tried to render vega objects both directly via the Altair output and by pasting in the HTML from a saved vegalite object, such as this chart. My slides simply show up blank, with the following warnings and errors in the console (none which seems directly related):

image

Exploring further, I noticed that javascript code inside <script> seems not to be executed, so this might be the issue since the vega code is wrapped in these tags. For example, if I paste the following code inside a slide, the header and paragraph shows up, but the number 8 is not printed at the end. Do you have any suggestion on how to start troubleshooting this and if it is at all possible to get js code inside <script> tags to execute properly?

<!DOCTYPE html>
<html>
<body>

<h2>My First Web Page</h2>
<p>My first paragraph.</p>

<script>
document.write(5 + 3);
</script>

</body>
</html> 

@ines
Copy link
Owner

ines commented Jan 15, 2021

@joelostblom Sorry for only getting to this now! I'm actually not so surprised that the inline script tags don't work, and it's likely related to the React/Gatsby setup 😅 (where inlining script tags like this also wouldn't easily work).

Can you share an example of the embed code you want to include? Does it require embedding inline <script>, or does it only require a JS library to be embedded on the page, and then uses some custom markup/classes/ids/web components for the widgets?

In general, here's how you can embed top-level JavaScript files in a Gatsby app: https://www.gatsbyjs.com/docs/custom-html/#adding-custom-javascript

@joelostblom
Copy link

joelostblom commented Jan 15, 2021

Thank you so much for getting back to me @ines!

The code I want to embed in a slide does require the use of inline <script> tags. It is VegaLite chart specifications, an example would look like the below and should render like this.

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-lite@4"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>
<body>
  <div id="vis"></div>
  <script type="text/javascript">
    var spec = {
      "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
      "config": {
        "view": {
          "continuousHeight": 300,
          "continuousWidth": 400
        }
      },
      "data": {
        "url": "https://vega.github.io/vega-datasets/data/cars.json"
      },
      "encoding": {
        "color": {
          "field": "Origin",
          "type": "nominal"
        },
        "x": {
          "field": "Horsepower",
          "type": "quantitative"
        },
        "y": {
          "field": "Miles_per_Gallon",
          "type": "quantitative"
        }
      },
      "mark": "point"
    };
    var opt = {"renderer": "canvas", "actions": false};
    vegaEmbed("#vis", spec, opt);
  </script>
</body>
</html>

I previously also opened an issue with some more details here in case it is helpful.

@joelostblom
Copy link

joelostblom commented Jan 15, 2021

I tried copying the sample code on the page you linked into a slide's .md file, but I cannot see any messages being logged to the console when I navigate to that slide. I only see console messages logged when couple them to a button as in the issue I linked in my last post.

This is the code I tried (I also tried adding a closing script tag, but to no avail)

<script
  dangerouslySetInnerHTML={{
    __html: `
            var name = 'world';
            console.log('Hello ' + name);
        `,
  }}
/>

@ines
Copy link
Owner

ines commented Jan 18, 2021

This is the code I tried (I also tried adding a closing script tag, but to no avail)

Ah yes, this only applies if you need to embed a top-level script on the page via the html.js – for example, to initialize some library etc.

The problem with the slide content is that it's all rendered through reveal.js and its Markdown extension: https://github.com/ines/course-starter-python/blob/master/src/components/slides.js#L82-L90 It's possible that reveal.js strips out script tags here.

Some things to try:

  • Put the plot in a .html file and embed it in an iframe. You should be able to put the file in /static and then refer to its path like you would for an image.
  • Here's an interesting approach using native web components: https://stackoverflow.com/a/51216163/6400719 It's quite similar to an iframe, though, a bit more involved and likely won't work in older browsers.

How important is it for your slides that the plots are generated on the fly and dynamically? Do you need any interactivity on them? Or could you just export them as SVGs instead and embed them as regular images? (I'm no export on VegaLite but it looks like you have to explicitly export the plot – just saving the HTML isn't going to work, because it draws on a <canvas> element under the hood if you generate the charts dynamically with JavaScript).

@joelostblom
Copy link

I have been using SVG so far, which I autogenerate from the plots via an RMarkdown hook, but unfortunately interactivity is important for some modules in this course so we will need it eventually.

My first instinct was also that the script tags might be stripped out, but I checked the HTML source of the rendered slide and they are still there. The plot code also works when pasted directly in Reveal.js slides so it is supported.

I tried iframes previously, but I couldn't get it to work for some reason, can't remember why. Anyways, I tried again because you brought it up and it is working now, thank you! I don't want to save files separately, but I discovered that there is a srcdoc param to iframe which can take the html code directly instead of a file URI. I am going to try writing an automatic hook that takes the Altair HTML output and reconstruct the appropriate iframe syntax around it. Can probably not get to it until the end of the week but will report back with how it goes, thanks again, yay!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants