describe('MergeCells', function () { var id = 'testContainer'; beforeEach(function () { this.$container = $('
').appendTo('body'); }); afterEach(function () { if (this.$container) { destroy(); this.$container.remove(); } }); describe('mergeCells option', function () { it('should merge cell in startup', function () { 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', function () { it('should allow to overwrite the initial settings using the updateSettings method', function () { 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\'', function () { 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\'', function () { 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', function () { it('should not copy text of cells that are merged into another cell', function () { 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', function () { 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', function () { 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', function () { 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', function () { 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', function () { it('getCell should return merged cell parent', function () { 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', function () { 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', function () { 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', function () { 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', 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 }); 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', function () { it('should shift the merged cells right, when inserting a column on the left side of them', function () { 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', function () { 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', function () { 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', function () { 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', function () { it('should return false if start and end cell is the same', function () { 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', function () { 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', function () { 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', function () { 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', function () { it('should not transform arrow right when entering a merged cell', function () { 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', function () { 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)', function () { 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', function () { 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', function () { 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)', function () { 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', function () { 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', function () { 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', function () { 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', function () { 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', function () { it('should disable `Merge cells` context menu item when context menu was triggered from corner header', function () { 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('')); }); }); });