Skip to content
Open
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
79 changes: 79 additions & 0 deletions dotcom-rendering/src/components/FootballMatchStat.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { css } from '@emotion/react';
import { space } from '@guardian/source/foundations';
import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { palette } from '../palette';
import { FootballMatchStat } from './FootballMatchStat';

const meta = {
title: 'Components/Football Match Stat',
component: FootballMatchStat,
decorators: [
(Story) => (
<div
css={css`
padding: ${space[4]}px;
background-color: ${palette(
'--football-live-blog-background',
)};
`}
>
<Story />
</div>
),
],
parameters: {
viewport: {
defaultViewport: 'mobileMedium',
},
},
} satisfies Meta<typeof FootballMatchStat>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default = {
args: {
label: 'Goal Attempts',
home: {
teamName: 'Manchester United',
teamColour: '#da020e',
value: 7,
},
away: {
teamName: 'Arsenal',
teamColour: '#023474',
value: 4,
},
},
} satisfies Story;

export const ShownAsPercentage = {
args: {
label: 'Possession',
home: {
teamName: 'West Ham',
teamColour: '#722642',
value: 39,
},
away: {
teamName: 'Newcastle',
teamColour: '#383838',
value: 61,
},
showPercentage: true,
},
} satisfies Story;

export const RaisedLabelOnDesktop = {
args: {
...Default.args,
raiseLabelOnDesktop: true,
},
} satisfies Story;

export const LargeNumbersOnDesktop = {
args: {
...Default.args,
largeNumbersOnDesktop: true,
},
} satisfies Story;
164 changes: 164 additions & 0 deletions dotcom-rendering/src/components/FootballMatchStat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { css } from '@emotion/react';
import {
from,
textSansBold14,
textSansBold15,
textSansBold20,
textSansBold28,
visuallyHidden,
} from '@guardian/source/foundations';
import { palette } from '../palette';

const containerCss = css`
position: relative;
padding: 5px 10px 10px;
border: 1px solid ${palette('--football-match-stat-border')};
border-radius: 6px;
&::before {
position: absolute;
content: '';
left: 50%;
bottom: 0;
width: 1px;
height: 24px;
background-color: ${palette('--football-match-stat-border')};
}
`;

const headerCss = css`
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-areas: 'home-stat label away-stat';
`;

const raiseLabelCss = css`
${from.desktop} {
grid-template-areas:
'label label label'
'home-stat . away-stat';
}
`;

const labelCss = css`
${textSansBold14};
grid-area: label;
justify-self: center;
color: ${palette('--football-match-stat-name')};
${from.desktop} {
${textSansBold15};
}
`;

const numberCss = css`
${textSansBold20};
grid-area: home-stat;
color: var(--match-stat-team-colour);
`;

const largeNumberCss = css`
${from.desktop} {
${textSansBold28}
}
`;

const awayStatCss = css`
grid-area: away-stat;
justify-self: end;
`;

const chartCss = css`
position: relative;
display: flex;
gap: 10px;
`;

const barCss = css`
height: 8px;
width: var(--match-stat-percentage);
background-color: var(--match-stat-team-colour);
border-radius: 8px;
`;

type MatchStatistic = {
teamName: string;
teamColour: string;
value: number;
};

type Props = {
label: string;
home: MatchStatistic;
away: MatchStatistic;
showPercentage?: boolean;
raiseLabelOnDesktop?: boolean;
largeNumbersOnDesktop?: boolean;
Comment on lines +93 to +94
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't love these prop names so I'm open to suggestions

};

const formatValue = (value: number, showPercentage: boolean) =>
`${value}${showPercentage ? '%' : ''}`;

export const FootballMatchStat = ({
label,
home,
away,
showPercentage = false,
raiseLabelOnDesktop = false,
largeNumbersOnDesktop = false,
}: Props) => {
const homePercentage = (home.value / (home.value + away.value)) * 100;
const awayPercentage = (away.value / (home.value + away.value)) * 100;

return (
<div css={containerCss}>
<div css={[headerCss, raiseLabelOnDesktop && raiseLabelCss]}>
<span css={labelCss}>{label}</span>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It might make sense for this to be a heading? (Although the heading level would need to be configurable as it can appear in different places.)

<span
css={[numberCss, largeNumbersOnDesktop && largeNumberCss]}
style={{ '--match-stat-team-colour': home.teamColour }}
>
<span
css={css`
${visuallyHidden}
`}
>
{away.teamName}
</span>
{formatValue(home.value, showPercentage)}
</span>
<span
css={[
numberCss,
awayStatCss,
largeNumbersOnDesktop && largeNumberCss,
]}
style={{ '--match-stat-team-colour': away.teamColour }}
>
<span
css={css`
${visuallyHidden}
`}
>
{away.teamName}
</span>
{formatValue(away.value, showPercentage)}
</span>
</div>
<div aria-hidden="true" css={chartCss}>
<div
css={barCss}
style={{
'--match-stat-percentage': `${homePercentage}%`,
'--match-stat-team-colour': home.teamColour,
}}
></div>
<div
css={barCss}
style={{
'--match-stat-percentage': `${awayPercentage}%`,
'--match-stat-team-colour': away.teamColour,
}}
></div>
</div>
</div>
);
};
75 changes: 75 additions & 0 deletions dotcom-rendering/src/components/FootballMiniMatchStats.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { css } from '@emotion/react';
import { breakpoints, from } from '@guardian/source/foundations';
import type { Meta, StoryObj } from '@storybook/react-webpack5';
import { palette } from '../palette';
import { FootballMiniMatchStats as FootballMiniMatchStatsComponent } from './FootballMiniMatchStats';

const gridCss = css`
background-color: ${palette('--football-live-blog-background')};
/**
* Extremely simplified live blog grid layout as we're only interested in
* the 240px wide left column added at the desktop breakpoint.
* dotcom-rendering/src/layouts/LiveLayout.tsx
*/
${from.desktop} {
display: grid;
grid-column-gap: 20px;
grid-template-columns: 240px 1fr;
}
`;

const containerCss = css`
padding: 10px;
${from.desktop} {
padding-left: 20px;
padding-right: 0;
}
`;

const meta = {
title: 'Components/Football Mini Match Stats',
component: FootballMiniMatchStatsComponent,
decorators: [
(Story) => (
<div css={gridCss}>
<div css={containerCss}>
<Story />
</div>
</div>
),
],
parameters: {
chromatic: {
viewports: [
breakpoints.mobileMedium,
breakpoints.tablet,
breakpoints.wide,
],
},
},
} satisfies Meta<typeof FootballMiniMatchStatsComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

export const FootballMiniMatchStats = {
args: {
homeTeam: {
name: 'Manchester United',
colour: '#da020e',
},
awayTeam: {
name: 'Arsenal',
colour: '#023474',
},
stats: [
{
label: 'Possession',
homeValue: 39,
awayValue: 61,
showPercentage: true,
},
{ label: 'Goal Attempts', homeValue: 7, awayValue: 4 },
],
},
} satisfies Story;
Loading
Loading