@reactual/handsontable
Version:
Spreadsheet-like data grid editor
758 lines (591 loc) • 23.8 kB
JavaScript
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);
});
});
});