diff --git a/package-lock.json b/package-lock.json index 0d789ea1..84e94612 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", @@ -4570,7 +4579,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -5139,7 +5148,7 @@ }, "humanize-url": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz", "integrity": "sha1-9KuZ4NKIF0yk4eUEB8VfuuRk7/8=", "dev": true, "requires": { @@ -7399,7 +7408,7 @@ "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", "dev": true, "requires": { - "@types/node": "10.3.0" + "@types/node": "10.3.3" } }, "parseurl": { @@ -9005,9 +9014,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 +9217,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..8288efba 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,13 @@ "devDependencies": { "enzyme": "^3.3.0", "enzyme-adapter-react-16": "^1.1.1", + "enzyme-to-json": "^3.3.4", "gh-pages": "^1.2.0" }, - "homepage": "http://adagold.github.io/inspiration-board" + "homepage": "https://lindseyls.github.io/inspiration-board/", + "jest": { + "snapshotSerializers": [ + "enzyme-to-json/serializer" + ] + } } diff --git a/src/App.js b/src/App.js index c4854e15..77cec019 100644 --- a/src/App.js +++ b/src/App.js @@ -1,18 +1,49 @@ import React, { Component } from 'react'; import './App.css'; + import Board from './components/Board'; +import Status from './components/Status'; class App extends Component { + constructor() { + super(); + + this.state = { + status: { + messasge: 'loaded the page', + type: 'success' + } + } + } + + updateStatus = (message, type) => { + this.setState({ + status: { + message: message, + type: type + } + }) + } + render() { + return (

Inspiration Board

- + + + + +
); } diff --git a/src/components/Board.css b/src/components/Board.css index ba21589d..12a5f861 100644 --- a/src/components/Board.css +++ b/src/components/Board.css @@ -6,6 +6,8 @@ .validation-errors-display { text-align: center; margin-bottom: 2em; + font-family: 'Raleway', sans-serif; + font-size: 1.25em; } .validation-errors-display__list { diff --git a/src/components/Board.js b/src/components/Board.js index 9222fd88..9c48ceaf 100644 --- a/src/components/Board.js +++ b/src/components/Board.js @@ -5,7 +5,9 @@ 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'; + +const CARDS_URL = "https://inspiration-board.herokuapp.com/boards/Luxi/cards"; class Board extends Component { constructor() { @@ -16,18 +18,100 @@ class Board extends Component { }; } + componentDidMount() { + console.log('In componentsDidMount'); + + this.props.updateStatusCallback('Loading...', 'success'); + + axios.get(CARDS_URL) + .then((response) => { + console.log('Success!'); + + this.props.updateStatusCallback('Successfully loaded all cards!', 'success'); + + const cards = response.data; + this.setState({ cards: cards }); + }) + .catch((error) => { + // console.log('Error :('); + // console.log(error); + + this.props.updateStatusCallback(error.message, 'error'); + }); + } + + addCard = (cardInfo) => { + console.log('In addCard'); + + axios.post(CARDS_URL, cardInfo) + .then((response) => { + + this.props.updateStatusCallback(`Successfully added card ${ response.data.card.id }!`, 'success'); + + let updateCards = this.state.cards; + updateCards.push(response.data); + + this.setState({ cards: updateCards }); + }) + .catch((error) => { + console.log(error); + + this.props.updateStatusCallback(`${error.message} - failed to add new card`, 'error'); + }); + } + + deleteCard = (index, id) => { + console.log('In deleteCard') + + let updateCards = this.state.cards; + updateCards.splice(index, 1) + this.setState({ cards: updateCards }); + + const DELETE_URL = `https://inspiration-board.herokuapp.com/cards/${id}` + + axios.delete(DELETE_URL) + .then((response) => { + console.log('Deleted!'); + console.log(response.data); + + this.props.updateStatusCallback('Successfully deleted card!', 'success'); + + }) + .catch((error) => { + // console.log('Error :('); + // console.log(error); + + this.props.updateStatusCallback(error.message, 'error'); + }); + } + render() { + + const cards = this.state.cards.map((card, index) => { + return + }); + return ( -
- Board +
+ + +
+ { cards }
- ) +
+ ); } } Board.propTypes = { - + // cards: PropTypes.array.isRequired, + updateStatusCallback: PropTypes.func.isRequired }; export default Board; diff --git a/src/components/Board.test.js b/src/components/Board.test.js index e69de29b..f6650a3c 100644 --- a/src/components/Board.test.js +++ b/src/components/Board.test.js @@ -0,0 +1,24 @@ +import React from 'react'; +import Board from './Board'; +import { mount, shallow } from 'enzyme'; + +describe ('Board', () => { + test('deep mount', () => { + const boardComponent = mount( + {} } /> + ); + + expect(boardComponent).toMatchSnapshot(); + + boardComponent.unmount(); + }); + + test('shallow mount', () => { + const boardComponent = shallow( + {} } /> + ); + + expect(boardComponent).toMatchSnapshot(); + + }); +}); diff --git a/src/components/Card.css b/src/components/Card.css index e86d4329..5d1e2c3a 100644 --- a/src/components/Card.css +++ b/src/components/Card.css @@ -44,4 +44,12 @@ .card__delete { align-self: start; font-family: 'Permanent Marker', Helvetica, sans-serif; + background-color: inherit; + border: none; + font-size: 1em; +} + +.card__delete:hover { + font-size: 2rem; + color: orange; } diff --git a/src/components/Card.js b/src/components/Card.js index 6788cc03..4f2df8ec 100644 --- a/src/components/Card.js +++ b/src/components/Card.js @@ -1,21 +1,40 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import emoji from 'emoji-dictionary'; import './Card.css'; -class Card extends Component { - render() { - return ( -
- Card -
- ) +const Card = (props) => { + + let onClickHandler = () => { + props.deleteCallback(props.index, props.id); } + + return ( +
+ + +
+

+ {props.text} +

+ +

+ {emoji.getUnicode(`${props.emoji}`)} +

+
+
+ ) } Card.propTypes = { - + text: PropTypes.string, + emoji: PropTypes.string, + index: PropTypes.number, + id: PropTypes.number, + deleteCallback: PropTypes.func }; export default Card; diff --git a/src/components/Card.test.js b/src/components/Card.test.js new file mode 100644 index 00000000..5496baaa --- /dev/null +++ b/src/components/Card.test.js @@ -0,0 +1,13 @@ +import React from 'react'; +import Card from './Card'; +import { shallow } from 'enzyme'; + +describe ('Card', () => { + test('shallow mount', () => { + const cardComponent = shallow( + {} } /> + ); + + expect(cardComponent).toMatchSnapshot(); + }); +}); diff --git a/src/components/NewCardForm.js b/src/components/NewCardForm.js index 47331423..eb023792 100644 --- a/src/components/NewCardForm.js +++ b/src/components/NewCardForm.js @@ -3,4 +3,88 @@ 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", "grin", "joy", "star_struck", "nerd_face", "kissing_heart", +"grinning", "thinking", "hugs", "grimacing", "beer", "clap", "sparkling_heart", "heart_eyes_cat", +"joy_cat", "smile_cat", "kissing_cat", "star", "clinking_glasses", "dog", "octopus", "woman_technologist"] + +class NewCardForm extends Component { + static propTypes = { + addCardCallback: PropTypes.func.isRequired, + } + + constructor() { + super(); + + this.state = { + text: '', + emoji: '' + }; + } + + onInputChange = (event) => { + console.log('form submitted'); + + let updatedInput = {}; + + updatedInput[event.target.name] = event.target.value; + + this.setState(updatedInput); + } + + onFormSubmit = (event) => { + event.preventDefault(); + + console.log(this.state); + this.props.addCardCallback(this.state); + + this.setState({ + text: '', + emoji: '' + }); + } + + + render() { + + const emojiList = EMOJI_LIST.map((name, index) => { + return + }); + + return ( +
+

