summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/lit-utils.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/lit-utils.mjs')
-rw-r--r--toolkit/content/widgets/lit-utils.mjs138
1 files changed, 138 insertions, 0 deletions
diff --git a/toolkit/content/widgets/lit-utils.mjs b/toolkit/content/widgets/lit-utils.mjs
new file mode 100644
index 0000000000..87bc9073a0
--- /dev/null
+++ b/toolkit/content/widgets/lit-utils.mjs
@@ -0,0 +1,138 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { LitElement } from "chrome://global/content/vendor/lit.all.mjs";
+
+/**
+ * Helper for our replacement of @query. Used with `static queries` property.
+ *
+ * https://github.com/lit/lit/blob/main/packages/reactive-element/src/decorators/query.ts
+ */
+function query(el, selector) {
+ return () => el.renderRoot.querySelector(selector);
+}
+
+/**
+ * Helper for our replacement of @queryAll. Used with `static queries` property.
+ *
+ * https://github.com/lit/lit/blob/main/packages/reactive-element/src/decorators/query-all.ts
+ */
+function queryAll(el, selector) {
+ return () => el.renderRoot.querySelectorAll(selector);
+}
+
+/**
+ * MozLitElement provides extensions to the lit-provided LitElement class.
+ *
+ *******
+ *
+ * `@query` support (define a getter for a querySelector):
+ *
+ * static get queries() {
+ * return {
+ * propertyName: ".aNormal .cssSelector",
+ * anotherName: { all: ".selectorFor .querySelectorAll" },
+ * };
+ * }
+ *
+ * This example would add properties that would be written like this without
+ * using `queries`:
+ *
+ * get propertyName() {
+ * return this.renderRoot?.querySelector(".aNormal .cssSelector");
+ * }
+ *
+ * get anotherName() {
+ * return this.renderRoot?.querySelectorAll(".selectorFor .querySelectorAll");
+ * }
+ *******
+ *
+ * Automatic Fluent support for shadow DOM.
+ *
+ * Fluent requires that a shadowRoot be connected before it can use Fluent.
+ * Shadow roots will get connected automatically.
+ *
+ *******
+ *
+ * Test helper for sending events after a change: `dispatchOnUpdateComplete`
+ *
+ * When some async stuff is going on and you want to wait for it in a test, you
+ * can use `this.dispatchOnUpdateComplete(myEvent)` and have the test wait on
+ * your event.
+ *
+ * The component will then wait for your reactive property change to take effect
+ * and dispatch the desired event.
+ *
+ * Example:
+ *
+ * async onClick() {
+ * let response = await this.getServerResponse(this.data);
+ * // Show the response status to the user.
+ * this.responseStatus = respose.status;
+ * this.dispatchOnUpdateComplete(
+ * new CustomEvent("status-shown")
+ * );
+ * }
+ *
+ * add_task(async testButton() {
+ * let button = this.setupAndGetButton();
+ * button.click();
+ * await BrowserTestUtils.waitForEvent(button, "status-shown");
+ * });
+ */
+export class MozLitElement extends LitElement {
+ constructor() {
+ super();
+ let { queries } = this.constructor;
+ if (queries) {
+ for (let [selectorName, selector] of Object.entries(queries)) {
+ if (selector.all) {
+ Object.defineProperty(this, selectorName, {
+ get: queryAll(this, selector.all),
+ });
+ } else {
+ Object.defineProperty(this, selectorName, {
+ get: query(this, selector),
+ });
+ }
+ }
+ }
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ if (
+ this.renderRoot == this.shadowRoot &&
+ !this._l10nRootConnected &&
+ document.l10n
+ ) {
+ document.l10n.connectRoot(this.renderRoot);
+ this._l10nRootConnected = true;
+ }
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ if (
+ this.renderRoot == this.shadowRoot &&
+ this._l10nRootConnected &&
+ document.l10n
+ ) {
+ document.l10n.disconnectRoot(this.renderRoot);
+ this._l10nRootConnected = false;
+ }
+ }
+
+ async dispatchOnUpdateComplete(event) {
+ await this.updateComplete;
+ this.dispatchEvent(event);
+ }
+
+ update() {
+ super.update();
+ if (document.l10n) {
+ document.l10n.translateFragment(this.renderRoot);
+ }
+ }
+}