From 0cfcaf784ce5191a8acd6e6c530fd63b7d0e7eef Mon Sep 17 00:00:00 2001 From: Sumedha Pramod Date: Fri, 14 Jul 2017 11:22:50 -0700 Subject: [PATCH] Chore: Cleaning up annotation strings/classes (#215) --- src/lib/Preview.js | 2 +- src/lib/annotations/AnnotationDialog.js | 136 +++++++++++------- src/lib/annotations/AnnotationThread.js | 22 ++- src/lib/annotations/Annotator.js | 33 +++-- src/lib/annotations/Annotator.scss | 2 +- src/lib/annotations/BoxAnnotations.js | 5 +- src/lib/annotations/CommentBox.js | 15 +- .../__tests__/AnnotationDialog-test.js | 62 ++++---- .../__tests__/AnnotationThread-test.js | 27 ++-- .../annotations/__tests__/Annotator-test.js | 14 +- .../annotations/__tests__/CommentBox-test.js | 8 +- .../__tests__/annotatorUtil-test.js | 33 +++-- src/lib/annotations/annotationConstants.js | 73 ++++++---- src/lib/annotations/annotatorUtil.js | 8 +- .../annotations/doc/CreateHighlightDialog.js | 25 ++-- src/lib/annotations/doc/DocAnnotator.js | 48 ++++--- src/lib/annotations/doc/DocHighlightDialog.js | 64 +++++---- src/lib/annotations/doc/DocHighlightThread.js | 65 +++++---- src/lib/annotations/doc/DocPointAnnotator.js | 13 +- src/lib/annotations/doc/DocPointThread.js | 4 +- .../doc/__tests__/DocAnnotator-test.js | 88 ++++++------ .../doc/__tests__/DocHighlightDialog-test.js | 54 +++---- .../doc/__tests__/DocHighlightThread-test.js | 56 ++++---- .../doc/__tests__/DocPointThread-test.js | 6 +- .../doc/__tests__/docAnnotatorUtil-test.js | 21 +-- src/lib/annotations/doc/docAnnotatorUtil.js | 14 +- src/lib/annotations/image/ImageAnnotator.js | 5 +- src/lib/annotations/image/ImagePointDialog.js | 3 +- src/lib/annotations/image/ImagePointThread.js | 4 +- .../image/__tests__/ImageAnnotator-test.js | 5 +- .../image/__tests__/ImagePointThread-test.js | 6 +- 31 files changed, 526 insertions(+), 395 deletions(-) diff --git a/src/lib/Preview.js b/src/lib/Preview.js index d9abe6817..9d6bb0b38 100644 --- a/src/lib/Preview.js +++ b/src/lib/Preview.js @@ -824,7 +824,7 @@ const PREVIEW_LOCATION = findScriptLocation(PREVIEW_SCRIPT_NAME, document.curren * @return {void} */ finishLoading(data = {}) { - // Show or hide annotate/print/download buttons + // Show or hide print/download buttons // canDownload is not supported by all of our browsers, so for now we need to check isMobile if (checkPermission(this.file, PERMISSION_DOWNLOAD) && this.options.showDownload && Browser.canDownload()) { this.ui.showDownloadButton(this.download); diff --git a/src/lib/annotations/AnnotationDialog.js b/src/lib/annotations/AnnotationDialog.js index a095b3db0..9710b76ab 100644 --- a/src/lib/annotations/AnnotationDialog.js +++ b/src/lib/annotations/AnnotationDialog.js @@ -6,9 +6,28 @@ import { CLASS_ACTIVE, CLASS_HIDDEN } from '../constants'; import { decodeKeydown } from '../util'; import { ICON_CLOSE, ICON_DELETE } from '../icons/icons'; +const CLASS_ANNOTATION_PLAIN_HIGHLIGHT = 'bp-plain-highlight'; +const CLASS_BUTTON_DELETE_COMMENT = 'delete-comment-btn'; +const CLASS_CANCEL_DELETE = 'cancel-delete-btn'; +const CLASS_CANNOT_ANNOTATE = 'cannot-annotate'; +const CLASS_COMMENTS_CONTAINER = 'annotation-comments'; +const CLASS_REPLY_CONTAINER = 'reply-container'; +const CLASS_REPLY_TEXTAREA = 'reply-textarea'; const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; - -@autobind class AnnotationDialog extends EventEmitter { +const CLASS_DELETE_CONFIRMATION = 'delete-confirmation'; +const CLASS_BUTTON_DELETE_CONFIRM = 'confirm-delete-btn'; + +const DATA_TYPE_POST = 'post-annotation-btn'; +const DATA_TYPE_CANCEL = 'cancel-annotation-btn'; +const DATA_TYPE_REPLY_TEXTAREA = 'reply-textarea'; +const DATA_TYPE_CANCEL_REPLY = 'cancel-reply-btn'; +const DATA_TYPE_POST_REPLY = 'post-reply-btn'; +const DATA_TYPE_DELETE = 'delete-btn'; +const DATA_TYPE_CANCEL_DELETE = 'cancel-delete-btn'; +const DATA_TYPE_CONFIRM_DELETE = 'confirm-delete-btn'; + +@autobind +class AnnotationDialog extends EventEmitter { //-------------------------------------------------------------------------- // Typedef //-------------------------------------------------------------------------- @@ -74,13 +93,13 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; this.element.appendChild(this.dialogEl); if (this.highlightDialogEl && !this.hasComments) { - this.element.classList.add('bp-plain-highlight'); + this.element.classList.add(CLASS_ANNOTATION_PLAIN_HIGHLIGHT); - const headerEl = this.element.querySelector('.bp-annotation-mobile-header'); + const headerEl = this.element.querySelector(constants.SELECTOR_MOBILE_DIALOG_HEADER); headerEl.classList.add(CLASS_HIDDEN); } - const dialogCloseButtonEl = this.element.querySelector('.bp-annotation-dialog-close'); + const dialogCloseButtonEl = this.element.querySelector(constants.SELECTOR_DIALOG_CLOSE); dialogCloseButtonEl.addEventListener('click', this.hideMobileDialog); this.element.classList.add(CLASS_ANIMATE_DIALOG); @@ -89,7 +108,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; } const textAreaEl = this.hasAnnotations - ? this.element.querySelector(constants.SELECTOR_REPLY_TEXTAREA) + ? this.element.querySelector(`.${CLASS_REPLY_TEXTAREA}`) : this.element.querySelector(constants.SELECTOR_ANNOTATION_TEXTAREA); // Don't re-position if reply textarea is already active @@ -119,7 +138,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; // If user cannot annotate, hide reply/edit/delete UI if (!this.canAnnotate) { - this.element.classList.add(constants.CLASS_CANNOT_ANNOTATE); + this.element.classList.add(CLASS_CANNOT_ANNOTATE); } // Focus the textarea if visible @@ -142,12 +161,12 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; // Clear annotations from dialog this.element.innerHTML = ` -
- +
+
`.trim(); - this.element.classList.remove('bp-plain-highlight'); + this.element.classList.remove(CLASS_ANNOTATION_PLAIN_HIGHLIGHT); - const dialogCloseButtonEl = this.element.querySelector('.bp-annotation-dialog-close'); + const dialogCloseButtonEl = this.element.querySelector(constants.SELECTOR_DIALOG_CLOSE); dialogCloseButtonEl.removeEventListener('click', this.hideMobileDialog); annotatorUtil.hideElement(this.element); @@ -168,6 +187,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; if (this.isMobile) { this.hideMobileDialog(); } + annotatorUtil.hideElement(this.element); this.deactivateReply(); } @@ -181,8 +201,8 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; addAnnotation(annotation) { // Show new section if needed if (!this.hasAnnotations) { - const createSectionEl = this.element.querySelector('[data-section="create"]'); - const showSectionEl = this.element.querySelector('[data-section="show"]'); + const createSectionEl = this.element.querySelector(constants.SECTION_CREATE); + const showSectionEl = this.element.querySelector(constants.SECTION_SHOW); annotatorUtil.hideElement(createSectionEl); annotatorUtil.showElement(showSectionEl); this.hasAnnotations = true; @@ -248,13 +268,14 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; setup(annotations) { // Generate HTML of dialog this.dialogEl = this.generateDialogEl(annotations.length); - this.dialogEl.classList.add('annotation-container'); + this.dialogEl.classList.add(constants.CLASS_ANNOTATION_CONTAINER); + // Setup shared mobile annotations dialog if (!this.isMobile) { this.element = document.createElement('div'); - this.element.setAttribute('data-type', 'annotation-dialog'); + this.element.setAttribute('data-type', constants.DATA_TYPE_ANNOTATION_DIALOG); this.element.classList.add(constants.CLASS_ANNOTATION_DIALOG); - this.element.innerHTML = '
'; + this.element.innerHTML = `
`; this.element.appendChild(this.dialogEl); // Adding thread number to dialog @@ -322,7 +343,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; this.hide(); } else { const dataType = annotatorUtil.findClosestDataType(event.target); - if (dataType === 'reply-textarea') { + if (dataType === CLASS_REPLY_TEXTAREA) { this.activateReply(); } } @@ -349,7 +370,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; if (this.element.classList.contains(CLASS_HIDDEN)) { annotatorUtil.showElement(this.element); - const replyTextArea = this.element.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextArea = this.element.querySelector(`.${CLASS_REPLY_TEXTAREA}`); const commentsTextArea = this.element.querySelector(constants.SELECTOR_ANNOTATION_TEXTAREA); if (replyTextArea.textContent !== '' || commentsTextArea.textContent !== '') { this.emit('annotationcommentpending'); @@ -388,41 +409,43 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; switch (dataType) { // Clicking 'Post' button to create an annotation - case 'post-annotation-btn': + case DATA_TYPE_POST: this.postAnnotation(); break; // Clicking 'Cancel' button to cancel the annotation - case 'cancel-annotation-btn': + case DATA_TYPE_CANCEL: if (this.isMobile) { + // Hide mobile dialog without destroying the thread this.hideMobileDialog(); } else { + // Cancels + destroys the annotation thread this.cancelAnnotation(); } this.deactivateReply(true); break; // Clicking inside reply text area - case 'reply-textarea': + case DATA_TYPE_REPLY_TEXTAREA: this.activateReply(); break; // Canceling a reply - case 'cancel-reply-btn': + case DATA_TYPE_CANCEL_REPLY: this.deactivateReply(true); break; // Clicking 'Post' button to create a reply annotation - case 'post-reply-btn': + case DATA_TYPE_POST_REPLY: this.postReply(); break; // Clicking trash icon to initiate deletion - case 'delete-btn': + case DATA_TYPE_DELETE: this.showDeleteConfirmation(annotationID); break; // Clicking 'Cancel' button to cancel deletion - case 'cancel-delete-btn': + case DATA_TYPE_CANCEL_DELETE: this.hideDeleteConfirmation(annotationID); break; // Clicking 'Delete' button to confirm deletion - case 'confirm-delete-btn': { + case DATA_TYPE_CONFIRM_DELETE: { this.deleteAnnotation(annotationID); break; } @@ -475,24 +498,26 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog';
${created}
${text}
- -
+
${__('annotation_delete_confirmation_message')}
-
- -
`.trim(); - const annotationContainerEl = this.dialogEl.querySelector(constants.SELECTOR_COMMENTS_CONTAINER); + const annotationContainerEl = this.dialogEl.querySelector(`.${CLASS_COMMENTS_CONTAINER}`); annotationContainerEl.appendChild(annotationEl); } @@ -517,7 +542,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; return; } - const replyTextEl = this.dialogEl.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextEl = this.dialogEl.querySelector(`.${CLASS_REPLY_TEXTAREA}`); // Don't activate if reply textarea is already active const isActive = replyTextEl.classList.contains(CLASS_ACTIVE); @@ -530,7 +555,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; annotatorUtil.showElement(replyButtonEls); // Auto scroll annotations dialog to bottom where new comment was added - const annotationsEl = this.dialogEl.querySelector('.annotation-container'); + const annotationsEl = this.dialogEl.querySelector(constants.SELECTOR_ANNOTATION_CONTAINER); if (annotationsEl) { annotationsEl.scrollTop = annotationsEl.scrollHeight - annotationsEl.clientHeight; } @@ -548,18 +573,19 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; return; } - const replyContainerEl = this.dialogEl.querySelector(constants.SELECTOR_REPLY_CONTAINER); - const replyTextEl = replyContainerEl.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyContainerEl = this.dialogEl.querySelector(`.${CLASS_REPLY_CONTAINER}`); + const replyTextEl = replyContainerEl.querySelector(`.${CLASS_REPLY_TEXTAREA}`); const replyButtonEls = replyContainerEl.querySelector(constants.SELECTOR_BUTTON_CONTAINER); annotatorUtil.resetTextarea(replyTextEl, clearText); annotatorUtil.hideElement(replyButtonEls); + // Only focus on textarea if dialog is visible if (annotatorUtil.isElementInViewport(replyTextEl)) { replyTextEl.focus(); } // Auto scroll annotations dialog to bottom where new comment was added - const annotationsEl = this.dialogEl.querySelector('.annotation-container'); + const annotationsEl = this.dialogEl.querySelector(constants.SELECTOR_ANNOTATION_CONTAINER); if (annotationsEl) { annotationsEl.scrollTop = annotationsEl.scrollHeight - annotationsEl.clientHeight; } @@ -572,7 +598,7 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; * @return {void} */ postReply() { - const replyTextEl = this.element.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextEl = this.element.querySelector(`.${CLASS_REPLY_TEXTAREA}`); const text = replyTextEl.value; if (text.trim() === '') { return; @@ -591,9 +617,9 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; */ showDeleteConfirmation(annotationID) { const annotationEl = this.element.querySelector(`[data-annotation-id="${annotationID}"]`); - const deleteConfirmationEl = annotationEl.querySelector('.delete-confirmation'); - const cancelDeleteButtonEl = annotationEl.querySelector('.cancel-delete-btn'); - const deleteButtonEl = annotationEl.querySelector('.delete-comment-btn'); + const deleteConfirmationEl = annotationEl.querySelector(`.${CLASS_DELETE_CONFIRMATION}`); + const cancelDeleteButtonEl = annotationEl.querySelector(`.${CLASS_CANCEL_DELETE}`); + const deleteButtonEl = annotationEl.querySelector(`.${CLASS_BUTTON_DELETE_COMMENT}`); annotatorUtil.hideElement(deleteButtonEl); annotatorUtil.showElement(deleteConfirmationEl); cancelDeleteButtonEl.focus(); @@ -608,8 +634,8 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; */ hideDeleteConfirmation(annotationID) { const annotationEl = this.element.querySelector(`[data-annotation-id="${annotationID}"]`); - const deleteConfirmationEl = annotationEl.querySelector('.delete-confirmation'); - const deleteButtonEl = annotationEl.querySelector('.delete-comment-btn'); + const deleteConfirmationEl = annotationEl.querySelector(`.${CLASS_DELETE_CONFIRMATION}`); + const deleteButtonEl = annotationEl.querySelector(`.${CLASS_BUTTON_DELETE_COMMENT}`); annotatorUtil.showElement(deleteButtonEl); annotatorUtil.hideElement(deleteConfirmationEl); deleteButtonEl.focus(); @@ -637,27 +663,29 @@ const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; const dialogEl = document.createElement('div'); dialogEl.innerHTML = `
- -
- -
-
-
- -
- -
diff --git a/src/lib/annotations/AnnotationThread.js b/src/lib/annotations/AnnotationThread.js index 63f1b16ec..15d6a1eaa 100644 --- a/src/lib/annotations/AnnotationThread.js +++ b/src/lib/annotations/AnnotationThread.js @@ -3,8 +3,8 @@ import autobind from 'autobind-decorator'; import Annotation from './Annotation'; import AnnotationService from './AnnotationService'; import * as annotatorUtil from './annotatorUtil'; -import * as constants from './annotationConstants'; import { ICON_PLACED_ANNOTATION } from '../icons/icons'; +import { STATES, TYPES, CLASS_ANNOTATION_POINT_BUTTON, DATA_TYPE_ANNOTATION_INDICATOR } from './annotationConstants'; @autobind class AnnotationThread extends EventEmitter { //-------------------------------------------------------------------------- @@ -92,7 +92,7 @@ import { ICON_PLACED_ANNOTATION } from '../icons/icons'; * @return {void} */ reset() { - this.state = constants.ANNOTATION_STATE_INACTIVE; + this.state = STATES.inactive; } /** @@ -144,7 +144,7 @@ import { ICON_PLACED_ANNOTATION } from '../icons/icons'; this.saveAnnotationToThread(tempAnnotation); // Changing state from pending - this.state = constants.ANNOTATION_STATE_HOVER; + this.state = STATES.hover; // Save annotation on server this.annotationService @@ -282,9 +282,9 @@ import { ICON_PLACED_ANNOTATION } from '../icons/icons'; */ setup() { if (this.annotations.length === 0) { - this.state = constants.ANNOTATION_STATE_PENDING; + this.state = STATES.pending; } else { - this.state = constants.ANNOTATION_STATE_INACTIVE; + this.state = STATES.inactive; } this.createDialog(); @@ -385,11 +385,7 @@ import { ICON_PLACED_ANNOTATION } from '../icons/icons'; * @return {void} */ cancelUnsavedAnnotation() { - if ( - !this.isMobile && - this.state !== constants.ANNOTATION_STATE_PENDING && - this.state !== constants.ANNOTATION_STATE_PENDING_ACTIVE - ) { + if (!this.isMobile && !annotatorUtil.isPending(this.state)) { return; } this.destroy(); @@ -407,8 +403,8 @@ import { ICON_PLACED_ANNOTATION } from '../icons/icons'; */ createElement() { const indicatorEl = document.createElement('button'); - indicatorEl.classList.add('bp-point-annotation-btn'); - indicatorEl.setAttribute('data-type', 'annotation-indicator'); + indicatorEl.classList.add(CLASS_ANNOTATION_POINT_BUTTON); + indicatorEl.setAttribute('data-type', DATA_TYPE_ANNOTATION_INDICATOR); indicatorEl.innerHTML = ICON_PLACED_ANNOTATION; return indicatorEl; } @@ -469,7 +465,7 @@ import { ICON_PLACED_ANNOTATION } from '../icons/icons'; * @return {void} */ createAnnotation(data) { - this.saveAnnotation(constants.ANNOTATION_TYPE_POINT, data.text); + this.saveAnnotation(TYPES.point, data.text); } /** diff --git a/src/lib/annotations/Annotator.js b/src/lib/annotations/Annotator.js index 8c401e19c..f6200e674 100644 --- a/src/lib/annotations/Annotator.js +++ b/src/lib/annotations/Annotator.js @@ -2,11 +2,20 @@ import EventEmitter from 'events'; import autobind from 'autobind-decorator'; import Notification from '../Notification'; import AnnotationService from './AnnotationService'; -import * as constants from './annotationConstants'; import * as annotatorUtil from './annotatorUtil'; import { CLASS_ACTIVE, CLASS_HIDDEN } from '../constants'; import { ICON_CLOSE } from '../icons/icons'; import './Annotator.scss'; +import { + DATA_TYPE_ANNOTATION_DIALOG, + CLASS_MOBILE_ANNOTATION_DIALOG, + CLASS_ANNOTATION_DIALOG, + CLASS_MOBILE_DIALOG_HEADER, + CLASS_DIALOG_CLOSE, + TYPES +} from './annotationConstants'; + +const CLASS_ANNOTATION_POINT_MODE = 'bp-point-annotation-mode'; @autobind class Annotator extends EventEmitter { //-------------------------------------------------------------------------- @@ -98,14 +107,14 @@ import './Annotator.scss'; setupMobileDialog() { // Generate HTML of dialog const mobileDialogEl = document.createElement('div'); - mobileDialogEl.setAttribute('data-type', 'annotation-dialog'); - mobileDialogEl.classList.add(constants.CLASS_MOBILE_ANNOTATION_DIALOG); - mobileDialogEl.classList.add(constants.CLASS_ANNOTATION_DIALOG); + mobileDialogEl.setAttribute('data-type', DATA_TYPE_ANNOTATION_DIALOG); + mobileDialogEl.classList.add(CLASS_MOBILE_ANNOTATION_DIALOG); + mobileDialogEl.classList.add(CLASS_ANNOTATION_DIALOG); mobileDialogEl.classList.add(CLASS_HIDDEN); mobileDialogEl.innerHTML = ` -
- +
+
`.trim(); this.container.appendChild(mobileDialogEl); @@ -237,7 +246,7 @@ import './Annotator.scss'; this.notification.hide(); this.emit('annotationmodeexit'); - this.annotatedElement.classList.remove(constants.CLASS_ANNOTATION_POINT_MODE); + this.annotatedElement.classList.remove(CLASS_ANNOTATION_POINT_MODE); if (buttonEl) { buttonEl.classList.remove(CLASS_ACTIVE); } @@ -250,7 +259,7 @@ import './Annotator.scss'; this.notification.show(__('notification_annotation_mode')); this.emit('annotationmodeenter'); - this.annotatedElement.classList.add(constants.CLASS_ANNOTATION_POINT_MODE); + this.annotatedElement.classList.add(CLASS_ANNOTATION_POINT_MODE); if (buttonEl) { buttonEl.classList.add(CLASS_ACTIVE); } @@ -495,7 +504,7 @@ import './Annotator.scss'; } // Get annotation location from click event, ignore click if location is invalid - const location = this.getLocationFromEvent(event, constants.ANNOTATION_TYPE_POINT); + const location = this.getLocationFromEvent(event, TYPES.point); if (!location) { this.togglePointModeHandler(); return; @@ -505,7 +514,7 @@ import './Annotator.scss'; this.togglePointModeHandler(); // Create new thread with no annotations, show indicator, and show dialog - const thread = this.createAnnotationThread([], location, constants.ANNOTATION_TYPE_POINT); + const thread = this.createAnnotationThread([], location, TYPES.point); if (thread) { thread.show(); @@ -536,7 +545,7 @@ import './Annotator.scss'; * @return {boolean} Whether or not in point mode */ isInPointMode() { - return this.annotatedElement.classList.contains(constants.CLASS_ANNOTATION_POINT_MODE); + return this.annotatedElement.classList.contains(CLASS_ANNOTATION_POINT_MODE); } //-------------------------------------------------------------------------- @@ -554,7 +563,7 @@ import './Annotator.scss'; let hasPendingThreads = false; Object.keys(this.threads).forEach((page) => { this.threads[page].forEach((pendingThread) => { - if (pendingThread.state === constants.ANNOTATION_STATE_PENDING) { + if (annotatorUtil.isPending(pendingThread.state)) { hasPendingThreads = true; pendingThread.destroy(); } diff --git a/src/lib/annotations/Annotator.scss b/src/lib/annotations/Annotator.scss index 7cbb23079..725a93eea 100644 --- a/src/lib/annotations/Annotator.scss +++ b/src/lib/annotations/Annotator.scss @@ -86,7 +86,7 @@ $avatar-color-9: #f22c44; fill: $highlight-yellow; } - &.highlight-active svg { + &.bp-is-active svg { fill: $highlight-yellow-active-hover; } diff --git a/src/lib/annotations/BoxAnnotations.js b/src/lib/annotations/BoxAnnotations.js index 2f1611646..b9bdf6fac 100644 --- a/src/lib/annotations/BoxAnnotations.js +++ b/src/lib/annotations/BoxAnnotations.js @@ -1,18 +1,19 @@ import DocAnnotator from './doc/DocAnnotator'; import ImageAnnotator from './image/ImageAnnotator'; +import { TYPES } from './annotationConstants'; const ANNOTATORS = [ { NAME: 'Document', CONSTRUCTOR: DocAnnotator, VIEWER: ['Document', 'Presentation'], - TYPE: ['point', 'highlight'] + TYPE: [TYPES.point, TYPES.highlight] }, { NAME: 'Image', CONSTRUCTOR: ImageAnnotator, VIEWER: ['Image', 'MultiImage'], - TYPE: ['point'] + TYPE: [TYPES.point] } ]; diff --git a/src/lib/annotations/CommentBox.js b/src/lib/annotations/CommentBox.js index 1db15e7d1..37d263650 100644 --- a/src/lib/annotations/CommentBox.js +++ b/src/lib/annotations/CommentBox.js @@ -1,5 +1,6 @@ import EventEmitter from 'events'; import { CLASS_ACTIVE } from '../constants'; +import * as constants from './annotationConstants'; import { hideElement, showElement } from './annotatorUtil'; // Display Text @@ -177,13 +178,13 @@ class CommentBox extends EventEmitter { const containerEl = document.createElement('section'); containerEl.classList.add('bp-create-highlight-comment'); containerEl.innerHTML = ` - -
- -
`.trim(); @@ -224,9 +225,9 @@ class CommentBox extends EventEmitter { const containerEl = this.createHTML(); // Reference HTML - this.textAreaEl = containerEl.querySelector('.annotation-textarea'); - this.cancelEl = containerEl.querySelector('.cancel-annotation-btn'); - this.postEl = containerEl.querySelector('.post-annotation-btn'); + this.textAreaEl = containerEl.querySelector(constants.SELECTOR_ANNOTATION_TEXTAREA); + this.cancelEl = containerEl.querySelector(constants.SELECTOR_ANNOTATION_BUTTON_CANCEL); + this.postEl = containerEl.querySelector(constants.SELECTOR_ANNOTATION_BUTTON_POST); // Add event listeners this.cancelEl.addEventListener('click', this.onCancel); diff --git a/src/lib/annotations/__tests__/AnnotationDialog-test.js b/src/lib/annotations/__tests__/AnnotationDialog-test.js index cc3ae0934..e032bf93a 100644 --- a/src/lib/annotations/__tests__/AnnotationDialog-test.js +++ b/src/lib/annotations/__tests__/AnnotationDialog-test.js @@ -5,7 +5,13 @@ import * as annotatorUtil from '../annotatorUtil'; import * as constants from '../annotationConstants'; import { CLASS_ACTIVE, CLASS_HIDDEN } from '../../constants'; +const CLASS_ANNOTATION_PLAIN_HIGHLIGHT = 'bp-plain-highlight'; +const CLASS_CANCEL_DELETE = 'cancel-delete-btn'; +const CLASS_CANNOT_ANNOTATE = 'cannot-annotate'; +const CLASS_REPLY_TEXTAREA = 'reply-textarea'; const CLASS_ANIMATE_DIALOG = 'bp-animate-show-dialog'; +const CLASS_BUTTON_DELETE_COMMENT = 'delete-comment-btn'; +const SELECTOR_DELETE_CONFIRMATION = '.delete-confirmation'; let dialog; const sandbox = sinon.sandbox.create(); @@ -103,7 +109,7 @@ describe('lib/annotations/AnnotationDialog', () => { dialog.deactivateReply(); dialog.show(); - expect(dialog.element).to.have.class(constants.CLASS_CANNOT_ANNOTATE); + expect(dialog.element).to.have.class(CLASS_CANNOT_ANNOTATE); }); it('should focus textarea if in viewport', () => { @@ -113,7 +119,7 @@ describe('lib/annotations/AnnotationDialog', () => { sandbox.stub(annotatorUtil, 'isElementInViewport').returns(true); dialog.show(); - expect(document.activeElement).to.have.class('reply-textarea'); + expect(document.activeElement).to.have.class(CLASS_REPLY_TEXTAREA); }); it('should activate reply textarea if dialog has annotations', () => { @@ -123,7 +129,7 @@ describe('lib/annotations/AnnotationDialog', () => { sandbox.stub(dialog, 'activateReply'); dialog.show(); - const textArea = dialog.element.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const textArea = dialog.element.querySelector(`.${CLASS_REPLY_TEXTAREA}`); expect(textArea).to.not.have.class(CLASS_ACTIVE); expect(dialog.activateReply).to.be.called; }); @@ -168,7 +174,7 @@ describe('lib/annotations/AnnotationDialog', () => { stubs.bind = sandbox.stub(dialog, 'bindDOMListeners'); dialog.show(); - expect(dialog.element).to.have.class('bp-plain-highlight'); + expect(dialog.element).to.have.class(CLASS_ANNOTATION_PLAIN_HIGHLIGHT); }); }); @@ -181,7 +187,7 @@ describe('lib/annotations/AnnotationDialog', () => { }); it('should hide and reset the mobile annotations dialog', () => { - dialog.element = document.querySelector('.bp-mobile-annotation-dialog'); + dialog.element = document.querySelector(constants.SELECTOR_MOBILE_ANNOTATION_DIALOG); stubs.hide = sandbox.stub(annotatorUtil, 'hideElement'); stubs.unbind = sandbox.stub(dialog, 'unbindDOMListeners'); stubs.cancel = sandbox.stub(dialog, 'cancelAnnotation'); @@ -195,13 +201,13 @@ describe('lib/annotations/AnnotationDialog', () => { }); it('should remove the animation class', () => { - dialog.element = document.querySelector('.bp-mobile-annotation-dialog'); + dialog.element = document.querySelector(constants.SELECTOR_MOBILE_ANNOTATION_DIALOG); dialog.hideMobileDialog(); expect(dialog.element.classList.contains(CLASS_ANIMATE_DIALOG)).to.be.false; }); it('should cancel unsaved annotations only if the dialog does not have annotations', () => { - dialog.element = document.querySelector('.bp-mobile-annotation-dialog'); + dialog.element = document.querySelector(constants.SELECTOR_MOBILE_ANNOTATION_DIALOG); stubs.cancel = sandbox.stub(dialog, 'cancelAnnotation'); dialog.hasAnnotations = false; @@ -241,8 +247,8 @@ describe('lib/annotations/AnnotationDialog', () => { dialog.annotatedElement.appendChild(dialog.element); dialog.addAnnotation(new Annotation({})); - const createSectionEl = document.querySelector('[data-section="create"]'); - const showSectionEl = document.querySelector('[data-section="show"]'); + const createSectionEl = document.querySelector(constants.SECTION_CREATE); + const showSectionEl = document.querySelector(constants.SECTION_SHOW); expect(createSectionEl).to.have.class(CLASS_HIDDEN); expect(showSectionEl).to.not.have.class(CLASS_HIDDEN); }); @@ -389,7 +395,7 @@ describe('lib/annotations/AnnotationDialog', () => { dialog.keydownHandler({ key: ' ', // space - target: dialog.element.querySelector('.reply-textarea'), + target: dialog.element.querySelector(`.${CLASS_REPLY_TEXTAREA}`), stopPropagation: () => {} }); expect(stubs.activate).to.be.called; @@ -480,14 +486,14 @@ describe('lib/annotations/AnnotationDialog', () => { }); it('should post annotation when post annotation button is clicked', () => { - stubs.findClosest.returns('post-annotation-btn'); + stubs.findClosest.returns(constants.CLASS_ANNOTATION_BUTTON_POST); dialog.clickHandler(stubs.event); expect(stubs.post).to.be.called; }); it('should cancel annotation when cancel annotation button is clicked', () => { - stubs.findClosest.returns('cancel-annotation-btn'); + stubs.findClosest.returns(constants.CLASS_ANNOTATION_BUTTON_CANCEL); dialog.isMobile = false; dialog.clickHandler(stubs.event); @@ -497,7 +503,7 @@ describe('lib/annotations/AnnotationDialog', () => { }); it('should only hide the mobile dialog when the cancel annotation button is clicked on mobile', () => { - stubs.findClosest.returns('cancel-annotation-btn'); + stubs.findClosest.returns(constants.CLASS_ANNOTATION_BUTTON_CANCEL); dialog.isMobile = true; dialog.clickHandler(stubs.event); @@ -507,7 +513,7 @@ describe('lib/annotations/AnnotationDialog', () => { }); it('should activate reply area when textarea is clicked', () => { - stubs.findClosest.returns('reply-textarea'); + stubs.findClosest.returns(CLASS_REPLY_TEXTAREA); dialog.clickHandler(stubs.event); expect(stubs.activate).to.be.called; @@ -536,7 +542,7 @@ describe('lib/annotations/AnnotationDialog', () => { }); it('should cancel deletion when cancel delete button is clicked', () => { - stubs.findClosest.onFirstCall().returns('cancel-delete-btn'); + stubs.findClosest.onFirstCall().returns(CLASS_CANCEL_DELETE); stubs.findClosest.onSecondCall().returns('someID'); dialog.clickHandler(stubs.event); @@ -616,7 +622,7 @@ describe('lib/annotations/AnnotationDialog', () => { permissions: { can_delete: false } }) ); - const deleteButton = document.querySelector('.delete-comment-btn'); + const deleteButton = document.querySelector(`.${CLASS_BUTTON_DELETE_COMMENT}`); expect(deleteButton).to.have.class(CLASS_HIDDEN); }); @@ -629,7 +635,7 @@ describe('lib/annotations/AnnotationDialog', () => { permissions: {} }) ); - const deleteButton = document.querySelector('.delete-comment-btn'); + const deleteButton = document.querySelector(`.${CLASS_BUTTON_DELETE_COMMENT}`); expect(deleteButton).to.have.class(CLASS_HIDDEN); }); @@ -642,7 +648,7 @@ describe('lib/annotations/AnnotationDialog', () => { permissions: { can_delete: true } }) ); - const deleteButton = document.querySelector('.delete-comment-btn'); + const deleteButton = document.querySelector(`.${CLASS_BUTTON_DELETE_COMMENT}`); expect(deleteButton).to.not.have.class(CLASS_HIDDEN); }); @@ -655,7 +661,7 @@ describe('lib/annotations/AnnotationDialog', () => { permissions: { can_delete: true } }) ); - const deleteConfirmation = document.querySelector('.delete-confirmation'); + const deleteConfirmation = document.querySelector(SELECTOR_DELETE_CONFIRMATION); expect(deleteConfirmation).to.have.class(CLASS_HIDDEN); }); @@ -715,7 +721,7 @@ describe('lib/annotations/AnnotationDialog', () => { }); it('should do nothing if reply textarea is already active', () => { - const replyTextEl = dialog.element.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextEl = dialog.element.querySelector(`.${CLASS_REPLY_TEXTAREA}`); replyTextEl.classList.add('bp-is-active'); sandbox.stub(annotatorUtil, 'showElement'); @@ -731,7 +737,7 @@ describe('lib/annotations/AnnotationDialog', () => { user: { id: 1, name: 'user' }, permissions: { can_delete: true } }); - const replyTextEl = document.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextEl = document.querySelector(`.${CLASS_REPLY_TEXTAREA}`); const buttonContainer = replyTextEl.parentNode.querySelector(constants.SELECTOR_BUTTON_CONTAINER); dialog.activateReply(); @@ -758,7 +764,7 @@ describe('lib/annotations/AnnotationDialog', () => { permissions: { can_delete: true } }) ); - const replyTextEl = document.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextEl = document.querySelector(`.${CLASS_REPLY_TEXTAREA}`); const buttonContainer = replyTextEl.parentNode.querySelector(constants.SELECTOR_BUTTON_CONTAINER); dialog.deactivateReply(); @@ -792,7 +798,7 @@ describe('lib/annotations/AnnotationDialog', () => { permissions: { can_delete: true } }) ); - const replyTextEl = document.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextEl = document.querySelector(`.${CLASS_REPLY_TEXTAREA}`); dialog.activateReply(); replyTextEl.innerHTML += 'the preview SDK is great!'; @@ -809,7 +815,7 @@ describe('lib/annotations/AnnotationDialog', () => { permissions: { can_delete: true } }) ); - const replyTextEl = document.querySelector(constants.SELECTOR_REPLY_TEXTAREA); + const replyTextEl = document.querySelector(`.${CLASS_REPLY_TEXTAREA}`); dialog.activateReply(); replyTextEl.innerHTML += 'the preview SDK is great!'; @@ -872,16 +878,16 @@ describe('lib/annotations/AnnotationDialog', () => { describe('generateDialogEl', () => { it('should generate a blank annotations dialog element', () => { const dialogEl = dialog.generateDialogEl(0); - const createSectionEl = dialogEl.querySelector('[data-section="create"]'); - const showSectionEl = dialogEl.querySelector('[data-section="show"]'); + const createSectionEl = dialogEl.querySelector(constants.SECTION_CREATE); + const showSectionEl = dialogEl.querySelector(constants.SECTION_SHOW); expect(createSectionEl).to.not.have.class(CLASS_HIDDEN); expect(showSectionEl).to.have.class(CLASS_HIDDEN); }); it('should generate an annotations dialog element with annotations', () => { const dialogEl = dialog.generateDialogEl(1); - const createSectionEl = dialogEl.querySelector('[data-section="create"]'); - const showSectionEl = dialogEl.querySelector('[data-section="show"]'); + const createSectionEl = dialogEl.querySelector(constants.SECTION_CREATE); + const showSectionEl = dialogEl.querySelector(constants.SECTION_SHOW); expect(createSectionEl).to.have.class(CLASS_HIDDEN); expect(showSectionEl).to.not.have.class(CLASS_HIDDEN); }); diff --git a/src/lib/annotations/__tests__/AnnotationThread-test.js b/src/lib/annotations/__tests__/AnnotationThread-test.js index a27a9ff6e..560167978 100644 --- a/src/lib/annotations/__tests__/AnnotationThread-test.js +++ b/src/lib/annotations/__tests__/AnnotationThread-test.js @@ -2,8 +2,13 @@ import AnnotationThread from '../AnnotationThread'; import Annotation from '../Annotation'; import * as annotatorUtil from '../annotatorUtil'; -import * as constants from '../annotationConstants'; import { CLASS_HIDDEN } from '../../constants'; +import { + STATES, + TYPES, + CLASS_ANNOTATION_POINT_BUTTON, + DATA_TYPE_ANNOTATION_INDICATOR +} from '../annotationConstants'; let thread; const sandbox = sinon.sandbox.create(); @@ -90,7 +95,7 @@ describe('lib/annotations/AnnotationThread', () => { describe('reset()', () => { it('should set the thread state to inactive', () => { thread.reset(); - expect(thread.state).to.equal(constants.ANNOTATION_STATE_INACTIVE); + expect(thread.state).to.equal(STATES.inactive); }); }); @@ -152,7 +157,7 @@ describe('lib/annotations/AnnotationThread', () => { thread: '1' }) ); - expect(thread.state).to.equal(constants.ANNOTATION_STATE_HOVER); + expect(thread.state).to.equal(STATES.hover); }); it('should delete the temporary annotation and broadcast an error if there was an error saving', (done) => { @@ -339,7 +344,7 @@ describe('lib/annotations/AnnotationThread', () => { it('should set state to pending if thread is initialized with no annotations', () => { thread.setup(); - expect(thread.state).to.equal(constants.ANNOTATION_STATE_PENDING); + expect(thread.state).to.equal(STATES.pending); }); it('should set state to inactive if thread is initialized with annotations', () => { @@ -356,7 +361,7 @@ describe('lib/annotations/AnnotationThread', () => { }); thread.setup(); - expect(thread.state).to.equal(constants.ANNOTATION_STATE_INACTIVE); + expect(thread.state).to.equal(STATES.inactive); }); }); @@ -366,7 +371,7 @@ describe('lib/annotations/AnnotationThread', () => { thread.setupElement(); expect(thread.element instanceof HTMLElement).to.be.true; - expect(thread.element).to.have.class('bp-point-annotation-btn'); + expect(thread.element).to.have.class(CLASS_ANNOTATION_POINT_BUTTON); expect(stubs.bind).to.be.called; }); }); @@ -470,12 +475,12 @@ describe('lib/annotations/AnnotationThread', () => { // 'pending' state thread.isMobile = false; - thread.state = constants.ANNOTATION_STATE_PENDING; + thread.state = STATES.pending; thread.cancelUnsavedAnnotation(); expect(thread.destroy).to.be.called; // 'pending-active' state - thread.state = constants.ANNOTATION_STATE_PENDING_ACTIVE; + thread.state = STATES.pending_ACTIVE; thread.cancelUnsavedAnnotation(); expect(thread.destroy).to.be.called; }); @@ -484,8 +489,8 @@ describe('lib/annotations/AnnotationThread', () => { describe('createElement()', () => { it('should create an element with the right class and attribute', () => { const element = thread.createElement(); - expect(element).to.have.class('bp-point-annotation-btn'); - expect(element).to.have.attribute('data-type', 'annotation-indicator'); + expect(element).to.have.class(CLASS_ANNOTATION_POINT_BUTTON); + expect(element).to.have.attribute('data-type', DATA_TYPE_ANNOTATION_INDICATOR); }); }); @@ -565,7 +570,7 @@ describe('lib/annotations/AnnotationThread', () => { it('should create a new point annotation', () => { sandbox.stub(thread, 'saveAnnotation'); thread.createAnnotation({ text: 'bleh' }); - expect(thread.saveAnnotation).to.be.calledWith(constants.ANNOTATION_TYPE_POINT, 'bleh'); + expect(thread.saveAnnotation).to.be.calledWith(TYPES.point, 'bleh'); }); }); diff --git a/src/lib/annotations/__tests__/Annotator-test.js b/src/lib/annotations/__tests__/Annotator-test.js index 22b0940ba..c9ec5ff7d 100644 --- a/src/lib/annotations/__tests__/Annotator-test.js +++ b/src/lib/annotations/__tests__/Annotator-test.js @@ -1,8 +1,8 @@ /* eslint-disable no-unused-expressions */ import Annotator from '../Annotator'; -import * as constants from '../annotationConstants'; import * as annotatorUtil from '../annotatorUtil'; import AnnotationService from '../AnnotationService'; +import { STATES, CLASS_ANNOTATION_POINT_MODE } from '../annotationConstants'; let annotator; let stubs = {}; @@ -270,7 +270,7 @@ describe('lib/annotations/Annotator', () => { expect(destroyStub).to.be.called; expect(annotator.notification.show).to.be.called; expect(annotator.emit).to.be.calledWith('annotationmodeenter'); - expect(annotatedEl).to.have.class(constants.CLASS_ANNOTATION_POINT_MODE); + expect(annotatedEl).to.have.class(CLASS_ANNOTATION_POINT_MODE); expect(annotator.unbindDOMListeners).to.be.called; expect(annotator.bindPointModeListeners).to.be.called; }); @@ -285,7 +285,7 @@ describe('lib/annotations/Annotator', () => { expect(destroyStub).to.be.called; expect(annotator.notification.hide).to.be.called; expect(annotator.emit).to.be.calledWith('annotationmodeexit'); - expect(annotatedEl).to.not.have.class(constants.CLASS_ANNOTATION_POINT_MODE); + expect(annotatedEl).to.not.have.class(CLASS_ANNOTATION_POINT_MODE); expect(annotator.unbindPointModeListeners).to.be.called; expect(annotator.bindDOMListeners).to.be.called; }); @@ -505,10 +505,10 @@ describe('lib/annotations/Annotator', () => { describe('isInPointMode', () => { it('should return whether the annotator is in point mode or not', () => { - annotator.annotatedElement.classList.add(constants.CLASS_ANNOTATION_POINT_MODE); + annotator.annotatedElement.classList.add(CLASS_ANNOTATION_POINT_MODE); expect(annotator.isInPointMode()).to.be.true; - annotator.annotatedElement.classList.remove(constants.CLASS_ANNOTATION_POINT_MODE); + annotator.annotatedElement.classList.remove(CLASS_ANNOTATION_POINT_MODE); expect(annotator.isInPointMode()).to.be.false; }); }); @@ -518,7 +518,7 @@ describe('lib/annotations/Annotator', () => { stubs.thread = { location: { page: 2 }, type: 'type', - state: constants.ANNOTATION_STATE_PENDING, + state: STATES.pending, destroy: () => {}, unbindCustomListenersOnThread: () => {}, removeAllListeners: () => {} @@ -555,7 +555,7 @@ describe('lib/annotations/Annotator', () => { const pendingThread = { location: { page: 2 }, type: 'type', - state: constants.ANNOTATION_STATE_PENDING, + state: STATES.pending, destroy: () => {}, unbindCustomListenersOnThread: () => {}, removeAllListeners: () => {} diff --git a/src/lib/annotations/__tests__/CommentBox-test.js b/src/lib/annotations/__tests__/CommentBox-test.js index 59419f299..f8ce7febb 100644 --- a/src/lib/annotations/__tests__/CommentBox-test.js +++ b/src/lib/annotations/__tests__/CommentBox-test.js @@ -1,6 +1,10 @@ /* eslint-disable no-unused-expressions */ import CommentBox from '../CommentBox'; import { CLASS_HIDDEN } from '../../constants'; +import { + SELECTOR_ANNOTATION_BUTTON_CANCEL, + SELECTOR_ANNOTATION_BUTTON_POST +} from '../annotationConstants'; describe('lib/annotations/CommentBox', () => { const sandbox = sinon.sandbox.create(); @@ -190,11 +194,11 @@ describe('lib/annotations/CommentBox', () => { }); it('should create a cancel button with the provided cancel text', () => { - expect(el.querySelector('.cancel-annotation-btn')).to.exist; + expect(el.querySelector(SELECTOR_ANNOTATION_BUTTON_CANCEL)).to.exist; }); it('should create a post button with the provided text', () => { - expect(el.querySelector('.post-annotation-btn')).to.exist; + expect(el.querySelector(SELECTOR_ANNOTATION_BUTTON_POST)).to.exist; }); }); diff --git a/src/lib/annotations/__tests__/annotatorUtil-test.js b/src/lib/annotations/__tests__/annotatorUtil-test.js index 9a7d525e5..0b029ed06 100644 --- a/src/lib/annotations/__tests__/annotatorUtil-test.js +++ b/src/lib/annotations/__tests__/annotatorUtil-test.js @@ -19,7 +19,12 @@ import { isPending, validateThreadParams } from '../annotatorUtil'; -import * as constants from '../annotationConstants'; +import { + STATES, + TYPES, + SELECTOR_ANNOTATION_DIALOG, + SELECTOR_ANNOTATION_CARET +} from '../annotationConstants'; const DIALOG_WIDTH = 81; @@ -233,15 +238,15 @@ describe('lib/annotations/annotatorUtil', () => { describe('isHighlightAnnotation()', () => { it('should return true if annotation is a plain highlight annotation', () => { - assert.ok(isHighlightAnnotation(constants.ANNOTATION_TYPE_HIGHLIGHT)); + assert.ok(isHighlightAnnotation(TYPES.highlight)); }); it('should return true if annotation is a highlight comment annotation', () => { - assert.ok(isHighlightAnnotation(constants.ANNOTATION_TYPE_HIGHLIGHT_COMMENT)); + assert.ok(isHighlightAnnotation(TYPES.highlight_comment)); }); it('should return false if annotation is a point annotation', () => { - assert.ok(!isHighlightAnnotation(constants.ANNOTATION_TYPE_POINT)); + assert.ok(!isHighlightAnnotation(TYPES.point)); }); }); @@ -293,11 +298,11 @@ describe('lib/annotations/annotatorUtil', () => { const browserX = 1; const pageWidth = 100; const initX = browserX - DIALOG_WIDTH / 2; - const dialogEl = document.querySelector('.bp-annotation-dialog'); + const dialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); const dialogX = repositionCaret(dialogEl, initX, DIALOG_WIDTH, browserX, pageWidth); - const annotationCaretEl = dialogEl.querySelector('.bp-annotation-caret'); + const annotationCaretEl = dialogEl.querySelector(SELECTOR_ANNOTATION_CARET); expect(dialogX).to.equal(0); // dialog aligned to the left expect(annotationCaretEl.style.left).to.equal('10px'); // caret aligned to the left }); @@ -306,11 +311,11 @@ describe('lib/annotations/annotatorUtil', () => { const browserX = 400; const pageWidth = 100; const initX = browserX - DIALOG_WIDTH / 2; - const dialogEl = document.querySelector('.bp-annotation-dialog'); + const dialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); const dialogX = repositionCaret(dialogEl, initX, DIALOG_WIDTH, browserX, pageWidth); - const annotationCaretEl = dialogEl.querySelector('.bp-annotation-caret'); + const annotationCaretEl = dialogEl.querySelector(SELECTOR_ANNOTATION_CARET); expect(dialogX).to.equal(19); // dialog aligned to the right expect(annotationCaretEl.style.left).to.equal('71px'); // caret aligned to the right }); @@ -319,11 +324,11 @@ describe('lib/annotations/annotatorUtil', () => { const browserX = 100; const pageWidth = 1000; const initX = browserX - DIALOG_WIDTH / 2; - const dialogEl = document.querySelector('.bp-annotation-dialog'); + const dialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); const dialogX = repositionCaret(dialogEl, initX, DIALOG_WIDTH, browserX, pageWidth); - const annotationCaretEl = dialogEl.querySelector('.bp-annotation-caret'); + const annotationCaretEl = dialogEl.querySelector(SELECTOR_ANNOTATION_CARET); expect(dialogX).to.equal(initX); // dialog x unchanged expect(annotationCaretEl.style.left).to.equal('50%'); // caret centered with dialog }); @@ -331,13 +336,13 @@ describe('lib/annotations/annotatorUtil', () => { describe('isPending()', () => { it('should return true if thread is pending or pending-active', () => { - expect(isPending(constants.ANNOTATION_STATE_PENDING)).to.be.true; - expect(isPending(constants.ANNOTATION_STATE_PENDING_ACTIVE)).to.be.true; + expect(isPending(STATES.pending)).to.be.true; + expect(isPending(STATES.pending_active)).to.be.true; }); it('should return false if thread is notpending', () => { - expect(isPending(constants.ANNOTATION_STATE_HOVER)).to.be.false; - expect(isPending(constants.ANNOTATION_STATE_INACTIVE)).to.be.false; + expect(isPending(STATES.hover)).to.be.false; + expect(isPending(STATES.inactive)).to.be.false; }); }); diff --git a/src/lib/annotations/annotationConstants.js b/src/lib/annotations/annotationConstants.js index 74b3c4d02..7903de518 100644 --- a/src/lib/annotations/annotationConstants.js +++ b/src/lib/annotations/annotationConstants.js @@ -1,44 +1,63 @@ export const CLASS_ANNOTATION_BUTTON_CANCEL = 'cancel-annotation-btn'; export const CLASS_ANNOTATION_BUTTON_POST = 'post-annotation-btn'; -export const CLASS_MOBILE_ANNOTATION_DIALOG = 'bp-mobile-annotation-dialog'; export const CLASS_ANNOTATION_DIALOG = 'bp-annotation-dialog'; -export const CLASS_ANNOTATION_DIALOG_HIGHLIGHT = 'bp-highlight-dialog'; -export const CLASS_ANNOTATION_TEXT_HIGHLIGHTED = 'bp-is-text-highlighted'; -export const CLASS_ANNOTATION_POINT = 'bp-show-point-annotation-btn'; -export const CLASS_ANNOTATION_POINT_ICON = 'bp-point-annotation-icon'; +export const CLASS_ANNOTATION_HIGHLIGHT_DIALOG = 'bp-annotation-highlight-dialog'; +export const CLASS_ANNOTATION_POINT_BUTTON = 'bp-point-annotation-btn'; export const CLASS_ANNOTATION_POINT_MODE = 'bp-point-annotation-mode'; +export const CLASS_ANNOTATION_CARET = 'bp-annotation-caret'; export const CLASS_ANNOTATION_TEXTAREA = 'annotation-textarea'; export const CLASS_BUTTON_CONTAINER = 'button-container'; -export const CLASS_BUTTON_DELETE_COMMENT = 'delete-comment-btn'; -export const CLASS_CANNOT_ANNOTATE = 'cannot-annotate'; -export const CLASS_COMMENTS_CONTAINER = 'annotation-comments'; export const CLASS_ANNOTATION_CONTAINER = 'annotation-container'; -export const CLASS_REPLY_CONTAINER = 'reply-container'; -export const CLASS_REPLY_TEXTAREA = 'reply-textarea'; +export const CLASS_MOBILE_ANNOTATION_DIALOG = 'bp-mobile-annotation-dialog'; +export const CLASS_MOBILE_DIALOG_HEADER = 'bp-annotation-mobile-header'; +export const CLASS_DIALOG_CLOSE = 'bp-annotation-dialog-close'; +export const CLASS_TEXTAREA = 'bp-textarea'; +export const CLASS_HIGHLIGHT_BTNS = 'bp-annotation-highlight-btns'; +export const CLASS_ADD_HIGHLIGHT_BTN = 'bp-add-highlight-btn'; +export const CLASS_ADD_HIGHLIGHT_COMMENT_BTN = 'bp-highlight-comment-btn'; +export const CLASS_ANNOTATION_LAYER = 'bp-annotation-layer'; + +export const DATA_TYPE_ANNOTATION_DIALOG = 'annotation-dialog'; +export const DATA_TYPE_ANNOTATION_INDICATOR = 'annotation-indicator'; + +export const SECTION_CREATE = '[data-section="create"]'; +export const SECTION_SHOW = '[data-section="show"]'; export const SELECTOR_ANNOTATION_BUTTON_CANCEL = `.${CLASS_ANNOTATION_BUTTON_CANCEL}`; export const SELECTOR_ANNOTATION_BUTTON_POST = `.${CLASS_ANNOTATION_BUTTON_POST}`; -export const SELECTOR_ANNOTATION_POINT = `.${CLASS_ANNOTATION_POINT}`; -export const SELECTOR_ANNOTATION_POINT_ICON = `.${CLASS_ANNOTATION_POINT_ICON}`; +export const SELECTOR_ANNOTATION_DIALOG = `.${CLASS_ANNOTATION_DIALOG}`; +export const SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG = `.${CLASS_ANNOTATION_HIGHLIGHT_DIALOG}`; +export const SELECTOR_ANNOTATION_POINT_BUTTON = `.${CLASS_ANNOTATION_POINT_BUTTON}`; +export const SELECTOR_ANNOTATION_POINT_MODE = `.${CLASS_ANNOTATION_POINT_MODE}`; +export const SELECTOR_ANNOTATION_CARET = `.${CLASS_ANNOTATION_CARET}`; export const SELECTOR_ANNOTATION_TEXTAREA = `.${CLASS_ANNOTATION_TEXTAREA}`; export const SELECTOR_BUTTON_CONTAINER = `.${CLASS_BUTTON_CONTAINER}`; -export const SELECTOR_BUTTON_DELETE_COMMENT = `.${CLASS_BUTTON_DELETE_COMMENT}`; -export const SELECTOR_COMMENTS_CONTAINER = `.${CLASS_COMMENTS_CONTAINER}`; -export const SELECTOR_REPLY_CONTAINER = `.${CLASS_REPLY_CONTAINER}`; -export const SELECTOR_REPLY_TEXTAREA = `.${CLASS_REPLY_TEXTAREA}`; +export const SELECTOR_ANNOTATION_CONTAINER = `.${CLASS_ANNOTATION_CONTAINER}`; +export const SELECTOR_MOBILE_ANNOTATION_DIALOG = `.${CLASS_MOBILE_ANNOTATION_DIALOG}`; +export const SELECTOR_MOBILE_DIALOG_HEADER = `.${CLASS_MOBILE_DIALOG_HEADER}`; +export const SELECTOR_DIALOG_CLOSE = `.${CLASS_DIALOG_CLOSE}`; +export const SELECTOR_HIGHLIGHT_BTNS = `.${CLASS_HIGHLIGHT_BTNS}`; +export const SELECTOR_ADD_HIGHLIGHT_BTN = `.${CLASS_ADD_HIGHLIGHT_BTN}`; + +export const STATES = { + hover: 'hover', // mouse is over + inactive: 'inactive', // not clicked and mouse is not over + pending: 'pending', // not saved + pending_active: 'pending-active' // not saved and pending comment +}; +export const PENDING_STATES = [STATES.pending, STATES.pending_active]; -export const ANNOTATION_STATE_HOVER = 'hover'; // mouse is over -export const ANNOTATION_STATE_INACTIVE = 'inactive'; // not clicked and mouse is not over -export const ANNOTATION_STATE_PENDING = 'pending'; // not saved -export const ANNOTATION_STATE_PENDING_ACTIVE = 'pending-active'; // not saved and pending comment -export const ANNOTATION_TYPE_POINT = 'point'; -export const ANNOTATION_TYPE_HIGHLIGHT = 'highlight'; -export const ANNOTATION_TYPE_HIGHLIGHT_COMMENT = 'highlight-comment'; -export const PENDING_STATES = [ANNOTATION_STATE_PENDING, ANNOTATION_STATE_PENDING_ACTIVE]; +export const TYPES = { + point: 'point', + highlight: 'highlight', + highlight_comment: 'highlight-comment' +}; -export const HIGHLIGHT_NORMAL_FILL_STYLE = 'rgba(254, 217, 78, 0.5)'; -export const HIGHLIGHT_ACTIVE_FILL_STYLE = 'rgba(255, 201, 0, 0.5)'; -export const HIGHLIGHT_ERASE_FILL_STYLE = 'rgba(255, 245, 132, 1)'; +export const HIGHLIGHT_FILL = { + normal: 'rgba(254, 217, 78, 0.5)', + active: 'rgba(255, 201, 0, 0.5)', + erase: 'rgba(255, 245, 132, 1)' +}; export const PAGE_PADDING_TOP = 15; export const PAGE_PADDING_BOTTOM = 15; diff --git a/src/lib/annotations/annotatorUtil.js b/src/lib/annotations/annotatorUtil.js index ed3e8aebd..804912d24 100644 --- a/src/lib/annotations/annotatorUtil.js +++ b/src/lib/annotations/annotatorUtil.js @@ -1,5 +1,5 @@ import { CLASS_ACTIVE, CLASS_HIDDEN, CLASS_INVISIBLE } from '../constants'; -import * as constants from './annotationConstants'; +import { TYPES, SELECTOR_ANNOTATION_CARET, PENDING_STATES } from './annotationConstants'; const AVATAR_COLOR_COUNT = 9; // 9 colors defined in Box React UI avatar code const THREAD_PARAMS = [ @@ -238,7 +238,7 @@ export function isPlainHighlight(annotations) { * @return {boolean} Whether or not annotation is a highlight */ export function isHighlightAnnotation(type) { - return type === constants.ANNOTATION_TYPE_HIGHLIGHT || type === constants.ANNOTATION_TYPE_HIGHLIGHT_COMMENT; + return type === TYPES.highlight || type === TYPES.highlight_comment; } //------------------------------------------------------------------------------ @@ -309,7 +309,7 @@ export function repositionCaret(dialogEl, dialogX, highlightDialogWidth, browser // ${pageWidth}px const dialogPastLeft = dialogX < 0; const dialogPastRight = dialogX + highlightDialogWidth > pageWidth; - const annotationCaretEl = dialogEl.querySelector('.bp-annotation-caret'); + const annotationCaretEl = dialogEl.querySelector(SELECTOR_ANNOTATION_CARET); if (dialogPastLeft && !dialogPastRight) { // Leave a minimum of 10 pixels so caret doesn't go off edge @@ -340,7 +340,7 @@ export function repositionCaret(dialogEl, dialogX, highlightDialogWidth, browser * @return {boolean} Whether annotation thread is in a pending state */ export function isPending(threadState) { - return constants.PENDING_STATES.indexOf(threadState) > -1; + return PENDING_STATES.indexOf(threadState) > -1; } /** diff --git a/src/lib/annotations/doc/CreateHighlightDialog.js b/src/lib/annotations/doc/CreateHighlightDialog.js index 44b044f7a..74f16e0f5 100644 --- a/src/lib/annotations/doc/CreateHighlightDialog.js +++ b/src/lib/annotations/doc/CreateHighlightDialog.js @@ -2,22 +2,25 @@ import EventEmitter from 'events'; import { ICON_HIGHLIGHT, ICON_HIGHLIGHT_COMMENT } from '../../icons/icons'; import CommentBox from '../CommentBox'; import { hideElement, showElement } from '../annotatorUtil'; +import * as constants from '../annotationConstants'; const CLASS_CREATE_DIALOG = 'bp-create-annotation-dialog'; const TITLE_HIGHLIGHT_TOGGLE = __('annotation_highlight_toggle'); const TITLE_HIGHLIGHT_COMMENT = __('annotation_highlight_comment'); +const DATA_TYPE_HIGHLIGHT = 'highlight-btn'; +const DATA_TYPE_ADD_HIGHLIGHT_COMMENT = 'add-highlight-comment-btn'; const CREATE_HIGHLIGHT_DIALOG_TEMPLATE = ` -
+
-
- - - @@ -306,12 +309,12 @@ class CreateHighlightDialog extends EventEmitter { highlightDialogEl.classList.add(CLASS_CREATE_DIALOG); highlightDialogEl.innerHTML = CREATE_HIGHLIGHT_DIALOG_TEMPLATE; - const containerEl = highlightDialogEl.querySelector('.bp-annotation-highlight-dialog'); + const containerEl = highlightDialogEl.querySelector(constants.SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG); // Reference HTML - this.highlightCreateEl = containerEl.querySelector('.bp-add-highlight-btn'); - this.commentCreateEl = containerEl.querySelector('.bp-highlight-comment-btn'); - this.buttonsEl = containerEl.querySelector('.bp-annotations-highlight-btns'); + 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); diff --git a/src/lib/annotations/doc/DocAnnotator.js b/src/lib/annotations/doc/DocAnnotator.js index 75ba2b7e3..8919d8eda 100644 --- a/src/lib/annotations/doc/DocAnnotator.js +++ b/src/lib/annotations/doc/DocAnnotator.js @@ -11,13 +11,25 @@ import DocHighlightThread from './DocHighlightThread'; import DocPointThread from './DocPointThread'; import CreateHighlightDialog, { CreateEvents } from './CreateHighlightDialog'; import * as annotatorUtil from '../annotatorUtil'; -import * as constants from '../annotationConstants'; import * as docAnnotatorUtil from './docAnnotatorUtil'; +import { + STATES, + TYPES, + DATA_TYPE_ANNOTATION_DIALOG, + DATA_TYPE_ANNOTATION_INDICATOR, + PAGE_PADDING_TOP, + PAGE_PADDING_BOTTOM, + CLASS_ANNOTATION_LAYER, + PENDING_STATES +} from '../annotationConstants'; const MOUSEMOVE_THROTTLE_MS = 50; const HOVER_TIMEOUT_MS = 75; const MOUSE_MOVE_MIN_DISTANCE = 5; +const SELECTOR_PREVIEW_DOC = '.bp-doc'; +const CLASS_DEFAULT_CURSOR = 'bp-use-default-cursor'; + /** * For filtering out and only showing the first thread in a list of threads. * @@ -40,7 +52,7 @@ function showFirstDialogFilter(thread, index) { * @return {boolean} True if the thread is in a state of hover */ function isThreadInHoverState(thread) { - return thread.state === constants.ANNOTATION_STATE_HOVER; + return thread.state === STATES.hover; } @autobind class DocAnnotator extends Annotator { @@ -140,7 +152,7 @@ function isThreadInHoverState(thread) { * @return {HTMLElement} Annotated element in the viewer */ getAnnotatedEl(containerEl) { - return containerEl.querySelector('.bp-doc'); + return containerEl.querySelector(SELECTOR_PREVIEW_DOC); } /** @@ -160,7 +172,7 @@ function isThreadInHoverState(thread) { let location = null; const zoomScale = annotatorUtil.getScale(this.annotatedElement); - if (annotationType === constants.ANNOTATION_TYPE_POINT) { + if (annotationType === TYPES.point) { // If there is a selection, ignore if (docAnnotatorUtil.isSelectionPresent()) { return location; @@ -175,17 +187,17 @@ function isThreadInHoverState(thread) { // If click is inside an annotation dialog, ignore const dataType = annotatorUtil.findClosestDataType(eventTarget); - if (dataType === 'annotation-dialog' || dataType === 'annotation-indicator') { + if (dataType === DATA_TYPE_ANNOTATION_DIALOG || dataType === DATA_TYPE_ANNOTATION_INDICATOR) { return location; } // Store coordinates at 100% scale in PDF space in PDF units const pageDimensions = pageEl.getBoundingClientRect(); const pageWidth = pageDimensions.width; - const pageHeight = pageDimensions.height - constants.PAGE_PADDING_TOP - constants.PAGE_PADDING_BOTTOM; + const pageHeight = pageDimensions.height - PAGE_PADDING_TOP - PAGE_PADDING_BOTTOM; const browserCoordinates = [ event.clientX - pageDimensions.left, - event.clientY - pageDimensions.top - constants.PAGE_PADDING_TOP + event.clientY - pageDimensions.top - PAGE_PADDING_TOP ]; const pdfCoordinates = docAnnotatorUtil.convertDOMSpaceToPDFSpace( browserCoordinates, @@ -233,7 +245,7 @@ function isThreadInHoverState(thread) { // and scale if needed (in case the representation changes size) const pageDimensions = pageEl.getBoundingClientRect(); const pageWidth = pageDimensions.width; - const pageHeight = pageDimensions.height - constants.PAGE_PADDING_TOP - constants.PAGE_PADDING_BOTTOM; + const pageHeight = pageDimensions.height - PAGE_PADDING_TOP - PAGE_PADDING_BOTTOM; const dimensions = { x: pageWidth / zoomScale, y: pageHeight / zoomScale @@ -315,13 +327,13 @@ function isThreadInHoverState(thread) { } this.createHighlightDialog.hide(); - const location = this.getLocationFromEvent(this.lastHighlightEvent, constants.ANNOTATION_TYPE_HIGHLIGHT); + const location = this.getLocationFromEvent(this.lastHighlightEvent, TYPES.highlight); if (!location) { return null; } const annotations = []; - const thread = this.createAnnotationThread(annotations, location, constants.ANNOTATION_TYPE_HIGHLIGHT); + const thread = this.createAnnotationThread(annotations, location, TYPES.highlight); this.lastHighlightEvent = null; if (!thread) { @@ -334,7 +346,7 @@ function isThreadInHoverState(thread) { thread.dialog.hasComments = true; } - thread.state = constants.ANNOTATION_STATE_HOVER; + thread.state = STATES.hover; thread.show(); thread.dialog.postAnnotation(commentText); @@ -576,7 +588,7 @@ function isThreadInHoverState(thread) { const thread = pageThreads[i]; // Determine if any highlight threads on page are pending or active // and ignore hover events of any highlights below - if (thread.state === constants.ANNOTATION_STATE_PENDING) { + if (thread.state === STATES.pending) { return; } @@ -698,7 +710,7 @@ function isThreadInHoverState(thread) { const pageDimensions = pageEl.getBoundingClientRect(); const pageLeft = pageDimensions.left; - const pageTop = pageDimensions.top + constants.PAGE_PADDING_TOP; + const pageTop = pageDimensions.top + PAGE_PADDING_TOP; this.createHighlightDialog.show(pageEl); if (!this.isMobile) { @@ -721,9 +733,9 @@ function isThreadInHoverState(thread) { let activeThread = null; // Destroy any pending highlights on click outside the highlight - const pendingThreads = this.getThreadsWithStates(constants.PENDING_STATES); + const pendingThreads = this.getThreadsWithStates(PENDING_STATES); pendingThreads.forEach((thread) => { - if (thread.type === constants.ANNOTATION_TYPE_POINT) { + if (thread.type === TYPES.point) { thread.destroy(); } else { thread.cancelFirstComment(); @@ -781,7 +793,7 @@ function isThreadInHoverState(thread) { * @return {void} */ useDefaultCursor() { - this.annotatedElement.classList.add('bp-use-default-cursor'); + this.annotatedElement.classList.add(CLASS_DEFAULT_CURSOR); } /** @@ -791,7 +803,7 @@ function isThreadInHoverState(thread) { * @return {void} */ removeDefaultCursor() { - this.annotatedElement.classList.remove('bp-use-default-cursor'); + this.annotatedElement.classList.remove(CLASS_DEFAULT_CURSOR); } /** @@ -817,7 +829,7 @@ function isThreadInHoverState(thread) { showHighlightsOnPage(page) { // Clear context if needed const pageEl = this.annotatedElement.querySelector(`[data-page-number="${page}"]`); - const annotationLayerEl = pageEl.querySelector('.bp-annotation-layer'); + const annotationLayerEl = pageEl.querySelector(`.${CLASS_ANNOTATION_LAYER}`); if (annotationLayerEl) { const context = annotationLayerEl.getContext('2d'); context.clearRect(0, 0, annotationLayerEl.width, annotationLayerEl.height); diff --git a/src/lib/annotations/doc/DocHighlightDialog.js b/src/lib/annotations/doc/DocHighlightDialog.js index b909f2c22..87b23e48e 100644 --- a/src/lib/annotations/doc/DocHighlightDialog.js +++ b/src/lib/annotations/doc/DocHighlightDialog.js @@ -3,20 +3,28 @@ import AnnotationDialog from '../AnnotationDialog'; import * as annotatorUtil from '../annotatorUtil'; import * as docAnnotatorUtil from './docAnnotatorUtil'; import { CLASS_HIDDEN, CLASS_ACTIVE } from '../../constants'; -import * as constants from '../annotationConstants'; import { replacePlaceholders, decodeKeydown } from '../../util'; import { ICON_HIGHLIGHT, ICON_HIGHLIGHT_COMMENT } from '../../icons/icons'; +import * as constants from '../annotationConstants'; + +const CLASS_HIGHLIGHT_DIALOG = 'bp-highlight-dialog'; +const CLASS_ANNOTATION_HIGHLIGHT_DIALOG = 'bp-annotation-highlight-dialog'; +const CLASS_TEXT_HIGHLIGHTED = 'bp-is-text-highlighted'; +const CLASS_HIGHLIGHT_LABEL = 'bp-annotation-highlight-label'; +const DATA_TYPE_HIGHLIGHT = 'highlight-btn'; +const DATA_TYPE_ADD_HIGHLIGHT_COMMENT = 'add-highlight-comment-btn'; const HIGHLIGHT_DIALOG_HEIGHT = 38; const PAGE_PADDING_BOTTOM = 15; const PAGE_PADDING_TOP = 15; -@autobind class DocHighlightDialog extends AnnotationDialog { +@autobind +class DocHighlightDialog extends AnnotationDialog { //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- - /** + /** D * Saves an annotation with the associated text or blank if only * highlighting. Only adds an annotation to the dialog if it contains text. * The annotation is still added to the thread on the server side. @@ -29,7 +37,7 @@ const PAGE_PADDING_TOP = 15; // If annotation is blank then display who highlighted the text // Will be displayed as '{name} highlighted' if (annotation.text === '' && annotation.user.id !== '0') { - const highlightLabelEl = this.highlightDialogEl.querySelector('.bp-annotation-highlight-label'); + const highlightLabelEl = this.highlightDialogEl.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`); highlightLabelEl.textContent = replacePlaceholders(__('annotation_who_highlighted'), [ annotation.user.name ]); @@ -120,7 +128,7 @@ const PAGE_PADDING_TOP = 15; // Displays comments dialog and hides highlight annotations button if (commentsDialogIsHidden) { - this.element.classList.remove(constants.CLASS_ANNOTATION_DIALOG_HIGHLIGHT); + this.element.classList.remove(CLASS_HIGHLIGHT_DIALOG); annotatorUtil.hideElement(this.highlightDialogEl); this.element.classList.add(constants.CLASS_ANNOTATION_DIALOG); @@ -136,7 +144,7 @@ const PAGE_PADDING_TOP = 15; this.element.classList.remove(constants.CLASS_ANNOTATION_DIALOG); annotatorUtil.hideElement(this.commentsDialogEl); - this.element.classList.add(constants.CLASS_ANNOTATION_DIALOG_HIGHLIGHT); + this.element.classList.add(CLASS_HIGHLIGHT_DIALOG); annotatorUtil.showElement(this.highlightDialogEl); this.hasComments = false; } @@ -154,8 +162,8 @@ const PAGE_PADDING_TOP = 15; * @return {void} */ toggleHighlightCommentsReply(hasAnnotations) { - const replyTextEl = this.commentsDialogEl.querySelector('[data-section="create"]'); - const commentTextEl = this.commentsDialogEl.querySelector('[data-section="show"]'); + const replyTextEl = this.commentsDialogEl.querySelector(constants.SECTION_CREATE); + const commentTextEl = this.commentsDialogEl.querySelector(constants.SECTION_SHOW); // Ensures that "Add a comment here" text area is shown if (hasAnnotations) { @@ -199,7 +207,7 @@ const PAGE_PADDING_TOP = 15; // Generate HTML of highlight dialog this.highlightDialogEl = this.generateHighlightDialogEl(); - this.highlightDialogEl.classList.add('bp-annotation-highlight-dialog'); + this.highlightDialogEl.classList.add(CLASS_ANNOTATION_HIGHLIGHT_DIALOG); // Generate HTML of comments dialog this.commentsDialogEl = this.generateDialogEl(annotations.length); @@ -215,9 +223,9 @@ const PAGE_PADDING_TOP = 15; } if (!this.isMobile) { - this.element.setAttribute('data-type', 'annotation-dialog'); + this.element.setAttribute('data-type', constants.DATA_TYPE_ANNOTATION_DIALOG); this.element.classList.add(constants.CLASS_ANNOTATION_DIALOG); - this.element.innerHTML = '
'; + this.element.innerHTML = `
`; this.element.appendChild(this.dialogEl); // Adding thread number to dialog @@ -228,14 +236,14 @@ const PAGE_PADDING_TOP = 15; // Indicate that text is highlighted in the highlight buttons dialog if (annotations.length > 0) { - this.dialogEl.classList.add(constants.CLASS_ANNOTATION_TEXT_HIGHLIGHTED); + this.dialogEl.classList.add(CLASS_TEXT_HIGHLIGHTED); } // Checks if highlight is a plain highlight annotation and if // user name has been populated. If userID is 0, user name will // be 'Some User' if (annotatorUtil.isPlainHighlight(annotations) && annotations[0].user.id !== '0') { - const highlightLabelEl = this.highlightDialogEl.querySelector('.bp-annotation-highlight-label'); + const highlightLabelEl = this.highlightDialogEl.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`); highlightLabelEl.textContent = replacePlaceholders(__('annotation_who_highlighted'), [ annotations[0].user.name ]); @@ -244,7 +252,7 @@ const PAGE_PADDING_TOP = 15; // Hide delete button on plain highlights if user doesn't have // permissions if (annotations[0].permissions && !annotations[0].permissions.can_delete) { - const addHighlightBtn = this.highlightDialogEl.querySelector('.bp-add-highlight-btn'); + const addHighlightBtn = this.highlightDialogEl.querySelector(constants.SELECTOR_ADD_HIGHLIGHT_BTN); annotatorUtil.hideElement(addHighlightBtn); } } @@ -322,11 +330,11 @@ const PAGE_PADDING_TOP = 15; switch (dataType) { // Clicking 'Highlight' button to create or remove a highlight - case 'highlight-btn': + case DATA_TYPE_HIGHLIGHT: this.drawAnnotation(); break; // Clicking 'Highlight' button to create a highlight - case 'add-highlight-comment-btn': + case DATA_TYPE_ADD_HIGHLIGHT_COMMENT: this.emit('annotationdraw'); this.toggleHighlightCommentsReply(false); this.toggleHighlightDialogs(); @@ -350,11 +358,11 @@ const PAGE_PADDING_TOP = 15; * @return {void} */ toggleHighlightIcon(fillStyle) { - const addHighlightBtn = this.dialogEl.querySelector('.bp-add-highlight-btn'); - if (fillStyle === constants.HIGHLIGHT_ACTIVE_FILL_STYLE) { - addHighlightBtn.classList.add('highlight-active'); + const addHighlightBtn = this.dialogEl.querySelector(constants.SELECTOR_ADD_HIGHLIGHT_BTN); + if (fillStyle === constants.HIGHLIGHT_FILL.active) { + addHighlightBtn.classList.add(CLASS_ACTIVE); } else { - addHighlightBtn.classList.remove('highlight-active'); + addHighlightBtn.classList.remove(CLASS_ACTIVE); } } @@ -370,12 +378,12 @@ const PAGE_PADDING_TOP = 15; * @return {void} */ toggleHighlight() { - const isTextHighlighted = this.dialogEl.classList.contains(constants.CLASS_ANNOTATION_TEXT_HIGHLIGHTED); + const isTextHighlighted = this.dialogEl.classList.contains(CLASS_TEXT_HIGHLIGHTED); // Creates a blank highlight annotation if (!isTextHighlighted) { this.hasComments = false; - this.dialogEl.classList.add(constants.CLASS_ANNOTATION_TEXT_HIGHLIGHTED); + this.dialogEl.classList.add(CLASS_TEXT_HIGHLIGHTED); this.emit('annotationcreate'); // Deletes blank highlight annotation if user has permission @@ -470,15 +478,15 @@ const PAGE_PADDING_TOP = 15; generateHighlightDialogEl() { const highlightDialogEl = document.createElement('div'); highlightDialogEl.innerHTML = ` - - - - diff --git a/src/lib/annotations/doc/DocHighlightThread.js b/src/lib/annotations/doc/DocHighlightThread.js index e1c4eab88..1f3804fa4 100644 --- a/src/lib/annotations/doc/DocHighlightThread.js +++ b/src/lib/annotations/doc/DocHighlightThread.js @@ -2,8 +2,14 @@ import autobind from 'autobind-decorator'; import AnnotationThread from '../AnnotationThread'; import DocHighlightDialog from './DocHighlightDialog'; import * as annotatorUtil from '../annotatorUtil'; -import * as constants from '../annotationConstants'; import * as docAnnotatorUtil from './docAnnotatorUtil'; +import { + STATES, + TYPES, + CLASS_ANNOTATION_LAYER, + SELECTOR_ADD_HIGHLIGHT_BTN, + HIGHLIGHT_FILL +} from '../annotationConstants'; const PAGE_PADDING_BOTTOM = 15; const PAGE_PADDING_TOP = 15; @@ -32,7 +38,7 @@ const HOVER_TIMEOUT_MS = 75; this.reset(); // Reset type from highlight-comment to highlight - this.type = constants.ANNOTATION_TYPE_HIGHLIGHT; + this.type = TYPES.highlight; } else { this.destroy(); } @@ -47,7 +53,7 @@ const HOVER_TIMEOUT_MS = 75; destroy() { super.destroy(); - if (this.state === constants.ANNOTATION_STATE_PENDING) { + if (this.state === STATES.pending) { window.getSelection().removeAllRanges(); } } @@ -61,7 +67,7 @@ const HOVER_TIMEOUT_MS = 75; * @return {void} */ hide() { - this.draw(constants.HIGHLIGHT_ERASE_FILL_STYLE); + this.draw(HIGHLIGHT_FILL.erase); } /** @@ -71,7 +77,7 @@ const HOVER_TIMEOUT_MS = 75; * @return {void} */ reset() { - this.state = constants.ANNOTATION_STATE_INACTIVE; + this.state = STATES.inactive; this.show(); } @@ -101,7 +107,7 @@ const HOVER_TIMEOUT_MS = 75; // Hide delete button on plain highlights if user doesn't have // permissions if (this.annotations.length && this.annotations[0].permissions && !this.annotations[0].permissions.can_delete) { - const addHighlightBtn = this.dialog.element.querySelector('.bp-add-highlight-btn'); + const addHighlightBtn = this.dialog.element.querySelector(SELECTOR_ADD_HIGHLIGHT_BTN); annotatorUtil.hideElement(addHighlightBtn); } } @@ -113,7 +119,7 @@ const HOVER_TIMEOUT_MS = 75; */ onMousedown() { // Destroy pending highlights on mousedown - if (this.state === constants.ANNOTATION_STATE_PENDING) { + if (this.state === STATES.pending) { this.destroy(); } } @@ -137,7 +143,7 @@ const HOVER_TIMEOUT_MS = 75; // If state is in hover, it means mouse is already over this highlight // so we can skip the is in highlight calculation if (!consumed && this.isOnHighlight(event)) { - this.state = constants.ANNOTATION_STATE_HOVER; + this.state = STATES.hover; return true; } @@ -166,7 +172,7 @@ const HOVER_TIMEOUT_MS = 75; * @return {void} */ activateDialog() { - this.state = constants.ANNOTATION_STATE_HOVER; + this.state = STATES.hover; // Setup the dialog element if it has not already been created if (!this.dialog.element) { @@ -189,10 +195,10 @@ const HOVER_TIMEOUT_MS = 75; // If mouse is in dialog, change state to hover or active-hover if (docAnnotatorUtil.isInDialog(event, this.dialog.element)) { // Keeps dialog open if comment is pending - if (this.state === constants.ANNOTATION_STATE_PENDING_ACTIVE) { + if (this.state === STATES.pending_ACTIVE) { return false; } - this.state = constants.ANNOTATION_STATE_HOVER; + this.state = STATES.hover; // If mouse is in highlight, change state to hover or active-hover } else if (this.isInHighlight(event)) { @@ -200,7 +206,7 @@ const HOVER_TIMEOUT_MS = 75; // No-op // If mouse is not in highlight and state is not already inactive, reset - } else if (this.state !== constants.ANNOTATION_STATE_INACTIVE) { + } else if (this.state !== STATES.inactive) { // Add timeout before resettting highlight to inactive so // hovering over line breaks doesn't cause flickering this.hoverTimeoutHandler = setTimeout(() => { @@ -231,17 +237,17 @@ const HOVER_TIMEOUT_MS = 75; */ show() { switch (this.state) { - case constants.ANNOTATION_STATE_PENDING: + case STATES.pending: this.showDialog(); break; - case constants.ANNOTATION_STATE_INACTIVE: + case STATES.inactive: this.hideDialog(); - this.draw(constants.HIGHLIGHT_NORMAL_FILL_STYLE); + this.draw(HIGHLIGHT_FILL.normal); break; - case constants.ANNOTATION_STATE_HOVER: - case constants.ANNOTATION_STATE_PENDING_ACTIVE: + case STATES.hover: + case STATES.pending_ACTIVE: this.showDialog(); - this.draw(constants.HIGHLIGHT_ACTIVE_FILL_STYLE); + this.draw(HIGHLIGHT_FILL.active); break; default: break; @@ -266,11 +272,8 @@ const HOVER_TIMEOUT_MS = 75; // Ensures that previously created annotations have the right type if (this.annotations.length) { - if ( - (this.annotations[0].text !== '' || this.annotations.length > 1) && - this.type === constants.ANNOTATION_TYPE_HIGHLIGHT - ) { - this.type = constants.ANNOTATION_TYPE_HIGHLIGHT_COMMENT; + if ((this.annotations[0].text !== '' || this.annotations.length > 1) && this.type === TYPES.highlight) { + this.type = TYPES.highlight_comment; } } } @@ -300,23 +303,23 @@ const HOVER_TIMEOUT_MS = 75; bindCustomListenersOnDialog() { // Annotation drawn this.dialog.addListener('annotationdraw', () => { - this.state = constants.ANNOTATION_STATE_PENDING_ACTIVE; + this.state = STATES.pending_ACTIVE; window.getSelection().removeAllRanges(); this.show(); }); // Annotation drawn this.dialog.addListener('annotationcommentpending', () => { - this.state = constants.ANNOTATION_STATE_PENDING_ACTIVE; + this.state = STATES.pending_ACTIVE; }); // Annotation created this.dialog.addListener('annotationcreate', (data) => { if (data) { - this.type = constants.ANNOTATION_TYPE_HIGHLIGHT_COMMENT; + this.type = TYPES.highlight_comment; this.dialog.toggleHighlightCommentsReply(this.annotations.length); } else { - this.type = constants.ANNOTATION_TYPE_HIGHLIGHT; + this.type = TYPES.highlight; } this.saveAnnotation(this.type, data ? data.text : ''); @@ -405,12 +408,12 @@ const HOVER_TIMEOUT_MS = 75; // transparency context.save(); context.globalCompositeOperation = 'destination-out'; - context.fillStyle = constants.HIGHLIGHT_ERASE_FILL_STYLE; + context.fillStyle = HIGHLIGHT_FILL.erase; context.fill(); context.restore(); // Draw actual highlight rectangle if needed - if (fillStyle !== constants.HIGHLIGHT_ERASE_FILL_STYLE) { + if (fillStyle !== HIGHLIGHT_FILL.erase) { context.fill(); // Update highlight icon hover to appropriate color @@ -517,10 +520,10 @@ const HOVER_TIMEOUT_MS = 75; return null; } - let annotationLayerEl = pageEl.querySelector('.bp-annotation-layer'); + let annotationLayerEl = pageEl.querySelector(`.${CLASS_ANNOTATION_LAYER}`); if (!annotationLayerEl) { annotationLayerEl = document.createElement('canvas'); - annotationLayerEl.classList.add('bp-annotation-layer'); + annotationLayerEl.classList.add(CLASS_ANNOTATION_LAYER); const pageDimensions = pageEl.getBoundingClientRect(); annotationLayerEl.width = pageDimensions.width; annotationLayerEl.height = pageDimensions.height - PAGE_PADDING_TOP - PAGE_PADDING_BOTTOM; diff --git a/src/lib/annotations/doc/DocPointAnnotator.js b/src/lib/annotations/doc/DocPointAnnotator.js index 2cc8a4b58..b9a66d3af 100644 --- a/src/lib/annotations/doc/DocPointAnnotator.js +++ b/src/lib/annotations/doc/DocPointAnnotator.js @@ -1,12 +1,17 @@ import * as docAnnotatorUtil from './docAnnotatorUtil'; import * as annotatorUtil from '../annotatorUtil'; -import { PAGE_PADDING_BOTTOM, PAGE_PADDING_TOP } from '../annotationConstants'; +import { + DATA_TYPE_ANNOTATION_DIALOG, + DATA_TYPE_ANNOTATION_INDICATOR, + PAGE_PADDING_BOTTOM, + PAGE_PADDING_TOP +} from '../annotationConstants'; export default class PointHighlightAnnotator { /** * Returns an annotation location on a document from the DOM event or null - * if no correct annotation location can be inferred from the event. Return - * the (x, y) coordinates and page the point is on in PDF units with the + * if no correct annotation location can be inferred from the event. Return + * the (x, y) coordinates and page the point is on in PDF units with the * lower left corner of the document as the origin. * * @override @@ -29,7 +34,7 @@ export default class PointHighlightAnnotator { // If click is inside an annotation dialog, ignore const dataType = annotatorUtil.findClosestDataType(eventTarget); - if (dataType === 'annotation-dialog' || dataType === 'annotation-indicator') { + if (dataType === DATA_TYPE_ANNOTATION_DIALOG || dataType === DATA_TYPE_ANNOTATION_INDICATOR) { return null; } diff --git a/src/lib/annotations/doc/DocPointThread.js b/src/lib/annotations/doc/DocPointThread.js index f6fc3ccb9..b759fa903 100644 --- a/src/lib/annotations/doc/DocPointThread.js +++ b/src/lib/annotations/doc/DocPointThread.js @@ -2,8 +2,8 @@ import autobind from 'autobind-decorator'; import AnnotationThread from '../AnnotationThread'; import DocPointDialog from './DocPointDialog'; import * as annotatorUtil from '../annotatorUtil'; -import * as constants from '../annotationConstants'; import * as docAnnotatorUtil from './docAnnotatorUtil'; +import { STATES } from '../annotationConstants'; const PAGE_PADDING_TOP = 15; const POINT_ANNOTATION_ICON_HEIGHT = 31; @@ -56,7 +56,7 @@ const POINT_ANNOTATION_ICON_WIDTH = 24; annotatorUtil.showElement(this.element); - if (this.state === constants.ANNOTATION_STATE_PENDING) { + if (this.state === STATES.pending) { this.showDialog(); } } diff --git a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js index f96bf7f9a..2fab3442d 100644 --- a/src/lib/annotations/doc/__tests__/DocAnnotator-test.js +++ b/src/lib/annotations/doc/__tests__/DocAnnotator-test.js @@ -9,12 +9,18 @@ import DocHighlightThread from '../DocHighlightThread'; import DocPointThread from '../DocPointThread'; import * as annotatorUtil from '../../annotatorUtil'; import * as docAnnotatorUtil from '../docAnnotatorUtil'; -import * as constants from '../../annotationConstants'; +import { + STATES, + TYPES, + DATA_TYPE_ANNOTATION_DIALOG +} from '../../annotationConstants'; let annotator; let stubs = {}; const sandbox = sinon.sandbox.create(); +const CLASS_DEFAULT_CURSOR = 'bp-use-default-cursor'; + describe('lib/annotations/doc/DocAnnotator', () => { before(() => { fixture.setBase('src/lib'); @@ -75,7 +81,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { highlightEls: [] }); - stubs.findClosest = sandbox.stub(annotatorUtil, 'findClosestDataType').returns('annotation-dialog'); + stubs.findClosest = sandbox.stub(annotatorUtil, 'findClosestDataType').returns(DATA_TYPE_ANNOTATION_DIALOG); stubs.scale = sandbox.stub(annotatorUtil, 'getScale').returns(1); // stub highlight methods @@ -86,15 +92,15 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs.restoreSel = sandbox.stub(rangy, 'restoreSelection'); }); - describe('point', () => { + describe(TYPES.point, () => { it('should not return a location if there is a selection present', () => { - expect(annotator.getLocationFromEvent({}, 'point')).to.be.null; + expect(annotator.getLocationFromEvent({}, TYPES.point)).to.be.null; }); it('should not return a location if click isn\'t on page', () => { stubs.selection.returns(false); stubs.getPageInfo.returns({ pageEl: null, page: -1 }); - expect(annotator.getLocationFromEvent({}, 'point')).to.be.null; + expect(annotator.getLocationFromEvent({}, TYPES.point)).to.be.null; }); it('should not return a location if click is on dialog', () => { @@ -103,7 +109,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { pageEl: document.querySelector('.annotated-element'), page: 1 }); - expect(annotator.getLocationFromEvent({}, 'point')).to.be.null; + expect(annotator.getLocationFromEvent({}, TYPES.point)).to.be.null; }); it('should return a valid point location if click is valid', () => { @@ -114,19 +120,19 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs.findClosest.returns('not-a-dialog'); sandbox.stub(docAnnotatorUtil, 'convertDOMSpaceToPDFSpace').returns([x, y]); - const location = annotator.getLocationFromEvent({}, 'point'); + const location = annotator.getLocationFromEvent({}, TYPES.point); expect(location).to.deep.equal({ x, y, page, dimensions }); }); }); - describe('highlight', () => { + describe(TYPES.highlight, () => { beforeEach(() => { annotator.setupAnnotations(); stubs.highlighter = sandbox.mock(annotator.highlighter); }); it('should not return a location if there is no selection present', () => { - expect(annotator.getLocationFromEvent({}, 'highlight')).to.be.null; + expect(annotator.getLocationFromEvent({}, TYPES.highlight)).to.be.null; }); it('should infer page from selection if it cannot be inferred from event', () => { @@ -142,13 +148,13 @@ describe('lib/annotations/doc/DocAnnotator', () => { page: 2 }); - annotator.getLocationFromEvent({}, 'highlight'); + annotator.getLocationFromEvent({}, TYPES.highlight); expect(stubs.getPageInfo).to.be.called.twice; }); it('should not return a valid highlight location if no highlights exist', () => { stubs.getPageInfo.returns({ pageEl: stubs.pageEl, page }); - expect(annotator.getLocationFromEvent({}, 'highlight')).to.deep.equal(null); + expect(annotator.getLocationFromEvent({}, TYPES.highlight)).to.deep.equal(null); }); it('should return a valid highlight location if selection is valid', () => { @@ -159,12 +165,12 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs.getHighlights.returns({ highlight: {}, highlightEls: [{}, {}] }); - const location = annotator.getLocationFromEvent({}, 'highlight'); + const location = annotator.getLocationFromEvent({}, TYPES.highlight); expect(location).to.deep.equal({ page, quadPoints, dimensions }); }); }); - describe('highlight-comment', () => { + describe(TYPES.highlight_comment, () => { beforeEach(() => { annotator.setupAnnotations(); stubs.highlighter = sandbox.mock(annotator.highlighter); @@ -172,7 +178,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { it('should not return a location if there is no selection present', () => { annotator.highlighter.highlights = []; - const location = annotator.getLocationFromEvent({}, 'highlight-comment'); + const location = annotator.getLocationFromEvent({}, TYPES.highlight_comment); expect(location).to.be.null; }); @@ -190,14 +196,14 @@ describe('lib/annotations/doc/DocAnnotator', () => { page: 2 }); - annotator.getLocationFromEvent({}, 'highlight-comment'); + annotator.getLocationFromEvent({}, TYPES.highlight_comment); expect(stubs.getSel).to.have.been.called; }); it('should not return a valid highlight location if no highlights exist', () => { annotator.highlighter.highlights = [{}]; stubs.getPageInfo.returns({ pageEl: stubs.pageEl, page }); - expect(annotator.getLocationFromEvent({}, 'highlight-comment')).to.deep.equal(null); + expect(annotator.getLocationFromEvent({}, TYPES.highlight_comment)).to.deep.equal(null); }); it('should return a valid highlight location if selection is valid', () => { @@ -207,7 +213,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs.points.onSecondCall().returns(quadPoints[1]); stubs.getHighlights.returns({ highlight: {}, highlightEls: [{}, {}] }); - const location = annotator.getLocationFromEvent({}, 'highlight-comment'); + const location = annotator.getLocationFromEvent({}, TYPES.highlight_comment); expect(location).to.deep.equal({ page, quadPoints, dimensions }); }); }); @@ -226,21 +232,21 @@ describe('lib/annotations/doc/DocAnnotator', () => { }); it('should create, add highlight thread to internal map, and return it', () => { - const thread = annotator.createAnnotationThread([], {}, 'highlight'); + const thread = annotator.createAnnotationThread([], {}, TYPES.highlight); expect(stubs.addThread).to.have.been.called; expect(thread instanceof DocHighlightThread).to.be.true; expect(annotator.handleValidationError).to.not.be.called; }); it('should create, add highlight comment thread to internal map, and return it', () => { - const thread = annotator.createAnnotationThread([], {}, 'highlight-comment'); + const thread = annotator.createAnnotationThread([], {}, TYPES.highlight_comment); expect(stubs.addThread).to.have.been.called; expect(thread instanceof DocHighlightThread).to.be.true; expect(annotator.handleValidationError).to.not.be.called; }); it('should create, add point thread to internal map, and return it', () => { - const thread = annotator.createAnnotationThread([], {}, 'point'); + const thread = annotator.createAnnotationThread([], {}, TYPES.point); expect(stubs.addThread).to.have.been.called; expect(thread instanceof DocPointThread).to.be.true; expect(annotator.handleValidationError).to.not.be.called; @@ -251,12 +257,12 @@ describe('lib/annotations/doc/DocAnnotator', () => { const annotation = new Annotation({ fileVersionId: 2, threadID: '1', - type: 'point', + type: TYPES.point, thread: '1', text: 'blah', location: { x: 0, y: 0 } }); - const thread = annotator.createAnnotationThread([annotation], {}, 'highlight'); + const thread = annotator.createAnnotationThread([annotation], {}, TYPES.highlight); expect(stubs.addThread).to.have.been.called; expect(thread.threadID).to.equal(annotation.threadID); @@ -268,7 +274,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { it('should emit error and return undefined if thread params are invalid', () => { stubs.validateThread.returns(false); sandbox.stub(annotator, 'emit'); - const thread = annotator.createAnnotationThread([], {}, 'highlight'); + const thread = annotator.createAnnotationThread([], {}, TYPES.highlight); expect(thread instanceof DocHighlightThread).to.be.false; expect(annotator.handleValidationError).to.be.called; }); @@ -341,7 +347,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, constants.ANNOTATION_TYPE_HIGHLIGHT); + expect(stubs.createAnnotationThread).to.be.calledWith([], location, TYPES.highlight); }); it('should bail out of making an annotation if thread is null', () => { @@ -694,7 +700,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { onMousemove: () => {}, hideDialog: () => {}, show: () => {}, - state: constants.ANNOTATION_STATE_HOVER + state: STATES.hover }; stubs.delayMock = sandbox.mock(stubs.delayThread); @@ -762,7 +768,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs.getThreads.returns([stubs.delayThread]); sandbox.stub(annotator, 'useDefaultCursor'); - stubs.delayThread.state = constants.ANNOTATION_STATE_HOVER; + stubs.delayThread.state = STATES.hover; annotator.mouseMoveEvent = { clientX: 3, clientY: 3 }; annotator.onHighlightCheck(); @@ -781,7 +787,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { }); it('should do nothing if there are pending, pending-active, active, or active hover highlight threads', () => { - stubs.thread.state = constants.ANNOTATION_STATE_PENDING; + stubs.thread.state = STATES.pending; stubs.threadMock.expects('onMousemove').returns(false).never(); stubs.getThreads.returns([stubs.thread]); @@ -935,16 +941,16 @@ describe('lib/annotations/doc/DocAnnotator', () => { }); it('should cancel the first comment of pending threads', () => { - stubs.thread.state = constants.ANNOTATION_STATE_PENDING; + stubs.thread.state = STATES.pending; stubs.getAllThreads.returns([stubs.thread]); // Point annotation - stubs.thread.type = constants.ANNOTATION_TYPE_POINT; + stubs.thread.type = TYPES.point; stubs.threadMock.expects('destroy'); annotator.highlightClickHandler(stubs.event); // Highlight annotation - stubs.thread.type = constants.ANNOTATION_TYPE_HIGHLIGHT; + stubs.thread.type = TYPES.highlight; stubs.threadMock.expects('cancelFirstComment'); annotator.highlightClickHandler(stubs.event); }); @@ -967,26 +973,26 @@ describe('lib/annotations/doc/DocAnnotator', () => { describe('getThreadsWithStates()', () => { it('return all of the threads in the specified state', () => { const thread1 = { - type: 'highlight', - state: constants.ANNOTATION_STATE_HOVER, + type: TYPES.highlight, + state: STATES.hover, unbindCustomListenersOnThread: sandbox.stub(), removeAllListeners: sandbox.stub() }; const thread2 = { - type: 'point', - state: constants.ANNOTATION_STATE_HOVER, + type: TYPES.point, + state: STATES.hover, unbindCustomListenersOnThread: sandbox.stub(), removeAllListeners: sandbox.stub() }; const thread3 = { - type: 'highlight', - state: constants.ANNOTATION_STATE_PENDING, + type: TYPES.highlight, + state: STATES.pending, unbindCustomListenersOnThread: sandbox.stub(), removeAllListeners: sandbox.stub() }; annotator.threads = { 0: [thread1, thread2], 1: [thread3] }; - const threads = annotator.getThreadsWithStates(constants.ANNOTATION_STATE_HOVER); + const threads = annotator.getThreadsWithStates(STATES.hover); expect(threads).to.deep.equal([thread1, thread2]); }); }); @@ -994,21 +1000,21 @@ describe('lib/annotations/doc/DocAnnotator', () => { describe('useDefaultCursor()', () => { it('should use the default cursor instead of the text cursor', () => { annotator.useDefaultCursor(); - expect(annotator.annotatedElement).to.have.class('bp-use-default-cursor'); + expect(annotator.annotatedElement).to.have.class(CLASS_DEFAULT_CURSOR); }); }); describe('removeDefaultCursor()', () => { it('should use the text cursor instead of the default cursor', () => { annotator.removeDefaultCursor(); - expect(annotator.annotatedElement).to.not.have.class('bp-use-default-cursor'); + expect(annotator.annotatedElement).to.not.have.class(CLASS_DEFAULT_CURSOR); }); }); describe('getHighlightThreadsOnPage()', () => { it('return the highlight threads on that page', () => { const thread = { - type: 'highlight', + type: TYPES.highlight, unbindCustomListenersOnThread: sandbox.stub(), removeAllListeners: sandbox.stub() }; @@ -1016,7 +1022,7 @@ describe('lib/annotations/doc/DocAnnotator', () => { stubs.isHighlight = sandbox.stub(annotatorUtil, 'isHighlightAnnotation').returns(thread); const threads = annotator.getHighlightThreadsOnPage(0); - expect(stubs.isHighlight).to.be.calledWith('highlight'); + expect(stubs.isHighlight).to.be.calledWith(TYPES.highlight); expect(threads).to.deep.equal([thread]); }); }); diff --git a/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js b/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js index d9a717970..e4ad32934 100644 --- a/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js +++ b/src/lib/annotations/doc/__tests__/DocHighlightDialog-test.js @@ -4,7 +4,7 @@ import Annotation from '../../Annotation'; import AnnotationDialog from '../../AnnotationDialog'; import * as annotatorUtil from '../../annotatorUtil'; import * as docAnnotatorUtil from '../docAnnotatorUtil'; -import { CLASS_HIDDEN } from '../../../constants'; +import { CLASS_HIDDEN, CLASS_ACTIVE } from '../../../constants'; import * as util from '../../../util'; import * as constants from '../../annotationConstants'; @@ -12,6 +12,10 @@ let dialog; const sandbox = sinon.sandbox.create(); let stubs = {}; +const CLASS_TEXT_HIGHLIGHTED = 'bp-is-text-highlighted'; +const CLASS_HIGHLIGHT_LABEL = 'bp-annotation-highlight-label'; +const DATA_TYPE_HIGHLIGHT_BTN = 'highlight-btn'; +const DATA_TYPE_ADD_HIGHLIGHT_COMMENT = 'add-highlight-comment-btn'; const PAGE_PADDING_TOP = 15; describe('lib/annotations/doc/DocHighlightDialog', () => { @@ -77,7 +81,7 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { }) ); - const highlightLabelEl = dialog.element.querySelector('.bp-annotation-highlight-label'); + const highlightLabelEl = dialog.element.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`); expect(highlightLabelEl).to.contain.html('Bob highlighted'); expect(dialog.position).to.be.called; }); @@ -141,7 +145,7 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { describe('toggleHighlightDialogs()', () => { it('should display comments dialog on toggle when comments dialog is currently hidden', () => { - const commentsDialogEl = dialog.element.querySelector('.annotation-container'); + const commentsDialogEl = dialog.element.querySelector(constants.SELECTOR_ANNOTATION_CONTAINER); commentsDialogEl.classList.add(CLASS_HIDDEN); sandbox.stub(annotatorUtil, 'hideElement'); @@ -154,7 +158,7 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { }); it('should display highlight buttons dialog on toggle when comments dialog is currently shown', () => { - const commentsDialogEl = dialog.element.querySelector('.annotation-container'); + const commentsDialogEl = dialog.element.querySelector(constants.SELECTOR_ANNOTATION_CONTAINER); commentsDialogEl.classList.remove(CLASS_HIDDEN); sandbox.stub(annotatorUtil, 'hideElement'); @@ -169,8 +173,8 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { describe('toggleHighlightCommentsReply()', () => { it('should display "Reply" text area in dialog when multiple comments exist', () => { - const replyTextEl = dialog.commentsDialogEl.querySelector('[data-section="create"]'); - const commentTextEl = dialog.commentsDialogEl.querySelector('[data-section="show"]'); + const replyTextEl = dialog.commentsDialogEl.querySelector(constants.SECTION_CREATE); + const commentTextEl = dialog.commentsDialogEl.querySelector(constants.SECTION_SHOW); sandbox.stub(dialog, 'position'); @@ -181,8 +185,8 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { }); it('should display "Add a comment here" text area in dialog when no comments exist', () => { - const replyTextEl = dialog.commentsDialogEl.querySelector('[data-section="create"]'); - const commentTextEl = dialog.commentsDialogEl.querySelector('[data-section="show"]'); + const replyTextEl = dialog.commentsDialogEl.querySelector(constants.SECTION_CREATE); + const commentTextEl = dialog.commentsDialogEl.querySelector(constants.SECTION_SHOW); sandbox.stub(dialog, 'position'); @@ -271,7 +275,7 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { it('should add the text highlighted class if thread has multiple annotations', () => { dialog.setup([stubs.annotation]); - expect(dialog.dialogEl).to.have.class(constants.CLASS_ANNOTATION_TEXT_HIGHLIGHTED); + expect(dialog.dialogEl).to.have.class(CLASS_TEXT_HIGHLIGHTED); }); it('should setup and show plain highlight dialog', () => { @@ -285,8 +289,8 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { stubs.annotation.permissions.can_delete = false; dialog.setup([stubs.annotation]); - const highlightLabelEl = dialog.highlightDialogEl.querySelector('.bp-annotation-highlight-label'); - const addHighlightBtn = dialog.highlightDialogEl.querySelector('.bp-add-highlight-btn'); + const highlightLabelEl = dialog.highlightDialogEl.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`); + const addHighlightBtn = dialog.highlightDialogEl.querySelector(constants.SELECTOR_ADD_HIGHLIGHT_BTN); expect(stubs.show).to.be.calledWith(highlightLabelEl); expect(stubs.hide).to.be.calledWith(addHighlightBtn); }); @@ -392,14 +396,14 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { }); it('should create/remove a highlight when the \'highlight-btn\' is pressed', () => { - stubs.dataType.returns('highlight-btn'); + stubs.dataType.returns(DATA_TYPE_HIGHLIGHT_BTN); dialog.mousedownHandler(event); expect(stubs.emit).to.be.calledWith('annotationdraw'); expect(dialog.toggleHighlight).to.be.called; }); it('should create highlight when the \'add-highlight-comment-btn\' is pressed', () => { - stubs.dataType.returns('add-highlight-comment-btn'); + stubs.dataType.returns(DATA_TYPE_ADD_HIGHLIGHT_COMMENT); dialog.mousedownHandler(event); expect(stubs.emit).to.be.calledWith('annotationdraw'); expect(dialog.toggleHighlightCommentsReply).to.be.called; @@ -417,31 +421,31 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { describe('toggleHighlightIcon()', () => { it('should display active highlight icon when highlight is active', () => { - const addHighlightBtn = dialog.element.querySelector('.bp-add-highlight-btn'); - dialog.toggleHighlightIcon(constants.HIGHLIGHT_ACTIVE_FILL_STYLE); - expect(addHighlightBtn).to.have.class('highlight-active'); + const addHighlightBtn = dialog.element.querySelector(constants.SELECTOR_ADD_HIGHLIGHT_BTN); + dialog.toggleHighlightIcon(constants.HIGHLIGHT_FILL.active); + expect(addHighlightBtn).to.have.class(CLASS_ACTIVE); }); it('should display normal \'text highlighted\' highlight icon when highlight is not active', () => { - const addHighlightBtn = dialog.element.querySelector('.bp-add-highlight-btn'); - dialog.toggleHighlightIcon(constants.HIGHLIGHT_NORMAL_FILL_STYLE); - expect(addHighlightBtn).to.not.have.class('highlight-active'); + const addHighlightBtn = dialog.element.querySelector(constants.SELECTOR_ADD_HIGHLIGHT_BTN); + dialog.toggleHighlightIcon(constants.HIGHLIGHT_FILL.normal); + expect(addHighlightBtn).to.not.have.class(CLASS_ACTIVE); }); }); describe('toggleHighlight()', () => { it('should delete a blank annotation if text is highlighted', () => { - dialog.dialogEl.classList.add(constants.CLASS_ANNOTATION_TEXT_HIGHLIGHTED); + dialog.dialogEl.classList.add(CLASS_TEXT_HIGHLIGHTED); dialog.toggleHighlight(); expect(dialog.hasComments).to.be.true; expect(stubs.emit).to.be.calledWith('annotationdelete'); }); it('should create a blank annotation if text is not highlighted', () => { - dialog.dialogEl.classList.remove(constants.CLASS_ANNOTATION_TEXT_HIGHLIGHTED); + dialog.dialogEl.classList.remove(CLASS_TEXT_HIGHLIGHTED); dialog.toggleHighlight(); - expect(dialog.dialogEl).to.have.class(constants.CLASS_ANNOTATION_TEXT_HIGHLIGHTED); + expect(dialog.dialogEl).to.have.class(CLASS_TEXT_HIGHLIGHTED); expect(dialog.hasComments).to.be.false; expect(stubs.emit).to.be.calledWith('annotationcreate'); }); @@ -471,7 +475,7 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { describe('getDialogWidth', () => { it('should calculate dialog width once annotator\'s user name has been populated', () => { - const highlightLabelEl = dialog.element.querySelector('.bp-annotation-highlight-label'); + const highlightLabelEl = dialog.element.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`); highlightLabelEl.innerHTML = 'Bob highlighted'; dialog.element.style.width = '100px'; @@ -510,7 +514,7 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { permissions: {} }) ); - const highlight = dialog.element.querySelector('.bp-annotation-highlight-dialog'); + const highlight = dialog.element.querySelector(constants.SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG); const comment = document.querySelector('.annotation-comment'); expect(comment).to.be.null; @@ -521,7 +525,7 @@ describe('lib/annotations/doc/DocHighlightDialog', () => { describe('generateHighlightDialogEl()', () => { it('should return a highlight annotation dialog DOM element', () => { const highlightEl = dialog.generateHighlightDialogEl(); - const highlightBtnEl = highlightEl.querySelector('.bp-annotations-highlight-btns'); + const highlightBtnEl = highlightEl.querySelector(constants.SELECTOR_HIGHLIGHT_BTNS); expect(highlightBtnEl).to.not.be.null; }); }); diff --git a/src/lib/annotations/doc/__tests__/DocHighlightThread-test.js b/src/lib/annotations/doc/__tests__/DocHighlightThread-test.js index 9fcdbc615..52d8bbcbc 100644 --- a/src/lib/annotations/doc/__tests__/DocHighlightThread-test.js +++ b/src/lib/annotations/doc/__tests__/DocHighlightThread-test.js @@ -2,9 +2,13 @@ import DocHighlightDialog from '../DocHighlightDialog'; import DocHighlightThread from '../DocHighlightThread'; import AnnotationService from '../../AnnotationService'; -import * as constants from '../../annotationConstants'; import * as annotatorUtil from '../../annotatorUtil'; import * as docAnnotatorUtil from '../docAnnotatorUtil'; +import { + STATES, + TYPES, + HIGHLIGHT_FILL, +} from '../../annotationConstants'; let highlightThread; const sandbox = sinon.sandbox.create(); @@ -73,7 +77,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { describe('destroy()', () => { it('should destroy the thread', () => { - highlightThread.state = constants.ANNOTATION_STATE_PENDING; + highlightThread.state = STATES.pending; // This stubs out a parent method by forcing the method we care about // in the prototype of the prototype of DocHighlightThread (ie @@ -105,7 +109,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { highlightThread.reset(); expect(highlightThread.show).to.be.called; - assert.equal(highlightThread.state, constants.ANNOTATION_STATE_INACTIVE); + assert.equal(highlightThread.state, STATES.inactive); }); }); @@ -118,7 +122,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { value: sandbox.stub() }); - highlightThread.saveAnnotation(constants.ANNOTATION_TYPE_HIGHLIGHT, ''); + highlightThread.saveAnnotation(TYPES.highlight, ''); }); it('should save a highlight comment annotation', () => { @@ -129,7 +133,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { value: sandbox.stub() }); - highlightThread.saveAnnotation(constants.ANNOTATION_TYPE_HIGHLIGHT, 'bleh'); + highlightThread.saveAnnotation(TYPES.highlight, 'bleh'); }); }); @@ -190,7 +194,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { describe('onMousedown()', () => { it('should destroy the thread when annotation is in pending state', () => { - highlightThread.state = constants.ANNOTATION_STATE_PENDING; + highlightThread.state = STATES.pending; sandbox.stub(highlightThread, 'destroy'); @@ -202,18 +206,18 @@ describe('lib/annotations/doc/DocHighlightThread', () => { describe('onClick()', () => { it('should set annotation to inactive if event has already been consumed', () => { - highlightThread.state = constants.ANNOTATION_STATE_HOVER; - highlightThread.type = constants.ANNOTATION_TYPE_HIGHLIGHT_COMMENT; + highlightThread.state = STATES.hover; + highlightThread.type = TYPES.highlight_comment; const isHighlightPending = highlightThread.onClick({}, true); expect(isHighlightPending).to.be.false; - expect(highlightThread.state).to.equal(constants.ANNOTATION_STATE_INACTIVE); + expect(highlightThread.state).to.equal(STATES.inactive); }); it('should set annotation to hover if mouse is hovering over highlight or dialog', () => { - highlightThread.state = constants.ANNOTATION_STATE_PENDING; - highlightThread.type = constants.ANNOTATION_TYPE_HIGHLIGHT_COMMENT; + highlightThread.state = STATES.pending; + highlightThread.type = TYPES.highlight_comment; sandbox.stub(highlightThread, 'isOnHighlight').returns(true); sandbox.stub(highlightThread, 'reset'); @@ -221,7 +225,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { expect(isHighlightPending).to.be.true; expect(highlightThread.reset).to.not.be.called; - expect(highlightThread.state).to.equal(constants.ANNOTATION_STATE_HOVER); + expect(highlightThread.state).to.equal(STATES.hover); }); }); @@ -257,11 +261,11 @@ describe('lib/annotations/doc/DocHighlightThread', () => { it('should set to hover and trigger dialog mouseenter event if thread is not in the active or active-hover state', () => { sandbox.stub(docAnnotatorUtil, 'isInDialog').returns(true); sandbox.stub(highlightThread.dialog, 'mouseenterHandler'); - highlightThread.state = constants.ANNOTATION_STATE_INACTIVE; + highlightThread.state = STATES.inactive; highlightThread.activateDialog(); - assert.equal(highlightThread.state, constants.ANNOTATION_STATE_HOVER); + assert.equal(highlightThread.state, STATES.hover); expect(highlightThread.dialog.mouseenterHandler).to.be.called; }); @@ -280,19 +284,19 @@ describe('lib/annotations/doc/DocHighlightThread', () => { it('should delay drawing highlight if mouse is hovering over a highlight dialog and not pending comment', () => { sandbox.stub(highlightThread, 'getPageEl').returns(highlightThread.annotatedElement); sandbox.stub(docAnnotatorUtil, 'isInDialog').returns(true); - highlightThread.state = constants.ANNOTATION_STATE_INACTIVE; + highlightThread.state = STATES.inactive; const result = highlightThread.onMousemove({}); expect(result).to.be.true; - expect(highlightThread.state).to.equal(constants.ANNOTATION_STATE_HOVER); + expect(highlightThread.state).to.equal(STATES.hover); }); it('should do nothing if mouse is hovering over a highlight dialog and pending comment', () => { sandbox.stub(highlightThread, 'getPageEl').returns(highlightThread.annotatedElement); sandbox.stub(docAnnotatorUtil, 'isInDialog').returns(true); sandbox.stub(highlightThread, 'activateDialog'); - highlightThread.state = constants.ANNOTATION_STATE_PENDING_ACTIVE; + highlightThread.state = STATES.pending_ACTIVE; const result = highlightThread.onMousemove({}); @@ -305,7 +309,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { sandbox.stub(docAnnotatorUtil, 'isInDialog').returns(false); sandbox.stub(highlightThread, 'isInHighlight').returns(true); sandbox.stub(highlightThread, 'activateDialog'); - highlightThread.state = constants.ANNOTATION_STATE_HOVER; + highlightThread.state = STATES.hover; const result = highlightThread.onMousemove({}); @@ -317,7 +321,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { sandbox.stub(highlightThread, 'getPageEl').returns(highlightThread.annotatedElement); sandbox.stub(docAnnotatorUtil, 'isInDialog').returns(false); sandbox.stub(highlightThread, 'isInHighlight').returns(false); - highlightThread.state = constants.ANNOTATION_STATE_HOVER; + highlightThread.state = STATES.hover; const result = highlightThread.onMousemove({}); @@ -329,11 +333,11 @@ describe('lib/annotations/doc/DocHighlightThread', () => { sandbox.stub(highlightThread, 'getPageEl').returns(highlightThread.annotatedElement); sandbox.stub(docAnnotatorUtil, 'isInDialog').returns(false); sandbox.stub(highlightThread, 'isInHighlight').returns(false); - highlightThread.state = constants.ANNOTATION_STATE_INACTIVE; + highlightThread.state = STATES.inactive; const result = highlightThread.onMousemove({}); - assert.equal(highlightThread.state, constants.ANNOTATION_STATE_INACTIVE); + assert.equal(highlightThread.state, STATES.inactive); expect(result).to.be.false; }); }); @@ -342,7 +346,7 @@ describe('lib/annotations/doc/DocHighlightThread', () => { it('should show the dialog if the state is pending', () => { sandbox.stub(highlightThread, 'showDialog'); - highlightThread.state = constants.ANNOTATION_STATE_PENDING; + highlightThread.state = STATES.pending; highlightThread.show(); expect(highlightThread.showDialog).to.be.called; @@ -352,22 +356,22 @@ describe('lib/annotations/doc/DocHighlightThread', () => { sandbox.stub(highlightThread, 'hideDialog'); sandbox.stub(highlightThread, 'draw'); - highlightThread.state = constants.ANNOTATION_STATE_INACTIVE; + highlightThread.state = STATES.inactive; highlightThread.show(); expect(highlightThread.hideDialog).to.be.called; - expect(highlightThread.draw).to.be.calledWith(constants.HIGHLIGHT_NORMAL_FILL_STYLE); + expect(highlightThread.draw).to.be.calledWith(HIGHLIGHT_FILL.normal); }); it('should show the dialog if the state is not pending and redraw the highlight as active', () => { sandbox.stub(highlightThread, 'showDialog'); sandbox.stub(highlightThread, 'draw'); - highlightThread.state = constants.ANNOTATION_STATE_HOVER; + highlightThread.state = STATES.hover; highlightThread.show(); expect(highlightThread.showDialog).to.be.called; - expect(highlightThread.draw).to.be.calledWith(constants.HIGHLIGHT_ACTIVE_FILL_STYLE); + expect(highlightThread.draw).to.be.calledWith(HIGHLIGHT_FILL.active); }); it('should do nothing if state is invalid', () => { diff --git a/src/lib/annotations/doc/__tests__/DocPointThread-test.js b/src/lib/annotations/doc/__tests__/DocPointThread-test.js index 0e6070e0e..ba30b0147 100644 --- a/src/lib/annotations/doc/__tests__/DocPointThread-test.js +++ b/src/lib/annotations/doc/__tests__/DocPointThread-test.js @@ -3,8 +3,8 @@ import DocPointDialog from '../DocPointDialog'; import DocPointThread from '../DocPointThread'; import AnnotationThread from '../../AnnotationThread'; import * as annotatorUtil from '../../annotatorUtil'; -import * as constants from '../../annotationConstants'; import * as docAnnotatorUtil from '../docAnnotatorUtil'; +import { STATES } from '../../annotationConstants'; let pointThread; const sandbox = sinon.sandbox.create(); @@ -93,7 +93,7 @@ describe('lib/annotations/doc/DocPointThread', () => { sandbox.stub(annotatorUtil, 'showElement'); sandbox.stub(pointThread, 'showDialog'); - pointThread.state = constants.ANNOTATION_STATE_PENDING; + pointThread.state = STATES.pending; pointThread.show(); expect(pointThread.showDialog).to.have.been.called; @@ -104,7 +104,7 @@ describe('lib/annotations/doc/DocPointThread', () => { sandbox.stub(annotatorUtil, 'showElement'); sandbox.stub(pointThread, 'showDialog'); - pointThread.state = constants.ANNOTATION_STATE_INACTIVE; + pointThread.state = STATES.inactive; pointThread.show(); expect(pointThread.showDialog).to.not.have.been.called; diff --git a/src/lib/annotations/doc/__tests__/docAnnotatorUtil-test.js b/src/lib/annotations/doc/__tests__/docAnnotatorUtil-test.js index 8cb257844..a5a82af1f 100644 --- a/src/lib/annotations/doc/__tests__/docAnnotatorUtil-test.js +++ b/src/lib/annotations/doc/__tests__/docAnnotatorUtil-test.js @@ -11,8 +11,11 @@ import { getBrowserCoordinatesFromLocation, getLowerRightCornerOfLastQuadPoint } from '../docAnnotatorUtil'; - -const DIALOG_CLASS = '.bp-annotation-dialog'; +import { + SELECTOR_ANNOTATION_DIALOG, + SELECTOR_ANNOTATION_CONTAINER, + CLASS_ANNOTATION_DIALOG +} from '../../annotationConstants'; describe('lib/annotations/doc/docAnnotatorUtil', () => { before(() => { @@ -49,13 +52,13 @@ describe('lib/annotations/doc/docAnnotatorUtil', () => { }); it('should return true if the event is in the given dialog', () => { - const dialogEl = document.querySelector(DIALOG_CLASS); + const dialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); const result = isInDialog({ clientX: 8, clientY: 8 }, dialogEl); expect(result).to.be.true; }); it('should return false if the event is in the given dialog', () => { - const dialogEl = document.querySelector(DIALOG_CLASS); + const dialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); const result = isInDialog({ clientX: 100, clientY: 100 }, dialogEl); expect(result).to.be.false; }); @@ -63,7 +66,7 @@ describe('lib/annotations/doc/docAnnotatorUtil', () => { describe('hasActiveDialog()', () => { it('should return false if no annotation dialog is open', () => { - const currDialogEl = document.querySelector(DIALOG_CLASS); + const currDialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); currDialogEl.classList.add('bp-is-hidden'); const result = hasActiveDialog(document); expect(result).to.be.false; @@ -71,11 +74,11 @@ describe('lib/annotations/doc/docAnnotatorUtil', () => { it('should return true if an annotion dialog is open', () => { const docEl = document.querySelector('.annotatedElement'); - const currDialogEl = document.querySelector(DIALOG_CLASS); + const currDialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); currDialogEl.classList.add('bp-is-hidden'); const openDialogEl = document.createElement('div'); - openDialogEl.classList.add('bp-annotation-dialog'); + openDialogEl.classList.add(CLASS_ANNOTATION_DIALOG); docEl.appendChild(openDialogEl); const result = hasActiveDialog(document); @@ -89,13 +92,13 @@ describe('lib/annotations/doc/docAnnotatorUtil', () => { docEl.classList.add('bp-doc-presentation'); docEl.style.height = 100; - const dialogEl = document.querySelector(DIALOG_CLASS); + const dialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG); const pageHeight = 20; const yPos = 5; fitDialogHeightInPage(docEl, dialogEl, pageHeight, yPos); - const annotationsEl = dialogEl.querySelector('.annotation-container'); + const annotationsEl = dialogEl.querySelector(SELECTOR_ANNOTATION_CONTAINER); expect(annotationsEl.style.maxHeight).to.not.be.undefined; }); }); diff --git a/src/lib/annotations/doc/docAnnotatorUtil.js b/src/lib/annotations/doc/docAnnotatorUtil.js index d1d8c18dc..a89080c50 100644 --- a/src/lib/annotations/doc/docAnnotatorUtil.js +++ b/src/lib/annotations/doc/docAnnotatorUtil.js @@ -1,5 +1,11 @@ import * as annotatorUtil from '../annotatorUtil'; -import { PAGE_PADDING_TOP, PAGE_PADDING_BOTTOM } from '../annotationConstants'; +import { + CLASS_ANNOTATION_DIALOG, + CLASS_ANNOTATION_HIGHLIGHT_DIALOG, + SELECTOR_ANNOTATION_CONTAINER, + PAGE_PADDING_TOP, + PAGE_PADDING_BOTTOM +} from '../annotationConstants'; const PREVIEW_PRESENTATION_CLASS = 'bp-doc-presentation'; const HEIGHT_PADDING = 30; @@ -61,8 +67,8 @@ export function isInDialog(event, dialogEl) { * @return {boolean} Whether or not a dialog is active */ export function hasActiveDialog(annotatedEl) { - const commentsDialogEl = annotatedEl.querySelector('.bp-annotation-dialog:not(.bp-is-hidden)'); - const highlightDialogEl = annotatedEl.querySelector('.bp-highlight-dialog:not(.bp-is-hidden)'); + const commentsDialogEl = annotatedEl.querySelector(`.${CLASS_ANNOTATION_DIALOG}:not(.bp-is-hidden)`); + const highlightDialogEl = annotatedEl.querySelector(`.${CLASS_ANNOTATION_HIGHLIGHT_DIALOG}:not(.bp-is-hidden)`); return !!(commentsDialogEl || highlightDialogEl); } @@ -85,7 +91,7 @@ export function fitDialogHeightInPage(annotatedElement, dialogEl, pageHeight, di const topPadding = (wrapperHeight - pageHeight) / 2; const maxHeight = wrapperHeight - dialogY - topPadding - HIGHLIGHT_DIALOG_HEIGHT; - const annotationsEl = dialogEl.querySelector('.annotation-container'); + const annotationsEl = dialogEl.querySelector(SELECTOR_ANNOTATION_CONTAINER); annotationsEl.style.maxHeight = `${maxHeight}px`; } } diff --git a/src/lib/annotations/image/ImageAnnotator.js b/src/lib/annotations/image/ImageAnnotator.js index 1006897f4..f85fdfe1b 100644 --- a/src/lib/annotations/image/ImageAnnotator.js +++ b/src/lib/annotations/image/ImageAnnotator.js @@ -3,6 +3,7 @@ import Annotator from '../Annotator'; import ImagePointThread from './ImagePointThread'; import * as annotatorUtil from '../annotatorUtil'; import * as imageAnnotatorUtil from './imageAnnotatorUtil'; +import { CLASS_ANNOTATION_POINT_BUTTON } from '../annotationConstants'; const IMAGE_NODE_NAME = 'img'; // Selector for image container OR multi-image container @@ -122,7 +123,7 @@ const ANNOTATED_ELEMENT_SELECTOR = '.bp-image, .bp-images-wrapper'; */ hideAllAnnotations() { const annotateButton = this.previewUI.getAnnotateButton(); - const annotations = this.annotatedElement.getElementsByClassName('bp-point-annotation-btn'); + const annotations = this.annotatedElement.getElementsByClassName(CLASS_ANNOTATION_POINT_BUTTON); for (let i = 0; i < annotations.length; i++) { annotatorUtil.hideElement(annotations[i]); } @@ -137,7 +138,7 @@ const ANNOTATED_ELEMENT_SELECTOR = '.bp-image, .bp-images-wrapper'; */ showAllAnnotations() { const annotateButton = this.previewUI.getAnnotateButton(); - const annotations = this.annotatedElement.getElementsByClassName('bp-point-annotation-btn'); + const annotations = this.annotatedElement.getElementsByClassName(CLASS_ANNOTATION_POINT_BUTTON); for (let i = 0; i < annotations.length; i++) { annotatorUtil.showElement(annotations[i]); } diff --git a/src/lib/annotations/image/ImagePointDialog.js b/src/lib/annotations/image/ImagePointDialog.js index 0182f2e54..8df482668 100644 --- a/src/lib/annotations/image/ImagePointDialog.js +++ b/src/lib/annotations/image/ImagePointDialog.js @@ -3,6 +3,7 @@ import AnnotationDialog from '../AnnotationDialog'; import * as annotatorUtil from '../annotatorUtil'; import * as imageAnnotatorUtil from './imageAnnotatorUtil'; +const IMAGE_NODE_NAME = 'img'; const PAGE_PADDING_TOP = 15; const POINT_ANNOTATION_ICON_HEIGHT = 31; const POINT_ANNOTATION_ICON_DOT_HEIGHT = 8; @@ -33,7 +34,7 @@ const POINT_ANNOTATION_ICON_DOT_HEIGHT = 8; // Get image tag inside viewer, based on page number. All images are page 1 by default. const imageEl = this.annotatedElement.querySelector(`[data-page-number="${this.location.page || 1}"]`) || - this.annotatedElement.querySelector('img'); + this.annotatedElement.querySelector(IMAGE_NODE_NAME); // Center middle of dialog with point - this coordinate is with respect to the page let dialogLeftX = browserX - dialogWidth / 2; diff --git a/src/lib/annotations/image/ImagePointThread.js b/src/lib/annotations/image/ImagePointThread.js index 19bce61c0..c2f70bc03 100644 --- a/src/lib/annotations/image/ImagePointThread.js +++ b/src/lib/annotations/image/ImagePointThread.js @@ -3,7 +3,7 @@ import AnnotationThread from '../AnnotationThread'; import ImagePointDialog from './ImagePointDialog'; import * as annotatorUtil from '../annotatorUtil'; import * as imageAnnotatorUtil from './imageAnnotatorUtil'; -import * as constants from '../annotationConstants'; +import { STATES } from '../annotationConstants'; const POINT_ANNOTATION_ICON_HEIGHT = 31; const POINT_ANNOTATION_ICON_DOT_HEIGHT = 8; @@ -33,7 +33,7 @@ const POINT_ANNOTATION_ICON_WIDTH = 24; annotatorUtil.showElement(this.element); - if (this.state === constants.ANNOTATION_STATE_PENDING) { + if (this.state === STATES.pending) { this.showDialog(); } } diff --git a/src/lib/annotations/image/__tests__/ImageAnnotator-test.js b/src/lib/annotations/image/__tests__/ImageAnnotator-test.js index 14fc0adb0..43835c322 100644 --- a/src/lib/annotations/image/__tests__/ImageAnnotator-test.js +++ b/src/lib/annotations/image/__tests__/ImageAnnotator-test.js @@ -3,6 +3,7 @@ import ImageAnnotator from '../ImageAnnotator'; import ImagePointThread from '../ImagePointThread'; import * as annotatorUtil from '../../annotatorUtil'; import * as imageAnnotatorUtil from '../imageAnnotatorUtil'; +import { SELECTOR_ANNOTATION_POINT_BUTTON } from '../../annotationConstants'; let annotator; const sandbox = sinon.sandbox.create(); @@ -101,7 +102,7 @@ describe('lib/annotations/image/ImageAnnotator', () => { describe('hideAllAnnotations()', () => { it('should hide all annotations on image', () => { annotator.hideAllAnnotations(); - const annotation = document.querySelector('.bp-point-annotation-btn'); + const annotation = document.querySelector(SELECTOR_ANNOTATION_POINT_BUTTON); const classList = Array.from(annotation.classList); expect(classList).to.include('bp-is-hidden'); }); @@ -110,7 +111,7 @@ describe('lib/annotations/image/ImageAnnotator', () => { describe('showAllAnnotations()', () => { it('should show all annotations on image', () => { annotator.showAllAnnotations(); - const annotation = document.querySelector('.bp-point-annotation-btn'); + const annotation = document.querySelector(SELECTOR_ANNOTATION_POINT_BUTTON); const classList = Array.from(annotation.classList); expect(classList).to.not.include('bp-is-hidden'); }); diff --git a/src/lib/annotations/image/__tests__/ImagePointThread-test.js b/src/lib/annotations/image/__tests__/ImagePointThread-test.js index 6cbd71550..37f03d6e4 100644 --- a/src/lib/annotations/image/__tests__/ImagePointThread-test.js +++ b/src/lib/annotations/image/__tests__/ImagePointThread-test.js @@ -2,7 +2,7 @@ import ImagePointDialog from '../ImagePointDialog'; import ImagePointThread from '../ImagePointThread'; import * as annotatorUtil from '../../annotatorUtil'; -import * as constants from '../../annotationConstants'; +import { STATES } from '../../annotationConstants'; import * as imageAnnotatorUtil from '../imageAnnotatorUtil'; let pointThread; @@ -50,7 +50,7 @@ describe('lib/annotations/image/ImagePointThread', () => { sandbox.stub(annotatorUtil, 'showElement'); sandbox.stub(pointThread, 'showDialog'); - pointThread.state = constants.ANNOTATION_STATE_PENDING; + pointThread.state = STATES.pending; pointThread.show(); expect(pointThread.showDialog).to.have.been.called; @@ -61,7 +61,7 @@ describe('lib/annotations/image/ImagePointThread', () => { sandbox.stub(annotatorUtil, 'showElement'); sandbox.stub(pointThread, 'showDialog'); - pointThread.state = constants.ANNOTATION_STATE_INACTIVE; + pointThread.state = STATES.inactive; pointThread.show(); expect(pointThread.showDialog).to.not.have.been.called;