1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-19 14:48:08 +02:00

[Editor] Fix the role of the different editors in order to make them interactive elements (bug 1953290)

Having some interactive elements forces the screen readers to switch to form mode
and consequently they delegate the keyboard stuff to the browser.
This patch sets an aria label on each editor in order to have a better description than just
'application'.
This commit is contained in:
Calixte Denizet 2025-03-12 20:37:02 +01:00
parent bec6287b0a
commit 7fce3eac93
9 changed files with 41 additions and 35 deletions

View file

@ -324,6 +324,19 @@ pdfjs-editor-signature-button =
.title = Add signature
pdfjs-editor-signature-button-label = Add signature
## Default editor aria labels
# “Highlight” is a noun, the string is used on the editor for highlights.
pdfjs-editor-highlight-editor =
.aria-label = Highlight editor
# “Drawing” is a noun, the string is used on the editor for drawings.
pdfjs-editor-ink-editor =
.aria-label = Drawing editor
pdfjs-editor-signature-editor =
.aria-label = Signature editor
pdfjs-editor-stamp-editor =
.aria-label = Image editor
## Remove button for the various kind of editor.
pdfjs-editor-remove-ink-button =
@ -360,10 +373,6 @@ pdfjs-editor-signature-add-signature-button-label = Add new signature
pdfjs-free-text2 =
.aria-label = Text Editor
.default-content = Start typing…
pdfjs-ink =
.aria-label = Draw Editor
pdfjs-ink-canvas =
.aria-label = User-created image
## Alt-text dialog

View file

@ -330,7 +330,7 @@ class AltText {
button.append(tooltip);
}
const element = this.#editor.getImageForAltText();
const element = this.#editor.getElementForAltText();
element?.setAttribute("aria-describedby", tooltip.id);
}
}

View file

