commit 032930c7c7426846e41cd0488abd968be76279af
parent dabbf9a4cd16cb65fe410e0cd5e9232330db10f6
Author: Erik Nordin <enordin@mozilla.com>
Date: Tue, 23 Dec 2025 16:05:29 +0000
Bug 2007194 - Migrate about:translations to moz-select r=translations-reviewers,gregtatum,fluent-reviewers,flod
This commit migrates the default HTML `<select>` elements
within the `about:translations` page to instead use the
reusable `moz-select` component.
Differential Revision: https://phabricator.services.mozilla.com/D277243
Diffstat:
6 files changed, 104 insertions(+), 57 deletions(-)
diff --git a/python/l10n/fluent_migrations/bug_2007194_about_translations_label_attrs.py b/python/l10n/fluent_migrations/bug_2007194_about_translations_label_attrs.py
@@ -0,0 +1,29 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from fluent.migrate.helpers import transforms_from
+
+
+def migrate(ctx):
+ """Bug 2007194 - Migrate about:translations to moz-select, part {index}."""
+
+ source = "toolkit/toolkit/about/aboutTranslations.ftl"
+ target = source
+
+ ctx.add_transforms(
+ target,
+ source,
+ transforms_from(
+ """
+about-translations-detect-default-label =
+ .label = { COPY_PATTERN(from_path, "about-translations-detect-default") }
+
+about-translations-detect-language-label =
+ .label = { COPY_PATTERN(from_path, "about-translations-detect-language") }
+
+about-translations-select-label =
+ .label = { COPY_PATTERN(from_path, "about-translations-select") }
+ """,
+ from_path=source,
+ ),
+ )
diff --git a/toolkit/components/translations/content/about-translations.css b/toolkit/components/translations/content/about-translations.css
@@ -82,9 +82,7 @@ body {
}
.about-translations-select {
- font-size: var(--font-size-large);
- width: 100%;
- margin: 0;
+ --select-min-height: var(--AT-square-button-large);
}
#about-translations-swap-languages-button {
diff --git a/toolkit/components/translations/content/about-translations.html b/toolkit/components/translations/content/about-translations.html
@@ -30,6 +30,10 @@
type="module"
src="chrome://global/content/elements/moz-message-bar.mjs"
></script>
+ <script
+ type="module"
+ src="chrome://global/content/elements/moz-select.mjs"
+ ></script>
</head>
<body>
@@ -84,17 +88,17 @@
</header>
<section id="about-translations-main-user-interface">
- <select
+ <moz-select
id="about-translations-source-select"
class="about-translations-select"
disabled
>
- <option
- id="about-translations-detect-language-option"
- data-l10n-id="about-translations-detect-default"
+ <moz-option
+ id="about-translations-detect-language-label-option"
+ data-l10n-id="about-translations-detect-default-label"
value="detect"
- ></option>
- </select>
+ ></moz-option>
+ </moz-select>
<moz-button
id="about-translations-swap-languages-button"
@@ -105,13 +109,16 @@
<div id="about-translations-swap-langauges-icon"></div>
</moz-button>
- <select
+ <moz-select
id="about-translations-target-select"
class="about-translations-select"
disabled
>
- <option data-l10n-id="about-translations-select" value=""></option>
- </select>
+ <moz-option
+ data-l10n-id="about-translations-select-label"
+ value=""
+ ></moz-option>
+ </moz-select>
<div id="about-translations-source-section">
<textarea
diff --git a/toolkit/components/translations/content/about-translations.mjs b/toolkit/components/translations/content/about-translations.mjs
@@ -190,17 +190,17 @@ class AboutTranslations {
*
* @returns {{
* copyButton: HTMLElement,
- * detectLanguageOption: HTMLOptionElement,
+ * detectLanguageOption: HTMLElement,
* languageLoadErrorMessage: HTMLElement,
* learnMoreLink: HTMLAnchorElement,
* mainUserInterface: HTMLElement,
- * sourceLanguageSelector: HTMLSelectElement,
+ * sourceLanguageSelector: HTMLElement,
* sourceSection: HTMLElement,
* sourceSectionActionsColumn: HTMLElement,
* sourceSectionClearButton: HTMLElement,
* sourceSectionTextArea: HTMLTextAreaElement,
* swapLanguagesButton: HTMLElement,
- * targetLanguageSelector: HTMLSelectElement,
+ * targetLanguageSelector: HTMLElement,
* targetSection: HTMLElement,
* targetSectionActionsRow: HTMLElement,
* targetSectionTextArea: HTMLTextAreaElement,
@@ -216,8 +216,10 @@ class AboutTranslations {
copyButton: /** @type {HTMLElement} */ (
document.getElementById("about-translations-copy-button")
),
- detectLanguageOption: /** @type {HTMLOptionElement} */ (
- document.getElementById("about-translations-detect-language-option")
+ detectLanguageOption: /** @type {HTMLElement} */ (
+ document.getElementById(
+ "about-translations-detect-language-label-option"
+ )
),
languageLoadErrorMessage: /** @type {HTMLElement} */ (
document.getElementById(
@@ -230,7 +232,7 @@ class AboutTranslations {
mainUserInterface: /** @type {HTMLElement} */ (
document.getElementById("about-translations-main-user-interface")
),
- sourceLanguageSelector: /** @type {HTMLSelectElement} */ (
+ sourceLanguageSelector: /** @type {HTMLElement} */ (
document.getElementById("about-translations-source-select")
),
sourceSection: /** @type {HTMLElement} */ (
@@ -248,7 +250,7 @@ class AboutTranslations {
swapLanguagesButton: /** @type {HTMLElement} */ (
document.getElementById("about-translations-swap-languages-button")
),
- targetLanguageSelector: /** @type {HTMLSelectElement} */ (
+ targetLanguageSelector: /** @type {HTMLElement} */ (
document.getElementById("about-translations-target-select")
),
targetSection: /** @type {HTMLElement} */ (
@@ -337,18 +339,18 @@ class AboutTranslations {
for (const { langTagKey, displayName } of this.#supportedLanguages
.sourceLanguages) {
- const option = document.createElement("option");
+ const option = document.createElement("moz-option");
option.value = langTagKey;
- option.text = displayName;
- sourceLanguageSelector.add(option);
+ option.setAttribute("label", displayName);
+ sourceLanguageSelector.append(option);
}
for (const { langTagKey, displayName } of this.#supportedLanguages
.targetLanguages) {
- const option = document.createElement("option");
+ const option = document.createElement("moz-option");
option.value = langTagKey;
- option.text = displayName;
- targetLanguageSelector.add(option);
+ option.setAttribute("label", displayName);
+ targetLanguageSelector.append(option);
}
}
@@ -359,6 +361,9 @@ class AboutTranslations {
await this.#populateLanguageSelectors();
const { sourceLanguageSelector, targetLanguageSelector } = this.elements;
+ this.#resetDetectLanguageOptionText();
+ sourceLanguageSelector.value = "detect";
+ targetLanguageSelector.value = "";
sourceLanguageSelector.disabled = false;
targetLanguageSelector.disabled = false;
}
@@ -383,7 +388,7 @@ class AboutTranslations {
copyButton.addEventListener("click", this.#onCopyButton);
learnMoreLink.addEventListener("click", this.#onLearnMoreLink);
sourceLanguageSelector.addEventListener(
- "input",
+ "change",
this.#onSourceLanguageInput
);
sourceSectionActionsColumn.addEventListener(
@@ -406,7 +411,7 @@ class AboutTranslations {
sourceSectionTextArea.addEventListener("blur", this.#onSourceTextAreaBlur);
swapLanguagesButton.addEventListener("click", this.#onSwapLanguagesButton);
targetLanguageSelector.addEventListener(
- "input",
+ "change",
this.#onTargetLanguageInput
);
targetSectionTextArea.addEventListener(
@@ -465,7 +470,7 @@ class AboutTranslations {
};
/**
- * Handles input events on the source-language selector.
+ * Handles change events on the source-language selector.
*/
#onSourceLanguageInput = () => {
const { sourceLanguageSelector } = this.elements;
@@ -482,7 +487,7 @@ class AboutTranslations {
};
/**
- * Handles input events on the target-language selector.
+ * Handles change events on the target-language selector.
*/
#onTargetLanguageInput = () => {
this.#disableSwapLanguagesButton();
@@ -755,7 +760,7 @@ class AboutTranslations {
}
/**
- * Sets the textContent of the about-translations-detect option in the
+ * Sets the label of the about-translations-detect option in the
* about-translations-source-select dropdown.
*
* Passing an empty display name will reset to the default string.
@@ -770,7 +775,7 @@ class AboutTranslations {
detectLanguageOption.setAttribute("language", detectedLanguage);
document.l10n.setAttributes(
detectLanguageOption,
- "about-translations-detect-language",
+ "about-translations-detect-language-label",
{ language: displayName }
);
}
@@ -784,7 +789,7 @@ class AboutTranslations {
detectLanguageOption.removeAttribute("language");
document.l10n.setAttributes(
detectLanguageOption,
- "about-translations-detect-default"
+ "about-translations-detect-default-label"
);
}
diff --git a/toolkit/components/translations/tests/browser/shared-head.js b/toolkit/components/translations/tests/browser/shared-head.js
@@ -204,9 +204,10 @@ async function openAboutTranslations({
const selectors = {
pageHeader: "header#about-translations-header",
mainUserInterface: "section#about-translations-main-user-interface",
- sourceLanguageSelector: "select#about-translations-source-select",
- targetLanguageSelector: "select#about-translations-target-select",
- detectLanguageOption: "option#about-translations-detect-language-option",
+ sourceLanguageSelector: "moz-select#about-translations-source-select",
+ targetLanguageSelector: "moz-select#about-translations-target-select",
+ detectLanguageOption:
+ "moz-option#about-translations-detect-language-label-option",
swapLanguagesButton: "moz-button#about-translations-swap-languages-button",
sourceSectionTextArea: "textarea#about-translations-source-textarea",
targetSectionTextArea: "textarea#about-translations-target-textarea",
@@ -4431,11 +4432,13 @@ class AboutTranslationsTestUtils {
try {
await this.#runInPage(
(selectors, { language }) => {
- const selector = content.document.querySelector(
- selectors.sourceLanguageSelector
+ const selector = Cu.waiveXrays(
+ content.document.querySelector(selectors.sourceLanguageSelector)
);
selector.value = language;
- selector.dispatchEvent(new content.Event("input"));
+ selector.dispatchEvent(
+ new content.Event("change", { bubbles: true })
+ );
},
{ language }
);
@@ -4454,11 +4457,13 @@ class AboutTranslationsTestUtils {
try {
await this.#runInPage(
(selectors, { language }) => {
- const selector = content.document.querySelector(
- selectors.targetLanguageSelector
+ const selector = Cu.waiveXrays(
+ content.document.querySelector(selectors.targetLanguageSelector)
);
selector.value = language;
- selector.dispatchEvent(new content.Event("input"));
+ selector.dispatchEvent(
+ new content.Event("change", { bubbles: true })
+ );
},
{ language }
);
@@ -4865,16 +4870,16 @@ class AboutTranslationsTestUtils {
let pageResult = {};
try {
pageResult = await this.#runInPage(selectors => {
- const selector = content.document.querySelector(
- selectors.sourceLanguageSelector
+ const selector = Cu.waiveXrays(
+ content.document.querySelector(selectors.sourceLanguageSelector)
);
const detectOptionElement = content.document.querySelector(
selectors.detectLanguageOption
);
return {
actualValue: selector.value,
- optionValues: Array.from(selector.options).map(
- option => option.value
+ optionValues: Array.from(selector.querySelectorAll("moz-option")).map(
+ option => option.getAttribute("value")
),
detectLanguageAttribute:
detectOptionElement?.getAttribute("language") ?? null,
@@ -4939,12 +4944,12 @@ class AboutTranslationsTestUtils {
try {
pageResult = await this.#runInPage(
selectors => {
- const selector = content.document.querySelector(
- selectors.targetLanguageSelector
- );
- const optionValues = Array.from(selector.options).map(
- option => option.value
+ const selector = Cu.waiveXrays(
+ content.document.querySelector(selectors.targetLanguageSelector)
);
+ const optionValues = Array.from(
+ selector.querySelectorAll("moz-option")
+ ).map(option => option.getAttribute("value"));
return {
actualValue: selector.value,
optionValues,
@@ -5012,8 +5017,8 @@ class AboutTranslationsTestUtils {
let pageResult = {};
try {
pageResult = await this.#runInPage(selectors => {
- const selector = content.document.querySelector(
- selectors.sourceLanguageSelector
+ const selector = Cu.waiveXrays(
+ content.document.querySelector(selectors.sourceLanguageSelector)
);
return { actualValue: selector.value };
});
@@ -5053,8 +5058,8 @@ class AboutTranslationsTestUtils {
if (defaultValue !== undefined) {
const expectedIdentifier = defaultValue
- ? "about-translations-detect-default"
- : "about-translations-detect-language";
+ ? "about-translations-detect-default-label"
+ : "about-translations-detect-language-label";
is(
localizationId,
expectedIdentifier,
diff --git a/toolkit/locales/en-US/toolkit/about/aboutTranslations.ftl b/toolkit/locales/en-US/toolkit/about/aboutTranslations.ftl
@@ -27,13 +27,15 @@ about-translations-input-placeholder =
# Text displayed on the source-language selector when no explicit option is selected
# and no language has been identified from the content of the source-language text area.
-about-translations-detect-default = Detect language
+about-translations-detect-default-label =
+ .label = Detect language
# Text displayed on the source-language selector when no explicit option is selected
# and a valid language has been identified from the content of the source-language text area.
# Variables:
# $language (string) - The localized display name of the detected language
-about-translations-detect-language = { $language } (detected)
+about-translations-detect-language-label =
+ .label = { $language } (detected)
# Placeholder text shown in the target-language output area when no translation has occurred.
about-translations-output-placeholder =
@@ -50,7 +52,8 @@ about-translations-copy-button-copied =
.title = Copy translation
# Text displayed on target-language selector when no language option is selected.
-about-translations-select = Select language
+about-translations-select-label =
+ .label = Select language
# A message displayed in the target-language output area while waiting for the translation to complete.
about-translations-translating-message = Translating…