Skip to content

Commit d5b1886

Browse files
Include progress bar to form
To give users a visual reference of the remaining questions we now show a progress bar to the top of the component. Closes #67
1 parent 2ab318f commit d5b1886

File tree

7 files changed

+90
-0
lines changed

7 files changed

+90
-0
lines changed

src/App.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
background-color: white;
55
border-radius: 4px;
66
padding: 20px;
7+
position: relative;
78

89
.h2 {
910
padding-bottom: 8px;

src/App.test.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,17 +318,26 @@ describe('App component', () => {
318318
await user.click(button);
319319

320320
let indexTextEl = screen.getByText('1 of 2');
321+
const progressBar = screen.getByRole('progressbar', {
322+
name: 'Quiz progress',
323+
});
321324

322325
for (let i = 0; i < 5; i++) {
323326
// The progress count does not change
324327
expect(indexTextEl).toBeInTheDocument();
328+
expect(progressBar).toHaveValue(0);
325329
await answerQuestionIncorrectly(user);
326330
await user.click(screen.getByText('Next Question'));
327331
}
328332

329333
// After the attempts of the first question have expire, the progress is updated
334+
expect(progressBar).toHaveValue(50);
330335
indexTextEl = screen.getByText('2 of 2');
331336
expect(indexTextEl).toBeInTheDocument();
337+
338+
await answerQuestionCorrectly(user);
339+
//After submitting the answer the progress bar updates
340+
expect(progressBar).toHaveValue(100);
332341
});
333342

334343
it('displays the remaining attempts after wrongly answering a question', async () => {

src/components/Form.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { QuestionPoolType, AnswerType, QuestionTypes } from '../types';
33
import { isAlreadySelected, validateSelectionIsCorrect } from '../util';
44
import Answers from './Answers';
55
import Question from './Question';
6+
import ProgressBar from './ProgressBar';
67
import { Context } from '../Context';
78
import Button from './Button';
89
import styles from './Form.module.scss';
@@ -31,6 +32,7 @@ const Form = ({
3132
const [showConfirmation, setShowConfirmation] = useState(false);
3233
const [submitted, setSubmitted] = useState(false);
3334
const [selections, setSelections] = useState<AnswerType[]>([]);
35+
const [progress, setProgress] = useState(0);
3436

3537
useEffect(() => {
3638
if (!submitted) {
@@ -98,6 +100,12 @@ const Form = ({
98100
}
99101
};
100102

103+
const handleProgressBar = (question: QuestionPoolType) => {
104+
if (question.correctlyAnswered || question.remainingAttempts === 0) {
105+
setProgress(progress + 100 / numberOfQuestions);
106+
}
107+
};
108+
101109
const submitHandler = (event: FormEvent) => {
102110
event.preventDefault();
103111

@@ -109,6 +117,7 @@ const Form = ({
109117
setSubmitted(true);
110118
updatePool(correctlyAnswered);
111119
addToResult(updatedQuestion(correctlyAnswered));
120+
handleProgressBar(updatedQuestion(correctlyAnswered));
112121
setSelections([]);
113122
};
114123

@@ -139,6 +148,7 @@ const Form = ({
139148

140149
return (
141150
<form onSubmit={submitHandler} className={styles.form}>
151+
<ProgressBar progress={progress} />
142152
<fieldset className={`${styles.form} ${styles.fieldset}`}>
143153
<legend className={`${styles.form} ${styles.legend}`}>
144154
{questionIndexText} {t('form.of')} {numberOfQuestions}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
@use './../variables.scss';
2+
3+
$initialBarWidth: 15px;
4+
5+
.initialProgress {
6+
position: absolute;
7+
top: 0;
8+
left: 0;
9+
height: 5px;
10+
width: $initialBarWidth;
11+
background-color: variables.$primary;
12+
}
13+
14+
.progressBar[value] {
15+
height: 5px;
16+
/* Reset the default appearance */
17+
-webkit-appearance: none;
18+
appearance: none;
19+
width: calc(100% - $initialBarWidth);
20+
position: absolute;
21+
top: 0;
22+
left: $initialBarWidth;
23+
24+
/* Firefox */
25+
background: white;
26+
border: none; /* Firefox adds a default border */
27+
28+
/* Chrome and Safari */
29+
&::-webkit-progress-bar {
30+
background: white;
31+
}
32+
33+
&::-webkit-progress-value {
34+
background-color: variables.$primary;
35+
}
36+
37+
/* Firefox */
38+
&::-moz-progress-bar {
39+
background-color: variables.$primary;
40+
}
41+
42+
&::-webkit-progress-value {
43+
transition: width 1s ease-in-out;
44+
}
45+
}

src/components/ProgressBar.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import styles from './ProgressBar.module.scss';
2+
import { useTranslation } from 'react-i18next';
3+
4+
const ProgressBar = ({ progress }: { progress: number }) => {
5+
const { t } = useTranslation();
6+
return (
7+
<>
8+
<span className={styles.initialProgress}></span>
9+
<progress
10+
aria-label={t('progress.ariaLabel')}
11+
className={styles.progressBar}
12+
value={progress}
13+
max={100}
14+
></progress>
15+
</>
16+
);
17+
};
18+
19+
export default ProgressBar;

src/locales/de.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ const de = {
2323
noBtn: 'Nein',
2424
yesBtn: 'Ja',
2525
},
26+
progress: {
27+
ariaLabel: 'Quiz-Fortschritt',
28+
},
2629
question: {
2730
correct: 'Ihre Antwort war richtig.',
2831
notCorrect: 'Ihre Antwort war nicht richtig.',

src/locales/en.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ const en = {
2424
noBtn: 'No',
2525
yesBtn: 'Yes',
2626
},
27+
progress: {
28+
ariaLabel: 'Quiz progress',
29+
},
2730
question: {
2831
correct: 'Your answer was correct.',
2932
notCorrect: 'Your answer was not correct.',

0 commit comments

Comments
 (0)