diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 1adcf24252369f..67cf753b95deea 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -840,18 +840,16 @@ class AnnotationEditor { altText.tabIndex = "0"; altText.addEventListener("contextmenu", noContextMenu); altText.addEventListener("pointerdown", event => event.stopPropagation()); - altText.addEventListener( - "click", - event => { - event.preventDefault(); - this._uiManager.editAltText(this); - }, - { capture: true } - ); + + const onClick = (event, keyboard = false) => { + this.#altTextButton.hidden = true; + event.preventDefault(); + this._uiManager.editAltText(this, /* isFromKeyboard */ keyboard); + }; + altText.addEventListener("click", onClick, { capture: true }); altText.addEventListener("keydown", event => { if (event.target === altText && event.key === "Enter") { - event.preventDefault(); - this._uiManager.editAltText(this); + onClick(event, true); } }); this.#setAltTextButtonState(); @@ -877,12 +875,13 @@ class AnnotationEditor { this.#altTextTooltip?.remove(); return; } + button.classList.add("done"); + AnnotationEditor._l10nPromise .get("editor_alt_text_edit_button_label") .then(msg => { button.setAttribute("aria-label", msg); }); - let tooltip = this.#altTextTooltip; if (!tooltip) { this.#altTextTooltip = tooltip = document.createElement("span"); @@ -916,7 +915,6 @@ class AnnotationEditor { this.#altTextTooltip?.classList.remove("show"); }); } - button.classList.add("done"); tooltip.innerText = this.#altTextDecorative ? await AnnotationEditor._l10nPromise.get( "editor_alt_text_decorative_tooltip" @@ -939,6 +937,14 @@ class AnnotationEditor { this.#altTextButton.disabled = !enabled; } + altTextFinish(wasFromKeyboard) { + if (!this.#altTextButton) { + return; + } + this.#altTextButton.hidden = false; + this.#altTextButton.focus({ focusVisible: wasFromKeyboard }); + } + getClientDimensions() { return this.div.getBoundingClientRect(); } diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index b631e979830429..7a198718e52b4d 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -768,8 +768,8 @@ class AnnotationEditorUIManager { ); } - editAltText(editor) { - this.#altTextManager?.editAltText(this, editor); + editAltText(editor, isFromKeyboard) { + this.#altTextManager?.editAltText(this, editor, isFromKeyboard); } onPageChanging({ pageNumber }) { diff --git a/test/integration/stamp_editor_spec.js b/test/integration/stamp_editor_spec.js index 40e5b64d2db883..608beaf0f8db06 100644 --- a/test/integration/stamp_editor_spec.js +++ b/test/integration/stamp_editor_spec.js @@ -241,6 +241,9 @@ describe("Stamp Editor", () => { // Click on the alt-text button. await page.click(buttonSelector); + // Check that the alt-text button has been hidden. + await page.waitForSelector(`${buttonSelector}[hidden]`); + // Wait for the alt-text dialog to be visible. await page.waitForSelector("#altTextDialog", { visible: true }); @@ -254,7 +257,7 @@ describe("Stamp Editor", () => { await page.click(saveButtonSelector); // Wait for the alt-text button to have the correct icon. - await page.waitForSelector(`${buttonSelector}.done`); + await page.waitForSelector(`${buttonSelector}:not([hidden]).done`); // Hover the button. await page.hover(buttonSelector); @@ -350,6 +353,30 @@ describe("Stamp Editor", () => { sel => document.querySelector(sel) === null, tooltipSelector ); + + // We check that the alt-text button works correctly with the + // keyboard. + await page.evaluate(sel => { + document.getElementById("viewerContainer").focus(); + return new Promise(resolve => { + setTimeout(() => { + const el = document.querySelector(sel); + el.addEventListener("focus", resolve, { once: true }); + el.focus({ focusVisible: true }); + }, 0); + }); + }, buttonSelector); + await (browserName === "chrome" + ? page.waitForSelector(`${buttonSelector}:focus`) + : page.waitForSelector(`${buttonSelector}:focus-visible`)); + await page.keyboard.press("Enter"); + await page.waitForSelector(`${buttonSelector}[hidden]`); + await page.waitForSelector("#altTextDialog", { visible: true }); + await page.keyboard.press("Escape"); + await page.waitForSelector(`${buttonSelector}:not([hidden])`); + await (browserName === "chrome" + ? page.waitForSelector(`${buttonSelector}:focus`) + : page.waitForSelector(`${buttonSelector}:focus-visible`)); }) ); }); diff --git a/web/alt_text_manager.js b/web/alt_text_manager.js index 48bd2df02a7e92..23e7aa9b669fc2 100644 --- a/web/alt_text_manager.js +++ b/web/alt_text_manager.js @@ -32,6 +32,8 @@ class AltTextManager { #hasUsedPointer = false; + #isFromKeyboard = false; + #optionDescription; #optionDecorative; @@ -134,7 +136,7 @@ class AltTextManager { this.#dialog.append(svg); } - async editAltText(uiManager, editor) { + async editAltText(uiManager, editor, isFromKeyboard) { if (this.#currentEditor || !editor) { return; } @@ -157,6 +159,7 @@ class AltTextManager { this.#previousAltText = this.#textarea.value = altText?.trim() || ""; this.#updateUIState(); + this.#isFromKeyboard = isFromKeyboard; this.#currentEditor = editor; this.#uiManager = uiManager; this.#uiManager.removeEditListeners(); @@ -264,6 +267,7 @@ class AltTextManager { this.#removeOnClickListeners(); this.#uiManager?.addEditListeners(); this.#eventBus._off("resize", this.#boundSetPosition); + this.#currentEditor.altTextFinish(this.#isFromKeyboard); this.#currentEditor = null; this.#uiManager = null; } diff --git a/web/annotation_editor_layer_builder.css b/web/annotation_editor_layer_builder.css index ef2810cb51f00f..8c8257439c79b1 100644 --- a/web/annotation_editor_layer_builder.css +++ b/web/annotation_editor_layer_builder.css @@ -472,6 +472,7 @@ min-width: 88px; z-index: 1; pointer-events: all; + opacity: var(--alt-text-button-opacity); color: var(--alt-text-fg-color); font: menu;