mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-10-04 18:34:57 +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', ()=> {
|
describe('_onListKeyDown', ()=> {
|
||||||
it('calls onItemSelected', ()=> {
|
it('calls onItemSelected', ()=> {
|
||||||
const onItemSelected = jasmine.createSpy('onItemSelected');
|
const onItemSelected = jasmine.createSpy('onItemSelected');
|
||||||
|
@ -36,6 +90,26 @@ describe('EditableList', ()=> {
|
||||||
|
|
||||||
expect(onItemSelected).toHaveBeenCalledWith('2', 1);
|
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', ()=> {
|
describe('_onCreateInputKeyDown', ()=> {
|
||||||
|
@ -79,16 +153,16 @@ describe('EditableList', ()=> {
|
||||||
|
|
||||||
it('binds correct click callbacks', ()=> {
|
it('binds correct click callbacks', ()=> {
|
||||||
const onClick = jasmine.createSpy('onClick');
|
const onClick = jasmine.createSpy('onClick');
|
||||||
const onDoubleClick = jasmine.createSpy('onDoubleClick');
|
const onEdit = jasmine.createSpy('onEdit');
|
||||||
const item = makeItem('item 1', 0, {}, {onClick, onDoubleClick});
|
const item = makeItem('item 1', 0, {}, {onClick, onEdit});
|
||||||
|
|
||||||
Simulate.click(item);
|
Simulate.click(item);
|
||||||
expect(onClick.calls[0].args[1]).toEqual('item 1');
|
expect(onClick.calls[0].args[1]).toEqual('item 1');
|
||||||
expect(onClick.calls[0].args[2]).toEqual(0);
|
expect(onClick.calls[0].args[2]).toEqual(0);
|
||||||
|
|
||||||
Simulate.doubleClick(item);
|
Simulate.doubleClick(item);
|
||||||
expect(onDoubleClick.calls[0].args[1]).toEqual('item 1');
|
expect(onEdit.calls[0].args[1]).toEqual('item 1');
|
||||||
expect(onDoubleClick.calls[0].args[2]).toEqual(0);
|
expect(onEdit.calls[0].args[2]).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly when item is selected', ()=> {
|
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
|
* 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
|
* @callback props.onItemSelected
|
||||||
* @param {(Component|string|number)} selectedItem - The selected item
|
* @param {(Component|string|number)} selectedItem - The selected item or null
|
||||||
* @param {number} idx - The index of the selected item
|
* 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
|
* 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) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this._doubleClickedItem = false;
|
this._beganEditing = false;
|
||||||
this.state = props.initialState || {
|
this.state = props.initialState || {
|
||||||
editing: null,
|
editing: null,
|
||||||
selected: (props.allowEmptySelection ? null : 0),
|
selected: (props.allowEmptySelection ? null : 0),
|
||||||
|
@ -127,9 +129,11 @@ class EditableList extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_selectItem = (item, idx)=> {
|
_selectItem = (item, idx)=> {
|
||||||
this.setState({selected: idx}, ()=> {
|
if (this.state.selected !== idx) {
|
||||||
this.props.onItemSelected(item, idx);
|
this.setState({selected: idx}, ()=> {
|
||||||
});
|
this.props.onItemSelected(item, idx);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -151,7 +155,7 @@ class EditableList extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onEditInputFocus = ()=> {
|
_onEditInputFocus = ()=> {
|
||||||
this._doubleClickedItem = false;
|
this._beganEditing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onEditInputKeyDown = (event, item, idx)=> {
|
_onEditInputKeyDown = (event, item, idx)=> {
|
||||||
|
@ -180,15 +184,15 @@ class EditableList extends Component {
|
||||||
this._selectItem(item, idx);
|
this._selectItem(item, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onItemDoubleClick = (event, item, idx)=> {
|
_onItemEdit = (event, item, idx)=> {
|
||||||
if (!React.isValidElement(item)) {
|
if (!React.isValidElement(item)) {
|
||||||
this._doubleClickedItem = true;
|
this._beganEditing = true;
|
||||||
this.setState({editing: idx});
|
this.setState({editing: idx});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onListBlur = ()=> {
|
_onListBlur = ()=> {
|
||||||
if (!this._doubleClickedItem && this.props.allowEmptySelection) {
|
if (!this._beganEditing && this.props.allowEmptySelection) {
|
||||||
this.setState({selected: null});
|
this.setState({selected: null});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,9 +269,10 @@ class EditableList extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlers object for testing
|
||||||
_renderItem = (item, idx, {editing, selected} = this.state, handlers = {})=> {
|
_renderItem = (item, idx, {editing, selected} = this.state, handlers = {})=> {
|
||||||
const onClick = handlers.onClick || this._onItemClick;
|
const onClick = handlers.onClick || this._onItemClick;
|
||||||
const onDoubleClick = handlers.onDoubleClick || this._onItemDoubleClick;
|
const onEdit = handlers.onEdit || this._onItemEdit;
|
||||||
|
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
'list-item': true,
|
'list-item': true,
|
||||||
|
@ -288,14 +293,14 @@ class EditableList extends Component {
|
||||||
className={classes}
|
className={classes}
|
||||||
key={idx}
|
key={idx}
|
||||||
onClick={_.partial(onClick, _, item, idx)}
|
onClick={_.partial(onClick, _, item, idx)}
|
||||||
onDoubleClick={_.partial(onDoubleClick, _, item, idx)}>
|
onDoubleClick={_.partial(onEdit, _, item, idx)}>
|
||||||
{itemToRender}
|
{itemToRender}
|
||||||
<RetinaImg
|
<RetinaImg
|
||||||
className="edit-icon"
|
className="edit-icon"
|
||||||
name="edit-icon.png"
|
name="edit-icon.png"
|
||||||
title="Edit Item"
|
title="Edit Item"
|
||||||
mode={RetinaImg.Mode.ContentIsMask}
|
mode={RetinaImg.Mode.ContentIsMask}
|
||||||
onClick={_.partial(onDoubleClick, _, item, idx)} />
|
onClick={_.partial(onEdit, _, item, idx)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue