Skip to content

Commit 5b7c4ac

Browse files
committed
small tweak
1 parent 9d84dac commit 5b7c4ac

6 files changed

Lines changed: 98 additions & 14 deletions

File tree

‎OpenVoting.Client/src/components/ActivePollsPage.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AuthPrompt } from './AuthPrompt';
55
import { useToast } from './useToast';
66
import type { Dispatch, SetStateAction } from 'react';
77
import type { CreatePollForm, PollResponse, SessionState } from '../types';
8-
import { formatWindow, pollStatusLabel } from '../utils/format';
8+
import { formatWindow, pollStatusLabel, shouldShowTotalVotes } from '../utils/format';
99

1010
export type ActivePollsPageProps = {
1111
sessionState: SessionState;
@@ -181,7 +181,7 @@ export function ActivePollsPage({ sessionState, me, activePolls, pollError, load
181181
{filteredActivePolls.map((p) => {
182182
const entryClass = p.status === 0 ? 'entry-card draft' : 'entry-card';
183183
const statusLabel = pollStatusLabel(p.status);
184-
const canHaveVotes = p.status === 2 || p.status === 3 || p.status === 4;
184+
const canHaveVotes = shouldShowTotalVotes(p.status);
185185
return (
186186
<li key={p.id} className={`${entryClass} live-poll-card`} data-testid={`active-poll-${p.id}`}>
187187
<div className="entry-head live-poll-card-head">
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { MemoryRouter, Route, Routes } from 'react-router-dom';
3+
import { PollDetailPage } from './PollDetailPage';
4+
import type { PollDetailResponse } from '../types';
5+
6+
const iso = () => new Date('2024-01-01T00:00:00.000Z').toISOString();
7+
8+
function createPollDetail(overrides: Partial<PollDetailResponse> = {}): PollDetailResponse {
9+
return {
10+
id: 'poll-1',
11+
title: 'Sample Poll',
12+
description: 'Description',
13+
status: 1,
14+
votingMethod: 1,
15+
submissionOpensAt: iso(),
16+
submissionClosesAt: iso(),
17+
votingOpensAt: iso(),
18+
votingClosesAt: iso(),
19+
hideEntriesUntilVoting: false,
20+
maxSelections: 2,
21+
requireRanking: false,
22+
totalVotes: 7,
23+
titleRequirement: 1,
24+
descriptionRequirement: 1,
25+
imageRequirement: 1,
26+
winners: [],
27+
entries: [],
28+
...overrides
29+
};
30+
}
31+
32+
function renderPage(detail: PollDetailResponse) {
33+
return render(
34+
<MemoryRouter initialEntries={['/polls/poll-1']}>
35+
<Routes>
36+
<Route
37+
path="/polls/:pollId"
38+
element={<PollDetailPage sessionState="authenticated" fetchDetail={vi.fn().mockResolvedValue(detail)} assetCache={{}} />}
39+
/>
40+
</Routes>
41+
</MemoryRouter>
42+
);
43+
}
44+
45+
describe('PollDetailPage', () => {
46+
it('hides total votes before voting starts', async () => {
47+
renderPage(createPollDetail({ status: 1, totalVotes: 7 }));
48+
49+
expect(await screen.findByText('Sample Poll')).toBeInTheDocument();
50+
expect(screen.queryByText('Total votes')).not.toBeInTheDocument();
51+
expect(screen.queryByText('7')).not.toBeInTheDocument();
52+
});
53+
54+
it('shows total votes once voting has started', async () => {
55+
renderPage(createPollDetail({ status: 2, totalVotes: 7 }));
56+
57+
expect(await screen.findByText('Sample Poll')).toBeInTheDocument();
58+
expect(screen.getByText('Total votes')).toBeInTheDocument();
59+
expect(screen.getByText('7')).toBeInTheDocument();
60+
});
61+
});

‎OpenVoting.Client/src/components/PollDetailPage.tsx‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Link, useParams } from 'react-router-dom';
33
import { AuthPrompt } from './AuthPrompt';
44
import { MarkdownText } from './MarkdownText';
55
import type { AssetUploadResponse, PollDetailResponse, PollWinnerResponse, SessionState } from '../types';
6-
import { formatWindow, pollStatusLabel, shouldShowVotingMethod, votingMethodLabel } from '../utils/format';
6+
import { formatWindow, pollStatusLabel, shouldShowTotalVotes, shouldShowVotingMethod, votingMethodLabel } from '../utils/format';
77

88
export type PollDetailPageProps = {
99
sessionState: SessionState;
@@ -78,6 +78,7 @@ export function PollDetailPage({ sessionState, fetchDetail, assetCache }: PollDe
7878
}
7979

8080
const isTie = detail.winners.length > 1 && detail.winners.every((w) => w.votes === detail.winners[0].votes);
81+
const showTotalVotes = shouldShowTotalVotes(detail.status);
8182
const showVotingMethod = shouldShowVotingMethod(detail.status, detail.votingMethod);
8283

8384
const winnerTitle = (winner: PollWinnerResponse) => {
@@ -127,10 +128,12 @@ export function PollDetailPage({ sessionState, fetchDetail, assetCache }: PollDe
127128
<p className="muted">Selections</p>
128129
<p className="metric">Up to {detail.maxSelections}</p>
129130
</div>
130-
<div>
131-
<p className="muted">Total votes</p>
132-
<p className="metric">{detail.totalVotes}</p>
133-
</div>
131+
{showTotalVotes && (
132+
<div>
133+
<p className="muted">Total votes</p>
134+
<p className="metric">{detail.totalVotes}</p>
135+
</div>
136+
)}
134137
<div>
135138
<p className="muted">Visibility</p>
136139
<p className="metric">{detail.hideEntriesUntilVoting ? 'Hidden until voting' : 'Entries visible'}</p>

‎OpenVoting.Client/src/components/currentPoll/PollHeaderSection.test.tsx‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ import { PollHeaderSection } from './PollHeaderSection';
44
import { createPollResponse } from '../../test/factories';
55

66
describe('PollHeaderSection', () => {
7+
it('hides total votes before voting starts', () => {
8+
render(
9+
<MemoryRouter>
10+
<PollHeaderSection poll={createPollResponse({ status: 0, totalVotes: 12 })} onRefreshPoll={vi.fn()} />
11+
</MemoryRouter>
12+
);
13+
14+
expect(screen.queryByText('Total votes')).not.toBeInTheDocument();
15+
expect(screen.queryByText('12')).not.toBeInTheDocument();
16+
});
17+
718
it('hides the voting method before voting starts', () => {
819
render(
920
<MemoryRouter>
@@ -18,10 +29,12 @@ describe('PollHeaderSection', () => {
1829
it('shows the voting method once voting has started', () => {
1930
render(
2031
<MemoryRouter>
21-
<PollHeaderSection poll={createPollResponse({ status: 2, votingMethod: 1 })} onRefreshPoll={vi.fn()} />
32+
<PollHeaderSection poll={createPollResponse({ status: 2, votingMethod: 1, totalVotes: 12 })} onRefreshPoll={vi.fn()} />
2233
</MemoryRouter>
2334
);
2435

36+
expect(screen.getByText('Total votes')).toBeInTheDocument();
37+
expect(screen.getByText('12')).toBeInTheDocument();
2538
expect(screen.getByText('Voting method')).toBeInTheDocument();
2639
expect(screen.getByText('Approval')).toBeInTheDocument();
2740
});

‎OpenVoting.Client/src/components/currentPoll/PollHeaderSection.tsx‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Link } from 'react-router-dom';
22
import type { PollResponse } from '../../types';
3-
import { formatWindow, isMaxTimestamp, pollStatusLabel, shouldShowVotingMethod, votingMethodLabel } from '../../utils/format';
3+
import { formatWindow, isMaxTimestamp, pollStatusLabel, shouldShowTotalVotes, shouldShowVotingMethod, votingMethodLabel } from '../../utils/format';
44
import { VotingMethodInfo } from '../VotingMethodInfo';
55
import { MarkdownText } from '../MarkdownText';
66

@@ -11,6 +11,7 @@ export type PollHeaderSectionProps = {
1111

1212
export function PollHeaderSection({ poll, onRefreshPoll }: PollHeaderSectionProps) {
1313
const showVotingWindow = !!poll && (poll.status === 2 || poll.status === 3 || poll.status === 4 || poll.status === 5) && !isMaxTimestamp(poll.votingOpensAt);
14+
const showTotalVotes = !!poll && shouldShowTotalVotes(poll.status);
1415
const showVotingMethod = !!poll && shouldShowVotingMethod(poll.status, poll.votingMethod);
1516

1617
return (
@@ -37,10 +38,12 @@ export function PollHeaderSection({ poll, onRefreshPoll }: PollHeaderSectionProp
3738
<p className="muted">Status</p>
3839
<p className="metric poll-header-detail-value">{pollStatusLabel(poll.status)}</p>
3940
</div>
40-
<div>
41-
<p className="muted">Total votes</p>
42-
<p className="metric poll-header-detail-value">{poll.totalVotes}</p>
43-
</div>
41+
{showTotalVotes && (
42+
<div>
43+
<p className="muted">Total votes</p>
44+
<p className="metric poll-header-detail-value">{poll.totalVotes}</p>
45+
</div>
46+
)}
4447
{showVotingMethod && (
4548
<div>
4649
<p className="muted">Voting method</p>

‎OpenVoting.Client/src/utils/format.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ export function votingMethodLabel(method: number) {
2828
}
2929
}
3030

31+
export function shouldShowTotalVotes(status: number) {
32+
return status === 2 || status === 3 || status === 4;
33+
}
34+
3135
export function shouldShowVotingMethod(status: number, method: number) {
32-
const votingHasStarted = status === 2 || status === 3 || status === 4;
36+
const votingHasStarted = shouldShowTotalVotes(status);
3337
const hasConfiguredMethod = method === 1 || method === 2;
3438
return votingHasStarted && hasConfiguredMethod;
3539
}

0 commit comments

Comments
 (0)