diff --git a/src/lib/annotations/Annotator.js b/src/lib/annotations/Annotator.js
index 74b2593d8..d8899e19c 100644
--- a/src/lib/annotations/Annotator.js
+++ b/src/lib/annotations/Annotator.js
@@ -298,6 +298,10 @@ class Annotator extends EventEmitter {
const pageThreads = this.getThreadsOnPage(pageNum);
Object.values(pageThreads).forEach((thread) => {
+ if (!this.isModeAnnotatable(thread.type)) {
+ return;
+ }
+
thread.show();
});
}
diff --git a/src/lib/annotations/BoxAnnotations.js b/src/lib/annotations/BoxAnnotations.js
index 7f14f504d..0c2fe9098 100644
--- a/src/lib/annotations/BoxAnnotations.js
+++ b/src/lib/annotations/BoxAnnotations.js
@@ -8,7 +8,7 @@ const ANNOTATORS = [
NAME: 'Document',
CONSTRUCTOR: DocAnnotator,
VIEWER: ['Document', 'Presentation'],
- TYPE: [TYPES.point, TYPES.highlight]
+ TYPE: [TYPES.point, TYPES.highlight, TYPES.highlight_comment]
},
{
NAME: 'Image',
diff --git a/src/lib/annotations/MobileAnnotator.scss b/src/lib/annotations/MobileAnnotator.scss
index f8410fa1a..aea37c45c 100644
--- a/src/lib/annotations/MobileAnnotator.scss
+++ b/src/lib/annotations/MobileAnnotator.scss
@@ -216,20 +216,21 @@ $tablet: "(min-width: 768px)";
z-index: 9999;
.bp-annotation-highlight-btns {
+ display: flex;
+ justify-content: flex-start;
padding: 0;
button {
- float: left;
+ flex-grow: 1;
padding: 15px 0;
- width: 50%;
- &:first-child::after {
+ &:nth-child(2)::before {
border-right: 1px solid $seesee;
bottom: 17px;
content: "";
height: 25px;
- left: 50%;
position: absolute;
+ right: 50%;
}
}
}
diff --git a/src/lib/annotations/__tests__/Annotator-test.js b/src/lib/annotations/__tests__/Annotator-test.js
index 27cee03e2..9fe1ebef1 100644
--- a/src/lib/annotations/__tests__/Annotator-test.js
+++ b/src/lib/annotations/__tests__/Annotator-test.js
@@ -238,6 +238,21 @@ describe('lib/annotations/Annotator', () => {
stubs.threadMock3.expects('show').never();
annotator.renderAnnotationsOnPage(1);
});
+
+ it('should not call show() if the thread type is disabled', () => {
+ const badType = 'not_accepted';
+ stubs.thread3.type = badType;
+ stubs.thread2.type = 'type';
+
+ stubs.threadMock3.expects('show').never();
+ stubs.threadMock2.expects('show').once();
+
+ const isModeAnn = sandbox.stub(annotator, 'isModeAnnotatable');
+ isModeAnn.withArgs(badType).returns(false);
+ isModeAnn.withArgs('type').returns(true);
+
+ annotator.renderAnnotationsOnPage('2');
+ });
});
describe('rotateAnnotations()', () => {
diff --git a/src/lib/annotations/doc/CreateHighlightDialog.js b/src/lib/annotations/doc/CreateHighlightDialog.js
index ea84fd90c..e9fced6ed 100644
--- a/src/lib/annotations/doc/CreateHighlightDialog.js
+++ b/src/lib/annotations/doc/CreateHighlightDialog.js
@@ -9,24 +9,20 @@ const TITLE_HIGHLIGHT_TOGGLE = __('annotation_highlight_toggle');
const TITLE_HIGHLIGHT_COMMENT = __('annotation_highlight_comment');
const DATA_TYPE_HIGHLIGHT = 'add-highlight-btn';
const DATA_TYPE_ADD_HIGHLIGHT_COMMENT = 'add-highlight-comment-btn';
-const CREATE_HIGHLIGHT_DIALOG_TEMPLATE = `
-
-
-
-
-
-
-
-
-
`.trim();
+
+const CARAT_TEMPLATE = ``;
+const HIGHLIGHT_BUTTON_TEMPLATE = `
+ `.trim();
+const COMMENT_BUTTON_TEMPLATE = `
+ `.trim();
/**
* Events emitted by this component.
@@ -71,6 +67,12 @@ class CreateHighlightDialog extends EventEmitter {
/** @property {boolean} - Whether or not this is visible. */
isVisible;
+ /** @property {boolean} - Whether or not to allow plain highlight interaction. */
+ allowHighlight;
+
+ /** @property {boolean} - Whether or not to allow comment interactions. */
+ allowComment;
+
/**
* A dialog used to create plain and comment highlights.
*
@@ -88,12 +90,19 @@ class CreateHighlightDialog extends EventEmitter {
this.parentEl = parentEl;
this.isMobile = config.isMobile || false;
this.hasTouch = config.hasTouch || false;
+ this.allowHighlight = config.allowHighlight !== undefined ? !!config.allowHighlight : true;
+ this.allowComment = config.allowComment !== undefined ? !!config.allowComment : true;
// Explicit scope binding for event listeners
- this.onHighlightClick = this.onHighlightClick.bind(this);
- this.onCommentClick = this.onCommentClick.bind(this);
- this.onCommentPost = this.onCommentPost.bind(this);
- this.onCommentCancel = this.onCommentCancel.bind(this);
+ if (this.allowHighlight) {
+ this.onHighlightClick = this.onHighlightClick.bind(this);
+ }
+
+ if (this.allowComment) {
+ this.onCommentClick = this.onCommentClick.bind(this);
+ this.onCommentPost = this.onCommentPost.bind(this);
+ this.onCommentCancel = this.onCommentCancel.bind(this);
+ }
}
/**
@@ -162,8 +171,10 @@ class CreateHighlightDialog extends EventEmitter {
hideElement(this.containerEl);
- this.commentBox.hide();
- this.commentBox.clear();
+ if (this.commentBox) {
+ this.commentBox.hide();
+ this.commentBox.clear();
+ }
}
/**
@@ -186,8 +197,6 @@ class CreateHighlightDialog extends EventEmitter {
// Event listeners
this.highlightCreateEl.removeEventListener('click', this.onHighlightClick);
this.commentCreateEl.removeEventListener('click', this.onCommentClick);
- this.commentBox.removeListener(CommentBox.CommentEvents.post, this.onCommentPost);
- this.commentBox.removeListener(CommentBox.CommentEvents.cancel, this.onCommentCancel);
if (this.hasTouch) {
this.highlightCreateEl.removeEventListener('touchstart', this.stopPropagation);
@@ -201,8 +210,12 @@ class CreateHighlightDialog extends EventEmitter {
this.containerEl = null;
this.parentEl = null;
- this.commentBox.destroy();
- this.commentBox = null;
+ if (this.commentBox) {
+ this.commentBox.removeListener(CommentBox.CommentEvents.post, this.onCommentPost);
+ this.commentBox.removeListener(CommentBox.CommentEvents.cancel, this.onCommentCancel);
+ this.commentBox.destroy();
+ this.commentBox = null;
+ }
}
//--------------------------------------------------------------------------
@@ -315,50 +328,77 @@ class CreateHighlightDialog extends EventEmitter {
* @return {HTMLElement} The element containing Highlight creation UI
*/
createElement() {
+ const caretTemplate = this.isMobile ? '' : CARAT_TEMPLATE;
+ const highlightTemplate = this.allowHighlight ? HIGHLIGHT_BUTTON_TEMPLATE : '';
+ const commentTemplate = this.allowComment ? COMMENT_BUTTON_TEMPLATE : '';
+
+ const createHighlightDialogTemplate = `
+ ${caretTemplate}
+
+
+
+ ${highlightTemplate}
+ ${commentTemplate}
+
+
+
`.trim();
+
const highlightDialogEl = document.createElement('div');
highlightDialogEl.classList.add(CLASS_CREATE_DIALOG);
- highlightDialogEl.innerHTML = CREATE_HIGHLIGHT_DIALOG_TEMPLATE;
+ highlightDialogEl.innerHTML = createHighlightDialogTemplate;
// Get rid of the caret
if (this.isMobile) {
highlightDialogEl.classList.add('bp-mobile-annotation-dialog');
highlightDialogEl.classList.add('bp-annotation-dialog');
- highlightDialogEl.querySelector('.bp-annotation-caret').remove();
}
const containerEl = highlightDialogEl.querySelector(constants.SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG);
// Reference HTML
- this.highlightCreateEl = containerEl.querySelector(constants.SELECTOR_ADD_HIGHLIGHT_BTN);
- this.commentCreateEl = containerEl.querySelector(`.${constants.CLASS_ADD_HIGHLIGHT_COMMENT_BTN}`);
this.buttonsEl = containerEl.querySelector(constants.SELECTOR_HIGHLIGHT_BTNS);
- // Create comment box
- this.commentBox = new CommentBox(containerEl);
-
// Stop interacting with this element from triggering outside actions
highlightDialogEl.addEventListener('click', this.stopPropagation);
highlightDialogEl.addEventListener('mouseup', this.stopPropagation);
highlightDialogEl.addEventListener('dblclick', this.stopPropagation);
- // Event listeners
- this.highlightCreateEl.addEventListener('click', this.onHighlightClick);
- this.commentCreateEl.addEventListener('click', this.onCommentClick);
- this.commentBox.addListener(CommentBox.CommentEvents.post, this.onCommentPost);
- this.commentBox.addListener(CommentBox.CommentEvents.cancel, this.onCommentCancel);
+ // Events for highlight button
+ if (this.allowHighlight) {
+ this.highlightCreateEl = containerEl.querySelector(constants.SELECTOR_ADD_HIGHLIGHT_BTN);
+ this.highlightCreateEl.addEventListener('click', this.onHighlightClick);
+
+ if (this.hasTouch) {
+ this.highlightCreateEl.addEventListener('touchstart', this.stopPropagation);
+ this.highlightCreateEl.addEventListener('touchend', this.onHighlightClick);
+ }
+ }
+
+ // Events for comment button
+ if (this.allowComment) {
+ // Create comment box
+ this.commentBox = new CommentBox(containerEl);
+ this.commentCreateEl = containerEl.querySelector(`.${constants.CLASS_ADD_HIGHLIGHT_COMMENT_BTN}`);
+ this.commentCreateEl.addEventListener('click', this.onCommentClick);
+
+ // Event listeners
+ this.commentBox.addListener(CommentBox.CommentEvents.post, this.onCommentPost);
+ this.commentBox.addListener(CommentBox.CommentEvents.cancel, this.onCommentCancel);
+
+ // Hide comment box, by default
+ this.commentBox.hide();
+
+ if (this.hasTouch) {
+ this.commentCreateEl.addEventListener('touchstart', this.stopPropagation);
+ this.commentCreateEl.addEventListener('touchend', this.onCommentClick);
+ }
+ }
// touch events
if (this.hasTouch) {
- this.highlightCreateEl.addEventListener('touchstart', this.stopPropagation);
- this.commentCreateEl.addEventListener('touchstart', this.stopPropagation);
- this.highlightCreateEl.addEventListener('touchend', this.onHighlightClick);
- this.commentCreateEl.addEventListener('touchend', this.onCommentClick);
highlightDialogEl.addEventListener('touchend', this.stopPropagation);
}
- // Hide comment box, by default
- this.commentBox.hide();
-
return highlightDialogEl;
}
}
diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js
index d74e27471..8a5c6c2db 100644
--- a/src/lib/annotations/doc/DocAnnotator.js
+++ b/src/lib/annotations/doc/DocAnnotator.js
@@ -46,7 +46,7 @@ const ANNOTATION_LAYER_CLASSES = [CLASS_ANNOTATION_LAYER_HIGHLIGHT, CLASS_ANNOTA
*/
function showFirstDialogFilter(thread, index) {
if (index === 0) {
- thread.show();
+ thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); // TODO(@jholdstock): remove flags on refactor.
} else {
thread.hideDialog();
}
@@ -86,6 +86,15 @@ class DocAnnotator extends Annotator {
/** @property {Selection} - For tracking diffs in text selection, for mobile highlights creation. */
lastSelection;
+ /** @property {boolean} - True if regular highlights are allowed to be read/written */
+ plainHighlightEnabled;
+
+ /** @property {boolean} - True if comment highlights are allowed to be read/written */
+ commentHighlightEnabled;
+
+ /** @property {Function} - Reference to filter function that has been bound TODO(@jholdstock): remove on refactor. */
+ showFirstDialogFilter;
+
/**
* Creates and mananges plain highlight and comment highlight and point annotations
* on document files.
@@ -98,20 +107,38 @@ class DocAnnotator extends Annotator {
constructor(data) {
super(data);
+ this.plainHighlightEnabled = this.isModeAnnotatable(TYPES.highlight);
+ this.commentHighlightEnabled = this.isModeAnnotatable(TYPES.highlight_comment);
+
+ // Don't bind to highlight specific handlers if we cannot highlight
+ if (!this.plainHighlightEnabled && !this.commentHighlightEnabled) {
+ return;
+ }
+
// Explicit scoping
- this.highlightCurrentSelection = this.highlightCurrentSelection.bind(this);
- this.createHighlightThread = this.createHighlightThread.bind(this);
- this.createPlainHighlight = this.createPlainHighlight.bind(this);
this.highlightCreateHandler = this.highlightCreateHandler.bind(this);
this.drawingSelectionHandler = this.drawingSelectionHandler.bind(this);
+ this.showFirstDialogFilter = showFirstDialogFilter.bind(this);
this.createHighlightDialog = new CreateHighlightDialog(this.container, {
isMobile: this.isMobile,
- hasTouch: this.hasTouch
+ hasTouch: this.hasTouch,
+ allowComment: this.commentHighlightEnabled,
+ allowHighlight: this.plainHighlightEnabled
});
- this.createHighlightDialog.addListener(CreateEvents.plain, this.createPlainHighlight);
- this.createHighlightDialog.addListener(CreateEvents.comment, this.highlightCurrentSelection);
- this.createHighlightDialog.addListener(CreateEvents.commentPost, this.createHighlightThread);
+
+ if (this.commentHighlightEnabled) {
+ this.highlightCurrentSelection = this.highlightCurrentSelection.bind(this);
+ this.createHighlightDialog.addListener(CreateEvents.comment, this.highlightCurrentSelection);
+
+ this.createHighlightThread = this.createHighlightThread.bind(this);
+ this.createHighlightDialog.addListener(CreateEvents.commentPost, this.createHighlightThread);
+ }
+
+ if (this.plainHighlightEnabled) {
+ this.createPlainHighlight = this.createPlainHighlight.bind(this);
+ this.createHighlightDialog.addListener(CreateEvents.plain, this.createPlainHighlight);
+ }
}
/**
@@ -121,10 +148,19 @@ class DocAnnotator extends Annotator {
*/
destroy() {
super.destroy();
+ if (!this.createHighlightDialog) {
+ return;
+ }
+
+ if (this.commentHighlightEnabled) {
+ this.createHighlightDialog.removeListener(CreateEvents.comment, this.highlightCurrentSelection);
+ this.createHighlightDialog.removeListener(CreateEvents.commentPost, this.createHighlightThread);
+ }
+
+ if (this.plainHighlightEnabled) {
+ this.createHighlightDialog.removeListener(CreateEvents.plain, this.createPlainHighlight);
+ }
- this.createHighlightDialog.removeListener(CreateEvents.plain, this.createPlainHighlight);
- this.createHighlightDialog.removeListener(CreateEvents.comment, this.highlightCurrentSelection);
- this.createHighlightDialog.removeListener(CreateEvents.commentPost, this.createHighlightThread);
this.createHighlightDialog.destroy();
this.createHighlightDialog = null;
}
@@ -347,17 +383,22 @@ class DocAnnotator extends Annotator {
if (commentText === '' || !this.lastHighlightEvent) {
return null;
}
- this.createHighlightDialog.hide();
+
+ if (this.createHighlightDialog) {
+ this.createHighlightDialog.hide();
+ }
+
this.isCreatingHighlight = false;
- const location = this.getLocationFromEvent(this.lastHighlightEvent, TYPES.highlight);
+ const highlightType = commentText ? TYPES.highlight_comment : TYPES.highlight;
+ const location = this.getLocationFromEvent(this.lastHighlightEvent, highlightType);
this.highlighter.removeAllHighlights();
if (!location) {
return null;
}
const annotations = [];
- const thread = this.createAnnotationThread(annotations, location, TYPES.highlight);
+ const thread = this.createAnnotationThread(annotations, location, highlightType);
this.lastHighlightEvent = null;
this.lastSelection = null;
@@ -372,7 +413,7 @@ class DocAnnotator extends Annotator {
}
thread.state = STATES.hover;
- thread.show();
+ thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled);
thread.dialog.postAnnotation(commentText);
this.bindCustomListenersOnThread(thread);
@@ -381,7 +422,7 @@ class DocAnnotator extends Annotator {
}
/**
- * Renders annotations from memory for a specified page.
+ * Override to factor in highlight types being filtered out, if disabled. Also scales annotation canvases.
*
* @override
* @param {number} pageNum - Page number
@@ -391,7 +432,19 @@ class DocAnnotator extends Annotator {
// Scale existing canvases on re-render
this.scaleAnnotationCanvases(pageNum);
- super.renderAnnotationsOnPage(pageNum);
+ if (!this.threads) {
+ return;
+ }
+
+ // TODO (@jholdstock|@spramod) remove this if statement, and make super call, upon refactor.
+ const pageThreads = this.getThreadsOnPage(pageNum);
+ Object.values(pageThreads).forEach((thread) => {
+ if (!this.isModeAnnotatable(thread.type)) {
+ return;
+ }
+
+ thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled);
+ });
// Destroy current pending highlight annotation
const highlightThreads = this.getHighlightThreadsOnPage(pageNum);
@@ -434,6 +487,10 @@ class DocAnnotator extends Annotator {
setupAnnotations() {
super.setupAnnotations();
+ if (!this.plainHighlightEnabled && !this.commentHighlightEnabled) {
+ return;
+ }
+
// Init rangy and rangy highlight
this.highlighter = rangy.createHighlighter();
this.highlighter.addClassApplier(
@@ -456,6 +513,11 @@ class DocAnnotator extends Annotator {
this.annotatedElement.addEventListener('mouseup', this.highlightMouseupHandler);
+ // Prevent all forms of highlight annotations if annotating (or plain AND comment highlights) is disabled
+ if (!this.permissions.canAnnotate || (!this.plainHighlightEnabled && !this.commentHighlightEnabled)) {
+ return;
+ }
+
if (this.hasTouch && this.isMobile) {
document.addEventListener('selectionchange', this.onSelectionChange);
this.annotatedElement.addEventListener('touchstart', this.drawingSelectionHandler);
@@ -485,6 +547,10 @@ class DocAnnotator extends Annotator {
this.highlightThrottleHandle = null;
}
+ if (!this.permissions.canAnnotate) {
+ return;
+ }
+
Object.values(this.modeControllers).forEach((controller) => controller.removeSelection());
if (this.hasTouch && this.isMobile) {
@@ -715,7 +781,7 @@ class DocAnnotator extends Annotator {
// hovered over at the same time, only the top-most highlight
// dialog will be displayed and the others will be hidden
// without delay
- delayThreads.forEach(showFirstDialogFilter);
+ delayThreads.forEach(this.showFirstDialogFilter);
}
/**
@@ -769,14 +835,17 @@ class DocAnnotator extends Annotator {
this.highlighter.removeAllHighlights();
}
- this.createHighlightDialog.hide();
+ if (this.createHighlightDialog) {
+ this.createHighlightDialog.hide();
+ }
+
this.isCreatingHighlight = false;
// Creating highlights is disabled on mobile for now since the
// event we would listen to, selectionchange, fires continuously and
// is unreliable. If the mouse moved or we double clicked text,
// we trigger the create handler instead of the click handler
- if (this.didMouseMove || event.type === 'dblclick') {
+ if (this.createHighlightDialog && (this.didMouseMove || event.type === 'dblclick')) {
this.highlightCreateHandler(event);
} else {
this.highlightClickHandler(event);
@@ -871,7 +940,7 @@ class DocAnnotator extends Annotator {
// Show active thread last
if (activeThread) {
- activeThread.show();
+ activeThread.show(this.plainHighlightEnabled, this.commentHighlightEnabled);
}
}
@@ -933,7 +1002,7 @@ class DocAnnotator extends Annotator {
}
this.getHighlightThreadsOnPage(page).forEach((thread) => {
- thread.show();
+ thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled);
});
}
diff --git a/src/lib/annotations/doc/DocHighlightDialog.js b/src/lib/annotations/doc/DocHighlightDialog.js
index dc393ed9d..0c7f5fec9 100644
--- a/src/lib/annotations/doc/DocHighlightDialog.js
+++ b/src/lib/annotations/doc/DocHighlightDialog.js
@@ -100,6 +100,33 @@ class DocHighlightDialog extends AnnotationDialog {
this.toggleHighlight();
}
+ /** TEMPORARY override to hide or show UI based on enabled annotation types.
+ *
+ * @param {boolean} [showPlain] - Whether or not show plain highlight UI
+ * @param {boolean} [showComment] - Whether or not show comment highlight UI
+ * @return {void}
+ */
+ show(showPlain = true, showComment = true) {
+ const plainButtonEl = this.highlightDialogEl.querySelector(`button.${constants.CLASS_ADD_HIGHLIGHT_BTN}`);
+ const commentButtonEl = this.highlightDialogEl.querySelector(
+ `button.${constants.CLASS_ADD_HIGHLIGHT_COMMENT_BTN}`
+ );
+
+ if (showPlain) {
+ annotatorUtil.showElement(plainButtonEl);
+ } else {
+ annotatorUtil.hideElement(plainButtonEl);
+ }
+
+ if (showComment) {
+ annotatorUtil.showElement(commentButtonEl);
+ } else {
+ annotatorUtil.hideElement(commentButtonEl);
+ }
+
+ super.show();
+ }
+
//--------------------------------------------------------------------------
// Abstract Implementations
//--------------------------------------------------------------------------
diff --git a/src/lib/annotations/doc/DocHighlightThread.js b/src/lib/annotations/doc/DocHighlightThread.js
index 386b9fabc..0e20d81e5 100644
--- a/src/lib/annotations/doc/DocHighlightThread.js
+++ b/src/lib/annotations/doc/DocHighlightThread.js
@@ -239,12 +239,14 @@ class DocHighlightThread extends AnnotationThread {
* the highlight in active state and show the 'delete' button.
*
* @override
+ * @param {boolean} [showPlain] - Whether or not plain highlight ui is shown (TEMPORARY UNTIL REFACTOR)
+ * @param {boolean} [showComment] - Whether or not comment highlight ui is shown (TEMPORARY UNTIL REFACTOR)
* @return {void}
*/
- show() {
+ show(showPlain, showComment) {
switch (this.state) {
case STATES.pending:
- this.showDialog();
+ this.showDialog(showPlain, showComment);
break;
case STATES.inactive:
this.hideDialog();
@@ -252,7 +254,7 @@ class DocHighlightThread extends AnnotationThread {
break;
case STATES.hover:
case STATES.pending_active:
- this.showDialog();
+ this.showDialog(showPlain, showComment);
this.draw(HIGHLIGHT_FILL.active);
break;
default:
@@ -260,6 +262,24 @@ class DocHighlightThread extends AnnotationThread {
}
}
+ /** Overridden to hide UI elements depending on whether or not comments or plain
+ * are allowed. Note: This will be deprecated upon proper refactor or comment highlight
+ * and plain highlights.
+ *
+ * @override
+ * @param {boolean} [showPlain] - Whether or not plain highlight ui is shown
+ * @param {boolean} [showComment] - Whether or not comment highlight ui is shown
+ * @return {void}
+ */
+ showDialog(showPlain, showComment) {
+ // Prevents the annotations dialog from being created each mousemove
+ if (!this.dialog.element) {
+ this.dialog.setup(this.annotations);
+ }
+
+ this.dialog.show(showPlain, showComment);
+ }
+
/**
* Creates the document highlight annotation dialog for the thread.
*
diff --git a/src/lib/annotations/doc/__tests__/CreateHighlightDialog-test.js b/src/lib/annotations/doc/__tests__/CreateHighlightDialog-test.js
index 86667db8f..ddcc093dc 100644
--- a/src/lib/annotations/doc/__tests__/CreateHighlightDialog-test.js
+++ b/src/lib/annotations/doc/__tests__/CreateHighlightDialog-test.js
@@ -1,6 +1,11 @@
/* eslint-disable no-unused-expressions */
import CreateHighlightDialog, { CreateEvents } from '../CreateHighlightDialog';
-import { CLASS_HIDDEN } from '../../annotationConstants';
+import {
+ CLASS_ADD_HIGHLIGHT_BTN,
+ CLASS_ADD_HIGHLIGHT_COMMENT_BTN,
+ CLASS_ANNOTATION_CARET,
+ CLASS_HIDDEN
+} from '../../annotationConstants';
import CommentBox from '../../CommentBox';
import * as annotatorUtil from '../../annotatorUtil';
@@ -22,6 +27,24 @@ describe('lib/annotations/doc/CreateHighlightDialog', () => {
parentEl = null;
});
+ describe('contructor()', () => {
+ it('should default to enable highlights and comments if no config passed in', () => {
+ const instance = new CreateHighlightDialog(document.createElement('div'));
+ expect(instance.allowHighlight).to.be.true;
+ expect(instance.allowComment).to.be.true;
+ });
+
+ it('should take config falsey value to disable highlights and comments, when passed in', () => {
+ const config = {
+ allowHighlight: 'this will falsey to true',
+ allowComment: false
+ };
+ const instance = new CreateHighlightDialog(document.createElement('div'), config);
+ expect(instance.allowHighlight).to.be.true;
+ expect(instance.allowComment).to.be.false;
+ });
+ });
+
describe('setParentEl()', () => {
it('should assign the new parent reference', () => {
const newParent = document.createElement('span');
@@ -111,6 +134,13 @@ describe('lib/annotations/doc/CreateHighlightDialog', () => {
dialog.hide();
expect(clearComment).to.be.called;
});
+
+ it('should do nothing with the comment box if it does not exist', () => {
+ const clearComment = sandbox.stub(dialog.commentBox, 'clear');
+ dialog.commentBox = null;
+ dialog.hide();
+ expect(clearComment).to.not.be.called;
+ });
});
describe('destroy()', () => {
@@ -348,27 +378,48 @@ describe('lib/annotations/doc/CreateHighlightDialog', () => {
});
describe('createElement()', () => {
- let containerEl;
-
- beforeEach(() => {
- containerEl = dialog.createElement();
- });
it('should create a div with the proper create highlight class', () => {
+ let containerEl = dialog.createElement();
expect(containerEl.nodeName).to.equal('DIV');
expect(containerEl.classList.contains(CLASS_CREATE_DIALOG)).to.be.true;
});
it('should make a reference to the highlight button', () => {
+ let containerEl = dialog.createElement();
expect(dialog.highlightCreateEl).to.exist;
});
it('should make a reference to the comment button', () => {
+ let containerEl = dialog.createElement();
expect(dialog.commentCreateEl).to.exist;
});
it('should create a comment box', () => {
+ let containerEl = dialog.createElement();
expect(dialog.commentBox).to.be.an.instanceof(CommentBox);
});
+
+ it('should not create the caret if on a mobile device', () => {
+ dialog.isMobile = true;
+ let containerEl = dialog.createElement();
+
+ expect(containerEl.querySelector(`.${CLASS_ANNOTATION_CARET}`)).to.not.exist;
+ });
+
+ it('should not create a highlight button if highlights are disabled', () => {
+ dialog.allowHighlight = false;
+ let containerEl = dialog.createElement();
+
+ expect(containerEl.querySelector(`.${CLASS_ADD_HIGHLIGHT_BTN}`)).to.not.exist;
+ });
+
+ it('should not create a comment box or button if comments are disabled', () => {
+ dialog.allowComment = false;
+ let containerEl = dialog.createElement();
+
+ expect(containerEl.querySelector(`.${CLASS_ADD_HIGHLIGHT_COMMENT_BTN}`)).to.not.exist;
+ expect(dialog.commentBox).to.not.exist;
+ });
});
});
diff --git a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js
index ef2470d47..ffb851a50 100644
--- a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js
+++ b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js
@@ -7,6 +7,7 @@ import DocAnnotator from '../DocAnnotator';
import DocHighlightThread from '../DocHighlightThread';
import DocDrawingThread from '../DocDrawingThread';
import DocPointThread from '../DocPointThread';
+import { CreateEvents } from '../CreateHighlightDialog';
import * as annotatorUtil from '../../annotatorUtil';
import * as docAnnotatorUtil from '../docAnnotatorUtil';
import {
@@ -31,13 +32,20 @@ describe('lib/annotations/doc/DocAnnotator', () => {
fixture.load('annotations/doc/__tests__/DocAnnotator-test.html');
annotator = new DocAnnotator({
- canAnnotate: true,
+ permissions: {
+ canAnnotate: true
+ },
container: document,
annotationService: {},
fileVersionId: 1,
isMobile: false,
options: {},
- modeButtons: {}
+ modeButtons: {},
+ options: {
+ annotator: {
+ TYPE: ['highlight', 'highlight-comment']
+ }
+ }
});
annotator.annotatedElement = annotator.getAnnotatedEl(document);
annotator.annotationService = {};
@@ -55,6 +63,56 @@ describe('lib/annotations/doc/DocAnnotator', () => {
stubs = {};
});
+ describe('constructor()', () => {
+ it('should not bind any plain highlight functions if they are disabled', () => {
+ const docAnno = new DocAnnotator({
+ permissions: {
+ canAnnotate: true
+ },
+ container: document,
+ annotationService: {},
+ fileVersionId: 1,
+ isMobile: false,
+ options: {},
+ modeButtons: {},
+ options: {
+ annotator: {
+ TYPE: ['highlight-comment']
+ }
+ }
+ });
+
+ const createPlain = sandbox.stub(docAnno, 'createPlainHighlight');
+ docAnno.createHighlightDialog.emit(CreateEvents.plain);
+
+ expect(createPlain).to.not.be.called;
+ });
+
+ it('should not bind any comment highlight functions if they are disabled', () => {
+ const docAnno = new DocAnnotator({
+ permissions: {
+ canAnnotate: true
+ },
+ container: document,
+ annotationService: {},
+ fileVersionId: 1,
+ isMobile: false,
+ options: {},
+ modeButtons: {},
+ options: {
+ annotator: {
+ TYPE: ['highlight']
+ }
+ }
+ });
+
+ const createComment = sandbox.stub(docAnno, 'createHighlightThread');
+ docAnno.createHighlightDialog.emit(CreateEvents.commentPost);
+
+ expect(createComment).to.not.be.called;
+ });
+ });
+
describe('getAnnotatedEl()', () => {
it('should return the annotated element as the document', () => {
expect(annotator.annotatedElement).to.not.be.null;
@@ -384,7 +442,7 @@ describe('lib/annotations/doc/DocAnnotator', () => {
stubs.createAnnotationThread.returns(thread);
annotator.createHighlightThread('some text with severe passive agression');
- expect(stubs.createAnnotationThread).to.be.calledWith([], location, TYPES.highlight);
+ expect(stubs.createAnnotationThread).to.be.calledWith([], location, TYPES.highlight_comment);
});
it('should bail out of making an annotation if thread is null', () => {
@@ -460,17 +518,11 @@ describe('lib/annotations/doc/DocAnnotator', () => {
});
describe('renderAnnotationsOnPage()', () => {
- const renderFunc = Annotator.prototype.renderAnnotationsOnPage;
beforeEach(() => {
- Object.defineProperty(Annotator.prototype, 'renderAnnotationsOnPage', { value: sandbox.mock() });
sandbox.stub(annotator, 'scaleAnnotationCanvases');
});
- afterEach(() => {
- Object.defineProperty(Annotator.prototype, 'renderAnnotationsOnPage', { value: renderFunc });
- });
-
it('should destroy any pending highlight annotations on the page', () => {
const pendingThread = { state: 'pending', destroy: () => {} };
stubs.pendingMock = sandbox.mock(pendingThread);
@@ -489,6 +541,24 @@ describe('lib/annotations/doc/DocAnnotator', () => {
annotator.renderAnnotationsOnPage(1);
expect(annotator.scaleAnnotationCanvases).to.be.called;
});
+
+ it('should call show on ONLY enabled annotation types', () => {
+ const plain = { state: 'I do not care', type: 'highlight', show: sandbox.stub() };
+ const comment = { state: 'I do not care', type: 'highlight-comment', show: sandbox.stub() };
+ const point = { state: 'I do not care', type: 'point', show: sandbox.stub() };
+ const threads = [plain, comment, point];
+ annotator.threads = { 1: threads };
+ sandbox.stub(annotator, 'getHighlightThreadsOnPage').returns(threads);
+ annotator.options.annotator.TYPE = ['highlight', 'point'];
+
+ annotator.renderAnnotationsOnPage(1);
+
+ expect(plain.show).to.be.called;
+ expect(point.show).to.be.called;
+ expect(comment.show).to.not.be.called;
+
+ annotator.threads = {};
+ });
});
describe('scaleAnnotationCanvases()', () => {
@@ -526,6 +596,16 @@ describe('lib/annotations/doc/DocAnnotator', () => {
expect(rangy.createHighlighter).to.have.been.called;
expect(stubs.highlighter.addClassApplier).to.have.been.called;
});
+
+ it('should not create a highlighter if all forms of highlight are disabled', () => {
+ sandbox.stub(rangy, 'createHighlighter');
+ annotator.plainHighlightEnabled = false;
+ annotator.commentHighlightEnabled = false;
+
+ annotator.setupAnnotations();
+
+ expect(rangy.createHighlighter).to.not.be.called;
+ });
});
describe('bindDOMListeners()', () => {
@@ -553,6 +633,7 @@ describe('lib/annotations/doc/DocAnnotator', () => {
annotator.permissions.canAnnotate = true;
annotator.isMobile = true;
annotator.hasTouch = true;
+
const docListen = sandbox.spy(document, 'addEventListener');
const annotatedElementListen = sandbox.spy(annotator.annotatedElement, 'addEventListener');
diff --git a/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js b/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js
index c29e9faf7..632f526da 100644
--- a/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js
+++ b/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js
@@ -198,8 +198,36 @@ describe('lib/annotations/doc/DocHighlightDialog', () => {
expect(dialog.highlightDialogEl.classList.remove).to.be.calledWith(constants.CLASS_HIDDEN);
});
+ });
+
+ describe('show()', () => {
+ beforeEach(() => {
+ Object.defineProperty(AnnotationDialog.prototype, 'show', { value: sandbox.stub() });
+ });
+
+ it('should show the highlight button if it has been enabled', () => {
+ dialog.show(true, true);
+ const button = dialog.highlightDialogEl.querySelector(`button.${constants.CLASS_ADD_HIGHLIGHT_BTN}`);
+ expect(button.classList.contains(constants.CLASS_HIDDEN)).to.be.false;
+ });
- it('should set hasComments to false');
+ it('should hide the highlight button if it has been disabled', () => {
+ dialog.show(false, true);
+ const button = dialog.highlightDialogEl.querySelector(`button.${constants.CLASS_ADD_HIGHLIGHT_BTN}`);
+ expect(button.classList.contains(constants.CLASS_HIDDEN)).to.be.true;
+ });
+
+ it('should show the comment button if it has been enabled', () => {
+ dialog.show(true, true);
+ const button = dialog.highlightDialogEl.querySelector(`button.${constants.CLASS_ADD_HIGHLIGHT_COMMENT_BTN}`);
+ expect(button.classList.contains(constants.CLASS_HIDDEN)).to.be.false;
+ });
+
+ it('should hide the comment button if it has been disabled', () => {
+ dialog.show(true, false);
+ const button = dialog.highlightDialogEl.querySelector(`button.${constants.CLASS_ADD_HIGHLIGHT_COMMENT_BTN}`);
+ expect(button.classList.contains(constants.CLASS_HIDDEN)).to.be.true;
+ });
});
describe('position()', () => {