Revision control

Copy as Markdown

Other Tools

/* 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/. */
const optionalAttributes = ["name", "placeholder", "required", "min", "max"];
/**
* Input, label and error message for account hub. You can listen to the normal
* input events.
*
* Template ID: #accountHubInputTemplate (from #accountHubInputTemplate.inc.xhtml)
*
* @tagname account-hub-input
* @attribute {string} id - ID used to create IDs for input and error message. Not observed.
* @attribute {string} l10n-label-id - The fluent ID of the input label.
* @attribute {string} l10n-error-id - The fluent ID of the error message.
* @attribute {string} type - The type of input (text, number, etc.). Not observed.
* @attribute {string} classes - The classes to be applied to the input element. Not observed.
* @attribute {string} name - The name of the input in the form. Not observed.
* @attribute {string} placeholder - The placeholder to show in the input. Not observed.
* @attribute {boolean} required - If the input is required. Not observed.
* @attribute {number} min - Minimum value if the input is of type number. Not observed.
* @attribute {number} max - Maximum value if the input is of type number. Not observed.
*/
class AccountHubInput extends HTMLElement {
static observedAttributes = ["l10n-label-id", "l10n-error-id"];
/**
* The internal input element.
*
* @type {HTMLInputElement}
*/
#input;
/**
* The internal label element.
*
* @type {HTMLLabelElement}
*/
#label;
/**
* Error message element for invalid state.
*
* @type {HTMLElement}
*/
#error;
/**
* Returns the value of the input element.
*
* @returns {string}
*/
get value() {
return this.#input.value;
}
/**
* Returns the number value of the input element.
*
* @returns {number}
*/
get valueAsNumber() {
return this.#input.valueAsNumber;
}
/**
* Sets the value of the input element.
*
* @param {string} newValue - Attribute value to be applied to the input.
*/
set value(newValue) {
this.#input.value = newValue;
}
connectedCallback() {
if (this.hasConnected) {
return;
}
this.hasConnected = true;
const template = document
.getElementById("accountHubInputTemplate")
.content.cloneNode(true);
this.appendChild(template);
this.#input = this.querySelector("input");
this.#label = this.querySelector("label");
this.#error = this.querySelector("span");
this.#input.id = `${this.id}Input`;
this.#input.type = this.getAttribute("type");
this.#input.className = this.getAttribute("classes");
this.#label.htmlFor = this.#input.id;
this.#error.id = `${this.#input.id}ErrorMessage`;
for (const attribute of optionalAttributes) {
const attributeValue = this.getAttribute(attribute);
if (attributeValue) {
this.#input.setAttribute(attribute, attributeValue);
}
}
this.attributeChangedCallback(
"l10n-label-id",
"",
this.getAttribute("l10n-label-id")
);
this.attributeChangedCallback(
"l10n-error-id",
"",
this.getAttribute("l10n-error-id")
);
}
async attributeChangedCallback(attribute, _oldValue, newValue) {
if (!this.hasConnected) {
return;
}
switch (attribute) {
case "l10n-label-id": {
const labelText = await document.l10n.formatValue(newValue);
document.l10n.setAttributes(this.#label, newValue);
this.#input.ariaLabel = labelText;
break;
}
case "l10n-error-id": {
if (newValue) {
document.l10n.setAttributes(this.#error, newValue);
}
break;
}
}
}
/**
* Sets the error state of the input.
*
* @param {string} error - Error message that determines error state. If
* empty, remove error state from input.
*/
setErrorState(error) {
if (!error?.length) {
this.#input.setCustomValidity("");
this.#input.ariaInvalid = "false";
this.#input.ariaDescribedByElements = [];
this.#error.role = null;
return;
}
this.#input.setCustomValidity(this.#label.textContent || error);
this.#input.ariaInvalid = "true";
this.#input.ariaDescribedByElements = [this.#error];
this.#error.role = "alert";
}
}
customElements.define("account-hub-input", AccountHubInput);