DEV: Allow registering a widget shim which renders using hbs (#20177)

This is an alternative way to use `RenderGlimmer` which can be more ergonomic for iterative updates of a codebase. For documentation, see `widgets/render-glimmer.js`
This commit is contained in:
David Taylor 2023-02-06 14:10:44 +00:00 committed by GitHub
parent 2f8ad17aed
commit 95999c80ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 7 deletions

View File

@ -2,6 +2,7 @@ import templateOnly from "@ember/component/template-only";
import { setComponentTemplate } from "@ember/component"; import { setComponentTemplate } from "@ember/component";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { assert } from "@ember/debug"; import { assert } from "@ember/debug";
import { createWidgetFrom } from "discourse/widgets/widget";
/* /*
@ -89,7 +90,9 @@ export default class RenderGlimmer {
template.name === "factory" template.name === "factory"
); );
this.renderInto = renderInto; this.renderInto = renderInto;
if (widget) {
this.widget = widget; this.widget = widget;
}
this.template = template; this.template = template;
this.data = data; this.data = data;
} }
@ -108,7 +111,9 @@ export default class RenderGlimmer {
destroy() { destroy() {
if (this._componentInfo) { if (this._componentInfo) {
this.widget._findView().unmountChildComponent(this._componentInfo); this.parentMountWidgetComponent.unmountChildComponent(
this._componentInfo
);
} }
} }
@ -132,7 +137,7 @@ export default class RenderGlimmer {
} }
connectComponent() { connectComponent() {
const { element, template, widget } = this; const { element, template } = this;
const component = templateOnly(); const component = templateOnly();
component.name = "Widgets/RenderGlimmer"; component.name = "Widgets/RenderGlimmer";
@ -143,9 +148,39 @@ export default class RenderGlimmer {
component, component,
@tracked data: this.data, @tracked data: this.data,
}; };
const parentMountWidgetComponent = widget._findView();
parentMountWidgetComponent.mountChildComponent(this._componentInfo); this.parentMountWidgetComponent.mountChildComponent(this._componentInfo);
}
get parentMountWidgetComponent() {
return this.widget?._findView() || this._emberView;
} }
} }
RenderGlimmer.prototype.type = "Widget"; RenderGlimmer.prototype.type = "Widget";
/**
* Define a widget shim which renders a Glimmer template. Designed for incrementally migrating
* a widget-based UI to Glimmer. Widget attrs will be made available to your template at `@data`.
* For more details, see documentation for the RenderGlimmer class.
* @param name - the widget's name (which can then be used in `.attach` elsewhere)
* @param tagName - a string describing a new wrapper element (e.g. `div.my-class`)
* @param template - a glimmer template compiled via ember-cli-htmlbars
*/
export function registerWidgetShim(name, tagName, template) {
const RenderGlimmerShim = class MyClass extends RenderGlimmer {
constructor(attrs) {
super(null, tagName, template, attrs);
return this;
}
get widget() {
return this.parentWidget;
}
didRenderWidget() {}
willRerenderWidget() {}
};
createWidgetFrom(RenderGlimmerShim, name, {});
}

View File

@ -30,6 +30,10 @@ export function queryRegistry(name) {
return _registry[name]; return _registry[name];
} }
export function deleteFromRegistry(name) {
return delete _registry[name];
}
const _decorators = {}; const _decorators = {};
export function decorateWidget(widgetName, cb) { export function decorateWidget(widgetName, cb) {

View File

@ -4,9 +4,11 @@ import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { click, fillIn, render } from "@ember/test-helpers"; import { click, fillIn, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars"; import { hbs } from "ember-cli-htmlbars";
import widgetHbs from "discourse/widgets/hbs-compiler"; import widgetHbs from "discourse/widgets/hbs-compiler";
import Widget from "discourse/widgets/widget"; import Widget, { deleteFromRegistry } from "discourse/widgets/widget";
import ClassicComponent from "@ember/component"; import ClassicComponent from "@ember/component";
import RenderGlimmer from "discourse/widgets/render-glimmer"; import RenderGlimmer, {
registerWidgetShim,
} from "discourse/widgets/render-glimmer";
import { bind } from "discourse-common/utils/decorators"; import { bind } from "discourse-common/utils/decorators";
class DemoWidget extends Widget { class DemoWidget extends Widget {
@ -126,12 +128,18 @@ module("Integration | Component | Widget | render-glimmer", function (hooks) {
this.registry.register("widget:demo-widget", DemoWidget); this.registry.register("widget:demo-widget", DemoWidget);
this.registry.register("widget:toggle-demo-widget", ToggleDemoWidget); this.registry.register("widget:toggle-demo-widget", ToggleDemoWidget);
this.registry.register("component:demo-component", DemoComponent); this.registry.register("component:demo-component", DemoComponent);
registerWidgetShim(
"render-glimmer-test-shim",
"div.my-wrapper",
hbs`<span class='shim-content'>{{@data.attr1}}</span>`
);
}); });
hooks.afterEach(function () { hooks.afterEach(function () {
this.registry.unregister("widget:demo-widget"); this.registry.unregister("widget:demo-widget");
this.registry.unregister("widget:toggle-demo-widget"); this.registry.unregister("widget:toggle-demo-widget");
this.registry.unregister("component:demo-component"); this.registry.unregister("component:demo-component");
deleteFromRegistry("render-glimmer-test-shim");
}); });
test("argument handling", async function (assert) { test("argument handling", async function (assert) {
@ -310,4 +318,13 @@ module("Integration | Component | Widget | render-glimmer", function (hooks) {
await click(".toggleButton"); await click(".toggleButton");
assert.strictEqual(query("div.glimmer-wrapper").innerText, "One"); assert.strictEqual(query("div.glimmer-wrapper").innerText, "One");
}); });
test("registerWidgetShim can register a fake widget", async function (assert) {
await render(
hbs`<MountWidget @widget="render-glimmer-test-shim" @args={{hash attr1="val1"}} />`
);
assert.dom("div.my-wrapper span.shim-content").exists();
assert.dom("div.my-wrapper span.shim-content").hasText("val1");
});
}); });