From 53a190ba64af8dd517463a376d305bb7b5715876 Mon Sep 17 00:00:00 2001 From: Jan Siegel Date: Wed, 3 Apr 2024 13:17:18 +0200 Subject: [PATCH] Ensure that the bottom/right cell borders are visible in the viewport after scrolling from the absolute top/absolute left table position. (#10887) * - Compensate for the header border when API-scrolling from the absolute top/absolute left position of the table - Modify the test cases to match the new scrollLeft/scrollTop values - Add a test case ensuring the cell borders are visible after scrolling * Add the changelog entry. * Simplify and clean up some of the conditions. * Correct the fresh test cases to the scroll values from this PR --- .changelogs/10887.json | 8 +++ .../walkontable/src/overlay/inlineStart.js | 23 +++++++- .../3rdparty/walkontable/src/overlay/top.js | 20 +++++++ .../src/3rdparty/walkontable/src/overlays.js | 47 ++++++++++++++- .../walkontable/test/spec/overlay.spec.js | 8 +-- .../walkontable/test/spec/scroll.spec.js | 4 +- .../__tests__/focusSelection.spec.js | 12 ++-- handsontable/src/helpers/dom/element.js | 20 +++++++ .../autoRowSize/__tests__/autoRowSize.spec.js | 4 +- .../__tests__/keyboardShortcuts.spec.js | 5 +- .../__tests__/dropdownMenu.spec.js | 5 +- .../__tests__/navigation.spec.js | 9 +-- .../test/e2e/core/scrollToFocusedCell.spec.js | 3 +- .../test/e2e/core/scrollViewportTo.spec.js | 59 +++++++++++++++++-- handsontable/test/e2e/core/selectAll.spec.js | 48 +++++++-------- .../beforeViewportScrollHorizontally.spec.js | 9 ++- .../beforeViewportScrollVertically.spec.js | 4 +- .../keyboardShortcuts/viewportScroll.spec.js | 10 ++-- 18 files changed, 235 insertions(+), 63 deletions(-) create mode 100644 .changelogs/10887.json diff --git a/.changelogs/10887.json b/.changelogs/10887.json new file mode 100644 index 00000000000..360dd7f5737 --- /dev/null +++ b/.changelogs/10887.json @@ -0,0 +1,8 @@ +{ + "issuesOrigin": "private", + "title": "Fixed a problem where the bottom/right cell borders were not visible in the viewport after scrolling from the absolute top/left positions of the table using the API.", + "type": "fixed", + "issueOrPR": 10887, + "breaking": false, + "framework": "none" +} diff --git a/handsontable/src/3rdparty/walkontable/src/overlay/inlineStart.js b/handsontable/src/3rdparty/walkontable/src/overlay/inlineStart.js index 5d996ec6613..5420ff7b7c3 100644 --- a/handsontable/src/3rdparty/walkontable/src/overlay/inlineStart.js +++ b/handsontable/src/3rdparty/walkontable/src/overlay/inlineStart.js @@ -2,6 +2,7 @@ import { addClass, getScrollbarWidth, getScrollLeft, + getMaximumScrollLeft, getWindowScrollTop, hasClass, outerWidth, @@ -247,9 +248,17 @@ export class InlineStartOverlay extends Overlay { * @returns {boolean} */ scrollTo(sourceCol, beyondRendered) { - let newX = this.getTableParentOffset(); + const { wtSettings } = this; + const rowHeaders = wtSettings.getSetting('rowHeaders'); + const fixedColumnsStart = wtSettings.getSetting('fixedColumnsStart'); const sourceInstance = this.wot.cloneSource ? this.wot.cloneSource : this.wot; const mainHolder = sourceInstance.wtTable.holder; + const rowHeaderBorderCompensation = ( + fixedColumnsStart === 0 && + rowHeaders.length > 0 && + !hasClass(mainHolder.parentNode, 'innerBorderInlineStart') + ) ? 1 : 0; + let newX = this.getTableParentOffset(); let scrollbarCompensation = 0; if (beyondRendered) { @@ -264,10 +273,11 @@ export class InlineStartOverlay extends Overlay { if (beyondRendered && mainHolder.offsetWidth !== mainHolder.clientWidth) { scrollbarCompensation = getScrollbarWidth(this.domBindings.rootDocument); } - if (beyondRendered) { newX += this.sumCellSizes(0, sourceCol + 1); newX -= this.wot.wtViewport.getViewportWidth(); + // Compensate for the right header border if scrolled from the absolute left. + newX += rowHeaderBorderCompensation; } else { newX += this.sumCellSizes(this.wtSettings.getSetting('fixedColumnsStart'), sourceCol); @@ -275,6 +285,15 @@ export class InlineStartOverlay extends Overlay { newX += scrollbarCompensation; + // If the table is scrolled all the way left when starting the scroll and going to be scrolled to the far right, + // we need to compensate for the potential header border width. + if ( + getMaximumScrollLeft(this.mainTableScrollableElement) === newX - rowHeaderBorderCompensation && + rowHeaderBorderCompensation > 0 + ) { + this.wot.wtOverlays.expandHiderHorizontallyBy(rowHeaderBorderCompensation); + } + return this.setScrollPosition(newX); } diff --git a/handsontable/src/3rdparty/walkontable/src/overlay/top.js b/handsontable/src/3rdparty/walkontable/src/overlay/top.js index 9108e8ae55a..3101d95ec78 100644 --- a/handsontable/src/3rdparty/walkontable/src/overlay/top.js +++ b/handsontable/src/3rdparty/walkontable/src/overlay/top.js @@ -1,5 +1,6 @@ import { addClass, + getMaximumScrollTop, getScrollbarWidth, getScrollTop, getWindowScrollLeft, @@ -275,6 +276,13 @@ export class TopOverlay extends Overlay { const { wot, wtSettings } = this; const sourceInstance = wot.cloneSource ? wot.cloneSource : wot; const mainHolder = sourceInstance.wtTable.holder; + const columnHeaders = wtSettings.getSetting('columnHeaders'); + const fixedRowsTop = wtSettings.getSetting('fixedRowsTop'); + const columnHeaderBorderCompensation = ( + fixedRowsTop === 0 && + columnHeaders.length > 0 && + !hasClass(mainHolder.parentNode, 'innerBorderTop') + ) ? 1 : 0; let newY = this.getTableParentOffset(); let scrollbarCompensation = 0; @@ -299,12 +307,24 @@ export class TopOverlay extends Overlay { newY -= wot.wtViewport.getViewportHeight() - this.sumCellSizes(totalRows - fixedRowsBottom, totalRows); // Fix 1 pixel offset when cell is selected newY += 1; + // Compensate for the bottom header border if scrolled from the absolute top. + newY += columnHeaderBorderCompensation; } else { newY += this.sumCellSizes(wtSettings.getSetting('fixedRowsTop'), sourceRow); } + newY += scrollbarCompensation; + // If the table is scrolled all the way up when starting the scroll and going to be scrolled to the bottom, + // we need to compensate for the potential header bottom border height. + if ( + getMaximumScrollTop(this.mainTableScrollableElement) === newY - columnHeaderBorderCompensation && + columnHeaderBorderCompensation > 0 + ) { + this.wot.wtOverlays.expandHiderVerticallyBy(columnHeaderBorderCompensation); + } + return this.setScrollPosition(newY); } diff --git a/handsontable/src/3rdparty/walkontable/src/overlays.js b/handsontable/src/3rdparty/walkontable/src/overlays.js index e50b9a91d94..273f62bf6f2 100644 --- a/handsontable/src/3rdparty/walkontable/src/overlays.js +++ b/handsontable/src/3rdparty/walkontable/src/overlays.js @@ -632,14 +632,33 @@ class Overlays { adjustElementsSize(force = false) { const { wtViewport } = this.wot; const { wtTable } = this; + const { rootWindow } = this.domBindings; + const isWindowScrolled = this.scrollableElement === rootWindow; const totalColumns = this.wtSettings.getSetting('totalColumns'); const totalRows = this.wtSettings.getSetting('totalRows'); const headerRowSize = wtViewport.getRowHeaderWidth(); const headerColumnSize = wtViewport.getColumnHeaderHeight(); - const hiderStyle = wtTable.hider.style; + const proposedHiderHeight = headerColumnSize + this.topOverlay.sumCellSizes(0, totalRows) + 1; + const proposedHiderWidth = headerRowSize + this.inlineStartOverlay.sumCellSizes(0, totalColumns); + const hiderElement = wtTable.hider; + const hiderStyle = hiderElement.style; + const isScrolledBeyondHiderHeight = () => { + return isWindowScrolled ? + false : + (this.scrollableElement.scrollTop > Math.max(0, proposedHiderHeight - wtTable.holder.clientHeight)); + }; + const isScrolledBeyondHiderWidth = () => { + return isWindowScrolled ? + false : + (this.scrollableElement.scrollLeft > Math.max(0, proposedHiderWidth - wtTable.holder.clientWidth)); + }; + const columnHeaderBorderCompensation = isScrolledBeyondHiderHeight() ? 1 : 0; + const rowHeaderBorderCompensation = isScrolledBeyondHiderWidth() ? 1 : 0; - hiderStyle.width = `${headerRowSize + this.inlineStartOverlay.sumCellSizes(0, totalColumns)}px`; - hiderStyle.height = `${headerColumnSize + this.topOverlay.sumCellSizes(0, totalRows) + 1}px`; + // If the elements are being adjusted after scrolling the table from the very beginning to the very end, + // we need to adjust the hider dimensions by the header border size. (https://github.com/handsontable/dev-handsontable/issues/1772) + hiderStyle.width = `${proposedHiderWidth + rowHeaderBorderCompensation}px`; + hiderStyle.height = `${proposedHiderHeight + columnHeaderBorderCompensation}px`; if (this.scrollbarSize > 0) { // todo refactoring, looking as a part of logic which should be moved outside the class const { @@ -666,6 +685,28 @@ class Overlays { this.bottomOverlay.adjustElementsSize(force); } + /** + * Expand the hider vertically element by the provided delta value. + * + * @param {number} heightDelta The delta value to expand the hider element by. + */ + expandHiderVerticallyBy(heightDelta) { + const { wtTable } = this; + + wtTable.hider.style.height = `${parseInt(wtTable.hider.style.height, 10) + heightDelta}px`; + } + + /** + * Expand the hider horizontally element by the provided delta value. + * + * @param {number} widthDelta The delta value to expand the hider element by. + */ + expandHiderHorizontallyBy(widthDelta) { + const { wtTable } = this; + + wtTable.hider.style.width = `${parseInt(wtTable.hider.style.width, 10) + widthDelta}px`; + } + /** * */ diff --git a/handsontable/src/3rdparty/walkontable/test/spec/overlay.spec.js b/handsontable/src/3rdparty/walkontable/test/spec/overlay.spec.js index 62f41a17db8..0d0f335d966 100644 --- a/handsontable/src/3rdparty/walkontable/test/spec/overlay.spec.js +++ b/handsontable/src/3rdparty/walkontable/test/spec/overlay.spec.js @@ -728,8 +728,8 @@ describe('WalkontableOverlay', () => { const baseRect = getTableRect(wt.wtTable); expect(baseRect).toEqual(jasmine.objectContaining({ - top: documentClientHeight - totalRowsHeight, - bottom: documentClientHeight + 1, // +1 innerBorderTop + top: documentClientHeight - totalRowsHeight - 1, // 1px header border compensation + bottom: documentClientHeight, left: documentClientWidth - totalColumnsWidth, })); expect(getTableRect(wt.wtOverlays.topOverlay.clone.wtTable)).toEqual(jasmine.objectContaining({ @@ -743,8 +743,8 @@ describe('WalkontableOverlay', () => { left: 0, })); expect(getTableRect(wt.wtOverlays.inlineStartOverlay.clone.wtTable)).toEqual(jasmine.objectContaining({ - top: documentClientHeight - totalRowsHeight, - bottom: documentClientHeight + 1, // +1 innerBorderTop + top: documentClientHeight - totalRowsHeight - 1, // 1px header border compensation + bottom: documentClientHeight, left: 0, })); }); diff --git a/handsontable/src/3rdparty/walkontable/test/spec/scroll.spec.js b/handsontable/src/3rdparty/walkontable/test/spec/scroll.spec.js index 13512bfe587..4ec4289214e 100644 --- a/handsontable/src/3rdparty/walkontable/test/spec/scroll.spec.js +++ b/handsontable/src/3rdparty/walkontable/test/spec/scroll.spec.js @@ -180,9 +180,9 @@ describe('WalkontableScroll', () => { const firstRow = getTableMaster().find('tbody tr:first'); const lastRow = getTableMaster().find('tbody tr:last'); - expect(firstRow.find('td:first').text()).toBe('H1'); + expect(firstRow.find('td:first').text()).toBe('I1'); expect(firstRow.find('td:last').text()).toBe('K1'); - expect(lastRow.find('td:first').text()).toBe('H8'); + expect(lastRow.find('td:first').text()).toBe('I8'); expect(lastRow.find('td:last').text()).toBe('K8'); }); diff --git a/handsontable/src/core/viewportScroll/__tests__/focusSelection.spec.js b/handsontable/src/core/viewportScroll/__tests__/focusSelection.spec.js index 00cd0369c9d..889e5b349d8 100644 --- a/handsontable/src/core/viewportScroll/__tests__/focusSelection.spec.js +++ b/handsontable/src/core/viewportScroll/__tests__/focusSelection.spec.js @@ -29,7 +29,8 @@ describe('Focus selection scroll', () => { keyDownUp('enter'); keyDownUp('enter'); // B4 - expect(topOverlay().getScrollPosition()).toBe(4); + // 92 row heights - 104 viewport width + 15 scrollbart offset + + 1 selection offset + 1 header border offset + expect(topOverlay().getScrollPosition()).toBe(5); keyDownUp('enter'); // B5 @@ -58,7 +59,8 @@ describe('Focus selection scroll', () => { keyDownUp(['shift', 'enter']); // B50 - expect(topOverlay().getScrollPosition()).toBe(1062); + // 1150 row heights - 104 viewport width + 15 scrollbart offset + 1 header border offset + expect(topOverlay().getScrollPosition()).toBe(1063); }); it('should scroll the viewport horizontally', () => { @@ -77,7 +79,8 @@ describe('Focus selection scroll', () => { keyDownUp('tab'); keyDownUp('tab'); // C2 - expect(inlineStartOverlay().getScrollPosition()).toBe(15); + // 150 column widths - 150 viewport width + 15 scrollbart offset + 1 header border offset + expect(inlineStartOverlay().getScrollPosition()).toBe(16); keyDownUp('tab'); // D2 @@ -102,6 +105,7 @@ describe('Focus selection scroll', () => { keyDownUp(['shift', 'tab']); // E2 - expect(inlineStartOverlay().getScrollPosition()).toBe(115); + // 250 column widths - 150 viewport width + 15 scrollbart offset + 1 header border offset + expect(inlineStartOverlay().getScrollPosition()).toBe(116); }); }); diff --git a/handsontable/src/helpers/dom/element.js b/handsontable/src/helpers/dom/element.js index 53c826004de..addba96bebc 100644 --- a/handsontable/src/helpers/dom/element.js +++ b/handsontable/src/helpers/dom/element.js @@ -639,6 +639,26 @@ export function getScrollableElement(element) { return rootWindow; } +/** + * Get the maximum available `scrollTop` value for the provided element. + * + * @param {HTMLElement} element The element to get the maximum scroll top value from. + * @returns {number} The maximum scroll top value. + */ +export function getMaximumScrollTop(element) { + return element.scrollHeight - element.clientHeight; +} + +/** + * Get the maximum available `scrollLeft` value for the provided element. + * + * @param {HTMLElement} element The element to get the maximum scroll left value from. + * @returns {number} The maximum scroll left value. + */ +export function getMaximumScrollLeft(element) { + return element.scrollWidth - element.clientWidth; +} + /** * Returns a DOM element responsible for trimming the provided element. * diff --git a/handsontable/src/plugins/autoRowSize/__tests__/autoRowSize.spec.js b/handsontable/src/plugins/autoRowSize/__tests__/autoRowSize.spec.js index f1c2b05b3c2..af841575446 100644 --- a/handsontable/src/plugins/autoRowSize/__tests__/autoRowSize.spec.js +++ b/handsontable/src/plugins/autoRowSize/__tests__/autoRowSize.spec.js @@ -283,8 +283,8 @@ describe('AutoRowSize', () => { keyDownUp('enter'); - expect(getInlineStartClone().find('.wtHolder').scrollTop()).toBe(89); - expect(getMaster().find('.wtHolder').scrollTop()).toBe(89); + expect(getInlineStartClone().find('.wtHolder').scrollTop()).toBe(90); + expect(getMaster().find('.wtHolder').scrollTop()).toBe(90); }); it('should consider CSS style of each instance separately', () => { diff --git a/handsontable/src/plugins/comments/__tests__/keyboardShortcuts.spec.js b/handsontable/src/plugins/comments/__tests__/keyboardShortcuts.spec.js index e73854695d8..b526a720c7d 100644 --- a/handsontable/src/plugins/comments/__tests__/keyboardShortcuts.spec.js +++ b/handsontable/src/plugins/comments/__tests__/keyboardShortcuts.spec.js @@ -45,6 +45,7 @@ describe('Comments keyboard shortcut', () => { data: createSpreadsheetData(500, 50), width: 300, height: 300, + colWidths: 50, rowHeaders: true, colHeaders: true, comments: true, @@ -71,8 +72,10 @@ describe('Comments keyboard shortcut', () => { expect(editor.value).toBe(''); expect(document.activeElement).toBe(editor); expect(plugin.range).toEqualCellRange('highlight: 400,40 from: 400,40 to: 400,40'); + + // 2050 column width - 250 viewport width + 15 scrollbar compensation + 1 header border compensation expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(1816); - expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(8965); + expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(8966); }); it('should open and edit a comment, make it active, and ready for typing', async() => { diff --git a/handsontable/src/plugins/dropdownMenu/__tests__/dropdownMenu.spec.js b/handsontable/src/plugins/dropdownMenu/__tests__/dropdownMenu.spec.js index 7f0fd43563f..dd650fe85a8 100644 --- a/handsontable/src/plugins/dropdownMenu/__tests__/dropdownMenu.spec.js +++ b/handsontable/src/plugins/dropdownMenu/__tests__/dropdownMenu.spec.js @@ -958,10 +958,11 @@ describe('DropdownMenu', () => { await sleep(10); - expect(inlineStartOverlay().getScrollPosition()).toBe(650); + // 900 column width - 250 viewport width + 1 header border compensation + expect(inlineStartOverlay().getScrollPosition()).toBe(651); dropdownMenu(6); // click on the column `G` header button - expect(inlineStartOverlay().getScrollPosition()).toBe(650); + expect(inlineStartOverlay().getScrollPosition()).toBe(651); }); }); diff --git a/handsontable/src/plugins/nestedHeaders/__tests__/navigation.spec.js b/handsontable/src/plugins/nestedHeaders/__tests__/navigation.spec.js index 564f44f680f..fe6da06217c 100644 --- a/handsontable/src/plugins/nestedHeaders/__tests__/navigation.spec.js +++ b/handsontable/src/plugins/nestedHeaders/__tests__/navigation.spec.js @@ -1535,7 +1535,8 @@ describe('NestedHeaders', () => { keyDownUp('arrowright'); // "C" expect(topOverlay().getScrollPosition()).toBe(0); - expect(inlineStartOverlay().getScrollPosition()).toBe(65); + // 300 column width - 250 viewport width + 15 scrollbar compensation + 1 header border compensation + expect(inlineStartOverlay().getScrollPosition()).toBe(66); keyDownUp('arrowright'); // "D" @@ -1648,7 +1649,7 @@ describe('NestedHeaders', () => { keyDownUp('arrowup'); // "C" expect(topOverlay().getScrollPosition()).toBe(0); - expect(inlineStartOverlay().getScrollPosition()).toBe(50); + expect(inlineStartOverlay().getScrollPosition()).toBe(51); }); it('should scroll the viewport correctly while navigating vertically using arrows ' + @@ -1706,12 +1707,12 @@ describe('NestedHeaders', () => { selectCell(-1, 20); expect(topOverlay().getScrollPosition()).toBe(0); - expect(inlineStartOverlay().getScrollPosition()).toBe(800); + expect(inlineStartOverlay().getScrollPosition()).toBe(801); keyDownUp('arrowup'); // "F" expect(topOverlay().getScrollPosition()).toBe(0); - expect(inlineStartOverlay().getScrollPosition()).toBe(800); + expect(inlineStartOverlay().getScrollPosition()).toBe(801); }); }); diff --git a/handsontable/test/e2e/core/scrollToFocusedCell.spec.js b/handsontable/test/e2e/core/scrollToFocusedCell.spec.js index a8e986555bc..0bdb8a4b361 100644 --- a/handsontable/test/e2e/core/scrollToFocusedCell.spec.js +++ b/handsontable/test/e2e/core/scrollToFocusedCell.spec.js @@ -146,7 +146,8 @@ describe('Core.scrollToFocusedCell', () => { await sleep(10); - expect(inlineStartOverlay().getScrollPosition()).toBe(2265); + // 2500 column width - 250 viewport width + 15 scrollbar compensation + 1 header border compensation + expect(inlineStartOverlay().getScrollPosition()).toBe(2266); expect(topOverlay().getScrollPosition()).toBe(5750); }); diff --git a/handsontable/test/e2e/core/scrollViewportTo.spec.js b/handsontable/test/e2e/core/scrollViewportTo.spec.js index add2bc52041..440ff496337 100644 --- a/handsontable/test/e2e/core/scrollViewportTo.spec.js +++ b/handsontable/test/e2e/core/scrollViewportTo.spec.js @@ -40,8 +40,8 @@ describe('Core.scrollViewportTo', () => { render(); expect(result).toBe(true); - expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(2315); - expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(3215); + expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(2316); + expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(3216); }); it('should scroll the viewport in such a way that the coordinates are glued to the bottom-start edge when ' + @@ -71,7 +71,7 @@ describe('Core.scrollViewportTo', () => { expect(result).toBe(true); expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(2502); - expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(3215); + expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(3216); }); it('should scroll the viewport in such a way that the coordinates are glued to the top-start edge when ' + @@ -130,7 +130,7 @@ describe('Core.scrollViewportTo', () => { render(); expect(result).toBe(true); - expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(2315); + expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(2316); expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(3450); }); }); @@ -153,7 +153,7 @@ describe('Core.scrollViewportTo', () => { expect(result).toBe(true); expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(0); - expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(3215); + expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(3216); }); it('should scroll the viewport in such a way that the coordinates are glued to the top edge (manual snapping)', () => { @@ -194,7 +194,7 @@ describe('Core.scrollViewportTo', () => { render(); expect(result).toBe(true); - expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(2315); + expect(hot.view._wt.wtOverlays.inlineStartOverlay.getScrollPosition()).toBe(2316); expect(hot.view._wt.wtOverlays.topOverlay.getScrollPosition()).toBe(0); }); @@ -889,6 +889,53 @@ describe('Core.scrollViewportTo', () => { expect(hot.view._wt.wtTable.getFirstVisibleRow()).toBe(-1); }); + it('should scroll the viewport to the desired cell from the top-left position, ' + + '(the snapping set to "end") with the bottom/right border being visible', async() => { + const hot = handsontable({ + data: createSpreadsheetData(30, 30), + colWidths: 50, + rowHeights: 30, + rowHeaders: true, + colHeaders: true, + width: 200, + height: 200, + }); + + hot.scrollViewportTo({ + row: 15, + col: 15, + horizontalSnap: 'end', + verticalSnap: 'bottom', + }); + await sleep(100); + + expect(document.elementsFromPoint( + hot.rootElement.offsetWidth - Handsontable.dom.getScrollbarWidth() - 50, + hot.rootElement.offsetHeight - Handsontable.dom.getScrollbarWidth() - 30 + )[0]).toEqual(hot.getCell(15, 15)); + + hot.scrollViewportTo({ + row: 0, + col: 0, + horizontalSnap: 'start', + verticalSnap: 'top', + }); + await sleep(100); + + hot.scrollViewportTo({ + row: 29, + col: 29, + horizontalSnap: 'end', + verticalSnap: 'bottom', + }); + await sleep(100); + + expect(document.elementsFromPoint( + hot.rootElement.offsetWidth - Handsontable.dom.getScrollbarWidth() - 50, + hot.rootElement.offsetHeight - Handsontable.dom.getScrollbarWidth() - 30 + )[0]).toEqual(hot.getCell(29, 29)); + }); + describe('using backward-compatible arguments', () => { it('should scroll the viewport using default snapping (top, start)', () => { const hot = handsontable({ diff --git a/handsontable/test/e2e/core/selectAll.spec.js b/handsontable/test/e2e/core/selectAll.spec.js index f87370434c2..5cc0f818816 100644 --- a/handsontable/test/e2e/core/selectAll.spec.js +++ b/handsontable/test/e2e/core/selectAll.spec.js @@ -59,18 +59,18 @@ describe('Core.selectAll', () => { selectAll(false); expect(` - | ║ - : - : - : - : - : - : - | - |===:===:===:===:===:===:===:===| - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : A : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | ║ - : - : - : - : - : - : - : - | + |===:===:===:===:===:===:===:===:===| + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : A : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | - ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | `).toBeMatchToSelectionPattern(); // "Select all" shouldn't scroll te table. @@ -97,18 +97,18 @@ describe('Core.selectAll', () => { selectAll(true); expect(` - | * ║ * : * : * : * : * : * : * | - |===:===:===:===:===:===:===:===| - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : A : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | - | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ * : * : * : * : * : * : * : * | + |===:===:===:===:===:===:===:===:===| + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : A : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | + | * ║ 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 | `).toBeMatchToSelectionPattern(); // "Select all" shouldn't scroll te table. diff --git a/handsontable/test/e2e/hooks/beforeViewportScrollHorizontally.spec.js b/handsontable/test/e2e/hooks/beforeViewportScrollHorizontally.spec.js index 8da222be9ea..e7b4dd68a48 100644 --- a/handsontable/test/e2e/hooks/beforeViewportScrollHorizontally.spec.js +++ b/handsontable/test/e2e/hooks/beforeViewportScrollHorizontally.spec.js @@ -90,6 +90,7 @@ describe('Hook', () => { data: createSpreadsheetData(100, 50), width: 300, height: 300, + colWidths: 50, rowHeaders: true, colHeaders: true, beforeViewportScrollHorizontally, @@ -97,7 +98,8 @@ describe('Hook', () => { scrollViewportTo({ col: 10 }); - expect(inlineStartOverlay().getScrollPosition()).toBe(1815); + // 2050 column width - 250 viewport width + 15 scrollbar compensation + 1 header border compensation + expect(inlineStartOverlay().getScrollPosition()).toBe(1816); expect(topOverlay().getScrollPosition()).toBe(0); }); @@ -108,6 +110,7 @@ describe('Hook', () => { data: createSpreadsheetData(100, 50), width: 300, height: 300, + colWidths: 50, rowHeaders: true, colHeaders: true, beforeViewportScrollHorizontally, @@ -124,7 +127,9 @@ describe('Hook', () => { scrollViewportTo({ col: 20 }); expect(beforeViewportScrollHorizontally).toHaveBeenCalledOnceWith(20); - expect(inlineStartOverlay().getScrollPosition()).toBe(665); + + // 900 column width - 250 viewport width + 15 scrollbar compensation + 1 header border compensation + expect(inlineStartOverlay().getScrollPosition()).toBe(666); expect(topOverlay().getScrollPosition()).toBe(0); }); diff --git a/handsontable/test/e2e/hooks/beforeViewportScrollVertically.spec.js b/handsontable/test/e2e/hooks/beforeViewportScrollVertically.spec.js index 7421f5e8195..f5ea2189840 100644 --- a/handsontable/test/e2e/hooks/beforeViewportScrollVertically.spec.js +++ b/handsontable/test/e2e/hooks/beforeViewportScrollVertically.spec.js @@ -98,7 +98,7 @@ describe('Hook', () => { scrollViewportTo({ row: 10 }); expect(inlineStartOverlay().getScrollPosition()).toBe(0); - expect(topOverlay().getScrollPosition()).toBe(685); + expect(topOverlay().getScrollPosition()).toBe(686); }); it('should be possible to change row to which the viewport is scrolled (case with hidden rows)', () => { @@ -125,7 +125,7 @@ describe('Hook', () => { expect(beforeViewportScrollVertically).toHaveBeenCalledOnceWith(20); expect(inlineStartOverlay().getScrollPosition()).toBe(0); - expect(topOverlay().getScrollPosition()).toBe(156); + expect(topOverlay().getScrollPosition()).toBe(157); }); it('should be possible to block viewport scrolling after returning `false`', () => { diff --git a/handsontable/test/e2e/keyboardShortcuts/viewportScroll.spec.js b/handsontable/test/e2e/keyboardShortcuts/viewportScroll.spec.js index 10727969704..4e87a16dbca 100644 --- a/handsontable/test/e2e/keyboardShortcuts/viewportScroll.spec.js +++ b/handsontable/test/e2e/keyboardShortcuts/viewportScroll.spec.js @@ -252,7 +252,7 @@ describe('Core viewport scroll keyboard shortcuts', () => { keyDownUp(['control/meta', 'backspace']); - expect(getCurrentScrollPosition()).toEqual({ x: 1100, y: 1835 }); + expect(getCurrentScrollPosition()).toEqual({ x: 1100, y: 1836 }); }); it('should scroll the viewport horizontally when the column header is focused and all rows are trimmed', async() => { @@ -321,7 +321,8 @@ describe('Core viewport scroll keyboard shortcuts', () => { keyDownUp(['control/meta', 'backspace']); - expect(getCurrentScrollPosition()).toEqual({ x: 1745, y: 1035 }); + // 2050 column width - 320 viewport width + 15 scrollbar compensation + 1 header border compensation + expect(getCurrentScrollPosition()).toEqual({ x: 1746, y: 1035 }); }); it('should scroll the viewport vertically when the row header is focused and all columns are trimmed', async() => { @@ -388,11 +389,12 @@ describe('Core viewport scroll keyboard shortcuts', () => { await sleep(100); - expect(getCurrentScrollPosition()).toEqual({ x: 995, y: 915 }); + // 1300 column width - 320 viewport width + 15 scrollbar compensation + 1 header border compensation + expect(getCurrentScrollPosition()).toEqual({ x: 996, y: 916 }); keyDownUp(['control/meta', 'backspace']); - expect(getCurrentScrollPosition()).toEqual({ x: 995, y: 915 }); + expect(getCurrentScrollPosition()).toEqual({ x: 996, y: 916 }); }); }); });