tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 481ed92a1239fbee2107982f177f171115234f87
parent a218aa6c9b92c60aee3c7e82db72c7949c19be73
Author: Julien Wajsberg <felash@gmail.com>
Date:   Thu,  8 Jan 2026 10:17:48 +0000

Bug 2006354 - Add new reusable widgets for different types of input r=mkennedy

Differential Revision: https://phabricator.services.mozilla.com/D276672

Diffstat:
Mbrowser/components/preferences/widgets/setting-control/setting-control.mjs | 8++++++--
Mbrowser/modules/BrowserUsageTelemetry.sys.mjs | 4++++
Mtoolkit/content/customElements.js | 10++++++++++
Mtoolkit/content/jar.mn | 4++++
Mtoolkit/content/tests/widgets/chrome.toml | 8++++++++
Atoolkit/content/tests/widgets/test_moz_input_email.html | 44++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/content/tests/widgets/test_moz_input_number.html | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/content/tests/widgets/test_moz_input_tel.html | 44++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/content/tests/widgets/test_moz_input_url.html | 44++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-email/moz-input-email.mjs | 26++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-email/moz-input-email.stories.mjs | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-number/moz-input-number.mjs | 26++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-number/moz-input-number.stories.mjs | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-tel/moz-input-tel.mjs | 26++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-tel/moz-input-tel.stories.mjs | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-url/moz-input-url.mjs | 26++++++++++++++++++++++++++
Atoolkit/content/widgets/moz-input-url/moz-input-url.stories.mjs | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 files changed, 931 insertions(+), 2 deletions(-)

