/* 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); } } }