diff --git a/app/assets/javascripts/discourse/app/helpers/plugin-outlet.gjs b/app/assets/javascripts/discourse/app/helpers/plugin-outlet.gjs
new file mode 100644
index 00000000000..f695a967ab2
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/helpers/plugin-outlet.gjs
@@ -0,0 +1,26 @@
+import { htmlSafe } from "@ember/template";
+import PluginOutlet from "discourse/components/plugin-outlet";
+import { connectorsExist } from "discourse/lib/plugin-connectors";
+import rawRenderGlimmer from "discourse/lib/raw-render-glimmer";
+import RawHandlebars from "discourse-common/lib/raw-handlebars";
+
+RawHandlebars.registerHelper("plugin-outlet", function (options) {
+ const { name, tagName, outletArgs } = options.hash;
+
+ if (!connectorsExist(name)) {
+ return htmlSafe("");
+ }
+
+ return htmlSafe(
+ rawRenderGlimmer(
+ this,
+ `${tagName || "span"}.hbr-ember-outlet`,
+
+ {{~! no whitespace ~}}
+
+ {{~! no whitespace ~}}
+ ,
+ { name, outletArgs }
+ )
+ );
+});
diff --git a/app/assets/javascripts/discourse/app/helpers/raw-hash.js b/app/assets/javascripts/discourse/app/helpers/raw-hash.js
new file mode 100644
index 00000000000..3f5329fa257
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/helpers/raw-hash.js
@@ -0,0 +1,5 @@
+import { registerRawHelper } from "discourse-common/lib/helpers";
+
+registerRawHelper("raw-hash", function (params) {
+ return params;
+});
diff --git a/app/assets/javascripts/discourse/app/lib/raw-render-glimmer.js b/app/assets/javascripts/discourse/app/lib/raw-render-glimmer.js
index 8a1e7c488e8..da0eb9815ce 100644
--- a/app/assets/javascripts/discourse/app/lib/raw-render-glimmer.js
+++ b/app/assets/javascripts/discourse/app/lib/raw-render-glimmer.js
@@ -12,7 +12,7 @@ let counter = 0;
*
* ```hbs
* {{! raw-templates/something-cool.hbr }}
- * {{{view.html}}}
+ * {{html-safe view.html}}
* ```
*
* ```gjs
@@ -21,7 +21,7 @@ let counter = 0;
* import rawRenderGlimmer from "discourse/lib/raw-render-glimmer";
*
* export default class SomethingCool extends EmberObject {
- * get html(){
+ * get html() {
* return rawRenderGlimmer(this, "div", Hello {{@data.name}} , { name: this.name });
* }
* ```
diff --git a/app/assets/javascripts/discourse/app/raw-templates/list/activity-column.hbr b/app/assets/javascripts/discourse/app/raw-templates/list/activity-column.hbr
index 384909db023..d2d4313272c 100644
--- a/app/assets/javascripts/discourse/app/raw-templates/list/activity-column.hbr
+++ b/app/assets/javascripts/discourse/app/raw-templates/list/activity-column.hbr
@@ -1,6 +1,7 @@
<{{tagName}} class="{{class}} {{cold-age-class topic.createdAt startDate=topic.bumpedAt class=""}} activity" title="{{html-safe topic.bumpedAtTitle}}">
{{~raw-plugin-outlet name="topic-list-before-relative-date"~}}
+ {{~plugin-outlet name="topic-list-before-relative-date" outletArgs=(raw-hash topic=topic)~}}
{{~format-date topic.bumpedAt format="tiny" noTitle="true"~}}
{{tagName}}>
diff --git a/app/assets/javascripts/discourse/app/raw-templates/list/posts-count-column.hbr b/app/assets/javascripts/discourse/app/raw-templates/list/posts-count-column.hbr
index 71d40199e97..28a7f200960 100644
--- a/app/assets/javascripts/discourse/app/raw-templates/list/posts-count-column.hbr
+++ b/app/assets/javascripts/discourse/app/raw-templates/list/posts-count-column.hbr
@@ -1,6 +1,7 @@
<{{view.tagName}} class='num posts-map posts {{view.likesHeat}} topic-list-data'>
{{raw-plugin-outlet name="topic-list-before-reply-count"}}
+ {{plugin-outlet name="topic-list-before-reply-count" outletArgs=(raw-hash topic=topic)}}
{{number topic.replyCount noTitle="true"}}
{{view.tagName}}>
diff --git a/app/assets/javascripts/discourse/app/raw-templates/list/topic-list-item.hbr b/app/assets/javascripts/discourse/app/raw-templates/list/topic-list-item.hbr
index c24e5be24f3..0cdb6e8a453 100644
--- a/app/assets/javascripts/discourse/app/raw-templates/list/topic-list-item.hbr
+++ b/app/assets/javascripts/discourse/app/raw-templates/list/topic-list-item.hbr
@@ -17,14 +17,17 @@
--}}
{{~raw-plugin-outlet name="topic-list-before-link"}}
+ {{~plugin-outlet name="topic-list-before-link" outletArgs=(raw-hash topic=topic)}}
{{~raw-plugin-outlet name="topic-list-before-status"}}
+ {{~plugin-outlet name="topic-list-before-status" outletArgs=(raw-hash topic=topic)}}
{{~raw "topic-status" topic=topic}}
{{~topic-link topic class="raw-link raw-topic-link"}}
{{~#if topic.featured_link}}
{{~topic-featured-link topic}}
{{~/if}}
{{~raw-plugin-outlet name="topic-list-after-title"}}
+ {{~plugin-outlet name="topic-list-after-title" outletArgs=(raw-hash topic=topic)}}
{{~raw "list/unread-indicator" includeUnreadIndicator=includeUnreadIndicator
topicId=topic.id
unreadClass=unreadClass~}}
@@ -36,6 +39,7 @@
{{#unless hideCategory}}
{{#unless topic.isPinnedUncategorized}}
{{~raw-plugin-outlet name="topic-list-before-category"}}
+ {{~plugin-outlet name="topic-list-before-category" outletArgs=(raw-hash topic=topic)}}
{{category-link topic.category}}
{{/unless}}
{{/unless}}
@@ -50,9 +54,11 @@
{{/if}}
{{~raw-plugin-outlet name="topic-list-main-link-bottom"}}
+ {{~plugin-outlet name="topic-list-main-link-bottom" outletArgs=(raw-hash topic=topic)}}
{{~raw-plugin-outlet name="topic-list-after-main-link"}}
+{{~plugin-outlet name="topic-list-after-main-link" outletArgs=(raw-hash topic=topic)}}
{{#if showPosters}}
{{raw "list/posters-column" posters=topic.featuredUsers}}
@@ -82,6 +88,7 @@
{{raw-plugin-outlet name="topic-list-before-view-count"}}
+ {{plugin-outlet name="topic-list-before-view-count" outletArgs=(raw-hash topic=topic)}}
{{number topic.views numberKey="views_long"}}
diff --git a/app/assets/javascripts/discourse/app/raw-templates/mobile/list/topic-list-item.hbr b/app/assets/javascripts/discourse/app/raw-templates/mobile/list/topic-list-item.hbr
index 4acd87b41d2..8c56a7f6b99 100644
--- a/app/assets/javascripts/discourse/app/raw-templates/mobile/list/topic-list-item.hbr
+++ b/app/assets/javascripts/discourse/app/raw-templates/mobile/list/topic-list-item.hbr
@@ -18,14 +18,17 @@
at the end of the link, preventing it from line wrapping onto its own line.
--}}
{{~raw-plugin-outlet name="topic-list-before-link"}}
+ {{~plugin-outlet name="topic-list-before-link" outletArgs=(raw-hash topic=topic)}}
{{~raw-plugin-outlet name="topic-list-before-status"}}
+ {{~plugin-outlet name="topic-list-before-status" outletArgs=(raw-hash topic=topic)}}
{{~raw "topic-status" topic=topic~}}
{{~topic-link topic class="raw-link raw-topic-link"}}
{{~#if topic.featured_link~}}
{{~topic-featured-link topic~}}
{{~/if~}}
{{~raw-plugin-outlet name="topic-list-after-title"}}
+ {{~plugin-outlet name="topic-list-after-title" outletArgs=(raw-hash topic=topic)}}
{{~#if topic.unseen~}}
{{~/if~}}
@@ -33,8 +36,10 @@
{{~raw "list/topic-excerpt" topic=topic~}}
{{~/if~}}
{{~raw-plugin-outlet name="topic-list-main-link-bottom"}}
+ {{~plugin-outlet name="topic-list-main-link-bottom" outletArgs=(raw-hash topic=topic)}}
{{~raw-plugin-outlet name="topic-list-after-main-link"}}
+ {{~plugin-outlet name="topic-list-after-main-link" outletArgs=(raw-hash topic=topic)}}
{{raw "list/post-count-or-badges" topic=topic postBadgesEnabled=showTopicPostBadges}}
@@ -42,6 +47,7 @@
{{#unless hideCategory}}
{{~raw-plugin-outlet name="topic-list-before-category"}}
+ {{~plugin-outlet name="topic-list-before-category" outletArgs=(raw-hash topic=topic)}}
{{category-link topic.category~}}
{{~/unless}}
{{~discourse-tags topic mode="list"}}
diff --git a/app/assets/javascripts/discourse/app/raw-templates/topic-list-header.hbr b/app/assets/javascripts/discourse/app/raw-templates/topic-list-header.hbr
index 11543c5151f..fbd04182db8 100644
--- a/app/assets/javascripts/discourse/app/raw-templates/topic-list-header.hbr
+++ b/app/assets/javascripts/discourse/app/raw-templates/topic-list-header.hbr
@@ -1,4 +1,5 @@
{{~raw-plugin-outlet name="topic-list-header-before"~}}
+{{~plugin-outlet name="topic-list-header-before"~}}
{{#if bulkSelectEnabled}}
{{#if canBulkSelect}}
@@ -8,6 +9,7 @@
{{/if}}
{{raw "topic-list-header-column" order='default' name=listTitle bulkSelectEnabled=bulkSelectEnabled showBulkToggle=toggleInTitle canBulkSelect=canBulkSelect canDoBulkActions=canDoBulkActions showTopicsAndRepliesToggle=showTopicsAndRepliesToggle newListSubset=newListSubset newRepliesCount=newRepliesCount newTopicsCount=newTopicsCount bulkSelectHelper=bulkSelectHelper }}
{{raw-plugin-outlet name="topic-list-header-after-main-link"}}
+{{plugin-outlet name="topic-list-header-after-main-link"}}
{{#if showPosters}}
{{raw "topic-list-header-column" name='posters' screenreaderOnly='true'}}
{{/if}}
@@ -21,3 +23,4 @@
{{raw "topic-list-header-column" sortable=sortable number='true' order='views' name='views'}}
{{raw "topic-list-header-column" sortable=sortable number='true' order='activity' name='activity'}}
{{~raw-plugin-outlet name="topic-list-header-after"~}}
+{{~plugin-outlet name="topic-list-header-after"~}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/tests/acceptance/hbr-plugin-outlet-test.gjs b/app/assets/javascripts/discourse/tests/acceptance/hbr-plugin-outlet-test.gjs
new file mode 100644
index 00000000000..472110f9f17
--- /dev/null
+++ b/app/assets/javascripts/discourse/tests/acceptance/hbr-plugin-outlet-test.gjs
@@ -0,0 +1,20 @@
+import { visit } from "@ember/test-helpers";
+import { hbs } from "ember-cli-htmlbars";
+import { test } from "qunit";
+import { acceptance } from "discourse/tests/helpers/qunit-helpers";
+import { registerTemporaryModule } from "discourse/tests/helpers/temporary-module-helper";
+
+acceptance("Hbr Plugin Outlet", function (needs) {
+ needs.hooks.beforeEach(function () {
+ registerTemporaryModule(
+ "discourse/theme-12/templates/connectors/topic-list-before-link/hello",
+ hbs`{{@outletArgs.topic.id}} `
+ );
+ });
+
+ test("renders ember plugin outlets in hbr contexts", async function (assert) {
+ await visit("/");
+ assert.dom(".lala").exists("renders the outlet");
+ assert.dom(".lala").hasText("11557", "has the topic id");
+ });
+});
diff --git a/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js b/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js
index 7c042fa03b4..e04a3622d2e 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/raw-plugin-outlet-test.js
@@ -1,11 +1,7 @@
import { visit } from "@ember/test-helpers";
import { compile } from "handlebars";
import { test } from "qunit";
-import {
- acceptance,
- exists,
- query,
-} from "discourse/tests/helpers/qunit-helpers";
+import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import {
addRawTemplate,
removeRawTemplate,
@@ -15,23 +11,20 @@ const CONNECTOR =
"javascripts/raw-test/connectors/topic-list-before-status/lala";
acceptance("Raw Plugin Outlet", function (needs) {
- needs.hooks.beforeEach(() => {
+ needs.hooks.beforeEach(function () {
addRawTemplate(
CONNECTOR,
compile(`{{context.topic.id}} `)
);
});
- needs.hooks.afterEach(() => {
+ needs.hooks.afterEach(function () {
removeRawTemplate(CONNECTOR);
});
+
test("Renders the raw plugin outlet", async function (assert) {
await visit("/");
- assert.ok(exists(".topic-lala"), "it renders the outlet");
- assert.strictEqual(
- query(".topic-lala:nth-of-type(1)").innerText,
- "11557",
- "it has the topic id"
- );
+ assert.dom(".topic-lala").exists("renders the outlet");
+ assert.dom(".topic-lala").hasText("11557", "has the topic id");
});
});
diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss
index 5116f759ec8..ea543897798 100644
--- a/app/assets/stylesheets/common/base/discourse.scss
+++ b/app/assets/stylesheets/common/base/discourse.scss
@@ -836,3 +836,7 @@ a#skip-link {
overflow: hidden !important;
margin-right: var(--scroll-gap, 0);
}
+
+.hbr-ember-outlet {
+ display: contents;
+}