diff --git a/browser/components/preferences/widgets/setting-control/setting-control.mjs b/browser/components/preferences/widgets/setting-control/setting-control.mjs @@ -80,10 +80,14 @@ const KNOWN_OPTIONS = new Map([ */ const ITEM_SLOT_BY_PARENT = new Map([ ["moz-checkbox", "nested"], - ["moz-input-text", "nested"], - ["moz-input-search", "nested"], + ["moz-input-email", "nested"], ["moz-input-folder", "nested"], + ["moz-input-number", "nested"], ["moz-input-password", "nested"], + ["moz-input-search", "nested"], + ["moz-input-tel", "nested"], + ["moz-input-text", "nested"], + ["moz-input-url", "nested"], ["moz-radio", "nested"], ["moz-radio-group", "nested"], // NOTE: moz-select does not support the nested slot. diff --git a/browser/modules/BrowserUsageTelemetry.sys.mjs b/browser/modules/BrowserUsageTelemetry.sys.mjs @@ -77,10 +77,14 @@ const UI_TARGET_CHANGE_ELEMENTS = new Set([ "moz-select", "moz-radio", "moz-toggle", + "moz-input-email", "moz-input-folder", + "moz-input-number", "moz-input-password", "moz-input-search", + "moz-input-tel", "moz-input-text", + "moz-input-url", "moz-visual-picker-item", "sync-device-name", ]); diff --git a/toolkit/content/customElements.js b/toolkit/content/customElements.js @@ -830,10 +830,18 @@ "chrome://global/content/elements/moz-input-color.mjs", ], [ + "moz-input-email", + "chrome://global/content/elements/moz-input-email.mjs", + ], + [ "moz-input-folder", "chrome://global/content/elements/moz-input-folder.mjs", ], [ + "moz-input-number", + "chrome://global/content/elements/moz-input-number.mjs", + ], + [ "moz-input-password", "chrome://global/content/elements/moz-input-password.mjs", ], @@ -841,7 +849,9 @@ "moz-input-search", "chrome://global/content/elements/moz-input-search.mjs", ], + ["moz-input-tel", "chrome://global/content/elements/moz-input-tel.mjs"], ["moz-input-text", "chrome://global/content/elements/moz-input-text.mjs"], + ["moz-input-url", "chrome://global/content/elements/moz-input-url.mjs"], ["moz-label", "chrome://global/content/elements/moz-label.mjs"], [ "moz-message-bar", diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn @@ -126,12 +126,16 @@ toolkit.jar: content/global/elements/moz-fieldset.css (widgets/moz-fieldset/moz-fieldset.css) content/global/elements/moz-fieldset.mjs (widgets/moz-fieldset/moz-fieldset.mjs) content/global/elements/moz-input-common.css (widgets/moz-input-common.css) + content/global/elements/moz-input-email.mjs (widgets/moz-input-email/moz-input-email.mjs) content/global/elements/moz-input-folder.css (widgets/moz-input-folder/moz-input-folder.css) content/global/elements/moz-input-folder.mjs (widgets/moz-input-folder/moz-input-folder.mjs) + content/global/elements/moz-input-number.mjs (widgets/moz-input-number/moz-input-number.mjs) content/global/elements/moz-input-password.mjs (widgets/moz-input-password/moz-input-password.mjs) content/global/elements/moz-input-search.mjs (widgets/moz-input-search/moz-input-search.mjs) + content/global/elements/moz-input-tel.mjs (widgets/moz-input-tel/moz-input-tel.mjs) content/global/elements/moz-input-text.css (widgets/moz-input-text/moz-input-text.css) content/global/elements/moz-input-text.mjs (widgets/moz-input-text/moz-input-text.mjs) + content/global/elements/moz-input-url.mjs (widgets/moz-input-url/moz-input-url.mjs) content/global/elements/moz-page-header.css (widgets/moz-page-header/moz-page-header.css) content/global/elements/moz-page-header.mjs (widgets/moz-page-header/moz-page-header.mjs) content/global/elements/moz-page-nav.css (widgets/moz-page-nav/moz-page-nav.css) diff --git a/toolkit/content/tests/widgets/chrome.toml b/toolkit/content/tests/widgets/chrome.toml @@ -54,14 +54,22 @@ skip-if = [ ["test_moz_input_elems_in_form.html"] +["test_moz_input_email.html"] + ["test_moz_input_folder.html"] +["test_moz_input_number.html"] + ["test_moz_input_password.html"] ["test_moz_input_search.html"] +["test_moz_input_tel.html"] + ["test_moz_input_text.html"] +["test_moz_input_url.html"] + ["test_moz_label.html"] ["test_moz_lit_element.html"] diff --git a/toolkit/content/tests/widgets/test_moz_input_email.html b/toolkit/content/tests/widgets/test_moz_input_email.html @@ -0,0 +1,44 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8" /> + <title>MozInputEmail Tests</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" /> + <link + rel="stylesheet" + href="chrome://mochikit/content/tests/SimpleTest/test.css" + /> + <script + type="module" + src="chrome://global/content/elements/moz-input-email.mjs" + ></script> + <script src="lit-test-helpers.js"></script> + <script class="testbody" type="application/javascript"> + let testHelpers = new InputTestHelpers(); + let html; + + add_setup(async function setup() { + ({ html } = await testHelpers.setupLit()); + testHelpers.setupTests({ + templateFn: (attrs, children) => + html`<moz-input-email ${attrs}>${children}</moz-input-email>`, + }); + }); + + add_task(async function testMozInputEmailProperties() { + await testHelpers.testCommonInputProperties("moz-input-email"); + }); + + add_task(async function testMozInputEmailEvents() { + await testHelpers.testTextBasedInputEvents("moz-input-email"); + }); + </script> + </head> + <body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"></pre> + </body> +</html> diff --git a/toolkit/content/tests/widgets/test_moz_input_number.html b/toolkit/content/tests/widgets/test_moz_input_number.html @@ -0,0 +1,91 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8" /> + <title>MozInputNumber Tests</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" /> + <link + rel="stylesheet" + href="chrome://mochikit/content/tests/SimpleTest/test.css" + /> + <script + type="module" + src="chrome://global/content/elements/moz-input-number.mjs" + ></script> + <script src="lit-test-helpers.js"></script> + <script class="testbody" type="application/javascript"> + let testHelpers = new InputTestHelpers(); + let html; + + add_setup(async function setup() { + ({ html } = await testHelpers.setupLit()); + testHelpers.setupTests({ + templateFn: (attrs, children) => + html`<moz-input-number ${attrs}>${children}</moz-input-number>`, + }); + }); + + add_task(async function testMozInputNumberProperties() { + await testHelpers.verifyLabel("moz-input-number"); + await testHelpers.verifyAriaLabel("moz-input-number"); + await testHelpers.verifyAriaDescription("moz-input-number"); + await testHelpers.verifyName("moz-input-number"); + + // Custom value test for number inputs + const INITIAL_VALUE = "42"; + const NEW_VALUE = "100"; + let valueTemplate = testHelpers.templateFn({ + label: "Testing value", + value: INITIAL_VALUE, + }); + let renderTarget = await testHelpers.renderTemplate(valueTemplate); + let input = renderTarget.querySelector("moz-input-number"); + + is(input.inputEl.value, INITIAL_VALUE, "Input value is set."); + input.value = NEW_VALUE; + await input.updateComplete; + is(input.inputEl.value, NEW_VALUE, "Input value is updated."); + + await testHelpers.verifyIcon("moz-input-number"); + await testHelpers.verifyDisabled("moz-input-number"); + await testHelpers.verifyDescription("moz-input-number"); + await testHelpers.verifySupportPage("moz-input-number"); + await testHelpers.verifyAccesskey("moz-input-number"); + await testHelpers.verifyNoWhitespace("moz-input-number"); + }); + + add_task(async function testMozInputNumberEvents() { + let { trackEvent, verifyEvents } = testHelpers.getInputEventHelpers(); + let target = await testHelpers.renderTemplate(); + let input = target.querySelector("moz-input-number"); + + input.addEventListener("click", trackEvent); + input.addEventListener("change", trackEvent); + input.addEventListener("input", trackEvent); + + const TEST_NUMBER = "42"; + synthesizeMouseAtCenter(input.inputEl, {}); + sendString(TEST_NUMBER); + input.blur(); + await TestUtils.waitForTick(); + + verifyEvents([ + { type: "click", localName: "moz-input-number", value: "" }, + ...Array.from(TEST_NUMBER).map((char, i) => ({ + type: "input", + localName: "moz-input-number", + value: TEST_NUMBER.substring(0, i + 1), + })), + { type: "change", localName: "moz-input-number", value: TEST_NUMBER }, + ]); + }); + </script> + </head> + <body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"></pre> + </body> +</html> diff --git a/toolkit/content/tests/widgets/test_moz_input_tel.html b/toolkit/content/tests/widgets/test_moz_input_tel.html @@ -0,0 +1,44 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8" /> + <title>MozInputTel Tests</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" /> + <link + rel="stylesheet" + href="chrome://mochikit/content/tests/SimpleTest/test.css" + /> + <script + type="module" + src="chrome://global/content/elements/moz-input-tel.mjs" + ></script> + <script src="lit-test-helpers.js"></script> + <script class="testbody" type="application/javascript"> + let testHelpers = new InputTestHelpers(); + let html; + + add_setup(async function setup() { + ({ html } = await testHelpers.setupLit()); + testHelpers.setupTests({ + templateFn: (attrs, children) => + html`<moz-input-tel ${attrs}>${children}</moz-input-tel>`, + }); + }); + + add_task(async function testMozInputTelProperties() { + await testHelpers.testCommonInputProperties("moz-input-tel"); + }); + + add_task(async function testMozInputTelEvents() { + await testHelpers.testTextBasedInputEvents("moz-input-tel"); + }); + </script> + </head> + <body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"></pre> + </body> +</html> diff --git a/toolkit/content/tests/widgets/test_moz_input_url.html b/toolkit/content/tests/widgets/test_moz_input_url.html @@ -0,0 +1,44 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8" /> + <title>MozInputUrl Tests</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" /> + <link + rel="stylesheet" + href="chrome://mochikit/content/tests/SimpleTest/test.css" + /> + <script + type="module" + src="chrome://global/content/elements/moz-input-url.mjs" + ></script> + <script src="lit-test-helpers.js"></script> + <script class="testbody" type="application/javascript"> + let testHelpers = new InputTestHelpers(); + let html; + + add_setup(async function setup() { + ({ html } = await testHelpers.setupLit()); + testHelpers.setupTests({ + templateFn: (attrs, children) => + html`<moz-input-url ${attrs}>${children}</moz-input-url>`, + }); + }); + + add_task(async function testMozInputUrlProperties() { + await testHelpers.testCommonInputProperties("moz-input-url"); + }); + + add_task(async function testMozInputUrlEvents() { + await testHelpers.testTextBasedInputEvents("moz-input-url"); + }); + </script> + </head> + <body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"></pre> + </body> +</html> diff --git a/toolkit/content/widgets/moz-input-email/moz-input-email.mjs b/toolkit/content/widgets/moz-input-email/moz-input-email.mjs @@ -0,0 +1,26 @@ +/* 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 MozInputText from "chrome://global/content/elements/moz-input-text.mjs"; + +/** + * An email input custom element. + * + * @tagname moz-input-email + * @property {string} label - The text of the label element + * @property {string} name - The name of the input control + * @property {string} value - The value of the input control + * @property {boolean} disabled - The disabled state of the input control + * @property {boolean} readonly - The readonly state of the input control + * @property {string} iconSrc - The src for an optional icon + * @property {string} description - The text for the description element that helps describe the input control + * @property {string} supportPage - Name of the SUMO support page to link to. + * @property {string} placeholder - Text to display when the input has no value. + */ +export default class MozInputEmail extends MozInputText { + inputTemplate() { + return super.inputTemplate({ type: "email" }); + } +} +customElements.define("moz-input-email", MozInputEmail); diff --git a/toolkit/content/widgets/moz-input-email/moz-input-email.stories.mjs b/toolkit/content/widgets/moz-input-email/moz-input-email.stories.mjs @@ -0,0 +1,143 @@ +/* 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 { html, ifDefined, classMap } from "../vendor/lit.all.mjs"; +import "./moz-input-email.mjs"; + +export default { + title: "UI Widgets/Input Email", + component: "moz-input-email", + argTypes: { + l10nId: { + options: [ + "moz-input-email-label", + "moz-input-email-placeholder", + "moz-input-email-description", + "moz-input-email-label-wrapped", + ], + control: { type: "select" }, + }, + }, + parameters: { + status: "in-development", + handles: ["change", "input"], + fluent: ` +moz-input-email-label = + .label = Email: +moz-input-email-placeholder = + .label = Email: + .placeholder = Placeholder text +moz-input-email-description = + .label = Email: + .description = Description for the email input + .placeholder = Placeholder text +moz-input-email-label-wrapped = + .label = Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tristique justo leo, ac pellentesque lacus gravida vitae. Nam pellentesque suscipit venenatis. + `, + }, +}; + +const Template = ({ + name, + value, + iconSrc, + disabled, + l10nId, + description, + supportPage, + accessKey, + hasSlottedDescription, + hasSlottedSupportLink, + ellipsized, +}) => html` + <moz-input-email + name=${name} + value=${ifDefined(value || null)} + iconsrc=${ifDefined(iconSrc || null)} + ?disabled=${disabled} + data-l10n-id=${l10nId} + support-page=${ifDefined(supportPage || null)} + accesskey=${ifDefined(accessKey || null)} + class=${classMap({ "text-truncated-ellipsis": ellipsized })} + > + ${hasSlottedDescription + ? html`<div slot="description">${description}</div>` + : ""} + ${hasSlottedSupportLink + ? html`<a slot="support-link" href="www.example.com">Click me!</a>` + : ""} + </moz-input-email> +`; + +export const Default = Template.bind({}); +Default.args = { + name: "example-moz-input-email", + value: "", + iconSrc: "", + disabled: false, + l10nId: "moz-input-email-label", + supportPage: "", + accessKey: "", + hasSlottedDescription: false, + hasSlottedSupportLink: false, +}; + +export const WithPlaceholder = Template.bind({}); +WithPlaceholder.args = { + ...Default.args, + l10nId: "moz-input-email-placeholder", +}; + +export const WithIcon = Template.bind({}); +WithIcon.args = { + ...Default.args, + iconSrc: "chrome://global/skin/icons/highlights.svg", +}; + +export const withDescription = Template.bind({}); +withDescription.args = { + ...Default.args, + l10nId: "moz-input-email-description", +}; + +export const WithSlottedDescription = Template.bind({}); +WithSlottedDescription.args = { + ...Default.args, + description: "This is a custom slotted description.", + hasSlottedDescription: true, +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + ...Default.args, + l10nId: "moz-input-email-description", + disabled: true, +}; + +export const WithAccesskey = Template.bind({}); +WithAccesskey.args = { + ...Default.args, + accessKey: "s", +}; + +export const WithSupportLink = Template.bind({}); +WithSupportLink.args = { + ...Default.args, + supportPage: "support-page", + l10nId: "moz-input-email-description", +}; + +export const WithSlottedSupportLink = Template.bind({}); +WithSlottedSupportLink.args = { + ...Default.args, + hasSlottedSupportLink: true, + l10nId: "moz-input-email-description", +}; + +export const WithEllipsizedLabel = Template.bind({}); +WithEllipsizedLabel.args = { + ...Default.args, + ellipsized: true, + l10nId: "moz-input-email-label-wrapped", +}; diff --git a/toolkit/content/widgets/moz-input-number/moz-input-number.mjs b/toolkit/content/widgets/moz-input-number/moz-input-number.mjs @@ -0,0 +1,26 @@ +/* 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 MozInputText from "chrome://global/content/elements/moz-input-text.mjs"; + +/** + * A number input custom element. + * + * @tagname moz-input-number + * @property {string} label - The text of the label element + * @property {string} name - The name of the input control + * @property {string} value - The value of the input control + * @property {boolean} disabled - The disabled state of the input control + * @property {boolean} readonly - The readonly state of the input control + * @property {string} iconSrc - The src for an optional icon + * @property {string} description - The text for the description element that helps describe the input control + * @property {string} supportPage - Name of the SUMO support page to link to. + * @property {string} placeholder - Text to display when the input has no value. + */ +export default class MozInputNumber extends MozInputText { + inputTemplate() { + return super.inputTemplate({ type: "number" }); + } +} +customElements.define("moz-input-number", MozInputNumber); diff --git a/toolkit/content/widgets/moz-input-number/moz-input-number.stories.mjs b/toolkit/content/widgets/moz-input-number/moz-input-number.stories.mjs @@ -0,0 +1,143 @@ +/* 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 { html, ifDefined, classMap } from "../vendor/lit.all.mjs"; +import "./moz-input-number.mjs"; + +export default { + title: "UI Widgets/Input Number", + component: "moz-input-number", + argTypes: { + l10nId: { + options: [ + "moz-input-number-label", + "moz-input-number-placeholder", + "moz-input-number-description", + "moz-input-number-label-wrapped", + ], + control: { type: "select" }, + }, + }, + parameters: { + status: "in-development", + handles: ["change", "input"], + fluent: ` +moz-input-number-label = + .label = Number: +moz-input-number-placeholder = + .label = Number: + .placeholder = Placeholder text +moz-input-number-description = + .label = Number: + .description = Description for the number input + .placeholder = Placeholder text +moz-input-number-label-wrapped = + .label = Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tristique justo leo, ac pellentesque lacus gravida vitae. Nam pellentesque suscipit venenatis. + `, + }, +}; + +const Template = ({ + name, + value, + iconSrc, + disabled, + l10nId, + description, + supportPage, + accessKey, + hasSlottedDescription, + hasSlottedSupportLink, + ellipsized, +}) => html` + <moz-input-number + name=${name} + value=${ifDefined(value || null)} + iconsrc=${ifDefined(iconSrc || null)} + ?disabled=${disabled} + data-l10n-id=${l10nId} + support-page=${ifDefined(supportPage || null)} + accesskey=${ifDefined(accessKey || null)} + class=${classMap({ "text-truncated-ellipsis": ellipsized })} + > + ${hasSlottedDescription + ? html`<div slot="description">${description}</div>` + : ""} + ${hasSlottedSupportLink + ? html`<a slot="support-link" href="www.example.com">Click me!</a>` + : ""} + </moz-input-number> +`; + +export const Default = Template.bind({}); +Default.args = { + name: "example-moz-input-number", + value: "", + iconSrc: "", + disabled: false, + l10nId: "moz-input-number-label", + supportPage: "", + accessKey: "", + hasSlottedDescription: false, + hasSlottedSupportLink: false, +}; + +export const WithPlaceholder = Template.bind({}); +WithPlaceholder.args = { + ...Default.args, + l10nId: "moz-input-number-placeholder", +}; + +export const WithIcon = Template.bind({}); +WithIcon.args = { + ...Default.args, + iconSrc: "chrome://global/skin/icons/highlights.svg", +}; + +export const withDescription = Template.bind({}); +withDescription.args = { + ...Default.args, + l10nId: "moz-input-number-description", +}; + +export const WithSlottedDescription = Template.bind({}); +WithSlottedDescription.args = { + ...Default.args, + description: "This is a custom slotted description.", + hasSlottedDescription: true, +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + ...Default.args, + l10nId: "moz-input-number-description", + disabled: true, +}; + +export const WithAccesskey = Template.bind({}); +WithAccesskey.args = { + ...Default.args, + accessKey: "s", +}; + +export const WithSupportLink = Template.bind({}); +WithSupportLink.args = { + ...Default.args, + supportPage: "support-page", + l10nId: "moz-input-number-description", +}; + +export const WithSlottedSupportLink = Template.bind({}); +WithSlottedSupportLink.args = { + ...Default.args, + hasSlottedSupportLink: true, + l10nId: "moz-input-number-description", +}; + +export const WithEllipsizedLabel = Template.bind({}); +WithEllipsizedLabel.args = { + ...Default.args, + ellipsized: true, + l10nId: "moz-input-number-label-wrapped", +}; diff --git a/toolkit/content/widgets/moz-input-tel/moz-input-tel.mjs b/toolkit/content/widgets/moz-input-tel/moz-input-tel.mjs @@ -0,0 +1,26 @@ +/* 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 MozInputText from "chrome://global/content/elements/moz-input-text.mjs"; + +/** + * A telephone input custom element. + * + * @tagname moz-input-tel + * @property {string} label - The text of the label element + * @property {string} name - The name of the input control + * @property {string} value - The value of the input control + * @property {boolean} disabled - The disabled state of the input control + * @property {boolean} readonly - The readonly state of the input control + * @property {string} iconSrc - The src for an optional icon + * @property {string} description - The text for the description element that helps describe the input control + * @property {string} supportPage - Name of the SUMO support page to link to. + * @property {string} placeholder - Text to display when the input has no value. + */ +export default class MozInputTel extends MozInputText { + inputTemplate() { + return super.inputTemplate({ type: "tel" }); + } +} +customElements.define("moz-input-tel", MozInputTel); diff --git a/toolkit/content/widgets/moz-input-tel/moz-input-tel.stories.mjs b/toolkit/content/widgets/moz-input-tel/moz-input-tel.stories.mjs @@ -0,0 +1,143 @@ +/* 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 { html, ifDefined, classMap } from "../vendor/lit.all.mjs"; +import "./moz-input-tel.mjs"; + +export default { + title: "UI Widgets/Input Tel", + component: "moz-input-tel", + argTypes: { + l10nId: { + options: [ + "moz-input-tel-label", + "moz-input-tel-placeholder", + "moz-input-tel-description", + "moz-input-tel-label-wrapped", + ], + control: { type: "select" }, + }, + }, + parameters: { + status: "in-development", + handles: ["change", "input"], + fluent: ` +moz-input-tel-label = + .label = Telephone: +moz-input-tel-placeholder = + .label = Telephone: + .placeholder = Placeholder text +moz-input-tel-description = + .label = Telephone: + .description = Description for the telephone input + .placeholder = Placeholder text +moz-input-tel-label-wrapped = + .label = Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tristique justo leo, ac pellentesque lacus gravida vitae. Nam pellentesque suscipit venenatis. + `, + }, +}; + +const Template = ({ + name, + value, + iconSrc, + disabled, + l10nId, + description, + supportPage, + accessKey, + hasSlottedDescription, + hasSlottedSupportLink, + ellipsized, +}) => html` + <moz-input-tel + name=${name} + value=${ifDefined(value || null)} + iconsrc=${ifDefined(iconSrc || null)} + ?disabled=${disabled} + data-l10n-id=${l10nId} + support-page=${ifDefined(supportPage || null)} + accesskey=${ifDefined(accessKey || null)} + class=${classMap({ "text-truncated-ellipsis": ellipsized })} + > + ${hasSlottedDescription + ? html`<div slot="description">${description}</div>` + : ""} + ${hasSlottedSupportLink + ? html`<a slot="support-link" href="www.example.com">Click me!</a>` + : ""} + </moz-input-tel> +`; + +export const Default = Template.bind({}); +Default.args = { + name: "example-moz-input-tel", + value: "", + iconSrc: "", + disabled: false, + l10nId: "moz-input-tel-label", + supportPage: "", + accessKey: "", + hasSlottedDescription: false, + hasSlottedSupportLink: false, +}; + +export const WithPlaceholder = Template.bind({}); +WithPlaceholder.args = { + ...Default.args, + l10nId: "moz-input-tel-placeholder", +}; + +export const WithIcon = Template.bind({}); +WithIcon.args = { + ...Default.args, + iconSrc: "chrome://global/skin/icons/highlights.svg", +}; + +export const withDescription = Template.bind({}); +withDescription.args = { + ...Default.args, + l10nId: "moz-input-tel-description", +}; + +export const WithSlottedDescription = Template.bind({}); +WithSlottedDescription.args = { + ...Default.args, + description: "This is a custom slotted description.", + hasSlottedDescription: true, +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + ...Default.args, + l10nId: "moz-input-tel-description", + disabled: true, +}; + +export const WithAccesskey = Template.bind({}); +WithAccesskey.args = { + ...Default.args, + accessKey: "s", +}; + +export const WithSupportLink = Template.bind({}); +WithSupportLink.args = { + ...Default.args, + supportPage: "support-page", + l10nId: "moz-input-tel-description", +}; + +export const WithSlottedSupportLink = Template.bind({}); +WithSlottedSupportLink.args = { + ...Default.args, + hasSlottedSupportLink: true, + l10nId: "moz-input-tel-description", +}; + +export const WithEllipsizedLabel = Template.bind({}); +WithEllipsizedLabel.args = { + ...Default.args, + ellipsized: true, + l10nId: "moz-input-tel-label-wrapped", +}; diff --git a/toolkit/content/widgets/moz-input-url/moz-input-url.mjs b/toolkit/content/widgets/moz-input-url/moz-input-url.mjs @@ -0,0 +1,26 @@ +/* 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 MozInputText from "chrome://global/content/elements/moz-input-text.mjs"; + +/** + * A URL input custom element. + * + * @tagname moz-input-url + * @property {string} label - The text of the label element + * @property {string} name - The name of the input control + * @property {string} value - The value of the input control + * @property {boolean} disabled - The disabled state of the input control + * @property {boolean} readonly - The readonly state of the input control + * @property {string} iconSrc - The src for an optional icon + * @property {string} description - The text for the description element that helps describe the input control + * @property {string} supportPage - Name of the SUMO support page to link to. + * @property {string} placeholder - Text to display when the input has no value. + */ +export default class MozInputUrl extends MozInputText { + inputTemplate() { + return super.inputTemplate({ type: "url" }); + } +} +customElements.define("moz-input-url", MozInputUrl); diff --git a/toolkit/content/widgets/moz-input-url/moz-input-url.stories.mjs b/toolkit/content/widgets/moz-input-url/moz-input-url.stories.mjs @@ -0,0 +1,143 @@ +/* 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 { html, ifDefined, classMap } from "../vendor/lit.all.mjs"; +import "./moz-input-url.mjs"; + +export default { + title: "UI Widgets/Input Url", + component: "moz-input-url", + argTypes: { + l10nId: { + options: [ + "moz-input-url-label", + "moz-input-url-placeholder", + "moz-input-url-description", + "moz-input-url-label-wrapped", + ], + control: { type: "select" }, + }, + }, + parameters: { + status: "in-development", + handles: ["change", "input"], + fluent: ` +moz-input-url-label = + .label = URL: +moz-input-url-placeholder = + .label = URL: + .placeholder = Placeholder text +moz-input-url-description = + .label = URL: + .description = Description for the URL input + .placeholder = Placeholder text +moz-input-url-label-wrapped = + .label = Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tristique justo leo, ac pellentesque lacus gravida vitae. Nam pellentesque suscipit venenatis. + `, + }, +}; + +const Template = ({ + name, + value, + iconSrc, + disabled, + l10nId, + description, + supportPage, + accessKey, + hasSlottedDescription, + hasSlottedSupportLink, + ellipsized, +}) => html` + <moz-input-url + name=${name} + value=${ifDefined(value || null)} + iconsrc=${ifDefined(iconSrc || null)} + ?disabled=${disabled} + data-l10n-id=${l10nId} + support-page=${ifDefined(supportPage || null)} + accesskey=${ifDefined(accessKey || null)} + class=${classMap({ "text-truncated-ellipsis": ellipsized })} + > + ${hasSlottedDescription + ? html`<div slot="description">${description}</div>` + : ""} + ${hasSlottedSupportLink + ? html`<a slot="support-link" href="www.example.com">Click me!</a>` + : ""} + </moz-input-url> +`; + +export const Default = Template.bind({}); +Default.args = { + name: "example-moz-input-url", + value: "", + iconSrc: "", + disabled: false, + l10nId: "moz-input-url-label", + supportPage: "", + accessKey: "", + hasSlottedDescription: false, + hasSlottedSupportLink: false, +}; + +export const WithPlaceholder = Template.bind({}); +WithPlaceholder.args = { + ...Default.args, + l10nId: "moz-input-url-placeholder", +}; + +export const WithIcon = Template.bind({}); +WithIcon.args = { + ...Default.args, + iconSrc: "chrome://global/skin/icons/highlights.svg", +}; + +export const withDescription = Template.bind({}); +withDescription.args = { + ...Default.args, + l10nId: "moz-input-url-description", +}; + +export const WithSlottedDescription = Template.bind({}); +WithSlottedDescription.args = { + ...Default.args, + description: "This is a custom slotted description.", + hasSlottedDescription: true, +}; + +export const Disabled = Template.bind({}); +Disabled.args = { + ...Default.args, + l10nId: "moz-input-url-description", + disabled: true, +}; + +export const WithAccesskey = Template.bind({}); +WithAccesskey.args = { + ...Default.args, + accessKey: "s", +}; + +export const WithSupportLink = Template.bind({}); +WithSupportLink.args = { + ...Default.args, + supportPage: "support-page", + l10nId: "moz-input-url-description", +}; + +export const WithSlottedSupportLink = Template.bind({}); +WithSlottedSupportLink.args = { + ...Default.args, + hasSlottedSupportLink: true, + l10nId: "moz-input-url-description", +}; + +export const WithEllipsizedLabel = Template.bind({}); +WithEllipsizedLabel.args = { + ...Default.args, + ellipsized: true, + l10nId: "moz-input-url-label-wrapped", +};