Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a13416399 | ||
|
|
e63b31b5e5 | ||
|
|
a69c190641 | ||
|
|
a621bebeec | ||
|
|
de9421b64f | ||
|
|
7faabfbf42 |
Generated
+10328
-9938
File diff suppressed because it is too large
Load Diff
+12
-3
@@ -7,19 +7,28 @@
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-scripts": "1.0.14"
|
||||
"react-scripts": "^1.0.17"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.1.2",
|
||||
"chai-enzyme": "^1.0.0-beta.0",
|
||||
"chai-jest-diff": "^1.0.1",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"enzyme": "^3.2.0",
|
||||
"enzyme-adapter-react-16": "^1.1.0",
|
||||
"eslint-config-prettier": "^2.6.0",
|
||||
"eslint-config-react-app": "^2.0.1",
|
||||
"eslint-plugin-prettier": "^2.3.1",
|
||||
"prettier": "^1.7.4"
|
||||
"prettier": "^1.7.4",
|
||||
"react-test-renderer": "^16.2.0",
|
||||
"sinon": "^4.1.3",
|
||||
"sinon-chai": "^2.14.0"
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
|
||||
+17
-2
@@ -6,6 +6,7 @@ import './App.css'
|
||||
import Card from './Card'
|
||||
import GuessCount from './GuessCount'
|
||||
import HallOfFame, { FAKE_HOF } from './HallOfFame'
|
||||
import HighScoreInput from './HighScoreInput'
|
||||
|
||||
const SIDE = 6
|
||||
const SYMBOLS = '😀🎉💖🎩🐶🐱🦄🐬🌍🌛🌞💫🍎🍌🍓🍐🍟🍿'
|
||||
@@ -16,9 +17,15 @@ class App extends Component {
|
||||
cards: this.generateCards(),
|
||||
currentPair: [],
|
||||
guesses: 0,
|
||||
hallOfFame: null,
|
||||
matchedCardIndices: [],
|
||||
}
|
||||
|
||||
// Arrow fx for binding
|
||||
displayHallOfFame = hallOfFame => {
|
||||
this.setState({ hallOfFame })
|
||||
}
|
||||
|
||||
generateCards() {
|
||||
const result = []
|
||||
const size = SIDE * SIDE
|
||||
@@ -75,7 +82,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { cards, guesses, matchedCardIndices } = this.state
|
||||
const { cards, guesses, hallOfFame, matchedCardIndices } = this.state
|
||||
const won = matchedCardIndices.length === cards.length
|
||||
return (
|
||||
<div className="memory">
|
||||
@@ -89,7 +96,15 @@ class App extends Component {
|
||||
onClick={this.handleCardClick}
|
||||
/>
|
||||
))}
|
||||
{won && <HallOfFame entries={FAKE_HOF} />}
|
||||
{won &&
|
||||
(hallOfFame ? (
|
||||
<HallOfFame entries={hallOfFame} />
|
||||
) : (
|
||||
<HighScoreInput
|
||||
guesses={guesses}
|
||||
onStored={this.displayHallOfFame}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { expect } from 'chai'
|
||||
import React from 'react'
|
||||
import { shallow } from 'enzyme'
|
||||
|
||||
import App from './App'
|
||||
import GuessCount from './GuessCount'
|
||||
|
||||
describe('<App/>', () => {
|
||||
it('renders without crashing', () => {
|
||||
const wrapper = shallow(<App />)
|
||||
})
|
||||
|
||||
it('contains a zero-guess counter', () => {
|
||||
const wrapper = shallow(<App />)
|
||||
|
||||
expect(wrapper).to.contain(<GuessCount guesses={0} />)
|
||||
})
|
||||
|
||||
it('has 36 cards', () => {
|
||||
const wrapper = shallow(<App />)
|
||||
expect(wrapper.find('Card')).to.have.length(36)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { expect } from 'chai'
|
||||
import React from 'react'
|
||||
import { shallow } from 'enzyme'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import Card from './Card'
|
||||
|
||||
describe('<Card/>', () => {
|
||||
it('should trigger its `onClick` prop when clicked', () => {
|
||||
const onClick = sinon.spy()
|
||||
const wrapper = shallow(
|
||||
<Card card="😁" feedback="hidden" index={0} onClick={onClick} />
|
||||
)
|
||||
|
||||
wrapper.simulate('click')
|
||||
expect(onClick).to.have.been.calledWith(0)
|
||||
})
|
||||
})
|
||||
@@ -38,3 +38,28 @@ export const FAKE_HOF = [
|
||||
{ id: 1, guesses: 31, date: '06/10/2017', player: 'Louisa' },
|
||||
{ id: 0, guesses: 48, date: '14/10/2017', player: 'Marc' },
|
||||
]
|
||||
|
||||
const HOF_KEY = '::Memory::HallofFame'
|
||||
const HOF_MAX_SIZE = 10
|
||||
|
||||
export function saveHOFEntry(entry, onStored) {
|
||||
entry.date = new Date().toLocaleDateString()
|
||||
entry.id = Date.now()
|
||||
|
||||
const entries = JSON.parse(localStorage.getItem(HOF_KEY) || '[]')
|
||||
const insertionPoint = entries.findIndex(
|
||||
({ guesses }) => guesses >= entry.guesses
|
||||
)
|
||||
|
||||
if (insertionPoint === -1) {
|
||||
entries.push(entry)
|
||||
} else {
|
||||
entries.splice(insertionPoint, 0, entry)
|
||||
}
|
||||
if (entries.length > HOF_MAX_SIZE) {
|
||||
entries.splice(HOF_MAX_SIZE, entries.length)
|
||||
}
|
||||
|
||||
localStorage.setItem(HOF_KEY, JSON.stringify(entries))
|
||||
onStored(entries)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.highScoreInput {
|
||||
font-size: 1em;
|
||||
width: calc(100% - 0.4em);
|
||||
margin: 0.2em;
|
||||
}
|
||||
.highScoreInput input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 0.2em;
|
||||
font-family: Menlo, Monaco, Consolas, Inconsolata, 'Courier New', monospace;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { Component } from 'react'
|
||||
|
||||
import './HighScoreInput.css'
|
||||
|
||||
import { saveHOFEntry } from './HallOfFame'
|
||||
|
||||
class HighScoreInput extends Component {
|
||||
state = { winner: '' }
|
||||
|
||||
// Arrow fx for binding
|
||||
handleWinnerUpdate = event => {
|
||||
this.setState({ winner: event.target.value.toUpperCase() })
|
||||
}
|
||||
|
||||
// Arrow fx for binding
|
||||
persistWinner = event => {
|
||||
event.preventDefault()
|
||||
const newEntry = { guesses: this.props.guesses, player: this.state.winner }
|
||||
saveHOFEntry(newEntry, this.props.onStored)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<form className="highScoreInput" onSubmit={this.persistWinner}>
|
||||
<p>
|
||||
<label>
|
||||
Bravo ! Entre ton prénom :
|
||||
<input
|
||||
autoComplete="given-name"
|
||||
type="text"
|
||||
onChange={this.handleWinnerUpdate}
|
||||
value={this.state.winner}
|
||||
/>
|
||||
</label>
|
||||
<button type="submit">J’ai gagné !</button>
|
||||
</p>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HighScoreInput.propTypes = {
|
||||
guesses: PropTypes.number.isRequired,
|
||||
onStored: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default HighScoreInput
|
||||
@@ -0,0 +1,15 @@
|
||||
import Adapter from 'enzyme-adapter-react-16'
|
||||
import chai from 'chai'
|
||||
import { configure as configureEnzyme } from 'enzyme'
|
||||
import createChaiEnzyme from 'chai-enzyme'
|
||||
import createChaiJestDiff from 'chai-jest-diff'
|
||||
import dirtyChai from 'dirty-chai'
|
||||
import sinonChai from 'sinon-chai'
|
||||
|
||||
chai
|
||||
.use(dirtyChai)
|
||||
.use(createChaiJestDiff())
|
||||
.use(createChaiEnzyme())
|
||||
.use(sinonChai)
|
||||
|
||||
configureEnzyme({ adapter: new Adapter() })
|
||||
Reference in New Issue
Block a user