diff --git a/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx b/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx index 9f1440322..09d22b69c 100644 --- a/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx +++ b/src/js/pages/ElectionFinder/ElectionFinderForElection.jsx @@ -381,6 +381,11 @@ function OfficeSectionItemInner ({ // eslint-disable-line react/no-multi-comp candidateName} /> `${window.location.origin}${getCandidatePath(candidate)}`} /> + {nextReleaseFeaturesEnabled && ( + + + + )} window.open(getCandidatePath(candidate), '_blank')}> @@ -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') }, ]} /> diff --git a/src/js/pages/ElectionFinder/ElectionFinderForState.jsx b/src/js/pages/ElectionFinder/ElectionFinderForState.jsx index d70258ba5..1b3beab37 100644 --- a/src/js/pages/ElectionFinder/ElectionFinderForState.jsx +++ b/src/js/pages/ElectionFinder/ElectionFinderForState.jsx @@ -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, @@ -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'; @@ -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'); @@ -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) { @@ -167,6 +173,7 @@ function ElectionFinderForState () { + {SORTED_STATES.map(([code, name]) => ( ))} @@ -239,12 +246,12 @@ function ElectionFinderForState () { key={googleCivicElectionId} onClick={() => onElectionSelect(googleCivicElectionId)} > - + + {election.election_name || ''} {election.election_day_text && ( - {formatDateUS(election.election_day_text)} + {formatDateLong(election.election_day_text)} )} - {election.election_name || ''} - + e.stopPropagation()}> + {SORTED_STATES.map(([code, name]) => ( ))} @@ -213,12 +216,16 @@ function ElectionFinderHome () { key={googleCivicElectionId} onClick={() => onElectionSelect(election)} > - + + + {election.state_code && election.state_code !== 'NA' ? + `${convertStateCodeToStateText(election.state_code)} – ${election.election_name || ''}` : + (election.election_name || '')} + {election.election_day_text && ( - {formatDateUS(election.election_day_text)} + {formatDateLong(election.election_day_text)} )} - {election.election_name || ''} - + e.stopPropagation()}> `${window.location.origin}${electionUrl}`} /> diff --git a/src/js/pages/ElectionFinder/electionFinderStyles.js b/src/js/pages/ElectionFinder/electionFinderStyles.js index cbaaea697..514eaeeb0 100644 --- a/src/js/pages/ElectionFinder/electionFinderStyles.js +++ b/src/js/pages/ElectionFinder/electionFinderStyles.js @@ -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; `; @@ -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;