diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index a7f8e1d73..4e6a5ec97 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -206,6 +206,9 @@ class AnnotationElement { const container = document.createElement("section"); container.setAttribute("data-annotation-id", data.id); + if (!(this instanceof WidgetAnnotationElement)) { + container.tabIndex = DEFAULT_TAB_INDEX; + } // The accessibility manager will move the annotation in the DOM in // order to match the visual ordering. @@ -1969,6 +1972,8 @@ class PopupAnnotationElement extends AnnotationElement { class PopupElement { #dateTimePromise = null; + #boundKeyDown = this.#keyDown.bind(this); + #boundHide = this.#hide.bind(this); #boundShow = this.#show.bind(this); @@ -2044,6 +2049,11 @@ class PopupElement { } } + // Attach the event listener to toggle the popup with the keyboard. + for (const element of elements) { + element.container?.addEventListener("keydown", this.#boundKeyDown); + } + this.#container.hidden = true; if (open) { this.#toggle(); @@ -2187,6 +2197,16 @@ class PopupElement { return p; } + #keyDown(event) { + if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) { + return; + } + + if (event.key === "Enter" || (event.key === "Escape" && this.#pinned)) { + this.#toggle(); + } + } + /** * Toggle the visibility of the popup. */ @@ -2195,9 +2215,11 @@ class PopupElement { if (this.#pinned) { this.#show(); this.#container.addEventListener("click", this.#boundToggle); + this.#container.addEventListener("keydown", this.#boundKeyDown); } else { this.#hide(); this.#container.removeEventListener("click", this.#boundToggle); + this.#container.removeEventListener("keydown", this.#boundKeyDown); } } diff --git a/test/integration/annotation_spec.js b/test/integration/annotation_spec.js index c324e1f5f..06e5562ce 100644 --- a/test/integration/annotation_spec.js +++ b/test/integration/annotation_spec.js @@ -527,4 +527,65 @@ describe("ResetForm action", () => { }); }); }); + + describe("Toggle popup with keyboard", () => { + describe("tagged_stamp.pdf", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "tagged_stamp.pdf", + "[data-annotation-id='20R']" + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must check that the popup has the correct visibility", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + let hidden = await page.$eval( + "[data-annotation-id='21R']", + el => el.hidden + ); + expect(hidden).withContext(`In ${browserName}`).toEqual(true); + await page.focus("[data-annotation-id='20R']"); + await page.keyboard.press("Enter"); + await page.waitForTimeout(10); + hidden = await page.$eval( + "[data-annotation-id='21R']", + el => el.hidden + ); + expect(hidden).withContext(`In ${browserName}`).toEqual(false); + + await page.keyboard.press("Enter"); + await page.waitForTimeout(10); + hidden = await page.$eval( + "[data-annotation-id='21R']", + el => el.hidden + ); + expect(hidden).withContext(`In ${browserName}`).toEqual(true); + + await page.keyboard.press("Enter"); + await page.waitForTimeout(10); + hidden = await page.$eval( + "[data-annotation-id='21R']", + el => el.hidden + ); + expect(hidden).withContext(`In ${browserName}`).toEqual(false); + + await page.keyboard.press("Escape"); + await page.waitForTimeout(10); + hidden = await page.$eval( + "[data-annotation-id='21R']", + el => el.hidden + ); + expect(hidden).withContext(`In ${browserName}`).toEqual(true); + }) + ); + }); + }); + }); });