diff --git a/app/assets/javascripts/discourse/app/components/plugin-outlet.js b/app/assets/javascripts/discourse/app/components/plugin-outlet.js index f73fbbcd12e..11a7f28641c 100644 --- a/app/assets/javascripts/discourse/app/components/plugin-outlet.js +++ b/app/assets/javascripts/discourse/app/components/plugin-outlet.js @@ -56,10 +56,8 @@ export default class PluginOutletComponent extends GlimmerComponentWithDeprecate }, }; - constructor() { - super(...arguments); - - this.connectors = renderedConnectorsFor( + get connectors() { + return renderedConnectorsFor( this.args.name, this.outletArgsWithDeprecations, this.context diff --git a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-connector-class-test.js b/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-connector-class-test.js deleted file mode 100644 index f5186065f1b..00000000000 --- a/app/assets/javascripts/discourse/tests/acceptance/plugin-outlet-connector-class-test.js +++ /dev/null @@ -1,102 +0,0 @@ -import { - acceptance, - count, - exists, - query, -} from "discourse/tests/helpers/qunit-helpers"; -import { click, visit } from "@ember/test-helpers"; -import { action } from "@ember/object"; -import { extraConnectorClass } from "discourse/lib/plugin-connectors"; -import { hbs } from "ember-cli-htmlbars"; -import { test } from "qunit"; -import { registerTemporaryModule } from "discourse/tests/helpers/temporary-module-helper"; - -const PREFIX = "discourse/plugins/some-plugin/templates/connectors"; - -acceptance("Plugin Outlet - Connector Class", function (needs) { - needs.hooks.beforeEach(() => { - extraConnectorClass("user-profile-primary/hello", { - actions: { - sayHello() { - this.set("hello", `${this.hello || ""}hello!`); - }, - }, - }); - - extraConnectorClass("user-profile-primary/hi", { - setupComponent() { - this.appEvents.on("hi:sayHi", this, this.say); - }, - - teardownComponent() { - this.appEvents.off("hi:sayHi", this, this.say); - }, - - @action - say() { - this.set("hi", "hi!"); - }, - - @action - sayHi() { - this.appEvents.trigger("hi:sayHi"); - }, - }); - - extraConnectorClass("user-profile-primary/dont-render", { - shouldRender(args) { - return args.model.get("username") !== "eviltrout"; - }, - }); - - registerTemporaryModule( - `${PREFIX}/user-profile-primary/hello`, - hbs`{{model.username}} - - - {{hello}}` - ); - registerTemporaryModule( - `${PREFIX}/user-profile-primary/hi`, - hbs` - {{hi}}` - ); - registerTemporaryModule( - `${PREFIX}/user-profile-primary/dont-render`, - hbs`I'm not rendered!` - ); - }); - - test("Renders a template into the outlet", async function (assert) { - await visit("/u/eviltrout"); - assert.strictEqual( - count(".user-profile-primary-outlet.hello"), - 1, - "it has class names" - ); - assert.ok( - !exists(".user-profile-primary-outlet.dont-render"), - "doesn't render" - ); - - await click(".say-hello"); - assert.strictEqual( - query(".hello-result").innerText, - "hello!", - "actions delegate properly" - ); - await click(".say-hello-using-this"); - assert.strictEqual( - query(".hello-result").innerText, - "hello!hello!", - "actions are made available on `this` and are bound correctly" - ); - - await click(".say-hi"); - assert.strictEqual( - query(".hi-result").innerText, - "hi!", - "actions delegate properly" - ); - }); -}); diff --git a/app/assets/javascripts/discourse/tests/integration/components/plugin-outlet-test.js b/app/assets/javascripts/discourse/tests/integration/components/plugin-outlet-test.js new file mode 100644 index 00000000000..fa45cfb08cc --- /dev/null +++ b/app/assets/javascripts/discourse/tests/integration/components/plugin-outlet-test.js @@ -0,0 +1,149 @@ +import { module, test } from "qunit"; +import { count, exists, query } from "discourse/tests/helpers/qunit-helpers"; +import { setupRenderingTest } from "discourse/tests/helpers/component-test"; +import { click, render, settled } from "@ember/test-helpers"; +import { action } from "@ember/object"; +import { extraConnectorClass } from "discourse/lib/plugin-connectors"; +import { hbs } from "ember-cli-htmlbars"; +import { registerTemporaryModule } from "discourse/tests/helpers/temporary-module-helper"; +import { getOwner } from "discourse-common/lib/get-owner"; + +const PREFIX = "discourse/plugins/some-plugin/templates/connectors"; + +module("Integration | Component | plugin-outlet", function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + extraConnectorClass("test-name/hello", { + actions: { + sayHello() { + this.set("hello", `${this.hello || ""}hello!`); + }, + }, + }); + + extraConnectorClass("test-name/hi", { + setupComponent() { + this.appEvents.on("hi:sayHi", this, this.say); + }, + + teardownComponent() { + this.appEvents.off("hi:sayHi", this, this.say); + }, + + @action + say() { + this.set("hi", "hi!"); + }, + + @action + sayHi() { + this.appEvents.trigger("hi:sayHi"); + }, + }); + + extraConnectorClass("test-name/conditional-render", { + shouldRender(args, context) { + return args.shouldDisplay || context.siteSettings.always_display; + }, + }); + + registerTemporaryModule( + `${PREFIX}/test-name/hello`, + hbs`{{username}} + + + {{hello}}` + ); + registerTemporaryModule( + `${PREFIX}/test-name/hi`, + hbs` + {{hi}}` + ); + registerTemporaryModule( + `${PREFIX}/test-name/conditional-render`, + hbs`I only render sometimes` + ); + }); + + test("Renders a template into the outlet", async function (assert) { + this.set("shouldDisplay", false); + await render( + hbs`` + ); + assert.strictEqual(count(".hello-username"), 1, "renders the hello outlet"); + assert.false( + exists(".conditional-render"), + "doesn't render conditional outlet" + ); + + await click(".say-hello"); + assert.strictEqual( + query(".hello-result").innerText, + "hello!", + "actions delegate properly" + ); + await click(".say-hello-using-this"); + assert.strictEqual( + query(".hello-result").innerText, + "hello!hello!", + "actions are made available on `this` and are bound correctly" + ); + + await click(".say-hi"); + assert.strictEqual( + query(".hi-result").innerText, + "hi!", + "actions delegate properly" + ); + }); + + test("Reevaluates shouldRender for argument changes", async function (assert) { + this.set("shouldDisplay", false); + await render( + hbs`` + ); + assert.false( + exists(".conditional-render"), + "doesn't render conditional outlet" + ); + + this.set("shouldDisplay", true); + await settled(); + assert.true(exists(".conditional-render"), "renders conditional outlet"); + }); + + test("Reevaluates shouldRender for other autotracked changes", async function (assert) { + this.set("shouldDisplay", false); + await render( + hbs`` + ); + assert.false( + exists(".conditional-render"), + "doesn't render conditional outlet" + ); + + getOwner(this).lookup("service:site-settings").always_display = true; + await settled(); + assert.true(exists(".conditional-render"), "renders conditional outlet"); + }); + + test("Other outlets are not re-rendered", async function (assert) { + this.set("shouldDisplay", false); + await render( + hbs`` + ); + + const otherOutletElement = query(".hello-username"); + otherOutletElement.someUniqueProperty = true; + + this.set("shouldDisplay", true); + await settled(); + assert.true(exists(".conditional-render"), "renders conditional outlet"); + + assert.true( + query(".hello-username").someUniqueProperty, + "other outlet is left untouched" + ); + }); +});