item.isAnnotation())) {
+ format = Zotero.QuickCopy.getNoteFormat();
+ items = [Zotero.QuickCopy.annotationsToNote(items)];
+ }
+
Zotero.debug("Dragging with format " + format);
format = Zotero.QuickCopy.unserializeSetting(format);
try {
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
index f00f00c459..01e3b9330a 100644
--- a/chrome/content/zotero/zoteroPane.js
+++ b/chrome/content/zotero/zoteroPane.js
@@ -2009,7 +2009,7 @@ var ZoteroPane = new function()
let format = Zotero.QuickCopy.getFormatFromURL(Zotero.QuickCopy.lastActiveURL);
format = Zotero.QuickCopy.unserializeSetting(format);
if (format.mode == 'bibliography') {
- canCopy = selectedItems.some(item => item.isRegularItem());
+ canCopy = selectedItems.some(item => item.isRegularItem() || item.isAnnotation());
}
else {
canCopy = true;
@@ -2018,6 +2018,7 @@ var ZoteroPane = new function()
document.getElementById('cmd_zotero_copyCitation').setAttribute('disabled', !canCopy);
document.getElementById('cmd_zotero_copyBibliography').setAttribute('disabled', !canCopy);
+ document.getElementById('cmd_zotero_copyAnnotation').setAttribute('disabled', !canCopy);
};
@@ -2177,6 +2178,13 @@ var ZoteroPane = new function()
else if (collectionTreeRow.isShare()) {
return false;
}
+ // If multiple items are selected, some are annotations and some are not, do nothing,
+ // since annotations have different treatment from other items
+ let selected = this.itemsView.getSelectedItems();
+ if (!selected.every(item => item.isAnnotation())
+ && selected.some(item => item.isAnnotation())) {
+ return false;
+ }
return true;
};
@@ -2226,15 +2234,19 @@ var ZoteroPane = new function()
if (!this.canDeleteSelectedItems()) {
return;
}
-
- if (collectionTreeRow.isPublications()) {
+ var prompt;
+ // Backspace on annotation items = prompt to erase
+ if (this.itemsView.getSelectedItems().every(item => item.isAnnotation())) {
+ prompt = toDelete;
+ }
+ else if (collectionTreeRow.isPublications()) {
let toRemoveFromPublications = {
title: Zotero.getString('pane.items.removeFromPublications.title'),
text: Zotero.getString(
'pane.items.removeFromPublications' + (this.itemsView.selection.count > 1 ? '.multiple' : '')
)
};
- var prompt = force ? toTrash : toRemoveFromPublications;
+ prompt = force ? toTrash : toRemoveFromPublications;
}
else if (collectionTreeRow.isLibrary(true)
|| collectionTreeRow.isSearch()
@@ -2242,11 +2254,11 @@ var ZoteroPane = new function()
|| collectionTreeRow.isRetracted()
|| collectionTreeRow.isDuplicates()) {
// In library, don't prompt if meta key was pressed
- var prompt = (force && !fromMenu) ? false : toTrash;
+ prompt = (force && !fromMenu) ? false : toTrash;
}
else if (collectionTreeRow.isCollection()) {
if (force) {
- var prompt = toTrash;
+ prompt = toTrash;
}
else {
// Ignore unmodified action if only child items are selected
@@ -2275,12 +2287,12 @@ var ZoteroPane = new function()
}
}
else {
- var prompt = toRemove;
+ prompt = toRemove;
}
}
}
else if (collectionTreeRow.isTrash() || collectionTreeRow.isBucket()) {
- var prompt = toDelete;
+ prompt = toDelete;
}
if (!prompt || Services.prompt.confirm(window, prompt.title, prompt.text)) {
@@ -2662,6 +2674,11 @@ var ZoteroPane = new function()
if (items.every(item => item.isNote() || item.isAttachment())) {
format = Zotero.QuickCopy.getNoteFormat();
}
+ // To copy annotations, wrap them in a temp note
+ if (items.every(item => item.isAnnotation())) {
+ format = Zotero.QuickCopy.getNoteFormat();
+ items = [Zotero.QuickCopy.annotationsToNote(items)];
+ }
format = Zotero.QuickCopy.unserializeSetting(format);
// In bibliography mode, remove notes and attachments
@@ -3699,7 +3716,8 @@ var ZoteroPane = new function()
&& !collectionTreeRow.isDuplicates()
&& !collectionTreeRow.isFeedsOrFeed()) {
if (items.some(item => attachmentsWithExtractableAnnotations(item).length)
- || items.some(item => isAttachmentWithExtractableAnnotations(item))) {
+ || items.some(item => isAttachmentWithExtractableAnnotations(item))
+ || items.some(item => item.isAnnotation())) {
let menuitem = menu.childNodes[m.createNoteFromAnnotations];
show.add(m.createNoteFromAnnotations);
let key;
@@ -3789,7 +3807,7 @@ var ZoteroPane = new function()
}
}
// Show "(Create|Add) Note from Annotations" on attachment with extractable annotations
- else if (isAttachmentWithExtractableAnnotations(item)) {
+ else if (isAttachmentWithExtractableAnnotations(item) || item.isAnnotation()) {
show.add(m.createNoteFromAnnotations);
show.add(m.sep2);
}
@@ -3852,6 +3870,9 @@ var ZoteroPane = new function()
show.add(m.sep5);
}
}
+ else if (item.isAnnotation()) {
+ // Some annotation specific menus?
+ }
else if (item.isFeedItem) {
show.add(m.toggleRead);
if (item.isRead) {
@@ -4005,6 +4026,17 @@ var ZoteroPane = new function()
show.add(m.changeParentItem);
}
+ // Only keep annotation-specific options if annotations are selected
+ let annotationsSelected = items.some(item => item.isAnnotation());
+ if (annotationsSelected) {
+ for (let i in m) {
+ if (i == 'createNoteFromAnnotations') {
+ continue;
+ }
+ show.delete(m[i]);
+ }
+ }
+
// Set labels, plural if necessary
menu.childNodes[m.findFile].setAttribute('label', Zotero.getString('pane.items.menu.findAvailableFile'));
menu.childNodes[m.moveToTrash].setAttribute('label', Zotero.getString('pane.items.menu.moveToTrash' + multiple));
@@ -4031,7 +4063,10 @@ var ZoteroPane = new function()
for (let x of show) {
menu.childNodes[x].setAttribute('hidden', false);
}
-
+
+ // No locate menu options if annotations are selected
+ if (annotationsSelected) return;
+
// add locate menu options
yield Zotero_LocateMenu.buildContextMenu(menu, true);
});
@@ -4955,6 +4990,9 @@ var ZoteroPane = new function()
else if (item.isAttachment()) {
yield this.viewAttachment(item.id, event);
}
+ else if (item.isAnnotation()) {
+ this.viewPDF(item.parentItemID, { annotationID: item.key });
+ }
}
});
@@ -5741,6 +5779,7 @@ var ZoteroPane = new function()
}
var attachments = [];
+ var annotations = [];
for (let item of items) {
if (item.isRegularItem()) {
// Find all child items with extractable annotations
@@ -5752,17 +5791,19 @@ var ZoteroPane = new function()
else if (isAttachmentWithExtractableAnnotations(item)) {
attachments.push(item);
}
+ else if (item.isAnnotation() && item.annotationType != 'ink') {
+ annotations.push(item);
+ }
else {
continue;
}
}
- if (!attachments.length) {
+ if (!attachments.length && !annotations.length) {
Zotero.debug("No attachments found", 2);
return;
}
- var annotations = [];
for (let attachment of attachments) {
if (attachment.isPDFAttachment()) {
try {
@@ -5858,6 +5899,9 @@ var ZoteroPane = new function()
else if (isAttachmentWithExtractableAnnotations(item)) {
attachments.push(item);
}
+ else if (item.isAnnotation()) {
+ annotations.push(item);
+ }
else {
continue;
}
@@ -5870,7 +5914,7 @@ var ZoteroPane = new function()
Zotero.logError(e);
}
}
- annotations.push(...attachment.getAnnotations().filter(x => x.annotationType != 'ink'));
+ annotations.push(...attachment.getAnnotations().filter(x => x.annotationType != 'ink' && x.annotationType != 'image'));
}
}
diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml
index 08103a5c49..595f90399f 100644
--- a/chrome/content/zotero/zoteroPane.xhtml
+++ b/chrome/content/zotero/zoteroPane.xhtml
@@ -110,6 +110,9 @@
+
@@ -388,6 +391,11 @@
command="cmd_zotero_copyBibliography"
key="key_copyBibliography"
hidden="true"/>
+
diff --git a/chrome/locale/en-US/zotero/zotero.ftl b/chrome/locale/en-US/zotero/zotero.ftl
index e256adb2ce..51fee32b99 100644
--- a/chrome/locale/en-US/zotero/zotero.ftl
+++ b/chrome/locale/en-US/zotero/zotero.ftl
@@ -96,6 +96,12 @@ menu-view-columns-move-left =
menu-view-columns-move-right =
.label = Move Column Right
+menu-edit-copy-annotation =
+ .label = { $count ->
+ [one] Copy Annotation
+ *[other] Copy { $count } Annotations
+ }
+
main-window-command =
.label = Library
main-window-key =
@@ -592,6 +598,8 @@ toggle-preview =
*[unknown] Toggle
} Attachment Preview
+annotation-image-not-available = [Image not available]
+
quicksearch-mode =
.aria-label = Quick Search mode
quicksearch-input =
diff --git a/scss/_zotero.scss b/scss/_zotero.scss
index f63195588f..f2924523a1 100644
--- a/scss/_zotero.scss
+++ b/scss/_zotero.scss
@@ -102,6 +102,7 @@
@import "elements/librariesCollectionsBox";
@import "elements/duplicatesMergePane";
@import "elements/itemMessagePane";
+@import "elements/itemPaneAnnotations";
@import "elements/itemDetails";
@import "elements/itemPane";
@import "elements/itemPaneCustomSection";
diff --git a/scss/abstracts/_variables.scss b/scss/abstracts/_variables.scss
index 75813464ae..01495e8e37 100644
--- a/scss/abstracts/_variables.scss
+++ b/scss/abstracts/_variables.scss
@@ -29,6 +29,9 @@ $font-size-base: 13px;
$font-size-h1: 20px;
$font-size-h2: 16px;
+// Font size used in tag selector and annotation rows of itemTree
+$font-size-small: 0.923076923em;
+
$line-height-base: 1.539;
$line-height-computed: ceil($font-size-base * $line-height-base);
diff --git a/scss/components/_item-tree.scss b/scss/components/_item-tree.scss
index 55509438e2..d4041c6288 100644
--- a/scss/components/_item-tree.scss
+++ b/scss/components/_item-tree.scss
@@ -113,7 +113,9 @@
}
.icon-item-type + .tag-swatch,
- .icon-item-type + .colored-tag-swatches {
+ .icon-item-type + .colored-tag-swatches,
+ .annotation-icon + .tag-swatch,
+ .annotation-icon + .colored-tag-swatches {
margin-inline-start: 4px;
}
@@ -218,6 +220,40 @@
opacity: 0.4;
}
}
+
+ .annotation-row {
+ .cell {
+ font-size: $font-size-small;
+ max-width: fit-content;
+ // Do not italicize CJK characters
+ font-synthesis: none;
+ &.title {
+ flex-grow: 1;
+ flex-basis: 0;
+ max-width: fit-content;
+ .cell-text::before {
+ content: attr(q-mark-open)
+ }
+ &::after {
+ content: attr(q-mark-close)
+ }
+ .italics {
+ font-style: italic;
+ }
+ }
+ &.annotation-comment {
+ flex-grow: 2;
+ flex-basis: 0;
+ }
+ }
+ &.tight .cell {
+ padding: 0 2px;
+ }
+ }
+
+ .cell .annotation-icon {
+ -moz-context-properties: fill;
+ }
.numNotes, .hasAttachment {
text-align: center;
diff --git a/scss/components/_tagSelector.scss b/scss/components/_tagSelector.scss
index 7c944d195e..f1d3555452 100644
--- a/scss/components/_tagSelector.scss
+++ b/scss/components/_tagSelector.scss
@@ -103,7 +103,7 @@
.tag-selector-item {
border-radius: 4px;
cursor: pointer;
- font-size: 0.916666667em;
+ font-size: $font-size-small;
line-height: 1.272727273;
overflow: hidden;
padding: 1px 4px;
diff --git a/scss/elements/_annotationRow.scss b/scss/elements/_annotationRow.scss
index a2de14c31c..734cc865af 100644
--- a/scss/elements/_annotationRow.scss
+++ b/scss/elements/_annotationRow.scss
@@ -5,6 +5,7 @@ annotation-row {
border-radius: 5px;
border: 1px solid var(--color-quinary-on-sidepane);
background: var(--material-background);
+ @include focus-ring;
.head {
display: flex;
@@ -56,7 +57,7 @@ annotation-row {
padding: 3px 8px;
@include comfortable {
- padding-block: 4px;
+ margin-block: 4px;
}
}
}
diff --git a/scss/elements/_itemMessagePane.scss b/scss/elements/_itemMessagePane.scss
index 1ab30dedb8..f657de6a38 100644
--- a/scss/elements/_itemMessagePane.scss
+++ b/scss/elements/_itemMessagePane.scss
@@ -11,6 +11,7 @@ item-message-pane {
background: var(--material-toolbar);
border-bottom: var(--material-panedivider);
height: 28px;
+ align-items: center;
&:empty {
display: none;
@@ -19,6 +20,9 @@ item-message-pane {
button {
height: 26px;
margin: 0;
+ @media (-moz-platform: macos) {
+ margin: 0px -2px;
+ }
flex-grow: 1;
}
}
diff --git a/scss/elements/_itemPaneAnnotations.scss b/scss/elements/_itemPaneAnnotations.scss
new file mode 100644
index 0000000000..a81daaad4d
--- /dev/null
+++ b/scss/elements/_itemPaneAnnotations.scss
@@ -0,0 +1,69 @@
+annotation-items-pane {
+
+ display: flex;
+ flex-direction: column;
+
+ .custom-head {
+ display: flex;
+ flex-direction: row;
+ align-self: stretch;
+ gap: 8px;
+ padding: 6px 8px;
+ background: var(--material-sidepane);
+ border-bottom: var(--material-panedivider);
+ height: 28px;
+ align-items: center;
+
+ &:empty {
+ display: none;
+ }
+
+ button {
+ height: 26px;
+ margin: 0;
+ @media (-moz-platform: macos) {
+ margin: 0px -2px;
+ }
+ flex-grow: 1;
+ }
+ }
+
+ // Make sure the summary containing name of top-level item is always visible
+ // and titles are cut off by letter and not by word
+ collapsible-section > .head .title-box .summary {
+ opacity: 1 !important;
+ width: 0;
+ white-space: nowrap;
+ display: inline;
+ }
+
+ // Annotation icon for collapsible section
+ collapsible-section > .head .title::before {
+ content: '';
+ width: 16px;
+ height: 16px;
+ background: icon-url("itempane/16/attachment-annotations.svg") no-repeat center;
+ -moz-context-properties: fill, fill-opacity, stroke, stroke-opacity;
+ fill: var(--tag-purple);
+ stroke: var(--tag-purple);
+ }
+
+ collapsible-section > .body {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ @include comfortable {
+ gap: 8px;
+ }
+ }
+
+ collapsible-section:not(:last-child) {
+ border-bottom: 1px solid var(--fill-quinary);
+ }
+
+ // Do not cut off annotation text and comment
+ annotation-row .body .comment,
+ annotation-row .body .quote {
+ -webkit-line-clamp: inherit !important;
+ }
+}
\ No newline at end of file
diff --git a/test/tests/itemPaneTest.js b/test/tests/itemPaneTest.js
index fbf52cb8ac..31f5c0bf27 100644
--- a/test/tests/itemPaneTest.js
+++ b/test/tests/itemPaneTest.js
@@ -2083,4 +2083,45 @@ describe("Item pane", function () {
Zotero.ItemPaneManager.unregisterSection(registeredID);
});
});
+
+ describe("AnnotationItemsPane", function () {
+ it("should display selected annotations groupped by parent item", async () => {
+ let toplevelItemOne = await createDataObject('item', { title: "Item one" });
+ let attachmentOne = await importFileAttachment('test.pdf', { title: 'PDF', parentItemID: toplevelItemOne.id });
+ let highlightOne = await createAnnotation('highlight', attachmentOne);
+
+ let toplevelItemTwo = await createDataObject('item', { title: "Item two" });
+ let attachmentTwo = await importFileAttachment('test.pdf', { title: 'PDF', parentItemID: toplevelItemTwo.id });
+ let highlightTwo = await createAnnotation('highlight', attachmentTwo);
+
+ ZoteroPane.itemsView.expandAllRows();
+
+ await ZoteroPane.itemsView.selectItems([highlightOne.id, highlightTwo.id]);
+
+ let sections = [...win.document.querySelectorAll("annotation-items-pane collapsible-section")];
+ // Top level items' titles are in section summaries
+ assert.equal(sections[0].summary, toplevelItemOne.getDisplayTitle());
+ assert.equal(sections[1].summary, toplevelItemTwo.getDisplayTitle());
+ // Each item's section contains its annotation
+ assert.equal(sections[0].querySelector("annotation-row").annotation.id, highlightOne.id);
+ assert.equal(sections[1].querySelector("annotation-row").annotation.id, highlightTwo.id);
+ });
+
+ it("should refresh when annotation is updated", async () => {
+ let toplevelItemOne = await createDataObject('item', { title: "Item one" });
+ let attachmentOne = await importFileAttachment('test.pdf', { title: 'PDF', parentItemID: toplevelItemOne.id });
+ let highlightOne = await createAnnotation('highlight', attachmentOne);
+
+ highlightOne.annotationText = "Annotation";
+ await highlightOne.saveTx();
+
+ ZoteroPane.itemsView.expandAllRows();
+ await ZoteroPane.itemsView.selectItems([highlightOne.id]);
+
+ assert.equal(win.document.querySelector("annotation-items-pane annotation-row .quote").textContent, "Annotation");
+ highlightOne.annotationText = "Updated";
+ await highlightOne.saveTx();
+ assert.equal(win.document.querySelector("annotation-items-pane annotation-row .quote").textContent, "Updated");
+ });
+ });
});
diff --git a/test/tests/itemTreeTest.js b/test/tests/itemTreeTest.js
index 00f26ed41d..abe3b41d93 100644
--- a/test/tests/itemTreeTest.js
+++ b/test/tests/itemTreeTest.js
@@ -225,23 +225,28 @@ describe("Zotero.ItemTree", function() {
var note1 = await createDataObject('item', { itemType: 'note', parentID: item1.id });
var note2 = await createDataObject('item', { itemType: 'note', parentID: item2.id });
var note3 = await createDataObject('item', { itemType: 'note', parentID: item3.id });
+
+ // one of the items has an attachment with annotations
+ var attachment = await importFileAttachment('test.pdf', { title: 'PDF', parentItemID: item1.id });
+ var highlight = await createAnnotation('highlight', attachment);
+ var underline = await createAnnotation('underline', attachment);
- var toSelect = [note1.id, note2.id, note3.id];
+ var toSelect = [note1.id, note2.id, note3.id, highlight.id, underline.id];
itemsView.collapseAllRows();
var numSelected = await itemsView.selectItems(toSelect);
- assert.equal(numSelected, 3);
+ assert.equal(numSelected, 5);
var selected = itemsView.getSelectedItems(true);
- assert.lengthOf(selected, 3);
+ assert.lengthOf(selected, 5);
assert.sameMembers(selected, toSelect);
// Again with the ids given in reverse order
itemsView.collapseAllRows();
toSelect = toSelect.reverse();
- var numSelected = await itemsView.selectItems(toSelect);
- assert.equal(numSelected, 3);
- var selected = itemsView.getSelectedItems(true);
- assert.lengthOf(selected, 3);
+ numSelected = await itemsView.selectItems(toSelect);
+ assert.equal(numSelected, 5);
+ selected = itemsView.getSelectedItems(true);
+ assert.lengthOf(selected, 5);
assert.sameMembers(selected, toSelect);
});
});
@@ -1587,4 +1592,133 @@ describe("Zotero.ItemTree", function() {
assert.equal(cellText.innerHTML, 'Review of Review of Book <another-tag/>');
});
});
+
+ describe("Annotations", function() {
+ let toplevelItem, attachment, highlight, underline, ink, image, note;
+
+ before(async () => {
+ var collection = await createDataObject('collection');
+ await select(win, collection);
+ });
+
+ beforeEach(async () => {
+ toplevelItem = await createDataObject('item', { title: "Item" });
+ attachment = await importFileAttachment('test.pdf', { title: 'PDF', parentItemID: toplevelItem.id });
+ highlight = await createAnnotation('highlight', attachment);
+ underline = await createAnnotation('underline', attachment);
+ ink = await createAnnotation('ink', attachment);
+ image = await createAnnotation('image', attachment);
+ note = await createAnnotation('image', attachment);
+ });
+
+ it("should display annotations as child rows of attachments", async () => {
+ zp.itemsView.expandAllRows();
+
+ var attachmentRowIndex = zp.itemsView.getRowIndexByID(attachment.id);
+
+ let offset = 0;
+ for (let annotation of attachment.getAnnotations()) {
+ let annotationRowIndex = zp.itemsView.getRowIndexByID(annotation.id);
+ offset += 1;
+ assert.equal(annotationRowIndex, attachmentRowIndex + offset);
+ }
+ });
+
+ it("should preserve order of annotation rows after sorting", async () => {
+ let itemAboveOne = await createDataObject('item', { title: "AAA" });
+ let itemAboveTwo = await createDataObject('item', { title: "BBB" });
+ let itemBelowOne = await createDataObject('item', { title: "ZZZ" });
+
+ // Initially, everything is sorted by title
+ var colIndex = itemsView.tree._getColumns().findIndex(column => column.dataKey == 'title');
+ await zp.itemsView.tree._columns.toggleSort(colIndex);
+
+ // Expand annotations
+ var itemRowIndex = zp.itemsView.getRowIndexByID(toplevelItem.id);
+ await zp.itemsView.toggleOpenState(itemRowIndex);
+
+ var attachmentRowIndex = zp.itemsView.getRowIndexByID(attachment.id);
+ await zp.itemsView.toggleOpenState(attachmentRowIndex);
+
+ // Record sequence of items
+ let rowIDs = zp.itemsView._rows.map(row => row.id);
+
+ // Sort by title in reverse
+ await zp.itemsView.tree._columns.toggleSort(colIndex);
+
+ attachmentRowIndex = zp.itemsView.getRowIndexByID(attachment.id);
+ // Make sure annotations appear after the attachment
+ let offset = 0;
+ for (let annotation of attachment.getAnnotations()) {
+ let annotationRowIndex = zp.itemsView.getRowIndexByID(annotation.id);
+ offset += 1;
+ assert.equal(annotationRowIndex, attachmentRowIndex + offset);
+ }
+
+ // Sort back and make sure the order of rows is the same as in the beginning
+ await zp.itemsView.tree._columns.toggleSort(colIndex);
+ assert.deepEqual(rowIDs, zp.itemsView._rows.map(row => row.id));
+ });
+
+ it("should erase annotation on escape when row is selected", async () => {
+ zp.itemsView.expandAllRows();
+
+ // Select and delete ink annotation
+ let inkID = ink.id;
+ await zp.itemsView.selectItems([inkID]);
+
+ await zp.itemsView.deleteSelection();
+
+ // Make sure it is deleted and the row is gone
+ assert.isFalse(Zotero.Items.get(inkID));
+ assert.isFalse(zp.itemsView.getRowIndexByID(inkID));
+ });
+
+ it("should add note from selected annotation rows of the same parent item", async () => {
+ zp.itemsView.expandAllRows();
+
+ // make sure underline has some text, just like highlight
+ underline.annotationText = "underline";
+ await underline.saveTx();
+ await zp.itemsView.selectItems([highlight.id, underline.id]);
+
+ // Click button in the header of annotations pane
+ win.document.querySelector("annotation-items-pane .custom-head button").click();
+ await waitForItemEvent('add');
+ await waitForItemEvent('modify');
+
+ // Make sure note is created as a child of top level item
+ let note = Zotero.Items.get(toplevelItem.getNotes()[0]);
+ assert.exists(note);
+ let text = note.getNote();
+ // Only two paragraphs, one for each annotation, should be added
+ assert.equal(text.split("").length - 1, 2);
+ });
+
+ it("should create note from selected annotation rows of different parent items", async () => {
+ let toplevelItemTwo = await createDataObject('item', { title: "Another entry" });
+ let attachmentTwo = await importFileAttachment('test.pdf', { title: 'PDF two', parentItemID: toplevelItemTwo.id });
+ let highlightTwo = await createAnnotation('highlight', attachmentTwo);
+
+ zp.itemsView.expandAllRows();
+
+ await zp.itemsView.selectItems([highlight.id, highlightTwo.id]);
+
+ // Click button in the header of annotations pane
+ win.document.querySelector("annotation-items-pane .custom-head button").click();
+ await waitForItemEvent('add');
+ await waitForItemEvent('modify');
+
+ let note = zp.getSelectedItems()[0];
+ assert.isTrue(note.isNote());
+ assert.isFalse(note.parentID);
+
+ let text = note.getNote();
+ // Only two paragraphs, one for each annotation, should be added
+ assert.equal(text.split("
").length - 1, 2);
+ // Headers of both top level items are present
+ assert.include(text, toplevelItem.getDisplayTitle());
+ assert.include(text, toplevelItemTwo.getDisplayTitle());
+ });
+ });
})
diff --git a/test/tests/searchTest.js b/test/tests/searchTest.js
index 1d5bbbced7..ef58603523 100644
--- a/test/tests/searchTest.js
+++ b/test/tests/searchTest.js
@@ -248,7 +248,7 @@ describe("Zotero.Search", function() {
s.libraryID = userLibraryID;
s.addCondition('tag', 'is', tag);
var matches = await s.search();
- assert.sameMembers(matches, [attachment.id]);
+ assert.sameMembers(matches, [annotation.id]);
});
// TEMP
@@ -421,7 +421,7 @@ describe("Zotero.Search", function() {
s.addCondition('annotationText', 'contains', str);
var matches = await s.search();
// TEMP: Match parent attachment
- assert.sameMembers(matches, [attachment.id]);
+ assert.sameMembers(matches, [annotation.id]);
});
});
@@ -437,7 +437,7 @@ describe("Zotero.Search", function() {
s.addCondition('annotationComment', 'contains', str);
var matches = await s.search();
// TEMP: Match parent attachment
- assert.sameMembers(matches, [attachment.id]);
+ assert.sameMembers(matches, [annotation.id]);
});
});
@@ -659,7 +659,7 @@ describe("Zotero.Search", function() {
s.addCondition('quicksearch-fields', 'contains', tag);
var matches = await s.search();
// TEMP: Match parent attachment
- assert.sameMembers(matches, [attachment.id]);
+ assert.sameMembers(matches, [annotation.id]);
});
})
@@ -674,7 +674,7 @@ describe("Zotero.Search", function() {
s.addCondition('quicksearch-everything', 'contains', comment);
var matches = await s.search();
// TEMP: Match parent attachment
- assert.sameMembers(matches, [attachment.id]);
+ assert.sameMembers(matches, [annotation.id]);
});
it("should not include items outside of scope during phrase search", async function () {