react-microspreadsheet
Version:
A pluggable spreadsheet component.
359 lines (294 loc) • 12.8 kB
text/coffeescript
Mousetrap = require 'mousetrap'
React = require 'react/addons'
testUtils = React.addons.TestUtils
expect = chai.expect
Spreadsheet = require '../src/Spreadsheet.coffee'
utils =
reset: ->
React.unmountComponentAtNode($('div#test-node')[0])
$('div#test-node').remove()
testNode: ->
testNode = $('<div>').attr('id', 'test-node')
$('body').append(testNode)
testNode.empty()
return testNode[0]
describe 'basic', ->
this.timeout 50000
describe 'text cells', ->
before (done) ->
cells = [
['','123','','123']
['asd','','sad','']
['','as','','sd']
['das','','123','']
]
React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
after utils.reset
it 'renders the table', (done) ->
expect($('table.microspreadsheet')).to.exist
done()
it 'renders the rows and cells at the right places', (done) ->
cells = []
for row in $('.microspreadsheet tr')
rowArray = []
for cell in $(row).find('td')
rowArray.push $(cell).text()
cells.push rowArray
expect(cells[0]).to.eql ['', 'A', 'B', 'C', 'D']
expect(cells[1]).to.eql ['1', '', '123', '', '123']
expect(cells[4]).to.eql ['4', 'das', '', '123', '']
done()
describe 'formulas (refs, expressions and sum() with matrixes)', (done) ->
before (done) ->
cells = [
['=A2','123']
['7','=B1+A2']
['=2+B3','=1+2']
['=SUM(A1,A2)','=sUm(A1:B2)']
]
React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
it 'renders the table', (done) ->
expect($('table.microspreadsheet')).to.exist
done()
it 'renders the rows and cells with the right values', (done) ->
cells = []
for row in $('.microspreadsheet tr')
rowArray = []
for cell in $(row).find('td')
rowArray.push $(cell).text()
cells.push rowArray
expect(cells[1]).to.eql ['1', '7', '123']
expect(cells[2]).to.eql ['2', '7', '130']
expect(cells[3]).to.eql ['3', '5', '3']
expect(cells[4]).to.eql ['4', '14', '267']
done()
describe 'dbclick, edit, click outside', ->
sheet = null
secondCell = null
input = null
before (done) ->
cells = [
['44','123']
['7','']
['=SUM(A1,A2)','=sUm(A1:B2)']
]
sheet = React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
after utils.reset
it 'finds the second cell and starts editing', (done) ->
secondCell = testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[1]
span = testUtils.findRenderedDOMComponentWithTag(secondCell, 'span')
testUtils.Simulate.doubleClick(span)
expect($('.microspreadsheet .cell input')[0]).to.exist
expect($('.microspreadsheet .cell input').val()).to.eql '123'
done()
it 'changes its value', (done) ->
input = testUtils.findRenderedDOMComponentWithTag(secondCell, 'input')
testUtils.Simulate.change(input, {target: {value: '16'}})
expect($('.microspreadsheet .cell input').val()).to.eql '16'
done()
it 'click at other cell and the table recalculates', (done) ->
$('.microspreadsheet .cell span').eq(3).click()
expect($('.microspreadsheet .cell input').length).to.eql 0
expect($('.microspreadsheet .cell').eq(1).text()).to.eql '16'
expect($('.microspreadsheet .cell').eq(4).text()).to.eql '51'
expect($('.microspreadsheet .cell').eq(5).text()).to.eql '67'
done()
describe 'change it from outside', ->
sheet = null
before (done) ->
cells = [
['=A2','123']
['7','=B1+A2']
['=2+B3','=1+2']
['=SUM(A1,A2)','=sUm(A1:B2)']
]
sheet = React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
after utils.reset
it 'renders the rows and cells with the right values', (done) ->
cells = []
for row in $('.microspreadsheet tr')
rowArray = []
for cell in $(row).find('td')
rowArray.push $(cell).text()
cells.push rowArray
expect(cells[1]).to.eql ['1', '7', '123']
expect(cells[2]).to.eql ['2', '7', '130']
expect(cells[3]).to.eql ['3', '5', '3']
expect(cells[4]).to.eql ['4', '14', '267']
done()
it 'changes when the `cells` prop changes', (done) ->
cells = [
['x3', '=A1']
['', '']
['', '']
['', '']
['', '']
['b', 'c']
]
sheet.setProps cells: cells
cells = []
for row in $('.microspreadsheet tr')
rowArray = []
for cell in $(row).find('td')
rowArray.push $(cell).text()
cells.push rowArray
expect(cells[1]).to.eql ['1', 'x3', 'x3']
expect(cells[2]).to.eql ['2', '', '']
expect(cells[3]).to.eql ['3', '', '']
expect(cells[4]).to.eql ['4', '', '']
expect(cells[5]).to.eql ['5', '', '']
expect(cells[6]).to.eql ['6', 'b', 'c']
done()
describe 'click to add reference', ->
sheet = null
secondCell = null
input = null
before (done) ->
cells = [
['5','2']
['7','9']
]
sheet = React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
after utils.reset
it 'finds the second cell and starts editing', (done) ->
secondCell = testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[1]
span = testUtils.findRenderedDOMComponentWithTag(secondCell, 'span')
testUtils.Simulate.doubleClick(span)
done()
it 'adds a "="', (done) ->
input = testUtils.findRenderedDOMComponentWithTag(secondCell, 'input')
testUtils.Simulate.change(input, {target: {value: '='}})
expect($('.microspreadsheet .cell input').val()).to.eql '='
done()
it 'clicks at other cell and add its addr to the input', (done) ->
fourthCell = testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[3]
span = testUtils.findRenderedDOMComponentWithTag(fourthCell, 'span')
testUtils.Simulate.click(span)
expect($('.microspreadsheet .cell input').val()).to.eql '=B2'
done()
it 'clicks at another cell and replace the previous one', (done) ->
thirdCell = testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[2]
span = testUtils.findRenderedDOMComponentWithTag(thirdCell, 'span')
testUtils.Simulate.click(span)
expect($('.microspreadsheet .cell input').val()).to.eql '=A2'
done()
it 'recalculates when enter is pressed', (done) ->
KeyEvent.simulate(0, 13)
expect($('.microspreadsheet .cell input').length).to.eql 0
expect($('.microspreadsheet .cell').eq(1).text()).to.eql '7'
expect($('.microspreadsheet .cell').eq(2).text()).to.eql '7'
expect($('.microspreadsheet .cell').eq(3).text()).to.eql '9'
done()
describe 'movement and keyboard shortcuts', ->
before (done) ->
cells = [
['5','2']
['7','9']
]
React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
after utils.reset
it 'starts at the first cell', (done) ->
expect($('.microspreadsheet .cell').eq(0).hasClass('selected')).to.eql true
done()
it 'goes right', (done) ->
KeyEvent.simulate(0, 39)
expect($('.microspreadsheet .cell').eq(1).hasClass('selected')).to.eql true
done()
it 'deletes the content inside', (done) ->
KeyEvent.simulate(0, 46)
expect($('.microspreadsheet .cell span').eq(1).text()).to.eql ''
done()
it 'does not go up', (done) ->
KeyEvent.simulate(0, 38)
expect($('.microspreadsheet .cell').eq(1).hasClass('selected')).to.eql true
done()
it 'goes down', (done) ->
KeyEvent.simulate(0, 40)
expect($('.microspreadsheet .cell').eq(3).hasClass('selected')).to.eql true
done()
it 'starts editing with a keypress (also replacing the text field with the corresponding char)', (done) ->
KeyEvent.simulate(80, 80)
expect($('.microspreadsheet .cell').eq(3).hasClass('selected')).to.eql true
expect($('.microspreadsheet .cell input')).to.have.length 1
expect($('.microspreadsheet .cell input').val()).to.eql 'P'
done()
it 'cancels the edit', (done) ->
KeyEvent.simulate(0, 27)
expect($('.microspreadsheet .cell').eq(3).text()).to.eql '9'
done()
describe 'undo, redo', ->
before (done) ->
cells = [
['a', 'b']
['c', 'd']
]
React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
after utils.reset
it 'starts editing with a keypress (also replacing the text field with the corresponding char)', (done) ->
KeyEvent.simulate(81, 81)
expect($('.microspreadsheet .cell input').val()).to.eql 'Q'
done()
it 'saves the edit', (done) ->
KeyEvent.simulate(0, 13)
expect($('.microspreadsheet .cell span').length).to.eql 4
expect($('.microspreadsheet .cell span').eq(0).text()).to.eql 'Q'
done()
it 'undoes the edit', (done) ->
KeyEvent.simulate(90, 90, ['ctrl'])
expect($('.microspreadsheet .cell span').eq(0).text()).to.eql 'a'
done()
it 'redoes the edit', (done) ->
KeyEvent.simulate(89, 89, ['ctrl'])
expect($('.microspreadsheet .cell span').eq(0).text()).to.eql 'Q'
done()
describe 'multi select', ->
sheet = null
before (done) ->
cells = [
['', 'b']
['a', '']
['', '']
['', '']
]
sheet = React.renderComponent Spreadsheet(cells: cells), utils.testNode(), done
after utils.reset
it 'starts selecting', (done) ->
first = testUtils.findRenderedDOMComponentWithTag(testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[0], 'span')
testUtils.Simulate.mouseDown(first)
expect($('.microspreadsheet .cell').eq(0).hasClass('multi-selected')).to.eql false
expect($('.microspreadsheet .cell').eq(1).hasClass('multi-selected')).to.eql false
expect($('.microspreadsheet .cell').eq(2).hasClass('multi-selected')).to.eql false
expect($('.microspreadsheet .cell').eq(3).hasClass('multi-selected')).to.eql false
done()
it 'moves transversally', (done) ->
first = testUtils.findRenderedDOMComponentWithTag(testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[0], 'span')
third = testUtils.findRenderedDOMComponentWithTag(testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[2], 'span')
testUtils.SimulateNative.mouseOut(first, {relatedTarget: $('.microspreadsheet .cell').eq(2)[0]})
testUtils.SimulateNative.mouseOver(third, {relatedTarget: $('.microspreadsheet .cell').eq(0)[0]})
expect($('.microspreadsheet .cell').eq(0).hasClass('multi-selected')).to.eql true
expect($('.microspreadsheet .cell').eq(1).hasClass('multi-selected')).to.eql true
expect($('.microspreadsheet .cell').eq(2).hasClass('multi-selected')).to.eql true
expect($('.microspreadsheet .cell').eq(3).hasClass('multi-selected')).to.eql false
done()
it 'moves back and the selection adjusts', (done) ->
third = testUtils.findRenderedDOMComponentWithTag(testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[2], 'span')
second = testUtils.findRenderedDOMComponentWithTag(testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[1], 'span')
testUtils.SimulateNative.mouseOut(third, {relatedTarget: $('.microspreadsheet .cell').eq(1)[0]})
testUtils.SimulateNative.mouseOver(second, {relatedTarget: $('.microspreadsheet .cell').eq(2)[0]})
expect($('.microspreadsheet .cell').eq(0).hasClass('multi-selected')).to.eql true
expect($('.microspreadsheet .cell').eq(1).hasClass('multi-selected')).to.eql true
expect($('.microspreadsheet .cell').eq(2).hasClass('multi-selected')).to.eql false
done()
it 'stops selecting and the selection continues', (done) ->
second = testUtils.findRenderedDOMComponentWithTag(testUtils.scryRenderedDOMComponentsWithClass(sheet, 'cell')[1], 'span')
testUtils.Simulate.mouseUp(second)
expect($('.microspreadsheet .cell').eq(0).hasClass('multi-selected')).to.eql true
expect($('.microspreadsheet .cell').eq(1).hasClass('multi-selected')).to.eql true
expect($('.microspreadsheet .cell').eq(2).hasClass('multi-selected')).to.eql false
done()
it 'deletes everything under the selection', (done) ->
KeyEvent.simulate(0, 46)
expect($('.microspreadsheet .cell').eq(0).text()).to.eql ''
expect($('.microspreadsheet .cell').eq(1).text()).to.eql ''
expect($('.microspreadsheet .cell').eq(2).text()).to.eql 'a'
done()