is there a way to display different MIME types already supported by JupyterLab?
chekos opened this issue ยท 14 comments
Hi!
I'm using this course-starter template for a project and bumped into this issue:
At first, I figured I could just change the way altair
renders it's charts like this:
changed it to alt.renderers.enable('kaggle')
which does add an altair
visualization to the page but it doesn't display.
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.
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:
course-starter-python/src/components/juniper.js
Lines 67 to 74 in 48d0455
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)?
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.
@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).
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:
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
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.
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
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
.
The markup looks pretty straightforward:
๐ฑ 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!
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):
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>
@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
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.
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);
`,
}}
/>
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).
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!