diff --git a/app/assets/javascripts/discourse/app/components/decorated-html.gjs b/app/assets/javascripts/discourse/app/components/decorated-html.gjs
index 62402126816d7..f91ba1a0c772e 100644
--- a/app/assets/javascripts/discourse/app/components/decorated-html.gjs
+++ b/app/assets/javascripts/discourse/app/components/decorated-html.gjs
@@ -4,7 +4,11 @@ import { htmlSafe, isHTMLSafe } from "@ember/template";
import { TrackedArray } from "@ember-compat/tracked-built-ins";
import helperFn from "discourse/helpers/helper-fn";
import deprecated from "discourse/lib/deprecated";
-import { isRailsTesting, isTesting } from "discourse/lib/environment";
+import {
+ isProduction,
+ isRailsTesting,
+ isTesting,
+} from "discourse/lib/environment";
import { POST_STREAM_DEPRECATION_OPTIONS } from "discourse/widgets/widget";
const detachedDocument = document.implementation.createHTMLDocument("detached");
@@ -83,19 +87,47 @@ export default class DecoratedHtml extends Component {
return cookedDiv;
}
+ /**
+ * Checks if a given HTML element belongs to the current document.
+ * In development mode, it warns if the element is not in the document.
+ *
+ * This is used to ensure components added using `renderGlimmer` are only rendered in the same document, preventing
+ * rendering errors that otherwise would crash the application.
+ *
+ * @param {Object} info - Object containing element information
+ * @param {Element} info.element - The DOM element to check
+ * @returns {boolean} True if element belongs to current document, false otherwise
+ */
+ isElementInDocument(info) {
+ const result = info.element.ownerDocument === document;
+
+ if (!isProduction() && !result) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ "The `renderGlimmer` definition below was unable to render the decorated HTML because the target element is not in the " +
+ "current document. This likely occurred because the element was removed by another decorator.\n",
+ info
+ );
+ }
+
+ return result;
+ }
+
{{~this.decoratedContent decorateArgs=@decorateArgs~}}
{{~#each this.renderGlimmerInfos as |info|~}}
- {{~#if info.append}}
- {{~#in-element info.element insertBefore=null~}}
-
- {{~/in-element~}}
- {{~else}}
- {{~#in-element info.element~}}
-
- {{~/in-element~}}
- {{~/if}}
+ {{~#if (this.isElementInDocument info)~}}
+ {{~#if info.append}}
+ {{~#in-element info.element insertBefore=null~}}
+
+ {{~/in-element~}}
+ {{~else}}
+ {{~#in-element info.element~}}
+
+ {{~/in-element~}}
+ {{~/if}}
+ {{~/if~}}
{{~/each~}}
}
diff --git a/app/assets/javascripts/discourse/tests/fixtures/poll.js b/app/assets/javascripts/discourse/tests/fixtures/poll.js
index 84de8a8ea9a61..6bb46fdca49e1 100644
--- a/app/assets/javascripts/discourse/tests/fixtures/poll.js
+++ b/app/assets/javascripts/discourse/tests/fixtures/poll.js
@@ -736,4 +736,206 @@ export default {
},
},
},
+ "/t/pie_chart_poll_with_mention.json": {
+ post_stream: {
+ posts: [
+ {
+ id: 294,
+ name: "",
+ username: "markvanlan",
+ avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png",
+ created_at: "2019-11-22T18:55:41.439Z",
+ cooked:
+ '\u003cdiv class="poll" data-poll-status="open" data-poll-max="3" data-poll-min="1" data-poll-results="always" data-poll-charttype="pie" data-poll-type="multiple" data-poll-name="poll"\u003e\n\u003cdiv\u003e\n\u003cdiv class="poll-container"\u003e\n\u003cul\u003e\n\u003cli data-poll-option-id="687a1ccf3c6a260f9aeeb7f68a1d463c"\u003e@user1\u003c/li\u003e\n\u003cli data-poll-option-id="9377906763a1221d31d656ea0c4a4495"\u003eA test for sure\u003c/li\u003e\n\u003cli data-poll-option-id="ecf47c65a85a0bb20029072b1b721977"\u003eWhy not give it some more\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/div\u003e\n\u003cdiv class="poll-info"\u003e\n\u003cp\u003e\n\u003cspan class="info-number"\u003e0\u003c/span\u003e\n\u003cspan class="info-label"\u003evoters\u003c/span\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e',
+ post_number: 1,
+ post_type: 1,
+ updated_at: "2019-11-22T18:55:41.439Z",
+ reply_count: 0,
+ reply_to_post_number: null,
+ quote_count: 0,
+ incoming_link_count: 0,
+ reads: 2,
+ readers_count: 1,
+ score: 0.2,
+ yours: false,
+ topic_id: 256,
+ topic_slug: "14-the-title-must-be-longer-i-guess",
+ display_username: "",
+ primary_group_name: "Team",
+ flair_name: null,
+ flair_url: null,
+ flair_bg_color: "",
+ flair_color: "",
+ version: 1,
+ can_edit: false,
+ can_delete: false,
+ can_recover: false,
+ can_wiki: false,
+ read: true,
+ user_title: "You are a member of the team",
+ actions_summary: [
+ { id: 2, can_act: true },
+ { id: 3, can_act: true },
+ { id: 4, can_act: true },
+ { id: 8, can_act: true },
+ { id: 6, can_act: true },
+ { id: 7, can_act: true },
+ ],
+ moderator: true,
+ admin: true,
+ staff: true,
+ user_id: 1,
+ hidden: false,
+ trust_level: 4,
+ deleted_at: null,
+ user_deleted: false,
+ edit_reason: null,
+ can_view_edit_history: true,
+ wiki: false,
+ user_custom_fields: { team: "Engineering", votes: [247, 251, 248] },
+ can_accept_answer: false,
+ can_unaccept_answer: false,
+ accepted_answer: false,
+ can_translate: false,
+ can_vote: true,
+ mentioned_users: [
+ {
+ status: {
+ "description": "Does it still works?",
+ "emoji": "thinking",
+ "ends_at": null,
+ "message_bus_last_id": 0
+ },
+ id: 196,
+ username: "user1",
+ name: "User1",
+ avatar_template: "/letter_avatar_proxy/v4/letter/m/34f0e0/{size}.png"
+ }
+ ],
+ polls: [
+ {
+ name: "poll",
+ type: "multiple",
+ status: "open",
+ results: "always",
+ min: 1,
+ max: 3,
+ options: [
+ {
+ id: "687a1ccf3c6a260f9aeeb7f68a1d463c",
+ html: '@user1',
+ votes: 2,
+ },
+ {
+ id: "9377906763a1221d31d656ea0c4a4495",
+ html: "A test for sure",
+ votes: 2,
+ },
+ {
+ id: "ecf47c65a85a0bb20029072b1b721977",
+ html: "Why not give it some more",
+ votes: 1,
+ },
+ ],
+ voters: 2,
+ chart_type: "pie",
+ },
+ ],
+ polls_votes: {
+ poll: [
+ "687a1ccf3c6a260f9aeeb7f68a1d463c",
+ "9377906763a1221d31d656ea0c4a4495",
+ ],
+ },
+ },
+ ],
+ stream: [294],
+ },
+ timeline_lookup: [[1, 2]],
+ suggested_topics: [],
+ tags: [],
+ id: 256,
+ title: "14 the title must be longer i guess",
+ fancy_title: "14 the title must be longer i guess",
+ posts_count: 1,
+ created_at: "2019-11-22T18:55:41.259Z",
+ views: 3,
+ reply_count: 0,
+ like_count: 0,
+ last_posted_at: "2019-11-22T18:55:41.439Z",
+ visible: true,
+ closed: false,
+ archived: false,
+ has_summary: false,
+ archetype: "regular",
+ slug: "14-the-title-must-be-longer-i-guess",
+ category_id: 1,
+ word_count: 24,
+ deleted_at: null,
+ user_id: 1,
+ featured_link: null,
+ pinned_globally: false,
+ pinned_at: null,
+ pinned_until: null,
+ image_url: null,
+ draft: null,
+ draft_key: "topic_256",
+ draft_sequence: 0,
+ posted: false,
+ unpinned: null,
+ pinned: false,
+ current_post_number: 1,
+ highest_post_number: 1,
+ last_read_post_number: 1,
+ last_read_post_id: 294,
+ deleted_by: null,
+ actions_summary: [
+ { id: 4, count: 0, hidden: false, can_act: true },
+ { id: 8, count: 0, hidden: false, can_act: true },
+ { id: 7, count: 0, hidden: false, can_act: true },
+ ],
+ chunk_size: 20,
+ bookmarked: false,
+ bookmarks: [],
+ topic_timer: null,
+ message_bus_last_id: 1,
+ participant_count: 1,
+ show_read_indicator: false,
+ can_vote: true,
+ vote_count: 0,
+ user_voted: false,
+ details: {
+ notification_level: 1,
+ notifications_reason_id: null,
+ can_create_post: true,
+ can_reply_as_new_topic: true,
+ can_flag_topic: true,
+ participants: [
+ {
+ id: 1,
+ username: "markvanlan",
+ name: "",
+ avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png",
+ post_count: 1,
+ primary_group_name: "Team",
+ flair_name: null,
+ flair_url: null,
+ flair_color: "",
+ flair_bg_color: "",
+ },
+ ],
+ created_by: {
+ id: 1,
+ username: "markvanlan",
+ name: "",
+ avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png",
+ },
+ last_poster: {
+ id: 1,
+ username: "markvanlan",
+ name: "",
+ avatar_template: "/user_avatar/localhost/markvanlan/{size}/11_2.png",
+ },
+ },
+ },
};
diff --git a/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js b/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js
index 9a284c5130420..c068ec43a380d 100644
--- a/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js
+++ b/plugins/poll/test/javascripts/acceptance/poll-pie-chart-test.js
@@ -28,4 +28,27 @@ acceptance("Rendering polls with pie charts", function (needs) {
.dom(".poll .poll-results-chart")
.exists({ count: 1 }, "Renders the chart div instead of bar container");
});
+
+ test("Renders results containing @mention correctly", async function (assert) {
+ // enable user status for the test to ensure the application renders without errors
+ this.siteSettings.enable_user_status = true;
+
+ await visit("/t/-/pie_chart_poll_with_mention");
+
+ assert
+ .dom(".poll .poll-info_counts-count:first-child .info-number")
+ .hasText("2", "it should display the right number of voters");
+
+ assert
+ .dom(".poll .poll-info_counts-count:last-child .info-number")
+ .hasText("5", "it should display the right number of votes");
+
+ assert
+ .dom(".poll-outer")
+ .hasClass("pie", "pie class is present on poll div");
+
+ assert
+ .dom(".poll .poll-results-chart")
+ .exists({ count: 1 }, "Renders the chart div instead of bar container");
+ });
});