Skip to content

Commit

Permalink
MNG-11 html quiz template (#19)
Browse files Browse the repository at this point in the history
* MNG-11 Add quiz template to index.html and create js file to render this template

* Update quiz template, add template for quiz answer

* MNG-11 Create renderQuizPage and functions for question/mode-title updates

Use templates provided in index.html to render the page.
Use service question syntax for getting the question.

* MNG-11 Update for quizPage, add question/answers items functions

Still needed to correct error in template reading

* MNG-11 Fiish rendering quiz questions

* MNG-11 All finished before user selects the answer

* Fix styles, to avoid cascading main page layout to the quizPage

* Fix styles to avoid cascading main page styles with quizApp id - after rebase

* MNG-11 Functions for selecting answers and checking the correctness

* MNG-11 Questions are now changing, collect styles in appSettings

* Update src/app/App.js change to arrow

Co-authored-by: Łukasz Dutka <lukaszdutka2@gmail.com>

* Small fix  for mode variable

Co-authored-by: Łukasz Dutka <lukaszdutka2@gmail.com>
  • Loading branch information
daria305 and lukaszdutka authored Jan 7, 2021
1 parent 1eb8841 commit 1352a01
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist
.cache
node_modules
coverage
.vscode
46 changes: 35 additions & 11 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="pl">

<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
Expand All @@ -14,16 +15,39 @@

<body>

<div id="pokequiz-app">
Łukasz Dutka
daria305
memeraki / Gosia Dziewit
Aleksandra Cypko / AleksandraCyp
mariusz-sm / Mariusz Smarz
Agata Ludwiczyńska/ AgataLudwiczynska
arendarczyk / Kamil Arendarczyk
</div>
<div id="pokequiz-app">
Łukasz Dutka
daria305
memeraki / Gosia Dziewit
Aleksandra Cypko / AleksandraCyp
mariusz-sm / Mariusz Smarz
Agata Ludwiczyńska/ AgataLudwiczynska
arendarczyk / Kamil Arendarczyk
</div>

<template id="quiz-template">
<div class="quiz-top-bar progress-bar">
<div id="timer"></div>
<div class="mode-title">
<h2></h2>
</div>
<div id="question-counter"></div>
</div>
<div id="quiz-body">
<div class="quiz-question"></div>
<div class="quiz-answers">
<ul class="quiz-answers-list"></ul>
</div>
</div>
</template>

<script src="src/index.js"></script>
<template id="quiz-li">
<li class="quiz-answer">
<div class="unchecked"></div>
</li>
</template>

<script src="src/index.js"></script>
</body>
</html>

</html>
11 changes: 9 additions & 2 deletions src/app/App.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { doc } from "prettier";
import { showStartingPage } from './showStartingPage.js';
import { showAPopUpScreen } from './showAPopUpScreen';
import { addHelpScreenTemplate } from "./addHelpScreenTemplate.js";
import { renderQuizPage } from './quizPage.js'
import { WHO_IS_THAT_POKEMON, WHAT_DOES_THIS_POKEMON_LOOK_LIKE } from "../service/modes.js"

export const App = ({options}) => {

let SELECTED_MODE = WHO_IS_THAT_POKEMON;

showStartingPage();
addHelpScreenTemplate();

Expand All @@ -18,10 +21,12 @@ export const App = ({options}) => {
document.querySelector('#whoIsThatPokemonOption').addEventListener('click',()=>{
if(style.display=='none'||help.style.display == 'none')
console.log("Who's that Pokemon?");
SELECTED_MODE = WHO_IS_THAT_POKEMON;
});
document.querySelector('#whatItLooksLikeOption').addEventListener('click',()=>{
if(style.display=='none'||help.style.display == 'none')
console.log("What it looks like?");
SELECTED_MODE = WHAT_DOES_THIS_POKEMON_LOOK_LIKE;
});
document.querySelector('#guessTheTypeOption').addEventListener('click',()=>{
if(style.display=='none'||help.style.display == 'none')
Expand All @@ -32,6 +37,8 @@ export const App = ({options}) => {
if(style.display=='none'||help.style.display == 'none')
console.log("Hall of Fame");
});
}

// start the game
document.querySelector("#startGameButton").addEventListener("click", () => renderQuizPage(SELECTED_MODE));
}

18 changes: 18 additions & 0 deletions src/app/appSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// css styles dictionary changed during app runtime
export const START_PAGE_STYLES = {
startPageClass: "start-page"
}

export const QUIZ_PAGE_STYLES = {
quizPageClass: "quiz-page",
quizAnswerTextClass: "answer-text",
quizAnswerImageClass: "answer-image",
quizQuestionTextClass: "question-text",
quizQuestionImageClass: "question-image",
wrongAnswerClass: "wrong-answer",
correctAnswerClass: "correct-answer",
uncheckedClass: "unchecked"
}

export const TIMEOUT_AFTER_ANSWER_SELECTION = 1; //miliseconds

223 changes: 223 additions & 0 deletions src/app/quizPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import {
QuestionService
} from "../service/QuestionService.js"

import {
QUIZ_PAGE_STYLES,
START_PAGE_STYLES,
TIMEOUT_AFTER_ANSWER_SELECTION
} from "./appSettings.js"

// will be filledi with mode object during page rendering
let CURRENT_MODE = null;
let GENERATOR = null;

// use to render the page for the first time, after the game start
export function renderQuizPage(mode) {
CURRENT_MODE = mode;

const appScreen = document.querySelector('#pokequiz-app');
appScreen.classList.add(QUIZ_PAGE_STYLES.quizPageClass)
appScreen.classList.remove(START_PAGE_STYLES.startPageClass)
const quizTemplate = document.getElementById('quiz-template');
appScreen.innerHTML = quizTemplate.innerHTML;
// TODO later - generate question using questionService - below are temporary dummy variables
const generatedQuestion = {
question: "quizQuestion",
questionNum: 1,
}
setupPageTitle(CURRENT_MODE);
//GENERATOR = new QuestionService.Generator()
//TODO setupTimer() -- here or directly in App
renderNextQuestion(CURRENT_MODE);
}


// use to update quizPage and change only the question, new answers and question counter
// not changing the timer and bar
// gent question generator and use generates next question if there is any left to answer
// otherwise finishes the game and redirect user to the summary page
export function renderNextQuestion(mode) {
//genQuestion nie powinno być przekazywane do funkcji tylko powinno być tu wywoływana
const genQuestion = getNextQuestion(mode); // TODO later pass generator and use generator.genQuestion(), and replace dummy function with real one, once it's implemented. Gonna be async
if (genQuestion) { // some questions still left to answer
const quizBody = document.querySelector("#quiz-body");
// Update question
const quizQuestionElem = quizBody.querySelector(".quiz-question");
updateQuestion(quizQuestionElem, genQuestion.question, mode);
// Update answers list
const quizUl = quizBody.querySelector(".quiz-answers-list");
updateAnswersList(quizUl, genQuestion.question, mode);
// Update question counter
const questionCounter = document.querySelector("#question-counter");
updateQuestionCounter(questionCounter, genQuestion.questionNum);
// listen for an answer selection
const answersOptions = [...quizBody.querySelector(".quiz-answers-list").children]
for (let option of answersOptions) {
option.addEventListener("mouseup", function selectEventFunc() {
selectAnswer(genQuestion.question, option.querySelector("div"));
})
}
} else { // no more questions left
console.log("You WON!, but sorry, we still don't have any summary page"); // TODO create summary page redirection
}
}

//TODO dummy function to be removed after generator is added
const getNextQuestion = (mode) => {
let q;
if (mode.name == "WHO_IS_THAT_POKEMON") {
q = {
question: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/1.png',
answers: ['bulbasaur', 'ivysaur', 'venusaur', 'charmander', 'dummy1', 'dummy2'],
correctAnswer: {
name: 'bulbasaur',
index: 1
}
}
} else if (mode.name == "WHAT_DOES_THIS_POKEMON_LOOK_LIKE") {
q = {
question: 'bulbasaur',
answers: ['https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/1.png',
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/4.png',
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/3.png',
'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/2.png'
],
correctAnswer: {
name: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/1.png',
index: 1
}
}
}
return {
question: q,
questionNum: 1,
}
}

// Changes the title corresponding to the chosen game mode
const setupPageTitle = (mode) => {
const modeHeader = document.querySelector(".mode-title h2")
modeHeader.innerText = mode.title // Setup mode title
}

// Updates the question div with styling and content depending on a type of a question
const updateQuestion = (questionElement, questionSet, mode) => {
if (mode.questionType === "image") {
questionElement.classList.add(QUIZ_PAGE_STYLES.quizQuestionImageClass);
const imgElem = createImgElement(questionSet.question); // add img from url
questionElement.appendChild(imgElem);

} else if (mode.questionType === "text") {
questionElement.classList.add(QUIZ_PAGE_STYLES.quizQuestionTextClass);
questionElement.innerText = questionSet.question; // add question as an inner text
}
}

// Creates img element with a selected image url
const createImgElement = (url) => {
const img = document.createElement("img");
img.setAttribute("src", url);
return img;
}

// Updates styles for question list based on mode type
// creates question items sor provided question set
const updateAnswersList = (answersElement, questionSet, mode) => {
for (let answer of questionSet.answers) {
const answerElement = createAnswerElement(answer, mode);
answersElement.appendChild(answerElement);
}
}

// returns children elements from the template
// for template.content replacement, which is not fully supported yet
const getTemplateContent = (template) => {
const dummyDiv = document.createElement("div"); //
dummyDiv.innerHTML = template.innerHTML;
return dummyDiv.children
}

const createAnswerElement = (answer, mode) => {
const liTemplate = document.querySelector("#quiz-li");
const li = getTemplateContent(liTemplate)[0];
const liFirstElem = li.children[0]

if (mode.answerType === "image") {
// first child of li receives an image
liFirstElem.classList.add(QUIZ_PAGE_STYLES.quizAnswerImageClass)
const imgElem = createImgElement(answer) // get img url
liFirstElem.appendChild(imgElem)

} else if (mode.answerType === "text") {
// first child of li receives text
liFirstElem.classList.add(QUIZ_PAGE_STYLES.quizAnswerTextClass)
liFirstElem.innerText = answer // add question as an inner text
}
return li
}

// Updates the question number ona quiz page
const updateQuestionCounter = (counterElem, questionNum) => {
counterElem.innerText = String(questionNum).padStart(2, '0'); //TODO maybe - total num of question in a game mode
}

// fires up on mouse up event on answers list li, additionally accepts questionSet
// check which answer was selected
// eventHandler is element to which eventListener was attached to
function selectAnswer(questionSet, eventHandler) {
const answer = getAnswerFromElement(eventHandler);
if (answer) {
questionSet.correctAnswer.name === answer ? correctAnswerSelected(eventHandler) : wrongAnswerSelected(eventHandler);
} else {
throw new Error('Answer was not found')
}
}


// Read selected answer value from clicked list item
const getAnswerFromElement = (target) => {
const targetClasses = [...target.classList];
let answer;
if (targetClasses.includes(QUIZ_PAGE_STYLES.uncheckedClass)) {
if (targetClasses.includes(QUIZ_PAGE_STYLES.quizAnswerTextClass)) {
answer = target.innerText
} else if (targetClasses.includes(QUIZ_PAGE_STYLES.quizAnswerImageClass)) {
answer = target.children[0].getAttribute("src")
}
return answer
}
}

const correctAnswerSelected = (selectedElem) => {
console.log("yes")
//TODO add correct-answer class and remove unchecked
selectedElem.classList.remove(QUIZ_PAGE_STYLES.uncheckedClass)
selectedElem.classList.add(QUIZ_PAGE_STYLES.correctAnswerClass)
//TODO store results
setTimeout(()=> {
resetQuizAfterQuestion();
renderNextQuestion(CURRENT_MODE);
}, TIMEOUT_AFTER_ANSWER_SELECTION)
}

const wrongAnswerSelected = (selectedElem) => {
console.log("no")
// add wrong-answer class and remove unchecked
selectedElem.classList.remove(QUIZ_PAGE_STYLES.uncheckedClass)
selectedElem.classList.add(QUIZ_PAGE_STYLES.wrongAnswerClass)
//TODO store results
setTimeout(()=> {
resetQuizAfterQuestion();
renderNextQuestion(CURRENT_MODE);
}, TIMEOUT_AFTER_ANSWER_SELECTION)
}

// removes question list items
const resetQuizAfterQuestion = () => {
const quizBody = document.getElementById('quiz-body');
const quizTemplate = document.getElementById('quiz-template');
quizBody.innerHTML = getTemplateContent(quizTemplate)[1].innerHTML // get the quiz body inner HTML from the template
}
// TODO check if any css class should be reset too

4 changes: 4 additions & 0 deletions src/app/showStartingPage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { START_PAGE_STYLES } from "./appSettings.js"

export const showStartingPage = () => {
const appScreen = document.querySelector('#pokequiz-app');
appScreen.classList.add(START_PAGE_STYLES.startPageClass)
const startingPageTemplate =
`<div id="headerWithLogo" class="firstColumn spanInPortrait disableWithPopUpScreen">
<img src="./static/assets/ui/logo.png" alt='Pokemon' id='pokemonLogo'/>
Expand Down Expand Up @@ -32,4 +35,5 @@ export const showStartingPage = () => {
<img src='./static/assets/ui/pikach1.png' alt='Pikachu' id='pikachuImg' class="secondColumn disableWithPopUpScreen"/>
`
appScreen.innerHTML = startingPageTemplate;

}
Loading

0 comments on commit 1352a01

Please sign in to comment.