DEV: Refactor lazy-video initialization for Glimmer compatibility (#32599)
Modernizes the renderGlimmer API to use Glimmer components, replacing deprecated ember-cli-htmlbars templates. Adds deprecation warnings for invalid parameters and improves compatibility with the new Glimmer Post Stream API.
This commit is contained in:
parent
5de93abbaa
commit
61de461412
|
@ -1,10 +1,11 @@
|
|||
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";
|
||||
import helperFn from "discourse/helpers/helper-fn";
|
||||
import deprecated from "discourse/lib/deprecated";
|
||||
import { POST_STREAM_DEPRECATION_OPTIONS } from "discourse/lib/plugin-api";
|
||||
import { POST_STREAM_DEPRECATION_OPTIONS } from "discourse/widgets/post-stream";
|
||||
|
||||
const detachedDocument = document.implementation.createHTMLDocument("detached");
|
||||
|
||||
|
@ -75,8 +76,30 @@ class DecorateHtmlHelper {
|
|||
this.#context = context;
|
||||
}
|
||||
|
||||
renderGlimmer(element, component, data) {
|
||||
const info = { element, component, data };
|
||||
renderGlimmer(targetElement, component, data) {
|
||||
if (!(targetElement instanceof Element)) {
|
||||
deprecated(
|
||||
"Invalid `targetElement` passed to `helper.renderGlimmer` while using `api.decorateCookedElement` with the Glimmer Post Stream. `targetElement` must be a valid HTML element. This call has been ignored to prevent errors.",
|
||||
POST_STREAM_DEPRECATION_OPTIONS
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasInternalComponentManager(component)) {
|
||||
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
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const info = {
|
||||
element: targetElement,
|
||||
component,
|
||||
data,
|
||||
};
|
||||
this.#renderGlimmerInfos.push(info);
|
||||
}
|
||||
|
||||
|
@ -107,7 +130,7 @@ class DecorateHtmlHelper {
|
|||
},
|
||||
scheduleRerender() {
|
||||
// This is a no-op when using the new glimmer components.
|
||||
// the component will rerender automatically when the model changes.
|
||||
// The component will rerender automatically when the model changes.
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -145,6 +145,7 @@ import {
|
|||
} from "discourse/widgets/post-small-action";
|
||||
import {
|
||||
addPostTransformCallback,
|
||||
POST_STREAM_DEPRECATION_OPTIONS,
|
||||
preventCloak,
|
||||
} from "discourse/widgets/post-stream";
|
||||
import { disableNameSuppression } from "discourse/widgets/poster-name";
|
||||
|
@ -193,12 +194,6 @@ const DEPRECATED_POST_STREAM_WIDGETS = [
|
|||
"topic-post-visited-line",
|
||||
];
|
||||
|
||||
export const POST_STREAM_DEPRECATION_OPTIONS = {
|
||||
since: "v3.5.0.beta1-dev",
|
||||
id: "discourse.post-stream-widget-overrides",
|
||||
// url: "", // TODO (glimmer-post-stream) uncomment when the topic is created on meta
|
||||
};
|
||||
|
||||
const blockedModifications = ["component:topic-list"];
|
||||
|
||||
const appliedModificationIds = new WeakMap();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { hasInternalComponentManager } from "@glimmer/manager";
|
||||
import { h } from "virtual-dom";
|
||||
import deprecated from "discourse/lib/deprecated";
|
||||
import Connector from "discourse/widgets/connector";
|
||||
import PostCooked from "discourse/widgets/post-cooked";
|
||||
import { POST_STREAM_DEPRECATION_OPTIONS } from "discourse/widgets/post-stream";
|
||||
import RawHtml from "discourse/widgets/raw-html";
|
||||
import RenderGlimmer from "discourse/widgets/render-glimmer";
|
||||
|
||||
|
@ -116,18 +119,16 @@ class DecoratorHelper {
|
|||
* Returns an element containing a rendered glimmer template. For full usage instructions,
|
||||
* see `widgets/render-glimmer.js`.
|
||||
*
|
||||
* DEPRECATION NOTICES:
|
||||
* - using a string describing a new wrapper element as the `targetElement` parameter is deprecated.
|
||||
* Use an existing HTML element instead.
|
||||
* - using a template compiled via `ember-cli-htmlbars` as the `component` parameter is deprecated.
|
||||
* You should provide a component instead.
|
||||
*
|
||||
* Example usage in a `.gjs` file:
|
||||
*
|
||||
* ```
|
||||
* api.decorateCookedElement((cooked, helper) => {
|
||||
* const iconName = "user-plus";
|
||||
* // Generate a new element with glimmer rendered inside
|
||||
* const glimmerElement = helper.renderGlimmer(
|
||||
* "div.my-wrapper-class",
|
||||
* <template><DButton @icon={{iconName}} @translatedLabel="Hello world from Glimmer Component" /></template>
|
||||
* );
|
||||
* cooked.appendChild(glimmerElement);
|
||||
*
|
||||
* // Or append to an existing element
|
||||
* helper.renderGlimmer(
|
||||
* cooked.querySelector(".some-container"),
|
||||
|
@ -137,15 +138,32 @@ class DecoratorHelper {
|
|||
* ```
|
||||
*
|
||||
*/
|
||||
renderGlimmer(renderInto, template, data) {
|
||||
renderGlimmer(targetElement, component, data) {
|
||||
// Ideally we should throw an error here, but we can't for now to prevent existing incompatible customizations from
|
||||
// crashing the app. Instead, we will log a deprecation warning while we're in the process of migrating to the new
|
||||
// Glimmer Post Stream API.
|
||||
if (!(targetElement instanceof Element)) {
|
||||
deprecated(
|
||||
"The `targetElement` parameter provided to `helper.renderGlimmer` is invalid. It must be an existing HTML element. Using a string to describe a new wrapper element is deprecated.",
|
||||
POST_STREAM_DEPRECATION_OPTIONS
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasInternalComponentManager(component)) {
|
||||
deprecated(
|
||||
"The `component` parameter provided to `helper.renderGlimmer` is invalid. It must be a valid Glimmer component. Using a template compiled via `ember-cli-htmlbars` is deprecated. Use the <template>...</template> syntax or replace it with a proper component.",
|
||||
POST_STREAM_DEPRECATION_OPTIONS
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.widget.postContentsDestroyCallbacks) {
|
||||
throw "renderGlimmer can only be used in the context of a post";
|
||||
}
|
||||
|
||||
const renderGlimmer = new RenderGlimmer(
|
||||
this.widget,
|
||||
renderInto,
|
||||
template,
|
||||
targetElement,
|
||||
component,
|
||||
data
|
||||
);
|
||||
renderGlimmer.init();
|
||||
|
|
|
@ -16,6 +16,12 @@ import RenderGlimmer from "discourse/widgets/render-glimmer";
|
|||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export const POST_STREAM_DEPRECATION_OPTIONS = {
|
||||
since: "v3.5.0.beta1-dev",
|
||||
id: "discourse.post-stream-widget-overrides",
|
||||
// url: "", // TODO (glimmer-post-stream) uncomment when the topic is created on meta
|
||||
};
|
||||
|
||||
export let havePostStreamWidgetExtensions = null;
|
||||
|
||||
registerDeprecationHandler((_, opts) => {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { render, settled } from "@ember/test-helpers";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { module, test } from "qunit";
|
||||
import DecoratedHtml from "discourse/components/decorated-html";
|
||||
import { withSilencedDeprecations } from "discourse/lib/deprecated";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
|
||||
module("Integration | Component | <DecoratedHtml />", function (hooks) {
|
||||
|
@ -48,4 +50,88 @@ module("Integration | Component | <DecoratedHtml />", function (hooks) {
|
|||
assert.dom("#appended").hasText("Appended");
|
||||
assert.dom("#render-glimmer").hasText("Hello from Glimmer Component");
|
||||
});
|
||||
|
||||
test("renderGlimmer is ignored if receives invalid arguments", async function (assert) {
|
||||
const state = new (class {
|
||||
@tracked html = htmlSafe("<h1>Initial</h1>");
|
||||
})();
|
||||
|
||||
const decorateWithStringTarget = (element, helper) => {
|
||||
element.innerHTML += "<div id='appended'>Appended</div>";
|
||||
|
||||
withSilencedDeprecations("discourse.post-stream-widget-overrides", () => {
|
||||
helper.renderGlimmer(
|
||||
"div",
|
||||
<template>
|
||||
<div id="render-glimmer">Hello from Glimmer Component</div>
|
||||
</template>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
await render(
|
||||
<template>
|
||||
<DecoratedHtml
|
||||
@html={{state.html}}
|
||||
@decorate={{decorateWithStringTarget}}
|
||||
/>
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
.dom("h1")
|
||||
.hasText(
|
||||
"Initial",
|
||||
"Initial content is rendered when a string is passed as the target element"
|
||||
);
|
||||
assert
|
||||
.dom("#appended")
|
||||
.hasText(
|
||||
"Appended",
|
||||
"Appended content is rendered when a string is passed as the target element"
|
||||
);
|
||||
assert
|
||||
.dom("#render-glimmer")
|
||||
.doesNotExist(
|
||||
"Glimmer component is not rendered when a string is passed as the target element"
|
||||
);
|
||||
|
||||
const decorateWithHbsTemplate = (element, helper) => {
|
||||
element.innerHTML += "<div id='appended'>Appended</div>";
|
||||
|
||||
withSilencedDeprecations("discourse.post-stream-widget-overrides", () => {
|
||||
helper.renderGlimmer(
|
||||
element,
|
||||
hbs("<div id='render-glimmer'>Hello from Glimmer Component</div>")
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
await render(
|
||||
<template>
|
||||
<DecoratedHtml
|
||||
@html={{state.html}}
|
||||
@decorate={{decorateWithHbsTemplate}}
|
||||
/>
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
.dom("h1")
|
||||
.hasText(
|
||||
"Initial",
|
||||
"Initial content is rendered when a template is passed as the component"
|
||||
);
|
||||
assert
|
||||
.dom("#appended")
|
||||
.hasText(
|
||||
"Appended",
|
||||
"Appended content is rendered when a template is passed as the component"
|
||||
);
|
||||
assert
|
||||
.dom("#render-glimmer")
|
||||
.doesNotExist(
|
||||
"Glimmer component is not rendered when a template is passed as the component"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import LazyVideo from "../discourse/components/lazy-video";
|
||||
import getVideoAttributes from "../lib/lazy-video-attributes";
|
||||
|
||||
function initLazyEmbed(api) {
|
||||
|
@ -23,9 +23,17 @@ function initLazyEmbed(api) {
|
|||
}
|
||||
};
|
||||
|
||||
const lazyVideo = helper.renderGlimmer(
|
||||
"p.lazy-video-wrapper",
|
||||
hbs`<LazyVideo @videoAttributes={{@data.param}} @onLoadedVideo={{@data.onLoadedVideo}}/>`,
|
||||
const lazyVideo = document.createElement("p");
|
||||
lazyVideo.classList.add("lazy-video-wrapper");
|
||||
|
||||
helper.renderGlimmer(
|
||||
lazyVideo,
|
||||
<template>
|
||||
<LazyVideo
|
||||
@videoAttributes={{@data.param}}
|
||||
@onLoadedVideo={{@data.onLoadedVideo}}
|
||||
/>
|
||||
</template>,
|
||||
{ param: videoAttributes, onLoadedVideo }
|
||||
);
|
||||
|
||||
|
@ -41,6 +49,6 @@ export default {
|
|||
name: "discourse-lazy-videos",
|
||||
|
||||
initialize() {
|
||||
withPluginApi("1.6.0", initLazyEmbed);
|
||||
withPluginApi(initLazyEmbed);
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue