diff --git a/functional-tests/helpers/constants.js b/functional-tests/helpers/constants.js index 52bc1304a..c43c65404 100644 --- a/functional-tests/helpers/constants.js +++ b/functional-tests/helpers/constants.js @@ -1,3 +1,4 @@ +// Preview CSS constants const CLASS_ACTIVE = 'bp-is-active'; exports.SELECTOR_ACTIVE = `.${CLASS_ACTIVE}`; const CLASS_HIDDEN = 'bp-is-hidden'; @@ -13,6 +14,7 @@ const CLASS_BUTTON_PLAIN = 'bp-btn-plain'; exports.SELECTOR_BUTTON_PLAIN = `.${CLASS_BUTTON_PLAIN}`; const CLASS_BUTTON_PRIMARY = 'bp-btn-primary'; exports.SELECTOR_BUTTON_PRIMARY = `.${CLASS_BUTTON_PRIMARY}`; + const CLASS_BOX_PREVIEW_HEADER = 'bp-header'; exports.SELECTOR_BOX_PREVIEW_HEADER = `.${CLASS_BOX_PREVIEW_HEADER}`; const CLASS_BOX_PREVIEW_BASE_HEADER = 'bp-base-header'; @@ -28,15 +30,17 @@ const CLASS_PREVIEW_PRESENTATION = 'bp-doc-presentation'; exports.SELECTOR_PREVIEW_PRESENTATION = `.${CLASS_PREVIEW_PRESENTATION}`; // Annotation CSS constants -const CLASS_ANNOTATIONS_LOADED = 'ba-annotations-loaded'; -exports.SELECTOR_ANNOTATIONS_LOADED = `.${CLASS_ANNOTATIONS_LOADED}`; const CLASS_ANNOTATED_ELEMENT = 'annotated-element'; exports.SELECTOR_ANNOTATED_ELEMENT = `.${CLASS_ANNOTATED_ELEMENT}`; +const CLASS_ANNOTATIONS_LOADED = 'ba-annotations-loaded'; +exports.SELECTOR_ANNOTATIONS_LOADED = `.${CLASS_ANNOTATIONS_LOADED}`; const CLASS_ANNOTATION_POINT_MARKER = 'ba-point-annotation-marker'; exports.SELECTOR_ANNOTATION_POINT_MARKER = `.${CLASS_ANNOTATION_POINT_MARKER}`; const CLASS_ANNOTATION_POINT_BUTTON = 'ba-point-annotation-btn'; exports.SELECTOR_ANNOTATION_POINT_BUTTON = `.${CLASS_ANNOTATION_POINT_BUTTON}`; -const CLASS_HIGHLIGHT_QUAD_CORNER = 'ba-quad-corner-container'; +const CLASS_HIGHLIGHT_QUAD_CORNER_CONTAINER = 'ba-quad-corner-container'; +exports.SELECTOR_HIGHLIGHT_QUAD_CORNER_CONTAINER = `.${CLASS_HIGHLIGHT_QUAD_CORNER_CONTAINER}`; +const CLASS_HIGHLIGHT_QUAD_CORNER = 'ba-quad-corner'; exports.SELECTOR_HIGHLIGHT_QUAD_CORNER = `.${CLASS_HIGHLIGHT_QUAD_CORNER}`; // Dialog CSS constants @@ -85,6 +89,12 @@ exports.SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG = `.${CLASS_ANNOTATION_HIGHLIGHT_DI const CLASS_ANNOTATION_PLAIN_HIGHLIGHT = 'ba-plain-highlight'; exports.SELECTOR_ANNOTATION_PLAIN_HIGHLIGHT = `.${CLASS_ANNOTATION_PLAIN_HIGHLIGHT}`; +const CLASS_HIGHLIGHT_DIALOG = 'ba-highlight-dialog'; +exports.SELECTOR_HIGHLIGHT_DIALOG = `.${CLASS_HIGHLIGHT_DIALOG}`; +const CLASS_TEXT_HIGHLIGHTED = 'ba-is-text-highlighted'; +exports.SELECTOR_TEXT_HIGHLIGHTED = `.${CLASS_TEXT_HIGHLIGHTED}`; +const CLASS_HIGHLIGHT_LABEL = 'ba-annotation-highlight-label'; +exports.SELECTOR_HIGHLIGHT_LABEL = `.${CLASS_HIGHLIGHT_LABEL}`; const CLASS_HIGHLIGHT_BTNS = 'ba-annotation-highlight-btns'; exports.SELECTOR_HIGHLIGHT_BTNS = `.${CLASS_HIGHLIGHT_BTNS}`; const CLASS_ADD_HIGHLIGHT_BTN = 'ba-add-highlight-btn'; diff --git a/functional-tests/helpers/mouseEvents.js b/functional-tests/helpers/mouseEvents.js new file mode 100644 index 000000000..64e8bad3c --- /dev/null +++ b/functional-tests/helpers/mouseEvents.js @@ -0,0 +1,54 @@ +/** + * Selects text on the document + * + * @param {Object} I - the codeceptjs I + * @param {string} selector - the selector to use + * + * @return {void} + */ +function selectText(I, selector) { + I.waitForElement(selector); + + I.executeScript( + /* eslint-disable prefer-arrow-callback, no-var, func-names */ + function (sel) { + const preview = document.querySelector('.bp-doc'); + const selectionEl = document.querySelector(sel); + const start = selectionEl.firstElementChild; + const end = selectionEl.lastElementChild; + + const selection = window.getSelection(); + selection.removeAllRanges(); + + /** + * Cross browser event creation + * @param {string} eventName the event name + * @param {HTMLElement} el - the DOM element to use + * @return {Event} the event + */ + function createNewEvent(eventName, el) { + const x = el.offsetLeft + 2; + const y = el.offsetTop + 2; + + let event; + if (typeof MouseEvent === 'function') { + event = new MouseEvent(eventName, { + bubbles: true, + clientX: x, + clientY: y + }); + } + + return event; + } + + preview.dispatchEvent(createNewEvent('mousedown', start)); + preview.dispatchEvent(createNewEvent('mousemove', end)); + selection.setBaseAndExtent(start, 0, end, 0); + preview.dispatchEvent(createNewEvent('mouseup', end)); + }, + selector + ); +} + +exports.selectText = selectText; \ No newline at end of file diff --git a/functional-tests/tests/plain_highlight.js b/functional-tests/tests/plain_highlight.js new file mode 100644 index 000000000..d2d9fa6ea --- /dev/null +++ b/functional-tests/tests/plain_highlight.js @@ -0,0 +1,45 @@ +const { + SELECTOR_ANNOTATIONS_LOADED, + SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG, + SELECTOR_ANNOTATION_DIALOG, + SELECTOR_ADD_HIGHLIGHT_BTN, + SELECTOR_HIGHLIGHT_LABEL +} = require('../helpers/constants'); + +const { selectText } = require('../helpers/mouseEvents'); + +Feature('Plain Highlight Annotation Sanity'); + +Before((I) => { + I.amOnPage('/'); +}); + +Scenario('Create a new plain highlight annotation @desktop @enabled', (I) => { + I.waitForVisible(SELECTOR_ANNOTATIONS_LOADED); + + I.say('Highlight dialog should appear after selecting text'); + selectText(I, '.textLayer'); + I.waitForVisible(SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG); + + I.say('Highlight selected text'); + I.click(SELECTOR_ADD_HIGHLIGHT_BTN); + + I.say('Highlight should be created and dialog should appear'); + I.waitForVisible(SELECTOR_ANNOTATION_DIALOG); + I.waitForText('Kanye West highlighted', 5, SELECTOR_HIGHLIGHT_LABEL); + I.waitForEnabled(SELECTOR_ADD_HIGHLIGHT_BTN); +}); + +Scenario('Delete the plain highlight annotation @desktop @enabled', (I) => { + I.waitForVisible(SELECTOR_ANNOTATIONS_LOADED); + + I.say('Highlight dialog should appear on click'); + I.click('.textLayer div'); + I.waitForVisible(SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG); + + I.say('Delete the highlight annotation'); + I.click(SELECTOR_ADD_HIGHLIGHT_BTN); + + I.say('Highlight should be deleted'); + I.waitForDetached(SELECTOR_ANNOTATION_DIALOG, 5); +}); diff --git a/functional-tests/views/index.pug b/functional-tests/views/index.pug index cbe3392cd..8bfa13c2c 100644 --- a/functional-tests/views/index.pug +++ b/functional-tests/views/index.pug @@ -16,11 +16,8 @@ html div(class='preview-container') script. var options = { - Image: { - enabledTypes: ['point'] - }, Document: { - enabledTypes: ['point', 'draw'] + enabledTypes: ['highlight'] } }; var boxAnnotations = new BoxAnnotations(options); diff --git a/src/constants.js b/src/constants.js index 4c93ded7a..5cfdb0030 100644 --- a/src/constants.js +++ b/src/constants.js @@ -91,6 +91,12 @@ export const SELECTOR_ANNOTATION_HIGHLIGHT_DIALOG = `.${CLASS_ANNOTATION_HIGHLIG export const CLASS_ANNOTATION_PLAIN_HIGHLIGHT = 'ba-plain-highlight'; export const SELECTOR_ANNOTATION_PLAIN_HIGHLIGHT = `.${CLASS_ANNOTATION_PLAIN_HIGHLIGHT}`; +export const CLASS_HIGHLIGHT_DIALOG = 'ba-highlight-dialog'; +export const SELECTOR_HIGHLIGHT_DIALOG = `.${CLASS_HIGHLIGHT_DIALOG}`; +export const CLASS_TEXT_HIGHLIGHTED = 'ba-is-text-highlighted'; +export const SELECTOR_TEXT_HIGHLIGHTED = `.${CLASS_TEXT_HIGHLIGHTED}`; +export const CLASS_HIGHLIGHT_LABEL = 'ba-annotation-highlight-label'; +export const SELECTOR_HIGHLIGHT_LABEL = `.${CLASS_HIGHLIGHT_LABEL}`; export const CLASS_HIGHLIGHT_BTNS = 'ba-annotation-highlight-btns'; export const SELECTOR_HIGHLIGHT_BTNS = `.${CLASS_HIGHLIGHT_BTNS}`; export const CLASS_ADD_HIGHLIGHT_BTN = 'ba-add-highlight-btn'; diff --git a/src/doc/DocHighlightDialog.js b/src/doc/DocHighlightDialog.js index 307d35c11..9ca1ab94c 100644 --- a/src/doc/DocHighlightDialog.js +++ b/src/doc/DocHighlightDialog.js @@ -4,10 +4,6 @@ import * as docUtil from './docUtil'; import { ICON_HIGHLIGHT, ICON_HIGHLIGHT_COMMENT } from '../icons/icons'; import * as constants from '../constants'; -const CLASS_HIGHLIGHT_DIALOG = 'ba-highlight-dialog'; -const CLASS_TEXT_HIGHLIGHTED = 'ba-is-text-highlighted'; -const CLASS_HIGHLIGHT_LABEL = 'ba-annotation-highlight-label'; - const HIGHLIGHT_DIALOG_HEIGHT = 38; const PAGE_PADDING_BOTTOM = 15; const PAGE_PADDING_TOP = 15; @@ -40,7 +36,7 @@ class DocHighlightDialog extends AnnotationDialog { // 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(`.${CLASS_HIGHLIGHT_LABEL}`); + const highlightLabelEl = this.highlightDialogEl.querySelector(`.${constants.CLASS_HIGHLIGHT_LABEL}`); highlightLabelEl.textContent = util.replacePlaceholders(this.localized.whoHighlighted, [ annotation.user.name ]); @@ -106,7 +102,7 @@ class DocHighlightDialog extends AnnotationDialog { util.hideElement(this.commentsDialogEl); - this.element.classList.add(CLASS_HIGHLIGHT_DIALOG); + this.element.classList.add(constants.CLASS_HIGHLIGHT_DIALOG); util.showElement(this.highlightDialogEl); this.hasComments = false; } @@ -187,7 +183,7 @@ class DocHighlightDialog extends AnnotationDialog { // Displays comments dialog and hides highlight annotations button if (commentsDialogIsHidden) { - this.element.classList.remove(CLASS_HIGHLIGHT_DIALOG); + this.element.classList.remove(constants.CLASS_HIGHLIGHT_DIALOG); util.hideElement(this.highlightDialogEl); this.element.classList.add(constants.CLASS_ANNOTATION_DIALOG); @@ -202,7 +198,7 @@ class DocHighlightDialog extends AnnotationDialog { this.element.classList.remove(constants.CLASS_ANNOTATION_DIALOG); util.hideElement(this.commentsDialogEl); - this.element.classList.add(CLASS_HIGHLIGHT_DIALOG); + this.element.classList.add(constants.CLASS_HIGHLIGHT_DIALOG); util.showElement(this.highlightDialogEl); this.hasComments = false; } @@ -301,14 +297,14 @@ class DocHighlightDialog extends AnnotationDialog { // Indicate that text is highlighted in the highlight buttons dialog if (firstAnnotation) { - this.dialogEl.classList.add(CLASS_TEXT_HIGHLIGHTED); + this.dialogEl.classList.add(constants.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 (util.isPlainHighlight(annotations) && firstAnnotation && firstAnnotation.user.id !== '0') { - const highlightLabelEl = this.highlightDialogEl.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`); + const highlightLabelEl = this.highlightDialogEl.querySelector(`.${constants.CLASS_HIGHLIGHT_LABEL}`); highlightLabelEl.textContent = util.replacePlaceholders(this.localized.whoHighlighted, [ firstAnnotation.user.name ]); @@ -442,12 +438,12 @@ class DocHighlightDialog extends AnnotationDialog { * @return {void} */ toggleHighlight() { - const isTextHighlighted = this.dialogEl.classList.contains(CLASS_TEXT_HIGHLIGHTED); + const isTextHighlighted = this.dialogEl.classList.contains(constants.CLASS_TEXT_HIGHLIGHTED); // Creates a blank highlight annotation if (!isTextHighlighted) { this.hasComments = false; - this.dialogEl.classList.add(CLASS_TEXT_HIGHLIGHTED); + this.dialogEl.classList.add(constants.CLASS_TEXT_HIGHLIGHTED); this.emit('annotationcreate'); // Deletes blank highlight annotation if user has permission @@ -529,7 +525,7 @@ class DocHighlightDialog extends AnnotationDialog { const highlightDialogEl = document.createElement('div'); const highlightLabelEl = document.createElement('span'); - highlightLabelEl.classList.add(CLASS_HIGHLIGHT_LABEL); + highlightLabelEl.classList.add(constants.CLASS_HIGHLIGHT_LABEL); highlightLabelEl.classList.add(constants.CLASS_HIDDEN); highlightDialogEl.appendChild(highlightLabelEl); diff --git a/src/doc/__tests__/DocHighlightDialog-test.js b/src/doc/__tests__/DocHighlightDialog-test.js index 5596cf7c4..7578f8d2a 100644 --- a/src/doc/__tests__/DocHighlightDialog-test.js +++ b/src/doc/__tests__/DocHighlightDialog-test.js @@ -10,9 +10,6 @@ let dialog; const sandbox = sinon.sandbox.create(); let stubs = {}; -const CLASS_HIGHLIGHT_DIALOG = 'ba-highlight-dialog'; -const CLASS_TEXT_HIGHLIGHTED = 'ba-is-text-highlighted'; -const CLASS_HIGHLIGHT_LABEL = 'ba-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; @@ -84,7 +81,7 @@ describe('doc/DocHighlightDialog', () => { }) ); - const highlightLabelEl = dialog.element.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`); + const highlightLabelEl = dialog.element.querySelector(`.${constants.CLASS_HIGHLIGHT_LABEL}`); expect(highlightLabelEl).to.contain.html('Bob highlighted'); expect(dialog.position).to.be.called; }); @@ -219,7 +216,7 @@ describe('doc/DocHighlightDialog', () => { }; dialog.hideCommentsDialog(); - expect(dialog.element.classList.contains(CLASS_HIGHLIGHT_DIALOG)).to.be.true; + expect(dialog.element.classList.contains(constants.CLASS_HIGHLIGHT_DIALOG)).to.be.true; }); it('should show the highlight dialog', () => { @@ -424,7 +421,7 @@ describe('doc/DocHighlightDialog', () => { it('should add the text highlighted class if thread has multiple annotations', () => { dialog.setup([stubs.annotation], false); - expect(dialog.dialogEl).to.have.class(CLASS_TEXT_HIGHLIGHTED); + expect(dialog.dialogEl).to.have.class(constants.CLASS_TEXT_HIGHLIGHTED); }); it('should setup and show plain highlight dialog', () => { @@ -581,17 +578,17 @@ describe('doc/DocHighlightDialog', () => { describe('toggleHighlight()', () => { it('should delete a blank annotation if text is highlighted', () => { - dialog.dialogEl.classList.add(CLASS_TEXT_HIGHLIGHTED); + dialog.dialogEl.classList.add(constants.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(CLASS_TEXT_HIGHLIGHTED); + dialog.dialogEl.classList.remove(constants.CLASS_TEXT_HIGHLIGHTED); dialog.toggleHighlight(); - expect(dialog.dialogEl).to.have.class(CLASS_TEXT_HIGHLIGHTED); + expect(dialog.dialogEl).to.have.class(constants.CLASS_TEXT_HIGHLIGHTED); expect(dialog.hasComments).to.be.false; expect(stubs.emit).to.be.calledWith('annotationcreate'); });