diff --git a/.travis.yml b/.travis.yml index a2f4ebc4b..d32fd0eca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,9 @@ language: node_js node_js: # we recommend testing addons with the same minimum supported node version as Ember CLI # so that your addon works for all apps - - '8' + - '12' -dist: trusty +dist: xenial addons: chrome: stable diff --git a/addon/-private/sticky/table-sticky-polyfill.js b/addon/-private/sticky/table-sticky-polyfill.js index 9416f6752..eb2503ca9 100644 --- a/addon/-private/sticky/table-sticky-polyfill.js +++ b/addon/-private/sticky/table-sticky-polyfill.js @@ -92,7 +92,7 @@ class TableStickyPolyfill { add row 0's height (25px) to its current offset and move on to row 1, where it will set each of that row's `th` cells' `top` to the current offset of `25px`. - * For the tfoot TableStickyPolyfill, its `respositionStickyElements` will + * For the tfoot TableStickyPolyfill, its `repositionStickyElements` will start at the bottom-most row, row 1, and set each of its `td` cells' `bottom` value to `0px`, then add row 1's height (20px) to its current offset and move on to the next row, row 0, where it will set each of that @@ -187,8 +187,9 @@ class TableStickyPolyfill { for (let i = 0; i < rows.length; i++) { // Work top-down (index order) for 'top', bottom-up (reverse index // order) for 'bottom' rows - let row = rows[this.side === 'top' ? i : rows.length - 1 - i]; - let height = heights[i]; + let index = this.side === 'top' ? i : rows.length - 1 - i; + let row = rows[index]; + let height = heights[index]; for (let child of row.children) { child.style.position = '-webkit-sticky'; diff --git a/package.json b/package.json index d77041dd8..f8334b701 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "release-it": "^12.3.4" }, "engines": { - "node": "8.* || >= 10.*" + "node": ">= 12" }, "ember-addon": { "configPath": "tests/dummy/config" diff --git a/tests/unit/-private/table-sticky-polyfill-test.js b/tests/unit/-private/table-sticky-polyfill-test.js index 31d9eb5e5..9e32364a8 100644 --- a/tests/unit/-private/table-sticky-polyfill-test.js +++ b/tests/unit/-private/table-sticky-polyfill-test.js @@ -52,14 +52,23 @@ const standardTemplate = hbs` `; -function constructMatrix(n, m, prefix = '') { +/** + * Constructs a matrix m by n + * @param m Rows + * @param n Columns + * @param prefix Prefix string + * @param skipPrefixOnRowIndices Skip adding the prefix string to the row (m) indices specified in the list + * @returns {Array} matrix + */ +function constructMatrix(m, n, prefix = '', skipPrefixOnRowIndices = []) { let rows = emberA(); - for (let i = 0; i < n; i++) { + for (let i = 0; i < m; i++) { let cols = emberA(); - for (let j = 0; j < m; j++) { - cols.pushObject(`${m}${prefix}`); + for (let j = 0; j < n; j++) { + let skipPrefix = skipPrefixOnRowIndices.includes(i); + cols.pushObject(skipPrefix ? n : `${n}${prefix}`); } rows.pushObject(cols); @@ -100,6 +109,36 @@ function verifyFooter(assert) { }); } +/** + * Verifies multi line header when scrolled to the bottom of the table + * @param assert + */ +function verifyMultiLineHeader(assert) { + let firstCellRect = find('thead tr:first-child th:first-child').getBoundingClientRect(); + let expectedOffset = firstCellRect.top; + + findAll('thead > tr').forEach(row => { + let firstCellRect = row.firstElementChild.getBoundingClientRect(); + expectedOffset += firstCellRect.height; + assert.equal(expectedOffset, firstCellRect.bottom); + }); +} + +/** + * Verifies multi line footer when scrolled to the top of the table + * @param assert + */ +function verifyMultiLineFooter(assert) { + let firstCellRect = find('tfoot tr:first-child td:first-child').getBoundingClientRect(); + let expectedOffset = firstCellRect.top; + + findAll('tfoot > tr').forEach(row => { + let firstCellRect = row.firstElementChild.getBoundingClientRect(); + expectedOffset += firstCellRect.height; + assert.equal(expectedOffset, firstCellRect.bottom); + }); +} + componentModule('Unit | Private | TableStickyPolyfill', function() { test('it works', async function(assert) { this.set('headerRows', constructMatrix(3, 3, 'thead')); @@ -293,4 +332,43 @@ componentModule('Unit | Private | TableStickyPolyfill', function() { 'the bottom of the last header cell is close to 50% of the way down the table' ); }); + + test('when the header has rows with varying heights', async function(assert) { + this.set( + 'headerRows', + constructMatrix(2, 3, 'table header has multiple lines of content', [1]) + ); + this.set('bodyRows', constructMatrix(20, 3, 'body')); + this.set('footerRows', constructMatrix(1, 3, 'footer')); + + await this.render(standardTemplate); + + setupTableStickyPolyfill(find('thead')); + setupTableStickyPolyfill(find('tfoot')); + + await wait(); + + let container = find('.ember-table'); + await scrollTo('.ember-table', 0, container.scrollHeight); + + verifyMultiLineHeader(assert); + }); + + test('when the footer has rows with varying heights', async function(assert) { + this.set('headerRows', constructMatrix(1, 3, 'header')); + this.set('bodyRows', constructMatrix(20, 3, 'body')); + this.set( + 'footerRows', + constructMatrix(2, 3, 'table footer has multiple lines of content', [1]) + ); + + await this.render(standardTemplate); + + setupTableStickyPolyfill(find('thead')); + setupTableStickyPolyfill(find('tfoot')); + + await wait(); + + verifyMultiLineFooter(assert); + }); });