Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/js/pages/ElectionFinder/ElectionFinderForElection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,11 @@ function OfficeSectionItemInner ({ // eslint-disable-line react/no-multi-comp
<CopyChip defaultLabel="Copy candidate name" getText={() => candidateName} />
<CopyChip defaultLabel="Copy link" getText={() => `${window.location.origin}${getCandidatePath(candidate)}`} />
<ActionDivider />
{nextReleaseFeaturesEnabled && (
<DarkTooltip title="About office">
<IconButton size="small"><InfoOutlined fontSize="small" /></IconButton>
</DarkTooltip>
)}
<DarkTooltip title="Open in new tab">
<IconButton size="small" onClick={() => window.open(getCandidatePath(candidate), '_blank')}>
<Launch fontSize="small" />
Expand All @@ -392,6 +397,7 @@ function OfficeSectionItemInner ({ // eslint-disable-line react/no-multi-comp
items={[
{ key: 'copy-name', icon: ContentCopy, label: 'Copy candidate name', onClick: () => copyAndToast(candidateName) },
{ key: 'copy-link', icon: ContentCopy, label: 'Copy link', onClick: () => copyAndToast(`${window.location.origin}${getCandidatePath(candidate)}`) },
...(nextReleaseFeaturesEnabled ? [{ key: 'about', icon: InfoOutlined, label: 'About office', onClick: () => {} }] : []),
{ key: 'open', icon: Launch, label: 'Open in new tab', onClick: () => window.open(getCandidatePath(candidate), '_blank') },
]}
/>
Expand Down
35 changes: 21 additions & 14 deletions src/js/pages/ElectionFinder/ElectionFinderForState.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import ElectionFinderHeader from './ElectionFinderHeader';
import RowKebabMenu from './RowKebabMenu';
import {
ActionDivider, DarkTooltip,
ElectionDatePill, ElectionLink, ElectionList, ElectionRow, ElectionRowActions,
ElectionDateText, ElectionLink, ElectionList, ElectionRow, ElectionRowActions, ElectionRowText,
FilterTab, FilterTabsRow, InlineSearchField, NoResults,
SearchIconButton, SectionTitle, SectionTitleRow, ShowMoreButton,
StateSelectWrapper, StateSelectNative, StateSelectLabel, StateSelectCaret,
Expand All @@ -30,16 +30,27 @@ const SORTED_STATES = Object.entries(stateCodeMap)
.filter(([code]) => code !== 'NA')
.sort((a, b) => a[1].localeCompare(b[1]));

function formatDateUS (dateString) {
const MONTH_ABBR = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

function formatDateLong (dateString) {
if (!dateString) return '';
const [y, m, d] = dateString.split('-');
return `${m}-${d}-${y}`;
return `${MONTH_ABBR[parseInt(m, 10) - 1]} ${parseInt(d, 10)}, ${y}`;
}

function sortByDateAsc (a, b) {
return (a.election_day_text || '').localeCompare(b.election_day_text || '');
}

// 'NA' must be matched explicitly: an empty state_code_list does not imply national,
// since some state-level elections come back without state_code_list populated.
function matchesStateCode (election, stateCode) {
if (stateCode === 'NA') {
return election.state_code === 'NA' || (election.state_code_list && election.state_code_list.includes('NA'));
}
return election.state_code_list && election.state_code_list.includes(stateCode);
}

function getBreadcrumbTabLabel (filterTab) {
if (filterTab === 'all') return 'All';
if (filterTab === 'past') return 'Past';
Expand Down Expand Up @@ -82,9 +93,7 @@ function ElectionFinderForState () {
// Auto-switch from "upcoming" to "all" when there are no upcoming elections for this state
useEffect(() => {
if (electionList.length > 0 && filterTab === 'upcoming') {
const stateElections = electionList.filter(
(el) => el.state_code_list && el.state_code_list.includes(selectedStateCode),
);
const stateElections = electionList.filter((el) => matchesStateCode(el, selectedStateCode));
const hasUpcoming = stateElections.some((el) => el.election_is_upcoming);
if (!hasUpcoming && stateElections.length > 0) {
setFilterTab('all');
Expand All @@ -110,10 +119,7 @@ function ElectionFinderForState () {
historyPush(`/election-finder/${selectedStateCode.toLowerCase()}/${googleCivicElectionId}`);
}, [selectedStateCode]);

// Filter elections for this state
let stateElections = electionList.filter(
(el) => el.state_code_list && el.state_code_list.includes(selectedStateCode),
);
let stateElections = electionList.filter((el) => matchesStateCode(el, selectedStateCode));

// Apply search filter before splitting upcoming/past so counts reflect the search
if (searchText) {
Expand Down Expand Up @@ -167,6 +173,7 @@ function ElectionFinderForState () {
<StateSelectCaret><ExpandMore fontSize="inherit" /></StateSelectCaret>
<StateSelectNative value={selectedStateCode || ''} onChange={onStateChange}>
<option value="">Select state</option>
<option value="NA">National</option>
{SORTED_STATES.map(([code, name]) => (
<option key={code} value={code}>{name}</option>
))}
Expand Down Expand Up @@ -239,12 +246,12 @@ function ElectionFinderForState () {
key={googleCivicElectionId}
onClick={() => onElectionSelect(googleCivicElectionId)}
>
<ElectionLink>
<ElectionRowText>
<ElectionLink>{election.election_name || ''}</ElectionLink>
{election.election_day_text && (
<ElectionDatePill>{formatDateUS(election.election_day_text)}</ElectionDatePill>
<ElectionDateText>{formatDateLong(election.election_day_text)}</ElectionDateText>
)}
{election.election_name || ''}
</ElectionLink>
</ElectionRowText>
<ElectionRowActions className="u-show-desktop-tablet" onClick={(e) => e.stopPropagation()}>
<CopyChip
defaultLabel="Copy link"
Expand Down
23 changes: 15 additions & 8 deletions src/js/pages/ElectionFinder/ElectionFinderHome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import ElectionActions from '../../actions/ElectionActions';
import { renderLog } from '../../common/utils/logging';
import { stateCodeMap } from '../../common/utils/addressFunctions';
import { stateCodeMap, convertStateCodeToStateText } from '../../common/utils/addressFunctions';
import isMobileScreenSize from '../../common/utils/isMobileScreenSize';
import { PageContentContainer } from '../../components/Style/pageLayoutStyles';
import historyPush from '../../common/utils/historyPush';
Expand All @@ -16,7 +16,7 @@ import ElectionFinderHeader from './ElectionFinderHeader';
import RowKebabMenu from './RowKebabMenu';
import {
ActionDivider, DarkTooltip,
ElectionDatePill, ElectionLink, ElectionList, ElectionRow, ElectionRowActions,
ElectionDateText, ElectionLink, ElectionList, ElectionRow, ElectionRowActions, ElectionRowText,
FilterTab, FilterTabsRow, InlineSearchField, NoResults,
SearchIconButton, SectionTitle, SectionTitleRow, ShowMoreButton,
StateSelectWrapper, StateSelectNative, StateSelectLabel, StateSelectCaret,
Expand All @@ -25,10 +25,12 @@ import webAppConfig from '../../config';

const nextReleaseFeaturesEnabled = webAppConfig.ENABLE_NEXT_RELEASE_FEATURES === undefined ? false : webAppConfig.ENABLE_NEXT_RELEASE_FEATURES;

function formatDateUS (dateString) {
const MONTH_ABBR = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

function formatDateLong (dateString) {
if (!dateString) return '';
const [y, m, d] = dateString.split('-');
return `${m}-${d}-${y}`;
return `${MONTH_ABBR[parseInt(m, 10) - 1]} ${parseInt(d, 10)}, ${y}`;
}

function sortByDateAsc (a, b) {
Expand Down Expand Up @@ -140,6 +142,7 @@ function ElectionFinderHome () {
<StateSelectCaret><ExpandMore fontSize="inherit" /></StateSelectCaret>
<StateSelectNative value={selectedStateCode} onChange={onStateChange}>
<option value="all">All states</option>
<option value="NA">National</option>
{SORTED_STATES.map(([code, name]) => (
<option key={code} value={code}>{name}</option>
))}
Expand Down Expand Up @@ -213,12 +216,16 @@ function ElectionFinderHome () {
key={googleCivicElectionId}
onClick={() => onElectionSelect(election)}
>
<ElectionLink>
<ElectionRowText>
<ElectionLink>
{election.state_code && election.state_code !== 'NA' ?
`${convertStateCodeToStateText(election.state_code)} – ${election.election_name || ''}` :
(election.election_name || '')}
</ElectionLink>
{election.election_day_text && (
<ElectionDatePill>{formatDateUS(election.election_day_text)}</ElectionDatePill>
<ElectionDateText>{formatDateLong(election.election_day_text)}</ElectionDateText>
)}
{election.election_name || ''}
</ElectionLink>
</ElectionRowText>
<ElectionRowActions className="u-show-desktop-tablet" onClick={(e) => e.stopPropagation()}>
<CopyChip defaultLabel="Copy link" getText={() => `${window.location.origin}${electionUrl}`} />
<ActionDivider />
Expand Down
15 changes: 9 additions & 6 deletions src/js/pages/ElectionFinder/electionFinderStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,11 @@ export const DetailTitle = styled('h2')`
margin: 0;
`;

export const ElectionDatePill = styled('span')`
display: inline-block;
export const ElectionDateText = styled('span')`
color: #848484;
font-size: 13px;
font-weight: 500;
margin-right: 8px;
vertical-align: middle;
font-size: 14px;
font-weight: 400;
margin-top: 2px;
white-space: nowrap;
`;

Expand All @@ -136,6 +134,11 @@ export const ElectionLink = styled('span')`
}
`;

export const ElectionRowText = styled('div')`
display: flex;
flex-direction: column;
`;

export const ElectionList = styled('div')`
display: flex;
flex-direction: column;
Expand Down
Loading