Skip to content

Commit

Permalink
Implement Lumino lifecycle events with native elements. (#2)
Browse files Browse the repository at this point in the history
* Implement Lumino lifecycle events with native elements.

This fixes the after-attach event to occur when the element is added to
a live DOM tree, fixing
#1.

* Cleanup of imports.
  • Loading branch information
blois authored Sep 2, 2021
1 parent 2df41bd commit 95a9324
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 10 deletions.
58 changes: 48 additions & 10 deletions src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
import { WidgetModel, WidgetView, IClassicComm } from '@jupyter-widgets/base';
import * as base from '@jupyter-widgets/base';
import * as controls from '@jupyter-widgets/controls';
import {ManagerBase} from '@jupyter-widgets/base-manager';
import { ManagerBase } from '@jupyter-widgets/base-manager';
import { JSONObject } from '@lumino/coreutils';
import {Loader} from './amd';
import { Widget } from '@lumino/widgets';

import { Loader } from './amd';
import { IComm, IWidgetManager, WidgetEnvironment } from './api';
import {swizzle} from './swizzle';
import { swizzle } from './swizzle';

export class Manager extends ManagerBase implements IWidgetManager {
private readonly models = new Map<string, Promise<WidgetModel>>();
Expand Down Expand Up @@ -135,13 +137,9 @@ export class Manager extends ManagerBase implements IWidgetManager {
conflate: () => false,
});

container.appendChild(view.el);

view.luminoWidget.processMessage({
type: 'after-attach',
isConflatable: false,
conflate: () => false,
});
const lifecycleAdapter = new LuminoLifecycleAdapter(view.luminoWidget);
lifecycleAdapter.appendChild(view.el);
container.appendChild(lifecycleAdapter);
}
}

Expand Down Expand Up @@ -221,4 +219,44 @@ class ClassicComm implements IClassicComm {
get comm_id() {
return this.id;
}
}

/**
* Custom element to provide Lumino lifecycle events driven by native DOM
* events.
*/
class LuminoLifecycleAdapter extends HTMLElement {
constructor(private readonly widget?: Widget) {
super();
}
connectedCallback() {
if (this.widget) {
this.widget.processMessage({
type: 'after-attach',
isConflatable: false,
conflate: () => false,
});
}
}
disconnectedCallback() {
if (this.widget) {
// We don't have a native event for before-detach, so just fire before
// the after-detach.
this.widget.processMessage({
type: 'before-detach',
isConflatable: false,
conflate: () => false,
});
this.widget.processMessage({
type: 'after-detach',
isConflatable: false,
conflate: () => false,
})
}
}
}
try {
window.customElements.define('colab-lumino-adapter', LuminoLifecycleAdapter);
} catch (error: unknown) {
// May have already been defined.
}
52 changes: 52 additions & 0 deletions test/manager.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,56 @@ describe('widget manager', () => {
await new Promise((resolve)=> setTimeout(resolve, 100));
window.onerror = oldHandler;
});

it('has proper lifecycle events', async () => {
const provider = new FakeState({
'123': {
state: {
"_view_module": "custom-widget",
"_view_name": "View",
},
"model_module": "custom-widget",
"model_name": "Model",
}
});
const manager = createWidgetManager(provider);
let modelClass;
let viewClass;

manager.loader.define('custom-widget', ['@jupyter-widgets/base'], (base) => {
class Model extends base.DOMWidgetModel {
constructor(...args) {
super(...args);
}
}
class View extends base.DOMWidgetView {
constructor(...args) {
super(...args);
this.hasBeenDisplayed = false;
this.displayed.then(() => {
this.hasBeenDisplayed = true;
});
}
}
modelClass = Model;
viewClass = View;

return {
Model,
View,
}
});

container.remove();

await manager.render('123', container);
const model = await manager.get_model('123');
expect(model).toBeInstanceOf(modelClass);
const view = await Object.values(model.views)[0];
expect(view).toBeInstanceOf(viewClass);
expect(view.hasBeenDisplayed).toBe(false);
document.body.appendChild(container);
await new Promise((resolve) => setTimeout(resolve, 0));
expect(view.hasBeenDisplayed).toBe(true);
});
});

0 comments on commit 95a9324

Please sign in to comment.