mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-21 22:54:11 +08:00
update(components): Add support to create new items inside EditableList
- Adds logic to allow simple item creation - Adds new onItemCreated callback - Updates specs
This commit is contained in:
parent
68a0a81f77
commit
31796e396d
3 changed files with 87 additions and 8 deletions
|
@ -38,6 +38,37 @@ describe('EditableList', ()=> {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('_onCreateInputKeyDown', ()=> {
|
||||||
|
it('calls onItemCreated', ()=> {
|
||||||
|
const onItemCreated = jasmine.createSpy('onItemCreated');
|
||||||
|
const list = makeList(['1', '2'], {initialState: {creatingItem: true}, onItemCreated});
|
||||||
|
const createItem = findRenderedDOMComponentWithClass(list, 'create-item');
|
||||||
|
const input = findRenderedDOMComponentWithTag(createItem, 'input');
|
||||||
|
findDOMNode(input).value = 'New Item';
|
||||||
|
|
||||||
|
Simulate.keyDown(input, {key: 'Enter'});
|
||||||
|
|
||||||
|
expect(onItemCreated).toHaveBeenCalledWith('New Item');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_onCreateItem', ()=> {
|
||||||
|
it('should call prop callback when provided', ()=> {
|
||||||
|
const onCreateItem = jasmine.createSpy('onCreateItem');
|
||||||
|
const list = makeList(['1', '2'], {onCreateItem});
|
||||||
|
|
||||||
|
list._onCreateItem();
|
||||||
|
expect(onCreateItem).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set state for creating item when no callback provided', ()=> {
|
||||||
|
const list = makeList(['1', '2']);
|
||||||
|
spyOn(list, 'setState');
|
||||||
|
list._onCreateItem();
|
||||||
|
expect(list.setState).toHaveBeenCalledWith({creatingItem: true});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('_renderItem', ()=> {
|
describe('_renderItem', ()=> {
|
||||||
const makeItem = (item, idx, state = {}, handlers = {})=> {
|
const makeItem = (item, idx, state = {}, handlers = {})=> {
|
||||||
const list = makeList();
|
const list = makeList();
|
||||||
|
@ -109,18 +140,26 @@ describe('EditableList', ()=> {
|
||||||
const innerList = findDOMNode(
|
const innerList = findDOMNode(
|
||||||
findRenderedDOMComponentWithClass(list, 'items-wrapper')
|
findRenderedDOMComponentWithClass(list, 'items-wrapper')
|
||||||
);
|
);
|
||||||
|
expect(()=> {
|
||||||
|
findRenderedDOMComponentWithClass(list, 'create-item');
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
expect(innerList.childNodes.length).toEqual(3);
|
expect(innerList.childNodes.length).toEqual(3);
|
||||||
items.forEach((item, idx)=> expect(innerList.childNodes[idx].textContent).toEqual(item));
|
items.forEach((item, idx)=> expect(innerList.childNodes[idx].textContent).toEqual(item));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders create input as an item when creating', ()=> {
|
||||||
|
const items = ['1', '2', '3'];
|
||||||
|
const list = makeList(items, {initialState: {creatingItem: true}});
|
||||||
|
const createItem = findRenderedDOMComponentWithClass(list, 'create-item');
|
||||||
|
expect(createItem).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('renders add button', ()=> {
|
it('renders add button', ()=> {
|
||||||
const onCreateItem = jasmine.createSpy('onCreateItem');
|
const list = makeList();
|
||||||
const list = makeList([], {onCreateItem});
|
|
||||||
const button = scryRenderedDOMComponentsWithClass(list, 'btn-editable-list')[0];
|
const button = scryRenderedDOMComponentsWithClass(list, 'btn-editable-list')[0];
|
||||||
|
|
||||||
Simulate.click(button);
|
expect(findDOMNode(button).textContent).toEqual('+');
|
||||||
|
|
||||||
expect(onCreateItem).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders delete button', ()=> {
|
it('renders delete button', ()=> {
|
||||||
|
@ -130,6 +169,7 @@ describe('EditableList', ()=> {
|
||||||
|
|
||||||
Simulate.click(button);
|
Simulate.click(button);
|
||||||
|
|
||||||
|
expect(findDOMNode(button).textContent).toEqual('—');
|
||||||
expect(onDeleteItem).toHaveBeenCalledWith('2', 1);
|
expect(onDeleteItem).toHaveBeenCalledWith('2', 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,15 +16,16 @@ class EditableList extends Component {
|
||||||
onDeleteItem: PropTypes.func,
|
onDeleteItem: PropTypes.func,
|
||||||
onItemEdited: PropTypes.func,
|
onItemEdited: PropTypes.func,
|
||||||
onItemSelected: PropTypes.func,
|
onItemSelected: PropTypes.func,
|
||||||
|
onItemCreated: PropTypes.func,
|
||||||
initialState: PropTypes.object,
|
initialState: PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
children: [],
|
children: [],
|
||||||
onCreateItem: ()=> {},
|
|
||||||
onDeleteItem: ()=> {},
|
onDeleteItem: ()=> {},
|
||||||
onItemEdited: ()=> {},
|
onItemEdited: ()=> {},
|
||||||
onItemSelected: ()=> {},
|
onItemSelected: ()=> {},
|
||||||
|
onItemCreated: ()=> {},
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -34,6 +35,7 @@ class EditableList extends Component {
|
||||||
this.state = props.initialState || {
|
this.state = props.initialState || {
|
||||||
editing: null,
|
editing: null,
|
||||||
selected: null,
|
selected: null,
|
||||||
|
creatingItem: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +56,13 @@ class EditableList extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onCreateInputKeyDown = (event)=> {
|
||||||
|
if (_.includes(['Enter', 'Return'], event.key)) {
|
||||||
|
this.setState({creatingItem: false});
|
||||||
|
this.props.onItemCreated(event.target.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onItemClick = (event, item, idx)=> {
|
_onItemClick = (event, item, idx)=> {
|
||||||
this._selectItem(item, idx);
|
this._selectItem(item, idx);
|
||||||
}
|
}
|
||||||
|
@ -83,7 +92,11 @@ class EditableList extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onCreateItem = ()=> {
|
_onCreateItem = ()=> {
|
||||||
this.props.onCreateItem();
|
if (this.props.onCreateItem) {
|
||||||
|
this.props.onCreateItem();
|
||||||
|
} else {
|
||||||
|
this.setState({creatingItem: true});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDeleteItem = ()=> {
|
_onDeleteItem = ()=> {
|
||||||
|
@ -137,7 +150,23 @@ class EditableList extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_renderCreateItem = (onCreateInputKeyDown = this._onCreateInputKeyDown)=> {
|
||||||
|
return (
|
||||||
|
<div className="create-item">
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
type="text"
|
||||||
|
onKeyDown={onCreateInputKeyDown} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let items = this._items.map((item, idx)=> this._renderItem(item, idx));
|
||||||
|
if (this.state.creatingItem === true) {
|
||||||
|
items = items.concat(this._renderCreateItem());
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`nylas-editable-list ${this.props.className}`}>
|
<div className={`nylas-editable-list ${this.props.className}`}>
|
||||||
<div
|
<div
|
||||||
|
@ -145,7 +174,7 @@ class EditableList extends Component {
|
||||||
tabIndex="1"
|
tabIndex="1"
|
||||||
onBlur={this._onListBlur}
|
onBlur={this._onListBlur}
|
||||||
onKeyDown={this._onListKeyDown}>
|
onKeyDown={this._onListKeyDown}>
|
||||||
{this._items.map((item, idx)=> this._renderItem(item, idx))}
|
{items}
|
||||||
</div>
|
</div>
|
||||||
<div className="buttons-wrapper">
|
<div className="buttons-wrapper">
|
||||||
<button className="btn btn-small btn-editable-list" onClick={this._onCreateItem}>+</button>
|
<button className="btn btn-small btn-editable-list" onClick={this._onCreateItem}>+</button>
|
||||||
|
|
|
@ -31,6 +31,16 @@
|
||||||
color: @text-color-inverse-very-subtle;
|
color: @text-color-inverse-very-subtle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.create-item {
|
||||||
|
padding: @padding-small-vertical @padding-small-horizontal;
|
||||||
|
border-top: 1px solid @border-color-divider;
|
||||||
|
input {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.buttons-wrapper {
|
.buttons-wrapper {
|
||||||
|
|
Loading…
Reference in a new issue