mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-05-07 17:38:32 -04:00
Add tests for when caret is underneath sticky
If focus moves to a textarea, we care more about the caret being overlapped than the textarea. This adds tests for the caret being overlapped on load and as a result of it moving underneath the sticky element from a keyboard event.
This commit is contained in:
@@ -6,6 +6,16 @@ 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];
|
||||
@@ -29,6 +39,21 @@ function getStickyGroupPosition (screenMock, opts) {
|
||||
|
||||
};
|
||||
|
||||
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');
|
||||
});
|
||||
@@ -306,6 +331,116 @@ describe("Stick to top/bottom of window when scrolling", () => {
|
||||
|
||||
});
|
||||
|
||||
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 name="notes"></textarea>');
|
||||
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.GOVUK.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;
|
||||
@@ -799,6 +934,112 @@ describe("Stick to top/bottom of window when scrolling", () => {
|
||||
|
||||
});
|
||||
|
||||
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 name="notes"></textarea>');
|
||||
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.GOVUK.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;
|
||||
|
||||
Reference in New Issue
Block a user