DEV: Convert PluginOutlet wrapper to a Glimmer component
PluginConnector remains a Classic Component, so this commit does not require any changes from plugin/theme developers. Two shims are introduced for backwards compatibility: - The component variable passed to shouldRender is replaced with a helperContext instance which includes all the common injections (the new PluginOutlet component instance does not have any of these) - A custom component manager is introduced so that parentView continues to work. Using parentView was never really intended as an API, so it's now deprecated and will print a warning to the console. Users should switch to using the outlet's explicit arguments, or data from a service (e.g. the Router service).
This commit is contained in:
parent
96a6bb69b5
commit
30025a96f3
|
@ -1,8 +1,19 @@
|
||||||
import Component from "@ember/component";
|
import GlimmerComponentWithDeprecatedParentView from "discourse/components/glimmer-component-with-deprecated-parent-view";
|
||||||
|
import ClassicComponent from "@ember/component";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
buildArgsWithDeprecations,
|
buildArgsWithDeprecations,
|
||||||
renderedConnectorsFor,
|
renderedConnectorsFor,
|
||||||
} from "discourse/lib/plugin-connectors";
|
} from "discourse/lib/plugin-connectors";
|
||||||
|
import { helperContext } from "discourse-common/lib/helpers";
|
||||||
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
|
import { get } from "@ember/object";
|
||||||
|
import { cached } from "@glimmer/tracking";
|
||||||
|
|
||||||
|
const PARENT_VIEW_DEPRECATION_MSG =
|
||||||
|
"parentView should not be used within plugin outlets. Use the available outlet arguments, or inject a service which can provide the context you need.";
|
||||||
|
const GET_DEPRECATION_MSG =
|
||||||
|
"Plugin outlet context is no longer an EmberObject - using `get()` is deprecated.";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A plugin outlet is an extension point for templates where other templates can
|
A plugin outlet is an extension point for templates where other templates can
|
||||||
|
@ -13,7 +24,7 @@ import {
|
||||||
If your handlebars template has:
|
If your handlebars template has:
|
||||||
|
|
||||||
```handlebars
|
```handlebars
|
||||||
{{plugin-outlet name="evil-trout"}}
|
<PluginOutlet @name="evil-trout" />
|
||||||
```
|
```
|
||||||
|
|
||||||
Then any handlebars files you create in the `connectors/evil-trout` directory
|
Then any handlebars files you create in the `connectors/evil-trout` directory
|
||||||
|
@ -29,26 +40,74 @@ import {
|
||||||
|
|
||||||
Will insert <b>Hello World</b> at that point in the template.
|
Will insert <b>Hello World</b> at that point in the template.
|
||||||
|
|
||||||
## Disabling
|
|
||||||
|
|
||||||
If a plugin returns a disabled status, the outlets will not be wired up for it.
|
|
||||||
The list of disabled plugins is returned via the `Site` singleton.
|
|
||||||
|
|
||||||
**/
|
**/
|
||||||
export default Component.extend({
|
|
||||||
tagName: "",
|
|
||||||
connectorTagName: "",
|
|
||||||
connectors: null,
|
|
||||||
|
|
||||||
init() {
|
export default class PluginOutletComponent extends GlimmerComponentWithDeprecatedParentView {
|
||||||
this._super(...arguments);
|
context = {
|
||||||
const name = this.name;
|
...helperContext(),
|
||||||
if (name) {
|
get parentView() {
|
||||||
const args = buildArgsWithDeprecations(
|
return this.parentView;
|
||||||
this.args || {},
|
},
|
||||||
this.deprecatedArgs || {}
|
get() {
|
||||||
);
|
deprecated(GET_DEPRECATION_MSG, {
|
||||||
this.set("connectors", renderedConnectorsFor(name, args, this));
|
id: "discourse.plugin-outlet-context-get",
|
||||||
|
});
|
||||||
|
return get(this, ...arguments);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
|
||||||
|
this.connectors = renderedConnectorsFor(
|
||||||
|
this.args.name,
|
||||||
|
this.outletArgsWithDeprecations,
|
||||||
|
this.context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traditionally, pluginOutlets had an argument named 'args'. However, that name is reserved
|
||||||
|
// in recent versions of ember so we need to migrate to outletArgs
|
||||||
|
@cached
|
||||||
|
get outletArgs() {
|
||||||
|
return this.args.outletArgs || this.args.args || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
@cached
|
||||||
|
get outletArgsWithDeprecations() {
|
||||||
|
if (!this.args.deprecatedArgs) {
|
||||||
|
return this.outletArgs;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
});
|
return buildArgsWithDeprecations(
|
||||||
|
this.outletArgs,
|
||||||
|
this.args.deprecatedArgs || {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get parentView() {
|
||||||
|
deprecated(`${PARENT_VIEW_DEPRECATION_MSG} (outlet: ${this.args.name})`, {
|
||||||
|
id: "discourse.plugin-outlet-parent-view",
|
||||||
|
});
|
||||||
|
return this._parentView;
|
||||||
|
}
|
||||||
|
set parentView(value) {
|
||||||
|
this._parentView = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Older plugin outlets have a `tagName` which we need to preserve for backwards-compatibility
|
||||||
|
get wrapperComponent() {
|
||||||
|
return PluginOutletWithTagNameWrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PluginOutletWithTagNameWrapper extends ClassicComponent {
|
||||||
|
// Overridden parentView to make this wrapper 'transparent'
|
||||||
|
// Calling this will trigger the deprecation notice in PluginOutletComponent
|
||||||
|
get parentView() {
|
||||||
|
return this._parentView.parentView;
|
||||||
|
}
|
||||||
|
set parentView(value) {
|
||||||
|
this._parentView = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,28 @@
|
||||||
{{#each this.connectors as |c|}}
|
{{#if @tagName}}
|
||||||
<PluginConnector
|
{{!
|
||||||
@connector={{c}}
|
Older outlets have a wrapper tagName. RFC0389 proposes an interface for dynamic tag names, which we may want to use in future.
|
||||||
@args={{this.args}}
|
But for now, this classic component wrapper takes care of the tagName.
|
||||||
@deprecatedArgs={{this.deprecatedArgs}}
|
}}
|
||||||
@class={{c.classNames}}
|
<this.wrapperComponent @tagName={{@tagName}}>
|
||||||
@tagName={{this.connectorTagName}}
|
{{#each this.connectors as |c|}}
|
||||||
/>
|
<PluginConnector
|
||||||
{{/each}}
|
@connector={{c}}
|
||||||
|
@args={{this.outletArgs}}
|
||||||
|
@deprecatedArgs={{@deprecatedArgs}}
|
||||||
|
@class={{c.classNames}}
|
||||||
|
@tagName={{or @connectorTagName ""}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</this.wrapperComponent>
|
||||||
|
{{else}}
|
||||||
|
{{! The modern path: no wrapper element = no classic component }}
|
||||||
|
{{#each this.connectors as |c|}}
|
||||||
|
<PluginConnector
|
||||||
|
@connector={{c}}
|
||||||
|
@args={{this.outletArgs}}
|
||||||
|
@deprecatedArgs={{@deprecatedArgs}}
|
||||||
|
@class={{c.classNames}}
|
||||||
|
@tagName={{or @connectorTagName ""}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { acceptance, query } from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import { hbs } from "ember-cli-htmlbars";
|
||||||
|
import { test } from "qunit";
|
||||||
|
import { visit } from "@ember/test-helpers";
|
||||||
|
import { withSilencedDeprecationsAsync } from "discourse-common/lib/deprecated";
|
||||||
|
import { registerTemporaryModule } from "discourse/tests/helpers/temporary-module-helper";
|
||||||
|
|
||||||
|
acceptance("Plugin Outlet - Deprecated parentView", function (needs) {
|
||||||
|
needs.hooks.beforeEach(function () {
|
||||||
|
registerTemporaryModule(
|
||||||
|
"discourse/templates/connectors/user-profile-primary/hello",
|
||||||
|
hbs`<span class='hello-username'>{{parentView.parentView.class}}</span>`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Can access parentview", async function (assert) {
|
||||||
|
await withSilencedDeprecationsAsync(
|
||||||
|
"discourse.plugin-outlet-parent-view",
|
||||||
|
async () => {
|
||||||
|
await visit("/u/eviltrout");
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".hello-username").innerText,
|
||||||
|
"user-main",
|
||||||
|
"it renders a value from parentView.parentView"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue