UNPKG

@reactual/handsontable

Version:

Spreadsheet-like data grid editor

758 lines (591 loc) 23.8 kB
describe('Core_view', () => { var id = 'testContainer'; beforeEach(function() { this.$container = $(`<div id="${id}"></div>`).appendTo('body'); }); afterEach(function() { if (this.$container) { destroy(); this.$container.remove(); } }); it('should focus cell after viewport is scrolled using down arrow', function() { this.$container[0].style.width = '400px'; this.$container[0].style.height = '60px'; handsontable({ startRows: 20 }); selectCell(0, 0); keyDown('arrow_down'); keyDown('arrow_down'); keyDown('arrow_down'); keyDown('arrow_down'); expect(getSelected()).toEqual([4, 0, 4, 0]); keyDown('enter'); expect(isEditorVisible()).toEqual(true); }); it('should not render "undefined" class name', function() { this.$container[0].style.width = '501px'; this.$container[0].style.height = '100px'; this.$container[0].style.overflow = 'hidden'; var hot = handsontable({ startRows: 10, startCols: 5, colWidths: [47, 47, 47, 47, 47], rowHeaders: true, colHeaders: true, stretchH: 'all' }); selectCell(0, 0); expect(this.$container.find('.undefined').length).toBe(0); }); it('should scroll viewport when partially visible cell is clicked', function() { this.$container[0].style.width = '400px'; this.$container[0].style.height = '60px'; var hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(10, 3), height: 60 }); var htCore = getHtCore(); var scrollTop = hot.rootElement.querySelector('.wtHolder').scrollTop; expect(scrollTop).toBe(0); expect(this.$container.height()).toEqual(60); expect(this.$container.find('.wtHolder .wtHider').height()).toBeGreaterThan(60); expect(htCore.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(htCore.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(htCore.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); htCore.find('tr:eq(3) td:eq(0)').simulate('mousedown'); expect(hot.rootElement.querySelector('.wtHolder').scrollTop).toBeGreaterThan(scrollTop); expect(getSelected()).toEqual([3, 0, 3, 0]); }); it('should scroll viewport without cell selection', function() { this.$container[0].style.width = '400px'; var hot1 = handsontable({ data: Handsontable.helper.createSpreadsheetData(20, 20), height: 100 }); hot1.scrollViewportTo(10, 10); var wtHolder = this.$container.find('.ht_master .wtHolder'); expect(wtHolder[0].scrollTop).toEqual(230); expect(wtHolder[0].scrollLeft).toEqual(500); }); it('should not throw error while scrolling viewport to 0, 0 (empty data)', function() { this.$container[0].style.width = '400px'; var hot1 = handsontable({ data: [], height: 100 }); expect(() => { hot1.view.scrollViewport({row: 0, col: 0}); }).not.toThrow(); }); it('should throw error while scrolling viewport below 0 (empty data)', function() { this.$container[0].style.width = '400px'; var hot1 = handsontable({ data: [], height: 100 }); expect(() => { hot1.view.scrollViewport({row: -1, col: 0}); }).toThrow(); expect(() => { hot1.view.scrollViewport({row: 0, col: -1}); }).toThrow(); expect(() => { hot1.view.scrollViewport({row: -1, col: -1}); }).toThrow(); }); it('should scroll viewport, respecting fixed rows', function() { this.$container[0].style.width = '400px'; this.$container[0].style.height = '60px'; var hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(10, 9), fixedRowsTop: 1, height: 60 }); var htCore = getHtCore(); var scrollTop = hot.rootElement.querySelector('.wtHolder').scrollTop; expect(scrollTop).toBe(0); expect(htCore.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(htCore.find('tr:eq(0) td:eq(1)').html()).toEqual('B1'); expect(htCore.find('tr:eq(0) td:eq(2)').html()).toEqual('C1'); selectCell(0, 0); keyDown('arrow_down'); keyDown('arrow_down'); keyDown('arrow_down'); keyDown('arrow_down'); expect(hot.rootElement.querySelector('.wtHolder').scrollTop).toBeGreaterThan(scrollTop); }); it('should enable to change fixedRowsTop with updateSettings', function() { this.$container[0].style.width = '400px'; this.$container[0].style.height = '60px'; var HOT = handsontable({ data: Handsontable.helper.createSpreadsheetData(10, 9), fixedRowsTop: 1, width: 200, height: 100 }); selectCell(0, 0); var htCore = getHtCore(); var topClone = getTopClone(); expect(topClone.find('tr').length).toEqual(1); expect(topClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(htCore.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(htCore.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(htCore.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); expect(htCore.find('tr:eq(3) td:eq(0)').html()).toEqual('A4'); keyDown('arrow_down'); keyDown('arrow_down'); keyDown('arrow_down'); keyDown('arrow_down'); expect(topClone.find('tr').length).toEqual(1); expect(topClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); HOT.updateSettings({ fixedRowsTop: 2 }); expect(topClone.find('tr').length).toEqual(2); expect(topClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(topClone.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(htCore.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(htCore.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(htCore.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); expect(htCore.find('tr:eq(3) td:eq(0)').html()).toEqual('A4'); }); it('should scroll viewport, respecting fixed columns', function() { this.$container[0].style.width = '200px'; this.$container[0].style.height = '100px'; handsontable({ data: Handsontable.helper.createSpreadsheetData(10, 9), fixedColumnsLeft: 1 }); var htCore = getHtCore(); var leftClone = this.$container.find('.ht_clone_left'); expect(leftClone.find('tr:eq(0) td').length).toEqual(1); expect(leftClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(leftClone.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(leftClone.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); expect(htCore.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(htCore.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(htCore.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); selectCell(0, 3); keyDown('arrow_right'); keyDown('arrow_right'); keyDown('arrow_right'); keyDown('arrow_right'); expect(leftClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(leftClone.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(leftClone.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); }); it('should enable to change fixedColumnsLeft with updateSettings', function() { this.$container[0].style.width = '200px'; this.$container[0].style.height = '100px'; var HOT = handsontable({ data: Handsontable.helper.createSpreadsheetData(10, 9), fixedColumnsLeft: 1 }); selectCell(0, 0); var leftClone = this.$container.find('.ht_clone_left'); expect(leftClone.find('tr:eq(0) td').length).toEqual(1); expect(leftClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(leftClone.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(leftClone.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); keyDown('arrow_right'); keyDown('arrow_right'); keyDown('arrow_right'); keyDown('arrow_right'); expect(leftClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(leftClone.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(leftClone.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); selectCell(0, 0); HOT.updateSettings({ fixedColumnsLeft: 2 }); expect(leftClone.find('tr:eq(0) td').length).toEqual(2); expect(leftClone.find('tr:eq(0) td:eq(0)').html()).toEqual('A1'); expect(leftClone.find('tr:eq(0) td:eq(1)').html()).toEqual('B1'); expect(leftClone.find('tr:eq(1) td:eq(0)').html()).toEqual('A2'); expect(leftClone.find('tr:eq(1) td:eq(1)').html()).toEqual('B2'); expect(leftClone.find('tr:eq(2) td:eq(0)').html()).toEqual('A3'); expect(leftClone.find('tr:eq(2) td:eq(1)').html()).toEqual('B3'); }); it('should not scroll viewport when last cell is clicked', () => { handsontable({ startRows: 40 }); var lastScroll; $(window).scrollTop(10000); lastScroll = $(window).scrollTop(); render(); // renders synchronously so we don't have to put stuff in waits/runs selectCell(39, 0); expect($(window).scrollTop()).toEqual(lastScroll); keyDown('arrow_right'); expect(getSelected()).toEqual([39, 1, 39, 1]); expect($(window).scrollTop()).toEqual(lastScroll); }); it('should not shrink table when width and height is not specified for container', function(done) { var initHeight; this.$container[0].style.overflow = 'hidden'; this.$container.wrap('<div style="width: 50px;"></div>'); handsontable({ startRows: 10, startCols: 10 }); setTimeout(() => { initHeight = spec().$container.height(); }, 250); setTimeout(() => { expect(spec().$container.height()).toEqual(initHeight); done(); }, 500); }); it('should allow height to be a number', function() { handsontable({ startRows: 10, startCols: 10, height: 107 }); expect(this.$container.height()).toEqual(107); }); it('should allow height to be a function', function() { handsontable({ startRows: 10, startCols: 10, height() { return 107; } }); expect(this.$container.height()).toEqual(107); }); it('should allow width to be a number', function() { handsontable({ startRows: 10, startCols: 10, width: 107, }); expect(this.$container.width()).toEqual(107); // rootElement is full width but this should do the trick }); it('should allow width to be a function', function() { handsontable({ startRows: 10, startCols: 10, width() { return 107; } }); expect(this.$container.width()).toEqual(107); // rootElement is full width but this should do the trick }); it('should fire beforeRender event after table has been scrolled', function(done) { this.$container[0].style.width = '400px'; this.$container[0].style.height = '60px'; this.$container[0].style.overflow = 'hidden'; var hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(100, 3) }); var beforeRenderCallback = jasmine.createSpy('beforeRenderCallback'); hot.addHook('beforeRender', beforeRenderCallback); this.$container.find('.ht_master .wtHolder').scrollTop(1000); setTimeout(() => { expect(beforeRenderCallback.calls.count()).toBe(1); done(); }, 200); }); it('should fire afterRender event after table has been scrolled', function(done) { this.$container[0].style.width = '400px'; this.$container[0].style.height = '60px'; this.$container[0].style.overflow = 'hidden'; var hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(20, 3) }); var afterRenderCallback = jasmine.createSpy('afterRenderCallback'); hot.addHook('afterRender', afterRenderCallback); this.$container.find('.ht_master .wtHolder').first().scrollTop(1000); setTimeout(() => { expect(afterRenderCallback.calls.count()).toBe(1); done(); }, 200); }); it('should fire afterRender event after table physically rendered', function(done) { this.$container[0].style.width = '400px'; this.$container[0].style.height = '60px'; this.$container[0].style.overflow = 'hidden'; var hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(20, 3) }); hot.addHook('afterRender', () => { hot.view.wt.wtTable.holder.style.overflow = 'scroll'; hot.view.wt.wtTable.holder.style.width = '220px'; }); this.$container.find('.ht_master .wtHolder').first().scrollTop(1000); setTimeout(() => { // after afterRender hook triggered element style shouldn't changed expect(hot.view.wt.wtTable.holder.style.overflow).toBe('scroll'); expect(hot.view.wt.wtTable.holder.style.width).toBe('220px'); done(); }, 100); }); // TODO fix these tests - https://github.com/handsontable/handsontable/issues/1559 describe('maximumVisibleElementWidth', () => { it('should return maximum width until right edge of the viewport', () => { var hot = handsontable({ startRows: 2, startCols: 10, width: 100, height: 100, }); expect(hot.view.maximumVisibleElementWidth(0)).toEqual(100); }); it('should return maximum width until right edge of the viewport (excluding the scrollbar)', () => { var hot = handsontable({ startRows: 10, startCols: 10, width: 100, height: 100, }); expect(hot.view.maximumVisibleElementWidth(200)).toBeLessThan(100); }); }); describe('maximumVisibleElementHeight', () => { it('should return maximum height until bottom edge of the viewport', () => { var hot = handsontable({ startRows: 10, startCols: 2, width: 120, height: 100, }); expect(hot.view.maximumVisibleElementHeight(0)).toEqual(100); }); it('should return maximum height until bottom edge of the viewport (excluding the scrollbar)', () => { var hot = handsontable({ startRows: 10, startCols: 10, width: 120, height: 100, }); expect(hot.view.maximumVisibleElementHeight()).toBeLessThan(100); }); }); describe('fixed column row heights', () => { it('should be the same as the row heights in the main table', () => { var hot = handsontable({ data: [['A', 'B', 'C', 'D'], ['a', 'b', 'c\nc', 'd'], ['aa', 'bb', 'cc', 'dd']], startRows: 3, startCols: 4, fixedColumnsLeft: 2, }); expect(hot.getCell(1, 2).clientHeight).toEqual(hot.getCell(1, 1).clientHeight); hot.setDataAtCell(1, 2, 'c'); expect(hot.getCell(1, 2).clientHeight).toEqual(hot.getCell(1, 1).clientHeight); }); it('should be the same as the row heights in the main table (after scroll)', function() { var myData = Handsontable.helper.createSpreadsheetData(20, 4); myData[1][3] = 'very\nlong\ntext'; myData[5][3] = 'very\nlong\ntext'; myData[10][3] = 'very\nlong\ntext'; myData[15][3] = 'very\nlong\ntext'; var hot = handsontable({ data: myData, startRows: 3, startCols: 4, fixedRowsTop: 2, fixedColumnsLeft: 2, width: 200, height: 200 }); var mainHolder = hot.view.wt.wtTable.holder; $(mainHolder).scrollTop(200); hot.render(); var masterTD = this.$container.find('.ht_master tbody tr:eq(5) td:eq(1)')[0]; var cloneTD = this.$container.find('.ht_clone_left tbody tr:eq(5) td:eq(1)')[0]; expect(cloneTD.clientHeight).toEqual(masterTD.clientHeight); }); it('should be the same as the row heights in the main table (after scroll, in corner)', function() { var myData = Handsontable.helper.createSpreadsheetData(20, 4); myData[1][3] = 'very\nlong\ntext'; myData[5][3] = 'very\nlong\ntext'; myData[10][3] = 'very\nlong\ntext'; myData[15][3] = 'very\nlong\ntext'; var hot = handsontable({ data: myData, startRows: 3, startCols: 4, fixedRowsTop: 2, fixedColumnsLeft: 2, width: 200, height: 200 }); var rowHeight = hot.getCell(1, 3).clientHeight; var mainHolder = hot.view.wt.wtTable.holder; expect(this.$container.find('.ht_clone_top_left_corner tbody tr:eq(1) td:eq(1)')[0].clientHeight).toEqual(rowHeight); $(mainHolder).scrollTop(200); hot.render(); expect(this.$container.find('.ht_clone_top_left_corner tbody tr:eq(1) td:eq(1)')[0].clientHeight).toEqual(rowHeight); }); }); describe('fixed column widths', () => { it('should set the columns width correctly after changes made during updateSettings', function() { var hot = handsontable({ startRows: 2, fixedColumnsLeft: 2, columns: [{ width: 50 }, { width: 80 }, { width: 110 }, { width: 140 }, { width: 30 }, { width: 30 }, { width: 30 }] }); var leftClone = this.$container.find('.ht_clone_left'); expect(Handsontable.dom.outerWidth(leftClone.find('tbody tr:nth-child(1) td:nth-child(2)')[0])).toEqual(80); hot.updateSettings({ manualColumnMove: [2, 0, 1], fixedColumnsLeft: 1 }); expect(leftClone.find('tbody tr:nth-child(1) td:nth-child(2)')[0]).toBe(undefined); hot.updateSettings({ manualColumnMove: false, fixedColumnsLeft: 2 }); expect(Handsontable.dom.outerWidth(leftClone.find('tbody tr:nth-child(1) td:nth-child(2)')[0])).toEqual(80); }); it('should set the columns width correctly after changes made during updateSettings when columns is a function', function() { var hot = handsontable({ startCols: 7, startRows: 2, fixedColumnsLeft: 2, columns(column) { var colMeta = {}; if (column === 0) { colMeta.width = 50; } else if (column === 1) { colMeta.width = 80; } else if (column === 2) { colMeta.width = 110; } else if (column === 3) { colMeta.width = 140; } else if ([4, 5, 6].indexOf(column) > -1) { colMeta.width = 30; } else { colMeta = null; } return colMeta; } }); var leftClone = this.$container.find('.ht_clone_left'); expect(Handsontable.dom.outerWidth(leftClone.find('tbody tr:nth-child(1) td:nth-child(2)')[0])).toEqual(80); hot.updateSettings({ manualColumnMove: [2, 0, 1], fixedColumnsLeft: 1 }); expect(leftClone.find('tbody tr:nth-child(1) td:nth-child(2)')[0]).toBe(undefined); hot.updateSettings({ manualColumnMove: false, fixedColumnsLeft: 2 }); expect(Handsontable.dom.outerWidth(leftClone.find('tbody tr:nth-child(1) td:nth-child(2)')[0])).toEqual(80); }); }); describe('stretchH', () => { it('should stretch all visible columns with the ratio appropriate to the container\'s width', function() { this.$container[0].style.width = '300px'; var hot = handsontable({ startRows: 5, startCols: 5, rowHeaders: true, colHeaders: true, stretchH: 'all' }), rowHeaderWidth = hot.view.wt.wtViewport.getRowHeaderWidth(), expectedCellWidth = (parseInt(this.$container[0].style.width, 10) - rowHeaderWidth) / 5; expect(getCell(0, 0).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 1).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 2).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 3).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 4).offsetWidth).toEqual(expectedCellWidth); this.$container[0].style.width = ''; this.$container.wrap('<div class="temp_wrapper" style="width:400px;"></div>'); hot.render(); expectedCellWidth = (parseInt($('.temp_wrapper')[0].style.width, 10) - rowHeaderWidth) / 5; expect(getCell(0, 0).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 1).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 2).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 3).offsetWidth).toEqual(expectedCellWidth); expect(getCell(0, 4).offsetWidth).toEqual(expectedCellWidth); this.$container.unwrap(); }); it('should stretch all visible columns with overflow hidden', function() { this.$container[0].style.width = '501px'; this.$container[0].style.height = '100px'; this.$container[0].style.overflow = 'hidden'; var hot = handsontable({ startRows: 10, startCols: 5, colWidths: [47, 47, 47, 47, 47], rowHeaders: true, colHeaders: true, stretchH: 'all' }); var masterTH = this.$container[0].querySelectorAll('.ht_master thead tr th'); var overlayTH = this.$container[0].querySelectorAll('.ht_clone_top thead tr th'); expect(masterTH[0].offsetWidth).toEqual(50); expect(overlayTH[0].offsetWidth).toEqual(50); expect(masterTH[1].offsetWidth).toBeInArray([86, 87, 88, 90]); expect(overlayTH[1].offsetWidth).toBeInArray([86, 87, 88, 90]); // if you get 90, it means it is calculated before scrollbars were applied, or show scroll on scrolling is enabled expect(masterTH[2].offsetWidth).toEqual(overlayTH[2].offsetWidth); expect(masterTH[3].offsetWidth).toEqual(overlayTH[3].offsetWidth); expect(masterTH[4].offsetWidth).toEqual(overlayTH[4].offsetWidth); expect(masterTH[5].offsetWidth).toEqual(overlayTH[5].offsetWidth); }); it('should respect stretched widths returned in beforeStretchingColumnWidth hook', function() { this.$container[0].style.width = '501px'; this.$container[0].style.height = '100px'; this.$container[0].style.overflow = 'hidden'; var callbackSpy = jasmine.createSpy(); callbackSpy.and.callFake((width, column) => { if (column === 1) { return 150; } return width; }); var hot = handsontable({ startRows: 2, startCols: 5, rowHeaders: true, colHeaders: true, stretchH: 'all', beforeStretchingColumnWidth: callbackSpy }); var $columnHeaders = this.$container.find('thead tr:eq(0) th'); expect($columnHeaders.eq(0).width()).toEqual(48); expect($columnHeaders.eq(1).width()).toEqual(73); expect($columnHeaders.eq(2).width()).toEqual(149); expect($columnHeaders.eq(3).width()).toEqual(74); expect($columnHeaders.eq(4).width()).toEqual(74); expect(callbackSpy).toHaveBeenCalled(); // First cycle to check what columns has permanent width expect(callbackSpy.calls.argsFor(0)[0]).not.toBeDefined(); expect(callbackSpy.calls.argsFor(0)[1]).toBe(0); expect(callbackSpy.calls.argsFor(1)[0]).not.toBeDefined(); expect(callbackSpy.calls.argsFor(1)[1]).toBe(1); expect(callbackSpy.calls.argsFor(2)[0]).not.toBeDefined(); expect(callbackSpy.calls.argsFor(2)[1]).toBe(2); expect(callbackSpy.calls.argsFor(3)[0]).not.toBeDefined(); expect(callbackSpy.calls.argsFor(3)[1]).toBe(3); expect(callbackSpy.calls.argsFor(4)[0]).not.toBeDefined(); expect(callbackSpy.calls.argsFor(4)[1]).toBe(4); // // Second cycle retrieve stretched width or permanent width expect(callbackSpy.calls.argsFor(5)[0]).toBe(75); expect(callbackSpy.calls.argsFor(6)[0]).toBe(75); expect(callbackSpy.calls.argsFor(7)[0]).toBe(75); expect(callbackSpy.calls.argsFor(8)[0]).toBe(75); expect(callbackSpy.calls.argsFor(9)[0]).toBe(75); }); }); });