From 4eed770cf10142afa58e464d88897b2c20ebe98c Mon Sep 17 00:00:00 2001 From: mfrydrychowicz Date: Wed, 6 Jan 2021 18:12:38 +0100 Subject: [PATCH] [#33] ranking for modes --- src/app/components/ranking.js | 70 +++++++++++++++++++++++++++++ src/app/logic/localStorageScore.js | 22 +++++---- static/assets/ui/contacts_24px.svg | 3 ++ styles/App.scss | 5 ++- styles/components/ranking.scss | 44 ++++++++++++++++++ test/app/components/ranking.test.js | 39 ++++++++++++++++ test/app/logic/localStorage.test.js | 2 +- 7 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 src/app/components/ranking.js create mode 100644 static/assets/ui/contacts_24px.svg create mode 100644 styles/components/ranking.scss create mode 100644 test/app/components/ranking.test.js diff --git a/src/app/components/ranking.js b/src/app/components/ranking.js new file mode 100644 index 0000000..e4bee5a --- /dev/null +++ b/src/app/components/ranking.js @@ -0,0 +1,70 @@ +function createEntry(place, scoreEntry) { + let nickname = '-', + correctNumber = '-', + questionsNumber = '-'; + + if (scoreEntry !== undefined) { + ({ nickname, correctNumber, questionsNumber } = scoreEntry); + } + + let div = document.createElement('div'); + div.className = 'ranking-row'; + div.innerHTML = ` +
+ ${place} +
+
+ ${nickname} +
+
+ ${correctNumber} / ${questionsNumber} +
+ `; + return div; +} + +function createRankingHeader() { + let div = document.createElement('div'); + div.className = 'ranking-row'; + div.style.fontWeight = 'bold'; + div.innerHTML = ` +
+ Place +
+
+ Player +
+
+ Answered +
+ `; + return div; +} + +function createHeader() { + let div = document.createElement('div'); + div.className = 'icon-header'; + div.setAttribute('data-testid', 'icon-header'); + div.innerHTML = ` + Ranking icon +

Mode Ranking

+ `; + return div; +} + +function ranking(scoreList) { + let parent = document.querySelector('.ranking-box'); + + let placeholders = []; + const places = ['1st', '2nd', '3rd']; + + for (let place = 0; place < places.length; ++place) { + placeholders.push(createEntry(places[place], scoreList[place])); + } + + parent.appendChild(createHeader()); + parent.appendChild(createRankingHeader()); + placeholders.forEach((item) => parent.appendChild(item)); +} + +export { ranking }; diff --git a/src/app/logic/localStorageScore.js b/src/app/logic/localStorageScore.js index 6f61d90..7631ed3 100644 --- a/src/app/logic/localStorageScore.js +++ b/src/app/logic/localStorageScore.js @@ -1,11 +1,17 @@ import { MODES } from '../modes'; -function saveScore(mode, nickname, score) { - if (!modeIsValid(mode) || !scoreIsValid(score)) return; +function saveScore(mode, nickname, correctNumber, questionsNumber) { + if ( + !modeIsValid(mode) || + !numberIsValid(correctNumber) || + !numberIsValid(questionsNumber) + ) + return; let scores = getScores(mode); + let percentage = (100 * correctNumber) / questionsNumber; - scores.push({ nickname, score }); + scores.push({ nickname, correctNumber, questionsNumber, percentage }); scores.sort(compareScores); if (scores.length === 4) { @@ -20,17 +26,17 @@ function getScores(mode) { } function compareScores(scoreA, scoreB) { - return scoreA.score - scoreB.score; + return scoreB.percentage - scoreA.percentage; } function modeIsValid(mode) { return MODES.includes(mode); } -function scoreIsValid(score) { - if (typeof score !== 'number') - throw new TypeError('score shall be of type "number"'); - return score >= 0; +function numberIsValid(number) { + if (typeof number !== 'number') + throw new TypeError('Passed variable shall be of type "number"'); + return number >= 0; } export { saveScore, getScores }; diff --git a/static/assets/ui/contacts_24px.svg b/static/assets/ui/contacts_24px.svg new file mode 100644 index 0000000..f02ac38 --- /dev/null +++ b/static/assets/ui/contacts_24px.svg @@ -0,0 +1,3 @@ + + + diff --git a/styles/App.scss b/styles/App.scss index be8f2c0..27ad8d9 100644 --- a/styles/App.scss +++ b/styles/App.scss @@ -3,12 +3,13 @@ @import './components/mainMenu.scss'; @import './components/image.scss'; @import './components/redButton.scss'; - +@import './components/ranking.scss'; :root { --border-radius: 15px; --box-shadow-color: red; - --box-shadow-property: 0px 4px 4px rgba(0, 0, 0, 0.25), + --box-shadow-simple: 0px 4px 4px rgba(0, 0, 0, 0.25); + --box-shadow-property: var(--box-shadow-simple), 4px 4px 40px rgba(255, 0, 0, 0.9), inset 4px 5px 4px rgba(0, 0, 0, 0.5); --base-black: #000000; --base-white: #ffffff; diff --git a/styles/components/ranking.scss b/styles/components/ranking.scss new file mode 100644 index 0000000..06ee958 --- /dev/null +++ b/styles/components/ranking.scss @@ -0,0 +1,44 @@ +.ranking-box { + background-color: white; + font-size: 1.5rem; + border-radius: var(--border-radius); + width: 100%; + height: 100%; + padding: 2rem; + box-shadow: var(--box-shadow-simple); +} + +.icon-header { + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + font-weight: bold; + font-size: 2rem; +} + +.icon-header > p { + margin-left: 2rem; +} + +.ranking-row { + background-color: white; + grid-column-gap: 10px; + margin-bottom: 10px; + display: grid; + grid-template-columns: 15% 70% 15%; + grid-template-areas: "place nickname score"; +} + +.place { + grid-area: place; +} + +.nickname { + grid-area: nickname; +} + +.score { + grid-area: score; +} \ No newline at end of file diff --git a/test/app/components/ranking.test.js b/test/app/components/ranking.test.js new file mode 100644 index 0000000..3900ce5 --- /dev/null +++ b/test/app/components/ranking.test.js @@ -0,0 +1,39 @@ +import { getScores, saveScore } from '../../../src/app/logic/localStorageScore'; +import { ranking } from '../../../src/app/components/ranking'; +import { screen } from '@testing-library/dom'; +import '@testing-library/jest-dom/extend-expect'; +import { PEOPLE_MODE } from '../../../src/app/modes'; + +test('screen has four items of ranking-row', () => { + document.body.innerHTML = `
`; + ranking(getScores()); + + let rows = document.querySelectorAll('.ranking-row'); + expect(rows.length).toBe(4); + expect(); +}); + +test('there is an img tag for icon', () => { + document.body.innerHTML = `
`; + ranking(getScores()); + + expect(screen.getByTestId('ranking-icon')).toBeTruthy(); +}); + +test('there is Lucy and Nancy in HTML', () => { + document.body.innerHTML = `
`; + saveScore(PEOPLE_MODE, 'Nancy', 12, 30); + saveScore(PEOPLE_MODE, 'Lucy', 14, 30); + + ranking(getScores(PEOPLE_MODE)); + + let nicknames = document.querySelectorAll('.nickname'); + let scores = document.querySelectorAll('.score'); + + expect(nicknames[1].textContent.includes('Lucy')).toBeTruthy(); + expect(nicknames[2].textContent.includes('Nancy')).toBeTruthy(); + expect(nicknames[3].textContent.includes('-')).toBeTruthy(); + expect(scores[1].textContent.includes('14 / 30')).toBeTruthy(); + expect(scores[2].textContent.includes('12 / 30')).toBeTruthy(); + expect(scores[3].textContent.includes('- / -')).toBeTruthy(); +}); diff --git a/test/app/logic/localStorage.test.js b/test/app/logic/localStorage.test.js index 72f0570..f08bcb3 100644 --- a/test/app/logic/localStorage.test.js +++ b/test/app/logic/localStorage.test.js @@ -38,7 +38,7 @@ describe('LocalStorageScore saveScore() and getScore()', () => { test('Should throw an error if correctNumber is not type of number', () => { expect(() => { saveScore(PEOPLE_MODE, 'Lucy', '12', 12); - }).toThrow('score shall be of type "number"'); + }).toThrow('Passed variable shall be of type "number"'); }); test('Add multiple scores', () => {