Skip to content

Commit

Permalink
feat: show total count instead of percentage and order by count
Browse files Browse the repository at this point in the history
  • Loading branch information
PascalinDe committed Feb 23, 2024
1 parent 801594b commit 667191e
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 53 deletions.
12 changes: 9 additions & 3 deletions web/frontend/src/pages/form/GroupedResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,15 @@ const GroupedResult: FC<GroupedResultProps> = ({ rankResult, selectResult, textR
const select = element as SelectQuestion;

if (selectResult.has(id)) {
res = countSelectResult(selectResult.get(id)).resultsInPercent.map((percent, index) => {
return { Candidate: select.Choices[index], Percentage: `${percent}%` };
});
res = countSelectResult(selectResult.get(id))
.map(([, totalCount], index) => {
return {
Candidate: select.Choices[index],
TotalCount: totalCount,
NumberOfBallots: selectResult.get(id).length,
};
})
.sort((x, y) => y.TotalCount - x.TotalCount);
dataToDownload.push({ Title: element.Title.En, Results: res });
}
break;
Expand Down
30 changes: 28 additions & 2 deletions web/frontend/src/pages/form/components/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ type ProgressBarProps = {
children: string;
};

const ProgressBar: FC<ProgressBarProps> = ({ isBest, children }) => {
type SelectProgressBarProps = {
percent: string;
totalCount: number;
numberOfBallots: number;
isBest: boolean;
};

export const ProgressBar: FC<ProgressBarProps> = ({ isBest, children }) => {
return (
<div className="sm:ml-1 md:ml-2 w-3/5 sm:w-4/5">
<div className="h-min bg-white rounded-full mr-1 md:mr-2 w-full flex items-center">
Expand All @@ -21,4 +28,23 @@ const ProgressBar: FC<ProgressBarProps> = ({ isBest, children }) => {
);
};

export default ProgressBar;
export const SelectProgressBar: FC<SelectProgressBarProps> = ({
percent,
totalCount,
numberOfBallots,
isBest,
}) => {
return (
<div className="sm:ml-1 md:ml-2 w-3/5 sm:w-4/5">
<div className="h-min bg-white rounded-full mr-1 md:mr-2 w-full flex items-center">
<div
className={`${!isBest && totalCount !== 0 && 'bg-indigo-300'} ${
!isBest && totalCount === 0 && 'bg-indigo-100'
} ${isBest && 'bg-indigo-500'} flex-none mr-2 text-white h-2 sm:h-3 p-0.5 rounded-full`}
style={{ width: `${percent}%` }}></div>

<div className="text-gray-700 text-sm">{`${totalCount}/${numberOfBallots}`}</div>
</div>
</div>
);
};
2 changes: 1 addition & 1 deletion web/frontend/src/pages/form/components/RankResult.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC } from 'react';
import { RankQuestion } from 'types/configuration';
import ProgressBar from './ProgressBar';
import { ProgressBar } from './ProgressBar';
import { countRankResult } from './utils/countResult';
import { default as i18n } from 'i18next';

Expand Down
22 changes: 15 additions & 7 deletions web/frontend/src/pages/form/components/SelectResult.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC } from 'react';
import { SelectQuestion } from 'types/configuration';
import ProgressBar from './ProgressBar';
import { SelectProgressBar } from './ProgressBar';
import { countSelectResult } from './utils/countResult';
import { default as i18n } from 'i18next';

Expand All @@ -11,25 +11,33 @@ type SelectResultProps = {

// Display the results of a select question.
const SelectResult: FC<SelectResultProps> = ({ select, selectResult }) => {
const { resultsInPercent, maxIndices } = countSelectResult(selectResult);
const sortedResults = countSelectResult(selectResult)
.map((result, index) => {
const tempResult: [string, number, number] = [...result, index];
return tempResult;
})
.sort((x, y) => y[1] - x[1]);
const maxCount = sortedResults[0][1];

const displayResults = () => {
return resultsInPercent.map((percent, index) => {
const isBest = maxIndices.includes(index);

return sortedResults.map(([percent, totalCount, origIndex], index) => {
return (
<React.Fragment key={index}>
<div className="px-2 sm:px-4 break-words max-w-xs w-max">
<span>
{
(select.ChoicesMap.has(i18n.language)
? select.ChoicesMap.get(i18n.language)
: select.ChoicesMap.get('en'))[index]
: select.ChoicesMap.get('en'))[origIndex]
}
</span>
:
</div>
<ProgressBar isBest={isBest}>{percent}</ProgressBar>
<SelectProgressBar
percent={percent}
totalCount={totalCount}
numberOfBallots={selectResult.length}
isBest={totalCount === maxCount}></SelectProgressBar>
</React.Fragment>
);
});
Expand Down
2 changes: 1 addition & 1 deletion web/frontend/src/pages/form/components/TextResult.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { FC } from 'react';
import { TextQuestion } from 'types/configuration';
import ProgressBar from './ProgressBar';
import { ProgressBar } from './ProgressBar';
import { countTextResult } from './utils/countResult';
import { default as i18n } from 'i18next';

Expand Down
37 changes: 13 additions & 24 deletions web/frontend/src/pages/form/components/utils/countResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,20 @@ const countRankResult = (rankResult: number[][], rank: RankQuestion) => {
// percentage of the total number of votes and which candidate(s) in the
// select.Choices has the most votes
const countSelectResult = (selectResult: number[][]) => {
const resultsInPercent: string[] = [];
const maxIndices: number[] = [];
let max = 0;

const results = selectResult.reduce((a, b) => {
return a.map((value, index) => {
const current = value + b[index];

if (current >= max) {
max = current;
}
return current;
let results: [string, number][] = [];

selectResult
.reduce(
(tally, currBallot) => tally.map((currCount, index) => currCount + currBallot[index]),
new Array(selectResult[0].length).fill(0)
)
.forEach((totalCount) => {
results.push([
(Math.round((totalCount / selectResult.length) * 100 * 100) / 100).toFixed(2).toString(),
totalCount,
]);
});
}, new Array(selectResult[0].length).fill(0));

results.forEach((count, index) => {
if (count === max) {
maxIndices.push(index);
}

const percentage = (count / selectResult.length) * 100;
const roundedPercentage = (Math.round(percentage * 100) / 100).toFixed(2);
resultsInPercent.push(roundedPercentage);
});
return { resultsInPercent, maxIndices };
return results;
};

// Count the number of votes for each candidate and returns the counts and the
Expand Down
7 changes: 6 additions & 1 deletion web/frontend/src/types/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ type TextResults = Map<ID, string[][]>;

interface DownloadedResults {
Title: string;
Results?: { Candidate: string; Percentage: string }[];
Results?: {
Candidate: string;
Percent?: string;
TotalCount?: number;
NumberOfBallots?: number;
}[];
}
interface BallotResults {
BallotNumber: number;
Expand Down
4 changes: 2 additions & 2 deletions web/frontend/tests/json/evoting/forms/combined.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@
"SelectResult": [
[
false,
false,
true,
true
],
[
false,
true,
false,
false,
true
Expand Down
31 changes: 19 additions & 12 deletions web/frontend/tests/result.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,29 @@ test('Assert form titles are displayed correctly', async ({ page }) => {

test('Assert grouped results are displayed correctly', async ({ page }) => {
// grouped results are displayed by default
for (const [index, scaffold] of Form.Configuration.Scaffold.entries()) {
let i = 1;
for (const expected of [
[
['Blue', '3/4'],
['Green', '2/4'],
['Red', '1/4'],
],
[
['Cyan', '2/4'],
['Magenta', '2/4'],
['Yellow', '1/4'],
['Key', '1/4'],
],
]) {
const resultGrid = await page
.getByTestId('content')
.locator(`xpath=./div/div/div[2]/div/div[2]/div/div/div[${index + 1}]/div/div/div[2]`);
.locator(`xpath=./div/div/div[2]/div/div[2]/div/div/div[${i}]/div/div/div[2]`);
i += 1;
let j = 1;
for (const [i, choice] of scaffold.Selects.at(0).Choices.entries()) {
await expect(resultGrid.locator(`xpath=./div[${j}]/span`)).toContainText(
JSON.parse(choice).en
);
for (const [title, totalCount] of expected) {
await expect(resultGrid.locator(`xpath=./div[${j}]/span`)).toContainText(title);
await expect(resultGrid.locator(`xpath=./div[${j + 1}]/div/div[2]`)).toContainText(
[
['33.33%', '33.33%', '66.67%'],
['25.00%', '50.00%', '25.00%', '25.00%'],
]
.at(index)
.at(i)
totalCount
);
j += 2;
}
Expand Down

0 comments on commit 667191e

Please sign in to comment.