DEV: Fix cooked content rendering issues in the Glimmer Post Stream (#32670)
This commit is contained in:
parent
0658773056
commit
c2ae2e244c
|
@ -1,5 +1,4 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hasInternalComponentManager } from "@glimmer/manager";
|
||||
import { untrack } from "@glimmer/validator";
|
||||
import { htmlSafe, isHTMLSafe } from "@ember/template";
|
||||
import { TrackedArray } from "@ember-compat/tracked-built-ins";
|
||||
|
@ -86,7 +85,7 @@ class DecorateHtmlHelper {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!hasInternalComponentManager(component)) {
|
||||
if (component.name === "factory") {
|
||||
deprecated(
|
||||
"Invalid `component` passed to `helper.renderGlimmer` while using `api.decorateCookedElement` with the Glimmer Post Stream. `component` must be a valid Glimmer component. If using a template compiled via ember-cli-htmlbars, replace it with the `<template>...</template>` syntax. This call has been ignored to prevent errors.",
|
||||
POST_STREAM_DEPRECATION_OPTIONS
|
||||
|
|
|
@ -63,14 +63,14 @@ export default class PostCookedHtml extends Component {
|
|||
createDetachedElement: this.#createDetachedElement,
|
||||
currentUser: this.currentUser,
|
||||
helper,
|
||||
renderNestedCookedContent: (
|
||||
renderNestedPostCookedHtml: (
|
||||
nestedElement,
|
||||
cooked,
|
||||
nestedPost,
|
||||
extraDecorators
|
||||
) => {
|
||||
const nestedArguments = {
|
||||
cooked,
|
||||
post: this.args.post,
|
||||
post: nestedPost,
|
||||
streamElement: false,
|
||||
highlightTerm: this.highlightTerm,
|
||||
extraDecorators: [
|
||||
...this.extraDecorators,
|
||||
|
|
|
@ -47,14 +47,14 @@ export default function (element, context) {
|
|||
|
||||
// cleanup code
|
||||
return () => {
|
||||
state.extractedMentions = [];
|
||||
|
||||
if (userStatusService.isEnabled) {
|
||||
post?.mentioned_users?.forEach((user) => {
|
||||
state.extractedMentions.forEach(({ user }) => {
|
||||
user.statusManager?.stopTrackingStatus?.();
|
||||
user.off?.("status-changed", element, _updateUserStatus);
|
||||
});
|
||||
}
|
||||
|
||||
state.extractedMentions = [];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ function _updateQuoteElements(aside, desc, context) {
|
|||
}
|
||||
|
||||
async function _toggleQuote(aside, context) {
|
||||
const { createDetachedElement, data, renderNestedCookedContent, state } =
|
||||
const { createDetachedElement, data, renderNestedPostCookedHtml, state } =
|
||||
context;
|
||||
|
||||
if (state.expanding) {
|
||||
|
@ -154,19 +154,20 @@ async function _toggleQuote(aside, context) {
|
|||
const postId = parseInt(aside.dataset.post, 10);
|
||||
|
||||
try {
|
||||
const result = await ajax(`/posts/by_number/${topicId}/${postId}`);
|
||||
const quotedPost = await ajax(`/posts/by_number/${topicId}/${postId}`);
|
||||
|
||||
const post = data.post;
|
||||
const quotedPosts = post.quoted || {};
|
||||
quotedPosts[result.id] = result;
|
||||
quotedPosts[quotedPost.id] = quotedPost;
|
||||
post.set("quoted", quotedPosts);
|
||||
|
||||
const div = createDetachedElement("div");
|
||||
div.classList.add("expanded-quote");
|
||||
div.dataset.postId = result.id;
|
||||
div.dataset.postId = quotedPost.id;
|
||||
|
||||
// inception
|
||||
renderNestedCookedContent(div, result.cooked, (element) =>
|
||||
renderNestedPostCookedHtml(div, quotedPost, (element) =>
|
||||
// to highlight the quoted text inside the original post content
|
||||
highlightHTML(element, originalText, {
|
||||
matchCase: true,
|
||||
})
|
||||
|
|
|
@ -644,6 +644,68 @@ import { i18n } from "discourse-i18n";
|
|||
}
|
||||
);
|
||||
|
||||
acceptance(
|
||||
`Cooked quoted content (glimmer_post_stream_mode = ${postStreamMode})`,
|
||||
function (needs) {
|
||||
needs.settings({
|
||||
glimmer_post_stream_mode: postStreamMode,
|
||||
});
|
||||
|
||||
needs.pretender((server, helper) => {
|
||||
server.get("/posts/by_number/280/3", () =>
|
||||
helper.response(
|
||||
200,
|
||||
topicFixtures["/t/280/1.json"].post_stream.posts[2]
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test("The quoted content is toggled correclty", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert
|
||||
.dom(
|
||||
"#post_5 .cooked .quote[data-topic='280'][data-post='3'] .quote-controls .quote-toggle"
|
||||
)
|
||||
.hasAttribute("aria-expanded", "false");
|
||||
assert
|
||||
.dom("#post_5 .quote[data-topic='280'][data-post='3']")
|
||||
.includesText(
|
||||
'So you could replace that lookup table with the "de" one to get German.'
|
||||
)
|
||||
.doesNotIncludeText(
|
||||
"Yep, all strings are going through a lookup table.*"
|
||||
);
|
||||
|
||||
await click(
|
||||
"#post_5 .cooked .quote[data-topic='280'][data-post='3'] .quote-controls .quote-toggle"
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(
|
||||
"#post_5 .cooked .quote[data-topic='280'][data-post='3'] .quote-controls .quote-toggle"
|
||||
)
|
||||
.hasAttribute("aria-expanded", "true");
|
||||
assert
|
||||
.dom("#post_5 .quote[data-topic='280'][data-post='3']")
|
||||
.includesText(
|
||||
'So you could replace that lookup table with the "de" one to get German.'
|
||||
)
|
||||
.includesText("Yep, all strings are going through a lookup table.*");
|
||||
|
||||
await click(
|
||||
"#post_5 .cooked .quote[data-topic='280'][data-post='3'] .quote-controls .quote-toggle"
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(
|
||||
"#post_5 .cooked .quote[data-topic='280'][data-post='3'] .quote-controls .quote-toggle"
|
||||
)
|
||||
.hasAttribute("aria-expanded", "false");
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
acceptance(
|
||||
`Topic stats update automatically (glimmer_post_stream_mode = ${postStreamMode})`,
|
||||
function (needs) {
|
||||
|
|
|
@ -2,9 +2,11 @@ import { tracked } from "@glimmer/tracking";
|
|||
import { htmlSafe } from "@ember/template";
|
||||
import { render, settled } from "@ember/test-helpers";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import curryComponent from "ember-curry-component";
|
||||
import { module, test } from "qunit";
|
||||
import DecoratedHtml from "discourse/components/decorated-html";
|
||||
import { withSilencedDeprecations } from "discourse/lib/deprecated";
|
||||
import { getOwnerWithFallback } from "discourse/lib/get-owner";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
|
||||
module("Integration | Component | <DecoratedHtml />", function (hooks) {
|
||||
|
@ -51,6 +53,36 @@ module("Integration | Component | <DecoratedHtml />", function (hooks) {
|
|||
assert.dom("#render-glimmer").hasText("Hello from Glimmer Component");
|
||||
});
|
||||
|
||||
test("can decorate content with renderGlimmer using a curried componet", async function (assert) {
|
||||
const state = new (class {
|
||||
@tracked html = htmlSafe("<h1>Initial</h1>");
|
||||
})();
|
||||
|
||||
const decorate = (element, helper) => {
|
||||
element.innerHTML += "<div id='appended'>Appended</div>";
|
||||
helper.renderGlimmer(
|
||||
element,
|
||||
curryComponent(
|
||||
<template>
|
||||
<div id="render-glimmer">Hello from {{@value}} Component</div>
|
||||
</template>,
|
||||
{ value: "Curried" },
|
||||
getOwnerWithFallback(this)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
await render(
|
||||
<template>
|
||||
<DecoratedHtml @html={{state.html}} @decorate={{decorate}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert.dom("h1").hasText("Initial");
|
||||
assert.dom("#appended").hasText("Appended");
|
||||
assert.dom("#render-glimmer").hasText("Hello from Curried Component");
|
||||
});
|
||||
|
||||
test("renderGlimmer is ignored if receives invalid arguments", async function (assert) {
|
||||
const state = new (class {
|
||||
@tracked html = htmlSafe("<h1>Initial</h1>");
|
||||
|
|
Loading…
Reference in New Issue