Create new card

+
+ +
+ + +
+ +
+ + +
+ +
+
+
+ ) + } + +} + +export default NewCardForm; diff --git a/src/components/NewCardForm.test.js b/src/components/NewCardForm.test.js index e69de29b..c528dfe5 100644 --- a/src/components/NewCardForm.test.js +++ b/src/components/NewCardForm.test.js @@ -0,0 +1,41 @@ +import React from 'react'; +import NewCardForm from './NewCardForm'; +import { mount, shallow } from 'enzyme'; + +describe('NewCardForm', () => { + test('that it matches an existing snapshot', () => { + // First Mount the Component in the testing DOM + // Arrange + const wrapper = mount( + {} } /> + ); + + // Assert that it looks like the last snapshot + expect(wrapper).toMatchSnapshot(); + + // Remove the component from the DOM (save memory and prevent side effects). + wrapper.unmount(); + }); + + test('keeps track of user input', () => { + const value = "new text value"; + const cardForm = shallow( + {} } /> + ); + + let textInput = cardForm.find('input[name="text"]'); + + textInput.simulate('change', { + target: { + name: 'text', + value: value + } + }); + + cardForm.update(); + + textInput = cardForm.find('input[name="text"]'); + + expect(textInput.getElement().props.value).toBe(value); + }); +}); diff --git a/src/components/Status.js b/src/components/Status.js new file mode 100644 index 00000000..c8859e4b --- /dev/null +++ b/src/components/Status.js @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class Status extends React.Component { + static propTypes = { + message: PropTypes.string, + type: PropTypes.string + } + + + render() { + + + return ( +
+ {this.props.type === 'error' ? "There was a problem: " : ""} + {this.props.message} +
+ ); + } +} + + +export default Status; diff --git a/src/components/Status.test.js b/src/components/Status.test.js new file mode 100644 index 00000000..7510adfb --- /dev/null +++ b/src/components/Status.test.js @@ -0,0 +1,31 @@ +import React from 'react'; +import Status from './Status'; +import { shallow } from 'enzyme'; + +describe ('Status', () => { + test('matches snapshot with error type', () => { + const statusComponent = shallow( + + ); + + expect(statusComponent).toMatchSnapshot(); + + statusComponent.unmount(); + }); + + test('matches snapshot with non-error type', () => { + const statusComponent = shallow( + + ); + + expect(statusComponent).toMatchSnapshot(); + + statusComponent.unmount(); + }); +}); diff --git a/src/components/__snapshots__/Board.test.js.snap b/src/components/__snapshots__/Board.test.js.snap new file mode 100644 index 00000000..a45953b0 --- /dev/null +++ b/src/components/__snapshots__/Board.test.js.snap @@ -0,0 +1,198 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Board deep mount 1`] = ` + +
+ +
+

+ Create new card +

+
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+
+`; + +exports[`Board shallow mount 1`] = ` +
+ +
+
+`; diff --git a/src/components/__snapshots__/Card.test.js.snap b/src/components/__snapshots__/Card.test.js.snap new file mode 100644 index 00000000..c7ca8cd3 --- /dev/null +++ b/src/components/__snapshots__/Card.test.js.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Card shallow mount 1`] = ` +
+ +
+

+

+

+
+`; diff --git a/src/components/__snapshots__/NewCardForm.test.js.snap b/src/components/__snapshots__/NewCardForm.test.js.snap new file mode 100644 index 00000000..3829d95a --- /dev/null +++ b/src/components/__snapshots__/NewCardForm.test.js.snap @@ -0,0 +1,178 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NewCardForm that it matches an existing snapshot 1`] = ` + +
+

+ Create new card +

+
+
+ + +
+
+ + +
+
+ +
+
+
+
+`; diff --git a/src/components/__snapshots__/Status.test.js.snap b/src/components/__snapshots__/Status.test.js.snap new file mode 100644 index 00000000..18e57767 --- /dev/null +++ b/src/components/__snapshots__/Status.test.js.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Status matches snapshot with error type 1`] = ` +
+ There was a problem: + test status +
+`; + +exports[`Status matches snapshot with non-error type 1`] = ` +
+ test status +
+`; 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" diff --git a/src/setupTests.js b/src/setupTests.js index fc7b0dce..82edfc9e 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1,4 +1,4 @@ -import Enzyme from 'enzyme'; +import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; -Enzyme.configure({ adapter: new Adapter() }); +configure({ adapter: new Adapter() });