From 77940ac67310eaee1065c0f19f41a24f4e8f43bc Mon Sep 17 00:00:00 2001 From: Jamie B <53781962+JamieB-gu@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:52:36 +0000 Subject: [PATCH] Applied Styles To The Football Match List (#13244) * Applied styles to the football match list To match the way it appears on frontend. Updated the stories to display more edge cases: narrow dates, uneven scores. Broke the component down into sub-components to which styles are applied. Added palette light and dark colours. Also made use of `Intl.DateTimeFormat` to handle formatting the dates and times with appropriate locales and timezones for the different editions. Added functions to the `edition` module to get the timezone and locale for a given edition. * Move the `grid` module into `src` So it can be used outside storybook. * Fix name shadowing in `grid` module `from` was an import and a function argument. * Fix name shadowing in `grid` module `span` was the name of the function and one of its parameters. --------- Co-authored-by: Marjan Kalanaki --- .../.storybook/decorators/gridDecorators.tsx | 2 +- .../FootballLiveMatches.stories.tsx | 18 +- .../src/components/FootballLiveMatches.tsx | 271 ++++++++++++++++-- .../{.storybook/decorators => src}/grid.ts | 18 +- dotcom-rendering/src/lib/edition.ts | 12 + dotcom-rendering/src/paletteDeclarations.ts | 16 ++ 6 files changed, 292 insertions(+), 45 deletions(-) rename dotcom-rendering/{.storybook/decorators => src}/grid.ts (92%) diff --git a/dotcom-rendering/.storybook/decorators/gridDecorators.tsx b/dotcom-rendering/.storybook/decorators/gridDecorators.tsx index ea457589605..5c118f2e2a2 100644 --- a/dotcom-rendering/.storybook/decorators/gridDecorators.tsx +++ b/dotcom-rendering/.storybook/decorators/gridDecorators.tsx @@ -1,6 +1,6 @@ import { css } from '@emotion/react'; import type { Decorator } from '@storybook/react'; -import { grid } from './grid'; +import { grid } from '../../src/grid'; import { from } from '@guardian/source/foundations'; /** diff --git a/dotcom-rendering/src/components/FootballLiveMatches.stories.tsx b/dotcom-rendering/src/components/FootballLiveMatches.stories.tsx index d17078c0bed..7039182666d 100644 --- a/dotcom-rendering/src/components/FootballLiveMatches.stories.tsx +++ b/dotcom-rendering/src/components/FootballLiveMatches.stories.tsx @@ -1,11 +1,18 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { centreColumnDecorator } from '../../.storybook/decorators/gridDecorators'; import { FootballLiveMatches as FootballLiveMatchesComponent } from './FootballLiveMatches'; const meta = { title: 'Components/Football Live Matches', component: FootballLiveMatchesComponent, - decorators: [centreColumnDecorator], + decorators: [ + // To make it easier to see the top border above the date + (Story) => ( + <> +
+ + + ), + ], } satisfies Meta; export default meta; @@ -14,7 +21,8 @@ type Story = StoryObj; export const FootballLiveMatches = { args: { - matches: [ + edition: 'UK', + days: [ { date: new Date('2025-01-24T00:00:00Z'), competitions: [ @@ -24,11 +32,11 @@ export const FootballLiveMatches = { nation: 'European', matches: [ { - dateTime: new Date('2025-01-24T19:45:00Z'), + dateTime: new Date('2025-01-24T11:11:00Z'), paId: '4482093', homeTeam: { name: 'Torino', - score: 1, + score: 10, }, awayTeam: { name: 'Cagliari', diff --git a/dotcom-rendering/src/components/FootballLiveMatches.tsx b/dotcom-rendering/src/components/FootballLiveMatches.tsx index e04f1003201..9afe14f3924 100644 --- a/dotcom-rendering/src/components/FootballLiveMatches.tsx +++ b/dotcom-rendering/src/components/FootballLiveMatches.tsx @@ -1,36 +1,247 @@ -import { Fragment } from 'react'; +import { css } from '@emotion/react'; +import { + from, + headlineBold17, + space, + textSans14, + textSansBold14, + until, +} from '@guardian/source/foundations'; +import { Fragment, type ReactNode } from 'react'; import type { FootballMatches } from '../footballMatches'; +import { grid } from '../grid'; +import { + type EditionId, + getLocaleFromEdition, + getTimeZoneFromEdition, +} from '../lib/edition'; +import { palette } from '../palette'; type Props = { - matches: FootballMatches; + days: FootballMatches; + edition: EditionId; }; -export const FootballLiveMatches = ({ matches }: Props) => ( - <> - {matches.map((day) => ( - -

{day.date.toString()}

- {day.competitions.map((competition) => ( - -

{competition.name}

-
    - {competition.matches.map((match) => ( -
  • - - {match.homeTeam.name} {match.homeTeam.score} - -{match.awayTeam.score}{' '} - {match.awayTeam.name} -
  • - ))} -
-
- ))} -
- ))} - +const getDateFormatter = (edition: EditionId): Intl.DateTimeFormat => + new Intl.DateTimeFormat('en-GB', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + timeZone: getTimeZoneFromEdition(edition), + }); + +const getTimeFormatter = (edition: EditionId): Intl.DateTimeFormat => + new Intl.DateTimeFormat(getLocaleFromEdition(edition), { + hour: '2-digit', + minute: '2-digit', + timeZoneName: 'short', + hour12: false, + timeZone: getTimeZoneFromEdition(edition), + }); + +const Day = (props: { children: ReactNode }) => ( +

+ {props.children} +

+); + +const CompetitionName = (props: { children: ReactNode }) => ( +

+ {props.children} +

+); + +const Matches = (props: { children: ReactNode }) => ( +
    +); + +const Match = (props: { children: ReactNode }) => ( +
  • +); + +const MatchTime = (props: { children: ReactNode; dateTime: string }) => ( +