From 650591df4e4081a0ca88ceb70b068a5fd4177dc0 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 29 Aug 2017 15:48:28 -0700 Subject: [PATCH 01/18] New: Temporary checks to hide ui base on disabled type --- src/lib/annotations/BoxAnnotations.js | 2 +- .../annotations/doc/CreateHighlightDialog.js | 124 ++++++++++++------ src/lib/annotations/doc/DocAnnotator.js | 105 +++++++++++---- src/lib/annotations/doc/DocHighlightDialog.js | 26 ++++ src/lib/annotations/doc/DocHighlightThread.js | 26 +++- 5 files changed, 210 insertions(+), 73 deletions(-) diff --git a/src/lib/annotations/BoxAnnotations.js b/src/lib/annotations/BoxAnnotations.js index dc3b547b3..59f708e1c 100644 --- a/src/lib/annotations/BoxAnnotations.js +++ b/src/lib/annotations/BoxAnnotations.js @@ -7,7 +7,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/doc/CreateHighlightDialog.js b/src/lib/annotations/doc/CreateHighlightDialog.js index ea84fd90c..acd980cab 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -9,24 +9,6 @@ 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(); /** * Events emitted by this component. @@ -71,6 +53,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 +76,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 +157,10 @@ class CreateHighlightDialog extends EventEmitter { hideElement(this.containerEl); - this.commentBox.hide(); - this.commentBox.clear(); + if (this.commentBox) { + this.commentBox.hide(); + this.commentBox.clear(); + } } /** @@ -315,6 +312,39 @@ class CreateHighlightDialog extends EventEmitter { * @return {HTMLElement} The element containing Highlight creation UI */ createElement() { + const caretTemplate = this.isMobile + ? '' + : `
`; + + const highlightTemplate = this.allowHighlight + ? ` + ` + : ''; + + const commentTemplate = this.allowComment + ? ` + ` + : ''; + + const CREATE_HIGHLIGHT_DIALOG_TEMPLATE = ` + ${caretTemplate} +
+
+ + ${highlightTemplate} + ${commentTemplate} + +
+
`.trim(); + const highlightDialogEl = document.createElement('div'); highlightDialogEl.classList.add(CLASS_CREATE_DIALOG); highlightDialogEl.innerHTML = CREATE_HIGHLIGHT_DIALOG_TEMPLATE; @@ -323,42 +353,52 @@ class CreateHighlightDialog extends EventEmitter { 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 6cd3702f3..d0004cce7 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -47,7 +47,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); } else { thread.hideDialog(); } @@ -87,6 +87,12 @@ 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; + /** * Creates and mananges plain highlight and comment highlight and point annotations * on document files. @@ -99,19 +105,35 @@ 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.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,12 +143,19 @@ class DocAnnotator extends Annotator { */ destroy() { super.destroy(); + if (this.createHighlightDialog) { + if (this.commentHighlightEnabled) { + this.createHighlightDialog.removeListener(CreateEvents.comment, this.highlightCurrentSelection); + this.createHighlightDialog.removeListener(CreateEvents.commentPost, this.createHighlightThread); + } - 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; + if (this.plainHighlightEnabled) { + this.createHighlightDialog.removeListener(CreateEvents.plain, this.createPlainHighlight); + } + + this.createHighlightDialog.destroy(); + this.createHighlightDialog = null; + } } /** @inheritdoc */ @@ -346,17 +375,21 @@ 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; @@ -371,7 +404,7 @@ class DocAnnotator extends Annotator { } thread.state = STATES.hover; - thread.show(); + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); thread.dialog.postAnnotation(commentText); this.bindCustomListenersOnThread(thread); @@ -380,7 +413,7 @@ class DocAnnotator extends Annotator { } /** - * Renders annotations from memory for a specified page. + * Override to factor in highlight types being filtered out, if disabled * * @override * @param {number} pageNum - Page number @@ -390,7 +423,17 @@ class DocAnnotator extends Annotator { // Scale existing canvases on re-render this.scaleAnnotationCanvases(pageNum); - super.renderAnnotationsOnPage(pageNum); + if (this.threads && this.threads[pageNum]) { + this.threads[pageNum].forEach((thread) => { + if ( + (!this.plainHighlightEnabled && thread.type === TYPES.highlight) || + (!this.commentHighlightEnabled && thread.type === TYPES.highlight_comment) + ) { + return; + } + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); + }); + } // Destroy current pending highlight annotation this.getHighlightThreadsOnPage(pageNum).forEach((thread) => { @@ -432,6 +475,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( @@ -454,11 +501,12 @@ class DocAnnotator extends Annotator { this.annotatedElement.addEventListener('mouseup', this.highlightMouseupHandler); - if (!this.canAnnotate) { + // Prevent all forms of highlight annotations if annotating (or plain AND comment highlights) is disabled + if (!this.canAnnotate || (!this.plainHighlightEnabled && !this.commentHighlightEnabled)) { return; } - if (this.hasTouch && this.isMobile) { + if (this.hasTouch && this.isMobile && this.createHighlightDialog) { document.addEventListener('selectionchange', this.onSelectionChange); } else { this.annotatedElement.addEventListener('dblclick', this.highlightMouseupHandler); @@ -489,7 +537,7 @@ class DocAnnotator extends Annotator { return; } - if (this.hasTouch && this.isMobile) { + if (this.hasTouch && this.isMobile && this.createHighlightDialog) { document.removeEventListener('selectionchange', this.onSelectionChange); } else { this.annotatedElement.removeEventListener('dblclick', this.highlightMouseupHandler); @@ -746,7 +794,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(showFirstDialogFilter.bind(this)); } /** @@ -787,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); @@ -890,7 +941,7 @@ class DocAnnotator extends Annotator { // Show active thread last if (activeThread) { - activeThread.show(); + activeThread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); } } /** @@ -966,7 +1017,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 70058230b..4db5afae4 100644 --- a/src/lib/annotations/doc/DocHighlightDialog.js +++ b/src/lib/annotations/doc/DocHighlightDialog.js @@ -100,6 +100,32 @@ class DocHighlightDialog extends AnnotationDialog { this.toggleHighlight(); } + /** + * + * @param {boolean} [showPlain] - Whether or not show plain highlight UI + * @param {boolean} [showComment] - Whether or not show comment highlight UI + */ + 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 c95579f32..1d990590c 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. * From 2b38cc9eae505f0a5535be6f7d90b24ab48cf150 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 29 Aug 2017 15:49:52 -0700 Subject: [PATCH 02/18] Chore: Fixed warning --- src/lib/annotations/doc/DocHighlightDialog.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/annotations/doc/DocHighlightDialog.js b/src/lib/annotations/doc/DocHighlightDialog.js index 4db5afae4..85d403900 100644 --- a/src/lib/annotations/doc/DocHighlightDialog.js +++ b/src/lib/annotations/doc/DocHighlightDialog.js @@ -100,10 +100,11 @@ 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}`); From 15032818c0867a75390147367cd55065cefeb86c Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Wed, 30 Aug 2017 10:23:44 -0700 Subject: [PATCH 03/18] Chore: feedback on PR --- src/lib/annotations/Annotator.js | 4 ++ .../annotations/doc/CreateHighlightDialog.js | 44 +++++++++---------- src/lib/annotations/doc/DocAnnotator.js | 10 ++--- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/lib/annotations/Annotator.js b/src/lib/annotations/Annotator.js index 582bfe187..ce0d7d8fe 100644 --- a/src/lib/annotations/Annotator.js +++ b/src/lib/annotations/Annotator.js @@ -279,6 +279,10 @@ class Annotator extends EventEmitter { renderAnnotationsOnPage(pageNum) { if (this.threads && this.threads[pageNum]) { this.threads[pageNum].forEach((thread) => { + if (!this.isModeAnnotatable(thread.type)) { + return; + } + thread.show(); }); } diff --git a/src/lib/annotations/doc/CreateHighlightDialog.js b/src/lib/annotations/doc/CreateHighlightDialog.js index acd980cab..b533551e8 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -10,6 +10,20 @@ 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 CARAT_TEMPLATE = `
`; +const HIGHLIGHT_BUTTON_TEMPLATE = ` + `.trim(); +const COMMENT_BUTTON_TEMPLATE = ` + `.trim(); + /** * Events emitted by this component. */ @@ -312,29 +326,11 @@ class CreateHighlightDialog extends EventEmitter { * @return {HTMLElement} The element containing Highlight creation UI */ createElement() { - const caretTemplate = this.isMobile - ? '' - : `
`; - - const highlightTemplate = this.allowHighlight - ? ` - ` - : ''; - - const commentTemplate = this.allowComment - ? ` - ` - : ''; - - const CREATE_HIGHLIGHT_DIALOG_TEMPLATE = ` + const caretTemplate = this.isMobile ? '' : CARAT_TEMPLATE; + const highlightTemplate = this.allowHighlight ? HIGHLIGHT_BUTTON_TEMPLATE : ''; + const commentTemplate = this.allowComment ? COMMENT_BUTTON_TEMPLATE : ''; + + const createHighlightDialogTemplate = ` ${caretTemplate}
@@ -347,7 +343,7 @@ class CreateHighlightDialog extends EventEmitter { 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) { diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index d0004cce7..15f5d9bae 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -126,6 +126,7 @@ class DocAnnotator extends Annotator { 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); } @@ -413,7 +414,7 @@ class DocAnnotator extends Annotator { } /** - * Override to factor in highlight types being filtered out, if disabled + * Override to factor in highlight types being filtered out, if disabled. Also scales annotation canvases. * * @override * @param {number} pageNum - Page number @@ -423,14 +424,13 @@ class DocAnnotator extends Annotator { // Scale existing canvases on re-render this.scaleAnnotationCanvases(pageNum); + // TODO (@jholdstock|@spramod) remove this if statement, and make super call, upon refactor. if (this.threads && this.threads[pageNum]) { this.threads[pageNum].forEach((thread) => { - if ( - (!this.plainHighlightEnabled && thread.type === TYPES.highlight) || - (!this.commentHighlightEnabled && thread.type === TYPES.highlight_comment) - ) { + if (!this.isModeAnnotatable(thread.type)) { return; } + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); }); } From d3749038da47d8ac1344b2763c57d65187a3d227 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Wed, 30 Aug 2017 10:57:41 -0700 Subject: [PATCH 04/18] Chore: Styling for mobile UI on disabled types --- src/lib/annotations/MobileAnnotator.scss | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/annotations/MobileAnnotator.scss b/src/lib/annotations/MobileAnnotator.scss index f8410fa1a..b6987f2f2 100644 --- a/src/lib/annotations/MobileAnnotator.scss +++ b/src/lib/annotations/MobileAnnotator.scss @@ -217,18 +217,19 @@ $tablet: "(min-width: 768px)"; .bp-annotation-highlight-btns { padding: 0; + display: flex; + justify-content: flex-start; button { - float: left; padding: 15px 0; - width: 50%; + flex-grow: 1; - &:first-child::after { + &:nth-child(2)::before { border-right: 1px solid $seesee; bottom: 17px; content: ""; height: 25px; - left: 50%; + right: 50%; position: absolute; } } From 88c92fb90f29a9efa891b6d0e9b57c350bacaa99 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Wed, 30 Aug 2017 10:59:56 -0700 Subject: [PATCH 05/18] Chore: linter warnings --- src/lib/annotations/MobileAnnotator.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/annotations/MobileAnnotator.scss b/src/lib/annotations/MobileAnnotator.scss index b6987f2f2..aea37c45c 100644 --- a/src/lib/annotations/MobileAnnotator.scss +++ b/src/lib/annotations/MobileAnnotator.scss @@ -216,21 +216,21 @@ $tablet: "(min-width: 768px)"; z-index: 9999; .bp-annotation-highlight-btns { - padding: 0; display: flex; justify-content: flex-start; + padding: 0; button { - padding: 15px 0; flex-grow: 1; + padding: 15px 0; &:nth-child(2)::before { border-right: 1px solid $seesee; bottom: 17px; content: ""; height: 25px; - right: 50%; position: absolute; + right: 50%; } } } From 4b8e2d2fd3b6207befb52377043793f129f09d89 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Thu, 31 Aug 2017 16:47:59 -0700 Subject: [PATCH 06/18] Chore: Tests for highlight enabled/disabled guards --- .../annotations/__tests__/Annotator-test.js | 15 ++++ .../annotations/doc/CreateHighlightDialog.js | 14 +-- src/lib/annotations/doc/DocAnnotator.js | 1 + .../__tests__/CreateHighlightDialog-test.js | 63 ++++++++++++-- .../doc/__tests__/DocAnnotator-test.js | 86 ++++++++++++++++++- .../doc/__tests__/DocHighlightDialog-test.js | 30 ++++++- 6 files changed, 193 insertions(+), 16 deletions(-) diff --git a/src/lib/annotations/__tests__/Annotator-test.js b/src/lib/annotations/__tests__/Annotator-test.js index 68f3b9348..a9af56858 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 b533551e8..c54219cdb 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -90,8 +90,8 @@ 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; + this.allowHighlight = config.allowHighlight !== undefined ? !!config.allowHighlight : true; + this.allowComment = config.allowComment !== undefined ? !!config.allowComment : true; // Explicit scope binding for event listeners if (this.allowHighlight) { @@ -197,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); @@ -212,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; + } } //-------------------------------------------------------------------------- diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index 15f5d9bae..1ae4900d2 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -380,6 +380,7 @@ class DocAnnotator extends Annotator { if (this.createHighlightDialog) { this.createHighlightDialog.hide(); } + this.isCreatingHighlight = false; const highlightType = commentText ? TYPES.highlight_comment : TYPES.highlight; 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 69e85fcdf..af72de439 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 { @@ -37,7 +38,12 @@ describe('lib/annotations/doc/DocAnnotator', () => { fileVersionId: 1, isMobile: false, options: {}, - modeButtons: {} + modeButtons: {}, + options: { + annotator: { + TYPE: ['highlight', 'highlight-comment'] + } + } }); annotator.annotatedElement = annotator.getAnnotatedEl(document); annotator.annotationService = {}; @@ -54,6 +60,52 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs = {}; }); + describe('constructor()', () => { + it('should not bind any plain highlight functions if they are disabled', () => { + const docAnno = new DocAnnotator({ + 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({ + 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; @@ -383,7 +435,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', () => { @@ -476,12 +528,30 @@ describe('lib/annotations/doc/DocAnnotator', () => { const threads = [pendingThread, inactiveThread]; sandbox.stub(annotator, 'getHighlightThreadsOnPage').returns(threads); - Object.defineProperty(Annotator.prototype, 'renderAnnotationsOnPage', { value: sandbox.mock() }); sandbox.stub(annotator, 'scaleAnnotationCanvases'); 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); + sandbox.stub(annotator, 'scaleAnnotationCanvases'); + 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()', () => { @@ -519,6 +589,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()', () => { diff --git a/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js b/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js index 28daa6504..1aee0641e 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()', () => { From 52b6d45991f700b43d7c5524ec963f55ded84d97 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Fri, 1 Sep 2017 15:06:28 -0700 Subject: [PATCH 07/18] Chore: Change binding of filter --- src/lib/annotations/doc/DocAnnotator.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index 72ae9ac1f..515e84f1c 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -47,7 +47,7 @@ const ANNOTATION_LAYER_CLASSES = [CLASS_ANNOTATION_LAYER_HIGHLIGHT, CLASS_ANNOTA */ function showFirstDialogFilter(thread, index) { if (index === 0) { - thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); // TODO(@jholdstock): remove flags on refactor. } else { thread.hideDialog(); } @@ -93,6 +93,9 @@ class DocAnnotator extends Annotator { /** @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. @@ -116,6 +119,7 @@ class DocAnnotator extends Annotator { // Explicit scoping 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, @@ -800,7 +804,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.bind(this)); + delayThreads.forEach(this.showFirstDialogFilter); } /** From f529a37ae4af2ebaf6b2e3288fb0ffd738be946b Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 29 Aug 2017 15:48:28 -0700 Subject: [PATCH 08/18] New: Temporary checks to hide ui base on disabled type --- src/lib/annotations/BoxAnnotations.js | 2 +- .../annotations/doc/CreateHighlightDialog.js | 124 ++++++++++++------ src/lib/annotations/doc/DocAnnotator.js | 111 ++++++++++++---- src/lib/annotations/doc/DocHighlightDialog.js | 26 ++++ src/lib/annotations/doc/DocHighlightThread.js | 26 +++- 5 files changed, 217 insertions(+), 72 deletions(-) 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/doc/CreateHighlightDialog.js b/src/lib/annotations/doc/CreateHighlightDialog.js index ea84fd90c..acd980cab 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -9,24 +9,6 @@ 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(); /** * Events emitted by this component. @@ -71,6 +53,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 +76,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 +157,10 @@ class CreateHighlightDialog extends EventEmitter { hideElement(this.containerEl); - this.commentBox.hide(); - this.commentBox.clear(); + if (this.commentBox) { + this.commentBox.hide(); + this.commentBox.clear(); + } } /** @@ -315,6 +312,39 @@ class CreateHighlightDialog extends EventEmitter { * @return {HTMLElement} The element containing Highlight creation UI */ createElement() { + const caretTemplate = this.isMobile + ? '' + : `
`; + + const highlightTemplate = this.allowHighlight + ? ` + ` + : ''; + + const commentTemplate = this.allowComment + ? ` + ` + : ''; + + const CREATE_HIGHLIGHT_DIALOG_TEMPLATE = ` + ${caretTemplate} +
+
+ + ${highlightTemplate} + ${commentTemplate} + +
+
`.trim(); + const highlightDialogEl = document.createElement('div'); highlightDialogEl.classList.add(CLASS_CREATE_DIALOG); highlightDialogEl.innerHTML = CREATE_HIGHLIGHT_DIALOG_TEMPLATE; @@ -323,42 +353,52 @@ class CreateHighlightDialog extends EventEmitter { 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 6f7435627..e69a7c051 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -47,7 +47,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); } else { thread.hideDialog(); } @@ -87,6 +87,12 @@ 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; + /** * Creates and mananges plain highlight and comment highlight and point annotations * on document files. @@ -99,20 +105,36 @@ 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.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); + } } /** @@ -122,12 +144,19 @@ class DocAnnotator extends Annotator { */ destroy() { super.destroy(); + if (this.createHighlightDialog) { + 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; + this.createHighlightDialog.destroy(); + this.createHighlightDialog = null; + } } /** @inheritdoc */ @@ -348,17 +377,21 @@ 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; @@ -373,7 +406,7 @@ class DocAnnotator extends Annotator { } thread.state = STATES.hover; - thread.show(); + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); thread.dialog.postAnnotation(commentText); this.bindCustomListenersOnThread(thread); @@ -382,7 +415,7 @@ class DocAnnotator extends Annotator { } /** - * Renders annotations from memory for a specified page. + * Override to factor in highlight types being filtered out, if disabled * * @override * @param {number} pageNum - Page number @@ -392,7 +425,17 @@ class DocAnnotator extends Annotator { // Scale existing canvases on re-render this.scaleAnnotationCanvases(pageNum); - super.renderAnnotationsOnPage(pageNum); + if (this.threads && this.threads[pageNum]) { + this.threads[pageNum].forEach((thread) => { + if ( + (!this.plainHighlightEnabled && thread.type === TYPES.highlight) || + (!this.commentHighlightEnabled && thread.type === TYPES.highlight_comment) + ) { + return; + } + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); + }); + } // Destroy current pending highlight annotation this.getHighlightThreadsOnPage(pageNum).forEach((thread) => { @@ -434,6 +477,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,7 +503,12 @@ class DocAnnotator extends Annotator { this.annotatedElement.addEventListener('mouseup', this.highlightMouseupHandler); - if (this.hasTouch && this.isMobile) { + // Prevent all forms of highlight annotations if annotating (or plain AND comment highlights) is disabled + if (!this.canAnnotate || (!this.plainHighlightEnabled && !this.commentHighlightEnabled)) { + return; + } + + if (this.hasTouch && this.isMobile && this.createHighlightDialog) { document.addEventListener('selectionchange', this.onSelectionChange); this.annotatedElement.addEventListener('touchstart', this.drawingSelectionHandler); } else { @@ -485,7 +537,11 @@ class DocAnnotator extends Annotator { this.highlightThrottleHandle = null; } - if (this.hasTouch && this.isMobile) { + if (!this.canAnnotate) { + return; + } + + if (this.hasTouch && this.isMobile && this.createHighlightDialog) { document.removeEventListener('selectionchange', this.onSelectionChange); this.annotatedElement.removeEventListener('touchstart', this.drawingSelectionHandler); } else { @@ -744,7 +800,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(showFirstDialogFilter.bind(this)); } /** @@ -798,14 +854,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); @@ -901,7 +960,7 @@ class DocAnnotator extends Annotator { // Show active thread last if (activeThread) { - activeThread.show(); + activeThread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); } } /** @@ -977,7 +1036,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..714b53f0f 100644 --- a/src/lib/annotations/doc/DocHighlightDialog.js +++ b/src/lib/annotations/doc/DocHighlightDialog.js @@ -100,6 +100,32 @@ class DocHighlightDialog extends AnnotationDialog { this.toggleHighlight(); } + /** + * + * @param {boolean} [showPlain] - Whether or not show plain highlight UI + * @param {boolean} [showComment] - Whether or not show comment highlight UI + */ + 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. * From e6da0859ab771bf34ca1019b42effa8878aeb590 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 29 Aug 2017 15:49:52 -0700 Subject: [PATCH 09/18] Chore: Fixed warning --- src/lib/annotations/doc/DocHighlightDialog.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/annotations/doc/DocHighlightDialog.js b/src/lib/annotations/doc/DocHighlightDialog.js index 714b53f0f..0c7f5fec9 100644 --- a/src/lib/annotations/doc/DocHighlightDialog.js +++ b/src/lib/annotations/doc/DocHighlightDialog.js @@ -100,10 +100,11 @@ 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}`); From 2aa6104ad16f503bf6f76de8f58e9e4e58b0085d Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Wed, 30 Aug 2017 10:23:44 -0700 Subject: [PATCH 10/18] Chore: feedback on PR --- src/lib/annotations/Annotator.js | 4 ++ .../annotations/doc/CreateHighlightDialog.js | 44 +++++++++---------- src/lib/annotations/doc/DocAnnotator.js | 10 ++--- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/lib/annotations/Annotator.js b/src/lib/annotations/Annotator.js index 70ed8f37c..8375c6ae5 100644 --- a/src/lib/annotations/Annotator.js +++ b/src/lib/annotations/Annotator.js @@ -295,6 +295,10 @@ class Annotator extends EventEmitter { renderAnnotationsOnPage(pageNum) { if (this.threads && this.threads[pageNum]) { this.threads[pageNum].forEach((thread) => { + if (!this.isModeAnnotatable(thread.type)) { + return; + } + thread.show(); }); } diff --git a/src/lib/annotations/doc/CreateHighlightDialog.js b/src/lib/annotations/doc/CreateHighlightDialog.js index acd980cab..b533551e8 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -10,6 +10,20 @@ 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 CARAT_TEMPLATE = `
`; +const HIGHLIGHT_BUTTON_TEMPLATE = ` + `.trim(); +const COMMENT_BUTTON_TEMPLATE = ` + `.trim(); + /** * Events emitted by this component. */ @@ -312,29 +326,11 @@ class CreateHighlightDialog extends EventEmitter { * @return {HTMLElement} The element containing Highlight creation UI */ createElement() { - const caretTemplate = this.isMobile - ? '' - : `
`; - - const highlightTemplate = this.allowHighlight - ? ` - ` - : ''; - - const commentTemplate = this.allowComment - ? ` - ` - : ''; - - const CREATE_HIGHLIGHT_DIALOG_TEMPLATE = ` + const caretTemplate = this.isMobile ? '' : CARAT_TEMPLATE; + const highlightTemplate = this.allowHighlight ? HIGHLIGHT_BUTTON_TEMPLATE : ''; + const commentTemplate = this.allowComment ? COMMENT_BUTTON_TEMPLATE : ''; + + const createHighlightDialogTemplate = ` ${caretTemplate}
@@ -347,7 +343,7 @@ class CreateHighlightDialog extends EventEmitter { 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) { diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index e69a7c051..5acd65f53 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -127,6 +127,7 @@ class DocAnnotator extends Annotator { 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); } @@ -415,7 +416,7 @@ class DocAnnotator extends Annotator { } /** - * Override to factor in highlight types being filtered out, if disabled + * Override to factor in highlight types being filtered out, if disabled. Also scales annotation canvases. * * @override * @param {number} pageNum - Page number @@ -425,14 +426,13 @@ class DocAnnotator extends Annotator { // Scale existing canvases on re-render this.scaleAnnotationCanvases(pageNum); + // TODO (@jholdstock|@spramod) remove this if statement, and make super call, upon refactor. if (this.threads && this.threads[pageNum]) { this.threads[pageNum].forEach((thread) => { - if ( - (!this.plainHighlightEnabled && thread.type === TYPES.highlight) || - (!this.commentHighlightEnabled && thread.type === TYPES.highlight_comment) - ) { + if (!this.isModeAnnotatable(thread.type)) { return; } + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); }); } From 9d5e0f870c46dbd8f089406ae5b9be3b5cee52bd Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Wed, 30 Aug 2017 10:57:41 -0700 Subject: [PATCH 11/18] Chore: Styling for mobile UI on disabled types --- src/lib/annotations/MobileAnnotator.scss | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/annotations/MobileAnnotator.scss b/src/lib/annotations/MobileAnnotator.scss index f8410fa1a..b6987f2f2 100644 --- a/src/lib/annotations/MobileAnnotator.scss +++ b/src/lib/annotations/MobileAnnotator.scss @@ -217,18 +217,19 @@ $tablet: "(min-width: 768px)"; .bp-annotation-highlight-btns { padding: 0; + display: flex; + justify-content: flex-start; button { - float: left; padding: 15px 0; - width: 50%; + flex-grow: 1; - &:first-child::after { + &:nth-child(2)::before { border-right: 1px solid $seesee; bottom: 17px; content: ""; height: 25px; - left: 50%; + right: 50%; position: absolute; } } From a2c63a0502ed62e93add8388d980bf15e5e9c46e Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Wed, 30 Aug 2017 10:59:56 -0700 Subject: [PATCH 12/18] Chore: linter warnings --- src/lib/annotations/MobileAnnotator.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/annotations/MobileAnnotator.scss b/src/lib/annotations/MobileAnnotator.scss index b6987f2f2..aea37c45c 100644 --- a/src/lib/annotations/MobileAnnotator.scss +++ b/src/lib/annotations/MobileAnnotator.scss @@ -216,21 +216,21 @@ $tablet: "(min-width: 768px)"; z-index: 9999; .bp-annotation-highlight-btns { - padding: 0; display: flex; justify-content: flex-start; + padding: 0; button { - padding: 15px 0; flex-grow: 1; + padding: 15px 0; &:nth-child(2)::before { border-right: 1px solid $seesee; bottom: 17px; content: ""; height: 25px; - right: 50%; position: absolute; + right: 50%; } } } From cf9c347bc65d295453ae112efa457d03d23e54da Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Thu, 31 Aug 2017 16:47:59 -0700 Subject: [PATCH 13/18] Chore: Tests for highlight enabled/disabled guards --- .../annotations/__tests__/Annotator-test.js | 15 ++++ .../annotations/doc/CreateHighlightDialog.js | 14 +-- src/lib/annotations/doc/DocAnnotator.js | 1 + .../__tests__/CreateHighlightDialog-test.js | 63 ++++++++++++-- .../doc/__tests__/DocAnnotator-test.js | 86 ++++++++++++++++++- .../doc/__tests__/DocHighlightDialog-test.js | 30 ++++++- 6 files changed, 193 insertions(+), 16 deletions(-) diff --git a/src/lib/annotations/__tests__/Annotator-test.js b/src/lib/annotations/__tests__/Annotator-test.js index 62c383639..8b3301976 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 b533551e8..c54219cdb 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -90,8 +90,8 @@ 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; + this.allowHighlight = config.allowHighlight !== undefined ? !!config.allowHighlight : true; + this.allowComment = config.allowComment !== undefined ? !!config.allowComment : true; // Explicit scope binding for event listeners if (this.allowHighlight) { @@ -197,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); @@ -212,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; + } } //-------------------------------------------------------------------------- diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index 5acd65f53..c2d7b9da7 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -382,6 +382,7 @@ class DocAnnotator extends Annotator { if (this.createHighlightDialog) { this.createHighlightDialog.hide(); } + this.isCreatingHighlight = false; const highlightType = commentText ? TYPES.highlight_comment : TYPES.highlight; 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 8a9243048..ee6d52ec5 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 { @@ -37,7 +38,12 @@ describe('lib/annotations/doc/DocAnnotator', () => { fileVersionId: 1, isMobile: false, options: {}, - modeButtons: {} + modeButtons: {}, + options: { + annotator: { + TYPE: ['highlight', 'highlight-comment'] + } + } }); annotator.annotatedElement = annotator.getAnnotatedEl(document); annotator.annotationService = {}; @@ -54,6 +60,52 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs = {}; }); + describe('constructor()', () => { + it('should not bind any plain highlight functions if they are disabled', () => { + const docAnno = new DocAnnotator({ + 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({ + 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; @@ -383,7 +435,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', () => { @@ -476,12 +528,30 @@ describe('lib/annotations/doc/DocAnnotator', () => { const threads = [pendingThread, inactiveThread]; sandbox.stub(annotator, 'getHighlightThreadsOnPage').returns(threads); - Object.defineProperty(Annotator.prototype, 'renderAnnotationsOnPage', { value: sandbox.mock() }); sandbox.stub(annotator, 'scaleAnnotationCanvases'); 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); + sandbox.stub(annotator, 'scaleAnnotationCanvases'); + 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()', () => { @@ -519,6 +589,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()', () => { 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()', () => { From 840246dd0864e1276ccd886c24914bec9f84000f Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Fri, 1 Sep 2017 15:06:28 -0700 Subject: [PATCH 14/18] Chore: Change binding of filter --- src/lib/annotations/doc/DocAnnotator.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index c2d7b9da7..a32637fa6 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -47,7 +47,7 @@ const ANNOTATION_LAYER_CLASSES = [CLASS_ANNOTATION_LAYER_HIGHLIGHT, CLASS_ANNOTA */ function showFirstDialogFilter(thread, index) { if (index === 0) { - thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); // TODO(@jholdstock): remove flags on refactor. } else { thread.hideDialog(); } @@ -93,6 +93,9 @@ class DocAnnotator extends Annotator { /** @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. @@ -116,6 +119,7 @@ class DocAnnotator extends Annotator { // Explicit scoping 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, @@ -801,7 +805,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.bind(this)); + delayThreads.forEach(this.showFirstDialogFilter); } /** From 7bc4a32c8b2ba38cd28929c454665e000fdaaec5 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 5 Sep 2017 12:45:21 -0700 Subject: [PATCH 15/18] Chore: fixed tests --- src/lib/annotations/doc/DocAnnotator.js | 8 ++++---- .../doc/__tests__/DocAnnotator-test.js | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index a32637fa6..ec5a870e5 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -509,11 +509,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.canAnnotate || (!this.plainHighlightEnabled && !this.commentHighlightEnabled)) { + if (!this.permissions.canAnnotate || (!this.plainHighlightEnabled && !this.commentHighlightEnabled)) { return; } - if (this.hasTouch && this.isMobile && this.createHighlightDialog) { + if (this.hasTouch && this.isMobile) { document.addEventListener('selectionchange', this.onSelectionChange); this.annotatedElement.addEventListener('touchstart', this.drawingSelectionHandler); } else { @@ -542,11 +542,11 @@ class DocAnnotator extends Annotator { this.highlightThrottleHandle = null; } - if (!this.canAnnotate) { + if (!this.permissions.canAnnotate) { return; } - if (this.hasTouch && this.isMobile && this.createHighlightDialog) { + if (this.hasTouch && this.isMobile) { document.removeEventListener('selectionchange', this.onSelectionChange); this.annotatedElement.removeEventListener('touchstart', this.drawingSelectionHandler); } else { diff --git a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js index ee6d52ec5..fcc64b0e4 100644 --- a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js +++ b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js @@ -32,7 +32,9 @@ 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, @@ -63,7 +65,9 @@ describe('lib/annotations/doc/DocAnnotator', () => { describe('constructor()', () => { it('should not bind any plain highlight functions if they are disabled', () => { const docAnno = new DocAnnotator({ - canAnnotate: true, + permissions: { + canAnnotate: true + }, container: document, annotationService: {}, fileVersionId: 1, @@ -85,7 +89,9 @@ describe('lib/annotations/doc/DocAnnotator', () => { it('should not bind any comment highlight functions if they are disabled', () => { const docAnno = new DocAnnotator({ - canAnnotate: true, + permissions: { + canAnnotate: true + }, container: document, annotationService: {}, fileVersionId: 1, @@ -611,7 +617,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { }); it('should bind DOM listeners if user can annotate', () => { - annotator.canAnnotate = true; + annotator.permissions.canAnnotate = true; stubs.elMock.expects('addEventListener').withArgs('mouseup', sinon.match.func); stubs.elMock.expects('addEventListener').withArgs('dblclick', sinon.match.func); @@ -626,6 +632,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'); @@ -646,7 +653,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { }); it('should unbind DOM listeners if user can annotate', () => { - annotator.canAnnotate = true; + annotator.permissions.canAnnotate = true; stubs.elMock.expects('removeEventListener').withArgs('mouseup', sinon.match.func); stubs.elMock.expects('removeEventListener').withArgs('mousedown', sinon.match.func); @@ -659,7 +666,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { it('should stop and destroy the requestAnimationFrame handle created by getHighlightMousemoveHandler()', () => { const rafHandle = 12; // RAF handles are integers - annotator.canAnnotate = true; + annotator.permissions.canAnnotate = true; annotator.highlightThrottleHandle = rafHandle; sandbox.stub(annotator, 'getHighlightMouseMoveHandler').returns(sandbox.stub()); From ba00e8054c36f06c57c0524adf0cb7379cfbddb2 Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 5 Sep 2017 12:58:09 -0700 Subject: [PATCH 16/18] Chore: forgot to save before commit --- src/lib/annotations/doc/__tests__/DocAnnotator-test.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js index d0937d053..8f1d5d812 100644 --- a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js +++ b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js @@ -543,10 +543,6 @@ describe('lib/annotations/doc/DocAnnotator', () => { const threads = [pendingThread, inactiveThread]; sandbox.stub(annotator, 'getHighlightThreadsOnPage').returns(threads); -<<<<<<< HEAD - sandbox.stub(annotator, 'scaleAnnotationCanvases'); -======= ->>>>>>> 34207995cd1fb439a301a41e5fc94e85dffa2f6c annotator.renderAnnotationsOnPage(1); expect(annotator.scaleAnnotationCanvases).to.be.called; From daca4a05eaa2608ceb492c30847a600c53f5692e Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 5 Sep 2017 13:22:11 -0700 Subject: [PATCH 17/18] Chore: Feedback --- .../annotations/doc/CreateHighlightDialog.js | 2 ++ src/lib/annotations/doc/DocAnnotator.js | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/lib/annotations/doc/CreateHighlightDialog.js b/src/lib/annotations/doc/CreateHighlightDialog.js index c54219cdb..e9fced6ed 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -380,9 +380,11 @@ class CreateHighlightDialog extends EventEmitter { 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(); diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index 8de7240df..1fdf256cd 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -148,19 +148,21 @@ class DocAnnotator extends Annotator { */ destroy() { super.destroy(); - if (this.createHighlightDialog) { - if (this.commentHighlightEnabled) { - this.createHighlightDialog.removeListener(CreateEvents.comment, this.highlightCurrentSelection); - this.createHighlightDialog.removeListener(CreateEvents.commentPost, this.createHighlightThread); - } + if (!this.createHighlightDialog) { + return; + } - if (this.plainHighlightEnabled) { - this.createHighlightDialog.removeListener(CreateEvents.plain, this.createPlainHighlight); - } + if (this.commentHighlightEnabled) { + this.createHighlightDialog.removeListener(CreateEvents.comment, this.highlightCurrentSelection); + this.createHighlightDialog.removeListener(CreateEvents.commentPost, this.createHighlightThread); + } - this.createHighlightDialog.destroy(); - this.createHighlightDialog = null; + if (this.plainHighlightEnabled) { + this.createHighlightDialog.removeListener(CreateEvents.plain, this.createPlainHighlight); } + + this.createHighlightDialog.destroy(); + this.createHighlightDialog = null; } /** @inheritdoc */ From 99bd67910dc6ae8e42c3dff60f842e104586e12c Mon Sep 17 00:00:00 2001 From: Justin Holdstock Date: Tue, 5 Sep 2017 13:44:05 -0700 Subject: [PATCH 18/18] Chore: missing test fix --- src/lib/annotations/doc/DocAnnotator.js | 19 +++++++++++-------- .../doc/__tests__/DocAnnotator-test.js | 7 ------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index 1fdf256cd..4c43314d5 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -432,16 +432,19 @@ class DocAnnotator extends Annotator { // Scale existing canvases on re-render this.scaleAnnotationCanvases(pageNum); + if (!this.threads) { + return; + } + // TODO (@jholdstock|@spramod) remove this if statement, and make super call, upon refactor. - if (this.threads && this.threads[pageNum]) { - this.threads[pageNum].forEach((thread) => { - if (!this.isModeAnnotatable(thread.type)) { - return; - } + const pageThreads = this.getThreadsOnPage(pageNum); + Object.values(pageThreads).forEach((thread) => { + if (!this.isModeAnnotatable(thread.type)) { + return; + } - thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); - }); - } + thread.show(this.plainHighlightEnabled, this.commentHighlightEnabled); + }); // Destroy current pending highlight annotation const highlightThreads = this.getHighlightThreadsOnPage(pageNum); diff --git a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js index 8f1d5d812..470f038c3 100644 --- a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js +++ b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js @@ -518,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); @@ -555,7 +549,6 @@ describe('lib/annotations/doc/DocAnnotator', () => { const threads = [plain, comment, point]; annotator.threads = { 1: threads }; sandbox.stub(annotator, 'getHighlightThreadsOnPage').returns(threads); - sandbox.stub(annotator, 'scaleAnnotationCanvases'); annotator.options.annotator.TYPE = ['highlight', 'point']; annotator.renderAnnotationsOnPage(1);