diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..19f374ad --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +#node_modules diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0e878c39 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# base image +FROM node:9.6.1 + +# set working directory +RUN mkdir /usr/inspiration-board +WORKDIR /usr/inspiration-board + +# add `/usr/src/app/node_modules/.bin` to $PATH +ENV PATH /usr/inspiration-board/node_modules/.bin:$PATH + +# install and cache app dependencies +COPY . /usr/inspiration-board +#COPY package.json /usr/src/app/package.json + + +RUN npm install --silent +RUN npm install react-scripts@1.1.1 -g --silent + +# start app +CMD ["npm", "start"] diff --git a/package-lock.json b/package-lock.json index 0d789ea1..1674a795 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@types/node": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.0.tgz", - "integrity": "sha512-hWzNviaVFIr1TqcRA8ou49JaSHp+Rfabmnqg2kNvusKqLhPU0rIsGPUj5WJJ7ld4Bb7qdgLmIhLfCD1qS08IVA==", + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.3.tgz", + "integrity": "sha512-/gwCgiI2e9RzzZTKbl+am3vgNqOt7a9fJ/uxv4SqYKxenoEDNVU3KZEadlpusWhQI0A0dOrZ0T68JYKVjzmgdQ==", "dev": true }, "abab": { @@ -2961,7 +2961,7 @@ "object.values": "1.0.4", "prop-types": "15.6.1", "react-reconciler": "0.7.0", - "react-test-renderer": "16.4.0" + "react-test-renderer": "16.4.1" } }, "enzyme-adapter-utils": { @@ -2975,6 +2975,15 @@ "prop-types": "15.6.1" } }, + "enzyme-to-json": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.4.tgz", + "integrity": "sha1-Z8YEDpMRgvGDQYry659DIyWKp38=", + "dev": true, + "requires": { + "lodash": "4.17.10" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -6085,6 +6094,21 @@ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-20.0.3.tgz", "integrity": "sha1-i8Bw6QQUqhVcEajWTIaaDVxx2lk=" }, + "jest-mock-axios": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/jest-mock-axios/-/jest-mock-axios-2.1.11.tgz", + "integrity": "sha512-rxF13chmHuelAh/rysNQEY4YqmOO94XmBO5Mk2+edFh2dOwe3j/XIRSnY8+m61t1658AU6nGoTh1s7tCgvridg==", + "dev": true, + "requires": { + "jest-mock-promise": "1.0.23" + } + }, + "jest-mock-promise": { + "version": "1.0.23", + "resolved": "https://registry.npmjs.org/jest-mock-promise/-/jest-mock-promise-1.0.23.tgz", + "integrity": "sha1-ySH9a1EqxUYJftvPR389QWllfkA=", + "dev": true + }, "jest-regex-util": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-20.0.3.tgz", @@ -7399,7 +7423,7 @@ "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", "dev": true, "requires": { - "@types/node": "10.3.0" + "@types/node": "10.3.3" } }, "parseurl": { @@ -9005,9 +9029,9 @@ "integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==" }, "react-is": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.0.tgz", - "integrity": "sha512-8ADZg/mBw+t2Fbr5Hm1K64v8q8Q6E+DprV5wQ5A8PSLW6XP0XJFMdUskVEW8efQ5oUgWHn8EYdHEPAMF0Co6hA==", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.1.tgz", + "integrity": "sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==", "dev": true }, "react-reconciler": { @@ -9208,15 +9232,15 @@ } }, "react-test-renderer": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.4.0.tgz", - "integrity": "sha512-Seh1t9xFY6TKiV/hRlPzUkqX1xHOiKIMsctfU0cggo1ajsLjoIJFL520LlrxV+4/VIj+clrCeH6s/aVv/vTStg==", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.4.1.tgz", + "integrity": "sha512-wyyiPxRZOTpKnNIgUBOB6xPLTpIzwcQMIURhZvzUqZzezvHjaGNsDPBhMac5fIY3Jf5NuKxoGvV64zDSOECPPQ==", "dev": true, "requires": { "fbjs": "0.8.16", "object-assign": "4.1.1", "prop-types": "15.6.1", - "react-is": "16.4.0" + "react-is": "16.4.1" } }, "read-pkg": { diff --git a/package.json b/package.json index ba61363d..c6ac3678 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,14 @@ "devDependencies": { "enzyme": "^3.3.0", "enzyme-adapter-react-16": "^1.1.1", - "gh-pages": "^1.2.0" + "enzyme-to-json": "^3.3.4", + "gh-pages": "^1.2.0", + "jest-mock-axios": "^2.1.11" + }, + "jest": { + "snapshotSerializers": [ + "enzyme-to-json/serializer" + ] }, "homepage": "http://adagold.github.io/inspiration-board" } diff --git a/src/App.css b/src/App.css index 5db345f2..d9cdf0e1 100644 --- a/src/App.css +++ b/src/App.css @@ -5,6 +5,7 @@ body { font-family: 'Raleway', sans-serif; + /*background-color: #92FED5;*/ } h1, h2 { diff --git a/src/App.js b/src/App.js index c4854e15..63390472 100644 --- a/src/App.js +++ b/src/App.js @@ -10,8 +10,8 @@ class App extends Component {

