diff --git a/app/assets/javascripts/fullscreenTable.js b/app/assets/javascripts/fullscreenTable.js index 5a690022b..799e518d1 100644 --- a/app/assets/javascripts/fullscreenTable.js +++ b/app/assets/javascripts/fullscreenTable.js @@ -24,10 +24,10 @@ .on('scroll', this.maintainHeight); if ( - window.GOVUK.stopScrollingAtFooter && - window.GOVUK.stopScrollingAtFooter.updateFooterTop + window.GOVUK.stickAtBottomWhenScrolling && + window.GOVUK.stickAtBottomWhenScrolling.recalculate ) { - window.GOVUK.stopScrollingAtFooter.updateFooterTop(); + window.GOVUK.stickAtBottomWhenScrolling.recalculate(); } this.maintainWidth(); diff --git a/package.json b/package.json index 71c11b281..098318c9f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "node": "10.15.3" }, "scripts": { - "test": "gulp lint && jest tests/javascripts", + "test": "gulp lint && jest --config tests/javascripts/jest.config.js tests/javascripts", "build": "gulp", "watch": "gulp watch" }, diff --git a/tests/javascripts/autofocus.test.js b/tests/javascripts/autofocus.test.js index 8419b2d62..cae67996f 100644 --- a/tests/javascripts/autofocus.test.js +++ b/tests/javascripts/autofocus.test.js @@ -1,18 +1,9 @@ beforeAll(() => { - // set up jQuery - window.jQuery = require('jquery'); - $ = window.jQuery; - - // load module code - require('govuk_frontend_toolkit/javascripts/govuk/modules.js'); require('../../app/assets/javascripts/autofocus.js'); }); afterAll(() => { - window.jQuery = null; - $ = null; - - delete window.GOVUK; + require('./support/teardown.js'); }); describe('Autofocus', () => { diff --git a/tests/javascripts/collapsibleCheckboxes.test.js b/tests/javascripts/collapsibleCheckboxes.test.js index b29a4d624..88cc74e17 100644 --- a/tests/javascripts/collapsibleCheckboxes.test.js +++ b/tests/javascripts/collapsibleCheckboxes.test.js @@ -1,13 +1,6 @@ const helpers = require('./support/helpers'); beforeAll(() => { - // set up jQuery - window.jQuery = require('jquery'); - $ = window.jQuery; - - // load module code - require('govuk_frontend_toolkit/javascripts/govuk/modules.js'); - // TODO: remove this when tests for sticky JS are written require('../../app/assets/javascripts/stick-to-window-when-scrolling.js'); @@ -15,10 +8,7 @@ beforeAll(() => { }); afterAll(() => { - window.jQuery = null; - $ = null; - - delete window.GOVUK; + require('./support/teardown.js'); }); diff --git a/tests/javascripts/fullscreenTable.test.js b/tests/javascripts/fullscreenTable.test.js new file mode 100644 index 000000000..c1130f1ce --- /dev/null +++ b/tests/javascripts/fullscreenTable.test.js @@ -0,0 +1,313 @@ +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/fullscreenTable.js'); +}); + +afterAll(() => { + require('./support/teardown.js'); +}); + +describe('FullscreenTable', () => { + let windowMock; + let container; + let tableFrame; + let table; + let numberColumnFrame; + + beforeEach(() => { + + const tableHeadings = () => { + let result = ''; + const headings = ['1', 'name', 'email address', 'age', 'building number', 'address line 1', 'address line 2', 'postcode']; + + headings.forEach((heading, idx) => { + if (idx === 0) { + result += ` + Row in file + `; + } else { + result += ` + ${heading} + `; + } + }); + + return result; + } + + const rowCells = (cells) => { + let result = ''; + + Object.keys(cells).forEach((key, idx) => { + if (idx === 0) { + result += ` + + ${key} + + `; + } else { + result += ` +
+ ${key} +
+ `; + } + }); + + return result; + }; + + const tableRows = () => { + let result = ''; + + const rows = [ + ['John Logie Baird', 'johnlbaird@gmail.com', '37', '22', 'Frith Street', 'Soho, London', 'W1D 4RF'], + ['Guglielmo Marconi', 'gmarconi@hotmail.com', '21', 'Pontecchio Marconi', 'Via Celestini 1', 'Bologna', ''], + ['Louis Braille', 'louisbraille@yahoo.co.uk', '', '56', 'Boulevard des Invalides', 'Paris', '75007'], + ['Ray Tomlinson', 'hedy.lamarr@msn.com', '25', '870', '870 Winter Street', 'Waltham', 'MA 02451'] + ]; + + rows.forEach(row => { + result += `${rowCells(row)}`; + }); + + return result; + + } + + windowMock = new helpers.WindowMock(jest); + + // set up DOM + document.body.innerHTML = + `
+
+ + + + + ${tableHeadings()} + + + + ${tableRows()} + +
+ people.csv +
+
+
`; + + container = document.querySelector('.fullscreen-content'); + + }); + + afterEach(() => { + + document.body.innerHTML = ''; + + }); + + describe("when it loads", () => { + + test("it fixes the number column for each row without changing the semantics", () => { + + // start module + window.GOVUK.modules.start(); + + tableFrame = document.querySelector('.fullscreen-scrollable-table'); + numberColumnFrame = document.querySelector('.fullscreen-fixed-table'); + + expect(tableFrame).not.toBeNull(); + expect(numberColumnFrame).not.toBeNull(); + expect(numberColumnFrame.getAttribute('role')).toEqual('presentation'); + + }); + + test("it calls the sticky JS to update any cached dimensions", () => { + + const stickyJSSpy = jest.spyOn(window.GOVUK.stickAtBottomWhenScrolling, 'recalculate'); + + // start module + window.GOVUK.modules.start(); + + expect(stickyJSSpy.mock.calls.length).toBe(1); + + stickyJSSpy.mockClear(); + + }); + + }); + + describe("the height of the table should fit the vertical space available to it", () => { + + let containerBoundingClientRectSpy; + + beforeEach(() => { + + // set the height and offset of the window and table container from the top of the document + // so just the top 268px of it appears on-screen + windowMock.setHeightTo(768); + container.setAttribute('style', 'height: 1000px'); + containerBoundingClientRectSpy = jest.spyOn(container, 'getBoundingClientRect') + containerBoundingClientRectSpy.mockImplementation(() => { return { top: 500 } }); + + // start module + window.GOVUK.modules.start(); + + tableFrame = document.querySelector('.fullscreen-scrollable-table'); + numberColumnFrame = document.querySelector('.fullscreen-fixed-table'); + + }); + + afterEach(() => { + + windowMock.reset(); + containerBoundingClientRectSpy.mockClear(); + + }); + + test("when the page has loaded", () => { + + // the frames should crop to the top 268px of the table that is visible + expect(window.getComputedStyle(tableFrame)['height']).toEqual('268px'); + expect(window.getComputedStyle(numberColumnFrame)['height']).toEqual('268px'); + + }); + + test("when the page has scrolled", () => { + + // scroll the window so the table fills the height of the window (768px) + windowMock.scrollBy(500); + + // the frames should crop to the window height + expect(window.getComputedStyle(tableFrame)['height']).toEqual('768px'); + expect(window.getComputedStyle(numberColumnFrame)['height']).toEqual('768px'); + + }); + + test("when the page has resized", () => { + + // resize the window by 232px (from 768px to 1000px) + windowMock.resizeTo({ height: 1000, width: 1024 }); + + // the frames should crop to the top 500px of the table now visible + expect(window.getComputedStyle(tableFrame)['height']).toEqual('500px'); + expect(window.getComputedStyle(numberColumnFrame)['height']).toEqual('500px'); + + }); + + }); + + describe("the width of the table should fit the horizontal space available to it", () => { + let rowNumberColumnCell; + + beforeEach(() => { + + rowNumberColumnCell = container.querySelector('.table-field-index'); + + // set main content column width (used as module as gauge for table width) + windowMock.setWidthTo(1024); + document.querySelector('main').setAttribute('style', 'width: 742px'); + + // set total width of column for row numbers in table to 40px + rowNumberColumnCell.setAttribute('style', 'width: 40px'); + + // start module + window.GOVUK.modules.start(); + + tableFrame = document.querySelector('.fullscreen-scrollable-table'); + numberColumnFrame = document.querySelector('.fullscreen-fixed-table'); + + }); + + afterEach(() => { + + windowMock.reset(); + + }); + + test("when the page has loaded", () => { + + // table should set its width to be that of `
`, minus margin-left for the row numbers column + expect(window.getComputedStyle(tableFrame)['width']).toEqual('702px'); // width of content column - numbers column + expect(window.getComputedStyle(tableFrame)['margin-left']).toEqual('40px'); // width of numbers column + + // table for number column has 4px extra to allow space for drop shadow + expect(window.getComputedStyle(numberColumnFrame)['width']).toEqual('44px'); + + }); + + test("when the page has resized", () => { + + // resize window and content column + document.querySelector('main').setAttribute('style', 'width: 720px'); + windowMock.resizeTo({ height: 768, width: 960 }); + + // table should set its width to be that of `
`, minus margin-left for the row numbers column + expect(window.getComputedStyle(tableFrame)['width']).toEqual('680px'); // width of content column - numbers column + expect(window.getComputedStyle(tableFrame)['margin-left']).toEqual('40px'); // width of numbers column + + // table for number column has 4px extra to allow space for drop shadow + expect(window.getComputedStyle(numberColumnFrame)['width']).toEqual('44px'); + + }); + + }); + + describe("when the table scrolls horizontally", () => { + let rightEdgeShadow; + + beforeEach(() => { + + // start module + window.GOVUK.modules.start(); + + tableFrame = document.querySelector('.fullscreen-scrollable-table'); + table = tableFrame.querySelector('table'); + numberColumnFrame = document.querySelector('.fullscreen-fixed-table'); + rightEdgeShadow = container.querySelector('.fullscreen-right-shadow'); + + tableFrame.setAttribute('style', 'width: 742px'); + table.setAttribute('style', 'width: 1000px'); + + }); + + test("the right edge of the table scroll area should have a drop-shadow if it isn't scrolled", () => { + + tableFrame.scrollLeft = 0; + helpers.triggerEvent(tableFrame, 'scroll'); + + expect(numberColumnFrame.classList.contains('fullscreen-scrolled-table')).toBe(false); + expect(rightEdgeShadow.classList.contains('visible')).toBe(true); + + }); + + test("the left edge of the table scroll area should have a drop-shadow if the table is scrolled to 100%", () => { + + // scroll to end of table + tableFrame.scrollLeft = 258; + helpers.triggerEvent(tableFrame, 'scroll'); + + expect(numberColumnFrame.classList.contains('fullscreen-scrolled-table')).toBe(true); + expect(rightEdgeShadow.classList.contains('visible')).toBe(false); + + }); + + test("both edges of the table scroll area should have a drop-shadow if the table is scrolled between 0% and 100%", () => { + + // scroll to middle of table + tableFrame.scrollLeft = 129; + helpers.triggerEvent(tableFrame, 'scroll'); + + expect(numberColumnFrame.classList.contains('fullscreen-scrolled-table')).toBe(true); + expect(rightEdgeShadow.classList.contains('visible')).toBe(true); + + }); + + }); + +}); diff --git a/tests/javascripts/jest.config.js b/tests/javascripts/jest.config.js new file mode 100644 index 000000000..9fde149df --- /dev/null +++ b/tests/javascripts/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + setupFiles: ['./support/setup.js'] +} diff --git a/tests/javascripts/listEntry.test.js b/tests/javascripts/listEntry.test.js index 80c67e3ae..dd58bdeb3 100644 --- a/tests/javascripts/listEntry.test.js +++ b/tests/javascripts/listEntry.test.js @@ -1,19 +1,11 @@ beforeAll(() => { - // set up jQuery & HoganJS - window.jQuery = require('jquery'); - $ = window.jQuery; Hogan = require('hogan.js'); - // load module code - require('govuk_frontend_toolkit/javascripts/govuk/modules.js'); require('../../app/assets/javascripts/listEntry.js'); }); afterAll(() => { - window.jQuery = null; - $ = null; - - delete window.GOVUK; + require('./support/teardown.js'); }); describe("List entry", () => { diff --git a/tests/javascripts/support/helpers.js b/tests/javascripts/support/helpers.js index f951f317b..52ce86cda 100644 --- a/tests/javascripts/support/helpers.js +++ b/tests/javascripts/support/helpers.js @@ -75,6 +75,66 @@ class ElementQuery { } }; +class WindowMock { + constructor (jest) { + this._defaults = { + height: window.innerHeight, + width: window.innerWidth + }; + this._spies = { + document: {} + }; + this._jest = jest; + } + + setHeightTo (height) { + + // mock DOM calls for window height + window.innerHeight = height; + // remove calls to document.documentElement.clientHeight when jQuery is gone. It's called to support older browsers like IE8 + this._spies.document.clientHeight = this._jest.spyOn(document.documentElement, 'clientHeight', 'get').mockImplementation(() => height); + + } + + setWidthTo (width) { + + // mock DOM calls for window width + window.innerWidth = width; + // remove calls to document.documentElement.clientWidth when jQuery is gone. It's called to support older browsers like IE8 + this._spies.document.clientWidth = this._jest.spyOn(document.documentElement, 'clientWidth', 'get').mockImplementation(() => height); + + } + + resizeTo (dimensions) { + + this.setHeightTo(dimensions.height); + this.setWidthTo(dimensions.width); + triggerEvent(window, 'resize'); + + } + + scrollBy (scrollPosition) { + + document.documentElement.scrollTop = scrollPosition; + triggerEvent(window, 'scroll'); + + } + + reset () { + + window.innerHeight = this._defaults.height; + window.innerWidth = this._defaults.width; + document.documentElement.scrollTop = 0; + + // reset all spies + Object.keys(this._spies).forEach(key => { + const objectSpies = this._spies[key]; + Object.keys(objectSpies).forEach(key => objectSpies[key].mockClear()); + }); + + } +} + // function to ask certain questions of a DOM Element const element = function (el) { return new ElementQuery(el); @@ -82,3 +142,4 @@ const element = function (el) { exports.triggerEvent = triggerEvent; exports.element = element; +exports.WindowMock = WindowMock; diff --git a/tests/javascripts/support/setup.js b/tests/javascripts/support/setup.js new file mode 100644 index 000000000..be3fa1e6b --- /dev/null +++ b/tests/javascripts/support/setup.js @@ -0,0 +1,6 @@ +// set up jQuery +window.jQuery = require('jquery'); +$ = window.jQuery; + +// load module code +require('govuk_frontend_toolkit/javascripts/govuk/modules.js'); diff --git a/tests/javascripts/support/teardown.js b/tests/javascripts/support/teardown.js new file mode 100644 index 000000000..c7d00bf8b --- /dev/null +++ b/tests/javascripts/support/teardown.js @@ -0,0 +1,4 @@ +window.jQuery = null; +$ = null; + +delete window.GOVUK;