From a143a573313d0ec23e3e14f5de5689ad8a3df92c Mon Sep 17 00:00:00 2001 From: panosalbanis Date: Sat, 16 Mar 2019 15:22:51 +0000 Subject: [PATCH 1/2] WIP Delete an item --- grocery-list-fe/src/apiCalls.js | 4 ++++ grocery-list-fe/src/components/Item/Item.css | 15 +++++++++++++++ grocery-list-fe/src/components/Item/Item.js | 5 ++++- grocery-list-fe/src/components/Item/Item.test.js | 14 ++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/grocery-list-fe/src/apiCalls.js b/grocery-list-fe/src/apiCalls.js index e9d84ab..b4b1446 100644 --- a/grocery-list-fe/src/apiCalls.js +++ b/grocery-list-fe/src/apiCalls.js @@ -21,4 +21,8 @@ const getGroceryList = async id => { return response.data || []; }; +const deleteItem = async () => { + return Promise.resolve('OK'); +}; + export { addGroceryList, addItem, getGroceryLists, getGroceryList }; diff --git a/grocery-list-fe/src/components/Item/Item.css b/grocery-list-fe/src/components/Item/Item.css index 18f410f..b28644f 100644 --- a/grocery-list-fe/src/components/Item/Item.css +++ b/grocery-list-fe/src/components/Item/Item.css @@ -9,3 +9,18 @@ li { .quantity { width: auto; } + +.deleteButton { + background: transparent; + border: 1px solid black; + border-radius: 2em; + color: black; + display: inline-block; + font-size: 12px; + height: 3em; + line-height: 1.5em; + margin: 0 0 8px; + padding: 0; + text-align: center; + width: 3em; +} diff --git a/grocery-list-fe/src/components/Item/Item.js b/grocery-list-fe/src/components/Item/Item.js index 97ce951..4551b62 100644 --- a/grocery-list-fe/src/components/Item/Item.js +++ b/grocery-list-fe/src/components/Item/Item.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import './Item.css'; function Item(props) { - const { name, quantity } = props; + const { name, quantity, onDeleteHandler } = props; return (
  • @@ -13,6 +13,9 @@ function Item(props) {

    {quantity}

    +
  • ); diff --git a/grocery-list-fe/src/components/Item/Item.test.js b/grocery-list-fe/src/components/Item/Item.test.js index 13eea8a..3d77c94 100644 --- a/grocery-list-fe/src/components/Item/Item.test.js +++ b/grocery-list-fe/src/components/Item/Item.test.js @@ -8,4 +8,18 @@ describe('Item', () => { expect(wrapper.find('.name').text()).toEqual('Milk'); expect(wrapper.find('.quantity').text()).toEqual('1'); }); + + it('renders a delete button', () => { + const wrapper = shallow(); + expect(wrapper.find('.deleteButton').length).toEqual(1); + }); + + it('calls the handler function when the `delete` button is pressed', () => { + const deleteHandlerSpy = jest.fn(); + const wrapper = shallow( + + ); + wrapper.find('.deleteButton').simulate('click'); + expect(deleteHandlerSpy).toHaveBeenCalledTimes(1); + }); }); From fefdfcb2115ae60809c50a52d4f27e4bcbe1dbe8 Mon Sep 17 00:00:00 2001 From: panosalbanis Date: Sun, 17 Mar 2019 15:02:05 +0000 Subject: [PATCH 2/2] WIP Add ability to delete an item on button press --- grocery-list-api/api/controllers/lists.js | 26 ++++++++++++++++-- grocery-list-api/api/swagger/swagger.yaml | 27 +++++++++++++++++++ grocery-list-fe/src/App.js | 9 ++++++- grocery-list-fe/src/apiCalls.js | 8 +++--- .../src/components/GroceryList/GroceryList.js | 19 +++++++++---- grocery-list-fe/src/components/Item/Item.js | 15 ++++++++--- 6 files changed, 88 insertions(+), 16 deletions(-) diff --git a/grocery-list-api/api/controllers/lists.js b/grocery-list-api/api/controllers/lists.js index 64a11a1..69c328f 100644 --- a/grocery-list-api/api/controllers/lists.js +++ b/grocery-list-api/api/controllers/lists.js @@ -23,7 +23,28 @@ const retrieveList = (req, res) => { const addItemToList = (req, res) => { req.groceryLists.findAndUpdate( { id: req.swagger.params.id.value }, - ({ id, items }) => ({ id, items: items.push(req.body) }) + ({ id, items }) => ({ + id, + items: items.push(Object.assign({ id: items.length + 1 }, req.body)) + }) + ); + res.status(204); + res.end(); +}; + +const deleteItemFromList = (req, res) => { + req.groceryLists.findAndUpdate( + { id: req.swagger.params.listId.value }, + ({ id, items }) => { + const index = items.findIndex( + item => item.id !== req.swagger.params.itemId.value + ); + items.splice(index, 1); + return { + id, + items + }; + } ); res.status(204); res.end(); @@ -35,5 +56,6 @@ module.exports = { retrieveLists, createList, retrieveList, - addItemToList + addItemToList, + deleteItemFromList }; diff --git a/grocery-list-api/api/swagger/swagger.yaml b/grocery-list-api/api/swagger/swagger.yaml index aa3e0ff..03a97d7 100644 --- a/grocery-list-api/api/swagger/swagger.yaml +++ b/grocery-list-api/api/swagger/swagger.yaml @@ -83,6 +83,33 @@ paths: description: Error schema: $ref: '#/definitions/ErrorResponse' + /lists/{listId}/{itemId}: + x-swagger-router-controller: lists + delete: + description: Deletes an item from a grocery list + operationId: deleteItemFromList + produces: + - text/plain; charset=utf-8 + consumes: + - application/json + parameters: + - name: listId + in: path + description: The Id of the grocery list to delete from + required: true + type: integer + - name: itemId + in: path + description: The Id of the grocery item to delete + required: true + type: integer + responses: + '204': + description: Success + default: + description: Error + schema: + $ref: '#/definitions/ErrorResponse' /swagger: x-swagger-pipe: swagger_raw definitions: diff --git a/grocery-list-fe/src/App.js b/grocery-list-fe/src/App.js index c5b1aaa..cda4b63 100755 --- a/grocery-list-fe/src/App.js +++ b/grocery-list-fe/src/App.js @@ -6,7 +6,8 @@ import { getGroceryLists, getGroceryList, addGroceryList, - addItem + addItem, + deleteItem } from './apiCalls'; import './App.css'; @@ -23,6 +24,7 @@ class App extends Component { this.updateGroceryLists = this.updateGroceryLists.bind(this); this.addGroceryListHandler = this.addGroceryListHandler.bind(this); this.addItemHandler = this.addItemHandler.bind(this); + this.deleteItemHandler = this.deleteItemHandler.bind(this); } async componentDidMount() { await this.updateGroceryLists(); @@ -42,6 +44,10 @@ class App extends Component { await this.updateGroceryList(listId); } + async deleteItemHandler(listId, itemId) { + await deleteItem(listId, itemId); + } + async updateGroceryLists() { const groceryLists = await getGroceryLists(); this.setState({ groceryLists }); @@ -70,6 +76,7 @@ class App extends Component { diff --git a/grocery-list-fe/src/apiCalls.js b/grocery-list-fe/src/apiCalls.js index b4b1446..c6f08e5 100644 --- a/grocery-list-fe/src/apiCalls.js +++ b/grocery-list-fe/src/apiCalls.js @@ -1,4 +1,4 @@ -import { get, post, put } from 'axios'; +import { get, post, put, delete as deleteMethod } from 'axios'; const apiVersion = 'v1'; const baseUrl = `http://localhost:10010/${apiVersion}`; @@ -21,8 +21,8 @@ const getGroceryList = async id => { return response.data || []; }; -const deleteItem = async () => { - return Promise.resolve('OK'); +const deleteItem = async (listId, itemId) => { + await deleteMethod(`${baseUrl}/lists/${listId}/${itemId}`); }; -export { addGroceryList, addItem, getGroceryLists, getGroceryList }; +export { addGroceryList, addItem, getGroceryLists, getGroceryList, deleteItem }; diff --git a/grocery-list-fe/src/components/GroceryList/GroceryList.js b/grocery-list-fe/src/components/GroceryList/GroceryList.js index aa22a00..30c7652 100644 --- a/grocery-list-fe/src/components/GroceryList/GroceryList.js +++ b/grocery-list-fe/src/components/GroceryList/GroceryList.js @@ -5,14 +5,21 @@ import AddItem from '../AddItem/AddItem'; import './GroceryList.css'; function GroceryList(props) { - const { list, addItemHandler } = props; + const { list, addItemHandler, deleteItemHandler } = props; return list && list.id ? (
      {list.items && - list.items.map(({ name, quantity }) => ( - + list.items.map(({ id, name, quantity }) => ( + ))}
    @@ -35,11 +42,13 @@ GroceryList.propTypes = { }) ) }), - addItemHandler: PropTypes.func + addItemHandler: PropTypes.func, + deleteItemHandler: PropTypes.func }; GroceryList.defaultProps = { - addItemHandler: () => null + addItemHandler: () => null, + deleteItemHandler: () => null }; export default GroceryList; diff --git a/grocery-list-fe/src/components/Item/Item.js b/grocery-list-fe/src/components/Item/Item.js index 4551b62..526af61 100644 --- a/grocery-list-fe/src/components/Item/Item.js +++ b/grocery-list-fe/src/components/Item/Item.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import './Item.css'; function Item(props) { - const { name, quantity, onDeleteHandler } = props; + const { listId, itemId, name, quantity, deleteItemHandler } = props; return (
  • @@ -13,7 +13,10 @@ function Item(props) {

    {quantity}

    -
  • @@ -22,13 +25,17 @@ function Item(props) { } Item.propTypes = { + listId: PropTypes.number.isRequired, + itemId: PropTypes.number.isRequired, name: PropTypes.string.isRequired, - quantity: PropTypes.number.isRequired + quantity: PropTypes.number.isRequired, + deleteItemHandler: PropTypes.func }; Item.defaultProps = { name: '', - quantity: '' + quantity: '', + deleteItemHandler: () => null }; export default Item;