diff options
Diffstat (limited to 'toolkit/content/widgets/lit-utils.mjs')
-rw-r--r-- | toolkit/content/widgets/lit-utils.mjs | 126 |
1 files changed, 126 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..35a0ba445a --- /dev/null +++ b/toolkit/content/widgets/lit-utils.mjs @@ -0,0 +1,126 @@ +/* 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; + } + } + + async dispatchOnUpdateComplete(event) { + await this.updateComplete; + this.dispatchEvent(event); + } + + update() { + super.update(); + if (document.l10n) { + document.l10n.translateFragment(this.renderRoot); + } + } +} |