Inspiration Board

); diff --git a/src/components/Board.js b/src/components/Board.js index 9222fd88..3ee3f545 100644 --- a/src/components/Board.js +++ b/src/components/Board.js @@ -5,21 +5,99 @@ import axios from 'axios'; import './Board.css'; import Card from './Card'; import NewCardForm from './NewCardForm'; -import CARD_DATA from '../data/card-data.json'; +// import CARD_DATA from '../data/card-data.json'; class Board extends Component { - constructor() { - super(); + constructor(props) { + super(props); this.state = { cards: [], }; } + componentDidMount = () => { + console.log('Component did mount was called') + + axios.get('https://inspiration-board.herokuapp.com/boards/angelap/cards') + .then((response) => { + this.setState({ cards: response.data }); + console.log(response.data) + }) + .catch((error) => { + console.log('Error is happening'); + console.log(error); + this.setState({ error: error.message}); + return error; + + }); + } + + renderCards = () => { + console.log('Rendering cards') + + const cardList = this.state.cards.map((card, index) => { + let singleCard = card["card"] + return ( + + ); + }); + return cardList; + } + + addCard = (note) => { + const cards = this.state.cards; + axios.post(`https://inspiration-board.herokuapp.com/boards/angelap/cards/`, note) + .then((response) => { + note.id = response.data.card.id; + cards.push({card: note}); + console.log(note); + this.setState({ + cards, + message: `Successfully Added a new Card` + }); + }) + .catch((error) => { + console.log(error) + this.setState({ + message: error.message, + }); + }) + } + + deleteCard = (id) => { + console.log(id) + axios.delete(`https://inspiration-board.herokuapp.com/boards/angelap/cards/${id}`) + .then((response) => { + this.componentDidMount(); + console.log(response) + }) + .catch((error) => { + console.log('Unable to delete card'); + console.log(error); + this.setState({ error: error.message}); + return error; + + }); + } + + + render() { return (
- Board + +

{this.state.error}

+

{this.state.message}

