diff --git a/dev-server/documents/html/z-index.mustache b/dev-server/documents/html/z-index.mustache
new file mode 100644
index 00000000000..2a4a1956618
--- /dev/null
+++ b/dev-server/documents/html/z-index.mustache
@@ -0,0 +1,26 @@
+
+
+
+
+ Hypothesis Client Test
+
+
+
+ z-index test page
+ This has a z-index of 0
+
+ - This has a z-index of 25000
+ - This has a z-index of 30000
+ - This has a z-index of 20000 (from parent)
+ - This has a z-index of 20000 (from parent)
+ - This has a z-index of 20000 (from parent)
+
+ This has a z-index of 0
+
+ {{{hypothesisScript}}}
+
+
\ No newline at end of file
diff --git a/dev-server/templates/index.mustache b/dev-server/templates/index.mustache
index 41a652a9a3c..8702b386b4d 100644
--- a/dev-server/templates/index.mustache
+++ b/dev-server/templates/index.mustache
@@ -18,6 +18,7 @@
Poems and Songs of Robert Burns
The Disappearance of Lady Carfax, by Arthur Conan Doyle
Anna Karenina, by Leo Tolstoy
+ z-index page
Test PDF documents
diff --git a/src/annotator/adder.js b/src/annotator/adder.js
index c64b9ee975c..5c64b2af48d 100644
--- a/src/annotator/adder.js
+++ b/src/annotator/adder.js
@@ -89,10 +89,6 @@ export class Adder {
// take position out of layout flow initially
position: 'absolute',
top: 0,
-
- // Assign a high Z-index so that the adder shows above any content on the
- // page
- zIndex: 10000,
});
this._view = /** @type {Window} */ (container.ownerDocument.defaultView);
@@ -193,6 +189,51 @@ export class Adder {
return { top, left, arrowDirection };
}
+ /**
+ * Find a Z index value that will cause the adder to appear on top of any
+ * content in the document when the adder is shown at (left, top).
+ *
+ * @param {number} left - Horizontal offset from left edge of viewport.
+ * @param {number} top - Vertical offset from top edge of viewport.
+ * @return {number} - greatest zIndex (default value of 1)
+ */
+ _findZindex(left, top) {
+ if (document.elementsFromPoint === undefined) {
+ // In case of not being able to use `document.elementsFromPoint`,
+ // default to the large arbitrary number (2^15)
+ return 32768;
+ }
+
+ const adderWidth = this._width();
+ const adderHeight = this._height();
+
+ // Find the Z index of all the elements in the screen for five positions
+ // around the adder (left-top, left-bottom, middle-center, right-top,
+ // right-bottom) and use the greatest.
+
+ // Unique elements so `getComputedStyle` is called the minimum amount of times.
+ const elements = new Set([
+ ...document.elementsFromPoint(left, top),
+ ...document.elementsFromPoint(left, top + adderHeight),
+ ...document.elementsFromPoint(
+ left + adderWidth / 2,
+ top + adderHeight / 2
+ ),
+ ...document.elementsFromPoint(left + adderWidth, top),
+ ...document.elementsFromPoint(left + adderWidth, top + adderHeight),
+ ]);
+
+ const zIndexes = [...elements]
+ .map(element => +getComputedStyle(element).zIndex)
+ .filter(Number.isInteger);
+
+ // Make sure the array contains at least one element,
+ // otherwise `Math.max(...[])` results in +Infinity
+ zIndexes.push(0);
+
+ return Math.max(...zIndexes) + 1;
+ }
+
/**
* Show the adder at the given position and with the arrow pointing in
* `arrowDirection`.
@@ -210,9 +251,12 @@ export class Adder {
const positionedAncestor = nearestPositionedAncestor(this._container);
const parentRect = positionedAncestor.getBoundingClientRect();
+ const zIndex = this._findZindex(left, top);
+
Object.assign(this._container.style, {
- top: toPx(top - parentRect.top),
left: toPx(left - parentRect.left),
+ top: toPx(top - parentRect.top),
+ zIndex,
});
this._isVisible = true;
diff --git a/src/annotator/test/adder-test.js b/src/annotator/test/adder-test.js
index 71db1fb6790..75aac1ccfbe 100644
--- a/src/annotator/test/adder-test.js
+++ b/src/annotator/test/adder-test.js
@@ -1,4 +1,6 @@
import { act } from 'preact/test-utils';
+import { createElement } from 'preact';
+import { mount } from 'enzyme';
import { Adder, ARROW_POINTING_UP, ARROW_POINTING_DOWN } from '../adder';
@@ -217,6 +219,95 @@ describe('Adder', () => {
});
});
+ describe('adder Z index', () => {
+ let container;
+
+ function getAdderZIndex(left, top) {
+ adderCtrl.showAt(left, top);
+ return parseInt(adderEl.style.zIndex);
+ }
+
+ beforeEach(() => {
+ container = document.createElement('div');
+ document.body.appendChild(container);
+ });
+
+ afterEach(() => {
+ container.remove();
+ });
+
+ it('returns default hard coded value if `document.elementsFromPoint` is not available', () => {
+ const elementsFromPointBackup = document.elementsFromPoint;
+ document.elementsFromPoint = undefined;
+ assert.strictEqual(getAdderZIndex(0, 0), 32768);
+ document.elementsFromPoint = elementsFromPointBackup;
+ });
+
+ it('returns default value of 1', () => {
+ // Even if not elements are found, it returns 1
+ assert.strictEqual(getAdderZIndex(-100000, -100000), 1);
+ assert.strictEqual(getAdderZIndex(100000, 100000), 1);
+ });
+
+ it('returns the greatest zIndex', () => {
+ const createComponent = (left, top, zIndex, attachTo) =>
+ mount(
+ ,
+ { attachTo }
+ );
+
+ const wrapper = createComponent(0, 0, 2, container);
+ assert.strictEqual(getAdderZIndex(0, 0), 3);
+
+ const initLeft = 10;
+ const initTop = 10;
+ const adderWidth = adderCtrl._width();
+ const adderHeight = adderCtrl._height();
+ const wrapperDOMNode = wrapper.getDOMNode();
+
+ // Create first element (left-top)
+ createComponent(initLeft, initTop, 3, wrapperDOMNode);
+ assert.strictEqual(getAdderZIndex(initLeft, initTop), 4);
+
+ // Create second element (left-bottom)
+ createComponent(initLeft, initTop + adderHeight, 5, wrapperDOMNode);
+ assert.strictEqual(getAdderZIndex(initLeft, initTop), 6);
+
+ // Create third element (middle-center)
+ createComponent(
+ initLeft + adderWidth / 2,
+ initTop + adderHeight / 2,
+ 6,
+ wrapperDOMNode
+ );
+ assert.strictEqual(getAdderZIndex(initLeft, initTop), 7);
+
+ // Create fourth element (right-top)
+ createComponent(initLeft + adderWidth, initTop, 7, wrapperDOMNode);
+ assert.strictEqual(getAdderZIndex(initLeft, initTop), 8);
+
+ // Create third element (right-bottom)
+ createComponent(
+ initLeft + adderWidth,
+ initTop + adderHeight,
+ 8,
+ wrapperDOMNode
+ );
+ assert.strictEqual(getAdderZIndex(initLeft, initTop), 9);
+
+ wrapper.unmount();
+ });
+ });
+
describe('#showAt', () => {
context('when the document and body elements have no offset', () => {
it('shows adder at target position', () => {