Bug 1999154 - Add the ability to undo comment deletion

This commit is contained in:
Marco Castelluccio 2026-01-21 13:37:20 +01:00
parent 6a4a3b060d
commit d9f67bd8ee
No known key found for this signature in database
GPG Key ID: 7EC7F621C5F89953
5 changed files with 181 additions and 2 deletions

View File

@ -561,6 +561,7 @@ pdfjs-editor-undo-bar-message-freetext = Text removed
pdfjs-editor-undo-bar-message-ink = Drawing removed
pdfjs-editor-undo-bar-message-stamp = Image removed
pdfjs-editor-undo-bar-message-signature = Signature removed
pdfjs-editor-undo-bar-message-comment = Comment removed
# Variables:
# $count (Number) - the number of removed annotations.
pdfjs-editor-undo-bar-message-multiple =

View File

@ -1177,6 +1177,22 @@ class AnnotationEditorUIManager {
this.#commentManager?.removeComments([editor.uid]);
}
/**
* Delete a comment from an editor with undo support.
* @param {AnnotationEditor} editor - The editor whose comment to delete.
* @param {string} savedComment - The comment text to save for undo.
*/
deleteComment(editor, savedComment) {
const undo = () => {
editor.comment = savedComment;
};
const cmd = () => {
this._editorUndoBar?.show(undo, "comment");
editor.comment = null;
};
this.addCommands({ cmd, undo, mustExec: true });
}
toggleComment(editor, isSelected, visibility = undefined) {
this.#commentManager?.toggleCommentPopup(editor, isSelected, visibility);
}

View File

@ -965,4 +965,159 @@ describe("Comment", () => {
);
});
});
describe("Undo deletion popup for comments (bug 1999154)", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
"page-fit",
null,
{ enableComment: true }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that deleting a comment can be undone using the undo button", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await highlightSpan(page, 1, "Abstract");
const editorSelector = getEditorSelector(0);
const comment = "Test comment for undo";
await editComment(page, editorSelector, comment);
// Stay in highlight mode - don't disable it
await waitAndClick(
page,
`${editorSelector} .annotationCommentButton`
);
await page.waitForSelector("#commentPopup", { visible: true });
await waitAndClick(page, "button.commentPopupDelete");
await page.waitForSelector("#editorUndoBar", { visible: true });
await page.waitForSelector("#editorUndoBarUndoButton", {
visible: true,
});
await page.click("#editorUndoBarUndoButton");
// Check that the comment is restored by hovering to show the popup
await page.hover(`${editorSelector} .annotationCommentButton`);
await page.waitForSelector("#commentPopup", { visible: true });
const popupText = await page.evaluate(
() =>
document.querySelector("#commentPopup .commentPopupText")
?.textContent
);
expect(popupText).withContext(`In ${browserName}`).toEqual(comment);
})
);
});
it("must check that the undo deletion popup displays 'Comment removed' message", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await highlightSpan(page, 1, "Abstract");
const editorSelector = getEditorSelector(0);
await editComment(page, editorSelector, "Test comment");
// Stay in highlight mode - don't disable it
await waitAndClick(
page,
`${editorSelector} .annotationCommentButton`
);
await page.waitForSelector("#commentPopup", { visible: true });
await waitAndClick(page, "button.commentPopupDelete");
await page.waitForFunction(() => {
const messageElement = document.querySelector(
"#editorUndoBarMessage"
);
return messageElement && messageElement.textContent.trim() !== "";
});
const message = await page.waitForSelector("#editorUndoBarMessage");
const messageText = await page.evaluate(
el => el.textContent,
message
);
expect(messageText)
.withContext(`In ${browserName}`)
.toContain("Comment removed");
})
);
});
it("must check that the undo bar closes when clicking the close button", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await highlightSpan(page, 1, "Abstract");
const editorSelector = getEditorSelector(0);
await editComment(page, editorSelector, "Test comment");
// Stay in highlight mode - don't disable it
await waitAndClick(
page,
`${editorSelector} .annotationCommentButton`
);
await page.waitForSelector("#commentPopup", { visible: true });
await waitAndClick(page, "button.commentPopupDelete");
await page.waitForSelector("#editorUndoBar", { visible: true });
await waitAndClick(page, "#editorUndoBarCloseButton");
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that deleting a comment can be undone using Ctrl+Z", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await highlightSpan(page, 1, "Abstract");
const editorSelector = getEditorSelector(0);
const comment = "Test comment for Ctrl+Z undo";
await editComment(page, editorSelector, comment);
// Stay in highlight mode - don't disable it
await waitAndClick(
page,
`${editorSelector} .annotationCommentButton`
);
await page.waitForSelector("#commentPopup", { visible: true });
await waitAndClick(page, "button.commentPopupDelete");
await page.waitForSelector("#editorUndoBar", { visible: true });
// Use Ctrl+Z to undo
await kbModifierDown(page);
await page.keyboard.press("z");
await kbModifierUp(page);
// The undo bar should be hidden after undo
await page.waitForSelector("#editorUndoBar", { hidden: true });
// Check that the comment is restored by hovering to show the popup
await page.hover(`${editorSelector} .annotationCommentButton`);
await page.waitForSelector("#commentPopup", { visible: true });
const popupText = await page.evaluate(
() =>
document.querySelector("#commentPopup .commentPopupText")
?.textContent
);
expect(popupText).withContext(`In ${browserName}`).toEqual(comment);
})
);
});
});
});

View File

@ -982,9 +982,15 @@ class CommentPopup {
},
},
});
this.#editor.comment = null;
this.#editor.focus();
const savedComment = this.#editor.comment?.text;
const editor = this.#editor;
this.destroy();
if (savedComment) {
editor._uiManager.deleteComment(editor, savedComment);
} else {
editor.comment = null;
}
editor.focus();
});
del.addEventListener("contextmenu", noContextMenu);
buttons.append(edit, del);

View File

@ -40,6 +40,7 @@ class EditorUndoBar {
stamp: "pdfjs-editor-undo-bar-message-stamp",
ink: "pdfjs-editor-undo-bar-message-ink",
signature: "pdfjs-editor-undo-bar-message-signature",
comment: "pdfjs-editor-undo-bar-message-comment",
_multiple: "pdfjs-editor-undo-bar-message-multiple",
});