@quantlab/handsontable
Version:
Spreadsheet-like data grid editor that provides copy/paste functionality compatible with Excel/Google Docs
680 lines (547 loc) • 23.8 kB
JavaScript
describe('MergeCells', () => {
var id = 'testContainer';
beforeEach(function() {
this.$container = $(`<div id="${id}"></div>`).appendTo('body');
});
afterEach(function() {
if (this.$container) {
destroy();
this.$container.remove();
}
});
describe('mergeCells option', () => {
it('should merge cell in startup', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5),
mergeCells: [
{row: 0, col: 0, rowspan: 2, colspan: 2}
]
});
var TD = hot.rootElement.querySelector('td');
expect(TD.getAttribute('rowspan')).toBe('2');
expect(TD.getAttribute('colspan')).toBe('2');
});
});
describe('mergeCells updateSettings', () => {
it('should allow to overwrite the initial settings using the updateSettings method', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 10),
mergeCells: [
{row: 0, col: 0, rowspan: 2, colspan: 2}
]
});
var TD = hot.rootElement.querySelector('td');
expect(TD.getAttribute('rowspan')).toBe('2');
expect(TD.getAttribute('colspan')).toBe('2');
updateSettings({
mergeCells: [
{row: 2, col: 2, rowspan: 2, colspan: 2}
]
});
TD = hot.rootElement.querySelector('td');
expect(TD.getAttribute('rowspan')).toBe(null);
expect(TD.getAttribute('colspan')).toBe(null);
TD = getCell(2, 2);
expect(TD.getAttribute('rowspan')).toBe('2');
expect(TD.getAttribute('colspan')).toBe('2');
});
it('should allow resetting the merged cells by changing it to \'true\'', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 10),
mergeCells: [
{row: 0, col: 0, rowspan: 2, colspan: 2}
]
});
var TD = hot.rootElement.querySelector('td');
expect(TD.getAttribute('rowspan')).toBe('2');
expect(TD.getAttribute('colspan')).toBe('2');
updateSettings({
mergeCells: true
});
TD = hot.rootElement.querySelector('td');
expect(TD.getAttribute('rowspan')).toBe(null);
expect(TD.getAttribute('colspan')).toBe(null);
});
it('should allow resetting and turning off the mergeCells plugin by changing mergeCells to \'false\'', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 10),
mergeCells: [
{row: 0, col: 0, rowspan: 2, colspan: 2}
]
});
var TD = hot.rootElement.querySelector('td');
expect(TD.getAttribute('rowspan')).toBe('2');
expect(TD.getAttribute('colspan')).toBe('2');
updateSettings({
mergeCells: false
});
TD = hot.rootElement.querySelector('td');
expect(TD.getAttribute('rowspan')).toBe(null);
expect(TD.getAttribute('colspan')).toBe(null);
});
});
describe('mergeCells copy', () => {
it('should not copy text of cells that are merged into another cell', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5),
mergeCells: [
{row: 0, col: 0, rowspan: 2, colspan: 2}
]
});
expect(hot.getCopyableText(0, 0, 2, 2)).toBe('A1\t\tC1\n\t\tC2\nA3\tB3\tC3');
});
});
describe('merged cells selection', () => {
it('should select the whole range of cells which form a merged cell', function() {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(4, 4),
mergeCells: [
{
row: 0,
col: 0,
colspan: 4,
rowspan: 1
}
]
});
var $table = this.$container.find('table.htCore');
var $td = $table.find('tr:eq(0) td:eq(0)');
expect($td.attr('rowspan')).toEqual('1');
expect($td.attr('colspan')).toEqual('4');
expect(hot.getSelected()).toBeUndefined();
hot.selectCell(0, 0);
expect(hot.getSelected()).toEqual([0, 0, 0, 3]);
deselectCell();
hot.selectCell(0, 1);
expect(hot.getSelected()).toEqual([0, 0, 0, 3]);
});
it('should always make a rectangular selection, when selecting merged and not merged cells', function() {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(4, 4),
mergeCells: [
{
row: 1,
col: 1,
colspan: 3,
rowspan: 2
}
]
});
var $table = this.$container.find('table.htCore');
var $td = $table.find('tr:eq(1) td:eq(1)');
expect($td.attr('rowspan')).toEqual('2');
expect($td.attr('colspan')).toEqual('3');
expect(hot.getSelected()).toBeUndefined();
hot.selectCell(0, 0);
expect(hot.getSelected()).toEqual([0, 0, 0, 0]);
deselectCell();
hot.selectCell(0, 0, 1, 1);
expect(hot.getSelected()).not.toEqual([0, 0, 1, 1]);
expect(hot.getSelected()).toEqual([0, 0, 2, 3]);
deselectCell();
hot.selectCell(0, 1, 1, 1);
expect(hot.getSelected()).toEqual([0, 1, 2, 3]);
});
it('should not switch the selection start point when selecting from non-merged cells to merged cells', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 10),
mergeCells: [
{row: 1, col: 1, rowspan: 3, colspan: 3},
{row: 3, col: 4, rowspan: 2, colspan: 2}
]
});
$(hot.getCell(6, 6)).simulate('mousedown');
expect(hot.getSelectedRange().from.col).toEqual(6);
expect(hot.getSelectedRange().from.row).toEqual(6);
$(hot.getCell(1, 1)).simulate('mouseenter');
expect(hot.getSelectedRange().from.col).toEqual(6);
expect(hot.getSelectedRange().from.row).toEqual(6);
$(hot.getCell(3, 3)).simulate('mouseenter');
expect(hot.getSelectedRange().from.col).toEqual(6);
expect(hot.getSelectedRange().from.row).toEqual(6);
$(hot.getCell(4, 4)).simulate('mouseenter');
expect(hot.getSelectedRange().from.col).toEqual(6);
expect(hot.getSelectedRange().from.row).toEqual(6);
});
it('should select cells in the correct direction when changing selections around a merged range', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 10),
mergeCells: [
{row: 4, col: 4, rowspan: 2, colspan: 2}
]
});
hot.selectCell(5, 5, 5, 2);
expect(hot.getSelectedRange().getDirection()).toEqual('SE-NW');
hot.selectCell(4, 4, 2, 5);
expect(hot.getSelectedRange().getDirection()).toEqual('SW-NE');
hot.selectCell(4, 4, 5, 7);
expect(hot.getSelectedRange().getDirection()).toEqual('NW-SE');
hot.selectCell(4, 5, 7, 5);
expect(hot.getSelectedRange().getDirection()).toEqual('NE-SW');
});
it('should not add an area class to the selected cell if a single merged cell is selected', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(6, 6),
mergeCells: [
{
row: 1,
col: 1,
colspan: 3,
rowspan: 2
}
]
});
selectCell(1, 1);
expect(getCell(1, 1).className.indexOf('area')).toEqual(-1);
selectCell(1, 1, 4, 4);
expect(getCell(1, 1).className.indexOf('area')).not.toEqual(-1);
selectCell(1, 1);
expect(getCell(1, 1).className.indexOf('area')).toEqual(-1);
selectCell(0, 0);
expect(getCell(1, 1).className.indexOf('area')).toEqual(-1);
});
});
describe('merged cells scroll', () => {
it('getCell should return merged cell parent', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5),
mergeCells: [
{row: 0, col: 0, rowspan: 2, colspan: 2}
],
height: 100,
width: 400
});
var mergedCellParent = hot.getCell(0, 0);
var mergedCellHidden = hot.getCell(1, 1);
expect(mergedCellHidden).toBe(mergedCellParent);
});
it('should scroll viewport to beginning of a merged cell when it\'s clicked', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5),
mergeCells: [
{row: 5, col: 0, rowspan: 2, colspan: 2}
],
height: 100,
width: 400
});
var mainHolder = hot.view.wt.wtTable.holder;
mainHolder.scrollTop = 130;
hot.render();
expect(mainHolder.scrollTop).toBe(130);
var TD = hot.getCell(5, 0);
mouseDown(TD);
mouseUp(TD);
var mergedCellScrollTop = mainHolder.scrollTop;
expect(mergedCellScrollTop).toBeLessThan(130);
expect(mergedCellScrollTop).toBeGreaterThan(0);
mainHolder.scrollTop = 0;
hot.render();
mainHolder.scrollTop = 130;
hot.render();
TD = hot.getCell(5, 2);
mouseDown(TD);
mouseUp(TD);
var regularCellScrollTop = mainHolder.scrollTop;
expect(mergedCellScrollTop).toBe(regularCellScrollTop);
});
it('should render whole merged cell even when most rows are not in the viewport - scrolled to top', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(40, 5),
mergeCells: [
{row: 1, col: 0, rowspan: 21, colspan: 2},
{row: 21, col: 2, rowspan: 18, colspan: 2}
],
height: 100,
width: 400
});
expect(hot.countRenderedRows()).toBe(39);
});
it('should render whole merged cell even when most rows are not in the viewport - scrolled to bottom', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(40, 5),
mergeCells: [
{row: 1, col: 0, rowspan: 21, colspan: 2},
{row: 21, col: 2, rowspan: 18, colspan: 2}
],
height: 100,
width: 400
});
var mainHolder = hot.view.wt.wtTable.holder;
$(mainHolder).scrollTop(99999);
hot.render();
expect(hot.countRenderedRows()).toBe(39);
});
it('should render whole merged cell even when most columns are not in the viewport - scrolled to the left', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(5, 40),
mergeCells: [
{row: 0, col: 1, rowspan: 2, colspan: 21},
{row: 2, col: 21, rowspan: 2, colspan: 18}
],
height: 100,
width: 400
});
expect(hot.countRenderedCols()).toBe(39);
});
it('should render whole merged cell even when most columns are not in the viewport - scrolled to the right', function() {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(5, 40),
mergeCells: [
{row: 0, col: 1, rowspan: 2, colspan: 21},
{row: 2, col: 21, rowspan: 2, colspan: 18}
],
height: 100,
width: 400
});
this.$container.scrollLeft(99999);
hot.render();
expect(hot.countRenderedCols()).toBe(39);
});
});
describe('merge cells shift', () => {
it('should shift the merged cells right, when inserting a column on the left side of them', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetData(20, 20),
mergeCells: [
{row: 1, col: 1, rowspan: 2, colspan: 2},
{row: 2, col: 5, rowspan: 2, colspan: 2}
],
height: 400,
width: 400
});
hot.alter('insert_col', 3, 2);
expect(hot.mergeCells.mergedCellInfoCollection[0].col).toEqual(1);
expect(hot.mergeCells.mergedCellInfoCollection[1].col).toEqual(6);
});
it('should shift the merged cells left, when removing a column on the left side of them', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetData(20, 20),
mergeCells: [
{row: 1, col: 1, rowspan: 2, colspan: 2},
{row: 2, col: 5, rowspan: 2, colspan: 2}
],
height: 400,
width: 400
});
hot.alter('remove_col', 3, 2);
expect(hot.mergeCells.mergedCellInfoCollection[0].col).toEqual(1);
expect(hot.mergeCells.mergedCellInfoCollection[1].col).toEqual(4);
});
it('should shift the merged cells down, when inserting a row above them', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetData(20, 20),
mergeCells: [
{row: 1, col: 1, rowspan: 2, colspan: 2},
{row: 5, col: 5, rowspan: 2, colspan: 2}
],
height: 400,
width: 400
});
hot.alter('insert_row', 3, 2);
expect(hot.mergeCells.mergedCellInfoCollection[0].row).toEqual(1);
expect(hot.mergeCells.mergedCellInfoCollection[1].row).toEqual(6);
});
it('should shift the merged cells down, when inserting a row above them', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetData(20, 20),
mergeCells: [
{row: 1, col: 1, rowspan: 2, colspan: 2},
{row: 5, col: 5, rowspan: 2, colspan: 2}
],
height: 400,
width: 400
});
hot.alter('remove_row', 3, 2);
expect(hot.mergeCells.mergedCellInfoCollection[0].row).toEqual(1);
expect(hot.mergeCells.mergedCellInfoCollection[1].row).toEqual(4);
});
});
xdescribe('canMergeRange', () => {
it('should return false if start and end cell is the same', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5)
});
var mergeCells = new Handsontable.plugins.MergeCells(hot);
var coordsFrom = new WalkontableCellCoords(0, 1);
var cellRange = new WalkontableCellRange(coordsFrom, coordsFrom, new WalkontableCellCoords(0, 1));
var result = mergeCells.canMergeRange(cellRange);
expect(result).toBe(false);
});
it('should return true for 2 consecutive cells in the same column', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5)
});
var mergeCells = new Handsontable.plugins.MergeCells(hot);
var coordsFrom = new WalkontableCellCoords(0, 1);
var cellRange = new WalkontableCellRange(coordsFrom, coordsFrom, new WalkontableCellCoords(1, 1));
var result = mergeCells.canMergeRange(cellRange);
expect(result).toBe(true);
});
it('should return true for 2 consecutive cells in the same row', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5)
});
var mergeCells = new Handsontable.MergeCells(hot);
var coordsFrom = new WalkontableCellCoords(0, 1);
var cellRange = new WalkontableCellRange(coordsFrom, coordsFrom, new WalkontableCellCoords(0, 2));
var result = mergeCells.canMergeRange(cellRange);
expect(result).toBe(true);
});
it('should return true for 4 neighboring cells', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5)
});
var mergeCells = new Handsontable.MergeCells(hot);
var coordsFrom = new WalkontableCellCoords(0, 1);
var cellRange = new WalkontableCellRange(coordsFrom, coordsFrom, new WalkontableCellCoords(1, 2));
var result = mergeCells.canMergeRange(cellRange);
expect(result).toBe(true);
});
});
xdescribe('modifyTransform', () => {
it('should not transform arrow right when entering a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(1, 0);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(0, 1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(0, 1));
});
it('should transform arrow right when leaving a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(1, 1);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(0, 1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(0, 3));
});
it('should transform arrow right when leaving a merged cell (return to desired row)', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var coords = new WalkontableCellCoords(2, 0);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var inDelta = new WalkontableCellCoords(0, 1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(-1, 1));
coords = new WalkontableCellCoords(1, 1);
currentSelection = new WalkontableCellRange(coords, coords, coords);
inDelta = new WalkontableCellCoords(0, 1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(1, 3));
});
it('should transform arrow left when entering a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(1, 4);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(0, -1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(0, -3));
});
it('should not transform arrow left when leaving a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(1, 1);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(0, -1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(0, -1));
});
it('should transform arrow left when leaving a merged cell (return to desired row)', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var coords = new WalkontableCellCoords(2, 4);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var inDelta = new WalkontableCellCoords(0, -1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(-1, -3));
coords = new WalkontableCellCoords(1, 1);
currentSelection = new WalkontableCellRange(coords, coords, coords);
inDelta = new WalkontableCellCoords(0, -1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(1, -1));
});
it('should not transform arrow down when entering a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(0, 1);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(0, -1);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(0, -1));
});
it('should transform arrow down when leaving a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(1, 1);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(1, 0);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(3, 0));
});
it('should transform arrow up when entering a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(4, 1);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(-1, 0);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(-3, 0));
});
it('should not transform arrow up when leaving a merged cell', () => {
var mergeCellsSettings = [
{row: 1, col: 1, rowspan: 3, colspan: 3}
];
var coords = new WalkontableCellCoords(1, 1);
var currentSelection = new WalkontableCellRange(coords, coords, coords);
var mergeCells = new Handsontable.MergeCells(mergeCellsSettings);
var inDelta = new WalkontableCellCoords(-1, 0);
mergeCells.modifyTransform('modifyTransformStart', currentSelection, inDelta);
expect(inDelta).toEqual(new WalkontableCellCoords(-1, 0));
});
});
describe('ContextMenu', () => {
it('should disable `Merge cells` context menu item when context menu was triggered from corner header', () => {
var hot = handsontable({
data: Handsontable.helper.createSpreadsheetObjectData(10, 5),
rowHeaders: true,
colHeaders: true,
contextMenu: true,
mergeCells: true,
});
$('.ht_clone_top_left_corner .htCore').find('thead').find('th').eq(0).simulate('mousedown', {which: 3});
contextMenu();
expect($('.htContextMenu tbody td.htDisabled').text()).toBe([
'Insert column on the left',
'Insert column on the right',
'Remove row',
'Remove column',
'Undo',
'Redo',
'Read only',
'Alignment',
'Merge cells',
].join(''));
});
});
});