diff --git a/js/src/errorwidget.js b/js/src/errorwidget.js new file mode 100644 index 000000000..27a76aeda --- /dev/null +++ b/js/src/errorwidget.js @@ -0,0 +1,32 @@ +import * as widgets from "@jupyter-widgets/base"; + +// create a Widget Model that captures an error object +export +function createErrorWidget(error) { + class ErrorWidget extends widgets.DOMWidgetModel { + constructor(attributes, options) { + attributes = {...attributes, + _view_name: 'ErrorWidgetView', + _view_module: 'voila-errorwidget', + _model_module_version: '1.0.0', + _view_module_version: '1.0.0', + failed_module: attributes._view_module, + failed_model_name: attributes._model_name, + error: error} + super(attributes, options); + console.log(attributes); + } + } + return ErrorWidget; +} + +export +class ErrorWidgetView extends widgets.DOMWidgetView { + render() { + console.log('render'); + const module = this.model.get('failed_module'); + const name = this.model.get('failed_model_name'); + const error = String(this.model.get('error').stack); + this.el.innerHTML = `Failed to load widget '${name}' from module '${module}', error:
${error}`; + } +} diff --git a/js/src/manager.js b/js/src/manager.js index 3f927d4dc..5845aa726 100644 --- a/js/src/manager.js +++ b/js/src/manager.js @@ -31,6 +31,7 @@ import { MessageLoop } from '@phosphor/messaging'; import { requireLoader } from './loader'; import { batchRateMap } from './utils'; +import * as errorwidget from './errorwidget'; if (typeof window !== "undefined" && typeof window.define !== "undefined") { window.define("@jupyter-widgets/base", base); @@ -49,6 +50,8 @@ if (typeof window !== "undefined" && typeof window.define !== "undefined") { window.define("@phosphor/algorithm", PhosphorAlgorithm); window.define("@phosphor/commands", PhosphorCommands); window.define("@phosphor/domutils", PhosphorDomutils); + + window.define("voila-errorwidget", errorwidget); } const WIDGET_MIMETYPE = 'application/vnd.jupyter.widget-view+json'; @@ -106,10 +109,19 @@ export class WidgetManager extends JupyterLabManager { } async loadClass(className, moduleName, moduleVersion) { + try { + return await this._loadClass(className, moduleName, moduleVersion) + } catch(error) { + return errorwidget.createErrorWidget(error); + }; + } + + async _loadClass(className, moduleName, moduleVersion) { if ( moduleName === '@jupyter-widgets/base' || moduleName === '@jupyter-widgets/controls' || - moduleName === '@jupyter-widgets/output' + moduleName === '@jupyter-widgets/output' || + moduleName === 'voila-errorwidget' ) { return super.loadClass(className, moduleName, moduleVersion); } @@ -120,7 +132,7 @@ export class WidgetManager extends JupyterLabManager { return module[className]; } else { - return Promise.reject("Class " + className + " not found in module " + moduleName + "@" + moduleVersion); + throw Error("Class " + className + " not found in module " + moduleName + "@" + moduleVersion); } }) } @@ -145,6 +157,11 @@ export class WidgetManager extends JupyterLabManager { version: output.OUTPUT_WIDGET_VERSION, exports: output }); + this.register({ + name: 'voila-errorwidget', + version: '1.0.0', + exports: errorwidget + }); } async _build_models() {