-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Anibel - Edges Inspiration Board #19
base: master
Are you sure you want to change the base?
Changes from all commits
4db0ad6
6e01508
cf04a04
0a69610
a10bc50
875e0cd
dcaf2f7
0484217
fb89492
ff41b5c
2874a1d
924e430
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,29 +5,95 @@ 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: [], | ||
url: this.props.url, | ||
boardName: this.props.boardName, | ||
cards: [] | ||
}; | ||
} | ||
|
||
componentDidMount() { | ||
const GET_INSPO_CARDS_URL = this.props.url + '/' + this.props.boardName + '/cards'; | ||
|
||
axios.get(GET_INSPO_CARDS_URL) | ||
.then((response) => { | ||
this.setState({ | ||
cards: response.data, | ||
}); | ||
}) | ||
.catch((error) => { | ||
this.setState({ | ||
error: error.message | ||
}); | ||
}); | ||
} | ||
|
||
deleteCard = (cardId) => { | ||
const deleteURL = "https://inspiration-board.herokuapp.com/cards/" + cardId; | ||
|
||
axios.delete(deleteURL) | ||
.then((response) => { | ||
const newCards = [...this.state.cards]; | ||
const index = newCards.findIndex(content => content.card.id === cardId); | ||
newCards.splice(index, 1); | ||
|
||
this.setState({ | ||
cards: newCards | ||
}); | ||
}) | ||
.catch((error) => { | ||
this.setState({ | ||
error: error.message | ||
}); | ||
}); | ||
}; | ||
|
||
addCard = (cardData) => { | ||
const POST_INSPO_CARDS_URL = this.props.url + '/' + this.props.boardName + '/cards'; | ||
axios.post(POST_INSPO_CARDS_URL, cardData) | ||
.then((response) => { | ||
cardData.id = response.data.card.id; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that you've kept all the API interaction logic in one component - the callbacks are a little more complex, but I would say it makes the app as a whole much easier to comprehend. Whether or not you intended it, this is a great example of the container component pattern well-applied. |
||
|
||
const updatedCardDeck = [...this.state.cards, {card: cardData}]; | ||
|
||
this.setState({ | ||
cards: updatedCardDeck | ||
}) | ||
|
||
}) | ||
.catch((error) => { | ||
this.setState({ | ||
error: error.message | ||
}); | ||
}); | ||
}; | ||
|
||
render() { | ||
const cardList = this.state.cards.map((cardContainer, i) => { | ||
return <Card key={i} | ||
card={cardContainer.card} | ||
deleteCardCallback={this.deleteCard} /> | ||
}); | ||
|
||
return ( | ||
<div> | ||
Board | ||
<div className="board"> | ||
<NewCardForm | ||
addCardCallback={this.addCard}/> | ||
{ cardList } | ||
</div> | ||
) | ||
} | ||
|
||
} | ||
|
||
Board.propTypes = { | ||
|
||
url: PropTypes.string.isRequired, | ||
boardName: PropTypes.string.isRequired | ||
}; | ||
|
||
export default Board; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,90 @@ import emoji from 'emoji-dictionary'; | |
import './NewCardForm.css'; | ||
|
||
const EMOJI_LIST = ["", "heart_eyes", "beer", "clap", "sparkling_heart", "heart_eyes_cat", "dog"] | ||
|
||
class NewCardForm extends Component{ | ||
|
||
constructor() { | ||
super(); | ||
|
||
this.state = { | ||
text: '', | ||
emoji: '' | ||
} | ||
} | ||
|
||
onInputChange = (event) => { | ||
const newState = {}; | ||
|
||
newState[event.target.name] = event.target.value; | ||
|
||
this.setState(newState); | ||
} | ||
|
||
resetState = () => { | ||
this.setState({ | ||
text: '', | ||
emoji: '' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good use of a helper method here. Could you call this from your constructor instead of repeating this work? |
||
}); | ||
} | ||
|
||
onFormSubmit = (event) => { | ||
event.preventDefault(); | ||
// const { text, emoji} = this.state; | ||
this.props.addCardCallback(this.state); | ||
this.resetState(); | ||
} | ||
|
||
render() { | ||
const emojiLib = require("emoji-dictionary"); | ||
const emojiOptions = EMOJI_LIST.map((emoji, i) => { | ||
return ( | ||
<option | ||
key={i} | ||
value={emoji}> | ||
{emojiLib.getUnicode(emoji)} | ||
</option>) | ||
}); | ||
|
||
return ( | ||
<section className="new-card-form"> | ||
<section className="new-card-form__header"> | ||
<h1>Send an Inspiration!</h1> | ||
</section> | ||
<form | ||
className="new-card-form__form" | ||
onSubmit={this.onFormSubmit}> | ||
|
||
|
||
<label htmlFor="Text" className="new-card-form__form-label"> | ||
</label> | ||
<textarea | ||
name="text" | ||
type="textarea" | ||
value={this.state.text} | ||
onChange={this.onInputChange} | ||
className="new-card-form__form-textarea"/> | ||
|
||
<select | ||
name="emoji" | ||
className="new-card-form__form-select" | ||
onChange={this.onInputChange} | ||
value={this.state.emoji}> | ||
{emojiOptions} | ||
</select> | ||
<input type="submit" value="Submit" className="new-card-form__form-button" /> | ||
|
||
|
||
</form> | ||
|
||
</section> | ||
); | ||
} | ||
} | ||
|
||
|
||
NewCardForm.propTypes = { | ||
addCardCallback: PropTypes.func, | ||
}; | ||
|
||
export default NewCardForm; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React from 'react'; | ||
import Board from '../Board'; | ||
import { shallow } from 'enzyme'; | ||
|
||
describe('Board', () => { | ||
test('that it matches an existing snapshot', () => { | ||
const wrapper = shallow( | ||
<Board | ||
url='test url' | ||
boardName='test board name'/>); | ||
|
||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
|
||
test('that it renders without crashing even with no url and boardName', () => { | ||
const wrapper = shallow( | ||
<Board | ||
url='' | ||
boardName=''/>); | ||
|
||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from 'react'; | ||
import Card from '../Card'; | ||
import { shallow } from 'enzyme'; | ||
|
||
describe('Card', () => { | ||
test('that it renders even when passed an empty object prop', () => { | ||
const wrapper = shallow( | ||
<Card | ||
card={{}}/>); | ||
|
||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
|
||
test('that it renders when passed an object prop with text and emoji fields', () => { | ||
const wrapper = shallow( | ||
<Card | ||
card={ | ||
{ | ||
card: { | ||
text: "test inspirational text", | ||
emoji: "dog" | ||
} | ||
} | ||
} | ||
/>); | ||
|
||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import NewCardForm from '../NewCardForm'; | ||
import { shallow } from 'enzyme'; | ||
|
||
describe('NewCardForm', () => { | ||
test('that it renders when passed an empty function', () => { | ||
const wrapper = shallow( | ||
<NewCardForm | ||
addCardCallback={() => {}}/>); | ||
|
||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
|
||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the URL and board name aren't going to be changed within this component, you don't need to keep them in
state
. If you read them fromprops
throughout the app, it will be more clear that they're read-only.