@ -1142,13 +1142,17 @@ class AnnotationEditor {
* @returns {HTMLDivElement | null}
*/
render() {
this.div = document.createElement("div");
this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360);
this.div.className = this.name;
this.div.setAttribute("id", this.id);
this.div.tabIndex = this.#disabled ? -1 : 0;
const div = (this.div = document.createElement("div"));
div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360);
div.className = this.name;
div.setAttribute("id", this.id);
div.tabIndex = this.#disabled ? -1 : 0;
div.setAttribute("role", "application");
if (this.defaultL10nId) {
div.setAttribute("data-l10n-id", this.defaultL10nId);
}
if (!this._isVisible) {
this.div.classList.add("hidden");
div.classList.add("hidden");
}
this.setInForeground();
@ -1156,23 +1160,22 @@ class AnnotationEditor {
const [parentWidth, parentHeight] = this.parentDimensions;
if (this.parentRotation % 180 !== 0) {
this.div.style.maxWidth = `${((100 * parentHeight) / parentWidth).toFixed(
div.style.maxWidth = `${((100 * parentHeight) / parentWidth).toFixed(
2
)}%`;
div.style.maxHeight = `${((100 * parentWidth) / parentHeight).toFixed(
2
)}%`;
this.div.style.maxHeight = `${(
(100 * parentWidth) /
parentHeight
).toFixed(2)}%`;
}
const [tx, ty] = this.getInitialTranslation();
this.translate(tx, ty);
bindEvents(this, this.div, ["pointerdown"]);
bindEvents(this, div, ["keydown", "pointerdown"]);
if (this.isResizable && this._uiManager._supportsPinchToZoom) {
this.#touchManager ||= new TouchManager({
container: this.div,
container: div,
isPinchingDisabled: () => !this.isSelected,
onPinchStart: this.#touchPinchStartCallback.bind(this),
onPinching: this.#touchPinchCallback.bind(this),
@ -1183,7 +1186,7 @@ class AnnotationEditor {
this._uiManager._editorUndoBar?.hide();
return this.div;
return div;
}
#touchPinchStartCallback() {
@ -1692,7 +1695,6 @@ class AnnotationEditor {
if (this.isResizable) {
this.#createResizers();
this.#resizersDiv.classList.remove("hidden");
bindEvents(this, this.div, ["keydown"]);
}
}
@ -1892,8 +1894,8 @@ class AnnotationEditor {
/**
* @returns {HTMLElement | null} the element requiring an alt text.
*/
getImageForAltText() {
return null;
getElementForAltText() {
return this.div;
}
/**

View file

@ -111,6 +111,7 @@ class HighlightEditor extends AnnotationEditor {
this.#methodOfCreation = params.methodOfCreation || "";
this.#text = params.text || "";
this._isDraggable = false;
this.defaultL10nId = "pdfjs-editor-highlight-editor";
if (params.highlightId > -1) {
this.#isFreeHighlight = true;

View file

@ -68,6 +68,7 @@ class InkEditor extends DrawingEditor {
constructor(params) {
super({ ...params, name: "inkEditor" });
this._willKeepAspectRatio = true;
this.defaultL10nId = "pdfjs-editor-ink-editor";
}
/** @inheritdoc */

View file

@ -79,6 +79,7 @@ class SignatureEditor extends DrawingEditor {
this._willKeepAspectRatio = true;
this.#signatureData = params.signatureData || null;
this.#description = null;
this.defaultL10nId = "pdfjs-editor-signature-editor";
}
/** @inheritdoc */
@ -156,7 +157,6 @@ class SignatureEditor extends DrawingEditor {
}
super.render();
this.div.setAttribute("role", "figure");
if (this._drawId === null) {
if (this.#signatureData) {
@ -259,7 +259,7 @@ class SignatureEditor extends DrawingEditor {
const { outline } = (this.#signatureData = data);
this.#isExtracted = outline instanceof ContourDrawOutline;
this.#description = description;
this.div.setAttribute("aria-label", description);
this.div.setAttribute("aria-description", description);
let drawingOptions;
if (this.#isExtracted) {
drawingOptions = SignatureEditor.getDefaultDrawingOptions();

View file

@ -56,6 +56,7 @@ class StampEditor extends AnnotationEditor {
super({ ...params, name: "stampEditor" });
this.#bitmapUrl = params.bitmapUrl;
this.#bitmapFile = params.bitmapFile;
this.defaultL10nId = "pdfjs-editor-stamp-editor";
}
/** @inheritdoc */
@ -353,7 +354,6 @@ class StampEditor extends AnnotationEditor {
super.render();
this.div.hidden = true;
this.div.setAttribute("role", "figure");
this.addAltTextButton();
@ -474,7 +474,7 @@ class StampEditor extends AnnotationEditor {
action: "inserted_image",
});
if (this.#bitmapFileName) {
canvas.setAttribute("aria-label", this.#bitmapFileName);
this.div.setAttribute("aria-description", this.#bitmapFileName);
}
}
@ -686,11 +686,6 @@ class StampEditor extends AnnotationEditor {
);
}
/** @inheritdoc */
getImageForAltText() {
return this.#canvas;
}
#serializeBitmap(toUrl) {
if (toUrl) {
if (this.#isSvg) {

View file

@ -184,7 +184,7 @@ describe("Signature Editor", () => {
// Check the aria label.
await page.waitForSelector(
`${editorSelector}[aria-label="Hello World"]`
`${editorSelector}[aria-description="Hello World"]`
);
// Edit the description.

View file

@ -361,9 +361,7 @@ describe("Stamp Editor", () => {
await page.click(saveButtonSelector);
// Check that the canvas has an aria-describedby attribute.
await page.waitForSelector(
`${editorSelector} canvas[aria-describedby]`
);
await page.waitForSelector(`${editorSelector}[aria-describedby]`);
// Wait for the alt-text button to have the correct icon.
await page.waitForSelector(`${buttonSelector}.done`);