+
+ {this.renderCards()} +
) } @@ -27,7 +105,7 @@ class Board extends Component { } Board.propTypes = { - + cards: PropTypes.array.isRequired, }; export default Board; diff --git a/src/components/Board.test.js b/src/components/Board.test.js index e69de29b..1862df05 100644 --- a/src/components/Board.test.js +++ b/src/components/Board.test.js @@ -0,0 +1,14 @@ + +import React, { Component } from 'react'; +import {mount, shallow} from 'enzyme'; +import Board from './Board' + +describe('Board', () => { + test('that it matches an existing snapshot', () => { + const wrapper = mount() + + expect(wrapper).toMatchSnapshot(); + + wrapper.unmount(); + }); +}); diff --git a/src/components/Card.css b/src/components/Card.css index e86d4329..c5283621 100644 --- a/src/components/Card.css +++ b/src/components/Card.css @@ -1,5 +1,5 @@ .card { - background-color: #F4FF81; + background-color: #D6CADA; padding: 1em 0; margin: 0.5rem; @@ -44,4 +44,5 @@ .card__delete { align-self: start; font-family: 'Permanent Marker', Helvetica, sans-serif; + margin-left: 8px; } diff --git a/src/components/Card.js b/src/components/Card.js index 6788cc03..8bf715ed 100644 --- a/src/components/Card.js +++ b/src/components/Card.js @@ -5,17 +5,39 @@ import emoji from 'emoji-dictionary'; import './Card.css'; class Card extends Component { + + displayEmoji = () => { + if (this.props.emoji) { + return emoji.getUnicode(this.props.emoji) + } + }; + + removeCard = (event) => { + console.log(event.target.id); + event.preventDefault(); + this.props.deleteCard(this.props.id); + } + render() { + console.log('Rendering a card') return (
- Card -
- ) - } -} + +
-Card.propTypes = { +

{this.props.text}

+

{this.displayEmoji()}

+
+ + ) + } + } -}; + Card.propTypes = { + text: PropTypes.string.isRequired, + emoji: PropTypes.string, + deleteCard: PropTypes.func, + id: PropTypes.number.isRequired, + }; -export default Card; + export default Card; diff --git a/src/components/Card.test.js b/src/components/Card.test.js new file mode 100644 index 00000000..e419b17e --- /dev/null +++ b/src/components/Card.test.js @@ -0,0 +1,14 @@ + +import React, { Component } from 'react'; +import {mount, shallow} from 'enzyme'; +import Card from './Card' + +describe('Card', () => { + test('that it matches an existing snapshot', () => { + const wrapper = shallow() + + expect(wrapper).toMatchSnapshot(); + + wrapper.unmount(); + }); +}); diff --git a/src/components/NewCardForm.js b/src/components/NewCardForm.js index 47331423..c4b61a61 100644 --- a/src/components/NewCardForm.js +++ b/src/components/NewCardForm.js @@ -3,4 +3,80 @@ import PropTypes from 'prop-types'; import emoji from 'emoji-dictionary'; import './NewCardForm.css'; -const EMOJI_LIST = ["", "heart_eyes", "beer", "clap", "sparkling_heart", "heart_eyes_cat", "dog"] +const EMOJI_LIST = ["", "heart_eyes", "beer", "clap", "sparkling_heart", "heart_eyes_cat", "dog", "joy", "sparkles", "clapping"] + +class NewCardForm extends Component { + constructor() { + super(); + + this.state = { + text:'', + emoji:'', + }; + } + + onFieldChange = (event) => { + const fieldName = event.target.name; + const fieldValue = event.target.value; + const updateState = {}; + updateState[fieldName] = fieldValue; + this.setState(updateState); + } + // + // valid = () => { + // return this.state.text.length > 0; + // } + + clearForm = () => { + this.setState({ + text: '', + emoji: '', + }) + } + + onFormSubmit = (event) => { + event.preventDefault(); + this.props.addCardCallBack(this.state) + this.clearForm() + } + + render() { + return( +
+

Create new card:

+
+
+ + +
+
+ + +
+
+ +
+
+
+ ); + } + +} + +NewCardForm.propTypes = { + addCardCallBack: PropTypes.func.isRequired, +} + +export default NewCardForm; diff --git a/src/components/NewCardForm.test.js b/src/components/NewCardForm.test.js index e69de29b..96ff187e 100644 --- a/src/components/NewCardForm.test.js +++ b/src/components/NewCardForm.test.js @@ -0,0 +1,44 @@ + +import React, { Component } from 'react'; +import {mount, shallow} from 'enzyme'; +import NewCardForm from './NewCardForm' + +describe('NewCardForm', () => { + test('that it matches an existing snapshot', () => { + const wrapper = shallow() + + expect(wrapper).toMatchSnapshot(); + + wrapper.unmount(); + }); + + test('When a user enters text in the text field the field is updated', () => { + // arrange + // shallow mounted the wrapper + + const wrapper = shallow({}} />) + + // find the input field + + let nameField = wrapper.find('input[name="text"]'); + + // Act + nameField.simulate('change', { + target: { + name: 'text', + value: 'Boomer loves you', + }, + }); + + //force the onChange event + wrapper.update(); + nameField = wrapper.find('input[name="text"]'); + + //assert + expect(nameField.getElement().props.value).toEqual('Boomer loves you'); + }); + + + +}); diff --git a/src/components/__snapshots__/Board.test.js.snap b/src/components/__snapshots__/Board.test.js.snap new file mode 100644 index 00000000..2fcbf518 --- /dev/null +++ b/src/components/__snapshots__/Board.test.js.snap @@ -0,0 +1,122 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Board that it matches an existing snapshot 1`] = ` + +
+ +
+

+ Create new card: +

+
+
+ + +
+
+ + +
+ +
+
+
+

+

+

+
+
+`; diff --git a/src/components/__snapshots__/Card.test.js.snap b/src/components/__snapshots__/Card.test.js.snap new file mode 100644 index 00000000..7e8d13e2 --- /dev/null +++ b/src/components/__snapshots__/Card.test.js.snap @@ -0,0 +1,203 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Card that it matches an existing snapshot 1`] = ` +ShallowWrapper { + "length": 1, + Symbol(enzyme.__root__): [Circular], + Symbol(enzyme.__unrendered__): , + Symbol(enzyme.__renderer__): Object { + "batchedUpdates": [Function], + "getNode": [Function], + "render": [Function], + "simulateEvent": [Function], + "unmount": [Function], + }, + Symbol(enzyme.__node__): Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children":
+

+

+ +

, + "className": "card", + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": Array [ +

, +

, + , + ], + "className": "card__content", + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": undefined, + "className": "card__content-text", + }, + "ref": null, + "rendered": null, + "type": "p", + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": undefined, + "className": "card__content-emoji", + }, + "ref": null, + "rendered": null, + "type": "p", + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": "Delete", + "className": "card__delete", + "onClick": [Function], + }, + "ref": null, + "rendered": "Delete", + "type": "button", + }, + ], + "type": "div", + }, + "type": "div", + }, + Symbol(enzyme.__nodes__): Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children":

+

+

+ +

, + "className": "card", + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": Array [ +

, +

, + , + ], + "className": "card__content", + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": undefined, + "className": "card__content-text", + }, + "ref": null, + "rendered": null, + "type": "p", + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": undefined, + "className": "card__content-emoji", + }, + "ref": null, + "rendered": null, + "type": "p", + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "host", + "props": Object { + "children": "Delete", + "className": "card__delete", + "onClick": [Function], + }, + "ref": null, + "rendered": "Delete", + "type": "button", + }, + ], + "type": "div", + }, + "type": "div", + }, + ], + Symbol(enzyme.__options__): Object { + "adapter": ReactSixteenAdapter { + "options": Object { + "enableComponentDidUpdateOnSetState": true, + }, + }, + }, +} +`; diff --git a/src/components/__snapshots__/NewCardForm.test.js.snap b/src/components/__snapshots__/NewCardForm.test.js.snap new file mode 100644 index 00000000..731f1961 --- /dev/null +++ b/src/components/__snapshots__/NewCardForm.test.js.snap @@ -0,0 +1,109 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NewCardForm that it matches an existing snapshot 1`] = ` +

+

+ Create new card: +

+
+
+ + +
+
+ + +
+ +
+
+`; diff --git a/src/data/card-data.json b/src/data/card-data.json index 1f9793ec..068e019d 100644 --- a/src/data/card-data.json +++ b/src/data/card-data.json @@ -6,7 +6,7 @@ }, { "text": "", - "Emoji": "heart_eyes" + "emoji": "heart_eyes" }, { "text": "REST is part of work"