mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-04-10 12:24:26 -04:00
Merge pull request #3465 from alphagov/add-scroll-to-reveal-method
Add scroll to reveal element method
This commit is contained in:
@@ -10,7 +10,10 @@
|
||||
var $el = el.$fixedEl;
|
||||
var $scrollArea = $el.closest('.sticky-scroll-area');
|
||||
|
||||
$scrollArea = $scrollArea.length ? $scrollArea : $el.parent();
|
||||
if($scrollArea.length === 0) {
|
||||
$scrollArea = $el.parent();
|
||||
$scrollArea.addClass('sticky-scroll-area');
|
||||
}
|
||||
|
||||
this._els = [el];
|
||||
this.edge = edge;
|
||||
@@ -46,11 +49,11 @@
|
||||
|
||||
return focused;
|
||||
},
|
||||
forCaret: function (evt) {
|
||||
var textarea = evt.target;
|
||||
forCaret: function ($textarea) {
|
||||
var textarea = $textarea.get(0);
|
||||
var caretCoordinates = window.getCaretCoordinates(textarea, textarea.selectionEnd);
|
||||
var focused = {
|
||||
'top': $(textarea).offset().top + caretCoordinates.top,
|
||||
'top': $textarea.offset().top + caretCoordinates.top,
|
||||
'height': caretCoordinates.height,
|
||||
'type': 'caret'
|
||||
};
|
||||
@@ -61,21 +64,23 @@
|
||||
}
|
||||
};
|
||||
ScrollArea.prototype.focusHandler = function (e) {
|
||||
var $focusedElement = $(document.activeElement);
|
||||
var nodeName = $focusedElement.get(0).nodeName.toLowerCase();
|
||||
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 $focusedElement.closest(this.selector).length > 0;
|
||||
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(e);
|
||||
focused = this.getFocusedDetails.forCaret($el);
|
||||
} else {
|
||||
if (isInSticky()) { return; }
|
||||
focused = this.getFocusedDetails.forElement($focusedElement);
|
||||
focused = this.getFocusedDetails.forElement($el);
|
||||
}
|
||||
|
||||
overlap = focusOverlap.getOverlap(focused, this.edge, endOfFurthestEl);
|
||||
@@ -586,6 +591,18 @@
|
||||
|
||||
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);
|
||||
|
||||
@@ -292,6 +292,61 @@ describe("Stick to top/bottom of window when scrolling", () => {
|
||||
|
||||
});
|
||||
|
||||
describe("if scrollToRevealElement is called with an element", () => {
|
||||
|
||||
let link;
|
||||
let linkBottom;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
const inputFormBottom = getScreenItemBottomPosition(inputForm);
|
||||
|
||||
inputForm.insertAdjacentHTML('afterEnd', '<a href="" id="formatting-options">Formatting options</a>');
|
||||
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.GOVUK.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.GOVUK.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;
|
||||
@@ -904,6 +959,61 @@ describe("Stick to top/bottom of window when scrolling", () => {
|
||||
|
||||
});
|
||||
|
||||
describe("if scrollToRevealElement is called with an element", () => {
|
||||
|
||||
let link;
|
||||
let linkBottom;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
const contentBottom = getScreenItemBottomPosition(content);
|
||||
|
||||
content.insertAdjacentHTML('afterEnd', '<a href="" id="formatting-options">Formatting options</a>');
|
||||
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.GOVUK.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.GOVUK.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;
|
||||
|
||||
Reference in New Issue
Block a user