Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ const SectionDiv = (props: {
<div
css={{
display: 'flex',
overflow: 'hidden',
'::before': {
...textSans12Object,
backgroundColor: palette(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { allModes } from '../../../.storybook/modes';
import preview from '../../../.storybook/preview';
import { palette } from '../../palette';
import { USCongress } from './USCongress';

/**
* Results from 2024:
* https://www.theguardian.com/us-news/ng-interactive/2024/nov/14/us-house-senate-and-governor-elections-2024-results-from-all-50-states
*/
const meta = preview.meta({
title: 'Components/Election Trackers/US Congress',
component: USCongress,
parameters: {
colourSchemeBackground: {
light: palette('--article-background'),
dark: palette('--article-background'),
},
chromatic: {
modes: {
'vertical mobile': allModes['vertical mobileMedium'],
'vertical tablet': allModes['vertical tablet'],
'vertical wide': allModes['vertical tablet'],
},
},
},
});

export const Empty = meta.story({
args: {
house: {
total: 435,
democrats: {
value: 0,
change: 0,
},
caucusWithDemocrats: {
value: 0,
change: 0,
},
republicans: {
value: 0,
change: 0,
},
caucusWithRepublicans: {
value: 0,
change: 0,
},
others: {
value: 0,
change: 0,
},
},
senate: {
total: 34,
democrats: {
value: 0,
change: 0,
holdovers: 28,
},
caucusWithDemocrats: {
value: 0,
change: 0,
holdovers: 0,
},
republicans: {
value: 0,
change: 0,
holdovers: 38,
},
caucusWithRepublicans: {
value: 0,
change: 0,
holdovers: 0,
},
others: {
value: 0,
change: 0,
holdovers: 0,
},
},
link: new URL('https://www.theguardian.com'),
linkText: 'Full results',
},
});

export const Final = Empty.extend({
args: {
house: {
total: Empty.composed.args.house.total,
democrats: {
value: 215,
change: 1,
},
caucusWithDemocrats: {
value: 0,
change: 0,
},
republicans: {
value: 220,
change: -1,
},
caucusWithRepublicans: {
value: 0,
change: 0,
},
others: {
value: 0,
change: 0,
},
},
senate: {
total: Empty.composed.args.senate.total,
democrats: {
value: 17,
change: -2,
holdovers: Empty.composed.args.senate.democrats.holdovers,
},
caucusWithDemocrats: {
value: 2,
change: -2,
holdovers:
Empty.composed.args.senate.caucusWithDemocrats.holdovers,
},
republicans: {
value: 15,
change: 4,
holdovers: Empty.composed.args.senate.republicans.holdovers,
},
caucusWithRepublicans: {
value: 0,
change: 0,
holdovers:
Empty.composed.args.senate.caucusWithRepublicans.holdovers,
},
others: {
value: 0,
change: 0,
holdovers: Empty.composed.args.senate.others.holdovers,
},
},
},
});
49 changes: 49 additions & 0 deletions dotcom-rendering/src/components/ElectionTrackers/USCongress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { ComponentProps } from 'react';
import { OnwardLink } from './OnwardLink';
import { SideBySide } from './SideBySide';
import { USHouse } from './USHouse';
import { USSenate } from './USSenate';

type Props = {
/**
* Results for the US Senate. See {@linkcode USSenate} for more details.
*/
senate: ComponentProps<typeof USSenate>;
/**
* Results for the US House of Representatives. See {@linkcode USSenate} for
* more details.
*/
house: ComponentProps<typeof USHouse>;
/**
* Text to be shown on the onward link to the full results page. For
* example, "Full results".
*/
linkText: string;
/**
* Onward link to the full results page.
*/
link: URL;
};

/**
* Represents results from elections to the US Congress, and includes a summary
* for both the Senate and the House of Representatives. Each can also be
* represented individually using the {@linkcode USSenate} or
* {@linkcode USHouse} components.
*/
export const USCongress = (props: Props) => (
<>
<SideBySide
from="tablet"
left={{
heading: 'Senate',
children: <USSenate {...props.senate} />,
}}
right={{
heading: 'House',
children: <USHouse {...props.house} />,
}}
/>
<OnwardLink link={props.link} text={props.linkText} />
</>
);
120 changes: 120 additions & 0 deletions dotcom-rendering/src/components/ElectionTrackers/USHouse.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { space } from '@guardian/source/foundations';
import { palette } from '../../palette';
import { ProgressNumber } from './ProgressNumber';
import { StackedProgress } from './StackedProgress';
import { Versus } from './Versus';

type Props = {
/**
* The total number of seats up for election.
*/
total: number;
democrats: Group;
caucusWithDemocrats: Group;
republicans: Group;
caucusWithRepublicans: Group;
others: Group;
};

type Group = {
/**
* The number of races called for this group so far.
*/
value: number;
/**
* The net change in seats for this group so far.
*/
change: number;
};

/**
* Represents results from elections to the US House of Representatives. It can
* be used on its own, or as part of the `USCongress` component, which also
* includes the Senate.
*/
export const USHouse = (props: Props) => (
<>
<Versus
left={{
name: `Democrats${democratIndependents(props) ? '*' : ''}`,
abbreviation: `Democrats${democratIndependents(props) ? '*' : ''}`,
value: props.democrats.value + props.caucusWithDemocrats.value,
change:
props.democrats.change + props.caucusWithDemocrats.change,
image: undefined,
colour: palette('--us-elections-democrats'),
}}
right={{
name: `Republicans${republicanIndependents(props) ? '*' : ''}`,
abbreviation: `Republicans${republicanIndependents(props) ? '*' : ''}`,
value:
props.republicans.value + props.caucusWithRepublicans.value,
change:
props.republicans.change +
props.caucusWithRepublicans.change,
image: undefined,
colour: palette('--us-elections-republicans'),
}}
colour="none"
faded={false}
banner={undefined}
/>
<StackedProgress
total={props.total}
label="to win"
calculateWinner={true}
excludedCopy={undefined}
sections={[
{
name: 'Democrats',
colour: palette('--us-elections-democrats'),
value:
props.democrats.value + props.caucusWithDemocrats.value,
align: 'left',
exclude: false,
},
{
name: 'Others',
colour: palette('--us-elections-others'),
value: props.others.value,
align: 'left',
exclude: false,
},
{
name: 'Republicans',
colour: palette('--us-elections-republicans'),
value:
props.republicans.value +
props.caucusWithRepublicans.value,
align: 'right',
exclude: false,
},
]}
css={{
paddingTop: space[2],
}}
/>
<ProgressNumber
progress={
props.democrats.value +
props.caucusWithDemocrats.value +
props.republicans.value +
props.caucusWithRepublicans.value +
props.others.value
}
total={props.total}
copy="races called"
additionalCopy={
democratIndependents(props) || republicanIndependents(props)
? '*includes independents'
: undefined
}
/>
</>
);

const democratIndependents = (props: Props) =>
props.caucusWithDemocrats.value > 0;

const republicanIndependents = (props: Props) =>
props.caucusWithRepublicans.value > 0;
Loading
Loading