Skip to content

Commit

Permalink
Fix: Create highlight dialog is misaligned on first highlight (#159)
Browse files Browse the repository at this point in the history
  • Loading branch information
pramodsum authored Apr 6, 2018
1 parent e6e619c commit d4d4fdd
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 62 deletions.
15 changes: 13 additions & 2 deletions src/__tests__/util-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ import {
isInDialog,
focusTextArea,
hasValidBoundaryCoordinates,
generateMobileDialogEl
generateMobileDialogEl,
getDialogWidth
} from '../util';
import {
STATES,
Expand All @@ -53,7 +54,8 @@ import {
CLASS_MOBILE_DIALOG_HEADER,
CLASS_DIALOG_CLOSE,
CLASS_ANNOTATION_PLAIN_HIGHLIGHT,
CLASS_ANIMATE_DIALOG
CLASS_ANIMATE_DIALOG,
CLASS_HIDDEN
} from '../constants';

const DIALOG_WIDTH = 81;
Expand Down Expand Up @@ -845,4 +847,13 @@ describe('util', () => {
expect(el).to.not.have.class(`.${CLASS_ANIMATE_DIALOG}`);
});
});

describe('getDialogWidth', () => {
it('should calculate dialog width without displaying the dialog', () => {
const dialogEl = document.querySelector(SELECTOR_ANNOTATION_DIALOG);
dialogEl.style.width = '100px';
expect(getDialogWidth(dialogEl)).to.equal(100);
expect(dialogEl).to.have.class(CLASS_HIDDEN);
});
});
});
2 changes: 1 addition & 1 deletion src/controllers/AnnotationModeController.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class AnnotationModeController extends EventEmitter {
* @return {void}
*/
unregisterThread(thread) {
if (!thread || !thread.location || !thread.location.page) {
if (!thread || !thread.location || !thread.location.page || !this.threads[thread.location.page]) {
return;
}

Expand Down
15 changes: 9 additions & 6 deletions src/doc/CreateHighlightDialog.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import CreateAnnotationDialog from '../CreateAnnotationDialog';
import { ICON_HIGHLIGHT, ICON_HIGHLIGHT_COMMENT } from '../icons/icons';
import { generateBtn, repositionCaret, getPageInfo } from '../util';
import { generateBtn, repositionCaret, getPageInfo, getDialogWidth, showElement } from '../util';
import { getDialogCoordsFromRange } from '../doc/docUtil';
import {
CREATE_EVENT,
Expand Down Expand Up @@ -120,14 +120,14 @@ class CreateHighlightDialog extends CreateAnnotationDialog {
const dialogParentEl = this.isMobile ? newParentEl : pageInfo.pageEl;
super.show(dialogParentEl);

if (!this.isMobile) {
this.setPosition(selection);
}

// Add to parent if it hasn't been added already
if (!this.parentEl.querySelector(`.${CLASS_CREATE_DIALOG}`)) {
this.parentEl.appendChild(this.containerEl);
}

if (!this.isMobile) {
this.setPosition(selection);
}
}

/** @inheritdoc */
Expand Down Expand Up @@ -188,7 +188,8 @@ class CreateHighlightDialog extends CreateAnnotationDialog {
return;
}

const dialogX = this.position.x - 1 - this.containerEl.clientWidth / 2;
const dialogWidth = getDialogWidth(this.containerEl);
const dialogX = this.position.x - 1 - dialogWidth / 2;
const xPos = repositionCaret(
this.containerEl,
dialogX,
Expand All @@ -201,6 +202,8 @@ class CreateHighlightDialog extends CreateAnnotationDialog {
this.containerEl.style.left = `${xPos}px`;
// Plus 5 pixels for caret
this.containerEl.style.top = `${this.position.y + 5}px`;

showElement(this.containerEl);
}

/**
Expand Down
33 changes: 1 addition & 32 deletions src/doc/DocHighlightDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class DocHighlightDialog extends AnnotationDialog {
const [browserX, browserY] = this.getScaledPDFCoordinates(pageDimensions, pageHeight);
pageEl.appendChild(this.element);

const highlightDialogWidth = this.getDialogWidth();
const highlightDialogWidth = util.getDialogWidth(this.element);

let dialogX = browserX - highlightDialogWidth / 2; // Center dialog
// Shorten extra transparent border top if showing comments dialog
Expand Down Expand Up @@ -470,37 +470,6 @@ class DocHighlightDialog extends AnnotationDialog {
}
}

/**
* Calculates the dialog width if the highlighter's name is to be displayed
* in the annotations dialog
*
* @private
* @return {number} Annotations dialog width
*/
getDialogWidth() {
// Ensure dialog will not be displayed off the page when
// calculating the dialog width
const prevDialogX = this.element.style.left;
this.element.style.left = 0;

// Switches to 'visibility: hidden' to ensure that dialog takes up
// DOM space while still being invisible
util.hideElementVisibility(this.element);
util.showElement(this.element);

this.highlightDialogWidth = this.element.getBoundingClientRect().width;

// Switches back to 'display: none' to so that it no longer takes up place
// in the DOM while remaining hidden
util.hideElement(this.element);
util.showInvisibleElement(this.element);

// Reset dialog left positioning
this.element.style.left = prevDialogX;

return this.highlightDialogWidth;
}

/**
* Get scaled coordinates for the lower center point of the highlight if the
* highlight has comments or the lower right corner of the highlight for
Expand Down
11 changes: 10 additions & 1 deletion src/doc/__tests__/CreateHighlightDialog-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,22 @@ describe('doc/CreateHighlightDialog', () => {

describe('updatePosition()', () => {
beforeEach(() => {
dialog.show();
sandbox.stub(util, 'showElement');
dialog.isMobile = false;
});

it('should do nothing on mobile devices', () => {
dialog.isMobile = true;
dialog.updatePosition();
expect(util.showElement).to.not.be.called;
});

it('should update the top of the ui element', () => {
const y = 50;
dialog.position.y = y;
dialog.updatePosition();
expect(dialog.containerEl.style.top).to.equal(`${y + 5}px`);
expect(util.showElement).to.be.called;
});

it('should update the left of the ui element, to center it', () => {
Expand All @@ -202,6 +210,7 @@ describe('doc/CreateHighlightDialog', () => {
sandbox.stub(util, 'repositionCaret').returns(x);
dialog.updatePosition();
expect(dialog.containerEl.style.left).to.equal(`${x}px`);
expect(util.showElement).to.be.called;
});
});

Expand Down
21 changes: 1 addition & 20 deletions src/doc/__tests__/DocHighlightDialog-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ describe('doc/DocHighlightDialog', () => {
describe('position()', () => {
beforeEach(() => {
stubs.scaled = sandbox.stub(dialog, 'getScaledPDFCoordinates').returns([150, 2]);
stubs.width = sandbox.stub(dialog, 'getDialogWidth');
stubs.width = sandbox.stub(util, 'getDialogWidth');
stubs.caret = sandbox.stub(util, 'repositionCaret');
stubs.show = sandbox.stub(util, 'showElement');
stubs.fit = sandbox.stub(dialog, 'fitDialogHeightInPage');
Expand Down Expand Up @@ -619,25 +619,6 @@ describe('doc/DocHighlightDialog', () => {
});
});

describe('getDialogWidth', () => {
it('should calculate dialog width once annotator\'s user name has been populated', () => {
const highlightLabelEl = dialog.element.querySelector(`.${CLASS_HIGHLIGHT_LABEL}`);
highlightLabelEl.innerHTML = 'Bob highlighted';
dialog.element.style.width = '100px';
dialog.element.style.left = '30px';

const width = dialog.getDialogWidth();
expect(width).to.equal(100);
expect(dialog.element.style.left).to.equal('30px');
});

it('should return previously set dialog width if already calculated', () => {
dialog.element.style.width = '252px';
const width = dialog.getDialogWidth();
expect(width).to.equal(252); // Default comments dialog width
});
});

describe('getScaledPDFCoordinates()', () => {
it('should lower right corner coordinates of dialog when a highlight does not have comments', () => {
dialog.hasComments = false;
Expand Down
29 changes: 29 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -835,3 +835,32 @@ export function generateMobileDialogEl() {

return el;
}

/**
* Calculates the dialog width when visible
*
* @param {HTMLElement} dialogEl - Annotation dialog element
* @return {number} Annotations dialog width
*/
export function getDialogWidth(dialogEl) {
const element = dialogEl;

// Switches to 'visibility: hidden' to ensure that dialog takes up
// DOM space while still being invisible
hideElementVisibility(element);
showElement(element);

// Ensure dialog will not be displayed off the page when
// calculating the dialog width
element.style.left = 0;

const boundingRect = element.getBoundingClientRect();
const dialogWidth = boundingRect.width;

// Switches back to 'display: none' to so that it no longer takes up place
// in the DOM while remaining hidden
hideElement(element);
showInvisibleElement(element);

return dialogWidth;
}

0 comments on commit d4d4fdd

Please sign in to comment.