Skip to content

Commit

Permalink
Switch to a lab-based app for the Voila frontend (#846)
Browse files Browse the repository at this point in the history
* Switch to a lab-based app for the Voila frontend

Co-authored-by: martinRenou <[email protected]>

* Remove `"private": true` in  `voila/package.json`

* Minor updates

* Convert `index.js` into typescript

* Pass "this" to the WidgetRenderer

We need to cast "as any" until this is merged and released: jupyter-widgets/ipywidgets#3625

* Remove install --pre ipywidgets

* Update snapshots

* Remove ipyvolume ui-test

Co-authored-by: martinRenou <[email protected]>
Co-authored-by: Duc Trung LE <[email protected]>
  • Loading branch information
3 people authored Oct 27, 2022
1 parent 86d64bd commit 20caacc
Show file tree
Hide file tree
Showing 58 changed files with 3,585 additions and 4,364 deletions.
1 change: 0 additions & 1 deletion .binder/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ dependencies:
- bqplot
- scipy
- ipympl
- ipympl
- xleaflet=0.16.0
- xeus-cling=0.13.0
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ node_modules
coverage
*.map.js
*.bundle.js
*.voila.js

# jetbrains IDE stuff
.idea/
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ config.rst
package-lock.json

share/jupyter/voila/templates/base/static/*voila.js
share/jupyter/voila/templates/base/static/*[woff|woff2|eot|svg]
share/jupyter/voila/templates/base/static/*.[woff|woff2|eot|svg]

share/jupyter/voila/templates/classic/static/labvariables.css
share/jupyter/voila/templates/classic/static/materialcolors.css
Expand Down
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ node_modules
build
notebooks/
.vscode/
.pytest_cache

ui-tests/playwright-report
ui-tests/playwright-report
17 changes: 17 additions & 0 deletions docs/source/contribute.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@ To lint the packages:
# run prettier
jlpm run prettier
About the Voila Frontend
========================

The Voila frontend is built as a JupyterLab-based application using JupyterLab components.

This makes it possible to reuse existing plugins and extensions for Jupyterlab such as core JupyterLab plugins like the JSON viewer,
as well as third-party mime renderers like the `FASTA viewer <https://github.com/jupyterlab/jupyter-renderers>`_.

The Voila frontend is able to load existing JupyterLab extensions installed as prebuilt extensions under ``${PREFIX}/share/labextensions``,
similar to the way it works in JupyterLab.

These extensions are typically distributed via ``pip`` and ``conda`` packages and can easily be installed by end users without requiring Node.js.
Widget packages usually now include a prebuilt extension for JupyterLab 3.0 by default, which should automatically work in Voila too.

Check out the `JupyterLab Documentation on prebuilt extensions <https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html#prebuilt-extensions>`_ for more info.

The code for the frontend is located under ``packages/voila``, with support for loading federated extensions in ``packages/voila/index.js``.

Tests
=====
Expand Down
22 changes: 0 additions & 22 deletions docs/source/using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,3 @@ The examples can then be served with:
cd notebooks/
voila
Using third-party Widgets with Voilà
====================================

By default, Voilà doesn't serve Jupyter Widgets installed as a classic notebook extension (nbextension).

Instead, it fallbacks to fetching the files from a CDN. This might result in an error (404) in case the
custom widget has not been published to ``npm``, or when Voilà runs in an environment without an Internet
connection.

To let the Voilà standalone app serve the nbextensions, use the ``enable_nbextensions`` flag as follows:

.. code-block:: bash
voila --enable_nbextensions=True
When using Voilà as a server extension:

.. code-block:: bash
jupyter notebook --VoilaConfiguration.enable_nbextensions=True
159 changes: 159 additions & 0 deletions notebooks/mimerenderers.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# JSON"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ipywidgets import Button, Output\n",
"from IPython import display\n",
"\n",
"button = Button(description='Output JSON')\n",
"output = Output()\n",
"obj = {\n",
" \"abcde\": 1234,\n",
" \"nested\": list(range(10))\n",
"}\n",
"\n",
"@output.capture()\n",
"def on_click(change):\n",
" display.display(display.JSON(obj))\n",
" \n",
" \n",
"button.on_click(on_click)\n",
"display.display(button)\n",
"output"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display.JSON(obj)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Fasta"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fasta_button = Button(description='Output FASTA')\n",
"fasta_output = Output()\n",
"\n",
"\n",
"def Fasta(data=''):\n",
" bundle = {}\n",
" bundle['application/vnd.fasta.fasta'] = data\n",
" bundle['text/plain'] = data\n",
" display.display(bundle, raw=True)\n",
" \n",
" \n",
"@fasta_output.capture()\n",
"def on_click(change):\n",
" Fasta(\"\"\">SEQUENCE_1\n",
"MTEITAAMVKELRESTGAGMMDCKNALSETNGDFDKAVQLLREKGLGKAAKKADRLAAEG\n",
"LVSVKVSDDFTIAAMRPSYLSYEDLDMTFVENEYKALVAELEKENEERRRLKDPNKPEHK\n",
"IPQFASRKQLSDAILKEAEEKIKEELKAQGKPEKIWDNIIPGKMNSFIADNSQLDSKLTL\n",
"MGQFYVMDDKKTVEQVIAEKEKEFGGKIKIVEFICFEVGEGLEKKTEDFAAEVAAQL\n",
">SEQUENCE_2\n",
"SATVSEINSETDFVAKNDQFIALTKDTTAHIQSNSLQSVEELHSSTINGVKFEEYLKSQI\n",
"ATIGENLVVRRFATLKAGANGVVNGYIHTNGRVGVVIAAACDSAEVASKSRDLLRQICMH\"\"\")\n",
"\n",
"\n",
"fasta_button.on_click(on_click)\n",
"display.display(fasta_button)\n",
"fasta_output"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# GeoJSON"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from IPython.display import GeoJSON\n",
"\n",
"\n",
"geojson_button = Button(description='Output GeoJSON')\n",
"geojson_output = Output()\n",
"\n",
" \n",
"@geojson_output.capture()\n",
"def on_click(change):\n",
" obj = GeoJSON({\n",
" \"type\": \"Feature\",\n",
" \"geometry\": {\n",
" \"type\": \"Point\",\n",
" \"coordinates\": [-118.4563712, 34.0163116]\n",
" }\n",
" })\n",
" display.display(obj)\n",
" \n",
"\n",
"geojson_button.on_click(on_click)\n",
"display.display(geojson_button)\n",
"geojson_output"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
4 changes: 2 additions & 2 deletions packages/jupyterlab-preview/src/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ export class VoilaPreview extends DocumentWidget<IFrame, INotebookModel> {
* Reload the preview.
*/
reload(): void {
const iframe = this.content.node.querySelector('iframe')!;
if (iframe.contentWindow) {
const iframe = this.content.node.querySelector('iframe');
if (iframe && iframe.contentWindow) {
iframe.contentWindow.location.reload();
}
}
Expand Down
62 changes: 43 additions & 19 deletions packages/voila/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"name": "@voila-dashboards/voila",
"private": true,
"version": "0.4.0",
"description": "The Voilà Frontend",
"author": "Voilà contributors",
Expand All @@ -11,44 +10,69 @@
"@jupyter-widgets/base": "^6.0.1",
"@jupyter-widgets/controls": "^5.0.1",
"@jupyter-widgets/jupyterlab-manager": "^5.0.3",
"@jupyter-widgets/output": "^6.0.1",
"@jupyterlab/application": "^3.0.0",
"@jupyterlab/apputils": "^3.0.0",
"@jupyterlab/coreutils": "^5.0.0",
"@jupyterlab/docregistry": "^3.0.0",
"@jupyterlab/javascript-extension": "~3.0.0",
"@jupyterlab/json-extension": "^3.0.0",
"@jupyterlab/logconsole": "^3.0.0",
"@jupyterlab/mainmenu": "^3.0.0",
"@jupyterlab/markdownviewer-extension": "^3.0.0",
"@jupyterlab/mathjax2-extension": "^3.0.0",
"@jupyterlab/nbformat": "^3.0.0",
"@jupyterlab/notebook": "^3.0.0",
"@jupyterlab/outputarea": "^3.0.0",
"@jupyterlab/rendermime": "^3.0.0",
"@jupyterlab/rendermime-extension": "^3.0.0",
"@jupyterlab/services": "^6.1.8",
"@lumino/algorithm": "^1.3.3",
"@lumino/commands": "^1.12.0",
"@lumino/domutils": "^1.2.3",
"@lumino/messaging": "^1.4.3",
"@lumino/signaling": "^1.4.3",
"@lumino/virtualdom": "^1.8.0",
"@lumino/widgets": "^1.18.0",
"mathjax-full": "^3.0.0"
"@jupyterlab/settingregistry": "^3.0.0",
"@jupyterlab/translation": "^3.0.0",
"@jupyterlab/ui-components": "^3.0.0",
"@lumino/algorithm": "^1.6.2",
"@lumino/commands": "^1.15.2",
"@lumino/coreutils": "^1.8.2",
"@lumino/disposable": "^1.7.2",
"@lumino/domutils": "^1.5.2",
"@lumino/dragdrop": "^1.10.2",
"@lumino/messaging": "^1.7.2",
"@lumino/properties": "^1.5.2",
"@lumino/signaling": "^1.7.2",
"@lumino/virtualdom": "^1.11.2",
"@lumino/widgets": "^1.26.2",
"react": "^17.0.1"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.3.1",
"@jupyterlab/builder": "^3.0.0",
"@types/node": "^18.8.3",
"babel-loader": "^8.0.5",
"css-loader": "^5.0.0",
"file-loader": "^4.0.0",
"css-loader": "~5.0.2",
"file-loader": "^6.2.0",
"fs-extra": "^9.1.0",
"glob": "~7.1.6",
"mini-css-extract-plugin": "~0.9.0",
"npm-run-all": "^4.1.5",
"p-limit": "^2.2.2",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"style-loader": "^2.0.0",
"svg-url-loader": "^7.1.1",
"typescript": "~4.1.3",
"url-loader": "^1.0.0",
"webpack": "^4.29.3",
"webpack-cli": "^3.2.3"
"url-loader": "^4.1.1",
"watch": "^1.0.2",
"webpack": "^5.24.1",
"webpack-bundle-analyzer": "^4.4.0",
"webpack-cli": "^4.5.0",
"webpack-merge": "^5.7.3",
"whatwg-fetch": "^3.0.0"
},
"style": "style/index.css",
"scripts": {
"build": "npm run build:lib && webpack",
"build": "npm run build:lib && webpack --mode=development",
"build:lib": "tsc",
"build:prod": "npm run build:lib && webpack --production",
"clean": "jlpm run clean:lib",
"build:prod": "npm run build:lib && webpack --mode=production",
"clean": "jlpm run clean:lib && rimraf build",
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "npm-run-all -p watch:*",
Expand Down
37 changes: 37 additions & 0 deletions packages/voila/publicpath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// We dynamically set the webpack public path based on the page config
// settings from the JupyterLab app. We copy some of the pageconfig parsing
// logic in @jupyterlab/coreutils below, since this must run before any other
// files are loaded (including @jupyterlab/coreutils).

/**
* Get global configuration data for the Jupyter application.
*
* @param name - The name of the configuration option.
*
* @returns The config value or an empty string if not found.
*
* #### Notes
* All values are treated as strings.
* For browser based applications, it is assumed that the page HTML
* includes a script tag with the id `jupyter-config-data` containing the
* configuration as valid JSON. In order to support the classic Notebook,
* we fall back on checking for `body` data of the given `name`.
*/
function getOption(name) {
let configData = Object.create(null);
// Use script tag if available.
if (typeof document !== 'undefined' && document) {
const el = document.getElementById('jupyter-config-data');

if (el) {
configData = JSON.parse(el.textContent || '{}');
}
}
return configData[name] || '';
}

// eslint-disable-next-line no-undef
__webpack_public_path__ = getOption('fullStaticUrl') + '/';
Loading

0 comments on commit 20caacc

Please sign in to comment.