update(specs): Add more test coverage to EditableList + refactors

This commit is contained in:
Juan Tejada 2015-12-11 11:03:59 -08:00
parent 0a2ee7be91
commit 1e8ffdd10b
2 changed files with 97 additions and 18 deletions

View file

@ -26,6 +26,60 @@ describe('EditableList', ()=> {
});
});
describe('_onItemEdit', ()=> {
it('enters editing mode when double click when item is not React Element', ()=> {
const list = makeList(['1', '2']);
spyOn(list, 'setState');
const item = scryRenderedDOMComponentsWithClass(list, 'editable-item')[0];
Simulate.doubleClick(item);
expect(list.setState).toHaveBeenCalledWith({editing: 0});
});
it('enters editing mode when edit icon clicked when item is not React Element', ()=> {
const list = makeList(['1', '2']);
spyOn(list, 'setState');
const editIcon = scryRenderedDOMComponentsWithClass(list, 'edit-icon')[0];
Simulate.click(editIcon);
expect(list.setState).toHaveBeenCalledWith({editing: 0});
});
it('does not enters editing mode when item is React Element', ()=> {
const item = <div></div>;
const list = makeList(['1', item]);
spyOn(list, 'setState');
list._onItemEdit({}, item, 1);
expect(list.setState).not.toHaveBeenCalled();
});
});
describe('_onListBlur', ()=> {
it('clears selection when requirements met', ()=> {
const list = makeList(['1', '2'], {allowEmptySelection: true});
const innerList = findRenderedDOMComponentWithClass(list, 'items-wrapper');
list._beganEditing = false;
spyOn(list, 'setState');
Simulate.blur(innerList);
expect(list.setState).toHaveBeenCalledWith({selected: null});
});
it('does not clear selection when entering edit mode', ()=> {
const list = makeList(['1', '2'], {allowEmptySelection: true});
const innerList = findRenderedDOMComponentWithClass(list, 'items-wrapper');
list._beganEditing = true;
spyOn(list, 'setState');
Simulate.blur(innerList);
expect(list.setState).not.toHaveBeenCalled();
});
});
describe('_onListKeyDown', ()=> {
it('calls onItemSelected', ()=> {
const onItemSelected = jasmine.createSpy('onItemSelected');
@ -36,6 +90,26 @@ describe('EditableList', ()=> {
expect(onItemSelected).toHaveBeenCalledWith('2', 1);
});
it('does not select an item when at the bottom of the list and moves down', ()=> {
const onItemSelected = jasmine.createSpy('onItemSelected');
const list = makeList(['1', '2'], {initialState: {selected: 1}, onItemSelected});
const innerList = findRenderedDOMComponentWithClass(list, 'items-wrapper');
Simulate.keyDown(innerList, {key: 'ArrowDown'});
expect(onItemSelected).not.toHaveBeenCalled();
});
it('does not select an item when at the top of the list and moves up', ()=> {
const onItemSelected = jasmine.createSpy('onItemSelected');
const list = makeList(['1', '2'], {initialState: {selected: 0}, onItemSelected});
const innerList = findRenderedDOMComponentWithClass(list, 'items-wrapper');
Simulate.keyDown(innerList, {key: 'ArrowUp'});
expect(onItemSelected).not.toHaveBeenCalled();
});
});
describe('_onCreateInputKeyDown', ()=> {
@ -79,16 +153,16 @@ describe('EditableList', ()=> {
it('binds correct click callbacks', ()=> {
const onClick = jasmine.createSpy('onClick');
const onDoubleClick = jasmine.createSpy('onDoubleClick');
const item = makeItem('item 1', 0, {}, {onClick, onDoubleClick});
const onEdit = jasmine.createSpy('onEdit');
const item = makeItem('item 1', 0, {}, {onClick, onEdit});
Simulate.click(item);
expect(onClick.calls[0].args[1]).toEqual('item 1');
expect(onClick.calls[0].args[2]).toEqual(0);
Simulate.doubleClick(item);
expect(onDoubleClick.calls[0].args[1]).toEqual('item 1');
expect(onDoubleClick.calls[0].args[2]).toEqual(0);
expect(onEdit.calls[0].args[1]).toEqual('item 1');
expect(onEdit.calls[0].args[2]).toEqual(0);
});
it('renders correctly when item is selected', ()=> {

View file

@ -59,10 +59,12 @@ class EditableList extends Component {
*/
/**
* If provided, this function will be called when an item is selected via click or arrow
* keys.
* keys. If the selection is cleared, it will receive null.
* @callback props.onItemSelected
* @param {(Component|string|number)} selectedItem - The selected item
* @param {number} idx - The index of the selected item
* @param {(Component|string|number)} selectedItem - The selected item or null
* when selection cleared
* @param {number} idx - The index of the selected item or null when selection
* cleared
*/
/**
* If provided, this function will be called when the user has entered a value to create
@ -103,7 +105,7 @@ class EditableList extends Component {
constructor(props) {
super(props);
this._doubleClickedItem = false;
this._beganEditing = false;
this.state = props.initialState || {
editing: null,
selected: (props.allowEmptySelection ? null : 0),
@ -127,9 +129,11 @@ class EditableList extends Component {
}
_selectItem = (item, idx)=> {
this.setState({selected: idx}, ()=> {
this.props.onItemSelected(item, idx);
});
if (this.state.selected !== idx) {
this.setState({selected: idx}, ()=> {
this.props.onItemSelected(item, idx);
});
}
}
/**
@ -151,7 +155,7 @@ class EditableList extends Component {
}
_onEditInputFocus = ()=> {
this._doubleClickedItem = false;
this._beganEditing = false;
}
_onEditInputKeyDown = (event, item, idx)=> {
@ -180,15 +184,15 @@ class EditableList extends Component {
this._selectItem(item, idx);
}
_onItemDoubleClick = (event, item, idx)=> {
_onItemEdit = (event, item, idx)=> {
if (!React.isValidElement(item)) {
this._doubleClickedItem = true;
this._beganEditing = true;
this.setState({editing: idx});
}
}
_onListBlur = ()=> {
if (!this._doubleClickedItem && this.props.allowEmptySelection) {
if (!this._beganEditing && this.props.allowEmptySelection) {
this.setState({selected: null});
}
}
@ -265,9 +269,10 @@ class EditableList extends Component {
);
}
// handlers object for testing
_renderItem = (item, idx, {editing, selected} = this.state, handlers = {})=> {
const onClick = handlers.onClick || this._onItemClick;
const onDoubleClick = handlers.onDoubleClick || this._onItemDoubleClick;
const onEdit = handlers.onEdit || this._onItemEdit;
const classes = classNames({
'list-item': true,
@ -288,14 +293,14 @@ class EditableList extends Component {
className={classes}
key={idx}
onClick={_.partial(onClick, _, item, idx)}
onDoubleClick={_.partial(onDoubleClick, _, item, idx)}>
onDoubleClick={_.partial(onEdit, _, item, idx)}>
{itemToRender}
<RetinaImg
className="edit-icon"
name="edit-icon.png"
title="Edit Item"
mode={RetinaImg.Mode.ContentIsMask}
onClick={_.partial(onDoubleClick, _, item, idx)} />
onClick={_.partial(onEdit, _, item, idx)} />
</div>
);
}