diff --git a/app/assets/javascripts/copyToClipboard.js b/app/assets/javascripts/copyToClipboard.js
index 344ee6716..c4d10b4f7 100644
--- a/app/assets/javascripts/copyToClipboard.js
+++ b/app/assets/javascripts/copyToClipboard.js
@@ -66,10 +66,6 @@
});
});
- if ('stickAtBottomWhenScrolling' in window.NotifyModules) {
- window.NotifyModules.stickAtBottomWhenScrolling.recalculate();
- }
-
};
};
diff --git a/app/assets/javascripts/enhancedTextbox.js b/app/assets/javascripts/enhancedTextbox.js
index 5bfce9670..7a279034f 100644
--- a/app/assets/javascripts/enhancedTextbox.js
+++ b/app/assets/javascripts/enhancedTextbox.js
@@ -68,9 +68,6 @@
this.textbox.style.height = Math.max(this.initialHeight, backgroundHeight) + 'px';
- if ('stickAtBottomWhenScrolling' in window.NotifyModules) {
- window.NotifyModules.stickAtBottomWhenScrolling.recalculate();
- }
};
diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js
index 41933efbb..a7cd56fbb 100644
--- a/app/assets/javascripts/index.js
+++ b/app/assets/javascripts/index.js
@@ -1,10 +1,6 @@
// Webpack Entry Point
// Vendor libraries
-import $ from 'jquery';
-window.jQuery = window.$ = $;
-
-import './jquery-expose.js';
import 'query-command-supported';
import 'textarea-caret';
import * as cbor from 'cbor-js';
@@ -77,7 +73,6 @@ import './collapsibleCheckboxes.js';
import './updateStatus.js';
import './main.js';
import './listEntry.js';
-import './stick-to-window-when-scrolling.js';
import './totalMessagesChart.js';
import './activityChart.js';
import './job-polling.js';
diff --git a/app/assets/javascripts/jquery-expose.js b/app/assets/javascripts/jquery-expose.js
deleted file mode 100644
index 11eb53e1a..000000000
--- a/app/assets/javascripts/jquery-expose.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// Expose jQuery and other libraries to window for legacy code
-if (typeof jQuery !== 'undefined') {
- window.jQuery = window.$ = jQuery;
-}
-if (typeof getCaretCoordinates !== 'undefined') {
- window.getCaretCoordinates = getCaretCoordinates;
-}
diff --git a/app/assets/javascripts/liveSearch.js b/app/assets/javascripts/liveSearch.js
index 469b34575..01e44ead7 100644
--- a/app/assets/javascripts/liveSearch.js
+++ b/app/assets/javascripts/liveSearch.js
@@ -63,11 +63,6 @@
liveRegion.textContent = resultsSummary(results);
}
- // make sticky JS recalculate its cache of the element's position
- // because live search can change the height document
- if (window.NotifyModules && 'stickAtBottomWhenScrolling' in window.NotifyModules) {
- window.NotifyModules.stickAtBottomWhenScrolling.recalculate();
- }
};
diff --git a/app/assets/javascripts/stick-to-window-when-scrolling.js b/app/assets/javascripts/stick-to-window-when-scrolling.js
deleted file mode 100644
index f04dc8058..000000000
--- a/app/assets/javascripts/stick-to-window-when-scrolling.js
+++ /dev/null
@@ -1,995 +0,0 @@
-;(function (window) {
- 'use strict';
-
- var $ = window.jQuery;
- var NotifyModules = window.NotifyModules || {};
- var _mode = 'default';
-
- // Constructor to make objects representing the area sticky elements can scroll in
- var ScrollArea = function (el, edge, selector) {
- var $el = el.$fixedEl;
- var $scrollArea = $el.closest('.sticky-scroll-area');
-
- if($scrollArea.length === 0) {
- $scrollArea = $el.parent();
- $scrollArea.addClass('sticky-scroll-area');
- }
-
- this._els = [el];
- this.edge = edge;
- this.selector = selector;
- this.node = $scrollArea.get(0);
- this.setEvents();
- };
- ScrollArea.prototype.addEl = function (el) {
- this._els.push(el);
- };
- ScrollArea.prototype.hasEl = function (el) {
- return $.inArray(el, this._els) !== -1;
- };
- ScrollArea.prototype.updateEls = function (usedEls) {
- this._els = usedEls;
- };
- ScrollArea.prototype.setEvents = function () {
- this.node.addEventListener('focus', this.focusHandler.bind(this), true);
- $(this.node).on('keyup', 'textarea', this.focusHandler.bind(this));
- };
- ScrollArea.prototype.removeEvents = function () {
- this.node.removeEventListener('focus', this.focusHandler.bind(this));
- $(this.node).find('textarea').off('keyup', 'textarea', this.focusHandler.bind(this));
- };
- ScrollArea.prototype.getFocusedDetails = {
- forElement: function ($focusedElement) {
- var focused = {
- 'top': $focusedElement.offset().top,
- 'height': $focusedElement.outerHeight(),
- 'type': 'element'
- };
- focused.bottom = focused.top + focused.height;
-
- return focused;
- },
- forCaret: function ($textarea) {
- var textarea = $textarea.get(0);
- var caretCoordinates = window.getCaretCoordinates(textarea, textarea.selectionEnd);
- var focused = {
- 'top': $textarea.offset().top + caretCoordinates.top,
- 'height': caretCoordinates.height,
- 'type': 'caret'
- };
-
- focused.bottom = focused.top + focused.height;
-
- return focused;
- }
- };
- ScrollArea.prototype.focusHandler = function (e) {
- this.scrollToRevealElement($(document.activeElement));
- };
- ScrollArea.prototype.scrollToRevealElement = function ($el) {
- var nodeName = $el.get(0).nodeName.toLowerCase();
- var endOfFurthestEl = focusOverlap.endOfFurthestEl(this._els, this.edge);
- var isInSticky = function () {
- return $el.closest(this.selector).length > 0;
- }.bind(this);
- var focused;
- var overlap;
-
- // if textarea is focused, we care about checking the caret, not the whole element
- if (nodeName === 'textarea') {
- focused = this.getFocusedDetails.forCaret($el);
- } else {
- if (isInSticky()) { return; }
- focused = this.getFocusedDetails.forElement($el);
- }
-
- overlap = focusOverlap.getOverlap(focused, this.edge, endOfFurthestEl);
-
- if (overlap > 0) {
- focusOverlap.adjustForOverlap(focused, this.edge, overlap);
- }
- };
- ScrollArea.prototype.destroy = function () {
- this.removeEvents();
- };
-
- // Object collecting together methods for interacting with scrollareas
- var scrollAreas = {
- _scrollAreas: [],
- getAreaForEl: function (el) {
- var loopIdx = this._scrollAreas.length;
-
- while(loopIdx--) {
- if (this._scrollAreas[loopIdx].hasEl(el)) {
- return this._scrollAreas[loopIdx];
- }
- }
-
- return false;
- },
- getAreaByEl: function (el) {
- var matches = $.grep(this._scrollAreas, function (area) {
- return $.inArray(el, area.els) !== -1;
- });
-
- return matches[0] || false;
- },
- addEl: function (el, edge, selector) {
- var scrollArea = this.getAreaForEl(el);
-
- if (!scrollArea) {
- this._scrollAreas.push(new ScrollArea(el, edge, selector));
- } else {
- scrollArea.addEl(el);
- }
- },
- syncEls: function (elsInDOM) {
- var self = this;
- var unusedAreas = [];
-
- var getUsed = function (area) {
- var used = [];
-
- $.each(elsInDOM, function (elIdx, el) {
- if (area.hasEl(el)) {
- used.push(el);
- }
- });
-
- return used;
- };
-
- var deleteUnused = function (idx, areaIdx) {
- // remove any events for overlap checking bound to the scrollArea
- self._scrollAreas[areaIdx].destroy();
- self._scrollAreas.splice(areaIdx, 1);
- };
-
- // update any scroll areas with els still in the DOM and track any with none
- $.each(this._scrollAreas, function (areaIdx, area) {
- var used = getUsed(area);
-
- if (!used.length) {
- unusedAreas.push(areaIdx);
- }
-
- area.updateEls(used);
- });
-
- // delete any scroll areas with no els still in DOM
- $.each(unusedAreas, deleteUnused);
- }
- };
-
- // Object collecting together methods for stopping sticky overlapping focused elements
- var focusOverlap = {
- getOverlap: function (focused, edge, endOfFurthestEl) {
- if (!endOfFurthestEl) { return 0; }
-
- if (edge === 'top') {
- return endOfFurthestEl - focused.top;
- } else {
- return focused.bottom - endOfFurthestEl;
- }
- },
- endOfFurthestEl: function (els, edge) {
- var stuckEls = $.grep(els, function (el) { return el.isStuck(); });
- var edgeOfEl;
- var offsets;
-
- if (edge === 'bottom') {
- edgeOfEl = function (el) {
- return el.$fixedEl.offset().top;
- };
- } else {
- edgeOfEl = function (el) {
- return el.$fixedEl.offset().top + el.height;
- };
- }
-
- if (!stuckEls.length) { return false; }
-
- offsets = $.map(stuckEls, function (el) { return edgeOfEl(el); });
-
- return offsets.reduce(function (accumulator, offset) {
- return (accumulator < offset) ? offset: accumulator;
- });
- },
- adjustForOverlap: function (focused, edge, overlap) {
- var scrollTop = $(window).scrollTop();
-
- // scroll so element becomes visible
- if (edge === 'top') {
- $(window).scrollTop(scrollTop - overlap);
- } else {
- $(window).scrollTop(scrollTop + overlap);
- }
- }
- };
-
- // Object collecting together methods for dealing with marking the edge of a sticky, or group of
- // sticky elements (as seen in dialog mode)
- var oppositeEdge = {
- _classes: {
- 'top': 'content-fixed__top',
- 'bottom': 'content-fixed__bottom'
- },
- _getClassForEdge: function (edge) {
- return this._classes[edge];
- },
- mark: function (sticky) {
- var edgeClass = this._getClassForEdge(sticky.edge);
- var els;
-
- if (_mode === 'dialog') {
- els = [dialog.getElementAtOppositeEnd(sticky)];
- } else {
- els = sticky._els;
- }
-
- els = $.grep(els, function (el) { return el.isStuck(); });
-
- $.each(els, function (i, el) {
- el.$fixedEl.addClass(edgeClass);
- });
- },
- unmark: function (sticky) {
- var edgeClass = this._getClassForEdge(sticky.edge);
-
- $.each(sticky._els, function (i, el) {
- el.$fixedEl.removeClass(edgeClass);
- });
- }
- };
-
- // Constructor for objects holding data for each element to have sticky behaviour
- var StickyElement = function ($el, sticky) {
- this._sticky = sticky;
- this.$fixedEl = $el;
- this._initialFixedClass = 'content-fixed-onload';
- this._fixedClass = 'content-fixed';
- this._appliedClass = null;
- this._$shim = null;
- this._stopped = false;
- this._hasLoaded = false;
- this._canBeStuck = true;
- this.verticalMargins = {
- 'top': parseInt(this.$fixedEl.css('margin-top'), 10),
- 'bottom': parseInt(this.$fixedEl.css('margin-bottom'), 10),
- };
- };
- StickyElement.prototype._getShimCSS = function () {
- return {
- 'width': this.horizontalSpace + 'px',
- 'height': this.height + 'px',
- 'margin-top': this.verticalMargins.top + 'px',
- 'margin-bottom': this.verticalMargins.bottom + 'px'
- };
- };
- StickyElement.prototype.stickyClass = function () {
- return (this._sticky._initialPositionsSet) ? this._fixedClass : this._initialFixedClass;
- };
- StickyElement.prototype.appliedClass = function () {
- return this._appliedClass;
- };
- StickyElement.prototype.removeStickyClasses = function (sticky) {
- this.$fixedEl.removeClass([
- this._initialFixedClass,
- this._fixedClass
- ].join(' '));
- };
- StickyElement.prototype.isStuck = function () {
- return this._appliedClass !== null;
- };
- StickyElement.prototype.stick = function (sticky) {
- this._appliedClass = this.stickyClass();
- this.$fixedEl.addClass(this._appliedClass);
- this._hasBeenCalled = true;
- };
- StickyElement.prototype.release = function (sticky) {
- this._appliedClass = null;
- this.removeStickyClasses(sticky);
- this._hasBeenCalled = true;
- };
- // When a sticky element is moved into the 'stuck' state, a shim is inserted into the
- // page to preserve the space the element occupies in the flow.
- StickyElement.prototype.addShim = function (position) {
- this._$shim = $('
 
');
- this._$shim.css(this._getShimCSS());
- this.$fixedEl[position](this._$shim);
- };
- StickyElement.prototype.removeShim = function () {
- if (this._$shim !== null) {
- this._$shim.remove();
- this._$shim = null;
- }
- };
- // Changes to the dimensions of a sticky element with a shim need to be passed on to the shim
- StickyElement.prototype.updateShim = function () {
- if (this._$shim) {
- this._$shim.css(this._getShimCSS());
- }
- };
- StickyElement.prototype.stop = function () {
- this._stopped = true;
- };
- StickyElement.prototype.unstop = function () {
- this._stopped = false;
- };
- StickyElement.prototype.isStopped = function () {
- return this._stopped;
- };
- StickyElement.prototype.isInPage = function () {
- var node = this.$fixedEl.get(0);
-
- return (node === document.body) ? false : document.body.contains(node);
- };
- StickyElement.prototype.canBeStuck = function (val) {
- if (val !== undefined) {
- this._canBeStuck = val;
- } else {
- return this._canBeStuck;
- }
- };
- StickyElement.prototype.hasLoaded = function (val) {
- if (val !== undefined) {
- this._hasLoaded = val;
- } else {
- return this._hasLoaded;
- }
- };
-
- // Object collecting together methods for treating sticky elements as if they
- // were wrapped by a dialog component
- var dialog = {
- hasResized: false,
- spaceBetweenStickys: 40,
- // we add padding of 20px around each sticky to give some space between it and the rest of the page
- // this shouldn't apply between stickys in a stack
- // (the in-page CSS handles this by each subsequent sticky in a sequence having margin: -40px)
- _getPaddingBetweenEls: function (els) {
- if (els.length <= 1) { return 0; }
-
- return (els.length - 1) * this.spaceBetweenStickys;
- },
- _getTotalHeight: function (els) {
- var reducer = function (accumulator, currentValue) {
- return accumulator + currentValue;
- };
- var combinedHeight = $.map(els, function (el) { return el.height; }).reduce(reducer);
-
- return combinedHeight - this._getPaddingBetweenEls(els);
- },
- _elsThatCanBeStuck: function (els) {
- return $.grep(els, function (el) { return el.canBeStuck(); });
- },
- getOffsetFromEdge: function (el, sticky) {
- var els = this._elsThatCanBeStuck(sticky._els).slice();
- var elIdx;
-
- // els must be arranged furtherest from window edge is stuck to first
- // default direction is order in document
- if (sticky.edge === 'top') {
- els.reverse();
- }
-
- elIdx = els.indexOf(el);
-
- // if next to window edge the dialog is stuck to, no offset
- if (elIdx === (els.length - 1)) { return 0; }
-
- // make els all those from this one to the window edge
- els = els.slice(elIdx + 1);
-
- // remove the space between those els and the one on the edge
- return this._getTotalHeight(els) - this.spaceBetweenStickys;
- },
- getOffsetFromEnd: function (el, sticky) {
- var els = this._elsThatCanBeStuck(sticky._els).slice();
- var elIdx;
-
- // els must be arranged furtherest from window edge is stuck to first
- // default direction is order in document
- if (sticky.edge === 'bottom') {
- els.reverse();
- }
-
- elIdx = els.indexOf(el);
-
- // if next to opposite edge to the one the dialog is stuck to, no offset
- if (elIdx === (els.length - 1)) { return 0; }
-
- // make els all those from this one to the window edge
- els = els.slice(elIdx + 1);
-
- return this._getTotalHeight(els) - this.spaceBetweenStickys;
- },
- // checks total height of all this._sticky elements against a height
- // unsticks each that won't fit and marks them as unstickable
- fitToHeight: function (sticky) {
- var self = this;
- var els = sticky._els.slice();
- var height = sticky.getWindowDimensions().height;
- var totalStickyHeight = function () {
- return self._getTotalHeight(self._elsThatCanBeStuck(els));
- };
- var dialogFitsHeight = function () {
- return totalStickyHeight() <= height;
- };
-
- // els must be arranged furtherest from window edge is stuck to first
- // default direction is order in document
- if (sticky.edge === 'top') {
- els.reverse();
- }
-
- // reset elements
- $.each(els, function (i, el) { el.canBeStuck(true); });
-
- while (self._elsThatCanBeStuck(els).length && !dialogFitsHeight()) {
- var currentEl = self._elsThatCanBeStuck(els)[0];
-
- sticky.reset(currentEl);
- currentEl.canBeStuck(false);
-
- if (!self.hasResized) { self.hasResized = true; }
- }
- },
- getElementAtStickyEdge: function (sticky) {
- var els = this._elsThatCanBeStuck(sticky._els);
- var idx = (sticky.edge === 'top') ? 0 : els.length - 1;
-
- return els[idx];
- },
- // get element at the end opposite the sticky edge
- getElementAtOppositeEnd: function (sticky) {
- var els = this._elsThatCanBeStuck(sticky._els);
- var idx = (sticky.edge === 'top') ? els.length - 1 : 0;
-
- return els[idx];
- },
- getInPageEdgePosition: function (sticky) {
- return this.getElementAtStickyEdge(sticky).inPageEdgePosition;
- },
- getHeight: function (els) {
- return this._getTotalHeight(this._elsThatCanBeStuck(els));
- },
- adjustForResize: function (sticky) {
- var windowHeight = sticky.getWindowDimensions().height;
-
- if (sticky.edge === 'top') {
- $(window).scrollTop(this.getInPageEdgePosition(sticky));
- } else {
- $(window).scrollTop(this.getInPageEdgePosition(sticky) - windowHeight);
- }
-
- this.hasResized = false;
- },
- releaseEl: function (el, sticky) {
- el.$fixedEl.css(sticky.edge, '');
- }
- };
-
- // Constructor for objects collecting together all generic behaviour for controlling the state of
- // sticky elements
- var Sticky = function (selector) {
- this._hasScrolled = false;
- this._scrollTimeout = false;
- this._windowHasResized = false;
- this._resizeTimeout = false;
- this._elsLoaded = false;
- this._initialPositionsSet = false;
- this._els = [];
-
- this.CSS_SELECTOR = selector;
- this.STOP_PADDING = 10;
- };
- Sticky.prototype.setMode = function (mode) {
- _mode = mode;
- };
- Sticky.prototype.getWindowDimensions = function () {
- return {
- height: $(global).height(),
- width: $(global).width()
- };
- };
- Sticky.prototype.getWindowPositions = function () {
- return {
- scrollTop: $(global).scrollTop()
- };
- };
- // Change state of sticky elements based on their position relative to the window
- Sticky.prototype.setElementPositions = function () {
- var self = this,
- windowDimensions = self.getWindowDimensions(),
- windowTop = self.getWindowPositions().scrollTop,
- windowPositions = {
- 'top': windowTop,
- 'bottom': windowTop + windowDimensions.height
- };
-
- var _setElementPosition = function (el) {
- if (self.viewportIsWideEnough(windowDimensions.width)) {
-
- if (self.windowNotPastScrolledFrom(windowPositions, self.getScrolledFrom(el))) {
- self.reset(el);
- } else { // past the point it sits in the document
- if (self.windowNotPastScrollingTo(windowPositions, self.getScrollingTo(el))) {
- self.stick(el);
- if (el.isStopped()) {
- self.unstop(el);
- }
- } else { // window past scrollingTo position
- if (!el.isStuck()) {
- self.stick(el);
- }
- self.stop(el);
- }
- }
-
- } else {
-
- self.reset(el);
-
- }
- };
-
- // clean up any existing styles marking the edges of sticky elements
- oppositeEdge.unmark(self);
-
- $.each(self._els, function (i, el) {
- if (el.canBeStuck()) {
- _setElementPosition(el);
- }
- });
-
- // add styles to mark the edge of sticky elements opposite to that stuck to the window
- oppositeEdge.mark(self);
-
- if (self._initialPositionsSet === false) { self._initialPositionsSet = true; }
- };
- // Store all the dimensions for a sticky element to limit DOM queries
- Sticky.prototype.setElementDimensions = function (el, callback) {
- var self = this;
- var $el = el.$fixedEl;
- var onHeightSet = function () {
- // if element is shim'ed, pass changes in dimension on to the shim
- if (el._$shim) {
- el.updateShim();
- }
- if (callback !== undefined) {
- callback();
- }
- };
-
- this.setElWidth(el);
- this.setElHeight(el, onHeightSet);
- };
- // Reset element to original state in the page
- Sticky.prototype.reset = function (el) {
- if (el.isStopped()) {
- this.unstop(el);
- }
- if (el.isStuck()) {
- this.release(el);
- }
- };
- // Recalculate stored dimensions for all sticky elements
- Sticky.prototype.recalculate = function () {
- var self = this;
- var onSyncComplete = function () {
- scrollAreas.syncEls(self._els);
- self.setEvents();
- if (_mode === 'dialog') {
- dialog.fitToHeight(self);
- if (dialog.hasResized) {
- dialog.adjustForResize(self);
- }
- }
- self.setElementPositions();
- };
-
- this.syncWithDOM(onSyncComplete);
- };
- // Public method to scroll so an element isn't covered by the sticky nav
- Sticky.prototype.scrollToRevealElement = function (el) {
- var $el = $(el);
- var scrollAreaNode = $el.closest('.sticky-scroll-area').get(0);
- var matches = $.grep(scrollAreas._scrollAreas, function (scrollArea) {
- return scrollArea.node === scrollAreaNode;
- });
-
- if (matches.length) {
- matches[0].scrollToRevealElement($el);
- }
- };
- Sticky.prototype.setElWidth = function (el) {
- var $el = el.$fixedEl;
- var scrollArea = scrollAreas.getAreaByEl(el);
- var width = $(scrollArea.node).width();
-
- el.horizontalSpace = width;
- // if stuck, element won't inherit width from parent so set explicitly
- if (el._$shim) {
- $el.width(width);
- }
- };
- Sticky.prototype.setElHeight = function (el, callback) {
- var self = this;
- var $el = el.$fixedEl;
- var $img = $el.find('img');
- var onload = function () {
- el.height = $el.outerHeight();
- // if element has a shim, the shim's offset represents the element's in-page position
- if (el._$shim) {
- el.inPageEdgePosition = self.getInPageEdgePosition(el._$shim);
- } else {
- el.inPageEdgePosition = self.getInPageEdgePosition($el);
- }
- callback();
- };
-
- if ((!el.hasLoaded()) && ($img.length > 0)) {
- var image = new window.Image();
- image.onload = function () {
- onload();
- };
- image.src = $img.attr('src');
- } else {
- onload();
- }
- };
- Sticky.prototype.allElementsLoaded = function (totalEls) {
- return this._els.length === totalEls;
- };
- Sticky.prototype.getElForNode = function (node) {
- var matches = $.grep(this._els, function (el) { return el.$fixedEl.is(node); });
-
- return !!matches.length ? matches[0] : false;
- };
- Sticky.prototype.add = function (el, setPositions, cb) {
- var self = this;
- var $el = $(el);
- var onDimensionsSet;
- var elObj = this.getElForNode(el);
- var exists = !!elObj;
-
- onDimensionsSet = function () {
- elObj.hasLoaded(true);
-
- // guard against adding elements already stored
- if (!exists) {
- self._els.push(elObj);
- }
-
- if (setPositions) {
- self.setElementPositions();
- }
-
- if (cb !== undefined) {
- cb();
- }
- };
-
- if (!exists) {
- elObj = new StickyElement($el, self);
- scrollAreas.addEl(elObj, self.edge, self.CSS_SELECTOR);
- }
-
- self.setElementDimensions(elObj, onDimensionsSet);
- };
- Sticky.prototype.remove = function (el) {
- if ($.inArray(el, this._els) !== -1) {
-
- // reset DOM node to original state
- this.reset(el);
-
- // remove sticky element object
- this._els = $.grep(this._els, function (_el) { return _el !== el; });
- }
- };
- // gets all sticky elements in the DOM and removes any in this._els no longer in attached to it
- Sticky.prototype.syncWithDOM = function (callback) {
- var self = this;
- var $els = $(self.CSS_SELECTOR);
- var numOfEls = $els.length;
- var onLoaded;
-
- onLoaded = function () {
- if (self._els.length === numOfEls) {
- self.endOfScrollArea = self.getEndOfScrollArea();
- if (callback !== undefined) {
- callback();
- }
- }
- };
-
- // remove any els no longer in the DOM
- if (this._els.length) {
- $.each(this._els, function (i, el) {
- if (!el.isInPage()) {
- self.remove(el);
- }
- });
- }
-
- if (numOfEls) {
- // reset flag marking page load
- this._initialPositionsSet = false;
-
- $els.each(function (i, el) {
- // delay setting position until all stickys are loaded
- self.add(el, false, onLoaded);
- });
- }
- };
- Sticky.prototype.init = function () {
- this.recalculate();
- };
- Sticky.prototype.setEvents = function () {
- this._scrollEvent = this.onScroll.bind(this);
- this._resizeEvent = this.onResize.bind(this);
-
- // flag when scrolling takes place and check (and re-position) sticky elements relative to
- // window position
- if (this._scrollTimeout === false) {
- $(global).scroll(this._scrollEvent);
- this._scrollTimeout = window.setInterval(this.checkScroll.bind(this), 50);
- }
-
- // Recalculate all dimensions when the window resizes
- if (this._resizeTimeout === false) {
- $(global).resize(this._resizeEvent);
- this._resizeTimeout = window.setInterval(this.checkResize.bind(this), 50);
- }
- };
- Sticky.prototype.clearEvents = function () {
- if (this._scrollTimeout !== false) {
- $(global).off('scroll', this._scrollEvent);
- window.clearInterval(this._scrollTimeout);
- this._scrollTimeout = false;
- }
-
- if (this._resizeTimeout !== false) {
- $(global).off('resize', this._resizeEvent);
- window.clearInterval(this._resizeTimeout);
- this._resizeTimeout = false;
- }
-
- };
- Sticky.prototype.viewportIsWideEnough = function (windowWidth) {
- return windowWidth > 768;
- };
- Sticky.prototype.onScroll = function () {
- this._hasScrolled = true;
- };
- Sticky.prototype.onResize = function () {
- this._windowHasResized = true;
- };
- Sticky.prototype.checkScroll = function () {
- var self = this;
-
- if (self._hasScrolled === true) {
- self._hasScrolled = false;
- self.setElementPositions();
- }
- };
- Sticky.prototype.checkResize = function () {
- var self = this,
- windowWidth = self.getWindowDimensions().width;
-
- if (self._windowHasResized === true) {
- self._windowHasResized = false;
-
- $.each(self._els, function (i, el) {
- if (!self.viewportIsWideEnough(windowWidth)) {
- self.reset(el);
- } else {
- self.setElementDimensions(el);
- }
- });
-
- if (self.viewportIsWideEnough(windowWidth)) {
- if (_mode === 'dialog') {
- dialog.fitToHeight(self);
- if (dialog.hasResized) {
- dialog.adjustForResize(self);
- }
- }
- self.setElementPositions();
- }
- }
- };
- Sticky.prototype.release = function (el) {
- if (el.isStuck()) {
- var $el = el.$fixedEl;
-
- el.removeStickyClasses(this);
- $el.css('width', '');
- // clear styles from any elements stuck while in a dialog mode
- dialog.releaseEl(el, this);
- el.removeShim();
- el.release(this);
- }
- };
-
- // Extension of sticky object to add behaviours specific to sticking to top of window
- var stickAtTop = new Sticky('.js-stick-at-top-when-scrolling');
- stickAtTop.edge = 'top';
- // Store furthest point sticky elements are allowed
- stickAtTop.getEndOfScrollArea = function () {
- var footer = $('.js-footer:eq(0)');
- if (footer.length === 0) {
- return 0;
- }
- return footer.offset().top - this.STOP_PADDING;
- };
- // position of the bottom edge when in the page flow
- stickAtTop.getInPageEdgePosition = function ($el) {
- return $el.offset().top;
- };
- stickAtTop.getScrolledFrom = function (el) {
- if (_mode === 'dialog') {
- return dialog.getInPageEdgePosition(this);
- } else {
- return el.inPageEdgePosition;
- }
- };
- stickAtTop.getScrollingTo = function (el) {
- var height = el.height;
-
- if (_mode === 'dialog') {
- height = dialog.getHeight(this._els);
- }
-
- return this.endOfScrollArea - height;
- };
- stickAtTop.getStoppingPosition = function (el) {
- var offset = 0;
-
- if (_mode === 'dialog') {
- offset = dialog.getOffsetFromEnd(el, this);
- }
-
- return (this.endOfScrollArea - offset) - el.height;
- };
- stickAtTop.windowNotPastScrolledFrom = function (windowPositions, scrolledFrom) {
- return scrolledFrom > windowPositions.top;
- };
- stickAtTop.windowNotPastScrollingTo = function (windowPositions, scrollingTo) {
- return windowPositions.top < scrollingTo;
- };
- stickAtTop.stick = function (el) {
- if (!el.isStuck()) {
- var $el = el.$fixedEl;
- var offset = 0;
-
- if (_mode === 'dialog') {
- offset = dialog.getOffsetFromEdge(el, this);
- }
-
- el.addShim('before');
- $el.css({
- // element will be absolutely positioned so cannot rely on parent element for width
- 'width': $el.width() + 'px',
- 'top': offset + 'px'
- });
- el.stick(this);
- }
- };
- stickAtTop.stop = function (el) {
- if (!el.isStopped()) {
- el.$fixedEl.css({
- 'position': 'absolute',
- 'top': this.getStoppingPosition(el)
- });
- el.stop();
- }
- };
- stickAtTop.unstop = function (el) {
- var offset = 0;
-
- if (_mode === 'dialog') {
- offset = dialog.getOffsetFromEdge(el, this);
- }
-
- el.$fixedEl.css({
- 'position': '',
- 'top': offset + 'px'
- });
- el.unstop();
- };
-
- // Extension of sticky object to add behaviours specific to sticking to bottom of window
- var stickAtBottom = new Sticky('.js-stick-at-bottom-when-scrolling');
- stickAtBottom.edge = 'bottom';
- // Store furthest point sticky elements are allowed
- stickAtBottom.getEndOfScrollArea = function () {
- var header = $('.js-header:eq(0)');
- if (header.length === 0) {
- return 0;
- }
- return (header.offset().top + header.outerHeight()) + this.STOP_PADDING;
- };
- // position of the bottom edge when in the page flow
- stickAtBottom.getInPageEdgePosition = function ($el) {
- return $el.offset().top + $el.outerHeight();
- };
- stickAtBottom.getScrolledFrom = function (el) {
- if (_mode === 'dialog') {
- return dialog.getInPageEdgePosition(this);
- } else {
- return el.inPageEdgePosition;
- }
- };
- stickAtBottom.getScrollingTo = function (el) {
- var height = el.height;
-
- if (_mode === 'dialog') {
- height = dialog.getHeight(this._els);
- }
-
- return this.endOfScrollArea + height;
- };
- stickAtBottom.getStoppingPosition = function (el) {
- var offset = 0;
-
- if (_mode === 'dialog') {
- offset = dialog.getOffsetFromEnd(el, this);
- }
-
- return this.endOfScrollArea + offset;
- };
- stickAtBottom.windowNotPastScrolledFrom = function (windowPositions, scrolledFrom) {
- return scrolledFrom < windowPositions.bottom;
- };
- stickAtBottom.windowNotPastScrollingTo = function (windowPositions, scrollingTo) {
- return windowPositions.bottom > scrollingTo;
- };
- stickAtBottom.stick = function (el) {
- if (!el.isStuck()) {
- var $el = el.$fixedEl;
- var offset = 0;
-
- if (_mode === 'dialog') {
- offset = dialog.getOffsetFromEdge(el, this);
- }
-
- el.addShim('after');
- $el.css({
- // element will be absolutely positioned so cannot rely on parent element for width
- 'width': $el.width() + 'px',
- 'bottom': offset + 'px'
- });
- el.stick(this);
- }
- };
- stickAtBottom.stop = function (el) {
- if (!el.isStopped()) {
- el.$fixedEl.css({
- 'position': 'absolute',
- 'top': this.getStoppingPosition(el),
- 'bottom': 'auto'
- });
- el.stop();
- }
- };
- stickAtBottom.unstop = function (el) {
- var offset = 0;
-
- if (_mode === 'dialog') {
- offset = dialog.getOffsetFromEdge(el, this);
- }
-
- el.$fixedEl.css({
- 'position': '',
- 'top': '',
- 'bottom': offset + 'px'
- });
- el.unstop();
- };
-
- NotifyModules.stickAtTopWhenScrolling = stickAtTop;
- NotifyModules.stickAtBottomWhenScrolling = stickAtBottom;
- window.NotifyModules = NotifyModules;
-})(window);
diff --git a/package-lock.json b/package-lock.json
index 516b982cc..088fe3d09 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,6 @@
"@uswds/uswds": "^3.13.0",
"cbor-js": "0.1.0",
"d3": "^7.9.0",
- "jquery": "3.7.1",
"playwright": "^1.56.1",
"python": "^0.0.4",
"query-command-supported": "1.0.0",
@@ -9564,11 +9563,6 @@
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
"dev": true
},
- "node_modules/jquery": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
- "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
- },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
diff --git a/package.json b/package.json
index d83d14cdb..412fb0890 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,6 @@
"@uswds/uswds": "^3.13.0",
"cbor-js": "0.1.0",
"d3": "^7.9.0",
- "jquery": "3.7.1",
"playwright": "^1.56.1",
"python": "^0.0.4",
"query-command-supported": "1.0.0",
diff --git a/tests/javascripts/collapsibleCheckboxes.test.js b/tests/javascripts/collapsibleCheckboxes.test.js
index af572565b..3a6ffda7a 100644
--- a/tests/javascripts/collapsibleCheckboxes.test.js
+++ b/tests/javascripts/collapsibleCheckboxes.test.js
@@ -1,9 +1,6 @@
const helpers = require('./support/helpers');
beforeAll(() => {
- // TODO: remove this when tests for sticky JS are written
- require('../../app/assets/javascripts/stick-to-window-when-scrolling.js');
-
require('../../app/assets/javascripts/collapsibleCheckboxes.js');
});
diff --git a/tests/javascripts/copyToClipboard.test.js b/tests/javascripts/copyToClipboard.test.js
index db573e5a2..479a447fc 100644
--- a/tests/javascripts/copyToClipboard.test.js
+++ b/tests/javascripts/copyToClipboard.test.js
@@ -100,11 +100,6 @@ describe('copy to clipboard', () => {
});
- test("It should tell any sticky JS present the page has changed", () => {
-
- expect(window.NotifyModules.stickAtBottomWhenScrolling.recalculate).toHaveBeenCalled();
-
- });
test("It should display the value", () => {
diff --git a/tests/javascripts/stick-to-window-when-scrolling.test.js b/tests/javascripts/stick-to-window-when-scrolling.test.js
deleted file mode 100644
index 7b418df23..000000000
--- a/tests/javascripts/stick-to-window-when-scrolling.test.js
+++ /dev/null
@@ -1,1446 +0,0 @@
-const helpers = require('./support/helpers');
-const PADDING_BETWEEN_STICKYS = 40;
-const PADDING_BEFORE_STOPPING_POINT = 10;
-
-function getScreenItemBottomPosition (screenItem) {
- return screenItem.offsetTop + screenItem.offsetHeight;
-};
-
-function getCaretPosition (caretPosition, textarea) {
-
- return {
- top: textarea.offsetTop + caretPosition.top,
- bottom: textarea.offsetTop + caretPosition.top + caretPosition.height,
- height: caretPosition.height
- };
-
-};
-
-function getStickyGroupPosition (screenMock, opts) {
-
- const edgePosition = screenMock.window[opts.edge];
- const height = opts.stickyEls
- .map(el => el.offsetHeight)
- .reduce((accumulator, height) => accumulator + height);
-
- if (opts.edge === 'top') {
- return {
- top: screenMock.window.top,
- bottom: edgePosition + height,
- height: height
- };
- } else {
- return {
- top: edgePosition - height,
- bottom: screenMock.window.bottom,
- height: height
- }
- }
-
-};
-
-class CaretCoordinates {
- constructor (data) {
- this.top = 5.5;
- this.left = 2;
- this.height = 19;
- }
-
- moveToLine (lineNumber) {
- const lineHeight = 30;
- const verticalPadding = 5.5;
-
- this.top = ((lineNumber - 1) * lineHeight) + verticalPadding;
- }
-}
-
-beforeAll(() => {
- require('../../app/assets/javascripts/stick-to-window-when-scrolling.js');
-});
-
-afterAll(() => {
- require('./support/teardown.js');
-});
-
-describe("Stick to top/bottom of window when scrolling", () => {
-
- let screenMock;
-
- describe("If intending to stick to the top", () => {
-
- let inputForm;
- let formFooter;
- let footer;
- let windowHeight;
- let getFurthestTopPoint;
-
- beforeEach(() => {
-
- document.body.innerHTML = `
-
-
-
-
-
- `;
-
- inputForm = document.querySelector('form > .grid-row');
- formFooter = document.querySelector('.page-footer');
- footer = document.querySelector('.js-footer');
-
- windowHeight = 940;
-
- // mock the rendering of all components
- screenMock = new helpers.ScreenMock(jest);
- screenMock.setWindow({
- width: 1990,
- height: windowHeight,
- scrollTop: 0
- });
- screenMock.mockPositionAndDimension('inputForm', inputForm, {
- offsetHeight: 168,
- offsetWidth: 727,
- offsetTop: 238
- });
- screenMock.mockPositionAndDimension('formFooter', formFooter, {
- offsetHeight: 200,
- offsetWidth: 727,
- offsetTop: inputForm.offsetTop + 200
- });
- screenMock.mockPositionAndDimension('footer', footer, {
- offsetHeight: 535,
- offsetWidth: 1990,
- offsetTop: formFooter.offsetTop + formFooter.offsetHeight
- });
-
- getFurthestTopPoint = (stickysHeight) => {
- return footer.offsetTop - PADDING_BEFORE_STOPPING_POINT - stickysHeight;
- };
-
- // the sticky JS polls for changes to position/dimension so we need to fake setTimeout and setInterval
- jest.useFakeTimers();
-
- });
-
- afterEach(() => {
-
- document.body.innerHTML = '';
-
- window.NotifyModules.stickAtTopWhenScrolling.clearEvents();
-
- screenMock.reset();
-
- });
-
- test("if top of viewport is above top of element on load, the element should not be marked as sticky", () => {
-
- // scroll position defaults to 0, element top defaults to 138px
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(false);
- expect(inputForm.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("if the window is 768px or less wide and the top of the viewport is below top of element on load, the element should not be marked as sticky", () => {
-
- screenMock.window.resizeTo({
- height: windowHeight,
- width: 768
- });
-
- // scroll past top of form
- screenMock.scrollTo(inputForm.offsetTop + 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- expect(inputForm.classList.contains('content-fixed')).toBe(false);
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- });
-
- describe("if top of viewport is below top of element but still in the scroll area on load", () => {
-
- beforeEach(() => {
-
- // scroll past the top of the form
- screenMock.scrollTo(inputForm.offsetTop + 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- test("the element should be marked as already sticky", () => {
-
- // `.content-fixed-onload` adds the drop-shadow without fading in to show it did not become sticky from user interaction
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(true);
- expect(inputForm.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("a 'shim' element with dimensions matching the sticky element should be added to the document to take up the space it no longer occupies", () => {
-
- const shim = inputForm.previousElementSibling;
-
- expect(shim).not.toBeNull();
- expect(shim.classList.contains('shim')).toBe(true);
- expect(shim.style.height).toEqual(`${inputForm.offsetHeight}px`);
- expect(shim.style.marginTop).toEqual(''); // 0px would return an empty string
- expect(shim.style.marginBottom).toEqual(''); // 0px would return an empty string
-
- });
-
- });
-
- test("if top of viewport is below the furthest point the top of the element can go in the scroll area on load, the element should be marked as stopped", () => {
-
- // the element should stop a set distance from the stopping point
- const furthestTopPoint = getFurthestTopPoint(inputForm.offsetHeight);
-
- // scroll past the furthest point
- screenMock.scrollTo(furthestTopPoint + 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- // `.content-fixed-onload` adds the drop-shadow without fading in to show it did not become sticky from user interaction
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(true);
- expect(inputForm.classList.contains('content-fixed')).toBe(false);
-
- // elements are stopped by adding inline styles
- expect(inputForm.style.position).toEqual('absolute');
- expect(inputForm.style.top).toEqual(`${furthestTopPoint}px`);
-
- });
-
- describe("if viewport top starts above element top", () => {
-
- beforeEach(() => {
-
- // default scroll position is above top of form
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- test("if window is scrolled so top of it is below the top of the element, the element should be marked so it becomes sticky to the user", () => {
-
- // scroll past top of form
- screenMock.scrollTo(inputForm.offsetTop + 10);
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a scroll
-
- // `content-fixed` fades the drop-shadow in to show it became sticky from user interaction
- expect(inputForm.classList.contains('content-fixed')).toBe(true);
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- });
-
- test("if window is scrolled so top of it is below the furthest point the top of the element can go in the scroll area, the element should be stopped", () => {
-
- const furthestTopPoint = getFurthestTopPoint(inputForm.offsetHeight);
-
- // scroll past top of form
- screenMock.scrollTo(furthestTopPoint + 10);
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a scroll
-
- // `content-fixed` fades the drop-shadow in to show it became sticky from user interaction
- expect(inputForm.classList.contains('content-fixed')).toBe(true);
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- // elements are stopped by adding inline styles
- expect(inputForm.style.position).toEqual('absolute');
- expect(inputForm.style.top).toEqual(`${furthestTopPoint}px`);
-
- });
-
- });
-
- describe("if viewport top starts below element top", () => {
-
- beforeEach(() => {
-
- // scroll past top of form
- screenMock.scrollTo(inputForm.offsetTop + 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- test("if window is scrolled so top of it is above the top of the element, the element should be made not sticky", () => {
-
- // scroll to top
- screenMock.scrollTo(0);
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a scroll
-
- expect(inputForm.classList.contains('content-fixed')).toBe(false);
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- });
-
- });
-
- describe("if scrollToRevealElement is called with an element", () => {
-
- let link;
- let linkBottom;
-
- beforeEach(() => {
-
- const inputFormBottom = getScreenItemBottomPosition(inputForm);
-
- inputForm.insertAdjacentHTML('afterEnd', 'Formatting options');
- link = document.querySelector('#formatting-options');
-
- screenMock.mockPositionAndDimension('link', link, {
- offsetHeight: 25, // 143px smaller than the sticky
- offsetWidth: 727,
- offsetTop: inputFormBottom
- });
-
- linkBottom = getScreenItemBottomPosition(link);
-
- // move the sticky over the link. It's 168px high so this position will cause it to overlap.
- screenMock.scrollTo(link.offsetTop - 140);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- afterEach(() => {
-
- screenMock.window.spies.window.scrollTo.mockClear();
-
- });
-
- test("the window should scroll so the element is revealed", () => {
-
- // update inputForm position as DOM normally would
- inputForm.offsetTop = screenMock.window.top;
-
- let stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [inputForm], edge: 'top' });
-
- // sticky position should overlap link position
- expect(stickyPosition.top).toBeLessThanOrEqual(link.offsetTop);
- expect(stickyPosition.bottom).toBeGreaterThanOrEqual(linkBottom);
-
- window.NotifyModules.stickAtTopWhenScrolling.scrollToRevealElement(link);
-
- stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [inputForm], edge: 'top' });
-
- // the bottom of the sticky element should be at the top of the link
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, link.offsetTop - stickyPosition.height]);
-
- });
-
- });
-
- describe("if element is made sticky and another element underneath it is focused", () => {
-
- let checkbox;
- let checkboxBottom;
-
- beforeEach(() => {
-
- const inputFormBottom = getScreenItemBottomPosition(inputForm);
-
- inputForm.insertAdjacentHTML('afterEnd', '');
- checkbox = document.querySelector('input[type=checkbox]');
-
- screenMock.mockPositionAndDimension('checkbox', checkbox, {
- offsetHeight: 50, // 118px smaller than the sticky
- offsetWidth: 727,
- offsetTop: inputFormBottom
- });
-
- checkboxBottom = getScreenItemBottomPosition(checkbox);
-
- // move the sticky over the checkbox. It's 168px high so this position will cause it to overlap.
- screenMock.scrollTo(checkbox.offsetTop - 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- afterEach(() => {
-
- screenMock.window.spies.window.scrollTo.mockClear();
-
- });
-
- test("the window should scroll so the focused element is revealed", () => {
-
- // update inputForm position as DOM normally would
- inputForm.offsetTop = screenMock.window.top;
-
- let stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [inputForm], edge: 'top' });
-
- // sticky position should overlap checkbox position
- expect(stickyPosition.top).toBeLessThanOrEqual(checkbox.offsetTop);
- expect(stickyPosition.bottom).toBeGreaterThanOrEqual(checkboxBottom);
-
- // the sticky element (page footer) is 50 high so should cover the last of the radios if the bottom edge of the viewport is at its bottom
- checkbox.focus();
-
- stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [inputForm], edge: 'top' });
-
- // the bottom of the sticky element should be at the top of the checkbox
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, checkbox.offsetTop - stickyPosition.height]);
-
- });
-
- });
-
- describe("if element is made sticky and overlaps a textarea", () => {
-
- let textarea;
- let textareaBottom;
- let caretCoordinates;
- let caretCoordinatesMock;
-
- beforeEach(() => {
-
- const inputFormBottom = getScreenItemBottomPosition(inputForm);
-
- inputForm.insertAdjacentHTML('afterEnd', '');
- textarea = document.querySelector('textarea');
-
- // line height: 30px, text height: 19px, lines: 10
- screenMock.mockPositionAndDimension('textarea', textarea, {
- offsetHeight: 300,
- offsetWidth: 727,
- offsetTop: inputFormBottom
- });
-
- textareaBottom = getScreenItemBottomPosition(textarea);
-
- // mock calls for caret position, relative to textarea
- caretCoordinatesMock = jest.fn(() => caretCoordinates);
- window.getCaretCoordinates = caretCoordinatesMock;
-
- // start caret on first line
- caretCoordinates = new CaretCoordinates();
-
- // move the sticky so it overlaps the top 168px of the textarea.
- screenMock.scrollTo(textarea.offsetTop);
-
- // update inputForm position as DOM normally would
- inputForm.offsetTop = screenMock.window.top;
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- afterEach(() => {
-
- screenMock.window.spies.window.scrollTo.mockClear();
- caretCoordinatesMock.mockClear();
-
- });
-
- test("if the textarea receives focus while its caret is underneath, the window should scroll to reveal the caret", () => {
-
- // caret is on first line
- const stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [inputForm], edge: 'top' });
- const caretPosition = getCaretPosition(caretCoordinates, textarea);
-
- // sticky position should overlap caret position
- expect(stickyPosition.top).toBeLessThanOrEqual(caretPosition.top);
- expect(stickyPosition.bottom).toBeGreaterThanOrEqual(caretPosition.bottom);
-
- // the sticky element (page footer) is 50 high so should cover the last of the radios if the bottom edge of the viewport is at its bottom
- textarea.focus();
-
- // the bottom of the sticky element should be at the top of the checkbox
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, caretPosition.top - stickyPosition.height]);
-
- });
-
- test("if the caret is moved so it isn't underneath the sticky element, the window shouldn't scroll", () => {
-
- // start caret on 7th line which isn't under the sticky element.
- caretCoordinates.moveToLine(7);
-
- const stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [inputForm], edge: 'top' });
- const caretPosition = getCaretPosition(caretCoordinates, textarea);
-
- // the sticky element should be above the caret
- expect(stickyPosition.bottom).toBeLessThan(caretPosition.top);
-
- textarea.focus();
-
- // no scrolling should have happened
- expect(screenMock.window.spies.window.scrollTo.mock.calls.length).toEqual(0);
-
- });
-
- test("if the caret is moved to be underneath the sticky element, the window should scroll to reveal the caret", () => {
-
- // start caret on 7th line which isn't under the sticky element.
- caretCoordinates.moveToLine(7);
-
- // make sure the textarea has focus
- textarea.focus();
-
- // line 6 is under the sticky element
- caretCoordinates.moveToLine(6);
-
- const stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [inputForm], edge: 'top' });
- const caretPosition = getCaretPosition(caretCoordinates, textarea);
-
- // sticky should now overlap the caret
- expect(stickyPosition.bottom).toBeGreaterThanOrEqual(caretPosition.top);
-
- // the sticky element (page footer) is 50 high so should cover the last of the radios if the bottom edge of the viewport is at its bottom
- helpers.triggerEvent(textarea, 'keyup', { interface: window.KeyboardEvent });
-
- // the bottom of the sticky element should be at the top of the checkbox
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, caretPosition.top - stickyPosition.height]);
-
- });
-
- });
-
- describe("if mode is set to 'dialog' and multiple sticky elements share the same scroll area", () => {
-
- let radios;
-
- beforeEach(() => {
-
- const inputFormBottom = getScreenItemBottomPosition(inputForm);
-
- // set mode to 'dialog' so sticky elements are treated as one item
- window.NotifyModules.stickAtTopWhenScrolling.setMode('dialog')
-
- // add another sticky element before the form footer
- radios = helpers.getRadioGroup({
- cssClasses: ['js-stick-at-top-when-scrolling'],
- name: 'choose-send-time',
- label: 'Choose send time',
- fields: [
- {
- label: 'Now',
- value: 'now',
- checked: true
- },
- {
- label: 'Tomorrow',
- value: 'tomorrow',
- checked: false
- },
- {
- label: 'Friday',
- value: 'friday',
- checked: false
- }
- ]
- });
- formFooter.parentNode.insertBefore(radios, formFooter);
- screenMock.mockPositionAndDimension('radios', radios, {
- offsetHeight: 175,
- offsetWidth: 727,
- offsetTop: inputFormBottom
- });
- const radiosBottom = getScreenItemBottomPosition(radios);
-
- // adjust other screen items
-
- // formFooter top should be at the radios bottom
- formFooter.offsetTop = radiosBottom;
-
- // footer top should be at formFooter's bottom
- footer.offsetTop = getScreenItemBottomPosition(formFooter);
-
- });
-
- afterEach(() => {
-
- window.NotifyModules.stickAtTopWhenScrolling.setMode('default');
-
- });
-
- describe("if window top is below the top of the highest element on load", () => {
-
- beforeEach(() => {
-
- // scroll past top of first sticky element
- screenMock.scrollTo(inputForm.offsetTop + 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- test("both should be marked as already sticky", () => {
-
- // `.content-fixed-onload` adds the drop-shadow without fading in to show it did not become sticky from user interaction
- expect(radios.classList.contains('content-fixed-onload')).toBe(true);
- expect(radios.classList.contains('content-fixed')).toBe(false);
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(true);
- expect(inputForm.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("the lowest element should have the drop-shadow", () => {
-
- expect(radios.classList.contains('content-fixed__top')).toBe(true);
-
- });
-
- test("they should be stacked from the top edge of the viewport in the order they appear in the document", () => {
-
- expect(inputForm.style.top).toEqual('0px');
-
- // dialog mode removes enough padding to deal with that each sticky element is given to give its content enough space from the surrounding page
- expect(radios.style.top).toEqual(`${inputForm.offsetHeight - PADDING_BETWEEN_STICKYS}px`);
-
- });
-
- });
-
- describe("if window top is below the furthest point the highest element can go on load", () => {
-
- let furthestTopPoint;
-
- beforeEach(() => {
-
- // ensure each sticky element is positioned correctly relative to the furthest point
- const dialogHeight = (inputForm.offsetHeight + radios.offsetHeight) - PADDING_BETWEEN_STICKYS;
-
- furthestTopPoint = getFurthestTopPoint(dialogHeight);
-
- // scroll past top of first sticky element
- screenMock.scrollTo(furthestTopPoint + 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- });
-
- test("both should be stopped so the bottom of their stack is at the furthest point", () => {
-
- expect(inputForm.style.position).toEqual('absolute');
- expect(radios.style.position).toEqual('absolute');
-
- expect(inputForm.style.top).toEqual(`${furthestTopPoint}px`);
- expect(radios.style.top).toEqual(`${footer.offsetTop - PADDING_BEFORE_STOPPING_POINT - radios.offsetHeight}px`);
-
- });
-
- });
-
- describe("if the group of sticky elements is taller than the window when stuck together", () => {
-
- // the sticky behaviour should stick as many elements as possible to the top of the viewport
- // the rest should be put back into their place in the document
- beforeEach(() => {
-
- const windowHeight = 460;
-
- function makeStickysBiggerThanWindow () {
-
- // make the radios too big to fit in the viewport
- const fields = ['Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
- .map(field => {
- return {
- label: field,
- value: field.toLowerCase(),
- checked: false
- }
- });
-
- radios.querySelector('fieldset').insertAdjacentHTML('beforeend', helpers.getRadios(fields, 'days'));
-
- radios.offsetHeight = 475;
-
- };
-
- function adjustScreenPositions () {
-
- const radiosBottom = getScreenItemBottomPosition(radios);
-
- screenMock.window.setHeightTo(windowHeight);
-
- // formFooter top should be at the radios bottom
- formFooter.offsetTop = radiosBottom;
-
- // footer top should be at formFooter's bottom
- footer.offsetTop = getScreenItemBottomPosition(formFooter);
-
- };
-
- makeStickysBiggerThanWindow();
- adjustScreenPositions();
-
- screenMock.scrollTo(inputForm.offsetTop + 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init()
-
- });
-
- test("the number of elements in the group that are stuck should be reduced until it fits into the viewport", () => {
-
- // when the group is stuck, pageFooter is closest to the viewport edge so should stay sticky
- expect(inputForm.classList.contains('content-fixed-onload')).toBe(true);
- expect(inputForm.classList.contains('content-fixed')).toBe(false);
-
- // radios are made not sticky
- expect(radios.classList.contains('content-fixed-onload')).toBe(false);
- expect(radios.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("window is scrolled so the sticky elements (stuck and not stuck) appear in sequence", () => {
-
- // we assume it's useful to start by seeing all the elements in the position they normally sit in the page
- expect(screenMock.window.top).toEqual(inputForm.offsetTop);
-
- });
-
- });
-
- });
-
- });
-
- describe("If intending to stick to the bottom", () => {
-
- let heading;
- let content;
- let pageFooter;
- let windowHeight;
- let getFurthestBottomPoint;
-
- beforeEach(() => {
-
- document.body.innerHTML = `
-
- Back
-
- Preview of ‘Content email’
-
-
-
-
This is the title
-
This is a paragraph, defined as a block of content where the contents run horizontally across lines.
-
-
-
-
-
-
these
-
are
-
bullet points
-
-
-
-
-
-
-
This block of text is formatted to stand out from the rest
-
-
This is a paragraph with a horizontal line underneath.
- `;
-
- heading = document.querySelector('.js-header');
- content = document.querySelector('.email-message-body');
- pageFooter = document.querySelector('.page-footer');
-
- windowHeight = 940;
-
- // mock the rendering of all components
- screenMock = new helpers.ScreenMock(jest);
- screenMock.setWindow({
- width: 1990,
- height: windowHeight,
- scrollTop: 0
- });
- screenMock.mockPositionAndDimension('heading', heading, {
- offsetHeight: 75,
- offsetWidth: 727,
- offsetTop: 185
- });
- screenMock.mockPositionAndDimension('content', content, {
- offsetHeight: 900,
- offsetWidth: 727,
- offsetTop: heading.offsetTop + heading.offsetHeight
- });
- screenMock.mockPositionAndDimension('pageFooter', pageFooter, {
- offsetHeight: 50,
- offsetWidth: 727,
- offsetTop: content.offsetTop + content.offsetHeight
- });
-
- getFurthestBottomPoint = (stickysTotalHeight) => {
- const headingBottom = getScreenItemBottomPosition(heading);
-
- return headingBottom + PADDING_BEFORE_STOPPING_POINT + stickysTotalHeight;
- };
-
- // the sticky JS polls for changes to position/dimension so we need to fake setTimeout and setInterval
- jest.useFakeTimers();
-
- });
-
- afterEach(() => {
-
- window.NotifyModules.stickAtBottomWhenScrolling.clearEvents();
-
- });
-
- test("if bottom of viewport is below bottom of element on load, the element should not be marked as sticky", () => {
-
- const pageFooterBottom = getScreenItemBottomPosition(pageFooter);
-
- // scroll so the bottom of the window goes past the bottom of the element
- screenMock.scrollTo((pageFooterBottom - windowHeight) + 10);
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- // `.content-fixed-onload` adds the drop-shadow without fading in to show it did not become sticky from user interaction
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(false);
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("if the window is 768px or less wide and the bottom of the viewport is above bottom of element on load, the element should not be marked as sticky", () => {
-
- const pageFooterBottom = getScreenItemBottomPosition(pageFooter);
-
- screenMock.window.resizeTo({
- height: windowHeight,
- width: 768
- });
-
- // scroll past top of form
- screenMock.scrollTo(pageFooterBottom - 10);
-
- window.NotifyModules.stickAtTopWhenScrolling.init();
-
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- });
-
- describe("if bottom of viewport is above bottom of element on load", () => {
-
- beforeEach(() => {
-
- // scroll position defaults to 0 so bottom of window starts at 940px. Element bottom defaults to 1160px.
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- });
-
- test("the element should be marked as already sticky", () => {
-
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(true);
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("a 'shim' element with dimensions matching the sticky element should be added to the document to take up the space it no longer occupies", () => {
-
- const shim = pageFooter.nextElementSibling;
-
- expect(shim).not.toBeNull();
- expect(shim.classList.contains('shim')).toBe(true);
- expect(shim.style.height).toEqual(`${pageFooter.offsetHeight}px`);
- expect(shim.style.marginTop).toEqual(''); // 0px would return an empty string
- expect(shim.style.marginBottom).toEqual(''); // 0px would return an empty string
-
- });
-
- });
-
- test("if bottom of viewport is above the furthest point the bottom of the element can go in the scroll area on load, the element should be marked as stopped", () => {
-
- // change window size so its bottom can go past stopping position
- const windowHeight = 200;
- const furthestBottomPoint = getFurthestBottomPoint(pageFooter.offsetHeight);
-
- screenMock.window.setHeightTo(windowHeight);
-
- // scroll the window bottom past the furthest point
- screenMock.scrollTo((furthestBottomPoint - windowHeight) - 10);
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- // `.content-fixed-onload` adds the drop-shadow without fading in to show it did not become sticky from user interaction
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(true);
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
-
- // elements are stopped by adding inline styles
- expect(pageFooter.style.position).toEqual('absolute');
- expect(pageFooter.style.top).toEqual(`${furthestBottomPoint - pageFooter.offsetHeight}px`);
-
- });
-
- describe("if viewport bottom starts below element bottom", () => {
-
- let pageFooterBottom;
-
- beforeEach(() => {
-
- pageFooterBottom = getScreenItemBottomPosition(pageFooter);
-
- // change window size so its bottom can go past stopping position
- const windowHeight = 600;
-
- screenMock.window.setHeightTo(windowHeight);
-
- // scroll to just below the element
- screenMock.scrollTo((pageFooterBottom - windowHeight) + 10);
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- });
-
- test("if window is scrolled so bottom of it is above the bottom of the element, the element should be marked so it becomes sticky to the user", () => {
-
- // scroll above bottom of sticky element
- screenMock.scrollTo((pageFooterBottom - windowHeight) - 10);
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a scroll
-
- // `content-fixed` fades the drop-shadow in to show it became sticky from user interaction
- expect(pageFooter.classList.contains('content-fixed')).toBe(true);
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- });
-
- test("if window is scrolled so bottom of it is above the furthest point the bottom of the element can go in the scroll area, the element should be stopped", () => {
-
- const furthestBottomPoint = getFurthestBottomPoint(pageFooter.offsetHeight);
-
- // scroll past furthest point
- screenMock.scrollTo((furthestBottomPoint - windowHeight) - 10);
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a scroll
-
- // `content-fixed` fades the drop-shadow in to show it became sticky from user interaction
- expect(pageFooter.classList.contains('content-fixed')).toBe(true);
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- // elements are stopped by adding inline styles
- expect(pageFooter.style.position).toEqual('absolute');
- expect(pageFooter.style.top).toEqual(`${furthestBottomPoint - pageFooter.offsetHeight}px`);
-
- });
-
- test("if window resizes so bottom is above the bottom of the element, the element should be marked so it becomes sticky to the user", () => {
-
- // resize window so the bottom is 30px above the bottom of the element
- screenMock.window.resizeTo({ height: 560, width: screenMock.window.width });
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a resize
-
- // `content-fixed` fades the drop-shadow in to show it became sticky from user interaction
- expect(pageFooter.classList.contains('content-fixed')).toBe(true);
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- });
-
- });
-
- describe("if scrollToRevealElement is called with an element", () => {
-
- let link;
- let linkBottom;
-
- beforeEach(() => {
-
- const contentBottom = getScreenItemBottomPosition(content);
-
- content.insertAdjacentHTML('afterEnd', 'Formatting options');
- link = document.querySelector('#formatting-options');
-
- screenMock.mockPositionAndDimension('link', link, {
- offsetHeight: 25, // 25px smaller than the sticky
- offsetWidth: 727,
- offsetTop: contentBottom
- });
-
- linkBottom = getScreenItemBottomPosition(link);
-
- // move the sticky over the link. It's 50px high so this position will cause it to overlap.
- screenMock.scrollTo((linkBottom - windowHeight) + 5);
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- });
-
- afterEach(() => {
-
- screenMock.window.spies.window.scrollTo.mockClear();
-
- });
-
- test("the window should scroll so the element is revealed", () => {
-
- // update inputForm position as DOM normally would
- pageFooter.offsetTop = screenMock.window.bottom - pageFooter.offsetHeight;
-
- let stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [pageFooter], edge: 'bottom' });
-
- // sticky position should overlap link position
- expect(stickyPosition.top).toBeLessThanOrEqual(link.offsetTop);
- expect(stickyPosition.bottom).toBeGreaterThanOrEqual(linkBottom);
-
- window.NotifyModules.stickAtBottomWhenScrolling.scrollToRevealElement(link)
-
- stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [pageFooter], edge: 'bottom' });
-
- // the top of the sticky element should be at the bottom of the link
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, (linkBottom + pageFooter.offsetHeight) - windowHeight]);
-
- });
-
- });
-
- describe("if viewport bottom starts above element bottom", () => {
-
- let pageFooterBottom;
- let pageFooterShim;
-
- beforeEach(() => {
-
- // scroll position defaults to 0 so bottom of window starts at 940px. Element bottom defaults to 1160px so will be sticky on load.
- pageFooterBottom = getScreenItemBottomPosition(pageFooter);
-
- // shim will be inserted, inheriting space in the page from pageFooter so store this data
- const pageFooterData = {
- offsetHeight: pageFooter.offsetHeight,
- offsetWidth: pageFooter.offsetWidth,
- offsetTop: pageFooter.offsetTop
- };
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- // add mock for shim
- pageFooterShim = document.querySelector('.shim');
- screenMock.mockPositionAndDimension('pageFooterShim', pageFooterShim, pageFooterData);
-
- });
-
- test("if window is scrolled so bottom of it is below the bottom of the element, the element should be made not sticky", () => {
-
- // scroll so bottom of window is below bottom of element
- screenMock.scrollTo((pageFooterBottom - windowHeight) + 10);
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a scroll
-
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(false); // check the class for onload isn't applied
-
- });
-
- test("if window resizes so bottom is below the bottom of the element, the element should be made not sticky", () => {
-
- // resize window so the bottom is 30px above the bottom of the element
- screenMock.window.resizeTo({ height: pageFooterBottom + 10, width: screenMock.window.width });
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a resize
-
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(false);
-
- });
-
- test("if window resizes so bottom is above the furthest point the bottom of the element can go in the scroll area, the element should be stopped", () => {
-
- const furthestBottomPoint = getFurthestBottomPoint(pageFooter.offsetHeight);
-
- // resize window so the bottom is above the furthest point
- screenMock.window.resizeTo({ height: furthestBottomPoint - 10, width: screenMock.window.width });
- jest.advanceTimersByTime(60); // fake advance of time to something similar to that for a resize
-
- expect(pageFooter.classList.contains('content-fixed')).toBe(false); // applied if made sticky after page load
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(true); // check the class for onload isn't applied
-
- // elements are stopped by adding inline styles
- expect(pageFooter.style.position).toEqual('absolute');
- expect(pageFooter.style.top).toEqual(`${furthestBottomPoint - pageFooter.offsetHeight}px`);
-
- });
-
- });
-
- describe("if element is made sticky and another element underneath it is focused", () => {
-
- let checkbox;
- let checkboxBottom;
-
- beforeEach(() => {
-
- const contentBottom = getScreenItemBottomPosition(content);
-
- content.insertAdjacentHTML('afterEnd', '');
- checkbox = document.querySelector('input[type=checkbox]');
-
- screenMock.mockPositionAndDimension('checkbox', checkbox, {
- offsetHeight: 40, // 10px smaller than the sticky
- offsetWidth: 727,
- offsetTop: contentBottom
- });
-
- checkboxBottom = getScreenItemBottomPosition(checkbox);
-
- // move the sticky over the checkbox. It's 50px high so this position will cause it to overlap.
- screenMock.scrollTo((checkboxBottom - windowHeight) + 5);
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- });
-
- afterEach(() => {
-
- screenMock.window.spies.window.scrollTo.mockClear();
-
- });
-
- test("the window should scroll so the focused element is revealed", () => {
-
- // update inputForm position as DOM normally would
- pageFooter.offsetTop = screenMock.window.bottom - pageFooter.offsetHeight;
-
- let stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [pageFooter], edge: 'bottom' });
-
- // sticky position should overlap checkbox position
- expect(stickyPosition.top).toBeLessThanOrEqual(checkbox.offsetTop);
- expect(stickyPosition.bottom).toBeGreaterThanOrEqual(checkboxBottom);
-
- // the sticky element (page footer) is 50 high so should cover the last of the radios if the bottom edge of the viewport is at its bottom
- checkbox.focus();
-
- stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [pageFooter], edge: 'bottom' });
-
- // the top of the sticky element should be at the bottom of the checkbox
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, (checkboxBottom + pageFooter.offsetHeight) - windowHeight]);
-
- });
-
- });
-
- describe("if element is made sticky and overlaps a textarea", () => {
-
- let textarea;
- let textareaBottom;
- let caretCoordinates;
- let caretCoordinatesMock;
-
- beforeEach(() => {
-
- const contentBottom = getScreenItemBottomPosition(content);
-
- content.insertAdjacentHTML('afterEnd', '');
- textarea = document.querySelector('textarea');
-
- // line height: 30px, text height: 19px, 10 lines.
- screenMock.mockPositionAndDimension('textarea', textarea, {
- offsetHeight: 300,
- offsetWidth: 727,
- offsetTop: contentBottom
- });
-
- textareaBottom = getScreenItemBottomPosition(textarea);
-
- // start caret on the last line
- caretCoordinates = new CaretCoordinates();
- caretCoordinates.moveToLine(10);
- caretCoordinatesMock = jest.fn(() => caretCoordinates);
- window.getCaretCoordinates = caretCoordinatesMock;
-
- // move the sticky so it overlaps the bottom 50px of the textarea.
- screenMock.scrollTo(textareaBottom - windowHeight);
-
- // update content position as DOM normally would
- pageFooter.offsetTop = screenMock.window.bottom - pageFooter.offsetHeight;
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- });
-
- afterEach(() => {
-
- screenMock.window.spies.window.scrollTo.mockClear();
- caretCoordinatesMock.mockClear();
-
- });
-
- test("if the textarea receives focus while its caret is underneath, the window should scroll to reveal the caret", () => {
-
- const stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [pageFooter], edge: 'bottom' });
- const caretPosition = getCaretPosition(caretCoordinates, textarea);
-
- // sticky position should overlap caret position
- expect(stickyPosition.top).toBeLessThan(caretPosition.bottom);
-
- textarea.focus();
-
- // the top of the sticky element should be at the bottom of the caret
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, caretPosition.bottom - (windowHeight - stickyPosition.height)]);
-
- });
-
- test("if the caret is moved so it isn't underneath the sticky element, the window shouldn't scroll", () => {
-
- // start caret on 8th line which isn't under the sticky element.
- caretCoordinates.moveToLine(8);
-
- const stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [pageFooter], edge: 'bottom' });
- const caretPosition = getCaretPosition(caretCoordinates, textarea);
-
- // the sticky element should be below the caret
- expect(stickyPosition.top).toBeGreaterThan(caretPosition.bottom);
-
- textarea.focus();
-
- // no scrolling should have happened
- expect(screenMock.window.spies.window.scrollTo.mock.calls.length).toEqual(0);
-
- });
-
- test("if the caret is moved to be underneath the sticky element, the window should scroll to reveal the caret", () => {
-
- // start caret on 8th line which isn't under the sticky element.
- caretCoordinates.moveToLine(8);
-
- // make sure the textarea has focus
- textarea.focus();
-
- // move the caret underneath the sticky element
- caretCoordinates.moveToLine(9);
-
- const stickyPosition = getStickyGroupPosition(screenMock, { stickyEls: [pageFooter], edge: 'bottom' });
- const caretPosition = getCaretPosition(caretCoordinates, textarea);
-
- // sticky position should overlap caret position
- expect(stickyPosition.top).toBeLessThan(caretPosition.bottom);
-
- // simulate a press of the down arrow
- helpers.triggerEvent(textarea, 'keyup', { interface: window.KeyboardEvent });
-
- // the top of the sticky element should be at the bottom of the caret
- expect(screenMock.window.spies.window.scrollTo.mock.calls[0]).toEqual([0, caretPosition.bottom - (windowHeight - stickyPosition.height)]);
-
- });
-
- });
-
- describe("if mode is set to 'dialog' and multiple sticky elements have the same scroll area", () => {
-
- let radios;
-
- beforeEach(() => {
-
- const contentBottom = getScreenItemBottomPosition(content);
-
- // set mode to 'dialog' so sticky elements are treated as one item
- window.NotifyModules.stickAtBottomWhenScrolling.setMode('dialog')
-
- // add another sticky element before the form footer
- radios = helpers.getRadioGroup({
- cssClasses: ['js-stick-at-bottom-when-scrolling'],
- name: 'choose-send-time',
- label: 'Choose send time',
- fields: [
- {
- label: 'Now',
- value: 'now',
- checked: true
- },
- {
- label: 'Tomorrow',
- value: 'tomorrow',
- checked: false
- },
- {
- label: 'Friday',
- value: 'friday',
- checked: false
- }
- ]
- });
-
- pageFooter.parentNode.insertBefore(radios, pageFooter);
- screenMock.mockPositionAndDimension('radios', radios, {
- offsetHeight: 175,
- offsetWidth: 727,
- offsetTop: contentBottom
- });
- const radiosBottom = getScreenItemBottomPosition(radios);
-
- // adjust other screen items
-
- // pageFooter top should be at the radios bottom
- pageFooter.offsetTop = radiosBottom;
-
- });
-
- afterEach(() => {
-
- window.NotifyModules.stickAtBottomWhenScrolling.setMode('default');
-
- });
-
- describe("if window bottom is above the bottom of the lowest element on load", () => {
-
- let pageFooterBottom;
-
- beforeEach(() => {
-
- pageFooterBottom = getScreenItemBottomPosition(pageFooter);
-
- // scroll to just above the element
- screenMock.scrollTo((pageFooterBottom - windowHeight) - 10);
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- });
-
- test("both should be marked as already sticky", () => {
-
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(true);
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("the highest element should have the drop-shadow", () => {
-
- expect(radios.classList.contains('content-fixed__bottom')).toBe(true);
-
- });
-
- test("they should be stacked from the bottom edge of the viewport in the order they appear in the document", () => {
-
- expect(radios.style.bottom).toEqual(`${pageFooter.offsetHeight - PADDING_BETWEEN_STICKYS}px`);
-
- });
-
- });
-
- describe("if window bottom is above the furthest point the lowest element can go on load", () => {
-
- let headingBottom;
-
- beforeEach(() => {
-
- const dialogHeight = (pageFooter.offsetHeight + radios.offsetHeight) - PADDING_BETWEEN_STICKYS;
- const furthestBottomPoint = getFurthestBottomPoint(dialogHeight);
-
- // change window size so its bottom can go past stopping position
- const windowHeight = 200;
-
- headingBottom = getScreenItemBottomPosition(heading);
-
- screenMock.window.setHeightTo(windowHeight);
-
- screenMock.scrollTo((furthestBottomPoint - windowHeight) - 10)
-
- window.NotifyModules.stickAtBottomWhenScrolling.init();
-
- });
-
- test("both should be stopped so the top of their stack is at the furthest point", () => {
-
- expect(radios.style.position).toEqual('absolute');
- expect(pageFooter.style.position).toEqual('absolute');
- expect(radios.style.top).toEqual(`${headingBottom + PADDING_BEFORE_STOPPING_POINT}px`);
- expect(pageFooter.style.top).toEqual(`${headingBottom + PADDING_BEFORE_STOPPING_POINT + (radios.offsetHeight - PADDING_BETWEEN_STICKYS)}px`);
-
- });
-
- });
-
- describe("if the group of sticky elements is taller than the window when stuck together", () => {
-
- let windowHeight;
- let pageFooterBottom;
-
- // the sticky behaviour should stick as many elements as possible to the bottom of the viewport
- // the rest should be put back into their place in the document
- beforeEach(() => {
-
- windowHeight = 460;
-
- function makeStickysBiggerThanWindow () {
-
- // make the radios too big to fit in the viewport
- const fields = ['Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
- .map(field => {
- return {
- label: field,
- value: field.toLowerCase(),
- checked: false
- }
- });
-
- radios.querySelector('fieldset').insertAdjacentHTML('beforeend', helpers.getRadios(fields, 'days'));
-
- radios.offsetHeight = 475;
-
- };
-
- function adjustScreenPositions () {
-
- const radiosBottom = getScreenItemBottomPosition(radios);
-
- screenMock.window.setHeightTo(windowHeight);
-
- // formFooter top should be at the radios bottom
- pageFooter.offsetTop = radiosBottom;
-
- };
-
- makeStickysBiggerThanWindow();
- adjustScreenPositions();
-
- pageFooterBottom = getScreenItemBottomPosition(pageFooter);
-
- screenMock.scrollTo((pageFooterBottom - windowHeight) - 10);
-
- window.NotifyModules.stickAtBottomWhenScrolling.init()
-
- });
-
- test("the number of elements in the group that are stuck should be reduced until it fits into the viewport", () => {
-
- // when the group is stuck, pageFooter is closest to the viewport edge so should stay sticky
- expect(pageFooter.classList.contains('content-fixed-onload')).toBe(true);
- expect(pageFooter.classList.contains('content-fixed')).toBe(false);
-
- // radios are made not sticky
- expect(radios.classList.contains('content-fixed-onload')).toBe(false);
- expect(radios.classList.contains('content-fixed')).toBe(false);
-
- });
-
- test("window is scrolled so the sticky elements (stuck and not stuck) appear in sequence", () => {
-
- // we assume it's useful to start by seeing all the elements in the position they normally sit in the page
- expect(screenMock.window.top).toEqual(pageFooterBottom - windowHeight);
-
- });
-
- });
-
- });
-
- });
-
-});
diff --git a/tests/javascripts/support/jest.setup.js b/tests/javascripts/support/jest.setup.js
index 59a5dee29..39c342018 100644
--- a/tests/javascripts/support/jest.setup.js
+++ b/tests/javascripts/support/jest.setup.js
@@ -1,8 +1,6 @@
const fs = require('fs');
const path = require('path');
-// Set up jQuery
-global.$ = global.jQuery = require('jquery');
// tests/jest.setup.js
global.io = jest.fn().mockReturnValue({
diff --git a/tests/javascripts/support/setup.js b/tests/javascripts/support/setup.js
index 17084446f..010be5c81 100644
--- a/tests/javascripts/support/setup.js
+++ b/tests/javascripts/support/setup.js
@@ -3,6 +3,3 @@ const path = require('path');
// Fill in the gaps where JSDOM doesn't quite match real browsers
require('./polyfills.js');
-
-// Make jQuery available everywhere
-global.$ = global.jQuery = require('jquery');
diff --git a/tests/javascripts/support/teardown.js b/tests/javascripts/support/teardown.js
index e4ee16fc4..9ba30070b 100644
--- a/tests/javascripts/support/teardown.js
+++ b/tests/javascripts/support/teardown.js
@@ -1,6 +1,3 @@
-window.jQuery = null;
-$ = null;
-
delete window.USWDS;
jest.restoreAllMocks();
diff --git a/webpack.config.js b/webpack.config.js
index e7c69700f..438273443 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -77,11 +77,6 @@ module.exports = {
},
plugins: [
- new webpack.ProvidePlugin({
- $: 'jquery',
- jQuery: 'jquery',
- 'window.jQuery': 'jquery'
- }),
new MiniCssExtractPlugin({
filename: '../css/styles.css'
}),