mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 13:14:16 +08:00
update(specs): Add more test coverage to EditableList + refactors
This commit is contained in:
parent
0a2ee7be91
commit
1e8ffdd10b
2 changed files with 97 additions and 18 deletions
|
@ -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', ()=> {
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue