mirror of
https://github.com/discourse/discourse.git
synced 2025-05-29 11:48:08 +08:00
FEATURE: adds a new plugin api to decorate plugin outlets (#8937)
``` api.decoratePluginOutlet( "discovery-list-container-top", elem => { if (elem.classList.contains("foo")) { elem.style.backgroundColor = "yellow"; } } ); ```
This commit is contained in:
@ -2,6 +2,19 @@ import Component from "@ember/component";
|
|||||||
import { defineProperty, computed } from "@ember/object";
|
import { defineProperty, computed } from "@ember/object";
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
import { buildArgsWithDeprecations } from "discourse/lib/plugin-connectors";
|
import { buildArgsWithDeprecations } from "discourse/lib/plugin-connectors";
|
||||||
|
import { afterRender } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
|
let _decorators = {};
|
||||||
|
|
||||||
|
// Don't call this directly: use `plugin-api/decoratePluginOutlet`
|
||||||
|
export function addPluginOutletDecorator(outletName, callback) {
|
||||||
|
_decorators[outletName] = _decorators[outletName] || [];
|
||||||
|
_decorators[outletName].push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetDecorators() {
|
||||||
|
_decorators = {};
|
||||||
|
}
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
init() {
|
init() {
|
||||||
@ -45,6 +58,19 @@ export default Component.extend({
|
|||||||
connectorClass.setupComponent.call(this, merged, this);
|
connectorClass.setupComponent.call(this, merged, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
this._decoratePluginOutlets();
|
||||||
|
},
|
||||||
|
|
||||||
|
@afterRender
|
||||||
|
_decoratePluginOutlets() {
|
||||||
|
(_decorators[this.connector.outletName] || []).forEach(dec =>
|
||||||
|
dec(this.element, this.args)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
import { iconNode } from "discourse-common/lib/icon-library";
|
import { iconNode } from "discourse-common/lib/icon-library";
|
||||||
import { addDecorator } from "discourse/widgets/post-cooked";
|
import { addDecorator } from "discourse/widgets/post-cooked";
|
||||||
|
import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
|
||||||
import ComposerEditor from "discourse/components/composer-editor";
|
import ComposerEditor from "discourse/components/composer-editor";
|
||||||
import DiscourseBanner from "discourse/components/discourse-banner";
|
import DiscourseBanner from "discourse/components/discourse-banner";
|
||||||
import { addButton } from "discourse/widgets/post-menu";
|
import { addButton } from "discourse/widgets/post-menu";
|
||||||
@ -51,7 +52,7 @@ import Composer from "discourse/models/composer";
|
|||||||
import { on } from "@ember/object/evented";
|
import { on } from "@ember/object/evented";
|
||||||
|
|
||||||
// If you add any methods to the API ensure you bump up this number
|
// If you add any methods to the API ensure you bump up this number
|
||||||
const PLUGIN_API_VERSION = "0.8.37";
|
const PLUGIN_API_VERSION = "0.8.38";
|
||||||
|
|
||||||
class PluginApi {
|
class PluginApi {
|
||||||
constructor(version, container) {
|
constructor(version, container) {
|
||||||
@ -983,6 +984,29 @@ class PluginApi {
|
|||||||
addGlobalNotice(id, text, options) {
|
addGlobalNotice(id, text, options) {
|
||||||
addGlobalNotice(id, text, options);
|
addGlobalNotice(id, text, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for decorating the rendered HTML content of a plugin-outlet after it's been rendered
|
||||||
|
*
|
||||||
|
* `callback` will be called when it is time to decorate it.
|
||||||
|
*
|
||||||
|
* For example, to add a yellow background to a connector:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* api.decoratePluginOutlet(
|
||||||
|
* "discovery-list-container-top",
|
||||||
|
* (elem, args) => {
|
||||||
|
* if (elem.classList.contains("foo")) {
|
||||||
|
* elem.style.backgroundColor = "yellow";
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* );
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
decoratePluginOutlet(outletName, callback, opts) {
|
||||||
|
addPluginOutletDecorator(outletName, callback, opts || {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _pluginv01;
|
let _pluginv01;
|
||||||
|
@ -73,6 +73,7 @@ function buildConnectorCache() {
|
|||||||
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
_connectorCache[outletName] = _connectorCache[outletName] || [];
|
||||||
|
|
||||||
_connectorCache[outletName].push({
|
_connectorCache[outletName].push({
|
||||||
|
outletName,
|
||||||
templateName: resource.replace("javascripts/", ""),
|
templateName: resource.replace("javascripts/", ""),
|
||||||
template: Ember.TEMPLATES[resource],
|
template: Ember.TEMPLATES[resource],
|
||||||
classNames: `${outletName}-outlet ${uniqueName}`,
|
classNames: `${outletName}-outlet ${uniqueName}`,
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
|
|
||||||
|
const PREFIX = "javascripts/single-test/connectors";
|
||||||
|
acceptance("Plugin Outlet - Decorator", {
|
||||||
|
loggedIn: true,
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
Ember.TEMPLATES[
|
||||||
|
`${PREFIX}/discovery-list-container-top/foo`
|
||||||
|
] = Ember.HTMLBars.compile("FOO");
|
||||||
|
Ember.TEMPLATES[
|
||||||
|
`${PREFIX}/discovery-list-container-top/bar`
|
||||||
|
] = Ember.HTMLBars.compile("BAR");
|
||||||
|
|
||||||
|
withPluginApi("0.8.38", api => {
|
||||||
|
api.decoratePluginOutlet(
|
||||||
|
"discovery-list-container-top",
|
||||||
|
(elem, args) => {
|
||||||
|
if (elem.classList.contains("foo")) {
|
||||||
|
elem.style.backgroundColor = "yellow";
|
||||||
|
|
||||||
|
if (args.category) {
|
||||||
|
elem.classList.add("in-category");
|
||||||
|
} else {
|
||||||
|
elem.classList.remove("in-category");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ id: "yellow-decorator" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
afterEach() {
|
||||||
|
delete Ember.TEMPLATES[`${PREFIX}/discovery-list-container-top/foo`];
|
||||||
|
delete Ember.TEMPLATES[`${PREFIX}/discovery-list-container-top/bar`];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test(
|
||||||
|
"Calls the plugin callback with the rendered outlet",
|
||||||
|
async assert => {
|
||||||
|
await visit("/");
|
||||||
|
|
||||||
|
const fooConnector = find(".discovery-list-container-top-outlet.foo ")[0];
|
||||||
|
const barConnector = find(".discovery-list-container-top-outlet.bar ")[0];
|
||||||
|
|
||||||
|
assert.ok(exists(fooConnector));
|
||||||
|
assert.equal(fooConnector.style.backgroundColor, "yellow");
|
||||||
|
assert.equal(barConnector.style.backgroundColor, "");
|
||||||
|
|
||||||
|
await visit("/c/bug");
|
||||||
|
|
||||||
|
assert.ok(fooConnector.classList.contains("in-category"));
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
|
||||||
|
assert.notOk(fooConnector.classList.contains("in-category"));
|
||||||
|
}
|
||||||
|
);
|
@ -18,6 +18,7 @@ import { initSearchData } from "discourse/widgets/search-menu";
|
|||||||
import { resetDecorators } from "discourse/widgets/widget";
|
import { resetDecorators } from "discourse/widgets/widget";
|
||||||
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
|
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
|
||||||
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
||||||
|
import { resetDecorators as resetPluginOutletDecorators } from "discourse/components/plugin-connector";
|
||||||
import { resetCache as resetOneboxCache } from "pretty-text/oneboxer";
|
import { resetCache as resetOneboxCache } from "pretty-text/oneboxer";
|
||||||
import { resetCustomPostMessageCallbacks } from "discourse/controllers/topic";
|
import { resetCustomPostMessageCallbacks } from "discourse/controllers/topic";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
@ -128,6 +129,7 @@ export function acceptance(name, options) {
|
|||||||
initSearchData();
|
initSearchData();
|
||||||
resetDecorators();
|
resetDecorators();
|
||||||
resetPostCookedDecorators();
|
resetPostCookedDecorators();
|
||||||
|
resetPluginOutletDecorators();
|
||||||
resetOneboxCache();
|
resetOneboxCache();
|
||||||
resetCustomPostMessageCallbacks();
|
resetCustomPostMessageCallbacks();
|
||||||
Discourse._runInitializer("instanceInitializers", function(
|
Discourse._runInitializer("instanceInitializers", function(
|
||||||
|
Reference in New Issue
Block a user