Skip to content

Commit

Permalink
Merge pull request #92 from mmircea16/display-discrete-values-in-stac…
Browse files Browse the repository at this point in the history
…ked-bar

Added the ability to show dicrete values in votes stackbar
  • Loading branch information
RaduCStefanescu authored Nov 4, 2020
2 parents a631e4e + 60a757c commit b7f656f
Show file tree
Hide file tree
Showing 12 changed files with 1,533 additions and 44 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@code4ro/reusable-components",
"version": "0.1.46",
"version": "0.1.47",
"description": "Component library for code4ro",
"keywords": [
"code4ro",
Expand Down
41 changes: 36 additions & 5 deletions src/components/ElectionMap/ElectionMap.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { createContext, PropsWithChildren, useContext, useCallback, useMemo } from "react";
import { ElectionMapScope, ElectionMapWinner, ElectionScopeIncomplete } from "../../types/Election";
import { mergeClasses, themable } from "../../hooks/theme";
import React, { createContext, PropsWithChildren, useCallback, useContext, useMemo } from "react";
import { ElectionMapScope, ElectionMapWinner, ElectionScopeIncomplete, ElectionType } from "../../types/Election";
import { ClassNames, mergeClasses, themable } from "../../hooks/theme";
import RomaniaMap from "../../assets/romania-map.svg";
import { useDimensions } from "../../hooks/useDimensions";
import { bucharestCenteredWorldZoom, HereMap, romaniaMapBounds } from "../HereMap/HereMap";
Expand All @@ -18,6 +18,7 @@ type Props = PropsWithChildren<{
maxHeight?: number;
selectedColor?: string;
defaultColor?: string;
electionType?: ElectionType;

api?: ElectionMapAPI;
ballotId?: number | null;
Expand All @@ -28,6 +29,25 @@ const defaultMaxHeight = 460;

export const ElectionMapOverlayURLContext = createContext<string>(electionMapOverlayUrl);

const isElectionWithNationalResults = function (
electionType:
| "referendum"
| "president"
| "senate"
| "house"
| "local_council"
| "county_council"
| "county_council_president"
| "mayor"
| "european_parliament"
| string
| undefined,
) {
return (
electionType === undefined ||
["referendum", "president", "senate", "house", "european_parliament"].includes(electionType)
);
};
export const ElectionMap = themable<Props>(
"ElectionMap",
cssClasses,
Expand All @@ -41,12 +61,14 @@ export const ElectionMap = themable<Props>(
children,
selectedColor,
defaultColor,
electionType,
api,
ballotId,
}) => {
const [ref, { width = 0 }] = useDimensions();

const showsSimpleMap = scope.type === "national";
const showsSimpleMap = scope.type === "national" && isElectionWithNationalResults(electionType);

const ar = aspectRatio ?? defaultAspectRatio;
let height = Math.min(maxHeight, width / ar);
if (!Number.isFinite(height)) {
Expand All @@ -64,7 +86,7 @@ export const ElectionMap = themable<Props>(
(localityId) => ({ ...scope, localityId }),
];
}
if ((scope.type === "locality" && scope.countyId == null) || scope.type === "county") {
if (scope.type === "locality" && scope.countyId == null) {
return [{ type: "national" }, scope.countyId ?? null, (countyId) => ({ ...scope, countyId })];
}
if (scope.type === "diaspora" || scope.type === "diaspora_country") {
Expand All @@ -74,6 +96,15 @@ export const ElectionMap = themable<Props>(
(countryId) => ({ type: "diaspora_country", countryId }),
];
}

if (scope.type === "county" && isElectionWithNationalResults(electionType)) {
return [{ type: "national" }, scope.countyId ?? null, (countyId) => ({ ...scope, countyId })];
}

if (scope.type === "county" && scope.countyId !== null) {
return [{ type: "county", countyId: scope.countyId }, null, (localityId) => ({ ...scope, localityId })];
}

return [{ type: "national" }, null, (countyId) => ({ type: "county", countyId })];
}, [scope, overlayBaseUrl]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */

import React from "react";
import { mockResults } from "../../util/mocks";
import { mockResults, mockCountyCouncilResults } from "../../util/mocks";
import { ElectionResultsStackedBar } from "./ElectionResultsStackedBar";

export default {
Expand All @@ -21,3 +21,16 @@ SimpleExample.args = {
SimpleExample.argTypes = {
results: { control: "object" },
};

export const ExampleWithDiscreteValues = (args: any) => {
return <ElectionResultsStackedBar {...args} />;
};

ExampleWithDiscreteValues.args = {
results: mockCountyCouncilResults,
displayPercentages: false,
};

ExampleWithDiscreteValues.argTypes = {
results: { control: "object" },
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import cssClasses from "./ElectionResultsStackedBar.module.scss";
type Props = {
results: ElectionResults;
meta?: ElectionBallotMeta | null;
displayPercentages?: boolean;
};

const defaultConstants = {
Expand All @@ -25,10 +26,12 @@ export const ElectionResultsStackedBar = themable<Props>(
"ElectionResultsStackedBar",
cssClasses,
defaultConstants,
)(({ classes, results, constants, meta }) => {
)(({ classes, results, constants, meta, displayPercentages }) => {
const { candidates } = results;
const { neutralColor, maxStackedBarItems, breakpoint1, breakpoint2, breakpoint3 } = constants;

const showPercentages = displayPercentages ?? true;

const [stackedBarItems, legendItems] = useMemo(() => {
const items: (HorizontalStackedBarItem & {
name: string;
Expand All @@ -37,7 +40,10 @@ export const ElectionResultsStackedBar = themable<Props>(
index: number;
})[] = [];

const percentageBasis = meta?.type === "referendum" ? results.eligibleVoters ?? 0 : results.validVotes;
const sumOfVotes = candidates.reduce((crtValue, candidate) => crtValue + candidate.votes, 0);

const percentageBasis =
meta?.type === "referendum" ? results.eligibleVoters ?? 0 : showPercentages ? results.validVotes : sumOfVotes;

const stackedBarCount = candidates.length === maxStackedBarItems + 1 ? maxStackedBarItems + 1 : maxStackedBarItems;
for (let i = 0; i < stackedBarCount; i++) {
Expand Down Expand Up @@ -94,7 +100,8 @@ export const ElectionResultsStackedBar = themable<Props>(
key={item.index}
name={item.name}
color={item.color}
percentage={item.percent}
value={showPercentages ? item.percent : item.value}
isPercentage={showPercentages}
iconUrl={(width >= breakpoint1 || item.index < 2) && width >= breakpoint3 ? item.logo : undefined}
rightAligned={index === stackedBarItems.length - 1}
/>
Expand All @@ -110,7 +117,7 @@ export const ElectionResultsStackedBar = themable<Props>(
name={item.name}
color={item.color}
votes={item.value}
percentage={item.percent}
percentage={showPercentages ? item.percent : undefined}
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
import React from "react";
import { ElectionScopeIncompleteResolved } from "../../types/Election";
import {
mockCountyCouncilElectionMeta,
mockCountyCouncilResults,
mockCountyElectionScope,
mockDiasporaElectionScope,
mockElectionAPI,
mockLocalCouncilElectionMeta,
mockLocalCouncilResults,
mockLocalityElectionScope,
mockMayorElectionMeta,
mockMayorResults,
mockNationalElectionScope,
mockPresidentialElectionMeta,
mockResults,
Expand All @@ -27,12 +34,35 @@ export const PresidentialElection = () => {
);
};

export const MayorPerCountyElection = () => {
return (
<ElectionResultsSummarySection
scope={mockCountyElectionScope}
meta={mockMayorElectionMeta}
results={mockMayorResults}
api={mockElectionAPI}
/>
);
};

export const LocalCouncilElection = () => {
return (
<ElectionResultsSummarySection
scope={mockNationalElectionScope}
scope={mockCountyElectionScope}
meta={mockLocalCouncilElectionMeta}
results={mockResults}
results={mockLocalCouncilResults}
api={mockElectionAPI}
/>
);
};

export const CountyCouncilElection = () => {
return (
<ElectionResultsSummarySection
scope={mockNationalElectionScope}
meta={mockCountyCouncilElectionMeta}
results={mockCountyCouncilResults}
api={mockElectionAPI}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { ReactNode } from "react";
import {
ElectionBallotMeta,
ElectionResults,
electionResultsShouldShowAsPercentages,
ElectionScopeIncomplete,
ElectionScopeIncompleteResolved,
electionScopeIsComplete,
Expand Down Expand Up @@ -45,6 +46,8 @@ export const ElectionResultsSummarySection = themable<Props>(

const completeness = electionScopeIsComplete(scope);

const displayPercentages = scope && meta ? electionResultsShouldShowAsPercentages(scope, meta) : true;

const topCandidate = results?.candidates && results.candidates[0];
const percentage = formatPercentage(
fractionOf(
Expand All @@ -57,6 +60,7 @@ export const ElectionResultsSummarySection = themable<Props>(
const map = width != null && (
<ElectionMap
scope={scope}
electionType={meta?.type}
onScopeChange={onScopeChange}
className={classes.map}
selectedColor={topCandidate && electionCandidateColor(topCandidate)}
Expand Down Expand Up @@ -104,7 +108,14 @@ export const ElectionResultsSummarySection = themable<Props>(
fie câștigătorii pentru această unitate au fost aleși în primul tur de scrutin.
</DivBodyHuge>
))}
{results && <ElectionResultsStackedBar className={classes.stackedBar} results={results} meta={meta} />}
{results && (
<ElectionResultsStackedBar
className={classes.stackedBar}
results={results}
meta={meta}
displayPercentages={displayPercentages}
/>
)}
<div style={{ width: "100%" }} ref={measureRef} />
{results && !mobileMap && separator}
{!mobileMap && (
Expand All @@ -115,6 +126,7 @@ export const ElectionResultsSummarySection = themable<Props>(
meta={meta}
results={results}
headers={tableHeaders}
displayVotesAsSeats={!displayPercentages}
/>
)}
{map}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import React from "react";
import { mockLocalCouncilElectionMeta, mockResults } from "../../util/mocks";
import {
mockCountyCouncilElectionMeta,
mockCountyCouncilResults,
mockLocalCouncilElectionMeta,
mockResults,
} from "../../util/mocks";
import { ElectionResultsSummaryTable } from "./ElectionResultsSummaryTable";

export default {
Expand All @@ -24,3 +29,19 @@ SimpleExample.argTypes = {
results: { control: "object" },
header: { control: "object" },
};

export const ExampleWithVotesAsSeats = (args: any) => {
return <ElectionResultsSummaryTable {...args} />;
};

ExampleWithVotesAsSeats.args = {
meta: mockCountyCouncilElectionMeta,
results: mockCountyCouncilResults,
header: { candidate: "Partid", seats: "Mand.", votes: "Voturi", percentage: "%" },
displayVotesAsSeats: true,
};

ExampleWithVotesAsSeats.argTypes = {
results: { control: "object" },
header: { control: "object" },
};
Loading

1 comment on commit b7f656f

@vercel
Copy link

@vercel vercel bot commented on b7f656f Nov 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.